@avi770/testteam 1.2.0 → 2.0.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/CHANGELOG.md +29 -1
- package/README.md +53 -6
- package/agents/24-signup-onboarding-tester.ts +429 -0
- package/agents/25-crud-flow-tester.ts +302 -0
- package/agents/26-form-validator.ts +297 -0
- package/agents/27-search-filter-tester.ts +326 -0
- package/agents/28-navigation-routing-tester.ts +425 -0
- package/agents/29-responsive-interaction-tester.ts +350 -0
- package/agents/30-multi-user-scenario-tester.ts +319 -0
- package/agents/31-load-tester.ts +134 -0
- package/agents/32-memory-leak-detector.ts +194 -0
- package/agents/33-bundle-analyzer.ts +132 -0
- package/agents/34-xss-scanner.ts +191 -0
- package/agents/35-csrf-tester.ts +82 -0
- package/agents/36-auth-fuzzer.ts +194 -0
- package/agents/37-dependency-scanner.ts +176 -0
- package/agents/38-secrets-scanner.ts +137 -0
- package/agents/39-api-contract-tester.ts +199 -0
- package/agents/40-rate-limit-tester.ts +94 -0
- package/agents/41-api-pagination-tester.ts +97 -0
- package/agents/42-graphql-tester.ts +222 -0
- package/agents/43-data-consistency-checker.ts +205 -0
- package/agents/44-backup-recovery-tester.ts +152 -0
- package/agents/45-data-privacy-scanner.ts +125 -0
- package/agents/46-seo-auditor.ts +294 -0
- package/agents/47-social-preview-tester.ts +232 -0
- package/agents/48-lighthouse-auditor.ts +213 -0
- package/agents/49-i18n-tester.ts +198 -0
- package/agents/50-timezone-tester.ts +173 -0
- package/agents/51-error-recovery-tester.ts +155 -0
- package/agents/52-offline-mode-tester.ts +180 -0
- package/agents/53-graceful-degradation-tester.ts +156 -0
- package/agents/54-websocket-tester.ts +151 -0
- package/agents/55-realtime-sync-tester.ts +194 -0
- package/agents/56-file-upload-tester.ts +194 -0
- package/agents/57-export-tester.ts +174 -0
- package/agents/58-payment-flow-tester.ts +183 -0
- package/agents/59-ssl-tls-auditor.ts +141 -0
- package/agents/60-dns-cdn-tester.ts +117 -0
- package/agents/61-docker-health-checker.ts +111 -0
- package/agents/62-env-config-validator.ts +152 -0
- package/agents/63-log-quality-auditor.ts +136 -0
- package/agents/64-analytics-tracker-tester.ts +165 -0
- package/agents/65-gdpr-compliance-tester.ts +215 -0
- package/agents/66-soc2-control-validator.ts +210 -0
- package/agents/67-wcag-aaa-tester.ts +241 -0
- package/agents/68-dead-code-detector.ts +135 -0
- package/agents/69-type-safety-auditor.ts +164 -0
- package/agents/70-complexity-analyzer.ts +179 -0
- package/agents/__tests__/24-signup-onboarding-tester.test.ts +274 -0
- package/agents/__tests__/25-crud-flow-tester.test.ts +322 -0
- package/agents/__tests__/26-form-validator.test.ts +345 -0
- package/agents/__tests__/27-search-filter-tester.test.ts +311 -0
- package/agents/__tests__/28-navigation-routing-tester.test.ts +328 -0
- package/agents/__tests__/29-responsive-interaction-tester.test.ts +297 -0
- package/agents/__tests__/30-multi-user-scenario-tester.test.ts +328 -0
- package/agents/__tests__/31-load-tester.test.ts +189 -0
- package/agents/__tests__/32-memory-leak-detector.test.ts +251 -0
- package/agents/__tests__/33-bundle-analyzer.test.ts +237 -0
- package/agents/__tests__/34-xss-scanner.test.ts +258 -0
- package/agents/__tests__/35-csrf-tester.test.ts +200 -0
- package/agents/__tests__/36-auth-fuzzer.test.ts +214 -0
- package/agents/__tests__/37-dependency-scanner.test.ts +266 -0
- package/agents/__tests__/38-secrets-scanner.test.ts +224 -0
- package/agents/__tests__/39-api-contract-tester.test.ts +312 -0
- package/agents/__tests__/40-rate-limit-tester.test.ts +192 -0
- package/agents/__tests__/41-api-pagination-tester.test.ts +198 -0
- package/agents/__tests__/42-graphql-tester.test.ts +252 -0
- package/agents/__tests__/43-data-consistency-checker.test.ts +232 -0
- package/agents/__tests__/44-backup-recovery-tester.test.ts +222 -0
- package/agents/__tests__/45-data-privacy-scanner.test.ts +223 -0
- package/agents/__tests__/46-seo-auditor.test.ts +261 -0
- package/agents/__tests__/47-social-preview-tester.test.ts +245 -0
- package/agents/__tests__/48-lighthouse-auditor.test.ts +276 -0
- package/agents/__tests__/49-i18n-tester.test.ts +201 -0
- package/agents/__tests__/50-timezone-tester.test.ts +172 -0
- package/agents/__tests__/51-error-recovery-tester.test.ts +162 -0
- package/agents/__tests__/52-offline-mode-tester.test.ts +164 -0
- package/agents/__tests__/53-graceful-degradation-tester.test.ts +168 -0
- package/agents/__tests__/54-websocket-tester.test.ts +157 -0
- package/agents/__tests__/55-realtime-sync-tester.test.ts +181 -0
- package/agents/__tests__/56-file-upload-tester.test.ts +172 -0
- package/agents/__tests__/57-export-tester.test.ts +169 -0
- package/agents/__tests__/58-payment-flow-tester.test.ts +182 -0
- package/agents/__tests__/59-ssl-tls-auditor.test.ts +179 -0
- package/agents/__tests__/60-dns-cdn-tester.test.ts +176 -0
- package/agents/__tests__/61-docker-health-checker.test.ts +150 -0
- package/agents/__tests__/62-env-config-validator.test.ts +166 -0
- package/agents/__tests__/63-log-quality-auditor.test.ts +175 -0
- package/agents/__tests__/64-analytics-tracker-tester.test.ts +158 -0
- package/agents/__tests__/65-gdpr-compliance-tester.test.ts +174 -0
- package/agents/__tests__/66-soc2-control-validator.test.ts +183 -0
- package/agents/__tests__/67-wcag-aaa-tester.test.ts +190 -0
- package/agents/__tests__/68-dead-code-detector.test.ts +174 -0
- package/agents/__tests__/69-type-safety-auditor.test.ts +173 -0
- package/agents/__tests__/70-complexity-analyzer.test.ts +177 -0
- package/agents/__tests__/registry.test.ts +13 -13
- package/agents/registry.ts +146 -5
- package/core/__tests__/integration.test.ts +4 -4
- package/core/__tests__/orchestrator.test.ts +17 -16
- package/core/license.ts +208 -211
- package/core/orchestrator.ts +6 -4
- package/dist/agents/24-signup-onboarding-tester.d.ts +35 -0
- package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -0
- package/dist/agents/24-signup-onboarding-tester.js +357 -0
- package/dist/agents/24-signup-onboarding-tester.js.map +1 -0
- package/dist/agents/25-crud-flow-tester.d.ts +11 -0
- package/dist/agents/25-crud-flow-tester.d.ts.map +1 -0
- package/dist/agents/25-crud-flow-tester.js +253 -0
- package/dist/agents/25-crud-flow-tester.js.map +1 -0
- package/dist/agents/26-form-validator.d.ts +12 -0
- package/dist/agents/26-form-validator.d.ts.map +1 -0
- package/dist/agents/26-form-validator.js +257 -0
- package/dist/agents/26-form-validator.js.map +1 -0
- package/dist/agents/27-search-filter-tester.d.ts +20 -0
- package/dist/agents/27-search-filter-tester.d.ts.map +1 -0
- package/dist/agents/27-search-filter-tester.js +267 -0
- package/dist/agents/27-search-filter-tester.js.map +1 -0
- package/dist/agents/28-navigation-routing-tester.d.ts +32 -0
- package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -0
- package/dist/agents/28-navigation-routing-tester.js +363 -0
- package/dist/agents/28-navigation-routing-tester.js.map +1 -0
- package/dist/agents/29-responsive-interaction-tester.d.ts +26 -0
- package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -0
- package/dist/agents/29-responsive-interaction-tester.js +272 -0
- package/dist/agents/29-responsive-interaction-tester.js.map +1 -0
- package/dist/agents/30-multi-user-scenario-tester.d.ts +24 -0
- package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -0
- package/dist/agents/30-multi-user-scenario-tester.js +254 -0
- package/dist/agents/30-multi-user-scenario-tester.js.map +1 -0
- package/dist/agents/31-load-tester.d.ts +12 -0
- package/dist/agents/31-load-tester.d.ts.map +1 -0
- package/dist/agents/31-load-tester.js +110 -0
- package/dist/agents/31-load-tester.js.map +1 -0
- package/dist/agents/32-memory-leak-detector.d.ts +12 -0
- package/dist/agents/32-memory-leak-detector.d.ts.map +1 -0
- package/dist/agents/32-memory-leak-detector.js +167 -0
- package/dist/agents/32-memory-leak-detector.js.map +1 -0
- package/dist/agents/33-bundle-analyzer.d.ts +10 -0
- package/dist/agents/33-bundle-analyzer.d.ts.map +1 -0
- package/dist/agents/33-bundle-analyzer.js +111 -0
- package/dist/agents/33-bundle-analyzer.js.map +1 -0
- package/dist/agents/34-xss-scanner.d.ts +11 -0
- package/dist/agents/34-xss-scanner.d.ts.map +1 -0
- package/dist/agents/34-xss-scanner.js +164 -0
- package/dist/agents/34-xss-scanner.js.map +1 -0
- package/dist/agents/35-csrf-tester.d.ts +11 -0
- package/dist/agents/35-csrf-tester.d.ts.map +1 -0
- package/dist/agents/35-csrf-tester.js +70 -0
- package/dist/agents/35-csrf-tester.js.map +1 -0
- package/dist/agents/36-auth-fuzzer.d.ts +13 -0
- package/dist/agents/36-auth-fuzzer.d.ts.map +1 -0
- package/dist/agents/36-auth-fuzzer.js +163 -0
- package/dist/agents/36-auth-fuzzer.js.map +1 -0
- package/dist/agents/37-dependency-scanner.d.ts +11 -0
- package/dist/agents/37-dependency-scanner.d.ts.map +1 -0
- package/dist/agents/37-dependency-scanner.js +139 -0
- package/dist/agents/37-dependency-scanner.js.map +1 -0
- package/dist/agents/38-secrets-scanner.d.ts +11 -0
- package/dist/agents/38-secrets-scanner.d.ts.map +1 -0
- package/dist/agents/38-secrets-scanner.js +116 -0
- package/dist/agents/38-secrets-scanner.js.map +1 -0
- package/dist/agents/39-api-contract-tester.d.ts +12 -0
- package/dist/agents/39-api-contract-tester.d.ts.map +1 -0
- package/dist/agents/39-api-contract-tester.js +142 -0
- package/dist/agents/39-api-contract-tester.js.map +1 -0
- package/dist/agents/40-rate-limit-tester.d.ts +12 -0
- package/dist/agents/40-rate-limit-tester.d.ts.map +1 -0
- package/dist/agents/40-rate-limit-tester.js +79 -0
- package/dist/agents/40-rate-limit-tester.js.map +1 -0
- package/dist/agents/41-api-pagination-tester.d.ts +12 -0
- package/dist/agents/41-api-pagination-tester.d.ts.map +1 -0
- package/dist/agents/41-api-pagination-tester.js +79 -0
- package/dist/agents/41-api-pagination-tester.js.map +1 -0
- package/dist/agents/42-graphql-tester.d.ts +13 -0
- package/dist/agents/42-graphql-tester.d.ts.map +1 -0
- package/dist/agents/42-graphql-tester.js +187 -0
- package/dist/agents/42-graphql-tester.js.map +1 -0
- package/dist/agents/43-data-consistency-checker.d.ts +11 -0
- package/dist/agents/43-data-consistency-checker.d.ts.map +1 -0
- package/dist/agents/43-data-consistency-checker.js +176 -0
- package/dist/agents/43-data-consistency-checker.js.map +1 -0
- package/dist/agents/44-backup-recovery-tester.d.ts +11 -0
- package/dist/agents/44-backup-recovery-tester.d.ts.map +1 -0
- package/dist/agents/44-backup-recovery-tester.js +128 -0
- package/dist/agents/44-backup-recovery-tester.js.map +1 -0
- package/dist/agents/45-data-privacy-scanner.d.ts +11 -0
- package/dist/agents/45-data-privacy-scanner.d.ts.map +1 -0
- package/dist/agents/45-data-privacy-scanner.js +100 -0
- package/dist/agents/45-data-privacy-scanner.js.map +1 -0
- package/dist/agents/46-seo-auditor.d.ts +12 -0
- package/dist/agents/46-seo-auditor.d.ts.map +1 -0
- package/dist/agents/46-seo-auditor.js +275 -0
- package/dist/agents/46-seo-auditor.js.map +1 -0
- package/dist/agents/47-social-preview-tester.d.ts +11 -0
- package/dist/agents/47-social-preview-tester.d.ts.map +1 -0
- package/dist/agents/47-social-preview-tester.js +219 -0
- package/dist/agents/47-social-preview-tester.js.map +1 -0
- package/dist/agents/48-lighthouse-auditor.d.ts +11 -0
- package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -0
- package/dist/agents/48-lighthouse-auditor.js +192 -0
- package/dist/agents/48-lighthouse-auditor.js.map +1 -0
- package/dist/agents/49-i18n-tester.d.ts +13 -0
- package/dist/agents/49-i18n-tester.d.ts.map +1 -0
- package/dist/agents/49-i18n-tester.js +172 -0
- package/dist/agents/49-i18n-tester.js.map +1 -0
- package/dist/agents/50-timezone-tester.d.ts +11 -0
- package/dist/agents/50-timezone-tester.d.ts.map +1 -0
- package/dist/agents/50-timezone-tester.js +152 -0
- package/dist/agents/50-timezone-tester.js.map +1 -0
- package/dist/agents/51-error-recovery-tester.d.ts +11 -0
- package/dist/agents/51-error-recovery-tester.d.ts.map +1 -0
- package/dist/agents/51-error-recovery-tester.js +134 -0
- package/dist/agents/51-error-recovery-tester.js.map +1 -0
- package/dist/agents/52-offline-mode-tester.d.ts +12 -0
- package/dist/agents/52-offline-mode-tester.d.ts.map +1 -0
- package/dist/agents/52-offline-mode-tester.js +161 -0
- package/dist/agents/52-offline-mode-tester.js.map +1 -0
- package/dist/agents/53-graceful-degradation-tester.d.ts +12 -0
- package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -0
- package/dist/agents/53-graceful-degradation-tester.js +130 -0
- package/dist/agents/53-graceful-degradation-tester.js.map +1 -0
- package/dist/agents/54-websocket-tester.d.ts +10 -0
- package/dist/agents/54-websocket-tester.d.ts.map +1 -0
- package/dist/agents/54-websocket-tester.js +132 -0
- package/dist/agents/54-websocket-tester.js.map +1 -0
- package/dist/agents/55-realtime-sync-tester.d.ts +11 -0
- package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -0
- package/dist/agents/55-realtime-sync-tester.js +175 -0
- package/dist/agents/55-realtime-sync-tester.js.map +1 -0
- package/dist/agents/56-file-upload-tester.d.ts +12 -0
- package/dist/agents/56-file-upload-tester.d.ts.map +1 -0
- package/dist/agents/56-file-upload-tester.js +166 -0
- package/dist/agents/56-file-upload-tester.js.map +1 -0
- package/dist/agents/57-export-tester.d.ts +11 -0
- package/dist/agents/57-export-tester.d.ts.map +1 -0
- package/dist/agents/57-export-tester.js +155 -0
- package/dist/agents/57-export-tester.js.map +1 -0
- package/dist/agents/58-payment-flow-tester.d.ts +11 -0
- package/dist/agents/58-payment-flow-tester.d.ts.map +1 -0
- package/dist/agents/58-payment-flow-tester.js +159 -0
- package/dist/agents/58-payment-flow-tester.js.map +1 -0
- package/dist/agents/59-ssl-tls-auditor.d.ts +10 -0
- package/dist/agents/59-ssl-tls-auditor.d.ts.map +1 -0
- package/dist/agents/59-ssl-tls-auditor.js +132 -0
- package/dist/agents/59-ssl-tls-auditor.js.map +1 -0
- package/dist/agents/60-dns-cdn-tester.d.ts +10 -0
- package/dist/agents/60-dns-cdn-tester.d.ts.map +1 -0
- package/dist/agents/60-dns-cdn-tester.js +105 -0
- package/dist/agents/60-dns-cdn-tester.js.map +1 -0
- package/dist/agents/61-docker-health-checker.d.ts +10 -0
- package/dist/agents/61-docker-health-checker.d.ts.map +1 -0
- package/dist/agents/61-docker-health-checker.js +95 -0
- package/dist/agents/61-docker-health-checker.js.map +1 -0
- package/dist/agents/62-env-config-validator.d.ts +10 -0
- package/dist/agents/62-env-config-validator.d.ts.map +1 -0
- package/dist/agents/62-env-config-validator.js +132 -0
- package/dist/agents/62-env-config-validator.js.map +1 -0
- package/dist/agents/63-log-quality-auditor.d.ts +9 -0
- package/dist/agents/63-log-quality-auditor.d.ts.map +1 -0
- package/dist/agents/63-log-quality-auditor.js +121 -0
- package/dist/agents/63-log-quality-auditor.js.map +1 -0
- package/dist/agents/64-analytics-tracker-tester.d.ts +10 -0
- package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -0
- package/dist/agents/64-analytics-tracker-tester.js +146 -0
- package/dist/agents/64-analytics-tracker-tester.js.map +1 -0
- package/dist/agents/65-gdpr-compliance-tester.d.ts +13 -0
- package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -0
- package/dist/agents/65-gdpr-compliance-tester.js +186 -0
- package/dist/agents/65-gdpr-compliance-tester.js.map +1 -0
- package/dist/agents/66-soc2-control-validator.d.ts +14 -0
- package/dist/agents/66-soc2-control-validator.d.ts.map +1 -0
- package/dist/agents/66-soc2-control-validator.js +178 -0
- package/dist/agents/66-soc2-control-validator.js.map +1 -0
- package/dist/agents/67-wcag-aaa-tester.d.ts +13 -0
- package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -0
- package/dist/agents/67-wcag-aaa-tester.js +207 -0
- package/dist/agents/67-wcag-aaa-tester.js.map +1 -0
- package/dist/agents/68-dead-code-detector.d.ts +9 -0
- package/dist/agents/68-dead-code-detector.d.ts.map +1 -0
- package/dist/agents/68-dead-code-detector.js +116 -0
- package/dist/agents/68-dead-code-detector.js.map +1 -0
- package/dist/agents/69-type-safety-auditor.d.ts +9 -0
- package/dist/agents/69-type-safety-auditor.d.ts.map +1 -0
- package/dist/agents/69-type-safety-auditor.js +148 -0
- package/dist/agents/69-type-safety-auditor.js.map +1 -0
- package/dist/agents/70-complexity-analyzer.d.ts +10 -0
- package/dist/agents/70-complexity-analyzer.d.ts.map +1 -0
- package/dist/agents/70-complexity-analyzer.js +154 -0
- package/dist/agents/70-complexity-analyzer.js.map +1 -0
- package/dist/agents/registry.d.ts +3 -3
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +146 -5
- package/dist/agents/registry.js.map +1 -1
- package/dist/core/license.js +2 -5
- package/dist/core/license.js.map +1 -1
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +6 -4
- package/dist/core/orchestrator.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { Finding } from '../core/types';
|
|
2
|
+
import { BaseAgent } from './base-agent';
|
|
3
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
4
|
+
import { ApiClient } from '../helpers/api-client';
|
|
5
|
+
|
|
6
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
7
|
+
|
|
8
|
+
export class DataConsistencyCheckerAgent extends BaseAgent {
|
|
9
|
+
readonly agentId = 43;
|
|
10
|
+
readonly agentName = 'Data Consistency Checker';
|
|
11
|
+
private baseUrl = '';
|
|
12
|
+
|
|
13
|
+
protected async preFlight(): Promise<void> {
|
|
14
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
15
|
+
this.baseUrl = env.baseUrl;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected async execute(): Promise<Finding[]> {
|
|
19
|
+
const findings: Finding[] = [];
|
|
20
|
+
const credentials =
|
|
21
|
+
this.config.auth.credentials?.['admin'] ??
|
|
22
|
+
Object.values(this.config.auth.credentials ?? {})[0];
|
|
23
|
+
|
|
24
|
+
let client: ApiClient;
|
|
25
|
+
|
|
26
|
+
if (credentials) {
|
|
27
|
+
try {
|
|
28
|
+
client = await Promise.race([
|
|
29
|
+
ApiClient.createAuthenticated(this.baseUrl, credentials),
|
|
30
|
+
new Promise<never>((_, reject) =>
|
|
31
|
+
setTimeout(() => reject(new Error('Auth timeout')), REQUEST_TIMEOUT_MS),
|
|
32
|
+
),
|
|
33
|
+
]);
|
|
34
|
+
} catch (authError) {
|
|
35
|
+
findings.push({
|
|
36
|
+
id: `${this.agentId}-auth-failed`,
|
|
37
|
+
type: 'infra-issue',
|
|
38
|
+
severity: 'medium',
|
|
39
|
+
agentId: this.agentId,
|
|
40
|
+
module: 'data-consistency',
|
|
41
|
+
description: `Authentication failed: ${authError instanceof Error ? authError.message.split('\n')[0] : String(authError)}`,
|
|
42
|
+
});
|
|
43
|
+
return findings;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
client = new ApiClient(this.baseUrl);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Test CRUD consistency for each module
|
|
50
|
+
const endpoints = this.config.modules.map(m => `/api${m.route}`);
|
|
51
|
+
|
|
52
|
+
for (const endpoint of endpoints) {
|
|
53
|
+
const endpointFindings = await this.testCrudConsistency(client, endpoint);
|
|
54
|
+
findings.push(...endpointFindings);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return findings;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async testCrudConsistency(client: ApiClient, endpoint: string): Promise<Finding[]> {
|
|
61
|
+
const findings: Finding[] = [];
|
|
62
|
+
const testData = { name: `consistency-test-${Date.now()}`, test: true };
|
|
63
|
+
|
|
64
|
+
// Step 1: Create
|
|
65
|
+
let createdId: string | null = null;
|
|
66
|
+
try {
|
|
67
|
+
const createResponse = await Promise.race([
|
|
68
|
+
client.post(endpoint, testData),
|
|
69
|
+
new Promise<never>((_, reject) =>
|
|
70
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
71
|
+
),
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
if (createResponse.status >= 200 && createResponse.status < 300) {
|
|
75
|
+
const body = createResponse.body as Record<string, unknown>;
|
|
76
|
+
createdId = String(body?.id ?? body?.['_id'] ?? '');
|
|
77
|
+
} else {
|
|
78
|
+
findings.push({
|
|
79
|
+
id: `${this.agentId}-create-failed-${endpoint.replace(/\//g, '_')}`,
|
|
80
|
+
type: 'infra-issue',
|
|
81
|
+
severity: 'low',
|
|
82
|
+
agentId: this.agentId,
|
|
83
|
+
module: 'data-consistency',
|
|
84
|
+
description: `Cannot test consistency — create returned ${createResponse.status} for ${endpoint}`,
|
|
85
|
+
});
|
|
86
|
+
return findings;
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
findings.push({
|
|
90
|
+
id: `${this.agentId}-create-error-${endpoint.replace(/\//g, '_')}`,
|
|
91
|
+
type: 'infra-issue',
|
|
92
|
+
severity: 'low',
|
|
93
|
+
agentId: this.agentId,
|
|
94
|
+
module: 'data-consistency',
|
|
95
|
+
description: `Cannot test consistency — create failed for ${endpoint}: ${error instanceof Error ? error.message : String(error)}`,
|
|
96
|
+
});
|
|
97
|
+
return findings;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!createdId) {
|
|
101
|
+
return findings;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Step 2: Read back and compare
|
|
105
|
+
try {
|
|
106
|
+
const readResponse = await Promise.race([
|
|
107
|
+
client.get(`${endpoint}/${createdId}`),
|
|
108
|
+
new Promise<never>((_, reject) =>
|
|
109
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
110
|
+
),
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
if (readResponse.status === 200) {
|
|
114
|
+
const body = readResponse.body as Record<string, unknown>;
|
|
115
|
+
if (body.name !== testData.name) {
|
|
116
|
+
findings.push({
|
|
117
|
+
id: `${this.agentId}-read-mismatch-${endpoint.replace(/\//g, '_')}`,
|
|
118
|
+
type: 'code-bug-logic',
|
|
119
|
+
severity: 'high',
|
|
120
|
+
agentId: this.agentId,
|
|
121
|
+
module: 'data-consistency',
|
|
122
|
+
description: `Data mismatch after create+read for ${endpoint}: expected name="${testData.name}", got name="${body.name}"`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
findings.push({
|
|
127
|
+
id: `${this.agentId}-read-failed-${endpoint.replace(/\//g, '_')}`,
|
|
128
|
+
type: 'code-bug-logic',
|
|
129
|
+
severity: 'high',
|
|
130
|
+
agentId: this.agentId,
|
|
131
|
+
module: 'data-consistency',
|
|
132
|
+
description: `Read after create returned ${readResponse.status} for ${endpoint}/${createdId}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Read failed
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Step 3: Update and verify
|
|
140
|
+
const updatedName = `updated-${Date.now()}`;
|
|
141
|
+
try {
|
|
142
|
+
await Promise.race([
|
|
143
|
+
client.put(`${endpoint}/${createdId}`, { name: updatedName }),
|
|
144
|
+
new Promise<never>((_, reject) =>
|
|
145
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
146
|
+
),
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
const verifyResponse = await Promise.race([
|
|
150
|
+
client.get(`${endpoint}/${createdId}`),
|
|
151
|
+
new Promise<never>((_, reject) =>
|
|
152
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
153
|
+
),
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
if (verifyResponse.status === 200) {
|
|
157
|
+
const body = verifyResponse.body as Record<string, unknown>;
|
|
158
|
+
if (body.name !== updatedName) {
|
|
159
|
+
findings.push({
|
|
160
|
+
id: `${this.agentId}-update-mismatch-${endpoint.replace(/\//g, '_')}`,
|
|
161
|
+
type: 'code-bug-logic',
|
|
162
|
+
severity: 'high',
|
|
163
|
+
agentId: this.agentId,
|
|
164
|
+
module: 'data-consistency',
|
|
165
|
+
description: `Data mismatch after update for ${endpoint}: expected name="${updatedName}", got name="${body.name}"`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
// Update flow failed
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Step 4: Delete and verify 404
|
|
174
|
+
try {
|
|
175
|
+
await Promise.race([
|
|
176
|
+
client.delete(`${endpoint}/${createdId}`),
|
|
177
|
+
new Promise<never>((_, reject) =>
|
|
178
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
179
|
+
),
|
|
180
|
+
]);
|
|
181
|
+
|
|
182
|
+
const deleteVerify = await Promise.race([
|
|
183
|
+
client.get(`${endpoint}/${createdId}`),
|
|
184
|
+
new Promise<never>((_, reject) =>
|
|
185
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
186
|
+
),
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
if (deleteVerify.status !== 404) {
|
|
190
|
+
findings.push({
|
|
191
|
+
id: `${this.agentId}-delete-not-404-${endpoint.replace(/\//g, '_')}`,
|
|
192
|
+
type: 'code-bug-logic',
|
|
193
|
+
severity: 'high',
|
|
194
|
+
agentId: this.agentId,
|
|
195
|
+
module: 'data-consistency',
|
|
196
|
+
description: `After delete, GET ${endpoint}/${createdId} returned ${deleteVerify.status} instead of 404`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// Delete flow failed
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return findings;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { stat } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import type { Finding } from '../core/types';
|
|
6
|
+
import { BaseAgent } from './base-agent';
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const EXEC_TIMEOUT_MS = 30_000;
|
|
10
|
+
|
|
11
|
+
export class BackupRecoveryTesterAgent extends BaseAgent {
|
|
12
|
+
readonly agentId = 44;
|
|
13
|
+
readonly agentName = 'Backup Recovery Tester';
|
|
14
|
+
|
|
15
|
+
protected async preFlight(): Promise<void> {
|
|
16
|
+
// No special pre-conditions
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected async execute(): Promise<Finding[]> {
|
|
20
|
+
const findings: Finding[] = [];
|
|
21
|
+
const projectRoot = this.config.projectRoot ?? process.cwd();
|
|
22
|
+
const schemaPath = join(projectRoot, 'prisma', 'schema.prisma');
|
|
23
|
+
|
|
24
|
+
// Check if prisma schema exists
|
|
25
|
+
try {
|
|
26
|
+
await stat(schemaPath);
|
|
27
|
+
} catch {
|
|
28
|
+
findings.push({
|
|
29
|
+
id: `${this.agentId}-no-prisma-schema`,
|
|
30
|
+
type: 'infra-issue',
|
|
31
|
+
severity: 'low',
|
|
32
|
+
agentId: this.agentId,
|
|
33
|
+
module: 'backup-recovery',
|
|
34
|
+
description: `No prisma/schema.prisma found in ${projectRoot} — skipping database recovery tests`,
|
|
35
|
+
});
|
|
36
|
+
return findings;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Test 1: Schema pull verification
|
|
40
|
+
const schemaFindings = await this.testSchemaPull(projectRoot);
|
|
41
|
+
findings.push(...schemaFindings);
|
|
42
|
+
|
|
43
|
+
// Test 2: Migration status
|
|
44
|
+
const migrationFindings = await this.testMigrationStatus(projectRoot);
|
|
45
|
+
findings.push(...migrationFindings);
|
|
46
|
+
|
|
47
|
+
return findings;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async testSchemaPull(projectRoot: string): Promise<Finding[]> {
|
|
51
|
+
const findings: Finding[] = [];
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const { stdout, stderr } = await execFileAsync(
|
|
55
|
+
'npx',
|
|
56
|
+
['prisma', 'db', 'pull', '--print'],
|
|
57
|
+
{ cwd: projectRoot, timeout: EXEC_TIMEOUT_MS },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (stderr && stderr.includes('Error')) {
|
|
61
|
+
findings.push({
|
|
62
|
+
id: `${this.agentId}-schema-pull-error`,
|
|
63
|
+
type: 'infra-issue',
|
|
64
|
+
severity: 'medium',
|
|
65
|
+
agentId: this.agentId,
|
|
66
|
+
module: 'backup-recovery',
|
|
67
|
+
description: `prisma db pull reported errors: ${stderr.split('\n')[0]}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.addEvidence({
|
|
72
|
+
type: 'report',
|
|
73
|
+
path: 'prisma-schema-pull',
|
|
74
|
+
description: `prisma db pull --print completed (${stdout.length} chars of schema output)`,
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
findings.push({
|
|
78
|
+
id: `${this.agentId}-schema-pull-failed`,
|
|
79
|
+
type: 'infra-issue',
|
|
80
|
+
severity: 'medium',
|
|
81
|
+
agentId: this.agentId,
|
|
82
|
+
module: 'backup-recovery',
|
|
83
|
+
description: `prisma db pull failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return findings;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async testMigrationStatus(projectRoot: string): Promise<Finding[]> {
|
|
91
|
+
const findings: Finding[] = [];
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
let stdout = '';
|
|
95
|
+
let stderr = '';
|
|
96
|
+
try {
|
|
97
|
+
const result = await execFileAsync(
|
|
98
|
+
'npx',
|
|
99
|
+
['prisma', 'migrate', 'status'],
|
|
100
|
+
{ cwd: projectRoot, timeout: EXEC_TIMEOUT_MS },
|
|
101
|
+
);
|
|
102
|
+
stdout = result.stdout;
|
|
103
|
+
stderr = result.stderr;
|
|
104
|
+
} catch (execError) {
|
|
105
|
+
const err = execError as { stdout?: string; stderr?: string };
|
|
106
|
+
stdout = err.stdout ?? '';
|
|
107
|
+
stderr = err.stderr ?? '';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const output = `${stdout}\n${stderr}`;
|
|
111
|
+
|
|
112
|
+
if (output.includes('pending') || output.includes('not yet applied')) {
|
|
113
|
+
findings.push({
|
|
114
|
+
id: `${this.agentId}-pending-migrations`,
|
|
115
|
+
type: 'infra-issue',
|
|
116
|
+
severity: 'high',
|
|
117
|
+
agentId: this.agentId,
|
|
118
|
+
module: 'backup-recovery',
|
|
119
|
+
description: 'Pending database migrations detected — schema may be out of sync with production',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (output.includes('drift') || output.includes('modified')) {
|
|
124
|
+
findings.push({
|
|
125
|
+
id: `${this.agentId}-schema-drift`,
|
|
126
|
+
type: 'infra-issue',
|
|
127
|
+
severity: 'high',
|
|
128
|
+
agentId: this.agentId,
|
|
129
|
+
module: 'backup-recovery',
|
|
130
|
+
description: 'Database schema drift detected — production database differs from migration history',
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.addEvidence({
|
|
135
|
+
type: 'report',
|
|
136
|
+
path: 'prisma-migrate-status',
|
|
137
|
+
description: `prisma migrate status output: ${output.substring(0, 200)}`,
|
|
138
|
+
});
|
|
139
|
+
} catch (error) {
|
|
140
|
+
findings.push({
|
|
141
|
+
id: `${this.agentId}-migration-status-error`,
|
|
142
|
+
type: 'infra-issue',
|
|
143
|
+
severity: 'medium',
|
|
144
|
+
agentId: this.agentId,
|
|
145
|
+
module: 'backup-recovery',
|
|
146
|
+
description: `prisma migrate status failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return findings;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { Finding } from '../core/types';
|
|
2
|
+
import { BaseAgent } from './base-agent';
|
|
3
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
4
|
+
import { ApiClient } from '../helpers/api-client';
|
|
5
|
+
|
|
6
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
7
|
+
|
|
8
|
+
interface PiiPattern {
|
|
9
|
+
name: string;
|
|
10
|
+
regex: RegExp;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const PII_PATTERNS: PiiPattern[] = [
|
|
14
|
+
{ name: 'Email', regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g },
|
|
15
|
+
{ name: 'Phone', regex: /(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}/g },
|
|
16
|
+
{ name: 'SSN', regex: /\d{3}-\d{2}-\d{4}/g },
|
|
17
|
+
{ name: 'Credit Card', regex: /\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}/g },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export class DataPrivacyScannerAgent extends BaseAgent {
|
|
21
|
+
readonly agentId = 45;
|
|
22
|
+
readonly agentName = 'Data Privacy Scanner';
|
|
23
|
+
private baseUrl = '';
|
|
24
|
+
|
|
25
|
+
protected async preFlight(): Promise<void> {
|
|
26
|
+
if (!this.config.modules || this.config.modules.length === 0) {
|
|
27
|
+
throw new Error('DataPrivacyScannerAgent requires at least one module in config.modules');
|
|
28
|
+
}
|
|
29
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
30
|
+
this.baseUrl = env.baseUrl;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected async execute(): Promise<Finding[]> {
|
|
34
|
+
const findings: Finding[] = [];
|
|
35
|
+
const credentials =
|
|
36
|
+
this.config.auth.credentials?.['admin'] ??
|
|
37
|
+
Object.values(this.config.auth.credentials ?? {})[0];
|
|
38
|
+
|
|
39
|
+
let client: ApiClient;
|
|
40
|
+
|
|
41
|
+
if (credentials) {
|
|
42
|
+
try {
|
|
43
|
+
client = await Promise.race([
|
|
44
|
+
ApiClient.createAuthenticated(this.baseUrl, credentials),
|
|
45
|
+
new Promise<never>((_, reject) =>
|
|
46
|
+
setTimeout(() => reject(new Error('Auth timeout')), REQUEST_TIMEOUT_MS),
|
|
47
|
+
),
|
|
48
|
+
]);
|
|
49
|
+
} catch (authError) {
|
|
50
|
+
findings.push({
|
|
51
|
+
id: `${this.agentId}-auth-failed`,
|
|
52
|
+
type: 'infra-issue',
|
|
53
|
+
severity: 'medium',
|
|
54
|
+
agentId: this.agentId,
|
|
55
|
+
module: 'data-privacy',
|
|
56
|
+
description: `Authentication failed: ${authError instanceof Error ? authError.message.split('\n')[0] : String(authError)}`,
|
|
57
|
+
});
|
|
58
|
+
return findings;
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
client = new ApiClient(this.baseUrl);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const endpoints = this.config.modules.map(m => `/api${m.route}`);
|
|
65
|
+
|
|
66
|
+
for (const endpoint of endpoints) {
|
|
67
|
+
const endpointFindings = await this.scanEndpointForPii(client, endpoint);
|
|
68
|
+
findings.push(...endpointFindings);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return findings;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private async scanEndpointForPii(client: ApiClient, endpoint: string): Promise<Finding[]> {
|
|
75
|
+
const findings: Finding[] = [];
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await Promise.race([
|
|
79
|
+
client.get(endpoint),
|
|
80
|
+
new Promise<never>((_, reject) =>
|
|
81
|
+
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT_MS),
|
|
82
|
+
),
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
if (response.status !== 200) {
|
|
86
|
+
return findings;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const responseText = JSON.stringify(response.body);
|
|
90
|
+
|
|
91
|
+
for (const pattern of PII_PATTERNS) {
|
|
92
|
+
pattern.regex.lastIndex = 0;
|
|
93
|
+
const matches = responseText.match(pattern.regex);
|
|
94
|
+
|
|
95
|
+
if (matches && matches.length > 0) {
|
|
96
|
+
findings.push({
|
|
97
|
+
id: `${this.agentId}-unmasked-${pattern.name.toLowerCase()}-${endpoint.replace(/\//g, '_')}`,
|
|
98
|
+
type: 'code-bug-security',
|
|
99
|
+
severity: 'high',
|
|
100
|
+
agentId: this.agentId,
|
|
101
|
+
module: 'data-privacy',
|
|
102
|
+
description: `Unmasked ${pattern.name} found in response from ${endpoint} (${matches.length} occurrence(s))`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.addEvidence({
|
|
108
|
+
type: 'log',
|
|
109
|
+
path: `pii-scan-${endpoint.replace(/\//g, '_')}`,
|
|
110
|
+
description: `PII scan for ${endpoint}: response size=${responseText.length} chars`,
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
findings.push({
|
|
114
|
+
id: `${this.agentId}-scan-error-${endpoint.replace(/\//g, '_')}`,
|
|
115
|
+
type: 'infra-issue',
|
|
116
|
+
severity: 'low',
|
|
117
|
+
agentId: this.agentId,
|
|
118
|
+
module: 'data-privacy',
|
|
119
|
+
description: `Failed to scan ${endpoint}: ${error instanceof Error ? error.message : String(error)}`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return findings;
|
|
124
|
+
}
|
|
125
|
+
}
|