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.
Files changed (108) hide show
  1. package/dist/commands/history.js +1 -1
  2. package/dist/commands/pack.js +1 -1
  3. package/dist/commands/run.d.ts +1 -1
  4. package/dist/commands/run.d.ts.map +1 -1
  5. package/dist/commands/run.js +1 -1
  6. package/dist/commands/secrets.js +1 -1
  7. package/dist/commands/serve.js +1 -1
  8. package/dist/commands/verify.js +1 -1
  9. package/dist/core/adapters/gitleaks-secrets.d.ts +115 -0
  10. package/dist/core/adapters/gitleaks-secrets.d.ts.map +1 -0
  11. package/dist/core/adapters/gitleaks-secrets.js +410 -0
  12. package/dist/core/adapters/k6-perf.d.ts +86 -0
  13. package/dist/core/adapters/k6-perf.d.ts.map +1 -0
  14. package/dist/core/adapters/k6-perf.js +398 -0
  15. package/dist/core/adapters/osv-deps.d.ts +124 -0
  16. package/dist/core/adapters/osv-deps.d.ts.map +1 -0
  17. package/dist/core/adapters/osv-deps.js +372 -0
  18. package/dist/core/adapters/playwright-api.d.ts +82 -0
  19. package/dist/core/adapters/playwright-api.d.ts.map +1 -0
  20. package/dist/core/adapters/playwright-api.js +252 -0
  21. package/dist/core/adapters/playwright-ui.d.ts +115 -0
  22. package/dist/core/adapters/playwright-ui.d.ts.map +1 -0
  23. package/dist/core/adapters/playwright-ui.js +346 -0
  24. package/dist/core/adapters/semgrep-sast.d.ts +100 -0
  25. package/dist/core/adapters/semgrep-sast.d.ts.map +1 -0
  26. package/dist/core/adapters/semgrep-sast.js +322 -0
  27. package/dist/core/adapters/zap-dast.d.ts +134 -0
  28. package/dist/core/adapters/zap-dast.d.ts.map +1 -0
  29. package/dist/core/adapters/zap-dast.js +424 -0
  30. package/dist/core/hooks/compose.d.ts +62 -0
  31. package/dist/core/hooks/compose.d.ts.map +1 -0
  32. package/dist/core/hooks/compose.js +225 -0
  33. package/dist/core/hooks/runner.d.ts +69 -0
  34. package/dist/core/hooks/runner.d.ts.map +1 -0
  35. package/dist/core/hooks/runner.js +303 -0
  36. package/dist/core/index.d.ts +74 -0
  37. package/dist/core/index.d.ts.map +1 -0
  38. package/dist/core/index.js +39 -0
  39. package/dist/core/pack/migrator.d.ts +52 -0
  40. package/dist/core/pack/migrator.d.ts.map +1 -0
  41. package/dist/core/pack/migrator.js +304 -0
  42. package/dist/core/pack/validator.d.ts +43 -0
  43. package/dist/core/pack/validator.d.ts.map +1 -0
  44. package/dist/core/pack/validator.js +292 -0
  45. package/dist/core/proof/bundle.d.ts +138 -0
  46. package/dist/core/proof/bundle.d.ts.map +1 -0
  47. package/dist/core/proof/bundle.js +160 -0
  48. package/dist/core/proof/canonicalize.d.ts +48 -0
  49. package/dist/core/proof/canonicalize.d.ts.map +1 -0
  50. package/dist/core/proof/canonicalize.js +105 -0
  51. package/dist/core/proof/index.d.ts +14 -0
  52. package/dist/core/proof/index.d.ts.map +1 -0
  53. package/dist/core/proof/index.js +18 -0
  54. package/dist/core/proof/schema.d.ts +218 -0
  55. package/dist/core/proof/schema.d.ts.map +1 -0
  56. package/dist/core/proof/schema.js +263 -0
  57. package/dist/core/proof/signer.d.ts +112 -0
  58. package/dist/core/proof/signer.d.ts.map +1 -0
  59. package/dist/core/proof/signer.js +226 -0
  60. package/dist/core/proof/verifier.d.ts +98 -0
  61. package/dist/core/proof/verifier.d.ts.map +1 -0
  62. package/dist/core/proof/verifier.js +302 -0
  63. package/dist/core/runner/phase3-runner.d.ts +102 -0
  64. package/dist/core/runner/phase3-runner.d.ts.map +1 -0
  65. package/dist/core/runner/phase3-runner.js +471 -0
  66. package/dist/core/secrets/crypto.d.ts +76 -0
  67. package/dist/core/secrets/crypto.d.ts.map +1 -0
  68. package/dist/core/secrets/crypto.js +225 -0
  69. package/dist/core/secrets/manager.d.ts +77 -0
  70. package/dist/core/secrets/manager.d.ts.map +1 -0
  71. package/dist/core/secrets/manager.js +219 -0
  72. package/dist/core/security/redaction-patterns-extended.d.ts +28 -0
  73. package/dist/core/security/redaction-patterns-extended.d.ts.map +1 -0
  74. package/dist/core/security/redaction-patterns-extended.js +247 -0
  75. package/dist/core/security/redactor.d.ts +72 -0
  76. package/dist/core/security/redactor.d.ts.map +1 -0
  77. package/dist/core/security/redactor.js +279 -0
  78. package/dist/core/serve/diagnostics-collector.d.ts +33 -0
  79. package/dist/core/serve/diagnostics-collector.d.ts.map +1 -0
  80. package/dist/core/serve/diagnostics-collector.js +149 -0
  81. package/dist/core/serve/health-checker.d.ts +45 -0
  82. package/dist/core/serve/health-checker.d.ts.map +1 -0
  83. package/dist/core/serve/health-checker.js +219 -0
  84. package/dist/core/serve/index.d.ts +9 -0
  85. package/dist/core/serve/index.d.ts.map +1 -0
  86. package/dist/core/serve/index.js +8 -0
  87. package/dist/core/serve/metrics-collector.d.ts +25 -0
  88. package/dist/core/serve/metrics-collector.d.ts.map +1 -0
  89. package/dist/core/serve/metrics-collector.js +322 -0
  90. package/dist/core/serve/process-manager.d.ts +37 -0
  91. package/dist/core/serve/process-manager.d.ts.map +1 -0
  92. package/dist/core/serve/process-manager.js +213 -0
  93. package/dist/core/serve/server.d.ts +37 -0
  94. package/dist/core/serve/server.d.ts.map +1 -0
  95. package/dist/core/serve/server.js +191 -0
  96. package/dist/core/types/pack-v1.d.ts +162 -0
  97. package/dist/core/types/pack-v1.d.ts.map +1 -0
  98. package/dist/core/types/pack-v1.js +5 -0
  99. package/dist/core/types/trust-score.d.ts +70 -0
  100. package/dist/core/types/trust-score.d.ts.map +1 -0
  101. package/dist/core/types/trust-score.js +191 -0
  102. package/dist/core/vault/cas.d.ts +87 -0
  103. package/dist/core/vault/cas.d.ts.map +1 -0
  104. package/dist/core/vault/cas.js +255 -0
  105. package/dist/core/vault/index.d.ts +205 -0
  106. package/dist/core/vault/index.d.ts.map +1 -0
  107. package/dist/core/vault/index.js +631 -0
  108. package/package.json +12 -6
@@ -0,0 +1,252 @@
1
+ /**
2
+ * QA360 Playwright API Adapter (Socle OOTB)
3
+ * Smoke tests for REST/GraphQL APIs with retry logic
4
+ */
5
+ import { chromium } from '@playwright/test';
6
+ import { SecurityRedactor } from '../security/redactor.js';
7
+ export class PlaywrightApiAdapter {
8
+ browser;
9
+ context;
10
+ redactor;
11
+ constructor() {
12
+ this.redactor = SecurityRedactor.forLogs();
13
+ }
14
+ /**
15
+ * Execute API smoke tests
16
+ */
17
+ async runSmokeTests(config) {
18
+ try {
19
+ await this.setupBrowser();
20
+ const results = [];
21
+ const smokeTests = config.target.smoke || [`GET ${config.target.baseUrl}/health -> 200`];
22
+ console.log(`🌐 Running API smoke tests (${smokeTests.length} endpoints)`);
23
+ for (const test of smokeTests) {
24
+ const testResult = await this.executeApiTest(test, config);
25
+ results.push(testResult);
26
+ if (testResult.success) {
27
+ console.log(` ✅ ${testResult.method} ${testResult.endpoint} -> ${testResult.status} (${testResult.responseTime}ms)`);
28
+ }
29
+ else {
30
+ console.log(` ❌ ${testResult.method} ${testResult.endpoint} -> ${testResult.error}`);
31
+ }
32
+ }
33
+ const summary = this.calculateSummary(results);
34
+ const junit = this.generateJUnit(results);
35
+ return {
36
+ success: summary.failed === 0,
37
+ results,
38
+ summary,
39
+ junit
40
+ };
41
+ }
42
+ finally {
43
+ await this.cleanup();
44
+ }
45
+ }
46
+ /**
47
+ * Execute single API test with retry logic
48
+ */
49
+ async executeApiTest(testSpec, config) {
50
+ const { method, endpoint, expectedStatus } = this.parseTestSpec(testSpec, config.target.baseUrl);
51
+ const maxRetries = config.retries || 1;
52
+ let lastError = '';
53
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
54
+ try {
55
+ const startTime = Date.now();
56
+ const response = await this.context.request.fetch(endpoint, {
57
+ method: method,
58
+ timeout: config.timeout || 10000,
59
+ headers: {
60
+ 'User-Agent': 'QA360-API-Smoke/1.0',
61
+ 'Accept': 'application/json, text/plain, */*'
62
+ }
63
+ });
64
+ const responseTime = Date.now() - startTime;
65
+ const status = response.status();
66
+ const success = status === expectedStatus;
67
+ // Get response body safely
68
+ let body;
69
+ try {
70
+ const contentType = response.headers()['content-type'] || '';
71
+ if (contentType.includes('application/json')) {
72
+ body = await response.json();
73
+ }
74
+ else {
75
+ const text = await response.text();
76
+ body = text.substring(0, 200); // Limit body size
77
+ }
78
+ }
79
+ catch {
80
+ body = '[Response body not readable]';
81
+ }
82
+ return {
83
+ endpoint,
84
+ method,
85
+ status,
86
+ responseTime,
87
+ success,
88
+ error: success ? undefined : `Expected status ${expectedStatus}, got ${status}`,
89
+ headers: response.headers(),
90
+ body: this.redactor.redactObject(body)
91
+ };
92
+ }
93
+ catch (error) {
94
+ lastError = error instanceof Error ? error.message : 'Unknown error';
95
+ // Check if this is a retryable error
96
+ const isRetryable = this.isRetryableError(lastError);
97
+ if (isRetryable && attempt < maxRetries) {
98
+ console.log(` 🔄 Retry ${attempt + 1}/${maxRetries} for ${method} ${endpoint} (${lastError})`);
99
+ await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1))); // Exponential backoff
100
+ continue;
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ return {
106
+ endpoint,
107
+ method,
108
+ status: 0,
109
+ responseTime: 0,
110
+ success: false,
111
+ error: this.redactor.redact(lastError)
112
+ };
113
+ }
114
+ /**
115
+ * Parse test specification string
116
+ */
117
+ parseTestSpec(spec, baseUrl) {
118
+ // Format: "GET /path -> 200" or "POST /api/users -> 201"
119
+ const match = spec.match(/^(\w+)\s+(.+?)\s*->\s*(\d+)$/);
120
+ if (!match) {
121
+ throw new Error(`Invalid test spec format: ${spec}. Expected: "METHOD /path -> STATUS"`);
122
+ }
123
+ const [, method, path, statusStr] = match;
124
+ const expectedStatus = parseInt(statusStr, 10);
125
+ // Build full endpoint URL
126
+ let endpoint = path;
127
+ if (!path.startsWith('http')) {
128
+ endpoint = baseUrl.replace(/\/$/, '') + (path.startsWith('/') ? path : `/${path}`);
129
+ }
130
+ return {
131
+ method: method.toUpperCase(),
132
+ endpoint,
133
+ expectedStatus
134
+ };
135
+ }
136
+ /**
137
+ * Check if error is retryable
138
+ */
139
+ isRetryableError(error) {
140
+ const retryablePatterns = [
141
+ /ECONNRESET/,
142
+ /ETIMEDOUT/,
143
+ /ECONNREFUSED/,
144
+ /502 Bad Gateway/,
145
+ /503 Service Unavailable/,
146
+ /504 Gateway Timeout/,
147
+ /timeout/i,
148
+ /network/i
149
+ ];
150
+ return retryablePatterns.some(pattern => pattern.test(error));
151
+ }
152
+ /**
153
+ * Calculate test summary
154
+ */
155
+ calculateSummary(results) {
156
+ const total = results.length;
157
+ const passed = results.filter(r => r.success).length;
158
+ const failed = total - passed;
159
+ const avgResponseTime = total > 0 ?
160
+ Math.round(results.reduce((sum, r) => sum + r.responseTime, 0) / total) : 0;
161
+ return { total, passed, failed, avgResponseTime };
162
+ }
163
+ /**
164
+ * Generate JUnit XML fragment
165
+ */
166
+ generateJUnit(results) {
167
+ const summary = this.calculateSummary(results);
168
+ const timestamp = new Date().toISOString();
169
+ let junit = `<?xml version="1.0" encoding="UTF-8"?>
170
+ <testsuite name="API Smoke Tests" tests="${summary.total}" failures="${summary.failed}" time="${summary.avgResponseTime / 1000}" timestamp="${timestamp}">
171
+ `;
172
+ for (const result of results) {
173
+ const testName = `${result.method} ${result.endpoint}`;
174
+ const time = result.responseTime / 1000;
175
+ junit += ` <testcase name="${this.escapeXml(testName)}" time="${time}">
176
+ `;
177
+ if (!result.success) {
178
+ junit += ` <failure message="${this.escapeXml(result.error || 'Test failed')}">${this.escapeXml(JSON.stringify(result, null, 2))}</failure>
179
+ `;
180
+ }
181
+ junit += ` </testcase>
182
+ `;
183
+ }
184
+ junit += `</testsuite>`;
185
+ return junit;
186
+ }
187
+ /**
188
+ * Escape XML special characters
189
+ */
190
+ escapeXml(str) {
191
+ return str
192
+ .replace(/&/g, '&amp;')
193
+ .replace(/</g, '&lt;')
194
+ .replace(/>/g, '&gt;')
195
+ .replace(/"/g, '&quot;')
196
+ .replace(/'/g, '&apos;');
197
+ }
198
+ /**
199
+ * Setup browser context
200
+ */
201
+ async setupBrowser() {
202
+ this.browser = await chromium.launch({
203
+ headless: true,
204
+ args: ['--no-sandbox', '--disable-dev-shm-usage']
205
+ });
206
+ this.context = await this.browser.newContext({
207
+ userAgent: 'QA360-API-Smoke/1.0',
208
+ extraHTTPHeaders: {
209
+ 'Accept': 'application/json, text/plain, */*'
210
+ }
211
+ });
212
+ }
213
+ /**
214
+ * Cleanup browser resources
215
+ */
216
+ async cleanup() {
217
+ if (this.context) {
218
+ await this.context.close();
219
+ }
220
+ if (this.browser) {
221
+ await this.browser.close();
222
+ }
223
+ }
224
+ /**
225
+ * Validate API target configuration
226
+ */
227
+ static validateConfig(target) {
228
+ const errors = [];
229
+ if (!target.baseUrl) {
230
+ errors.push('API target requires baseUrl');
231
+ }
232
+ else {
233
+ try {
234
+ new URL(target.baseUrl);
235
+ }
236
+ catch {
237
+ errors.push('API target baseUrl must be a valid URL');
238
+ }
239
+ }
240
+ if (target.smoke) {
241
+ for (const test of target.smoke) {
242
+ if (!/^\w+\s+.+\s*->\s*\d+$/.test(test)) {
243
+ errors.push(`Invalid smoke test format: ${test}. Expected: "METHOD /path -> STATUS"`);
244
+ }
245
+ }
246
+ }
247
+ return {
248
+ valid: errors.length === 0,
249
+ errors
250
+ };
251
+ }
252
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * QA360 Playwright UI Adapter (Socle OOTB)
3
+ * UI smoke tests + accessibility via axe-core
4
+ */
5
+ import { WebTarget, PackBudgets } from '../types/pack-v1.js';
6
+ export interface UiTestConfig {
7
+ target: WebTarget;
8
+ budgets?: PackBudgets;
9
+ timeout?: number;
10
+ login?: {
11
+ url?: string;
12
+ username?: string;
13
+ password?: string;
14
+ usernameSelector?: string;
15
+ passwordSelector?: string;
16
+ submitSelector?: string;
17
+ };
18
+ }
19
+ export interface UiTestResult {
20
+ page: string;
21
+ success: boolean;
22
+ loadTime: number;
23
+ error?: string;
24
+ screenshot?: string;
25
+ accessibility?: {
26
+ score: number;
27
+ violations: Array<{
28
+ id: string;
29
+ impact: 'minor' | 'moderate' | 'serious' | 'critical';
30
+ description: string;
31
+ nodes: number;
32
+ }>;
33
+ };
34
+ domSnapshot?: {
35
+ title: string;
36
+ url: string;
37
+ elements: {
38
+ buttons: number;
39
+ links: number;
40
+ forms: number;
41
+ inputs: number;
42
+ };
43
+ };
44
+ }
45
+ export interface UiSmokeResult {
46
+ success: boolean;
47
+ results: UiTestResult[];
48
+ summary: {
49
+ total: number;
50
+ passed: number;
51
+ failed: number;
52
+ avgLoadTime: number;
53
+ avgA11yScore: number;
54
+ };
55
+ junit?: string;
56
+ }
57
+ export declare class PlaywrightUiAdapter {
58
+ private browser?;
59
+ private context?;
60
+ private page?;
61
+ private redactor;
62
+ constructor();
63
+ /**
64
+ * Execute UI smoke tests with accessibility
65
+ */
66
+ runSmokeTests(config: UiTestConfig): Promise<UiSmokeResult>;
67
+ /**
68
+ * Test single page with accessibility
69
+ */
70
+ private testPage;
71
+ /**
72
+ * Perform login if configured
73
+ */
74
+ private performLogin;
75
+ /**
76
+ * Run accessibility tests using axe-core
77
+ */
78
+ private runAccessibilityTests;
79
+ /**
80
+ * Get DOM snapshot for debugging
81
+ */
82
+ private getDomSnapshot;
83
+ /**
84
+ * Take screenshot for debugging
85
+ */
86
+ private takeScreenshot;
87
+ /**
88
+ * Calculate test summary
89
+ */
90
+ private calculateSummary;
91
+ /**
92
+ * Generate JUnit XML fragment
93
+ */
94
+ private generateJUnit;
95
+ /**
96
+ * Escape XML special characters
97
+ */
98
+ private escapeXml;
99
+ /**
100
+ * Setup browser context
101
+ */
102
+ private setupBrowser;
103
+ /**
104
+ * Cleanup browser resources
105
+ */
106
+ private cleanup;
107
+ /**
108
+ * Validate UI target configuration
109
+ */
110
+ static validateConfig(target: WebTarget): {
111
+ valid: boolean;
112
+ errors: string[];
113
+ };
114
+ }
115
+ //# sourceMappingURL=playwright-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-ui.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/playwright-ui.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAa7D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,KAAK,CAAC;YAChB,EAAE,EAAE,MAAM,CAAC;YACX,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;YACtD,WAAW,EAAE,MAAM,CAAC;YACpB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ,CAAC;IACF,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE;YACR,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,IAAI,CAAC,CAAO;IACpB,OAAO,CAAC,QAAQ,CAAmB;;IAMnC;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IA0CjE;;OAEG;YACW,QAAQ;IAuDtB;;OAEG;YACW,YAAY;IA8B1B;;OAEG;YACW,qBAAqB;IAkEnC;;OAEG;YACW,cAAc;IAyB5B;;OAEG;YACW,cAAc;IAc5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB;;OAEG;IACH,OAAO,CAAC,SAAS;IASjB;;OAEG;YACW,YAAY;IAc1B;;OAEG;YACW,OAAO;IAYrB;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;CA4B/E"}