• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agentic-dev-library / thumbcode / 21933729139

12 Feb 2026 04:36AM UTC coverage: 28.401% (+0.7%) from 27.702%
21933729139

Pull #116

github

web-flow
Merge b9d1b07d1 into c6c31bd07
Pull Request #116: refactor: decompose 9 monolith files into focused modules

406 of 2268 branches covered (17.9%)

Branch coverage included in aggregate %.

365 of 845 new or added lines in 22 files covered. (43.2%)

1 existing line in 1 file now uncovered.

1120 of 3105 relevant lines covered (36.07%)

7.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.51
/packages/core/src/credentials/KeyValidator.ts
1
/**
2
 * Key Validator
3
 *
4
 * Validates API credentials against their respective provider endpoints.
5
 * Handles GitHub, Anthropic, and OpenAI key validation.
6
 */
7

8
import { secureFetch } from '../api/api';
9
import type { CredentialType, ValidationResult } from './types';
10
import { validate } from './validation';
11

12
// Validation API endpoints
13
const VALIDATION_ENDPOINTS = {
5✔
14
  github: 'https://api.github.com/user',
15
  anthropic: 'https://api.anthropic.com/v1/messages',
16
  openai: 'https://api.openai.com/v1/models',
17
} as const;
18

19
export class KeyValidator {
20
  /**
21
   * Validate a credential against its respective API
22
   */
23
  async validateCredential(type: CredentialType, secret: string): Promise<ValidationResult> {
24
    if (!validate(type, secret)) {
3!
NEW
25
      return { isValid: false, message: 'Invalid credential format' };
×
26
    }
27

28
    try {
3✔
29
      switch (type) {
3!
30
        case 'github':
31
          return this.validateGitHubToken(secret);
1✔
32
        case 'anthropic':
33
          return this.validateAnthropicKey(secret);
1✔
34
        case 'openai':
35
          return this.validateOpenAIKey(secret);
1✔
36
        case 'mcp_server':
NEW
37
          return { isValid: true, message: 'MCP server credentials accepted' };
×
38
        default:
NEW
39
          return { isValid: true, message: 'Credential accepted without validation' };
×
40
      }
41
    } catch (error) {
NEW
42
      return {
×
43
        isValid: false,
44
        message: error instanceof Error ? error.message : 'Validation failed',
×
45
      };
46
    }
47
  }
48

49
  /**
50
   * Validate a GitHub personal access token
51
   */
52
  private async validateGitHubToken(token: string): Promise<ValidationResult> {
53
    try {
1✔
54
      const response = await secureFetch(VALIDATION_ENDPOINTS.github, {
1✔
55
        headers: {
56
          Authorization: `Bearer ${token}`,
57
          Accept: 'application/vnd.github.v3+json',
58
        },
59
      });
60

61
      if (!response.ok) {
1!
NEW
62
        if (response.status === 401) {
×
NEW
63
          return { isValid: false, message: 'Invalid GitHub token' };
×
64
        }
NEW
65
        return { isValid: false, message: `GitHub API error: ${response.status}` };
×
66
      }
67

68
      const user = await response.json();
1✔
69
      const expiresAt = response.headers.get('github-authentication-token-expiration');
1✔
70

71
      return {
1✔
72
        isValid: true,
73
        message: `Authenticated as ${user.login}`,
74
        expiresAt: expiresAt ? new Date(expiresAt) : undefined,
1!
75
        metadata: {
76
          username: user.login,
77
          avatarUrl: user.avatar_url,
78
          name: user.name,
79
          scopes: response.headers.get('x-oauth-scopes')?.split(', ') || [],
2✔
80
          rateLimit: parseInt(response.headers.get('x-ratelimit-remaining') || '0', 10),
2✔
81
        },
82
      };
83
    } catch (error) {
NEW
84
      return {
×
85
        isValid: false,
86
        message: error instanceof Error ? error.message : 'GitHub validation failed',
×
87
      };
88
    }
89
  }
90

91
  /**
92
   * Validate an Anthropic API key
93
   */
94
  private async validateAnthropicKey(apiKey: string): Promise<ValidationResult> {
95
    try {
1✔
96
      const response = await secureFetch(VALIDATION_ENDPOINTS.anthropic, {
1✔
97
        method: 'POST',
98
        headers: {
99
          'x-api-key': apiKey,
100
          'anthropic-version': '2023-06-01',
101
          'Content-Type': 'application/json',
102
        },
103
        body: JSON.stringify({
104
          model: 'claude-3-haiku-20240307',
105
          max_tokens: 1,
106
          messages: [{ role: 'user', content: 'Hi' }],
107
        }),
108
      });
109

110
      if (response.ok || response.status === 200) {
1!
111
        return {
1✔
112
          isValid: true,
113
          message: 'Anthropic API key is valid',
114
          metadata: {
115
            rateLimit: parseInt(
116
              response.headers.get('anthropic-ratelimit-requests-remaining') || '0',
2✔
117
              10
118
            ),
119
          },
120
        };
121
      }
122

NEW
123
      if (response.status === 401) {
×
NEW
124
        return { isValid: false, message: 'Invalid Anthropic API key' };
×
125
      }
126

NEW
127
      if (response.status === 429) {
×
NEW
128
        return {
×
129
          isValid: true,
130
          message: 'Anthropic API key valid but rate limited',
131
        };
132
      }
133

NEW
134
      return { isValid: false, message: `Anthropic API error: ${response.status}` };
×
135
    } catch (error) {
NEW
136
      return {
×
137
        isValid: false,
138
        message: error instanceof Error ? error.message : 'Anthropic validation failed',
×
139
      };
140
    }
141
  }
142

143
  /**
144
   * Validate an OpenAI API key
145
   */
146
  private async validateOpenAIKey(apiKey: string): Promise<ValidationResult> {
147
    try {
1✔
148
      const response = await secureFetch(VALIDATION_ENDPOINTS.openai, {
1✔
149
        headers: {
150
          Authorization: `Bearer ${apiKey}`,
151
        },
152
      });
153

154
      if (response.ok) {
1!
155
        return {
1✔
156
          isValid: true,
157
          message: 'OpenAI API key is valid',
158
          metadata: {
159
            rateLimit: parseInt(response.headers.get('x-ratelimit-remaining-requests') || '0', 10),
2✔
160
          },
161
        };
162
      }
163

NEW
164
      if (response.status === 401) {
×
NEW
165
        return { isValid: false, message: 'Invalid OpenAI API key' };
×
166
      }
167

NEW
168
      return { isValid: false, message: `OpenAI API error: ${response.status}` };
×
169
    } catch (error) {
NEW
170
      return {
×
171
        isValid: false,
172
        message: error instanceof Error ? error.message : 'OpenAI validation failed',
×
173
      };
174
    }
175
  }
176

177
  /**
178
   * Mask a secret for display purposes
179
   */
180
  maskSecret(secret: string, type: CredentialType): string {
NEW
181
    if (!secret) return '';
×
182

NEW
183
    switch (type) {
×
184
      case 'github':
NEW
185
        if (
×
186
          secret.startsWith('ghp_') ||
×
187
          secret.startsWith('gho_') ||
188
          secret.startsWith('ghs_')
189
        ) {
NEW
190
          return `${secret.slice(0, 7)}...${secret.slice(-4)}`;
×
191
        }
NEW
192
        return `${secret.slice(0, 4)}...${secret.slice(-4)}`;
×
193

194
      case 'anthropic':
NEW
195
        if (secret.startsWith('sk-ant-')) {
×
NEW
196
          return `sk-ant-...${secret.slice(-4)}`;
×
197
        }
NEW
198
        return `${secret.slice(0, 4)}...${secret.slice(-4)}`;
×
199

200
      case 'openai':
NEW
201
        if (secret.startsWith('sk-')) {
×
NEW
202
          return `sk-...${secret.slice(-4)}`;
×
203
        }
NEW
204
        return `${secret.slice(0, 4)}...${secret.slice(-4)}`;
×
205

206
      default:
NEW
207
        return `${secret.slice(0, 4)}...${secret.slice(-4)}`;
×
208
    }
209
  }
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc