@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,186 @@
|
|
|
1
|
+
import { BaseAgent } from './base-agent';
|
|
2
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
3
|
+
import { login } from '../helpers/navigation';
|
|
4
|
+
/** Keywords that indicate cookie consent banner. */
|
|
5
|
+
const CONSENT_KEYWORDS = ['cookie', 'consent', 'privacy', 'gdpr', 'accept all', 'accept cookies'];
|
|
6
|
+
/** Common privacy policy link selectors. */
|
|
7
|
+
const PRIVACY_LINK_SELECTORS = [
|
|
8
|
+
'a:has-text("Privacy")',
|
|
9
|
+
'a:has-text("Privacy Policy")',
|
|
10
|
+
'a[href*="privacy"]',
|
|
11
|
+
'a[href*="datenschutz"]',
|
|
12
|
+
];
|
|
13
|
+
export class GdprComplianceTesterAgent extends BaseAgent {
|
|
14
|
+
agentId = 65;
|
|
15
|
+
agentName = 'GDPR Compliance Tester';
|
|
16
|
+
baseUrl = '';
|
|
17
|
+
async preFlight() {
|
|
18
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
19
|
+
this.baseUrl = env.baseUrl;
|
|
20
|
+
}
|
|
21
|
+
async execute() {
|
|
22
|
+
const findings = [];
|
|
23
|
+
const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
|
|
24
|
+
const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
|
|
25
|
+
const credentials = this.config.auth.credentials?.['admin'] ??
|
|
26
|
+
Object.values(this.config.auth.credentials ?? {})[0];
|
|
27
|
+
const { chromium } = await import('playwright');
|
|
28
|
+
let browser = null;
|
|
29
|
+
try {
|
|
30
|
+
browser = await chromium.launch({ headless: true });
|
|
31
|
+
// Test 1: Cookie consent banner (fresh context, no previous cookies)
|
|
32
|
+
const freshContext = await browser.newContext();
|
|
33
|
+
const freshPage = await freshContext.newPage();
|
|
34
|
+
try {
|
|
35
|
+
await freshPage.goto(this.baseUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
36
|
+
await new Promise(resolve => setTimeout(resolve, 2_000));
|
|
37
|
+
const consentFindings = await this.checkCookieConsent(freshPage);
|
|
38
|
+
findings.push(...consentFindings);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
findings.push({
|
|
42
|
+
id: `${this.agentId}-nav-failed`,
|
|
43
|
+
type: 'infra-issue',
|
|
44
|
+
severity: 'medium',
|
|
45
|
+
agentId: this.agentId,
|
|
46
|
+
module: 'gdpr-compliance-tester',
|
|
47
|
+
description: 'Failed to navigate to base URL for GDPR testing',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
await freshPage.close().catch(() => undefined);
|
|
51
|
+
await freshContext.close().catch(() => undefined);
|
|
52
|
+
// Test 2: Privacy policy link
|
|
53
|
+
const page = await browser.newPage();
|
|
54
|
+
try {
|
|
55
|
+
await page.goto(this.baseUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
56
|
+
const privacyFindings = await this.checkPrivacyPolicy(page);
|
|
57
|
+
findings.push(...privacyFindings);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Already reported nav failure
|
|
61
|
+
}
|
|
62
|
+
// Test 3: Delete account option (requires login)
|
|
63
|
+
if (credentials) {
|
|
64
|
+
try {
|
|
65
|
+
await login(page, credentials, loginUrl);
|
|
66
|
+
const deleteAccountFindings = await this.checkDeleteAccount(page);
|
|
67
|
+
findings.push(...deleteAccountFindings);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
findings.push({
|
|
71
|
+
id: `${this.agentId}-login-failed`,
|
|
72
|
+
type: 'infra-issue',
|
|
73
|
+
severity: 'low',
|
|
74
|
+
agentId: this.agentId,
|
|
75
|
+
module: 'gdpr-compliance-tester',
|
|
76
|
+
description: 'Login failed — cannot check delete account option',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
await page.close().catch(() => undefined);
|
|
81
|
+
}
|
|
82
|
+
catch (playwrightError) {
|
|
83
|
+
findings.push({
|
|
84
|
+
id: `${this.agentId}-playwright-failure`,
|
|
85
|
+
type: 'infra-issue',
|
|
86
|
+
severity: 'medium',
|
|
87
|
+
agentId: this.agentId,
|
|
88
|
+
module: 'gdpr-compliance-tester',
|
|
89
|
+
description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
if (browser)
|
|
94
|
+
await browser.close().catch(() => undefined);
|
|
95
|
+
}
|
|
96
|
+
return findings;
|
|
97
|
+
}
|
|
98
|
+
async checkCookieConsent(page) {
|
|
99
|
+
const findings = [];
|
|
100
|
+
try {
|
|
101
|
+
const bodyText = await page.evaluate(() => document.body.innerText.toLowerCase());
|
|
102
|
+
const hasConsentText = CONSENT_KEYWORDS.some(keyword => bodyText.includes(keyword));
|
|
103
|
+
if (!hasConsentText) {
|
|
104
|
+
findings.push({
|
|
105
|
+
id: `${this.agentId}-no-cookie-consent`,
|
|
106
|
+
type: 'code-bug-security',
|
|
107
|
+
severity: 'high',
|
|
108
|
+
agentId: this.agentId,
|
|
109
|
+
module: 'gdpr-compliance-tester',
|
|
110
|
+
description: 'No cookie consent banner detected on first visit',
|
|
111
|
+
suggestedFix: 'Implement a cookie consent banner that appears on first visit',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Check for accept button
|
|
116
|
+
const acceptBtn = await page.locator('button:has-text("Accept"), button:has-text("Agree"), button:has-text("OK"), button:has-text("Got it")').count();
|
|
117
|
+
if (acceptBtn === 0) {
|
|
118
|
+
findings.push({
|
|
119
|
+
id: `${this.agentId}-no-consent-button`,
|
|
120
|
+
type: 'code-bug-security',
|
|
121
|
+
severity: 'medium',
|
|
122
|
+
agentId: this.agentId,
|
|
123
|
+
module: 'gdpr-compliance-tester',
|
|
124
|
+
description: 'Cookie consent text found but no accept/agree button detected',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Evaluation failed
|
|
131
|
+
}
|
|
132
|
+
return findings;
|
|
133
|
+
}
|
|
134
|
+
async checkPrivacyPolicy(page) {
|
|
135
|
+
const findings = [];
|
|
136
|
+
const selector = PRIVACY_LINK_SELECTORS.join(', ');
|
|
137
|
+
const privacyLinkCount = await page.locator(selector).count();
|
|
138
|
+
if (privacyLinkCount === 0) {
|
|
139
|
+
findings.push({
|
|
140
|
+
id: `${this.agentId}-no-privacy-policy`,
|
|
141
|
+
type: 'code-bug-security',
|
|
142
|
+
severity: 'medium',
|
|
143
|
+
agentId: this.agentId,
|
|
144
|
+
module: 'gdpr-compliance-tester',
|
|
145
|
+
description: 'No privacy policy link found on the page',
|
|
146
|
+
suggestedFix: 'Add a privacy policy link accessible from the main page',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return findings;
|
|
150
|
+
}
|
|
151
|
+
async checkDeleteAccount(page) {
|
|
152
|
+
const findings = [];
|
|
153
|
+
// Try settings/account pages
|
|
154
|
+
const settingsRoutes = ['/settings', '/account', '/profile', '/settings/account'];
|
|
155
|
+
for (const route of settingsRoutes) {
|
|
156
|
+
try {
|
|
157
|
+
await page.goto(`${this.baseUrl}${route}`, { waitUntil: 'domcontentloaded', timeout: 5_000 });
|
|
158
|
+
const deleteText = await page.evaluate(() => {
|
|
159
|
+
const body = document.body.innerText.toLowerCase();
|
|
160
|
+
return body.includes('delete account') || body.includes('remove account') || body.includes('close account');
|
|
161
|
+
});
|
|
162
|
+
if (deleteText) {
|
|
163
|
+
this.addEvidence({
|
|
164
|
+
type: 'log',
|
|
165
|
+
path: '',
|
|
166
|
+
description: `Delete account option found at ${route}`,
|
|
167
|
+
});
|
|
168
|
+
return findings;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Route not found — try next
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
findings.push({
|
|
176
|
+
id: `${this.agentId}-no-delete-account`,
|
|
177
|
+
type: 'test-bug',
|
|
178
|
+
severity: 'medium',
|
|
179
|
+
agentId: this.agentId,
|
|
180
|
+
module: 'gdpr-compliance-tester',
|
|
181
|
+
description: 'No "Delete account" option found in settings — GDPR right to erasure may not be accessible',
|
|
182
|
+
});
|
|
183
|
+
return findings;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=65-gdpr-compliance-tester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"65-gdpr-compliance-tester.js","sourceRoot":"","sources":["../../agents/65-gdpr-compliance-tester.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Finding } from '../core/types';
|
|
2
|
+
import { BaseAgent } from './base-agent';
|
|
3
|
+
export declare class Soc2ControlValidatorAgent extends BaseAgent {
|
|
4
|
+
readonly agentId = 66;
|
|
5
|
+
readonly agentName = "SOC2 Control Validator";
|
|
6
|
+
private baseUrl;
|
|
7
|
+
protected preFlight(): Promise<void>;
|
|
8
|
+
protected execute(): Promise<Finding[]>;
|
|
9
|
+
private checkAuditLogging;
|
|
10
|
+
private checkSessionTimeout;
|
|
11
|
+
private checkMfaUI;
|
|
12
|
+
private checkRoleBasedAccess;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=66-soc2-control-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"66-soc2-control-validator.d.ts","sourceRoot":"","sources":["../../agents/66-soc2-control-validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAmBzC,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,OAAO,MAAM;IACtB,QAAQ,CAAC,SAAS,4BAA4B;IAC9C,OAAO,CAAC,OAAO,CAAM;cAEL,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;cAK1B,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAyB7C,OAAO,CAAC,iBAAiB;IA8CzB,OAAO,CAAC,mBAAmB;YA+Bb,UAAU;IA4CxB,OAAO,CAAC,oBAAoB;CA8B7B"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { BaseAgent } from './base-agent';
|
|
4
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
5
|
+
/** Glob-like manual scan for .ts files under src/. */
|
|
6
|
+
function findTsFiles(dir, results = []) {
|
|
7
|
+
if (!fs.existsSync(dir))
|
|
8
|
+
return results;
|
|
9
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const fullPath = path.join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git') {
|
|
13
|
+
findTsFiles(fullPath, results);
|
|
14
|
+
}
|
|
15
|
+
else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
16
|
+
results.push(fullPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
21
|
+
export class Soc2ControlValidatorAgent extends BaseAgent {
|
|
22
|
+
agentId = 66;
|
|
23
|
+
agentName = 'SOC2 Control Validator';
|
|
24
|
+
baseUrl = '';
|
|
25
|
+
async preFlight() {
|
|
26
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
27
|
+
this.baseUrl = env.baseUrl;
|
|
28
|
+
}
|
|
29
|
+
async execute() {
|
|
30
|
+
const findings = [];
|
|
31
|
+
const projectRoot = this.config.projectRoot ?? process.cwd();
|
|
32
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
33
|
+
// Check 1: Audit logging evidence
|
|
34
|
+
const auditFindings = this.checkAuditLogging(srcDir);
|
|
35
|
+
findings.push(...auditFindings);
|
|
36
|
+
// Check 2: Session timeout
|
|
37
|
+
const sessionFindings = this.checkSessionTimeout(srcDir);
|
|
38
|
+
findings.push(...sessionFindings);
|
|
39
|
+
// Check 3: MFA UI
|
|
40
|
+
const mfaFindings = await this.checkMfaUI();
|
|
41
|
+
findings.push(...mfaFindings);
|
|
42
|
+
// Check 4: Role-based access
|
|
43
|
+
const rbacFindings = this.checkRoleBasedAccess(srcDir);
|
|
44
|
+
findings.push(...rbacFindings);
|
|
45
|
+
return findings;
|
|
46
|
+
}
|
|
47
|
+
checkAuditLogging(srcDir) {
|
|
48
|
+
const findings = [];
|
|
49
|
+
if (!fs.existsSync(srcDir)) {
|
|
50
|
+
findings.push({
|
|
51
|
+
id: `${this.agentId}-no-src`,
|
|
52
|
+
type: 'infra-issue',
|
|
53
|
+
severity: 'low',
|
|
54
|
+
agentId: this.agentId,
|
|
55
|
+
module: 'soc2-control-validator',
|
|
56
|
+
description: 'No src/ directory found — cannot check for audit logging',
|
|
57
|
+
});
|
|
58
|
+
return findings;
|
|
59
|
+
}
|
|
60
|
+
const tsFiles = findTsFiles(srcDir);
|
|
61
|
+
let hasAuditLogging = false;
|
|
62
|
+
for (const filePath of tsFiles) {
|
|
63
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
64
|
+
if (/audit/i.test(content) && (/log/i.test(content) || /trail/i.test(content) || /event/i.test(content))) {
|
|
65
|
+
hasAuditLogging = true;
|
|
66
|
+
this.addEvidence({
|
|
67
|
+
type: 'log',
|
|
68
|
+
path: filePath,
|
|
69
|
+
description: 'Audit logging evidence found',
|
|
70
|
+
});
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!hasAuditLogging) {
|
|
75
|
+
findings.push({
|
|
76
|
+
id: `${this.agentId}-no-audit-logging`,
|
|
77
|
+
type: 'code-bug-security',
|
|
78
|
+
severity: 'medium',
|
|
79
|
+
agentId: this.agentId,
|
|
80
|
+
module: 'soc2-control-validator',
|
|
81
|
+
description: 'No audit logging evidence found in source code',
|
|
82
|
+
suggestedFix: 'Implement audit logging for security-relevant events',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return findings;
|
|
86
|
+
}
|
|
87
|
+
checkSessionTimeout(srcDir) {
|
|
88
|
+
const findings = [];
|
|
89
|
+
if (!fs.existsSync(srcDir))
|
|
90
|
+
return findings;
|
|
91
|
+
const tsFiles = findTsFiles(srcDir);
|
|
92
|
+
let hasSessionTimeout = false;
|
|
93
|
+
for (const filePath of tsFiles) {
|
|
94
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
95
|
+
if (/sessionTimeout/i.test(content) || /session.*timeout/i.test(content) || /setTimeout.*session/i.test(content) || /idle.*timeout/i.test(content)) {
|
|
96
|
+
hasSessionTimeout = true;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!hasSessionTimeout) {
|
|
101
|
+
findings.push({
|
|
102
|
+
id: `${this.agentId}-no-session-timeout`,
|
|
103
|
+
type: 'code-bug-security',
|
|
104
|
+
severity: 'medium',
|
|
105
|
+
agentId: this.agentId,
|
|
106
|
+
module: 'soc2-control-validator',
|
|
107
|
+
description: 'No session timeout configuration found in source code',
|
|
108
|
+
suggestedFix: 'Implement session timeout to auto-logout inactive users',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return findings;
|
|
112
|
+
}
|
|
113
|
+
async checkMfaUI() {
|
|
114
|
+
const findings = [];
|
|
115
|
+
try {
|
|
116
|
+
const { chromium } = await import('playwright');
|
|
117
|
+
let browser = null;
|
|
118
|
+
try {
|
|
119
|
+
browser = await chromium.launch({ headless: true });
|
|
120
|
+
const page = await browser.newPage();
|
|
121
|
+
await page.goto(this.baseUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
122
|
+
const bodyText = await page.evaluate(() => document.body.innerText.toLowerCase());
|
|
123
|
+
const hasMfaUI = bodyText.includes('two-factor') ||
|
|
124
|
+
bodyText.includes('2fa') ||
|
|
125
|
+
bodyText.includes('mfa') ||
|
|
126
|
+
bodyText.includes('multi-factor') ||
|
|
127
|
+
bodyText.includes('authenticator');
|
|
128
|
+
if (!hasMfaUI) {
|
|
129
|
+
findings.push({
|
|
130
|
+
id: `${this.agentId}-no-mfa`,
|
|
131
|
+
type: 'code-bug-security',
|
|
132
|
+
severity: 'medium',
|
|
133
|
+
agentId: this.agentId,
|
|
134
|
+
module: 'soc2-control-validator',
|
|
135
|
+
description: 'No MFA/2FA UI elements detected',
|
|
136
|
+
suggestedFix: 'Implement multi-factor authentication for SOC2 compliance',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
await page.close().catch(() => undefined);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
if (browser)
|
|
143
|
+
await browser.close().catch(() => undefined);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Playwright not available — skip MFA UI check
|
|
148
|
+
}
|
|
149
|
+
return findings;
|
|
150
|
+
}
|
|
151
|
+
checkRoleBasedAccess(srcDir) {
|
|
152
|
+
const findings = [];
|
|
153
|
+
if (!fs.existsSync(srcDir))
|
|
154
|
+
return findings;
|
|
155
|
+
const tsFiles = findTsFiles(srcDir);
|
|
156
|
+
let hasRbac = false;
|
|
157
|
+
for (const filePath of tsFiles) {
|
|
158
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
159
|
+
if (/role/i.test(content) && (/guard/i.test(content) || /permission/i.test(content) || /authorize/i.test(content) || /access/i.test(content))) {
|
|
160
|
+
hasRbac = true;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!hasRbac) {
|
|
165
|
+
findings.push({
|
|
166
|
+
id: `${this.agentId}-no-rbac`,
|
|
167
|
+
type: 'code-bug-security',
|
|
168
|
+
severity: 'medium',
|
|
169
|
+
agentId: this.agentId,
|
|
170
|
+
module: 'soc2-control-validator',
|
|
171
|
+
description: 'No role-based access control evidence found in source code',
|
|
172
|
+
suggestedFix: 'Implement RBAC with role checks on protected resources',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return findings;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=66-soc2-control-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"66-soc2-control-validator.js","sourceRoot":"","sources":["../../agents/66-soc2-control-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,sDAAsD;AACtD,SAAS,WAAW,CAAC,GAAW,EAAE,UAAoB,EAAE;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAExC,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAClF,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,yBAA0B,SAAQ,SAAS;IAC7C,OAAO,GAAG,EAAE,CAAC;IACb,SAAS,GAAG,wBAAwB,CAAC;IACtC,OAAO,GAAG,EAAE,CAAC;IAEX,KAAK,CAAC,SAAS;QACvB,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC7B,CAAC;IAES,KAAK,CAAC,OAAO;QACrB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAE7C,kCAAkC;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;QAEhC,2BAA2B;QAC3B,MAAM,eAAe,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACzD,QAAQ,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC;QAElC,kBAAkB;QAClB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC5C,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;QAE9B,6BAA6B;QAC7B,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;QAE/B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,iBAAiB,CAAC,MAAc;QACtC,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,SAAS;gBAC5B,IAAI,EAAE,aAAa;gBACnB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,wBAAwB;gBAChC,WAAW,EAAE,0DAA0D;aACxE,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,eAAe,GAAG,KAAK,CAAC;QAE5B,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;gBACzG,eAAe,GAAG,IAAI,CAAC;gBACvB,IAAI,CAAC,WAAW,CAAC;oBACf,IAAI,EAAE,KAAK;oBACX,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,8BAA8B;iBAC5C,CAAC,CAAC;gBACH,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,mBAAmB;gBACtC,IAAI,EAAE,mBAAmB;gBACzB,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,wBAAwB;gBAChC,WAAW,EAAE,gDAAgD;gBAC7D,YAAY,EAAE,sDAAsD;aACrE,CAAC,CAAC;QACL,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,mBAAmB,CAAC,MAAc;QACxC,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,QAAQ,CAAC;QAE5C,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAE9B,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnJ,iBAAiB,GAAG,IAAI,CAAC;gBACzB,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,qBAAqB;gBACxC,IAAI,EAAE,mBAAmB;gBACzB,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,wBAAwB;gBAChC,WAAW,EAAE,uDAAuD;gBACpE,YAAY,EAAE,yDAAyD;aACxE,CAAC,CAAC;QACL,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YAChD,IAAI,OAAO,GAAmB,IAAI,CAAC;YAEnC,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBAErC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;gBAElF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;gBAElF,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;oBAC9C,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;oBACxB,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;oBACxB,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC;oBACjC,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;gBAErC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,QAAQ,CAAC,IAAI,CAAC;wBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,SAAS;wBAC5B,IAAI,EAAE,mBAAmB;wBACzB,QAAQ,EAAE,QAAQ;wBAClB,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,MAAM,EAAE,wBAAwB;wBAChC,WAAW,EAAE,iCAAiC;wBAC9C,YAAY,EAAE,2DAA2D;qBAC1E,CAAC,CAAC;gBACL,CAAC;gBAED,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC5C,CAAC;oBAAS,CAAC;gBACT,IAAI,OAAO;oBAAE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,oBAAoB,CAAC,MAAc;QACzC,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,QAAQ,CAAC;QAE5C,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;gBAC9I,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,UAAU;gBAC7B,IAAI,EAAE,mBAAmB;gBACzB,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,wBAAwB;gBAChC,WAAW,EAAE,4DAA4D;gBACzE,YAAY,EAAE,wDAAwD;aACvE,CAAC,CAAC;QACL,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Finding } from '../core/types';
|
|
2
|
+
import { BaseAgent } from './base-agent';
|
|
3
|
+
export declare class WcagAaaTesterAgent extends BaseAgent {
|
|
4
|
+
readonly agentId = 67;
|
|
5
|
+
readonly agentName = "WCAG AAA Tester";
|
|
6
|
+
private baseUrl;
|
|
7
|
+
protected preFlight(): Promise<void>;
|
|
8
|
+
protected execute(): Promise<Finding[]>;
|
|
9
|
+
private checkContrast;
|
|
10
|
+
private checkTextResize;
|
|
11
|
+
private checkKeyboardNavigation;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=67-wcag-aaa-tester.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"67-wcag-aaa-tester.d.ts","sourceRoot":"","sources":["../../agents/67-wcag-aaa-tester.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,OAAO,MAAM;IACtB,QAAQ,CAAC,SAAS,qBAAqB;IACvC,OAAO,CAAC,OAAO,CAAM;cAEL,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;cAS1B,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YAyE/B,aAAa;YA0Db,eAAe;YAkCf,uBAAuB;CAuDtC"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { BaseAgent } from './base-agent';
|
|
2
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
3
|
+
import { login } from '../helpers/navigation';
|
|
4
|
+
export class WcagAaaTesterAgent extends BaseAgent {
|
|
5
|
+
agentId = 67;
|
|
6
|
+
agentName = 'WCAG AAA Tester';
|
|
7
|
+
baseUrl = '';
|
|
8
|
+
async preFlight() {
|
|
9
|
+
if (!this.config.modules || this.config.modules.length === 0) {
|
|
10
|
+
throw new Error('WcagAaaTesterAgent requires at least one module in config.modules');
|
|
11
|
+
}
|
|
12
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
13
|
+
this.baseUrl = env.baseUrl;
|
|
14
|
+
}
|
|
15
|
+
async execute() {
|
|
16
|
+
const findings = [];
|
|
17
|
+
const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
|
|
18
|
+
const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
|
|
19
|
+
const credentials = this.config.auth.credentials?.['admin'] ??
|
|
20
|
+
Object.values(this.config.auth.credentials ?? {})[0];
|
|
21
|
+
const { chromium } = await import('playwright');
|
|
22
|
+
let browser = null;
|
|
23
|
+
try {
|
|
24
|
+
browser = await chromium.launch({ headless: true });
|
|
25
|
+
const page = await browser.newPage();
|
|
26
|
+
if (credentials) {
|
|
27
|
+
try {
|
|
28
|
+
await login(page, credentials, loginUrl);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
findings.push({
|
|
32
|
+
id: `${this.agentId}-login-failed`,
|
|
33
|
+
type: 'infra-issue',
|
|
34
|
+
severity: 'medium',
|
|
35
|
+
agentId: this.agentId,
|
|
36
|
+
module: 'wcag-aaa-tester',
|
|
37
|
+
description: 'Login failed — continuing anonymously',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const mod of this.config.modules) {
|
|
42
|
+
const fullUrl = mod.route.startsWith('http')
|
|
43
|
+
? mod.route
|
|
44
|
+
: `${this.baseUrl}${mod.route}`;
|
|
45
|
+
try {
|
|
46
|
+
await page.goto(fullUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Test 1: Text contrast ratio >= 7:1 (AAA)
|
|
52
|
+
const contrastFindings = await this.checkContrast(page, mod.id);
|
|
53
|
+
findings.push(...contrastFindings);
|
|
54
|
+
// Test 2: Text resizable to 200%
|
|
55
|
+
const resizeFindings = await this.checkTextResize(page, mod.id);
|
|
56
|
+
findings.push(...resizeFindings);
|
|
57
|
+
// Test 3: Keyboard navigation
|
|
58
|
+
const keyboardFindings = await this.checkKeyboardNavigation(page, mod.id);
|
|
59
|
+
findings.push(...keyboardFindings);
|
|
60
|
+
}
|
|
61
|
+
await page.close().catch(() => undefined);
|
|
62
|
+
}
|
|
63
|
+
catch (playwrightError) {
|
|
64
|
+
findings.push({
|
|
65
|
+
id: `${this.agentId}-playwright-failure`,
|
|
66
|
+
type: 'infra-issue',
|
|
67
|
+
severity: 'medium',
|
|
68
|
+
agentId: this.agentId,
|
|
69
|
+
module: 'wcag-aaa-tester',
|
|
70
|
+
description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
if (browser)
|
|
75
|
+
await browser.close().catch(() => undefined);
|
|
76
|
+
}
|
|
77
|
+
return findings;
|
|
78
|
+
}
|
|
79
|
+
async checkContrast(page, moduleId) {
|
|
80
|
+
const findings = [];
|
|
81
|
+
try {
|
|
82
|
+
const lowContrastCount = await page.evaluate(() => {
|
|
83
|
+
function getLuminance(r, g, b) {
|
|
84
|
+
const sRGB = [r, g, b].map(c => {
|
|
85
|
+
const s = c / 255;
|
|
86
|
+
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
87
|
+
});
|
|
88
|
+
return 0.2126 * sRGB[0] + 0.7152 * sRGB[1] + 0.0722 * sRGB[2];
|
|
89
|
+
}
|
|
90
|
+
function parseColor(color) {
|
|
91
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
92
|
+
if (match)
|
|
93
|
+
return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const elements = document.querySelectorAll('p, span, a, h1, h2, h3, h4, h5, h6, li, td, th, label');
|
|
97
|
+
let lowContrast = 0;
|
|
98
|
+
for (const el of Array.from(elements).slice(0, 50)) {
|
|
99
|
+
const style = window.getComputedStyle(el);
|
|
100
|
+
const fg = parseColor(style.color);
|
|
101
|
+
const bg = parseColor(style.backgroundColor);
|
|
102
|
+
if (fg && bg) {
|
|
103
|
+
const fgLum = getLuminance(...fg);
|
|
104
|
+
const bgLum = getLuminance(...bg);
|
|
105
|
+
const ratio = (Math.max(fgLum, bgLum) + 0.05) / (Math.min(fgLum, bgLum) + 0.05);
|
|
106
|
+
if (ratio < 7) {
|
|
107
|
+
lowContrast++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return lowContrast;
|
|
112
|
+
});
|
|
113
|
+
if (lowContrastCount > 0) {
|
|
114
|
+
findings.push({
|
|
115
|
+
id: `${this.agentId}-low-contrast-${moduleId}`,
|
|
116
|
+
type: 'test-bug',
|
|
117
|
+
severity: 'medium',
|
|
118
|
+
agentId: this.agentId,
|
|
119
|
+
module: moduleId,
|
|
120
|
+
description: `${lowContrastCount} element(s) have contrast ratio below 7:1 (WCAG AAA requirement)`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Contrast check failed — non-critical
|
|
126
|
+
}
|
|
127
|
+
return findings;
|
|
128
|
+
}
|
|
129
|
+
async checkTextResize(page, moduleId) {
|
|
130
|
+
const findings = [];
|
|
131
|
+
try {
|
|
132
|
+
const hasOverflow = await page.evaluate(() => {
|
|
133
|
+
document.documentElement.style.fontSize = '200%';
|
|
134
|
+
// Wait a tick for reflow
|
|
135
|
+
const body = document.body;
|
|
136
|
+
const hasHorizontalOverflow = body.scrollWidth > window.innerWidth;
|
|
137
|
+
// Reset
|
|
138
|
+
document.documentElement.style.fontSize = '';
|
|
139
|
+
return hasHorizontalOverflow;
|
|
140
|
+
});
|
|
141
|
+
if (hasOverflow) {
|
|
142
|
+
findings.push({
|
|
143
|
+
id: `${this.agentId}-text-resize-overflow-${moduleId}`,
|
|
144
|
+
type: 'test-bug',
|
|
145
|
+
severity: 'medium',
|
|
146
|
+
agentId: this.agentId,
|
|
147
|
+
module: moduleId,
|
|
148
|
+
description: `Content overflows horizontally when text is resized to 200% in ${moduleId}`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Resize check failed — non-critical
|
|
154
|
+
}
|
|
155
|
+
return findings;
|
|
156
|
+
}
|
|
157
|
+
async checkKeyboardNavigation(page, moduleId) {
|
|
158
|
+
const findings = [];
|
|
159
|
+
try {
|
|
160
|
+
// Tab through elements and check focus visibility
|
|
161
|
+
const focusableCount = await page.evaluate(() => {
|
|
162
|
+
const focusable = document.querySelectorAll('a, button, input, select, textarea, [tabindex]');
|
|
163
|
+
return focusable.length;
|
|
164
|
+
});
|
|
165
|
+
if (focusableCount === 0) {
|
|
166
|
+
findings.push({
|
|
167
|
+
id: `${this.agentId}-no-focusable-${moduleId}`,
|
|
168
|
+
type: 'test-bug',
|
|
169
|
+
severity: 'medium',
|
|
170
|
+
agentId: this.agentId,
|
|
171
|
+
module: moduleId,
|
|
172
|
+
description: `No focusable elements found in ${moduleId} — keyboard navigation may not work`,
|
|
173
|
+
});
|
|
174
|
+
return findings;
|
|
175
|
+
}
|
|
176
|
+
// Press Tab a few times and check if focus is visible
|
|
177
|
+
for (let i = 0; i < Math.min(5, focusableCount); i++) {
|
|
178
|
+
await page.keyboard.press('Tab');
|
|
179
|
+
}
|
|
180
|
+
const hasFocusIndicator = await page.evaluate(() => {
|
|
181
|
+
const focused = document.activeElement;
|
|
182
|
+
if (!focused || focused === document.body)
|
|
183
|
+
return false;
|
|
184
|
+
const style = window.getComputedStyle(focused);
|
|
185
|
+
const hasOutline = style.outlineStyle !== 'none' && style.outlineWidth !== '0px';
|
|
186
|
+
const hasBoxShadow = style.boxShadow !== 'none';
|
|
187
|
+
const hasBorder = style.borderColor !== style.backgroundColor;
|
|
188
|
+
return hasOutline || hasBoxShadow || hasBorder;
|
|
189
|
+
});
|
|
190
|
+
if (!hasFocusIndicator) {
|
|
191
|
+
findings.push({
|
|
192
|
+
id: `${this.agentId}-no-focus-indicator-${moduleId}`,
|
|
193
|
+
type: 'test-bug',
|
|
194
|
+
severity: 'medium',
|
|
195
|
+
agentId: this.agentId,
|
|
196
|
+
module: moduleId,
|
|
197
|
+
description: `Focus indicator not visible during keyboard navigation in ${moduleId}`,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Keyboard nav check failed — non-critical
|
|
203
|
+
}
|
|
204
|
+
return findings;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=67-wcag-aaa-tester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"67-wcag-aaa-tester.js","sourceRoot":"","sources":["../../agents/67-wcag-aaa-tester.ts"],"names":[],"mappings":"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"}
|