@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,136 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import type { Finding } from '../core/types';
|
|
4
|
+
import { BaseAgent } from './base-agent';
|
|
5
|
+
|
|
6
|
+
/** Glob-like manual scan for .ts files under src/. */
|
|
7
|
+
function findTsFiles(dir: string, results: string[] = []): string[] {
|
|
8
|
+
if (!fs.existsSync(dir)) return results;
|
|
9
|
+
|
|
10
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const fullPath = path.join(dir, entry.name);
|
|
13
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git') {
|
|
14
|
+
findTsFiles(fullPath, results);
|
|
15
|
+
} else if (entry.isFile() && entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts') && !entry.name.endsWith('.spec.ts')) {
|
|
16
|
+
results.push(fullPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class LogQualityAuditorAgent extends BaseAgent {
|
|
23
|
+
readonly agentId = 63;
|
|
24
|
+
readonly agentName = 'Log Quality Auditor';
|
|
25
|
+
|
|
26
|
+
protected async preFlight(): Promise<void> {
|
|
27
|
+
// File-based agent
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected async execute(): Promise<Finding[]> {
|
|
31
|
+
const findings: Finding[] = [];
|
|
32
|
+
|
|
33
|
+
const projectRoot = this.config.projectRoot ?? process.cwd();
|
|
34
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(srcDir)) {
|
|
37
|
+
findings.push({
|
|
38
|
+
id: `${this.agentId}-no-src-dir`,
|
|
39
|
+
type: 'infra-issue',
|
|
40
|
+
severity: 'low',
|
|
41
|
+
agentId: this.agentId,
|
|
42
|
+
module: 'log-quality-auditor',
|
|
43
|
+
description: 'No src/ directory found in project root',
|
|
44
|
+
});
|
|
45
|
+
return findings;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const tsFiles = findTsFiles(srcDir);
|
|
49
|
+
let consoleLogCount = 0;
|
|
50
|
+
let consoleErrorWithoutContextCount = 0;
|
|
51
|
+
let hasStructuredLogging = false;
|
|
52
|
+
|
|
53
|
+
for (const filePath of tsFiles) {
|
|
54
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
55
|
+
const lines = content.split('\n');
|
|
56
|
+
|
|
57
|
+
// Check for structured logging libraries
|
|
58
|
+
if (content.includes('winston') || content.includes('pino') || content.includes('bunyan') || content.includes('log4js')) {
|
|
59
|
+
hasStructuredLogging = true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
63
|
+
const line = lines[lineNum];
|
|
64
|
+
|
|
65
|
+
// Check for console.log
|
|
66
|
+
if (/console\.log\(/.test(line)) {
|
|
67
|
+
consoleLogCount++;
|
|
68
|
+
if (consoleLogCount <= 5) {
|
|
69
|
+
findings.push({
|
|
70
|
+
id: `${this.agentId}-console-log-${consoleLogCount}`,
|
|
71
|
+
type: 'test-bug',
|
|
72
|
+
severity: 'low',
|
|
73
|
+
agentId: this.agentId,
|
|
74
|
+
module: 'log-quality-auditor',
|
|
75
|
+
file: filePath,
|
|
76
|
+
line: lineNum + 1,
|
|
77
|
+
description: `console.log() found — use structured logging instead`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check for console.error without structured context
|
|
83
|
+
if (/console\.error\(/.test(line)) {
|
|
84
|
+
// Check if it's just a string (no object context)
|
|
85
|
+
if (/console\.error\(\s*['"`]/.test(line) || /console\.error\(\s*[^{]/.test(line)) {
|
|
86
|
+
consoleErrorWithoutContextCount++;
|
|
87
|
+
if (consoleErrorWithoutContextCount <= 3) {
|
|
88
|
+
findings.push({
|
|
89
|
+
id: `${this.agentId}-unstructured-error-${consoleErrorWithoutContextCount}`,
|
|
90
|
+
type: 'test-bug',
|
|
91
|
+
severity: 'medium',
|
|
92
|
+
agentId: this.agentId,
|
|
93
|
+
module: 'log-quality-auditor',
|
|
94
|
+
file: filePath,
|
|
95
|
+
line: lineNum + 1,
|
|
96
|
+
description: 'console.error() without structured context — include error object or metadata',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Summary findings
|
|
105
|
+
if (consoleLogCount > 5) {
|
|
106
|
+
findings.push({
|
|
107
|
+
id: `${this.agentId}-console-log-summary`,
|
|
108
|
+
type: 'test-bug',
|
|
109
|
+
severity: 'low',
|
|
110
|
+
agentId: this.agentId,
|
|
111
|
+
module: 'log-quality-auditor',
|
|
112
|
+
description: `${consoleLogCount} total console.log() calls found (showing first 5)`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!hasStructuredLogging) {
|
|
117
|
+
findings.push({
|
|
118
|
+
id: `${this.agentId}-no-structured-logging`,
|
|
119
|
+
type: 'test-bug',
|
|
120
|
+
severity: 'medium',
|
|
121
|
+
agentId: this.agentId,
|
|
122
|
+
module: 'log-quality-auditor',
|
|
123
|
+
description: 'No structured logging library (winston, pino, bunyan) detected',
|
|
124
|
+
suggestedFix: 'Consider adopting a structured logging library like pino or winston',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.addEvidence({
|
|
129
|
+
type: 'log',
|
|
130
|
+
path: '',
|
|
131
|
+
description: `Scanned ${tsFiles.length} TypeScript files: ${consoleLogCount} console.log, ${consoleErrorWithoutContextCount} unstructured console.error`,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return findings;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { Browser, Page, Request } from 'playwright';
|
|
2
|
+
import type { Finding } from '../core/types';
|
|
3
|
+
import { BaseAgent } from './base-agent';
|
|
4
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
5
|
+
import { login } from '../helpers/navigation';
|
|
6
|
+
|
|
7
|
+
/** Analytics domains to detect. */
|
|
8
|
+
const ANALYTICS_DOMAINS = [
|
|
9
|
+
'google-analytics.com',
|
|
10
|
+
'www.google-analytics.com',
|
|
11
|
+
'analytics.google.com',
|
|
12
|
+
'segment.com',
|
|
13
|
+
'api.segment.io',
|
|
14
|
+
'cdn.segment.com',
|
|
15
|
+
'mixpanel.com',
|
|
16
|
+
'api.mixpanel.com',
|
|
17
|
+
'amplitude.com',
|
|
18
|
+
'api.amplitude.com',
|
|
19
|
+
'api2.amplitude.com',
|
|
20
|
+
'gtag',
|
|
21
|
+
'googletagmanager.com',
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
export class AnalyticsTrackerTesterAgent extends BaseAgent {
|
|
25
|
+
readonly agentId = 64;
|
|
26
|
+
readonly agentName = 'Analytics Tracker Tester';
|
|
27
|
+
private baseUrl = '';
|
|
28
|
+
|
|
29
|
+
protected async preFlight(): Promise<void> {
|
|
30
|
+
if (!this.config.modules || this.config.modules.length === 0) {
|
|
31
|
+
throw new Error('AnalyticsTrackerTesterAgent requires at least one module in config.modules');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
35
|
+
this.baseUrl = env.baseUrl;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected async execute(): Promise<Finding[]> {
|
|
39
|
+
const findings: Finding[] = [];
|
|
40
|
+
|
|
41
|
+
const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
|
|
42
|
+
const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
|
|
43
|
+
const credentials =
|
|
44
|
+
this.config.auth.credentials?.['admin'] ??
|
|
45
|
+
Object.values(this.config.auth.credentials ?? {})[0];
|
|
46
|
+
|
|
47
|
+
const { chromium } = await import('playwright');
|
|
48
|
+
|
|
49
|
+
let browser: Browser | null = null;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
browser = await chromium.launch({ headless: true });
|
|
53
|
+
const page = await browser.newPage();
|
|
54
|
+
|
|
55
|
+
// Track analytics requests
|
|
56
|
+
const analyticsRequests: { url: string; module: string }[] = [];
|
|
57
|
+
const pageviewUrls: string[] = [];
|
|
58
|
+
|
|
59
|
+
page.on('request', (request: Request) => {
|
|
60
|
+
const url = request.url();
|
|
61
|
+
const isAnalytics = ANALYTICS_DOMAINS.some(domain => url.includes(domain));
|
|
62
|
+
if (isAnalytics) {
|
|
63
|
+
analyticsRequests.push({ url, module: 'unknown' });
|
|
64
|
+
if (url.includes('pageview') || url.includes('collect') || url.includes('/t?') || url.includes('/g/collect')) {
|
|
65
|
+
pageviewUrls.push(url);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (credentials) {
|
|
71
|
+
try {
|
|
72
|
+
await login(page, credentials, loginUrl);
|
|
73
|
+
} catch {
|
|
74
|
+
findings.push({
|
|
75
|
+
id: `${this.agentId}-login-failed`,
|
|
76
|
+
type: 'infra-issue',
|
|
77
|
+
severity: 'medium',
|
|
78
|
+
agentId: this.agentId,
|
|
79
|
+
module: 'analytics-tracker-tester',
|
|
80
|
+
description: 'Login failed — continuing anonymously',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Navigate through modules
|
|
86
|
+
for (const mod of this.config.modules) {
|
|
87
|
+
const preNavCount = analyticsRequests.length;
|
|
88
|
+
const fullUrl = mod.route.startsWith('http')
|
|
89
|
+
? mod.route
|
|
90
|
+
: `${this.baseUrl}${mod.route}`;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
await page.goto(fullUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
94
|
+
await new Promise(resolve => setTimeout(resolve, 2_000));
|
|
95
|
+
} catch {
|
|
96
|
+
// Navigation failure — skip module
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Tag recent analytics requests with module
|
|
101
|
+
for (let i = preNavCount; i < analyticsRequests.length; i++) {
|
|
102
|
+
analyticsRequests[i].module = mod.id;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Analyze results
|
|
107
|
+
if (analyticsRequests.length === 0) {
|
|
108
|
+
findings.push({
|
|
109
|
+
id: `${this.agentId}-no-analytics`,
|
|
110
|
+
type: 'test-bug',
|
|
111
|
+
severity: 'low',
|
|
112
|
+
agentId: this.agentId,
|
|
113
|
+
module: 'analytics-tracker-tester',
|
|
114
|
+
description: 'No analytics tracking detected — no requests to analytics providers found',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check for duplicate pageviews
|
|
119
|
+
if (pageviewUrls.length > this.config.modules.length) {
|
|
120
|
+
findings.push({
|
|
121
|
+
id: `${this.agentId}-duplicate-pageviews`,
|
|
122
|
+
type: 'test-bug',
|
|
123
|
+
severity: 'low',
|
|
124
|
+
agentId: this.agentId,
|
|
125
|
+
module: 'analytics-tracker-tester',
|
|
126
|
+
description: `${pageviewUrls.length} pageview events fired for ${this.config.modules.length} pages — possible duplicate tracking`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Report detected analytics
|
|
131
|
+
const uniqueDomains = new Set(
|
|
132
|
+
analyticsRequests.map(r => {
|
|
133
|
+
try {
|
|
134
|
+
return new URL(r.url).hostname;
|
|
135
|
+
} catch {
|
|
136
|
+
return r.url;
|
|
137
|
+
}
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (uniqueDomains.size > 0) {
|
|
142
|
+
this.addEvidence({
|
|
143
|
+
type: 'log',
|
|
144
|
+
path: '',
|
|
145
|
+
description: `Analytics providers detected: ${Array.from(uniqueDomains).join(', ')}`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await page.close().catch(() => undefined);
|
|
150
|
+
} catch (playwrightError) {
|
|
151
|
+
findings.push({
|
|
152
|
+
id: `${this.agentId}-playwright-failure`,
|
|
153
|
+
type: 'infra-issue',
|
|
154
|
+
severity: 'medium',
|
|
155
|
+
agentId: this.agentId,
|
|
156
|
+
module: 'analytics-tracker-tester',
|
|
157
|
+
description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
|
|
158
|
+
});
|
|
159
|
+
} finally {
|
|
160
|
+
if (browser) await browser.close().catch(() => undefined);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return findings;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type { Browser, Page } from 'playwright';
|
|
2
|
+
import type { Finding } from '../core/types';
|
|
3
|
+
import { BaseAgent } from './base-agent';
|
|
4
|
+
import { resolveEnvironment } from '../helpers/env-resolver';
|
|
5
|
+
import { login } from '../helpers/navigation';
|
|
6
|
+
|
|
7
|
+
/** Keywords that indicate cookie consent banner. */
|
|
8
|
+
const CONSENT_KEYWORDS = ['cookie', 'consent', 'privacy', 'gdpr', 'accept all', 'accept cookies'] as const;
|
|
9
|
+
|
|
10
|
+
/** Common privacy policy link selectors. */
|
|
11
|
+
const PRIVACY_LINK_SELECTORS = [
|
|
12
|
+
'a:has-text("Privacy")',
|
|
13
|
+
'a:has-text("Privacy Policy")',
|
|
14
|
+
'a[href*="privacy"]',
|
|
15
|
+
'a[href*="datenschutz"]',
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
18
|
+
export class GdprComplianceTesterAgent extends BaseAgent {
|
|
19
|
+
readonly agentId = 65;
|
|
20
|
+
readonly agentName = 'GDPR Compliance Tester';
|
|
21
|
+
private baseUrl = '';
|
|
22
|
+
|
|
23
|
+
protected async preFlight(): Promise<void> {
|
|
24
|
+
const { env } = await resolveEnvironment(this.config, this.phase);
|
|
25
|
+
this.baseUrl = env.baseUrl;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected async execute(): Promise<Finding[]> {
|
|
29
|
+
const findings: Finding[] = [];
|
|
30
|
+
|
|
31
|
+
const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
|
|
32
|
+
const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
|
|
33
|
+
const credentials =
|
|
34
|
+
this.config.auth.credentials?.['admin'] ??
|
|
35
|
+
Object.values(this.config.auth.credentials ?? {})[0];
|
|
36
|
+
|
|
37
|
+
const { chromium } = await import('playwright');
|
|
38
|
+
|
|
39
|
+
let browser: Browser | null = null;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
browser = await chromium.launch({ headless: true });
|
|
43
|
+
|
|
44
|
+
// Test 1: Cookie consent banner (fresh context, no previous cookies)
|
|
45
|
+
const freshContext = await browser.newContext();
|
|
46
|
+
const freshPage = await freshContext.newPage();
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await freshPage.goto(this.baseUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
50
|
+
await new Promise(resolve => setTimeout(resolve, 2_000));
|
|
51
|
+
|
|
52
|
+
const consentFindings = await this.checkCookieConsent(freshPage);
|
|
53
|
+
findings.push(...consentFindings);
|
|
54
|
+
} catch {
|
|
55
|
+
findings.push({
|
|
56
|
+
id: `${this.agentId}-nav-failed`,
|
|
57
|
+
type: 'infra-issue',
|
|
58
|
+
severity: 'medium',
|
|
59
|
+
agentId: this.agentId,
|
|
60
|
+
module: 'gdpr-compliance-tester',
|
|
61
|
+
description: 'Failed to navigate to base URL for GDPR testing',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await freshPage.close().catch(() => undefined);
|
|
66
|
+
await freshContext.close().catch(() => undefined);
|
|
67
|
+
|
|
68
|
+
// Test 2: Privacy policy link
|
|
69
|
+
const page = await browser.newPage();
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
await page.goto(this.baseUrl, { waitUntil: 'domcontentloaded', timeout: 10_000 });
|
|
73
|
+
|
|
74
|
+
const privacyFindings = await this.checkPrivacyPolicy(page);
|
|
75
|
+
findings.push(...privacyFindings);
|
|
76
|
+
} catch {
|
|
77
|
+
// Already reported nav failure
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Test 3: Delete account option (requires login)
|
|
81
|
+
if (credentials) {
|
|
82
|
+
try {
|
|
83
|
+
await login(page, credentials, loginUrl);
|
|
84
|
+
|
|
85
|
+
const deleteAccountFindings = await this.checkDeleteAccount(page);
|
|
86
|
+
findings.push(...deleteAccountFindings);
|
|
87
|
+
} catch {
|
|
88
|
+
findings.push({
|
|
89
|
+
id: `${this.agentId}-login-failed`,
|
|
90
|
+
type: 'infra-issue',
|
|
91
|
+
severity: 'low',
|
|
92
|
+
agentId: this.agentId,
|
|
93
|
+
module: 'gdpr-compliance-tester',
|
|
94
|
+
description: 'Login failed — cannot check delete account option',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await page.close().catch(() => undefined);
|
|
100
|
+
} catch (playwrightError) {
|
|
101
|
+
findings.push({
|
|
102
|
+
id: `${this.agentId}-playwright-failure`,
|
|
103
|
+
type: 'infra-issue',
|
|
104
|
+
severity: 'medium',
|
|
105
|
+
agentId: this.agentId,
|
|
106
|
+
module: 'gdpr-compliance-tester',
|
|
107
|
+
description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
|
|
108
|
+
});
|
|
109
|
+
} finally {
|
|
110
|
+
if (browser) await browser.close().catch(() => undefined);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return findings;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async checkCookieConsent(page: Page): Promise<Finding[]> {
|
|
117
|
+
const findings: Finding[] = [];
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const bodyText = await page.evaluate(() => document.body.innerText.toLowerCase());
|
|
121
|
+
|
|
122
|
+
const hasConsentText = CONSENT_KEYWORDS.some(keyword => bodyText.includes(keyword));
|
|
123
|
+
|
|
124
|
+
if (!hasConsentText) {
|
|
125
|
+
findings.push({
|
|
126
|
+
id: `${this.agentId}-no-cookie-consent`,
|
|
127
|
+
type: 'code-bug-security',
|
|
128
|
+
severity: 'high',
|
|
129
|
+
agentId: this.agentId,
|
|
130
|
+
module: 'gdpr-compliance-tester',
|
|
131
|
+
description: 'No cookie consent banner detected on first visit',
|
|
132
|
+
suggestedFix: 'Implement a cookie consent banner that appears on first visit',
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
// Check for accept button
|
|
136
|
+
const acceptBtn = await page.locator('button:has-text("Accept"), button:has-text("Agree"), button:has-text("OK"), button:has-text("Got it")').count();
|
|
137
|
+
if (acceptBtn === 0) {
|
|
138
|
+
findings.push({
|
|
139
|
+
id: `${this.agentId}-no-consent-button`,
|
|
140
|
+
type: 'code-bug-security',
|
|
141
|
+
severity: 'medium',
|
|
142
|
+
agentId: this.agentId,
|
|
143
|
+
module: 'gdpr-compliance-tester',
|
|
144
|
+
description: 'Cookie consent text found but no accept/agree button detected',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
// Evaluation failed
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return findings;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private async checkPrivacyPolicy(page: Page): Promise<Finding[]> {
|
|
156
|
+
const findings: Finding[] = [];
|
|
157
|
+
|
|
158
|
+
const selector = PRIVACY_LINK_SELECTORS.join(', ');
|
|
159
|
+
const privacyLinkCount = await page.locator(selector).count();
|
|
160
|
+
|
|
161
|
+
if (privacyLinkCount === 0) {
|
|
162
|
+
findings.push({
|
|
163
|
+
id: `${this.agentId}-no-privacy-policy`,
|
|
164
|
+
type: 'code-bug-security',
|
|
165
|
+
severity: 'medium',
|
|
166
|
+
agentId: this.agentId,
|
|
167
|
+
module: 'gdpr-compliance-tester',
|
|
168
|
+
description: 'No privacy policy link found on the page',
|
|
169
|
+
suggestedFix: 'Add a privacy policy link accessible from the main page',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return findings;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private async checkDeleteAccount(page: Page): Promise<Finding[]> {
|
|
177
|
+
const findings: Finding[] = [];
|
|
178
|
+
|
|
179
|
+
// Try settings/account pages
|
|
180
|
+
const settingsRoutes = ['/settings', '/account', '/profile', '/settings/account'];
|
|
181
|
+
|
|
182
|
+
for (const route of settingsRoutes) {
|
|
183
|
+
try {
|
|
184
|
+
await page.goto(`${this.baseUrl}${route}`, { waitUntil: 'domcontentloaded', timeout: 5_000 });
|
|
185
|
+
|
|
186
|
+
const deleteText = await page.evaluate(() => {
|
|
187
|
+
const body = document.body.innerText.toLowerCase();
|
|
188
|
+
return body.includes('delete account') || body.includes('remove account') || body.includes('close account');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (deleteText) {
|
|
192
|
+
this.addEvidence({
|
|
193
|
+
type: 'log',
|
|
194
|
+
path: '',
|
|
195
|
+
description: `Delete account option found at ${route}`,
|
|
196
|
+
});
|
|
197
|
+
return findings;
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// Route not found — try next
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
findings.push({
|
|
205
|
+
id: `${this.agentId}-no-delete-account`,
|
|
206
|
+
type: 'test-bug',
|
|
207
|
+
severity: 'medium',
|
|
208
|
+
agentId: this.agentId,
|
|
209
|
+
module: 'gdpr-compliance-tester',
|
|
210
|
+
description: 'No "Delete account" option found in settings — GDPR right to erasure may not be accessible',
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return findings;
|
|
214
|
+
}
|
|
215
|
+
}
|