Skip to content

Commit cbd0cd6

Browse files
Copilothotlong
andcommitted
Implement SubtleCrypto-based browser signature verification
Replace TODO stub in verifyCryptoSignatureBrowser with a real implementation using the Web Crypto API. Supports both RS256 (RSASSA-PKCS1-v1_5 with SHA-256) and ES256 (ECDSA with P-256/SHA-256), matching the existing Node.js implementation pattern. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 1d250c3 commit cbd0cd6

1,278 files changed

Lines changed: 1325 additions & 215449 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/core/src/security/plugin-signature-verifier.ts

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,55 @@ export class PluginSignatureVerifier {
336336
}
337337

338338
private async verifyCryptoSignatureBrowser(
339-
_data: string,
340-
_signature: string,
341-
_publicKey: string
339+
data: string,
340+
signature: string,
341+
publicKey: string
342342
): Promise<boolean> {
343-
// Browser implementation using SubtleCrypto
344-
// TODO: Implement SubtleCrypto-based verification
345-
this.logger.warn('Browser signature verification not yet implemented');
346-
return false;
343+
try {
344+
const subtle = globalThis.crypto?.subtle;
345+
if (!subtle) {
346+
this.logger.error('SubtleCrypto not available in this environment');
347+
return false;
348+
}
349+
350+
// Decode PEM public key to raw DER bytes
351+
const pemBody = publicKey
352+
.replace(/-----BEGIN PUBLIC KEY-----/, '')
353+
.replace(/-----END PUBLIC KEY-----/, '')
354+
.replace(/\s/g, '');
355+
const keyBytes = Uint8Array.from(atob(pemBody), c => c.charCodeAt(0));
356+
357+
// Configure algorithms based on RS256 or ES256
358+
let importAlgorithm: RsaHashedImportParams | EcKeyImportParams;
359+
let verifyAlgorithm: AlgorithmIdentifier | EcdsaParams;
360+
361+
if (this.config.algorithm === 'ES256') {
362+
importAlgorithm = { name: 'ECDSA', namedCurve: 'P-256' };
363+
verifyAlgorithm = { name: 'ECDSA', hash: 'SHA-256' };
364+
} else {
365+
importAlgorithm = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };
366+
verifyAlgorithm = { name: 'RSASSA-PKCS1-v1_5' };
367+
}
368+
369+
const cryptoKey = await subtle.importKey(
370+
'spki',
371+
keyBytes,
372+
importAlgorithm,
373+
false,
374+
['verify']
375+
);
376+
377+
// Decode base64 signature to ArrayBuffer
378+
const signatureBytes = Uint8Array.from(atob(signature), c => c.charCodeAt(0));
379+
380+
// Encode data to ArrayBuffer
381+
const dataBytes = new TextEncoder().encode(data);
382+
383+
return await subtle.verify(verifyAlgorithm, cryptoKey, signatureBytes, dataBytes);
384+
} catch (error) {
385+
this.logger.error('Browser signature verification failed', error as Error);
386+
return false;
387+
}
347388
}
348389

349390
private validateConfig(): void {
Lines changed: 1 addition & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -1,225 +1,7 @@
11
{
22
"$ref": "#/definitions/AICodeReviewResult",
33
"definitions": {
4-
"AICodeReviewResult": {
5-
"type": "object",
6-
"properties": {
7-
"assessment": {
8-
"type": "string",
9-
"enum": [
10-
"excellent",
11-
"good",
12-
"acceptable",
13-
"needs-improvement",
14-
"poor"
15-
]
16-
},
17-
"score": {
18-
"type": "number",
19-
"minimum": 0,
20-
"maximum": 100
21-
},
22-
"issues": {
23-
"type": "array",
24-
"items": {
25-
"type": "object",
26-
"properties": {
27-
"severity": {
28-
"type": "string",
29-
"enum": [
30-
"critical",
31-
"error",
32-
"warning",
33-
"info",
34-
"style"
35-
]
36-
},
37-
"category": {
38-
"type": "string",
39-
"enum": [
40-
"bug",
41-
"security",
42-
"performance",
43-
"maintainability",
44-
"style",
45-
"documentation",
46-
"testing",
47-
"type-safety",
48-
"best-practice"
49-
]
50-
},
51-
"file": {
52-
"type": "string"
53-
},
54-
"line": {
55-
"type": "integer"
56-
},
57-
"column": {
58-
"type": "integer"
59-
},
60-
"message": {
61-
"type": "string"
62-
},
63-
"suggestion": {
64-
"type": "string"
65-
},
66-
"autoFixable": {
67-
"type": "boolean",
68-
"default": false
69-
},
70-
"autoFix": {
71-
"type": "string",
72-
"description": "Automated fix code"
73-
}
74-
},
75-
"required": [
76-
"severity",
77-
"category",
78-
"file",
79-
"message"
80-
],
81-
"additionalProperties": false
82-
}
83-
},
84-
"highlights": {
85-
"type": "array",
86-
"items": {
87-
"type": "object",
88-
"properties": {
89-
"category": {
90-
"type": "string"
91-
},
92-
"description": {
93-
"type": "string"
94-
},
95-
"file": {
96-
"type": "string"
97-
}
98-
},
99-
"required": [
100-
"category",
101-
"description"
102-
],
103-
"additionalProperties": false
104-
}
105-
},
106-
"metrics": {
107-
"type": "object",
108-
"properties": {
109-
"complexity": {
110-
"type": "number"
111-
},
112-
"maintainability": {
113-
"type": "number",
114-
"minimum": 0,
115-
"maximum": 100
116-
},
117-
"testCoverage": {
118-
"type": "number",
119-
"minimum": 0,
120-
"maximum": 100
121-
},
122-
"duplicateCode": {
123-
"type": "number",
124-
"minimum": 0,
125-
"maximum": 100
126-
},
127-
"technicalDebt": {
128-
"type": "string",
129-
"description": "Estimated technical debt"
130-
}
131-
},
132-
"additionalProperties": false
133-
},
134-
"recommendations": {
135-
"type": "array",
136-
"items": {
137-
"type": "object",
138-
"properties": {
139-
"priority": {
140-
"type": "string",
141-
"enum": [
142-
"high",
143-
"medium",
144-
"low"
145-
]
146-
},
147-
"title": {
148-
"type": "string"
149-
},
150-
"description": {
151-
"type": "string"
152-
},
153-
"effort": {
154-
"type": "string",
155-
"enum": [
156-
"trivial",
157-
"small",
158-
"medium",
159-
"large"
160-
]
161-
}
162-
},
163-
"required": [
164-
"priority",
165-
"title",
166-
"description"
167-
],
168-
"additionalProperties": false
169-
}
170-
},
171-
"security": {
172-
"type": "object",
173-
"properties": {
174-
"vulnerabilities": {
175-
"type": "array",
176-
"items": {
177-
"type": "object",
178-
"properties": {
179-
"severity": {
180-
"type": "string",
181-
"enum": [
182-
"critical",
183-
"high",
184-
"medium",
185-
"low"
186-
]
187-
},
188-
"type": {
189-
"type": "string"
190-
},
191-
"description": {
192-
"type": "string"
193-
},
194-
"remediation": {
195-
"type": "string"
196-
}
197-
},
198-
"required": [
199-
"severity",
200-
"type",
201-
"description"
202-
],
203-
"additionalProperties": false
204-
}
205-
},
206-
"score": {
207-
"type": "number",
208-
"minimum": 0,
209-
"maximum": 100
210-
}
211-
},
212-
"additionalProperties": false
213-
}
214-
},
215-
"required": [
216-
"assessment",
217-
"score",
218-
"issues",
219-
"recommendations"
220-
],
221-
"additionalProperties": false
222-
}
4+
"AICodeReviewResult": {}
2235
},
2246
"$schema": "http://json-schema.org/draft-07/schema#"
2257
}
Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,7 @@
11
{
22
"$ref": "#/definitions/AIKnowledge",
33
"definitions": {
4-
"AIKnowledge": {
5-
"type": "object",
6-
"properties": {
7-
"topics": {
8-
"type": "array",
9-
"items": {
10-
"type": "string"
11-
},
12-
"description": "Topics/Tags to recruit knowledge from"
13-
},
14-
"indexes": {
15-
"type": "array",
16-
"items": {
17-
"type": "string"
18-
},
19-
"description": "Vector Store Indexes"
20-
}
21-
},
22-
"required": [
23-
"topics",
24-
"indexes"
25-
],
26-
"additionalProperties": false
27-
}
4+
"AIKnowledge": {}
285
},
296
"$schema": "http://json-schema.org/draft-07/schema#"
307
}
Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,7 @@
11
{
22
"$ref": "#/definitions/AIModelConfig",
33
"definitions": {
4-
"AIModelConfig": {
5-
"type": "object",
6-
"properties": {
7-
"provider": {
8-
"type": "string",
9-
"enum": [
10-
"openai",
11-
"azure_openai",
12-
"anthropic",
13-
"local"
14-
],
15-
"default": "openai"
16-
},
17-
"model": {
18-
"type": "string",
19-
"description": "Model name (e.g. gpt-4, claude-3-opus)"
20-
},
21-
"temperature": {
22-
"type": "number",
23-
"minimum": 0,
24-
"maximum": 2,
25-
"default": 0.7
26-
},
27-
"maxTokens": {
28-
"type": "number"
29-
},
30-
"topP": {
31-
"type": "number"
32-
}
33-
},
34-
"required": [
35-
"model"
36-
],
37-
"additionalProperties": false
38-
}
4+
"AIModelConfig": {}
395
},
406
"$schema": "http://json-schema.org/draft-07/schema#"
417
}

0 commit comments

Comments
 (0)