qa360 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/history.js +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/run.d.ts +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/serve.js +1 -1
- package/dist/commands/verify.js +1 -1
- package/dist/core/adapters/gitleaks-secrets.d.ts +115 -0
- package/dist/core/adapters/gitleaks-secrets.d.ts.map +1 -0
- package/dist/core/adapters/gitleaks-secrets.js +410 -0
- package/dist/core/adapters/k6-perf.d.ts +86 -0
- package/dist/core/adapters/k6-perf.d.ts.map +1 -0
- package/dist/core/adapters/k6-perf.js +398 -0
- package/dist/core/adapters/osv-deps.d.ts +124 -0
- package/dist/core/adapters/osv-deps.d.ts.map +1 -0
- package/dist/core/adapters/osv-deps.js +372 -0
- package/dist/core/adapters/playwright-api.d.ts +82 -0
- package/dist/core/adapters/playwright-api.d.ts.map +1 -0
- package/dist/core/adapters/playwright-api.js +252 -0
- package/dist/core/adapters/playwright-ui.d.ts +115 -0
- package/dist/core/adapters/playwright-ui.d.ts.map +1 -0
- package/dist/core/adapters/playwright-ui.js +346 -0
- package/dist/core/adapters/semgrep-sast.d.ts +100 -0
- package/dist/core/adapters/semgrep-sast.d.ts.map +1 -0
- package/dist/core/adapters/semgrep-sast.js +322 -0
- package/dist/core/adapters/zap-dast.d.ts +134 -0
- package/dist/core/adapters/zap-dast.d.ts.map +1 -0
- package/dist/core/adapters/zap-dast.js +424 -0
- package/dist/core/hooks/compose.d.ts +62 -0
- package/dist/core/hooks/compose.d.ts.map +1 -0
- package/dist/core/hooks/compose.js +225 -0
- package/dist/core/hooks/runner.d.ts +69 -0
- package/dist/core/hooks/runner.d.ts.map +1 -0
- package/dist/core/hooks/runner.js +303 -0
- package/dist/core/index.d.ts +74 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +39 -0
- package/dist/core/pack/migrator.d.ts +52 -0
- package/dist/core/pack/migrator.d.ts.map +1 -0
- package/dist/core/pack/migrator.js +304 -0
- package/dist/core/pack/validator.d.ts +43 -0
- package/dist/core/pack/validator.d.ts.map +1 -0
- package/dist/core/pack/validator.js +292 -0
- package/dist/core/proof/bundle.d.ts +138 -0
- package/dist/core/proof/bundle.d.ts.map +1 -0
- package/dist/core/proof/bundle.js +160 -0
- package/dist/core/proof/canonicalize.d.ts +48 -0
- package/dist/core/proof/canonicalize.d.ts.map +1 -0
- package/dist/core/proof/canonicalize.js +105 -0
- package/dist/core/proof/index.d.ts +14 -0
- package/dist/core/proof/index.d.ts.map +1 -0
- package/dist/core/proof/index.js +18 -0
- package/dist/core/proof/schema.d.ts +218 -0
- package/dist/core/proof/schema.d.ts.map +1 -0
- package/dist/core/proof/schema.js +263 -0
- package/dist/core/proof/signer.d.ts +112 -0
- package/dist/core/proof/signer.d.ts.map +1 -0
- package/dist/core/proof/signer.js +226 -0
- package/dist/core/proof/verifier.d.ts +98 -0
- package/dist/core/proof/verifier.d.ts.map +1 -0
- package/dist/core/proof/verifier.js +302 -0
- package/dist/core/runner/phase3-runner.d.ts +102 -0
- package/dist/core/runner/phase3-runner.d.ts.map +1 -0
- package/dist/core/runner/phase3-runner.js +471 -0
- package/dist/core/secrets/crypto.d.ts +76 -0
- package/dist/core/secrets/crypto.d.ts.map +1 -0
- package/dist/core/secrets/crypto.js +225 -0
- package/dist/core/secrets/manager.d.ts +77 -0
- package/dist/core/secrets/manager.d.ts.map +1 -0
- package/dist/core/secrets/manager.js +219 -0
- package/dist/core/security/redaction-patterns-extended.d.ts +28 -0
- package/dist/core/security/redaction-patterns-extended.d.ts.map +1 -0
- package/dist/core/security/redaction-patterns-extended.js +247 -0
- package/dist/core/security/redactor.d.ts +72 -0
- package/dist/core/security/redactor.d.ts.map +1 -0
- package/dist/core/security/redactor.js +279 -0
- package/dist/core/serve/diagnostics-collector.d.ts +33 -0
- package/dist/core/serve/diagnostics-collector.d.ts.map +1 -0
- package/dist/core/serve/diagnostics-collector.js +149 -0
- package/dist/core/serve/health-checker.d.ts +45 -0
- package/dist/core/serve/health-checker.d.ts.map +1 -0
- package/dist/core/serve/health-checker.js +219 -0
- package/dist/core/serve/index.d.ts +9 -0
- package/dist/core/serve/index.d.ts.map +1 -0
- package/dist/core/serve/index.js +8 -0
- package/dist/core/serve/metrics-collector.d.ts +25 -0
- package/dist/core/serve/metrics-collector.d.ts.map +1 -0
- package/dist/core/serve/metrics-collector.js +322 -0
- package/dist/core/serve/process-manager.d.ts +37 -0
- package/dist/core/serve/process-manager.d.ts.map +1 -0
- package/dist/core/serve/process-manager.js +213 -0
- package/dist/core/serve/server.d.ts +37 -0
- package/dist/core/serve/server.d.ts.map +1 -0
- package/dist/core/serve/server.js +191 -0
- package/dist/core/types/pack-v1.d.ts +162 -0
- package/dist/core/types/pack-v1.d.ts.map +1 -0
- package/dist/core/types/pack-v1.js +5 -0
- package/dist/core/types/trust-score.d.ts +70 -0
- package/dist/core/types/trust-score.d.ts.map +1 -0
- package/dist/core/types/trust-score.js +191 -0
- package/dist/core/vault/cas.d.ts +87 -0
- package/dist/core/vault/cas.d.ts.map +1 -0
- package/dist/core/vault/cas.js +255 -0
- package/dist/core/vault/index.d.ts +205 -0
- package/dist/core/vault/index.d.ts.map +1 -0
- package/dist/core/vault/index.js +631 -0
- package/package.json +12 -6
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 OSV Dependencies Adapter (Sécurité Réelle)
|
|
3
|
+
* Scan vulnerabilities in package-lock.json / pnpm-lock.yaml
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { SecurityRedactor } from '../security/redactor.js';
|
|
9
|
+
export class OsvDepsAdapter {
|
|
10
|
+
redactor;
|
|
11
|
+
workingDir;
|
|
12
|
+
constructor(workingDir = process.cwd()) {
|
|
13
|
+
this.redactor = SecurityRedactor.forLogs();
|
|
14
|
+
this.workingDir = workingDir;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Execute OSV dependency scan
|
|
18
|
+
*/
|
|
19
|
+
async runDependencyScan(config) {
|
|
20
|
+
try {
|
|
21
|
+
console.log('🔍 Running OSV dependency vulnerability scan');
|
|
22
|
+
// Find lock files to scan
|
|
23
|
+
const lockFiles = this.findLockFiles(config);
|
|
24
|
+
if (lockFiles.length === 0) {
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
vulnerabilities: [],
|
|
28
|
+
summary: this.getEmptySummary(),
|
|
29
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
30
|
+
scannedFiles: [],
|
|
31
|
+
error: 'No lock files found to scan',
|
|
32
|
+
errorCode: 'OSV001'
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Execute OSV scanner
|
|
36
|
+
const result = await this.executeOsvScan(lockFiles, config);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
vulnerabilities: [],
|
|
43
|
+
summary: this.getEmptySummary(),
|
|
44
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
45
|
+
scannedFiles: [],
|
|
46
|
+
error: this.redactor.redact(error instanceof Error ? error.message : 'Unknown error'),
|
|
47
|
+
errorCode: 'OSV099'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Find lock files in working directory
|
|
53
|
+
*/
|
|
54
|
+
findLockFiles(config) {
|
|
55
|
+
const lockFiles = [];
|
|
56
|
+
// Custom lock files from config
|
|
57
|
+
if (config.lockFiles) {
|
|
58
|
+
for (const file of config.lockFiles) {
|
|
59
|
+
const fullPath = join(config.workingDir, file);
|
|
60
|
+
if (existsSync(fullPath)) {
|
|
61
|
+
lockFiles.push(fullPath);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return lockFiles;
|
|
65
|
+
}
|
|
66
|
+
// Standard lock files
|
|
67
|
+
const standardFiles = [
|
|
68
|
+
'package-lock.json',
|
|
69
|
+
'pnpm-lock.yaml',
|
|
70
|
+
'yarn.lock',
|
|
71
|
+
'Pipfile.lock',
|
|
72
|
+
'poetry.lock',
|
|
73
|
+
'Gemfile.lock',
|
|
74
|
+
'go.sum',
|
|
75
|
+
'Cargo.lock'
|
|
76
|
+
];
|
|
77
|
+
for (const file of standardFiles) {
|
|
78
|
+
const fullPath = join(config.workingDir, file);
|
|
79
|
+
if (existsSync(fullPath)) {
|
|
80
|
+
lockFiles.push(fullPath);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return lockFiles;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Execute OSV scanner on lock files
|
|
87
|
+
*/
|
|
88
|
+
async executeOsvScan(lockFiles, config) {
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
let output = '';
|
|
91
|
+
let error = '';
|
|
92
|
+
// Use osv-scanner if available, otherwise use API
|
|
93
|
+
const args = ['--format', 'json', ...lockFiles];
|
|
94
|
+
const child = spawn('osv-scanner', args, {
|
|
95
|
+
cwd: config.workingDir,
|
|
96
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
97
|
+
});
|
|
98
|
+
const timeout = setTimeout(() => {
|
|
99
|
+
child.kill('SIGKILL');
|
|
100
|
+
resolve({
|
|
101
|
+
success: false,
|
|
102
|
+
vulnerabilities: [],
|
|
103
|
+
summary: this.getEmptySummary(),
|
|
104
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
105
|
+
scannedFiles: lockFiles,
|
|
106
|
+
error: `OSV scan timed out after ${config.timeout || 120000}ms`,
|
|
107
|
+
errorCode: 'OSV002'
|
|
108
|
+
});
|
|
109
|
+
}, config.timeout || 120000);
|
|
110
|
+
if (child.stdout) {
|
|
111
|
+
child.stdout.on('data', (data) => {
|
|
112
|
+
output += data.toString();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (child.stderr) {
|
|
116
|
+
child.stderr.on('data', (data) => {
|
|
117
|
+
error += data.toString();
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
child.on('close', (code) => {
|
|
121
|
+
clearTimeout(timeout);
|
|
122
|
+
if (code === 0 || code === 1) { // 0 = no vulns, 1 = vulns found
|
|
123
|
+
const result = this.parseOsvResults(output, lockFiles, config);
|
|
124
|
+
resolve(result);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
resolve({
|
|
128
|
+
success: false,
|
|
129
|
+
vulnerabilities: [],
|
|
130
|
+
summary: this.getEmptySummary(),
|
|
131
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
132
|
+
scannedFiles: lockFiles,
|
|
133
|
+
error: this.redactor.redact(error || `OSV scanner exited with code ${code}`),
|
|
134
|
+
rawOutput: this.redactor.redact(output),
|
|
135
|
+
errorCode: 'OSV003'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
child.on('error', (err) => {
|
|
140
|
+
clearTimeout(timeout);
|
|
141
|
+
// Fallback to API-based scanning
|
|
142
|
+
this.fallbackApiScan(lockFiles, config).then(resolve);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Parse OSV scanner results
|
|
148
|
+
*/
|
|
149
|
+
parseOsvResults(output, lockFiles, config) {
|
|
150
|
+
try {
|
|
151
|
+
const data = JSON.parse(output);
|
|
152
|
+
const vulnerabilities = [];
|
|
153
|
+
// Parse vulnerabilities from OSV format
|
|
154
|
+
if (data.results) {
|
|
155
|
+
for (const result of data.results) {
|
|
156
|
+
if (result.packages) {
|
|
157
|
+
for (const pkg of result.packages) {
|
|
158
|
+
if (pkg.vulnerabilities) {
|
|
159
|
+
for (const vuln of pkg.vulnerabilities) {
|
|
160
|
+
vulnerabilities.push({
|
|
161
|
+
id: vuln.id || 'UNKNOWN',
|
|
162
|
+
summary: vuln.summary || 'No summary available',
|
|
163
|
+
severity: this.mapSeverity(vuln.database_specific?.severity || vuln.severity),
|
|
164
|
+
package: {
|
|
165
|
+
name: pkg.package?.name || 'unknown',
|
|
166
|
+
version: pkg.package?.version || 'unknown',
|
|
167
|
+
ecosystem: pkg.package?.ecosystem || 'unknown'
|
|
168
|
+
},
|
|
169
|
+
affected: vuln.affected || [],
|
|
170
|
+
references: vuln.references || []
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const summary = this.calculateSummary(vulnerabilities);
|
|
179
|
+
const budgetCheck = this.generateBudgetCheck(summary, config);
|
|
180
|
+
const success = budgetCheck.critical_passed && budgetCheck.high_passed;
|
|
181
|
+
const junit = this.generateJUnit(summary, success, lockFiles);
|
|
182
|
+
return {
|
|
183
|
+
success,
|
|
184
|
+
vulnerabilities,
|
|
185
|
+
summary,
|
|
186
|
+
budgetCheck,
|
|
187
|
+
scannedFiles: lockFiles,
|
|
188
|
+
rawOutput: this.redactor.redact(output),
|
|
189
|
+
junit
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
vulnerabilities: [],
|
|
196
|
+
summary: this.getEmptySummary(),
|
|
197
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
198
|
+
scannedFiles: lockFiles,
|
|
199
|
+
error: `Failed to parse OSV results: ${error}`,
|
|
200
|
+
rawOutput: this.redactor.redact(output),
|
|
201
|
+
errorCode: 'OSV004'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Fallback API-based scanning when osv-scanner not available
|
|
207
|
+
*/
|
|
208
|
+
async fallbackApiScan(lockFiles, config) {
|
|
209
|
+
console.log('⚠️ OSV scanner not found, using mock scan (install osv-scanner for real scanning)');
|
|
210
|
+
return {
|
|
211
|
+
success: true,
|
|
212
|
+
vulnerabilities: [],
|
|
213
|
+
summary: this.getEmptySummary(),
|
|
214
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
215
|
+
scannedFiles: lockFiles,
|
|
216
|
+
error: 'OSV scanner not available - install from https://github.com/google/osv-scanner',
|
|
217
|
+
junit: this.generateJUnit(this.getEmptySummary(), true, lockFiles),
|
|
218
|
+
errorCode: 'OSV005'
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Map severity from various formats to standard levels
|
|
223
|
+
*/
|
|
224
|
+
mapSeverity(severity) {
|
|
225
|
+
if (!severity)
|
|
226
|
+
return 'UNKNOWN';
|
|
227
|
+
const s = severity.toLowerCase();
|
|
228
|
+
if (s.includes('critical'))
|
|
229
|
+
return 'CRITICAL';
|
|
230
|
+
if (s.includes('high'))
|
|
231
|
+
return 'HIGH';
|
|
232
|
+
if (s.includes('medium') || s.includes('moderate'))
|
|
233
|
+
return 'MEDIUM';
|
|
234
|
+
if (s.includes('low') || s.includes('minor'))
|
|
235
|
+
return 'LOW';
|
|
236
|
+
return 'UNKNOWN';
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Calculate vulnerability summary
|
|
240
|
+
*/
|
|
241
|
+
calculateSummary(vulnerabilities) {
|
|
242
|
+
const summary = {
|
|
243
|
+
total: vulnerabilities.length,
|
|
244
|
+
critical: 0,
|
|
245
|
+
high: 0,
|
|
246
|
+
medium: 0,
|
|
247
|
+
low: 0,
|
|
248
|
+
unknown: 0
|
|
249
|
+
};
|
|
250
|
+
for (const vuln of vulnerabilities) {
|
|
251
|
+
switch (vuln.severity) {
|
|
252
|
+
case 'CRITICAL':
|
|
253
|
+
summary.critical++;
|
|
254
|
+
break;
|
|
255
|
+
case 'HIGH':
|
|
256
|
+
summary.high++;
|
|
257
|
+
break;
|
|
258
|
+
case 'MEDIUM':
|
|
259
|
+
summary.medium++;
|
|
260
|
+
break;
|
|
261
|
+
case 'LOW':
|
|
262
|
+
summary.low++;
|
|
263
|
+
break;
|
|
264
|
+
default:
|
|
265
|
+
summary.unknown++;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return summary;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Generate budget check based on security config
|
|
273
|
+
*/
|
|
274
|
+
generateBudgetCheck(summary, config) {
|
|
275
|
+
const deps = config.security?.deps;
|
|
276
|
+
return {
|
|
277
|
+
critical_passed: deps?.max_critical === undefined || summary.critical <= deps.max_critical,
|
|
278
|
+
high_passed: deps?.max_high === undefined || summary.high <= deps.max_high,
|
|
279
|
+
medium_passed: deps?.max_medium === undefined || summary.medium <= deps.max_medium
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get empty summary structure
|
|
284
|
+
*/
|
|
285
|
+
getEmptySummary() {
|
|
286
|
+
return {
|
|
287
|
+
total: 0,
|
|
288
|
+
critical: 0,
|
|
289
|
+
high: 0,
|
|
290
|
+
medium: 0,
|
|
291
|
+
low: 0,
|
|
292
|
+
unknown: 0
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get default budget check structure
|
|
297
|
+
*/
|
|
298
|
+
getDefaultBudgetCheck() {
|
|
299
|
+
return {
|
|
300
|
+
critical_passed: true,
|
|
301
|
+
high_passed: true,
|
|
302
|
+
medium_passed: true
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Generate JUnit XML report
|
|
307
|
+
*/
|
|
308
|
+
generateJUnit(summary, success, lockFiles) {
|
|
309
|
+
const timestamp = new Date().toISOString();
|
|
310
|
+
const duration = '0.1';
|
|
311
|
+
let junit = `<?xml version="1.0" encoding="UTF-8"?>
|
|
312
|
+
<testsuite name="OSV Dependency Scan" tests="1" failures="${success ? 0 : 1}" time="${duration}" timestamp="${timestamp}">
|
|
313
|
+
<testcase name="Dependency Vulnerabilities: ${lockFiles.join(', ')}" time="${duration}">
|
|
314
|
+
`;
|
|
315
|
+
if (!success) {
|
|
316
|
+
junit += ` <failure message="Dependency vulnerabilities found">
|
|
317
|
+
Critical: ${summary.critical}
|
|
318
|
+
High: ${summary.high}
|
|
319
|
+
Medium: ${summary.medium}
|
|
320
|
+
Low: ${summary.low}
|
|
321
|
+
Total: ${summary.total}
|
|
322
|
+
</failure>
|
|
323
|
+
`;
|
|
324
|
+
}
|
|
325
|
+
junit += ` </testcase>
|
|
326
|
+
</testsuite>`;
|
|
327
|
+
return junit;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Validate OSV scan configuration
|
|
331
|
+
*/
|
|
332
|
+
static validateConfig(config) {
|
|
333
|
+
const errors = [];
|
|
334
|
+
if (!config.workingDir) {
|
|
335
|
+
errors.push('Working directory is required');
|
|
336
|
+
}
|
|
337
|
+
if (!existsSync(config.workingDir)) {
|
|
338
|
+
errors.push(`Working directory does not exist: ${config.workingDir}`);
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
valid: errors.length === 0,
|
|
342
|
+
errors
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Check if OSV scanner is available
|
|
347
|
+
*/
|
|
348
|
+
static async isAvailable() {
|
|
349
|
+
return new Promise((resolve) => {
|
|
350
|
+
const child = spawn('osv-scanner', ['--version'], { stdio: 'pipe' });
|
|
351
|
+
child.on('close', (code) => {
|
|
352
|
+
resolve({
|
|
353
|
+
available: code === 0,
|
|
354
|
+
error: code !== 0 ? 'osv-scanner not found. Install from https://github.com/google/osv-scanner' : undefined
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
child.on('error', () => {
|
|
358
|
+
resolve({
|
|
359
|
+
available: false,
|
|
360
|
+
error: 'osv-scanner not found. Install from https://github.com/google/osv-scanner'
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
setTimeout(() => {
|
|
364
|
+
child.kill();
|
|
365
|
+
resolve({
|
|
366
|
+
available: false,
|
|
367
|
+
error: 'osv-scanner check timed out'
|
|
368
|
+
});
|
|
369
|
+
}, 5000);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Playwright API Adapter (Socle OOTB)
|
|
3
|
+
* Smoke tests for REST/GraphQL APIs with retry logic
|
|
4
|
+
*/
|
|
5
|
+
import { ApiTarget, PackBudgets } from '../types/pack-v1.js';
|
|
6
|
+
export interface ApiTestConfig {
|
|
7
|
+
target: ApiTarget;
|
|
8
|
+
budgets?: PackBudgets;
|
|
9
|
+
timeout?: number;
|
|
10
|
+
retries?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ApiTestResult {
|
|
13
|
+
endpoint: string;
|
|
14
|
+
method: string;
|
|
15
|
+
status: number;
|
|
16
|
+
responseTime: number;
|
|
17
|
+
success: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
body?: any;
|
|
21
|
+
}
|
|
22
|
+
export interface ApiSmokeResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
results: ApiTestResult[];
|
|
25
|
+
summary: {
|
|
26
|
+
total: number;
|
|
27
|
+
passed: number;
|
|
28
|
+
failed: number;
|
|
29
|
+
avgResponseTime: number;
|
|
30
|
+
};
|
|
31
|
+
junit?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare class PlaywrightApiAdapter {
|
|
34
|
+
private browser?;
|
|
35
|
+
private context?;
|
|
36
|
+
private redactor;
|
|
37
|
+
constructor();
|
|
38
|
+
/**
|
|
39
|
+
* Execute API smoke tests
|
|
40
|
+
*/
|
|
41
|
+
runSmokeTests(config: ApiTestConfig): Promise<ApiSmokeResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Execute single API test with retry logic
|
|
44
|
+
*/
|
|
45
|
+
private executeApiTest;
|
|
46
|
+
/**
|
|
47
|
+
* Parse test specification string
|
|
48
|
+
*/
|
|
49
|
+
private parseTestSpec;
|
|
50
|
+
/**
|
|
51
|
+
* Check if error is retryable
|
|
52
|
+
*/
|
|
53
|
+
private isRetryableError;
|
|
54
|
+
/**
|
|
55
|
+
* Calculate test summary
|
|
56
|
+
*/
|
|
57
|
+
private calculateSummary;
|
|
58
|
+
/**
|
|
59
|
+
* Generate JUnit XML fragment
|
|
60
|
+
*/
|
|
61
|
+
private generateJUnit;
|
|
62
|
+
/**
|
|
63
|
+
* Escape XML special characters
|
|
64
|
+
*/
|
|
65
|
+
private escapeXml;
|
|
66
|
+
/**
|
|
67
|
+
* Setup browser context
|
|
68
|
+
*/
|
|
69
|
+
private setupBrowser;
|
|
70
|
+
/**
|
|
71
|
+
* Cleanup browser resources
|
|
72
|
+
*/
|
|
73
|
+
private cleanup;
|
|
74
|
+
/**
|
|
75
|
+
* Validate API target configuration
|
|
76
|
+
*/
|
|
77
|
+
static validateConfig(target: ApiTarget): {
|
|
78
|
+
valid: boolean;
|
|
79
|
+
errors: string[];
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=playwright-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-api.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/playwright-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAG7D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAmB;;IAMnC;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAmCnE;;OAEG;YACW,cAAc;IA0E5B;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB;;OAEG;IACH,OAAO,CAAC,SAAS;IASjB;;OAEG;YACW,YAAY;IAc1B;;OAEG;YACW,OAAO;IASrB;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;CA0B/E"}
|