@avi770/testteam 3.0.0 → 3.2.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 +166 -5
- package/README.md +92 -19
- package/bin/testteam.js +32 -4
- package/dist/agents/01-analyst.d.ts +2 -2
- package/dist/agents/01-analyst.js +1 -1
- package/dist/agents/02-seed-architect.d.ts +2 -2
- package/dist/agents/02-seed-architect.js +2 -2
- package/dist/agents/03-test-generator.d.ts +2 -2
- package/dist/agents/03-test-generator.js +2 -2
- package/dist/agents/04-unit-runner.d.ts +2 -2
- package/dist/agents/04-unit-runner.d.ts.map +1 -1
- package/dist/agents/04-unit-runner.js +12 -3
- package/dist/agents/04-unit-runner.js.map +1 -1
- package/dist/agents/05-browser-crawler.d.ts +2 -2
- package/dist/agents/05-browser-crawler.d.ts.map +1 -1
- package/dist/agents/05-browser-crawler.js +24 -12
- package/dist/agents/05-browser-crawler.js.map +1 -1
- package/dist/agents/06-api-exerciser.d.ts +2 -2
- package/dist/agents/06-api-exerciser.js +2 -2
- package/dist/agents/07-security-scout.d.ts +2 -2
- package/dist/agents/07-security-scout.js +2 -2
- package/dist/agents/08-a11y-guardian.d.ts +2 -2
- package/dist/agents/08-a11y-guardian.d.ts.map +1 -1
- package/dist/agents/08-a11y-guardian.js +9 -5
- package/dist/agents/08-a11y-guardian.js.map +1 -1
- package/dist/agents/09-healer.d.ts +2 -2
- package/dist/agents/09-healer.js +2 -2
- package/dist/agents/10-reporter.d.ts +2 -2
- package/dist/agents/10-reporter.d.ts.map +1 -1
- package/dist/agents/10-reporter.js +55 -27
- package/dist/agents/10-reporter.js.map +1 -1
- package/dist/agents/100-error-handling-auditor.d.ts +63 -0
- package/dist/agents/100-error-handling-auditor.d.ts.map +1 -0
- package/dist/agents/100-error-handling-auditor.js +334 -0
- package/dist/agents/100-error-handling-auditor.js.map +1 -0
- package/dist/agents/101-rate-limit-auditor.d.ts +72 -0
- package/dist/agents/101-rate-limit-auditor.d.ts.map +1 -0
- package/dist/agents/101-rate-limit-auditor.js +295 -0
- package/dist/agents/101-rate-limit-auditor.js.map +1 -0
- package/dist/agents/102-dockerfile-auditor.d.ts +62 -0
- package/dist/agents/102-dockerfile-auditor.d.ts.map +1 -0
- package/dist/agents/102-dockerfile-auditor.js +337 -0
- package/dist/agents/102-dockerfile-auditor.js.map +1 -0
- package/dist/agents/103-ci-workflow-auditor.d.ts +57 -0
- package/dist/agents/103-ci-workflow-auditor.d.ts.map +1 -0
- package/dist/agents/103-ci-workflow-auditor.js +247 -0
- package/dist/agents/103-ci-workflow-auditor.js.map +1 -0
- package/dist/agents/104-n-plus-one-detector.d.ts +57 -0
- package/dist/agents/104-n-plus-one-detector.d.ts.map +1 -0
- package/dist/agents/104-n-plus-one-detector.js +329 -0
- package/dist/agents/104-n-plus-one-detector.js.map +1 -0
- package/dist/agents/105-unbounded-query-auditor.d.ts +50 -0
- package/dist/agents/105-unbounded-query-auditor.d.ts.map +1 -0
- package/dist/agents/105-unbounded-query-auditor.js +284 -0
- package/dist/agents/105-unbounded-query-auditor.js.map +1 -0
- package/dist/agents/106-hardcoded-config-auditor.d.ts +54 -0
- package/dist/agents/106-hardcoded-config-auditor.d.ts.map +1 -0
- package/dist/agents/106-hardcoded-config-auditor.js +251 -0
- package/dist/agents/106-hardcoded-config-auditor.js.map +1 -0
- package/dist/agents/107-open-redirect-detector.d.ts +52 -0
- package/dist/agents/107-open-redirect-detector.d.ts.map +1 -0
- package/dist/agents/107-open-redirect-detector.js +263 -0
- package/dist/agents/107-open-redirect-detector.js.map +1 -0
- package/dist/agents/108-sql-injection-detector.d.ts +51 -0
- package/dist/agents/108-sql-injection-detector.d.ts.map +1 -0
- package/dist/agents/108-sql-injection-detector.js +323 -0
- package/dist/agents/108-sql-injection-detector.js.map +1 -0
- package/dist/agents/109-path-traversal-detector.d.ts +51 -0
- package/dist/agents/109-path-traversal-detector.d.ts.map +1 -0
- package/dist/agents/109-path-traversal-detector.js +244 -0
- package/dist/agents/109-path-traversal-detector.js.map +1 -0
- package/dist/agents/11-fixer.d.ts +4 -2
- package/dist/agents/11-fixer.d.ts.map +1 -1
- package/dist/agents/11-fixer.js +52 -11
- package/dist/agents/11-fixer.js.map +1 -1
- package/dist/agents/110-mass-assignment-detector.d.ts +52 -0
- package/dist/agents/110-mass-assignment-detector.d.ts.map +1 -0
- package/dist/agents/110-mass-assignment-detector.js +199 -0
- package/dist/agents/110-mass-assignment-detector.js.map +1 -0
- package/dist/agents/111-dynamic-eval-detector.d.ts +46 -0
- package/dist/agents/111-dynamic-eval-detector.d.ts.map +1 -0
- package/dist/agents/111-dynamic-eval-detector.js +233 -0
- package/dist/agents/111-dynamic-eval-detector.js.map +1 -0
- package/dist/agents/112-taint-tracker.d.ts +226 -0
- package/dist/agents/112-taint-tracker.d.ts.map +1 -0
- package/dist/agents/112-taint-tracker.js +1273 -0
- package/dist/agents/112-taint-tracker.js.map +1 -0
- package/dist/agents/113-response-contract-auditor.d.ts +92 -0
- package/dist/agents/113-response-contract-auditor.d.ts.map +1 -0
- package/dist/agents/113-response-contract-auditor.js +694 -0
- package/dist/agents/113-response-contract-auditor.js.map +1 -0
- package/dist/agents/114-static-a11y-auditor.d.ts +66 -0
- package/dist/agents/114-static-a11y-auditor.d.ts.map +1 -0
- package/dist/agents/114-static-a11y-auditor.js +377 -0
- package/dist/agents/114-static-a11y-auditor.js.map +1 -0
- package/dist/agents/115-multihop-taint-tracker.d.ts +84 -0
- package/dist/agents/115-multihop-taint-tracker.d.ts.map +1 -0
- package/dist/agents/115-multihop-taint-tracker.js +340 -0
- package/dist/agents/115-multihop-taint-tracker.js.map +1 -0
- package/dist/agents/116-runtime-contract-capture.d.ts +79 -0
- package/dist/agents/116-runtime-contract-capture.d.ts.map +1 -0
- package/dist/agents/116-runtime-contract-capture.js +274 -0
- package/dist/agents/116-runtime-contract-capture.js.map +1 -0
- package/dist/agents/117-aria-rule-engine.d.ts +52 -0
- package/dist/agents/117-aria-rule-engine.d.ts.map +1 -0
- package/dist/agents/117-aria-rule-engine.js +415 -0
- package/dist/agents/117-aria-rule-engine.js.map +1 -0
- package/dist/agents/118-insecure-crypto-auditor.d.ts +48 -0
- package/dist/agents/118-insecure-crypto-auditor.d.ts.map +1 -0
- package/dist/agents/118-insecure-crypto-auditor.js +232 -0
- package/dist/agents/118-insecure-crypto-auditor.js.map +1 -0
- package/dist/agents/119-secrets-scanner.d.ts +44 -0
- package/dist/agents/119-secrets-scanner.d.ts.map +1 -0
- package/dist/agents/119-secrets-scanner.js +242 -0
- package/dist/agents/119-secrets-scanner.js.map +1 -0
- package/dist/agents/12-ux-inspector.d.ts +2 -2
- package/dist/agents/12-ux-inspector.d.ts.map +1 -1
- package/dist/agents/12-ux-inspector.js +8 -4
- package/dist/agents/12-ux-inspector.js.map +1 -1
- package/dist/agents/120-async-safety-auditor.d.ts +48 -0
- package/dist/agents/120-async-safety-auditor.d.ts.map +1 -0
- package/dist/agents/120-async-safety-auditor.js +250 -0
- package/dist/agents/120-async-safety-auditor.js.map +1 -0
- package/dist/agents/13-performance-profiler.d.ts +2 -2
- package/dist/agents/13-performance-profiler.d.ts.map +1 -1
- package/dist/agents/13-performance-profiler.js +5 -4
- package/dist/agents/13-performance-profiler.js.map +1 -1
- package/dist/agents/14-data-integrity-auditor.d.ts +2 -2
- package/dist/agents/14-data-integrity-auditor.js +4 -4
- package/dist/agents/14-data-integrity-auditor.js.map +1 -1
- package/dist/agents/15-regression-sentinel.d.ts +6 -5
- package/dist/agents/15-regression-sentinel.d.ts.map +1 -1
- package/dist/agents/15-regression-sentinel.js +5 -4
- package/dist/agents/15-regression-sentinel.js.map +1 -1
- package/dist/agents/16-chaos-agent.d.ts +2 -2
- package/dist/agents/16-chaos-agent.d.ts.map +1 -1
- package/dist/agents/16-chaos-agent.js +11 -4
- package/dist/agents/16-chaos-agent.js.map +1 -1
- package/dist/agents/17-documentation-validator.d.ts +2 -2
- package/dist/agents/17-documentation-validator.d.ts.map +1 -1
- package/dist/agents/17-documentation-validator.js +5 -2
- package/dist/agents/17-documentation-validator.js.map +1 -1
- package/dist/agents/18-integration-watchdog.d.ts +2 -2
- package/dist/agents/18-integration-watchdog.d.ts.map +1 -1
- package/dist/agents/18-integration-watchdog.js +5 -2
- package/dist/agents/18-integration-watchdog.js.map +1 -1
- package/dist/agents/19-tenant-isolation-auditor.d.ts +2 -2
- package/dist/agents/19-tenant-isolation-auditor.js +4 -4
- package/dist/agents/19-tenant-isolation-auditor.js.map +1 -1
- package/dist/agents/20-workflow-completion-tester.d.ts +2 -2
- package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -1
- package/dist/agents/20-workflow-completion-tester.js +10 -6
- package/dist/agents/20-workflow-completion-tester.js.map +1 -1
- package/dist/agents/21-state-session-tester.d.ts +2 -2
- package/dist/agents/21-state-session-tester.d.ts.map +1 -1
- package/dist/agents/21-state-session-tester.js +15 -5
- package/dist/agents/21-state-session-tester.js.map +1 -1
- package/dist/agents/22-email-notification-verifier.d.ts +2 -2
- package/dist/agents/22-email-notification-verifier.js +2 -2
- package/dist/agents/23-migration-tester.d.ts +2 -2
- package/dist/agents/23-migration-tester.js +1 -1
- package/dist/agents/24-signup-onboarding-tester.d.ts +2 -2
- package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -1
- package/dist/agents/24-signup-onboarding-tester.js +13 -10
- package/dist/agents/24-signup-onboarding-tester.js.map +1 -1
- package/dist/agents/25-crud-flow-tester.d.ts +2 -2
- package/dist/agents/25-crud-flow-tester.d.ts.map +1 -1
- package/dist/agents/25-crud-flow-tester.js +12 -6
- package/dist/agents/25-crud-flow-tester.js.map +1 -1
- package/dist/agents/26-form-validator.d.ts +2 -2
- package/dist/agents/26-form-validator.d.ts.map +1 -1
- package/dist/agents/26-form-validator.js +12 -6
- package/dist/agents/26-form-validator.js.map +1 -1
- package/dist/agents/27-search-filter-tester.d.ts +2 -2
- package/dist/agents/27-search-filter-tester.d.ts.map +1 -1
- package/dist/agents/27-search-filter-tester.js +12 -6
- package/dist/agents/27-search-filter-tester.js.map +1 -1
- package/dist/agents/28-navigation-routing-tester.d.ts +2 -2
- package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -1
- package/dist/agents/28-navigation-routing-tester.js +12 -6
- package/dist/agents/28-navigation-routing-tester.js.map +1 -1
- package/dist/agents/29-responsive-interaction-tester.d.ts +2 -2
- package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -1
- package/dist/agents/29-responsive-interaction-tester.js +12 -6
- package/dist/agents/29-responsive-interaction-tester.js.map +1 -1
- package/dist/agents/30-multi-user-scenario-tester.d.ts +2 -2
- package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -1
- package/dist/agents/30-multi-user-scenario-tester.js +20 -13
- package/dist/agents/30-multi-user-scenario-tester.js.map +1 -1
- package/dist/agents/31-load-tester.d.ts +2 -2
- package/dist/agents/31-load-tester.js +2 -2
- package/dist/agents/32-memory-leak-detector.d.ts +2 -2
- package/dist/agents/32-memory-leak-detector.d.ts.map +1 -1
- package/dist/agents/32-memory-leak-detector.js +5 -4
- package/dist/agents/32-memory-leak-detector.js.map +1 -1
- package/dist/agents/33-bundle-analyzer.d.ts +2 -2
- package/dist/agents/33-bundle-analyzer.js +1 -1
- package/dist/agents/34-xss-scanner.d.ts +2 -2
- package/dist/agents/34-xss-scanner.d.ts.map +1 -1
- package/dist/agents/34-xss-scanner.js +12 -6
- package/dist/agents/34-xss-scanner.js.map +1 -1
- package/dist/agents/35-csrf-tester.d.ts +2 -2
- package/dist/agents/35-csrf-tester.js +2 -2
- package/dist/agents/36-auth-fuzzer.d.ts +2 -2
- package/dist/agents/36-auth-fuzzer.js +2 -2
- package/dist/agents/37-dependency-scanner.d.ts +2 -2
- package/dist/agents/37-dependency-scanner.js +1 -1
- package/dist/agents/38-secrets-scanner.d.ts +2 -2
- package/dist/agents/38-secrets-scanner.d.ts.map +1 -1
- package/dist/agents/38-secrets-scanner.js +39 -4
- package/dist/agents/38-secrets-scanner.js.map +1 -1
- package/dist/agents/39-api-contract-tester.d.ts +2 -2
- package/dist/agents/39-api-contract-tester.js +2 -2
- package/dist/agents/40-rate-limit-tester.d.ts +2 -2
- package/dist/agents/40-rate-limit-tester.js +2 -2
- package/dist/agents/41-api-pagination-tester.d.ts +2 -2
- package/dist/agents/41-api-pagination-tester.js +2 -2
- package/dist/agents/42-graphql-tester.d.ts +2 -2
- package/dist/agents/42-graphql-tester.js +2 -2
- package/dist/agents/43-data-consistency-checker.d.ts +2 -2
- package/dist/agents/43-data-consistency-checker.js +3 -3
- package/dist/agents/44-backup-recovery-tester.d.ts +2 -2
- package/dist/agents/44-backup-recovery-tester.js +1 -1
- package/dist/agents/45-data-privacy-scanner.d.ts +2 -2
- package/dist/agents/45-data-privacy-scanner.js +3 -3
- package/dist/agents/46-seo-auditor.d.ts +2 -2
- package/dist/agents/46-seo-auditor.d.ts.map +1 -1
- package/dist/agents/46-seo-auditor.js +12 -6
- package/dist/agents/46-seo-auditor.js.map +1 -1
- package/dist/agents/47-social-preview-tester.d.ts +2 -2
- package/dist/agents/47-social-preview-tester.d.ts.map +1 -1
- package/dist/agents/47-social-preview-tester.js +12 -6
- package/dist/agents/47-social-preview-tester.js.map +1 -1
- package/dist/agents/48-lighthouse-auditor.d.ts +2 -2
- package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -1
- package/dist/agents/48-lighthouse-auditor.js +5 -4
- package/dist/agents/48-lighthouse-auditor.js.map +1 -1
- package/dist/agents/49-i18n-tester.d.ts +2 -2
- package/dist/agents/49-i18n-tester.d.ts.map +1 -1
- package/dist/agents/49-i18n-tester.js +12 -6
- package/dist/agents/49-i18n-tester.js.map +1 -1
- package/dist/agents/50-timezone-tester.d.ts +2 -2
- package/dist/agents/50-timezone-tester.d.ts.map +1 -1
- package/dist/agents/50-timezone-tester.js +40 -33
- package/dist/agents/50-timezone-tester.js.map +1 -1
- package/dist/agents/51-error-recovery-tester.d.ts +2 -2
- package/dist/agents/51-error-recovery-tester.d.ts.map +1 -1
- package/dist/agents/51-error-recovery-tester.js +12 -7
- package/dist/agents/51-error-recovery-tester.js.map +1 -1
- package/dist/agents/52-offline-mode-tester.d.ts +2 -2
- package/dist/agents/52-offline-mode-tester.d.ts.map +1 -1
- package/dist/agents/52-offline-mode-tester.js +12 -7
- package/dist/agents/52-offline-mode-tester.js.map +1 -1
- package/dist/agents/53-graceful-degradation-tester.d.ts +2 -2
- package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -1
- package/dist/agents/53-graceful-degradation-tester.js +10 -3
- package/dist/agents/53-graceful-degradation-tester.js.map +1 -1
- package/dist/agents/54-websocket-tester.d.ts +2 -2
- package/dist/agents/54-websocket-tester.d.ts.map +1 -1
- package/dist/agents/54-websocket-tester.js +12 -6
- package/dist/agents/54-websocket-tester.js.map +1 -1
- package/dist/agents/55-realtime-sync-tester.d.ts +2 -2
- package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -1
- package/dist/agents/55-realtime-sync-tester.js +101 -96
- package/dist/agents/55-realtime-sync-tester.js.map +1 -1
- package/dist/agents/56-file-upload-tester.d.ts +2 -2
- package/dist/agents/56-file-upload-tester.d.ts.map +1 -1
- package/dist/agents/56-file-upload-tester.js +17 -13
- package/dist/agents/56-file-upload-tester.js.map +1 -1
- package/dist/agents/57-export-tester.d.ts +2 -2
- package/dist/agents/57-export-tester.d.ts.map +1 -1
- package/dist/agents/57-export-tester.js +8 -4
- package/dist/agents/57-export-tester.js.map +1 -1
- package/dist/agents/58-payment-flow-tester.d.ts +2 -2
- package/dist/agents/58-payment-flow-tester.d.ts.map +1 -1
- package/dist/agents/58-payment-flow-tester.js +8 -4
- package/dist/agents/58-payment-flow-tester.js.map +1 -1
- package/dist/agents/59-ssl-tls-auditor.d.ts +2 -2
- package/dist/agents/59-ssl-tls-auditor.js +2 -2
- package/dist/agents/60-dns-cdn-tester.d.ts +2 -2
- package/dist/agents/60-dns-cdn-tester.js +2 -2
- package/dist/agents/61-docker-health-checker.d.ts +2 -2
- package/dist/agents/61-docker-health-checker.js +1 -1
- package/dist/agents/62-env-config-validator.d.ts +2 -2
- package/dist/agents/62-env-config-validator.js +1 -1
- package/dist/agents/63-log-quality-auditor.d.ts +2 -2
- package/dist/agents/63-log-quality-auditor.js +1 -1
- package/dist/agents/64-analytics-tracker-tester.d.ts +2 -2
- package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -1
- package/dist/agents/64-analytics-tracker-tester.js +8 -4
- package/dist/agents/64-analytics-tracker-tester.js.map +1 -1
- package/dist/agents/65-gdpr-compliance-tester.d.ts +2 -2
- package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -1
- package/dist/agents/65-gdpr-compliance-tester.js +55 -40
- package/dist/agents/65-gdpr-compliance-tester.js.map +1 -1
- package/dist/agents/66-soc2-control-validator.d.ts +2 -2
- package/dist/agents/66-soc2-control-validator.d.ts.map +1 -1
- package/dist/agents/66-soc2-control-validator.js +29 -21
- package/dist/agents/66-soc2-control-validator.js.map +1 -1
- package/dist/agents/67-wcag-aaa-tester.d.ts +2 -2
- package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -1
- package/dist/agents/67-wcag-aaa-tester.js +12 -6
- package/dist/agents/67-wcag-aaa-tester.js.map +1 -1
- package/dist/agents/68-dead-code-detector.d.ts +2 -2
- package/dist/agents/68-dead-code-detector.d.ts.map +1 -1
- package/dist/agents/68-dead-code-detector.js +6 -3
- package/dist/agents/68-dead-code-detector.js.map +1 -1
- package/dist/agents/69-type-safety-auditor.d.ts +2 -2
- package/dist/agents/69-type-safety-auditor.js +1 -1
- package/dist/agents/70-complexity-analyzer.d.ts +2 -2
- package/dist/agents/70-complexity-analyzer.js +1 -1
- package/dist/agents/71-unit-testing-agent.d.ts +15 -0
- package/dist/agents/71-unit-testing-agent.d.ts.map +1 -0
- package/dist/agents/71-unit-testing-agent.js +220 -0
- package/dist/agents/71-unit-testing-agent.js.map +1 -0
- package/dist/agents/72-integration-testing-agent.d.ts +13 -0
- package/dist/agents/72-integration-testing-agent.d.ts.map +1 -0
- package/dist/agents/72-integration-testing-agent.js +243 -0
- package/dist/agents/72-integration-testing-agent.js.map +1 -0
- package/dist/agents/73-system-testing-agent.d.ts +11 -0
- package/dist/agents/73-system-testing-agent.d.ts.map +1 -0
- package/dist/agents/73-system-testing-agent.js +175 -0
- package/dist/agents/73-system-testing-agent.js.map +1 -0
- package/dist/agents/74-acceptance-testing-agent.d.ts +13 -0
- package/dist/agents/74-acceptance-testing-agent.d.ts.map +1 -0
- package/dist/agents/74-acceptance-testing-agent.js +254 -0
- package/dist/agents/74-acceptance-testing-agent.js.map +1 -0
- package/dist/agents/75-sanity-testing-agent.d.ts +15 -0
- package/dist/agents/75-sanity-testing-agent.d.ts.map +1 -0
- package/dist/agents/75-sanity-testing-agent.js +240 -0
- package/dist/agents/75-sanity-testing-agent.js.map +1 -0
- package/dist/agents/76-regression-testing-agent.d.ts +14 -0
- package/dist/agents/76-regression-testing-agent.d.ts.map +1 -0
- package/dist/agents/76-regression-testing-agent.js +230 -0
- package/dist/agents/76-regression-testing-agent.js.map +1 -0
- package/dist/agents/77-browser-load-testing-agent.d.ts +11 -0
- package/dist/agents/77-browser-load-testing-agent.d.ts.map +1 -0
- package/dist/agents/77-browser-load-testing-agent.js +128 -0
- package/dist/agents/77-browser-load-testing-agent.js.map +1 -0
- package/dist/agents/78-stress-testing-agent.d.ts +11 -0
- package/dist/agents/78-stress-testing-agent.d.ts.map +1 -0
- package/dist/agents/78-stress-testing-agent.js +146 -0
- package/dist/agents/78-stress-testing-agent.js.map +1 -0
- package/dist/agents/79-endurance-testing-agent.d.ts +12 -0
- package/dist/agents/79-endurance-testing-agent.d.ts.map +1 -0
- package/dist/agents/79-endurance-testing-agent.js +165 -0
- package/dist/agents/79-endurance-testing-agent.js.map +1 -0
- package/dist/agents/80-usability-testing-agent.d.ts +11 -0
- package/dist/agents/80-usability-testing-agent.d.ts.map +1 -0
- package/dist/agents/80-usability-testing-agent.js +196 -0
- package/dist/agents/80-usability-testing-agent.js.map +1 -0
- package/dist/agents/81-compatibility-testing-agent.d.ts +11 -0
- package/dist/agents/81-compatibility-testing-agent.d.ts.map +1 -0
- package/dist/agents/81-compatibility-testing-agent.js +224 -0
- package/dist/agents/81-compatibility-testing-agent.js.map +1 -0
- package/dist/agents/82-exploratory-testing-agent.d.ts +14 -0
- package/dist/agents/82-exploratory-testing-agent.d.ts.map +1 -0
- package/dist/agents/82-exploratory-testing-agent.js +345 -0
- package/dist/agents/82-exploratory-testing-agent.js.map +1 -0
- package/dist/agents/83-static-analysis-agent.d.ts +14 -0
- package/dist/agents/83-static-analysis-agent.d.ts.map +1 -0
- package/dist/agents/83-static-analysis-agent.js +261 -0
- package/dist/agents/83-static-analysis-agent.js.map +1 -0
- package/dist/agents/84-governance-testing-agent.d.ts +28 -0
- package/dist/agents/84-governance-testing-agent.d.ts.map +1 -0
- package/dist/agents/84-governance-testing-agent.js +591 -0
- package/dist/agents/84-governance-testing-agent.js.map +1 -0
- package/dist/agents/85-stagehand-agent.d.ts +22 -0
- package/dist/agents/85-stagehand-agent.d.ts.map +1 -0
- package/dist/agents/85-stagehand-agent.js +81 -0
- package/dist/agents/85-stagehand-agent.js.map +1 -0
- package/dist/agents/86-browser-use-agent.d.ts +31 -0
- package/dist/agents/86-browser-use-agent.d.ts.map +1 -0
- package/dist/agents/86-browser-use-agent.js +121 -0
- package/dist/agents/86-browser-use-agent.js.map +1 -0
- package/dist/agents/87-connection-mapper.d.ts +93 -0
- package/dist/agents/87-connection-mapper.d.ts.map +1 -0
- package/dist/agents/87-connection-mapper.js +658 -0
- package/dist/agents/87-connection-mapper.js.map +1 -0
- package/dist/agents/88-localhost-walkthrough.d.ts +272 -0
- package/dist/agents/88-localhost-walkthrough.d.ts.map +1 -0
- package/dist/agents/88-localhost-walkthrough.js +1203 -0
- package/dist/agents/88-localhost-walkthrough.js.map +1 -0
- package/dist/agents/89-repair-retest.d.ts +63 -0
- package/dist/agents/89-repair-retest.d.ts.map +1 -0
- package/dist/agents/89-repair-retest.js +227 -0
- package/dist/agents/89-repair-retest.js.map +1 -0
- package/dist/agents/90-response-shape-validator.d.ts +35 -0
- package/dist/agents/90-response-shape-validator.d.ts.map +1 -0
- package/dist/agents/90-response-shape-validator.js +156 -0
- package/dist/agents/90-response-shape-validator.js.map +1 -0
- package/dist/agents/91-boundary-fuzzer.d.ts +99 -0
- package/dist/agents/91-boundary-fuzzer.d.ts.map +1 -0
- package/dist/agents/91-boundary-fuzzer.js +0 -0
- package/dist/agents/91-boundary-fuzzer.js.map +1 -0
- package/dist/agents/92-repair-simulator.d.ts +89 -0
- package/dist/agents/92-repair-simulator.d.ts.map +1 -0
- package/dist/agents/92-repair-simulator.js +401 -0
- package/dist/agents/92-repair-simulator.js.map +1 -0
- package/dist/agents/93-env-var-auditor.d.ts +64 -0
- package/dist/agents/93-env-var-auditor.d.ts.map +1 -0
- package/dist/agents/93-env-var-auditor.js +435 -0
- package/dist/agents/93-env-var-auditor.js.map +1 -0
- package/dist/agents/94-schema-validator.d.ts +148 -0
- package/dist/agents/94-schema-validator.d.ts.map +1 -0
- package/dist/agents/94-schema-validator.js +567 -0
- package/dist/agents/94-schema-validator.js.map +1 -0
- package/dist/agents/95-contract-drift.d.ts +87 -0
- package/dist/agents/95-contract-drift.d.ts.map +1 -0
- package/dist/agents/95-contract-drift.js +335 -0
- package/dist/agents/95-contract-drift.js.map +1 -0
- package/dist/agents/96-cookie-security-auditor.d.ts +86 -0
- package/dist/agents/96-cookie-security-auditor.d.ts.map +1 -0
- package/dist/agents/96-cookie-security-auditor.js +339 -0
- package/dist/agents/96-cookie-security-auditor.js.map +1 -0
- package/dist/agents/97-healthcheck-validator.d.ts +62 -0
- package/dist/agents/97-healthcheck-validator.d.ts.map +1 -0
- package/dist/agents/97-healthcheck-validator.js +204 -0
- package/dist/agents/97-healthcheck-validator.js.map +1 -0
- package/dist/agents/98-cors-csp-auditor.d.ts +70 -0
- package/dist/agents/98-cors-csp-auditor.d.ts.map +1 -0
- package/dist/agents/98-cors-csp-auditor.js +308 -0
- package/dist/agents/98-cors-csp-auditor.js.map +1 -0
- package/dist/agents/99-logging-hygiene-auditor.d.ts +67 -0
- package/dist/agents/99-logging-hygiene-auditor.d.ts.map +1 -0
- package/dist/agents/99-logging-hygiene-auditor.js +325 -0
- package/dist/agents/99-logging-hygiene-auditor.js.map +1 -0
- package/dist/agents/base-agent.d.ts +74 -4
- package/dist/agents/base-agent.d.ts.map +1 -1
- package/dist/agents/base-agent.js +106 -1
- package/dist/agents/base-agent.js.map +1 -1
- package/dist/agents/browser-use-client.d.ts +68 -0
- package/dist/agents/browser-use-client.d.ts.map +1 -0
- package/dist/agents/browser-use-client.js +92 -0
- package/dist/agents/browser-use-client.js.map +1 -0
- package/dist/agents/lib/source-scan.d.ts +53 -0
- package/dist/agents/lib/source-scan.d.ts.map +1 -0
- package/dist/agents/lib/source-scan.js +279 -0
- package/dist/agents/lib/source-scan.js.map +1 -0
- package/dist/agents/registry.d.ts +27 -9
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +365 -151
- package/dist/agents/registry.js.map +1 -1
- package/dist/agents/stagehand-runner.d.ts +104 -0
- package/dist/agents/stagehand-runner.d.ts.map +1 -0
- package/dist/agents/stagehand-runner.js +153 -0
- package/dist/agents/stagehand-runner.js.map +1 -0
- package/dist/bridge/agent-registry.d.ts +21 -0
- package/dist/bridge/agent-registry.d.ts.map +1 -0
- package/dist/bridge/agent-registry.js +224 -0
- package/dist/bridge/agent-registry.js.map +1 -0
- package/dist/bridge/api-contract-reader.d.ts +55 -0
- package/dist/bridge/api-contract-reader.d.ts.map +1 -0
- package/dist/bridge/api-contract-reader.js +103 -0
- package/dist/bridge/api-contract-reader.js.map +1 -0
- package/dist/bridge/compliance-reader.d.ts +47 -0
- package/dist/bridge/compliance-reader.d.ts.map +1 -0
- package/dist/bridge/compliance-reader.js +91 -0
- package/dist/bridge/compliance-reader.js.map +1 -0
- package/dist/bridge/data-integrity-reader.d.ts +77 -0
- package/dist/bridge/data-integrity-reader.d.ts.map +1 -0
- package/dist/bridge/data-integrity-reader.js +110 -0
- package/dist/bridge/data-integrity-reader.js.map +1 -0
- package/dist/bridge/design-reader.d.ts +51 -0
- package/dist/bridge/design-reader.d.ts.map +1 -0
- package/dist/bridge/design-reader.js +105 -0
- package/dist/bridge/design-reader.js.map +1 -0
- package/dist/bridge/file-scanner.d.ts +21 -0
- package/dist/bridge/file-scanner.d.ts.map +1 -0
- package/dist/bridge/file-scanner.js +117 -0
- package/dist/bridge/file-scanner.js.map +1 -0
- package/dist/bridge/finding-normalize.d.ts +24 -0
- package/dist/bridge/finding-normalize.d.ts.map +1 -0
- package/dist/bridge/finding-normalize.js +46 -0
- package/dist/bridge/finding-normalize.js.map +1 -0
- package/dist/bridge/http-client.d.ts +44 -0
- package/dist/bridge/http-client.d.ts.map +1 -0
- package/dist/bridge/http-client.js +130 -0
- package/dist/bridge/http-client.js.map +1 -0
- package/dist/bridge/knowledge-reader.d.ts +10 -0
- package/dist/bridge/knowledge-reader.d.ts.map +1 -0
- package/dist/bridge/knowledge-reader.js +46 -0
- package/dist/bridge/knowledge-reader.js.map +1 -0
- package/dist/bridge/loop-engine-reader.d.ts +77 -0
- package/dist/bridge/loop-engine-reader.d.ts.map +1 -0
- package/dist/bridge/loop-engine-reader.js +73 -0
- package/dist/bridge/loop-engine-reader.js.map +1 -0
- package/dist/bridge/playwright-pool.d.ts +33 -0
- package/dist/bridge/playwright-pool.d.ts.map +1 -0
- package/dist/bridge/playwright-pool.js +89 -0
- package/dist/bridge/playwright-pool.js.map +1 -0
- package/dist/bridge/rate-limiter.d.ts +40 -0
- package/dist/bridge/rate-limiter.d.ts.map +1 -0
- package/dist/bridge/rate-limiter.js +33 -0
- package/dist/bridge/rate-limiter.js.map +1 -0
- package/dist/bridge/reliability-reader.d.ts +67 -0
- package/dist/bridge/reliability-reader.d.ts.map +1 -0
- package/dist/bridge/reliability-reader.js +146 -0
- package/dist/bridge/reliability-reader.js.map +1 -0
- package/dist/bridge/router.d.ts +26 -0
- package/dist/bridge/router.d.ts.map +1 -0
- package/dist/bridge/router.js +137 -0
- package/dist/bridge/router.js.map +1 -0
- package/dist/bridge/run-stream.d.ts +47 -0
- package/dist/bridge/run-stream.d.ts.map +1 -0
- package/dist/bridge/run-stream.js +67 -0
- package/dist/bridge/run-stream.js.map +1 -0
- package/dist/bridge/runs-reader.d.ts +41 -0
- package/dist/bridge/runs-reader.d.ts.map +1 -0
- package/dist/bridge/runs-reader.js +185 -0
- package/dist/bridge/runs-reader.js.map +1 -0
- package/dist/bridge/sentinel-reader.d.ts +55 -0
- package/dist/bridge/sentinel-reader.d.ts.map +1 -0
- package/dist/bridge/sentinel-reader.js +88 -0
- package/dist/bridge/sentinel-reader.js.map +1 -0
- package/dist/bridge/server.d.ts +83 -0
- package/dist/bridge/server.d.ts.map +1 -0
- package/dist/bridge/server.js +1103 -0
- package/dist/bridge/server.js.map +1 -0
- package/dist/bridge/shell-executor.d.ts +49 -0
- package/dist/bridge/shell-executor.d.ts.map +1 -0
- package/dist/bridge/shell-executor.js +181 -0
- package/dist/bridge/shell-executor.js.map +1 -0
- package/dist/bridge/tech-debt-reader.d.ts +57 -0
- package/dist/bridge/tech-debt-reader.d.ts.map +1 -0
- package/dist/bridge/tech-debt-reader.js +119 -0
- package/dist/bridge/tech-debt-reader.js.map +1 -0
- package/dist/bridge/types.d.ts +63 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +7 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/clients/agent-mvp.d.ts +3 -1
- package/dist/clients/agent-mvp.d.ts.map +1 -1
- package/dist/clients/agent-mvp.js +16 -5
- package/dist/clients/agent-mvp.js.map +1 -1
- package/dist/clients/llm-council.d.ts +47 -0
- package/dist/clients/llm-council.d.ts.map +1 -0
- package/dist/clients/llm-council.js +52 -0
- package/dist/clients/llm-council.js.map +1 -0
- package/dist/clients/total-recall.d.ts +2 -2
- package/dist/clients/total-recall.d.ts.map +1 -1
- package/dist/clients/total-recall.js +18 -3
- package/dist/clients/total-recall.js.map +1 -1
- package/dist/core/agent-contract.d.ts +21 -0
- package/dist/core/agent-contract.d.ts.map +1 -0
- package/dist/core/agent-contract.js +18 -0
- package/dist/core/agent-contract.js.map +1 -0
- package/dist/core/api-contract/api-contract-validator.d.ts +178 -0
- package/dist/core/api-contract/api-contract-validator.d.ts.map +1 -0
- package/dist/core/api-contract/api-contract-validator.js +796 -0
- package/dist/core/api-contract/api-contract-validator.js.map +1 -0
- package/dist/core/api-contract/index.d.ts +16 -0
- package/dist/core/api-contract/index.d.ts.map +1 -0
- package/dist/core/api-contract/index.js +24 -0
- package/dist/core/api-contract/index.js.map +1 -0
- package/dist/core/api-contract/types.d.ts +235 -0
- package/dist/core/api-contract/types.d.ts.map +1 -0
- package/dist/core/api-contract/types.js +27 -0
- package/dist/core/api-contract/types.js.map +1 -0
- package/dist/core/blackboard/blackboard.d.ts +34 -0
- package/dist/core/blackboard/blackboard.d.ts.map +1 -0
- package/dist/core/blackboard/blackboard.js +133 -0
- package/dist/core/blackboard/blackboard.js.map +1 -0
- package/dist/core/blackboard/coordination.d.ts +27 -0
- package/dist/core/blackboard/coordination.d.ts.map +1 -0
- package/dist/core/blackboard/coordination.js +31 -0
- package/dist/core/blackboard/coordination.js.map +1 -0
- package/dist/core/blackboard/direct-channel.d.ts +26 -0
- package/dist/core/blackboard/direct-channel.d.ts.map +1 -0
- package/dist/core/blackboard/direct-channel.js +26 -0
- package/dist/core/blackboard/direct-channel.js.map +1 -0
- package/dist/core/blackboard/index.d.ts +10 -0
- package/dist/core/blackboard/index.d.ts.map +1 -0
- package/dist/core/blackboard/index.js +4 -0
- package/dist/core/blackboard/index.js.map +1 -0
- package/dist/core/blackboard/types.d.ts +36 -0
- package/dist/core/blackboard/types.d.ts.map +1 -0
- package/dist/core/blackboard/types.js +2 -0
- package/dist/core/blackboard/types.js.map +1 -0
- package/dist/core/canvas/schema.d.ts +81 -0
- package/dist/core/canvas/schema.d.ts.map +1 -0
- package/dist/core/canvas/schema.js +144 -0
- package/dist/core/canvas/schema.js.map +1 -0
- package/dist/core/canvas/store.d.ts +41 -0
- package/dist/core/canvas/store.d.ts.map +1 -0
- package/dist/core/canvas/store.js +121 -0
- package/dist/core/canvas/store.js.map +1 -0
- package/dist/core/ci-output.d.ts +1 -1
- package/dist/core/ci-output.d.ts.map +1 -1
- package/dist/core/ci-output.js +2 -0
- package/dist/core/ci-output.js.map +1 -1
- package/dist/core/cli.d.ts +12 -1
- package/dist/core/cli.d.ts.map +1 -1
- package/dist/core/cli.js +308 -43
- package/dist/core/cli.js.map +1 -1
- package/dist/core/compliance/auditor.d.ts +119 -0
- package/dist/core/compliance/auditor.d.ts.map +1 -0
- package/dist/core/compliance/auditor.js +577 -0
- package/dist/core/compliance/auditor.js.map +1 -0
- package/dist/core/compliance/index.d.ts +11 -0
- package/dist/core/compliance/index.d.ts.map +1 -0
- package/dist/core/compliance/index.js +10 -0
- package/dist/core/compliance/index.js.map +1 -0
- package/dist/core/compliance/types.d.ts +174 -0
- package/dist/core/compliance/types.d.ts.map +1 -0
- package/dist/core/compliance/types.js +12 -0
- package/dist/core/compliance/types.js.map +1 -0
- package/dist/core/conductor/conductor.d.ts +37 -0
- package/dist/core/conductor/conductor.d.ts.map +1 -0
- package/dist/core/conductor/conductor.js +96 -0
- package/dist/core/conductor/conductor.js.map +1 -0
- package/dist/core/conductor/index.d.ts +9 -0
- package/dist/core/conductor/index.d.ts.map +1 -0
- package/dist/core/conductor/index.js +3 -0
- package/dist/core/conductor/index.js.map +1 -0
- package/dist/core/conductor/model-router.d.ts +17 -0
- package/dist/core/conductor/model-router.d.ts.map +1 -0
- package/dist/core/conductor/model-router.js +29 -0
- package/dist/core/conductor/model-router.js.map +1 -0
- package/dist/core/conductor/types.d.ts +33 -0
- package/dist/core/conductor/types.d.ts.map +1 -0
- package/dist/core/conductor/types.js +2 -0
- package/dist/core/conductor/types.js.map +1 -0
- package/dist/core/config.d.ts +148 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +53 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/data-integrity/data-integrity.d.ts +291 -0
- package/dist/core/data-integrity/data-integrity.d.ts.map +1 -0
- package/dist/core/data-integrity/data-integrity.js +892 -0
- package/dist/core/data-integrity/data-integrity.js.map +1 -0
- package/dist/core/data-integrity/index.d.ts +16 -0
- package/dist/core/data-integrity/index.d.ts.map +1 -0
- package/dist/core/data-integrity/index.js +17 -0
- package/dist/core/data-integrity/index.js.map +1 -0
- package/dist/core/data-integrity/types.d.ts +236 -0
- package/dist/core/data-integrity/types.d.ts.map +1 -0
- package/dist/core/data-integrity/types.js +14 -0
- package/dist/core/data-integrity/types.js.map +1 -0
- package/dist/core/disaster-recovery/index.d.ts +13 -0
- package/dist/core/disaster-recovery/index.d.ts.map +1 -0
- package/dist/core/disaster-recovery/index.js +3 -0
- package/dist/core/disaster-recovery/index.js.map +1 -0
- package/dist/core/disaster-recovery/simulator.d.ts +158 -0
- package/dist/core/disaster-recovery/simulator.d.ts.map +1 -0
- package/dist/core/disaster-recovery/simulator.js +553 -0
- package/dist/core/disaster-recovery/simulator.js.map +1 -0
- package/dist/core/disaster-recovery/types.d.ts +299 -0
- package/dist/core/disaster-recovery/types.d.ts.map +1 -0
- package/dist/core/disaster-recovery/types.js +33 -0
- package/dist/core/disaster-recovery/types.js.map +1 -0
- package/dist/core/escalation/heal-or-ask.d.ts +20 -0
- package/dist/core/escalation/heal-or-ask.d.ts.map +1 -0
- package/dist/core/escalation/heal-or-ask.js +19 -0
- package/dist/core/escalation/heal-or-ask.js.map +1 -0
- package/dist/core/escalation/index.d.ts +9 -0
- package/dist/core/escalation/index.d.ts.map +1 -0
- package/dist/core/escalation/index.js +3 -0
- package/dist/core/escalation/index.js.map +1 -0
- package/dist/core/escalation/pause-gate.d.ts +48 -0
- package/dist/core/escalation/pause-gate.d.ts.map +1 -0
- package/dist/core/escalation/pause-gate.js +96 -0
- package/dist/core/escalation/pause-gate.js.map +1 -0
- package/dist/core/escalation/types.d.ts +33 -0
- package/dist/core/escalation/types.d.ts.map +1 -0
- package/dist/core/escalation/types.js +9 -0
- package/dist/core/escalation/types.js.map +1 -0
- package/dist/core/evidence.d.ts +32 -1
- package/dist/core/evidence.d.ts.map +1 -1
- package/dist/core/evidence.js +99 -1
- package/dist/core/evidence.js.map +1 -1
- package/dist/core/feature-bdd/fix.d.ts +84 -0
- package/dist/core/feature-bdd/fix.d.ts.map +1 -0
- package/dist/core/feature-bdd/fix.js +121 -0
- package/dist/core/feature-bdd/fix.js.map +1 -0
- package/dist/core/feature-bdd/generate.d.ts +96 -0
- package/dist/core/feature-bdd/generate.d.ts.map +1 -0
- package/dist/core/feature-bdd/generate.js +228 -0
- package/dist/core/feature-bdd/generate.js.map +1 -0
- package/dist/core/feature-bdd/llm-provider.d.ts +92 -0
- package/dist/core/feature-bdd/llm-provider.d.ts.map +1 -0
- package/dist/core/feature-bdd/llm-provider.js +187 -0
- package/dist/core/feature-bdd/llm-provider.js.map +1 -0
- package/dist/core/feature-bdd/run.d.ts +56 -0
- package/dist/core/feature-bdd/run.d.ts.map +1 -0
- package/dist/core/feature-bdd/run.js +175 -0
- package/dist/core/feature-bdd/run.js.map +1 -0
- package/dist/core/feature-bdd/schema.d.ts +111 -0
- package/dist/core/feature-bdd/schema.d.ts.map +1 -0
- package/dist/core/feature-bdd/schema.js +272 -0
- package/dist/core/feature-bdd/schema.js.map +1 -0
- package/dist/core/feature-bdd/store.d.ts +145 -0
- package/dist/core/feature-bdd/store.d.ts.map +1 -0
- package/dist/core/feature-bdd/store.js +470 -0
- package/dist/core/feature-bdd/store.js.map +1 -0
- package/dist/core/finding-correlation.d.ts +55 -0
- package/dist/core/finding-correlation.d.ts.map +1 -0
- package/dist/core/finding-correlation.js +96 -0
- package/dist/core/finding-correlation.js.map +1 -0
- package/dist/core/fix-loop.d.ts +20 -1
- package/dist/core/fix-loop.d.ts.map +1 -1
- package/dist/core/fix-loop.js +34 -0
- package/dist/core/fix-loop.js.map +1 -1
- package/dist/core/governance/calibration.d.ts +31 -0
- package/dist/core/governance/calibration.d.ts.map +1 -0
- package/dist/core/governance/calibration.js +78 -0
- package/dist/core/governance/calibration.js.map +1 -0
- package/dist/core/governance/degradation.d.ts +35 -0
- package/dist/core/governance/degradation.d.ts.map +1 -0
- package/dist/core/governance/degradation.js +25 -0
- package/dist/core/governance/degradation.js.map +1 -0
- package/dist/core/governance/ethical-constraint.d.ts +55 -0
- package/dist/core/governance/ethical-constraint.d.ts.map +1 -0
- package/dist/core/governance/ethical-constraint.js +98 -0
- package/dist/core/governance/ethical-constraint.js.map +1 -0
- package/dist/core/governance/index.d.ts +9 -0
- package/dist/core/governance/index.d.ts.map +1 -0
- package/dist/core/governance/index.js +9 -0
- package/dist/core/governance/index.js.map +1 -0
- package/dist/core/harness/audit-log.d.ts +12 -0
- package/dist/core/harness/audit-log.d.ts.map +1 -0
- package/dist/core/harness/audit-log.js +62 -0
- package/dist/core/harness/audit-log.js.map +1 -0
- package/dist/core/harness/authorization.d.ts +24 -0
- package/dist/core/harness/authorization.d.ts.map +1 -0
- package/dist/core/harness/authorization.js +48 -0
- package/dist/core/harness/authorization.js.map +1 -0
- package/dist/core/harness/harness.d.ts +64 -0
- package/dist/core/harness/harness.d.ts.map +1 -0
- package/dist/core/harness/harness.js +188 -0
- package/dist/core/harness/harness.js.map +1 -0
- package/dist/core/harness/index.d.ts +10 -0
- package/dist/core/harness/index.d.ts.map +1 -0
- package/dist/core/harness/index.js +4 -0
- package/dist/core/harness/index.js.map +1 -0
- package/dist/core/harness/types.d.ts +88 -0
- package/dist/core/harness/types.d.ts.map +1 -0
- package/dist/core/harness/types.js +2 -0
- package/dist/core/harness/types.js.map +1 -0
- package/dist/core/health-check.d.ts +6 -0
- package/dist/core/health-check.d.ts.map +1 -1
- package/dist/core/health-check.js +14 -2
- package/dist/core/health-check.js.map +1 -1
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +58 -18
- package/dist/core/init.js.map +1 -1
- package/dist/core/knowledge/cached-map.d.ts +17 -0
- package/dist/core/knowledge/cached-map.d.ts.map +1 -0
- package/dist/core/knowledge/cached-map.js +23 -0
- package/dist/core/knowledge/cached-map.js.map +1 -0
- package/dist/core/knowledge/index.d.ts +10 -0
- package/dist/core/knowledge/index.d.ts.map +1 -0
- package/dist/core/knowledge/index.js +4 -0
- package/dist/core/knowledge/index.js.map +1 -0
- package/dist/core/knowledge/system-map.d.ts +50 -0
- package/dist/core/knowledge/system-map.d.ts.map +1 -0
- package/dist/core/knowledge/system-map.js +121 -0
- package/dist/core/knowledge/system-map.js.map +1 -0
- package/dist/core/knowledge/traversal.d.ts +12 -0
- package/dist/core/knowledge/traversal.d.ts.map +1 -0
- package/dist/core/knowledge/traversal.js +37 -0
- package/dist/core/knowledge/traversal.js.map +1 -0
- package/dist/core/knowledge/types.d.ts +41 -0
- package/dist/core/knowledge/types.d.ts.map +1 -0
- package/dist/core/knowledge/types.js +2 -0
- package/dist/core/knowledge/types.js.map +1 -0
- package/dist/core/license-gen.d.ts +1 -1
- package/dist/core/license-gen.d.ts.map +1 -1
- package/dist/core/license-gen.js +10 -5
- package/dist/core/license-gen.js.map +1 -1
- package/dist/core/license.d.ts +12 -2
- package/dist/core/license.d.ts.map +1 -1
- package/dist/core/license.js +104 -28
- package/dist/core/license.js.map +1 -1
- package/dist/core/loop-engine/circuit-breaker.d.ts +24 -0
- package/dist/core/loop-engine/circuit-breaker.d.ts.map +1 -0
- package/dist/core/loop-engine/circuit-breaker.js +48 -0
- package/dist/core/loop-engine/circuit-breaker.js.map +1 -0
- package/dist/core/loop-engine/demo.d.ts +35 -0
- package/dist/core/loop-engine/demo.d.ts.map +1 -0
- package/dist/core/loop-engine/demo.js +71 -0
- package/dist/core/loop-engine/demo.js.map +1 -0
- package/dist/core/loop-engine/event-store.d.ts +8 -0
- package/dist/core/loop-engine/event-store.d.ts.map +1 -0
- package/dist/core/loop-engine/event-store.js +9 -0
- package/dist/core/loop-engine/event-store.js.map +1 -0
- package/dist/core/loop-engine/index.d.ts +11 -0
- package/dist/core/loop-engine/index.d.ts.map +1 -0
- package/dist/core/loop-engine/index.js +11 -0
- package/dist/core/loop-engine/index.js.map +1 -0
- package/dist/core/loop-engine/kernel.d.ts +66 -0
- package/dist/core/loop-engine/kernel.d.ts.map +1 -0
- package/dist/core/loop-engine/kernel.js +196 -0
- package/dist/core/loop-engine/kernel.js.map +1 -0
- package/dist/core/loop-engine/tracing.d.ts +12 -0
- package/dist/core/loop-engine/tracing.d.ts.map +1 -0
- package/dist/core/loop-engine/tracing.js +15 -0
- package/dist/core/loop-engine/tracing.js.map +1 -0
- package/dist/core/loop-engine/types.d.ts +92 -0
- package/dist/core/loop-engine/types.d.ts.map +1 -0
- package/dist/core/loop-engine/types.js +21 -0
- package/dist/core/loop-engine/types.js.map +1 -0
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +101 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/orchestrator.d.ts +79 -8
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +340 -33
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/phase-gate.d.ts +2 -2
- package/dist/core/quality-score/calculator.d.ts +125 -0
- package/dist/core/quality-score/calculator.d.ts.map +1 -0
- package/dist/core/quality-score/calculator.js +489 -0
- package/dist/core/quality-score/calculator.js.map +1 -0
- package/dist/core/quality-score/from-run.d.ts +27 -0
- package/dist/core/quality-score/from-run.d.ts.map +1 -0
- package/dist/core/quality-score/from-run.js +64 -0
- package/dist/core/quality-score/from-run.js.map +1 -0
- package/dist/core/quality-score/index.d.ts +9 -0
- package/dist/core/quality-score/index.d.ts.map +1 -0
- package/dist/core/quality-score/index.js +9 -0
- package/dist/core/quality-score/index.js.map +1 -0
- package/dist/core/quality-score/types.d.ts +225 -0
- package/dist/core/quality-score/types.d.ts.map +1 -0
- package/dist/core/quality-score/types.js +26 -0
- package/dist/core/quality-score/types.js.map +1 -0
- package/dist/core/report-html-script.d.ts +3 -0
- package/dist/core/report-html-script.d.ts.map +1 -0
- package/dist/core/report-html-script.js +47 -0
- package/dist/core/report-html-script.js.map +1 -0
- package/dist/core/report-html-styles.d.ts +3 -0
- package/dist/core/report-html-styles.d.ts.map +1 -0
- package/dist/core/report-html-styles.js +231 -0
- package/dist/core/report-html-styles.js.map +1 -0
- package/dist/core/report-html.d.ts +1 -1
- package/dist/core/report-html.d.ts.map +1 -1
- package/dist/core/report-html.js +5 -280
- package/dist/core/report-html.js.map +1 -1
- package/dist/core/report-upload.d.ts +8 -0
- package/dist/core/report-upload.d.ts.map +1 -1
- package/dist/core/report-upload.js +17 -4
- package/dist/core/report-upload.js.map +1 -1
- package/dist/core/run-counter.d.ts.map +1 -1
- package/dist/core/run-counter.js +25 -1
- package/dist/core/run-counter.js.map +1 -1
- package/dist/core/run-events/emitter.d.ts +112 -0
- package/dist/core/run-events/emitter.d.ts.map +1 -0
- package/dist/core/run-events/emitter.js +234 -0
- package/dist/core/run-events/emitter.js.map +1 -0
- package/dist/core/run-events/frame-sink.d.ts +24 -0
- package/dist/core/run-events/frame-sink.d.ts.map +1 -0
- package/dist/core/run-events/frame-sink.js +32 -0
- package/dist/core/run-events/frame-sink.js.map +1 -0
- package/dist/core/run-events/index.d.ts +7 -0
- package/dist/core/run-events/index.d.ts.map +1 -0
- package/dist/core/run-events/index.js +5 -0
- package/dist/core/run-events/index.js.map +1 -0
- package/dist/core/run-events/loop-event-sink.d.ts +56 -0
- package/dist/core/run-events/loop-event-sink.d.ts.map +1 -0
- package/dist/core/run-events/loop-event-sink.js +60 -0
- package/dist/core/run-events/loop-event-sink.js.map +1 -0
- package/dist/core/run-events/sse.d.ts +47 -0
- package/dist/core/run-events/sse.d.ts.map +1 -0
- package/dist/core/run-events/sse.js +64 -0
- package/dist/core/run-events/sse.js.map +1 -0
- package/dist/core/run-events/types.d.ts +147 -0
- package/dist/core/run-events/types.d.ts.map +1 -0
- package/dist/core/run-events/types.js +17 -0
- package/dist/core/run-events/types.js.map +1 -0
- package/dist/core/run-mode/capture.d.ts +37 -0
- package/dist/core/run-mode/capture.d.ts.map +1 -0
- package/dist/core/run-mode/capture.js +43 -0
- package/dist/core/run-mode/capture.js.map +1 -0
- package/dist/core/run-mode/index.d.ts +9 -0
- package/dist/core/run-mode/index.d.ts.map +1 -0
- package/dist/core/run-mode/index.js +3 -0
- package/dist/core/run-mode/index.js.map +1 -0
- package/dist/core/run-mode/run-mode.d.ts +35 -0
- package/dist/core/run-mode/run-mode.d.ts.map +1 -0
- package/dist/core/run-mode/run-mode.js +51 -0
- package/dist/core/run-mode/run-mode.js.map +1 -0
- package/dist/core/run-mode/types.d.ts +36 -0
- package/dist/core/run-mode/types.d.ts.map +1 -0
- package/dist/core/run-mode/types.js +15 -0
- package/dist/core/run-mode/types.js.map +1 -0
- package/dist/core/run-quota.d.ts +22 -0
- package/dist/core/run-quota.d.ts.map +1 -0
- package/dist/core/run-quota.js +44 -0
- package/dist/core/run-quota.js.map +1 -0
- package/dist/core/security-audit/index.d.ts +9 -0
- package/dist/core/security-audit/index.d.ts.map +1 -0
- package/dist/core/security-audit/index.js +10 -0
- package/dist/core/security-audit/index.js.map +1 -0
- package/dist/core/security-audit/sentinel.d.ts +196 -0
- package/dist/core/security-audit/sentinel.d.ts.map +1 -0
- package/dist/core/security-audit/sentinel.js +725 -0
- package/dist/core/security-audit/sentinel.js.map +1 -0
- package/dist/core/security-audit/types.d.ts +240 -0
- package/dist/core/security-audit/types.d.ts.map +1 -0
- package/dist/core/security-audit/types.js +42 -0
- package/dist/core/security-audit/types.js.map +1 -0
- package/dist/core/tech-debt/index.d.ts +11 -0
- package/dist/core/tech-debt/index.d.ts.map +1 -0
- package/dist/core/tech-debt/index.js +11 -0
- package/dist/core/tech-debt/index.js.map +1 -0
- package/dist/core/tech-debt/tech-debt-tracker.d.ts +46 -0
- package/dist/core/tech-debt/tech-debt-tracker.d.ts.map +1 -0
- package/dist/core/tech-debt/tech-debt-tracker.js +533 -0
- package/dist/core/tech-debt/tech-debt-tracker.js.map +1 -0
- package/dist/core/tech-debt/types.d.ts +263 -0
- package/dist/core/tech-debt/types.d.ts.map +1 -0
- package/dist/core/tech-debt/types.js +2 -0
- package/dist/core/tech-debt/types.js.map +1 -0
- package/dist/core/tester/diff-planner.d.ts +18 -0
- package/dist/core/tester/diff-planner.d.ts.map +1 -0
- package/dist/core/tester/diff-planner.js +37 -0
- package/dist/core/tester/diff-planner.js.map +1 -0
- package/dist/core/tester/honest-report.d.ts +13 -0
- package/dist/core/tester/honest-report.d.ts.map +1 -0
- package/dist/core/tester/honest-report.js +64 -0
- package/dist/core/tester/honest-report.js.map +1 -0
- package/dist/core/tester/index.d.ts +9 -0
- package/dist/core/tester/index.d.ts.map +1 -0
- package/dist/core/tester/index.js +3 -0
- package/dist/core/tester/index.js.map +1 -0
- package/dist/core/tester/types.d.ts +55 -0
- package/dist/core/tester/types.d.ts.map +1 -0
- package/dist/core/tester/types.js +8 -0
- package/dist/core/tester/types.js.map +1 -0
- package/dist/core/triggers/index.d.ts +9 -0
- package/dist/core/triggers/index.d.ts.map +1 -0
- package/dist/core/triggers/index.js +3 -0
- package/dist/core/triggers/index.js.map +1 -0
- package/dist/core/triggers/trigger-bus.d.ts +49 -0
- package/dist/core/triggers/trigger-bus.d.ts.map +1 -0
- package/dist/core/triggers/trigger-bus.js +167 -0
- package/dist/core/triggers/trigger-bus.js.map +1 -0
- package/dist/core/triggers/types.d.ts +56 -0
- package/dist/core/triggers/types.d.ts.map +1 -0
- package/dist/core/triggers/types.js +13 -0
- package/dist/core/triggers/types.js.map +1 -0
- package/dist/core/trust.d.ts +12 -0
- package/dist/core/trust.d.ts.map +1 -0
- package/dist/core/trust.js +13 -0
- package/dist/core/trust.js.map +1 -0
- package/dist/core/types.d.ts +24 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/ui-ux/index.d.ts +12 -0
- package/dist/core/ui-ux/index.d.ts.map +1 -0
- package/dist/core/ui-ux/index.js +13 -0
- package/dist/core/ui-ux/index.js.map +1 -0
- package/dist/core/ui-ux/orchestrator.d.ts +206 -0
- package/dist/core/ui-ux/orchestrator.d.ts.map +1 -0
- package/dist/core/ui-ux/orchestrator.js +672 -0
- package/dist/core/ui-ux/orchestrator.js.map +1 -0
- package/dist/core/ui-ux/types.d.ts +339 -0
- package/dist/core/ui-ux/types.d.ts.map +1 -0
- package/dist/core/ui-ux/types.js +17 -0
- package/dist/core/ui-ux/types.js.map +1 -0
- package/dist/enterprise/audit-trail.d.ts +31 -0
- package/dist/enterprise/audit-trail.d.ts.map +1 -0
- package/dist/enterprise/audit-trail.js +111 -0
- package/dist/enterprise/audit-trail.js.map +1 -0
- package/dist/enterprise/sla.d.ts +26 -0
- package/dist/enterprise/sla.d.ts.map +1 -0
- package/dist/enterprise/sla.js +101 -0
- package/dist/enterprise/sla.js.map +1 -0
- package/dist/helpers/element-discovery.js +1 -1
- package/dist/helpers/element-discovery.js.map +1 -1
- package/dist/helpers/env-resolver.d.ts +2 -2
- package/dist/helpers/quality-gate.d.ts.map +1 -1
- package/dist/helpers/quality-gate.js +21 -3
- package/dist/helpers/quality-gate.js.map +1 -1
- package/dist/helpers/shape-fingerprint.d.ts +18 -0
- package/dist/helpers/shape-fingerprint.d.ts.map +1 -0
- package/dist/helpers/shape-fingerprint.js +40 -0
- package/dist/helpers/shape-fingerprint.js.map +1 -0
- package/dist/sdk/custom-agent.d.ts +51 -0
- package/dist/sdk/custom-agent.d.ts.map +1 -0
- package/dist/sdk/custom-agent.js +94 -0
- package/dist/sdk/custom-agent.js.map +1 -0
- package/dist/sdk/index.d.ts +5 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +3 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/loader.d.ts +28 -0
- package/dist/sdk/loader.d.ts.map +1 -0
- package/dist/sdk/loader.js +140 -0
- package/dist/sdk/loader.js.map +1 -0
- package/package.json +46 -20
- package/agents/01-analyst.ts +0 -100
- package/agents/02-seed-architect.ts +0 -59
- package/agents/03-test-generator.ts +0 -191
- package/agents/04-unit-runner.ts +0 -160
- package/agents/05-browser-crawler.ts +0 -790
- package/agents/06-api-exerciser.ts +0 -311
- package/agents/07-security-scout.ts +0 -188
- package/agents/08-a11y-guardian.ts +0 -212
- package/agents/09-healer.ts +0 -228
- package/agents/10-reporter.ts +0 -266
- package/agents/11-fixer.ts +0 -253
- package/agents/12-ux-inspector.ts +0 -444
- package/agents/13-performance-profiler.ts +0 -271
- package/agents/14-data-integrity-auditor.ts +0 -417
- package/agents/15-regression-sentinel.ts +0 -308
- package/agents/16-chaos-agent.ts +0 -228
- package/agents/17-documentation-validator.ts +0 -266
- package/agents/18-integration-watchdog.ts +0 -178
- package/agents/19-tenant-isolation-auditor.ts +0 -199
- package/agents/20-workflow-completion-tester.ts +0 -203
- package/agents/21-state-session-tester.ts +0 -262
- package/agents/22-email-notification-verifier.ts +0 -244
- package/agents/23-migration-tester.ts +0 -80
- package/agents/24-signup-onboarding-tester.ts +0 -429
- package/agents/25-crud-flow-tester.ts +0 -302
- package/agents/26-form-validator.ts +0 -297
- package/agents/27-search-filter-tester.ts +0 -326
- package/agents/28-navigation-routing-tester.ts +0 -425
- package/agents/29-responsive-interaction-tester.ts +0 -350
- package/agents/30-multi-user-scenario-tester.ts +0 -319
- package/agents/31-load-tester.ts +0 -134
- package/agents/32-memory-leak-detector.ts +0 -194
- package/agents/33-bundle-analyzer.ts +0 -132
- package/agents/34-xss-scanner.ts +0 -191
- package/agents/35-csrf-tester.ts +0 -82
- package/agents/36-auth-fuzzer.ts +0 -194
- package/agents/37-dependency-scanner.ts +0 -176
- package/agents/38-secrets-scanner.ts +0 -137
- package/agents/39-api-contract-tester.ts +0 -199
- package/agents/40-rate-limit-tester.ts +0 -94
- package/agents/41-api-pagination-tester.ts +0 -97
- package/agents/42-graphql-tester.ts +0 -222
- package/agents/43-data-consistency-checker.ts +0 -205
- package/agents/44-backup-recovery-tester.ts +0 -152
- package/agents/45-data-privacy-scanner.ts +0 -125
- package/agents/46-seo-auditor.ts +0 -294
- package/agents/47-social-preview-tester.ts +0 -232
- package/agents/48-lighthouse-auditor.ts +0 -213
- package/agents/49-i18n-tester.ts +0 -198
- package/agents/50-timezone-tester.ts +0 -173
- package/agents/51-error-recovery-tester.ts +0 -155
- package/agents/52-offline-mode-tester.ts +0 -180
- package/agents/53-graceful-degradation-tester.ts +0 -156
- package/agents/54-websocket-tester.ts +0 -151
- package/agents/55-realtime-sync-tester.ts +0 -194
- package/agents/56-file-upload-tester.ts +0 -194
- package/agents/57-export-tester.ts +0 -174
- package/agents/58-payment-flow-tester.ts +0 -183
- package/agents/59-ssl-tls-auditor.ts +0 -141
- package/agents/60-dns-cdn-tester.ts +0 -117
- package/agents/61-docker-health-checker.ts +0 -111
- package/agents/62-env-config-validator.ts +0 -152
- package/agents/63-log-quality-auditor.ts +0 -136
- package/agents/64-analytics-tracker-tester.ts +0 -165
- package/agents/65-gdpr-compliance-tester.ts +0 -215
- package/agents/66-soc2-control-validator.ts +0 -210
- package/agents/67-wcag-aaa-tester.ts +0 -241
- package/agents/68-dead-code-detector.ts +0 -135
- package/agents/69-type-safety-auditor.ts +0 -164
- package/agents/70-complexity-analyzer.ts +0 -179
- package/agents/__tests__/01-analyst.test.ts +0 -188
- package/agents/__tests__/02-seed-architect.test.ts +0 -152
- package/agents/__tests__/03-test-generator-full.test.ts +0 -321
- package/agents/__tests__/03-test-generator.test.ts +0 -318
- package/agents/__tests__/04-unit-runner.test.ts +0 -320
- package/agents/__tests__/05-browser-crawler-beta.test.ts +0 -492
- package/agents/__tests__/05-browser-crawler-release.test.ts +0 -412
- package/agents/__tests__/05-browser-crawler-uat.test.ts +0 -578
- package/agents/__tests__/05-browser-crawler.test.ts +0 -518
- package/agents/__tests__/06-api-exerciser.test.ts +0 -619
- package/agents/__tests__/07-security-scout.test.ts +0 -382
- package/agents/__tests__/08-a11y-guardian.test.ts +0 -530
- package/agents/__tests__/09-healer.test.ts +0 -384
- package/agents/__tests__/10-reporter.test.ts +0 -366
- package/agents/__tests__/11-fixer.test.ts +0 -406
- package/agents/__tests__/12-ux-inspector-extended.test.ts +0 -465
- package/agents/__tests__/12-ux-inspector.test.ts +0 -443
- package/agents/__tests__/13-performance-profiler.test.ts +0 -411
- package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +0 -573
- package/agents/__tests__/14-data-integrity-auditor.test.ts +0 -407
- package/agents/__tests__/15-regression-sentinel.test.ts +0 -657
- package/agents/__tests__/16-chaos-agent.test.ts +0 -427
- package/agents/__tests__/17-documentation-validator.test.ts +0 -402
- package/agents/__tests__/18-integration-watchdog.test.ts +0 -263
- package/agents/__tests__/19-tenant-isolation-auditor.test.ts +0 -400
- package/agents/__tests__/20-workflow-completion-tester.test.ts +0 -586
- package/agents/__tests__/21-state-session-tester.test.ts +0 -374
- package/agents/__tests__/22-email-notification-verifier.test.ts +0 -441
- package/agents/__tests__/23-migration-tester.test.ts +0 -145
- package/agents/__tests__/24-signup-onboarding-tester.test.ts +0 -274
- package/agents/__tests__/25-crud-flow-tester.test.ts +0 -322
- package/agents/__tests__/26-form-validator.test.ts +0 -345
- package/agents/__tests__/27-search-filter-tester.test.ts +0 -311
- package/agents/__tests__/28-navigation-routing-tester.test.ts +0 -328
- package/agents/__tests__/29-responsive-interaction-tester.test.ts +0 -297
- package/agents/__tests__/30-multi-user-scenario-tester.test.ts +0 -328
- package/agents/__tests__/31-load-tester.test.ts +0 -189
- package/agents/__tests__/32-memory-leak-detector.test.ts +0 -251
- package/agents/__tests__/33-bundle-analyzer.test.ts +0 -237
- package/agents/__tests__/34-xss-scanner.test.ts +0 -258
- package/agents/__tests__/35-csrf-tester.test.ts +0 -200
- package/agents/__tests__/36-auth-fuzzer.test.ts +0 -214
- package/agents/__tests__/37-dependency-scanner.test.ts +0 -266
- package/agents/__tests__/38-secrets-scanner.test.ts +0 -224
- package/agents/__tests__/39-api-contract-tester.test.ts +0 -312
- package/agents/__tests__/40-rate-limit-tester.test.ts +0 -192
- package/agents/__tests__/41-api-pagination-tester.test.ts +0 -198
- package/agents/__tests__/42-graphql-tester.test.ts +0 -252
- package/agents/__tests__/43-data-consistency-checker.test.ts +0 -232
- package/agents/__tests__/44-backup-recovery-tester.test.ts +0 -222
- package/agents/__tests__/45-data-privacy-scanner.test.ts +0 -223
- package/agents/__tests__/46-seo-auditor.test.ts +0 -261
- package/agents/__tests__/47-social-preview-tester.test.ts +0 -245
- package/agents/__tests__/48-lighthouse-auditor.test.ts +0 -276
- package/agents/__tests__/49-i18n-tester.test.ts +0 -201
- package/agents/__tests__/50-timezone-tester.test.ts +0 -172
- package/agents/__tests__/51-error-recovery-tester.test.ts +0 -162
- package/agents/__tests__/52-offline-mode-tester.test.ts +0 -164
- package/agents/__tests__/53-graceful-degradation-tester.test.ts +0 -168
- package/agents/__tests__/54-websocket-tester.test.ts +0 -157
- package/agents/__tests__/55-realtime-sync-tester.test.ts +0 -181
- package/agents/__tests__/56-file-upload-tester.test.ts +0 -172
- package/agents/__tests__/57-export-tester.test.ts +0 -169
- package/agents/__tests__/58-payment-flow-tester.test.ts +0 -182
- package/agents/__tests__/59-ssl-tls-auditor.test.ts +0 -179
- package/agents/__tests__/60-dns-cdn-tester.test.ts +0 -176
- package/agents/__tests__/61-docker-health-checker.test.ts +0 -150
- package/agents/__tests__/62-env-config-validator.test.ts +0 -166
- package/agents/__tests__/63-log-quality-auditor.test.ts +0 -175
- package/agents/__tests__/64-analytics-tracker-tester.test.ts +0 -158
- package/agents/__tests__/65-gdpr-compliance-tester.test.ts +0 -174
- package/agents/__tests__/66-soc2-control-validator.test.ts +0 -183
- package/agents/__tests__/67-wcag-aaa-tester.test.ts +0 -190
- package/agents/__tests__/68-dead-code-detector.test.ts +0 -174
- package/agents/__tests__/69-type-safety-auditor.test.ts +0 -173
- package/agents/__tests__/70-complexity-analyzer.test.ts +0 -177
- package/agents/__tests__/base-agent.test.ts +0 -188
- package/agents/__tests__/registry.test.ts +0 -218
- package/agents/base-agent.ts +0 -85
- package/agents/registry.ts +0 -279
- package/baselines/api-schemas/.gitkeep +0 -0
- package/baselines/performance/.gitkeep +0 -0
- package/baselines/screenshots/.gitkeep +0 -0
- package/core/__tests__/ci-output.test.ts +0 -430
- package/core/__tests__/cli.test.ts +0 -387
- package/core/__tests__/config.test.ts +0 -78
- package/core/__tests__/cost-tracker.test.ts +0 -158
- package/core/__tests__/evidence.test.ts +0 -265
- package/core/__tests__/fix-loop.test.ts +0 -210
- package/core/__tests__/health-check.test.ts +0 -44
- package/core/__tests__/init.test.ts +0 -609
- package/core/__tests__/integration.test.ts +0 -204
- package/core/__tests__/license-gen.test.ts +0 -227
- package/core/__tests__/license.test.ts +0 -326
- package/core/__tests__/multi-browser.test.ts +0 -278
- package/core/__tests__/orchestrator.test.ts +0 -520
- package/core/__tests__/phase-gate.test.ts +0 -43
- package/core/__tests__/report-html.test.ts +0 -398
- package/core/__tests__/report-upload.test.ts +0 -325
- package/core/__tests__/run-counter.test.ts +0 -234
- package/core/ci-output.ts +0 -240
- package/core/cli.ts +0 -354
- package/core/config.ts +0 -178
- package/core/cost-tracker.ts +0 -59
- package/core/evidence.ts +0 -132
- package/core/fix-loop.ts +0 -85
- package/core/health-check.ts +0 -54
- package/core/init.ts +0 -546
- package/core/license-gen.ts +0 -212
- package/core/license.ts +0 -208
- package/core/messages.ts +0 -67
- package/core/multi-browser.ts +0 -136
- package/core/orchestrator.ts +0 -427
- package/core/phase-gate.ts +0 -55
- package/core/report-html.ts +0 -657
- package/core/report-upload.ts +0 -188
- package/core/run-counter.ts +0 -175
- package/core/types.ts +0 -57
- package/dist/core/multi-browser.d.ts +0 -36
- package/dist/core/multi-browser.d.ts.map +0 -1
- package/dist/core/multi-browser.js +0 -88
- package/dist/core/multi-browser.js.map +0 -1
- package/helpers/__tests__/api-client.test.ts +0 -199
- package/helpers/__tests__/element-discovery.test.ts +0 -202
- package/helpers/__tests__/form-filler-extended.test.ts +0 -212
- package/helpers/__tests__/form-filler.test.ts +0 -99
- package/helpers/__tests__/modal-handler.test.ts +0 -152
- package/helpers/__tests__/navigation.test.ts +0 -214
- package/helpers/__tests__/quality-gate.test.ts +0 -117
- package/helpers/__tests__/screenshot.test.ts +0 -139
- package/helpers/__tests__/seed-validator.test.ts +0 -114
- package/helpers/api-client.ts +0 -111
- package/helpers/element-discovery.ts +0 -105
- package/helpers/env-resolver.ts +0 -69
- package/helpers/form-filler.ts +0 -126
- package/helpers/modal-handler.ts +0 -108
- package/helpers/navigation.ts +0 -100
- package/helpers/quality-gate.ts +0 -180
- package/helpers/screenshot.ts +0 -111
- package/helpers/seed-validator.ts +0 -70
|
@@ -0,0 +1,1273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-File Taint Tracker — bounded 1-hop interprocedural taint analysis.
|
|
3
|
+
*
|
|
4
|
+
* The single-file security scanners (#107 open-redirect, #108 SQLi,
|
|
5
|
+
* #109 path-traversal, #110 mass-assignment, #111 dynamic-eval) only fire
|
|
6
|
+
* when an attacker-controlled value and a dangerous sink appear in the SAME
|
|
7
|
+
* expression. Real code launders request data through helper functions, so
|
|
8
|
+
* the source and the sink live in different places:
|
|
9
|
+
*
|
|
10
|
+
* // routes/user.ts
|
|
11
|
+
* app.get('/u', (req, res) => res.json(runQuery(req.query.id))); // source
|
|
12
|
+
* // db/helpers.ts
|
|
13
|
+
* export function runQuery(id) { // tainted param
|
|
14
|
+
* return db.$queryRawUnsafe(`SELECT * FROM u WHERE id = ${id}`); // sink
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* Neither line alone trips a line-level scanner. This agent connects them:
|
|
18
|
+
*
|
|
19
|
+
* 1. Find "sink-wrapper" functions: a standalone function (declaration or
|
|
20
|
+
* `const fn = (…) =>`) whose body reaches a dangerous sink
|
|
21
|
+
* (sql / eval / command / fs-path / redirect) using one of its own
|
|
22
|
+
* parameters. The tainted parameter slot is recorded.
|
|
23
|
+
* 2. Find call sites of those wrappers anywhere in the codebase whose
|
|
24
|
+
* argument in the dangerous slot is request-tainted — `req`/`request`/
|
|
25
|
+
* `.body`/`.query`/`.params`, either directly or via a local variable
|
|
26
|
+
* assigned from such.
|
|
27
|
+
* 3. Emit a flow: source(call site) → wrapper(param) → sink.
|
|
28
|
+
*
|
|
29
|
+
* Bounded to ONE hop (source → wrapper → sink) to stay linear and
|
|
30
|
+
* low-false-positive: a finding requires BOTH a confirmed sink-wrapper AND a
|
|
31
|
+
* confirmed taint source reaching its dangerous slot. Cross-file flows (call
|
|
32
|
+
* site and sink in different files) are the headline; same-file 1-hop flows
|
|
33
|
+
* the line scanners miss are also reported.
|
|
34
|
+
*
|
|
35
|
+
* Known limitations (kept deliberately, to bound false positives): only
|
|
36
|
+
* standalone functions and `const fn = …` arrows are treated as wrappers
|
|
37
|
+
* (class/object methods are not), and taint is followed a single hop.
|
|
38
|
+
*
|
|
39
|
+
* Findings:
|
|
40
|
+
* - 112-taint-flow high code-bug-security
|
|
41
|
+
* - 112-clean / 112-summary info
|
|
42
|
+
*
|
|
43
|
+
* Persists `evidence/taint-analysis.json`. Read-only, parallel-safe, static.
|
|
44
|
+
*/
|
|
45
|
+
import * as fs from 'node:fs';
|
|
46
|
+
import * as path from 'node:path';
|
|
47
|
+
import { BaseAgent } from './base-agent.js';
|
|
48
|
+
import { maskNonCode, isTestPath } from './lib/source-scan.js';
|
|
49
|
+
const SRC_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
|
|
50
|
+
const SKIP_DIRS = new Set([
|
|
51
|
+
'node_modules', '.git', 'dist', 'build', '.next', '.nuxt', '.run', 'coverage', 'reports', 'baselines', 'evidence',
|
|
52
|
+
]);
|
|
53
|
+
/**
|
|
54
|
+
* Request-input taint markers — kept in sync with the line-level scanners.
|
|
55
|
+
* `req`/`request` + the `.body`/`.query`/`.params` accessors also cover Koa
|
|
56
|
+
* (`ctx.request.body`, `ctx.params`) and Hapi (`request.payload`).
|
|
57
|
+
*
|
|
58
|
+
* The `(?!\s*\()` after the accessors disambiguates a request PROPERTY ACCESS
|
|
59
|
+
* (`req.query.id`, `ctx.params`, `request.body`) from a same-named METHOD CALL —
|
|
60
|
+
* notably TypeORM / query-builder `queryRunner.query(sql)` / `conn.params(...)`,
|
|
61
|
+
* which would otherwise false-positive as request input. (No real request accessor
|
|
62
|
+
* is invoked as `.query(`/`.params(`/`.body(`, so excluding the call form is loss-free.)
|
|
63
|
+
* This is the prerequisite for safely recognising NestJS class-method sink-wrappers,
|
|
64
|
+
* which were reverted earlier precisely because of this collision.
|
|
65
|
+
*
|
|
66
|
+
* NB: bare `args`/`payload` (GraphQL resolver args / queue payloads) false-
|
|
67
|
+
* positive on their pervasive non-request use (`execFile(cmd, args)`), so they
|
|
68
|
+
* aren't here. GraphQL resolver args are handled separately via
|
|
69
|
+
* `extractResolvers` (below), which gates `args` as a taint source on a
|
|
70
|
+
* distinctive 3/4-arg resolver signature `(parent, args, context|info, …)`.
|
|
71
|
+
* Queue `payload` remains a deferred gap (no equally distinctive shape).
|
|
72
|
+
*/
|
|
73
|
+
export const REQUEST_RE = /\b(?:req|request)\b|\.(?:body|query|params)\b(?!\s*\(|\s*\?\.\s*\()/;
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
75
|
+
// Generic source helpers
|
|
76
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
77
|
+
function walk(dir, results = []) {
|
|
78
|
+
if (!fs.existsSync(dir))
|
|
79
|
+
return results;
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (SKIP_DIRS.has(entry.name))
|
|
89
|
+
continue;
|
|
90
|
+
const full = path.join(dir, entry.name);
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
walk(full, results);
|
|
93
|
+
}
|
|
94
|
+
else if (entry.isFile() && SRC_EXTENSIONS.has(path.extname(entry.name))) {
|
|
95
|
+
results.push(full);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
100
|
+
function relativise(absPath, root) {
|
|
101
|
+
return path.relative(root, absPath).split(path.sep).join('/');
|
|
102
|
+
}
|
|
103
|
+
function lineOf(content, index) {
|
|
104
|
+
return content.slice(0, index).split('\n').length;
|
|
105
|
+
}
|
|
106
|
+
function snippetOf(content, index) {
|
|
107
|
+
const start = content.lastIndexOf('\n', index) + 1;
|
|
108
|
+
let end = content.indexOf('\n', index);
|
|
109
|
+
if (end === -1)
|
|
110
|
+
end = content.length;
|
|
111
|
+
return content.slice(start, end).trim().slice(0, 160);
|
|
112
|
+
}
|
|
113
|
+
function skipWs(content, i) {
|
|
114
|
+
while (i < content.length && /\s/.test(content[i]))
|
|
115
|
+
i++;
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
/** String-aware balanced-delimiter reader. Returns the slice including delimiters. */
|
|
119
|
+
export function readBalanced(content, openIdx, open, close) {
|
|
120
|
+
if (content[openIdx] !== open)
|
|
121
|
+
return null;
|
|
122
|
+
let depth = 0;
|
|
123
|
+
let inString = null;
|
|
124
|
+
let escape = false;
|
|
125
|
+
for (let i = openIdx; i < content.length; i++) {
|
|
126
|
+
const ch = content[i];
|
|
127
|
+
if (escape) {
|
|
128
|
+
escape = false;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (inString) {
|
|
132
|
+
if (ch === '\\')
|
|
133
|
+
escape = true;
|
|
134
|
+
else if (ch === inString)
|
|
135
|
+
inString = null;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
139
|
+
inString = ch;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (ch === open)
|
|
143
|
+
depth++;
|
|
144
|
+
else if (ch === close) {
|
|
145
|
+
depth--;
|
|
146
|
+
if (depth === 0)
|
|
147
|
+
return { text: content.slice(openIdx, i + 1), end: i };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Read an arrow-expression body (no braces) up to a top-level `;` or newline.
|
|
154
|
+
* With `stopAtComma`, also stop at a top-level `,` — needed for a concise arrow
|
|
155
|
+
* inside an argument list (`f(x => expr, other)`), whose body ends at the comma
|
|
156
|
+
* separating it from the next argument.
|
|
157
|
+
*/
|
|
158
|
+
function readExprBody(content, start, stopAtComma = false) {
|
|
159
|
+
let depth = 0;
|
|
160
|
+
let inString = null;
|
|
161
|
+
let escape = false;
|
|
162
|
+
for (let i = start; i < content.length; i++) {
|
|
163
|
+
const ch = content[i];
|
|
164
|
+
if (escape) {
|
|
165
|
+
escape = false;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (inString) {
|
|
169
|
+
if (ch === '\\')
|
|
170
|
+
escape = true;
|
|
171
|
+
else if (ch === inString)
|
|
172
|
+
inString = null;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
176
|
+
inString = ch;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (ch === '(' || ch === '[' || ch === '{')
|
|
180
|
+
depth++;
|
|
181
|
+
else if (ch === ')' || ch === ']' || ch === '}') {
|
|
182
|
+
if (depth === 0)
|
|
183
|
+
return content.slice(start, i);
|
|
184
|
+
depth--;
|
|
185
|
+
}
|
|
186
|
+
else if (depth === 0 && (ch === ';' || ch === '\n' || (stopAtComma && ch === ','))) {
|
|
187
|
+
return content.slice(start, i);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return content.slice(start);
|
|
191
|
+
}
|
|
192
|
+
/** Split a parenthesised argument list (incl. the outer parens) into top-level pieces. */
|
|
193
|
+
export function splitArgs(parenText) {
|
|
194
|
+
const inner = parenText.slice(1, -1);
|
|
195
|
+
if (inner.trim() === '')
|
|
196
|
+
return [];
|
|
197
|
+
const parts = [];
|
|
198
|
+
let depth = 0;
|
|
199
|
+
let inString = null;
|
|
200
|
+
let escape = false;
|
|
201
|
+
let cur = '';
|
|
202
|
+
for (let i = 0; i < inner.length; i++) {
|
|
203
|
+
const ch = inner[i];
|
|
204
|
+
if (escape) {
|
|
205
|
+
cur += ch;
|
|
206
|
+
escape = false;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (inString) {
|
|
210
|
+
cur += ch;
|
|
211
|
+
if (ch === '\\')
|
|
212
|
+
escape = true;
|
|
213
|
+
else if (ch === inString)
|
|
214
|
+
inString = null;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
218
|
+
inString = ch;
|
|
219
|
+
cur += ch;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (ch === '(' || ch === '[' || ch === '{') {
|
|
223
|
+
depth++;
|
|
224
|
+
cur += ch;
|
|
225
|
+
}
|
|
226
|
+
else if (ch === ')' || ch === ']' || ch === '}') {
|
|
227
|
+
depth--;
|
|
228
|
+
cur += ch;
|
|
229
|
+
}
|
|
230
|
+
else if (ch === ',' && depth === 0) {
|
|
231
|
+
parts.push(cur.trim());
|
|
232
|
+
cur = '';
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
cur += ch;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
parts.push(cur.trim());
|
|
239
|
+
return parts;
|
|
240
|
+
}
|
|
241
|
+
/** Reduce a single parameter token to the identifier(s) it binds. */
|
|
242
|
+
function paramIdentifiers(token) {
|
|
243
|
+
let t = token.trim();
|
|
244
|
+
if (t.startsWith('...'))
|
|
245
|
+
t = t.slice(3).trim();
|
|
246
|
+
// strip a default value: `x = 1` → `x`
|
|
247
|
+
const eq = topLevelEquals(t);
|
|
248
|
+
if (eq !== -1)
|
|
249
|
+
t = t.slice(0, eq).trim();
|
|
250
|
+
if (t.startsWith('{') || t.startsWith('[')) {
|
|
251
|
+
const open = t[0];
|
|
252
|
+
const close = open === '{' ? '}' : ']';
|
|
253
|
+
// Take only the destructuring pattern, dropping any trailing `: Type` annotation.
|
|
254
|
+
const pat = readBalanced(t, 0, open, close);
|
|
255
|
+
return destructuredBindings(pat ? pat.text : t, open);
|
|
256
|
+
}
|
|
257
|
+
// strip a type annotation: `x: Foo` → `x`
|
|
258
|
+
const colon = t.indexOf(':');
|
|
259
|
+
if (colon !== -1)
|
|
260
|
+
t = t.slice(0, colon).trim();
|
|
261
|
+
// strip an optional marker: `q?` → `q` (e.g. NestJS `@Query('q') q?: string`)
|
|
262
|
+
if (t.endsWith('?'))
|
|
263
|
+
t = t.slice(0, -1).trim();
|
|
264
|
+
return t ? [t] : [];
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Identifiers actually *bound* (introduced into scope) by a destructuring pattern.
|
|
268
|
+
* For object patterns, a `key:` to the left of a colon is a property name, not a
|
|
269
|
+
* binding — only the right-hand side is bound (`{ a: b }` binds `b`, not `a`;
|
|
270
|
+
* shorthand `{ a }` binds `a`). Array patterns bind every identifier. This keeps
|
|
271
|
+
* a renamed property key (`{ input: cmd }` → `cmd`, never `input`) from being
|
|
272
|
+
* mistaken for an in-scope taint name (adversarial-review FP-2).
|
|
273
|
+
*/
|
|
274
|
+
export function destructuredBindings(pattern, kind) {
|
|
275
|
+
if (kind === '[') {
|
|
276
|
+
return [...new Set(pattern.match(/[A-Za-z_$][\w$]*/g) ?? [])];
|
|
277
|
+
}
|
|
278
|
+
const ids = [];
|
|
279
|
+
const re = /([A-Za-z_$][\w$]*)\s*(:)?/g;
|
|
280
|
+
let m;
|
|
281
|
+
while ((m = re.exec(pattern)) !== null) {
|
|
282
|
+
if (m[2] === ':')
|
|
283
|
+
continue; // property key (rename target) — not a binding
|
|
284
|
+
ids.push(m[1]);
|
|
285
|
+
}
|
|
286
|
+
return [...new Set(ids)];
|
|
287
|
+
}
|
|
288
|
+
/** Index of the first `=` that is an assignment (not `=>`, `==`, `<=`, `>=`, `!=`). */
|
|
289
|
+
function topLevelEquals(t) {
|
|
290
|
+
for (let i = 0; i < t.length; i++) {
|
|
291
|
+
if (t[i] !== '=')
|
|
292
|
+
continue;
|
|
293
|
+
if (t[i + 1] === '=' || t[i + 1] === '>')
|
|
294
|
+
continue;
|
|
295
|
+
if (t[i - 1] === '=' || t[i - 1] === '!' || t[i - 1] === '<' || t[i - 1] === '>')
|
|
296
|
+
continue;
|
|
297
|
+
return i;
|
|
298
|
+
}
|
|
299
|
+
return -1;
|
|
300
|
+
}
|
|
301
|
+
/** Locate a function body after its parameter list, tolerating a return-type annotation. */
|
|
302
|
+
function findBody(content, afterParenIdx) {
|
|
303
|
+
let i = skipWs(content, afterParenIdx);
|
|
304
|
+
if (content[i] === ':') {
|
|
305
|
+
i++;
|
|
306
|
+
let d = 0;
|
|
307
|
+
while (i < content.length) {
|
|
308
|
+
const c = content[i];
|
|
309
|
+
if (c === '<' || c === '(' || c === '[')
|
|
310
|
+
d++;
|
|
311
|
+
else if (c === '>' || c === ')' || c === ']')
|
|
312
|
+
d--;
|
|
313
|
+
else if (d === 0) {
|
|
314
|
+
if (c === '{')
|
|
315
|
+
break;
|
|
316
|
+
if (c === '=' && content[i + 1] === '>')
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
i++;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (content[i] === '{') {
|
|
323
|
+
const body = readBalanced(content, i, '{', '}');
|
|
324
|
+
return body ? { text: body.text, openIdx: i } : null;
|
|
325
|
+
}
|
|
326
|
+
if (content[i] === '=' && content[i + 1] === '>') {
|
|
327
|
+
const j = skipWs(content, i + 2);
|
|
328
|
+
if (content[j] === '{') {
|
|
329
|
+
const body = readBalanced(content, j, '{', '}');
|
|
330
|
+
return body ? { text: body.text, openIdx: j } : null;
|
|
331
|
+
}
|
|
332
|
+
return { text: readExprBody(content, j), openIdx: j };
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
function paramSlotsOf(parenText) {
|
|
337
|
+
return splitArgs(parenText).map(paramIdentifiers);
|
|
338
|
+
}
|
|
339
|
+
export function extractFunctions(content) {
|
|
340
|
+
const fns = [];
|
|
341
|
+
// function NAME(params) [: T] { ... }
|
|
342
|
+
const fnDecl = /(?:\bexport\s+)?(?:\bdefault\s+)?(?:\basync\s+)?\bfunction\s*\*?\s*([A-Za-z_$][\w$]*)\s*\(/g;
|
|
343
|
+
let m;
|
|
344
|
+
while ((m = fnDecl.exec(content)) !== null) {
|
|
345
|
+
const parenIdx = m.index + m[0].length - 1;
|
|
346
|
+
const params = readBalanced(content, parenIdx, '(', ')');
|
|
347
|
+
if (!params)
|
|
348
|
+
continue;
|
|
349
|
+
const body = findBody(content, params.end + 1);
|
|
350
|
+
if (!body)
|
|
351
|
+
continue;
|
|
352
|
+
fns.push({
|
|
353
|
+
name: m[1],
|
|
354
|
+
paramSlots: paramSlotsOf(params.text),
|
|
355
|
+
bodyText: body.text,
|
|
356
|
+
bodyOpenIdx: body.openIdx,
|
|
357
|
+
declLine: lineOf(content, m.index),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
// const NAME = (params) => ... | = async (params) => ... | = function (params) { ... }
|
|
361
|
+
const arrowParen = /(?:\bexport\s+)?\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?(?:function\s*\*?\s*)?\(/g;
|
|
362
|
+
while ((m = arrowParen.exec(content)) !== null) {
|
|
363
|
+
const parenIdx = m.index + m[0].length - 1;
|
|
364
|
+
const params = readBalanced(content, parenIdx, '(', ')');
|
|
365
|
+
if (!params)
|
|
366
|
+
continue;
|
|
367
|
+
const body = findBody(content, params.end + 1);
|
|
368
|
+
if (!body)
|
|
369
|
+
continue;
|
|
370
|
+
fns.push({
|
|
371
|
+
name: m[1],
|
|
372
|
+
paramSlots: paramSlotsOf(params.text),
|
|
373
|
+
bodyText: body.text,
|
|
374
|
+
bodyOpenIdx: body.openIdx,
|
|
375
|
+
declLine: lineOf(content, m.index),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
// const NAME = (async) IDENT => ... (single param, no parens)
|
|
379
|
+
const arrowSingle = /(?:\bexport\s+)?\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?([A-Za-z_$][\w$]*)\s*=>/g;
|
|
380
|
+
while ((m = arrowSingle.exec(content)) !== null) {
|
|
381
|
+
const arrowIdx = content.indexOf('=>', m.index + m[0].length - 2);
|
|
382
|
+
if (arrowIdx === -1)
|
|
383
|
+
continue;
|
|
384
|
+
const j = skipWs(content, arrowIdx + 2);
|
|
385
|
+
let bodyText;
|
|
386
|
+
let bodyOpenIdx;
|
|
387
|
+
if (content[j] === '{') {
|
|
388
|
+
const body = readBalanced(content, j, '{', '}');
|
|
389
|
+
if (!body)
|
|
390
|
+
continue;
|
|
391
|
+
bodyText = body.text;
|
|
392
|
+
bodyOpenIdx = j;
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
bodyText = readExprBody(content, j);
|
|
396
|
+
bodyOpenIdx = j;
|
|
397
|
+
}
|
|
398
|
+
fns.push({
|
|
399
|
+
name: m[1],
|
|
400
|
+
paramSlots: [[m[2]]],
|
|
401
|
+
bodyText,
|
|
402
|
+
bodyOpenIdx,
|
|
403
|
+
declLine: lineOf(content, m.index),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
// NB: class/object methods are NOT treated as STANDALONE wrappers here — on
|
|
407
|
+
// query-builder/ORM code (TypeORM's `QueryRunner.query()`) the `.query`/`.params`
|
|
408
|
+
// accessors collide with method names. NestJS class-method flows are handled
|
|
409
|
+
// separately and conservatively by `extractClasses` + the NestJS pass in
|
|
410
|
+
// `analyzeTaint`, which only matches same-class `this.method()` call sites (a bare
|
|
411
|
+
// `.find(`/`.map(` member call can never collide with an Array.prototype method).
|
|
412
|
+
return fns;
|
|
413
|
+
}
|
|
414
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
415
|
+
// NestJS class extraction (controllers / providers)
|
|
416
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
417
|
+
/**
|
|
418
|
+
* NestJS request-binding param decorators (names only). A param decorated with one
|
|
419
|
+
* of these carries client-controlled input, so its bound identifier is a taint
|
|
420
|
+
* source — the class-method analogue of GraphQL's resolver `args`. Distinctive
|
|
421
|
+
* (low false-positive, like the resolver signature gate); a bare param named
|
|
422
|
+
* `body`/`query`/`payload` is NOT a source without the decorator.
|
|
423
|
+
*
|
|
424
|
+
* `@MessageBody`/`@Payload` extend this to NestJS microservice + queue handlers
|
|
425
|
+
* (`@MessagePattern`/`@EventPattern`/`@Process`), where the deserialised message/job
|
|
426
|
+
* payload crosses a trust boundary just like an HTTP `@Body`. A BARE queue
|
|
427
|
+
* `payload`/`job.data` (plain BullMQ `new Worker(name, job => …)`, no decorator) is
|
|
428
|
+
* deliberately NOT a source — no distinctive non-decorator shape to gate on without
|
|
429
|
+
* false-positiving on pervasive non-queue `payload` use (tracked in CLEANUP_PHASE.md).
|
|
430
|
+
*/
|
|
431
|
+
const NEST_SOURCE_NAMES = new Set([
|
|
432
|
+
'Body', 'Query', 'Param', 'Headers', 'Req', 'Request', 'Ip', 'Session',
|
|
433
|
+
'RawBody', 'UploadedFile', 'UploadedFiles', 'HostParam', 'Cookies', 'MessageBody',
|
|
434
|
+
'Payload',
|
|
435
|
+
]);
|
|
436
|
+
/** TS parameter-property modifiers stripped before reading the bound identifier. */
|
|
437
|
+
const PARAM_MODIFIERS = new Set(['public', 'private', 'protected', 'readonly']);
|
|
438
|
+
/** Method names that are really keywords/control-flow, never method definitions. */
|
|
439
|
+
const NON_METHOD_NAMES = new Set([
|
|
440
|
+
'if', 'for', 'while', 'switch', 'catch', 'return', 'await', 'typeof', 'function',
|
|
441
|
+
'new', 'do', 'else', 'yield', 'void', 'delete', 'in', 'of', 'instanceof', 'class',
|
|
442
|
+
'super', 'constructor',
|
|
443
|
+
]);
|
|
444
|
+
/**
|
|
445
|
+
* Strip leading decorators from a parameter token; report whether any is a source
|
|
446
|
+
* decorator. Decorator arguments are read paren-BALANCED, so a decorator with a
|
|
447
|
+
* call/object argument — `@Body(new ValidationPipe())`, `@Param('id', ParseIntPipe)` —
|
|
448
|
+
* is fully consumed rather than truncated at the first inner `)` (critic FN-3).
|
|
449
|
+
*/
|
|
450
|
+
function parseDecoratedParam(token) {
|
|
451
|
+
let t = token.trim();
|
|
452
|
+
let isSource = false;
|
|
453
|
+
for (;;) {
|
|
454
|
+
if (t[0] !== '@')
|
|
455
|
+
break;
|
|
456
|
+
const nameMatch = t.slice(1).match(/^[A-Za-z_$][\w$]*/);
|
|
457
|
+
if (!nameMatch)
|
|
458
|
+
break;
|
|
459
|
+
if (NEST_SOURCE_NAMES.has(nameMatch[0]))
|
|
460
|
+
isSource = true;
|
|
461
|
+
let rest = t.slice(1 + nameMatch[0].length).replace(/^\s+/, '');
|
|
462
|
+
if (rest[0] === '(') {
|
|
463
|
+
const bal = readBalanced(rest, 0, '(', ')');
|
|
464
|
+
if (!bal)
|
|
465
|
+
break; // malformed — stop consuming
|
|
466
|
+
rest = rest.slice(bal.end + 1).replace(/^\s+/, '');
|
|
467
|
+
}
|
|
468
|
+
t = rest;
|
|
469
|
+
}
|
|
470
|
+
// Strip TS parameter-property modifiers (`readonly`/`private`/… — valid only on
|
|
471
|
+
// constructor params, defensive here) so the bound identifier is read cleanly (FN-1).
|
|
472
|
+
let prev;
|
|
473
|
+
do {
|
|
474
|
+
prev = t;
|
|
475
|
+
const w = t.match(/^([A-Za-z_$][\w$]*)\s+/);
|
|
476
|
+
if (w && PARAM_MODIFIERS.has(w[1]))
|
|
477
|
+
t = t.slice(w[0].length);
|
|
478
|
+
} while (t !== prev);
|
|
479
|
+
return { ids: paramIdentifiers(t), isSource };
|
|
480
|
+
}
|
|
481
|
+
/** Parse a (decorated) parameter list into per-slot bindings + decorator-sourced ids. */
|
|
482
|
+
function parseDecoratedParams(parenText) {
|
|
483
|
+
const slots = [];
|
|
484
|
+
const sourceIds = [];
|
|
485
|
+
for (const tok of splitArgs(parenText)) {
|
|
486
|
+
const { ids, isSource } = parseDecoratedParam(tok);
|
|
487
|
+
slots.push(ids);
|
|
488
|
+
if (isSource)
|
|
489
|
+
sourceIds.push(...ids);
|
|
490
|
+
}
|
|
491
|
+
return { slots, sourceIds };
|
|
492
|
+
}
|
|
493
|
+
/** Extract the method definitions from a class body (text incl. outer braces). */
|
|
494
|
+
function extractClassMethods(classBodyText, classBodyAbsIdx) {
|
|
495
|
+
const methods = [];
|
|
496
|
+
const re = /([A-Za-z_$][\w$]*)\s*\(/g;
|
|
497
|
+
let m;
|
|
498
|
+
while ((m = re.exec(classBodyText)) !== null) {
|
|
499
|
+
const name = m[1];
|
|
500
|
+
if (NON_METHOD_NAMES.has(name))
|
|
501
|
+
continue;
|
|
502
|
+
// The char before the name (skipping whitespace) must not be `.`/`?` — that
|
|
503
|
+
// would be a `this.foo(` / `a?.foo(` CALL, not a method definition.
|
|
504
|
+
let p = m.index - 1;
|
|
505
|
+
while (p >= 0 && /\s/.test(classBodyText[p]))
|
|
506
|
+
p--;
|
|
507
|
+
if (p >= 0 && (classBodyText[p] === '.' || classBodyText[p] === '?'))
|
|
508
|
+
continue;
|
|
509
|
+
const parenIdx = m.index + m[0].length - 1;
|
|
510
|
+
const params = readBalanced(classBodyText, parenIdx, '(', ')');
|
|
511
|
+
if (!params)
|
|
512
|
+
continue;
|
|
513
|
+
const body = findBody(classBodyText, params.end + 1);
|
|
514
|
+
if (!body || body.text[0] !== '{')
|
|
515
|
+
continue; // a real method has a brace body
|
|
516
|
+
const { slots, sourceIds } = parseDecoratedParams(params.text);
|
|
517
|
+
methods.push({
|
|
518
|
+
name,
|
|
519
|
+
paramSlots: slots,
|
|
520
|
+
sourceParamIds: sourceIds,
|
|
521
|
+
bodyText: body.text,
|
|
522
|
+
bodyAbsIdx: classBodyAbsIdx + body.openIdx,
|
|
523
|
+
bodyStart: classBodyAbsIdx + body.openIdx,
|
|
524
|
+
bodyEnd: classBodyAbsIdx + body.openIdx + body.text.length,
|
|
525
|
+
});
|
|
526
|
+
re.lastIndex = body.openIdx + body.text.length; // skip the body; don't re-scan its calls
|
|
527
|
+
}
|
|
528
|
+
return methods;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Extract class declarations and their methods. Used by the NestJS pass to find
|
|
532
|
+
* class-method sink-wrappers and decorator-bound request sources. Standalone
|
|
533
|
+
* functions remain the job of `extractFunctions`.
|
|
534
|
+
*/
|
|
535
|
+
export function extractClasses(content) {
|
|
536
|
+
const classes = [];
|
|
537
|
+
const re = /\bclass\s+([A-Za-z_$][\w$]*)/g;
|
|
538
|
+
let m;
|
|
539
|
+
while ((m = re.exec(content)) !== null) {
|
|
540
|
+
// Walk to the class body `{`, skipping any `extends X<…>` / `implements Y, Z`.
|
|
541
|
+
let depth = 0;
|
|
542
|
+
let openIdx = -1;
|
|
543
|
+
for (let i = m.index + m[0].length; i < content.length; i++) {
|
|
544
|
+
const c = content[i];
|
|
545
|
+
if (c === '<' || c === '(' || c === '[')
|
|
546
|
+
depth++;
|
|
547
|
+
else if (c === '>' || c === ')' || c === ']')
|
|
548
|
+
depth--;
|
|
549
|
+
else if (depth === 0 && c === '{') {
|
|
550
|
+
openIdx = i;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
else if (depth === 0 && c === ';')
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
if (openIdx === -1)
|
|
557
|
+
continue;
|
|
558
|
+
const body = readBalanced(content, openIdx, '{', '}');
|
|
559
|
+
if (!body)
|
|
560
|
+
continue;
|
|
561
|
+
classes.push({
|
|
562
|
+
name: m[1],
|
|
563
|
+
methods: extractClassMethods(body.text, openIdx),
|
|
564
|
+
bodyStart: openIdx,
|
|
565
|
+
bodyEnd: openIdx + body.text.length,
|
|
566
|
+
});
|
|
567
|
+
re.lastIndex = openIdx + body.text.length; // don't descend into nested classes twice
|
|
568
|
+
}
|
|
569
|
+
return classes;
|
|
570
|
+
}
|
|
571
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
572
|
+
// GraphQL resolver detection
|
|
573
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
574
|
+
/**
|
|
575
|
+
* Recognise the canonical GraphQL resolver signature so the resolver's `args`
|
|
576
|
+
* parameter can be treated as a taint source. Bare `args` was rejected as a
|
|
577
|
+
* global taint marker — it false-positives on `execFile(cmd, args)`; requiring
|
|
578
|
+
* a distinctive resolver shape removes that whole class of FP.
|
|
579
|
+
*
|
|
580
|
+
* Apollo / Yoga / graphql-tools resolvers take `(parent, args, context, info)`.
|
|
581
|
+
* The discriminators kept here are the *names of the later params*:
|
|
582
|
+
* - 4-arg shape with `context`/`ctx` at slot 2 or `info` at slot 3; or
|
|
583
|
+
* - 3-arg shape with `context`/`ctx`/`info` at slot 2.
|
|
584
|
+
* A bare 2-arg function with `args` (the original FP shape) is NOT recognised.
|
|
585
|
+
* NestJS `@Resolver()` class methods are not recognised here (class-method
|
|
586
|
+
* extraction is a separate, deferred gap).
|
|
587
|
+
*/
|
|
588
|
+
const CONTEXT_PARAM_NAMES = new Set(['context', 'ctx', '_context', '_ctx']);
|
|
589
|
+
const INFO_PARAM_NAMES = new Set(['info', '_info']);
|
|
590
|
+
export function isResolverSignature(fn) {
|
|
591
|
+
const slots = fn.paramSlots;
|
|
592
|
+
if (slots.length !== 3 && slots.length !== 4)
|
|
593
|
+
return false;
|
|
594
|
+
const slotHas = (idx, names) => !!slots[idx] && slots[idx].some((n) => names.has(n));
|
|
595
|
+
if (slots.length === 4)
|
|
596
|
+
return slotHas(2, CONTEXT_PARAM_NAMES) || slotHas(3, INFO_PARAM_NAMES);
|
|
597
|
+
return slotHas(2, CONTEXT_PARAM_NAMES) || slotHas(2, INFO_PARAM_NAMES);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Pick up object-literal member functions for resolver detection. `extractFunctions`
|
|
601
|
+
* deliberately skips these (object/class methods as sink *wrappers* regressed FPs
|
|
602
|
+
* on ORM query-builders — see the note in that function). Resolvers gate on a
|
|
603
|
+
* strict signature, so broader extraction here is safe: the canonical Apollo
|
|
604
|
+
* shape is `{ Query: { user: async (parent, args, context, info) => … } }` /
|
|
605
|
+
* `{ user(parent, args, context, info) { … } }`, which would never be reached
|
|
606
|
+
* by the standalone-function extractor.
|
|
607
|
+
*
|
|
608
|
+
* Patterns picked up:
|
|
609
|
+
* - `KEY: async? (params) [: T] => body` (arrow-valued property)
|
|
610
|
+
* - `async? KEY(params) [: T] { body }` (method shorthand)
|
|
611
|
+
*/
|
|
612
|
+
function extractObjectMemberFns(content) {
|
|
613
|
+
const fns = [];
|
|
614
|
+
const skipTypeAnnotationTo = (i, terminators) => {
|
|
615
|
+
if (content[i] !== ':')
|
|
616
|
+
return i;
|
|
617
|
+
let d = 0;
|
|
618
|
+
i++;
|
|
619
|
+
while (i < content.length) {
|
|
620
|
+
const c = content[i];
|
|
621
|
+
if (c === '<' || c === '(' || c === '[')
|
|
622
|
+
d++;
|
|
623
|
+
else if (c === '>' || c === ')' || c === ']')
|
|
624
|
+
d--;
|
|
625
|
+
else if (d === 0) {
|
|
626
|
+
if (c === '=' && content[i + 1] === '>')
|
|
627
|
+
return i;
|
|
628
|
+
if (terminators.has(c))
|
|
629
|
+
return i;
|
|
630
|
+
}
|
|
631
|
+
i++;
|
|
632
|
+
}
|
|
633
|
+
return i;
|
|
634
|
+
};
|
|
635
|
+
// `KEY : ASYNC? ( PARAMS ) [: T] => BODY`
|
|
636
|
+
const arrowRe = /[\s,{(]([A-Za-z_$][\w$]*)\s*:\s*(?:async\s+)?\(/g;
|
|
637
|
+
let m;
|
|
638
|
+
const ARROW_TERMINATORS = new Set([',', ';', '}', '\n']);
|
|
639
|
+
while ((m = arrowRe.exec(content)) !== null) {
|
|
640
|
+
const parenIdx = m.index + m[0].length - 1;
|
|
641
|
+
const params = readBalanced(content, parenIdx, '(', ')');
|
|
642
|
+
if (!params)
|
|
643
|
+
continue;
|
|
644
|
+
let i = skipWs(content, params.end + 1);
|
|
645
|
+
i = skipTypeAnnotationTo(i, ARROW_TERMINATORS);
|
|
646
|
+
if (content[i] !== '=' || content[i + 1] !== '>')
|
|
647
|
+
continue;
|
|
648
|
+
const bodyStart = skipWs(content, i + 2);
|
|
649
|
+
let bodyText;
|
|
650
|
+
let bodyOpenIdx;
|
|
651
|
+
if (content[bodyStart] === '{') {
|
|
652
|
+
const b = readBalanced(content, bodyStart, '{', '}');
|
|
653
|
+
if (!b)
|
|
654
|
+
continue;
|
|
655
|
+
bodyText = b.text;
|
|
656
|
+
bodyOpenIdx = bodyStart;
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
bodyText = readExprBody(content, bodyStart);
|
|
660
|
+
bodyOpenIdx = bodyStart;
|
|
661
|
+
}
|
|
662
|
+
fns.push({
|
|
663
|
+
name: m[1],
|
|
664
|
+
paramSlots: paramSlotsOf(params.text),
|
|
665
|
+
bodyText,
|
|
666
|
+
bodyOpenIdx,
|
|
667
|
+
declLine: lineOf(content, m.index),
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
// `ASYNC? KEY ( PARAMS ) [: T] { BODY }` — object method shorthand
|
|
671
|
+
const methodRe = /[,{]\s*(?:async\s+)?([A-Za-z_$][\w$]*)\s*\(/g;
|
|
672
|
+
const METHOD_TERMINATORS = new Set([',', ';', '}', '\n']);
|
|
673
|
+
while ((m = methodRe.exec(content)) !== null) {
|
|
674
|
+
const parenIdx = m.index + m[0].length - 1;
|
|
675
|
+
const params = readBalanced(content, parenIdx, '(', ')');
|
|
676
|
+
if (!params)
|
|
677
|
+
continue;
|
|
678
|
+
let i = skipWs(content, params.end + 1);
|
|
679
|
+
i = skipTypeAnnotationTo(i, METHOD_TERMINATORS);
|
|
680
|
+
if (content[i] !== '{')
|
|
681
|
+
continue;
|
|
682
|
+
const body = readBalanced(content, i, '{', '}');
|
|
683
|
+
if (!body)
|
|
684
|
+
continue;
|
|
685
|
+
fns.push({
|
|
686
|
+
name: m[1],
|
|
687
|
+
paramSlots: paramSlotsOf(params.text),
|
|
688
|
+
bodyText: body.text,
|
|
689
|
+
bodyOpenIdx: i,
|
|
690
|
+
declLine: lineOf(content, m.index),
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
return fns;
|
|
694
|
+
}
|
|
695
|
+
export function extractResolvers(content) {
|
|
696
|
+
const out = [];
|
|
697
|
+
const all = [...extractFunctions(content), ...extractObjectMemberFns(content)];
|
|
698
|
+
for (const fn of all) {
|
|
699
|
+
if (!isResolverSignature(fn))
|
|
700
|
+
continue;
|
|
701
|
+
const argsNames = fn.paramSlots[1] ?? [];
|
|
702
|
+
if (argsNames.length === 0)
|
|
703
|
+
continue;
|
|
704
|
+
out.push({
|
|
705
|
+
fn,
|
|
706
|
+
bodyStart: fn.bodyOpenIdx,
|
|
707
|
+
bodyEnd: fn.bodyOpenIdx + fn.bodyText.length,
|
|
708
|
+
taintedNames: argsNames,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
return out;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Inline arrow-callback scopes within `content` — `(p) => …` / `p => …` forms that
|
|
715
|
+
* `extractFunctions` does NOT pick up (it only finds declarations and assigned
|
|
716
|
+
* arrows, not anonymous callbacks like `items.map(args => …)`). Each entry gives
|
|
717
|
+
* the callback's bound parameter identifiers and its body offset range, so a call
|
|
718
|
+
* site inside the callback can be tested for parameter shadowing.
|
|
719
|
+
*/
|
|
720
|
+
function inlineArrowScopes(content) {
|
|
721
|
+
const out = [];
|
|
722
|
+
let i = content.indexOf('=>');
|
|
723
|
+
while (i !== -1) {
|
|
724
|
+
// Walk back over whitespace to the end of the parameter list.
|
|
725
|
+
let j = i - 1;
|
|
726
|
+
while (j >= 0 && /\s/.test(content[j]))
|
|
727
|
+
j--;
|
|
728
|
+
let paramText = null;
|
|
729
|
+
if (content[j] === ')') {
|
|
730
|
+
// Scan back to the matching '(' (content is masked, so parens in strings are gone).
|
|
731
|
+
let depth = 0;
|
|
732
|
+
let k = j;
|
|
733
|
+
for (; k >= 0; k--) {
|
|
734
|
+
if (content[k] === ')')
|
|
735
|
+
depth++;
|
|
736
|
+
else if (content[k] === '(') {
|
|
737
|
+
depth--;
|
|
738
|
+
if (depth === 0)
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (k >= 0)
|
|
743
|
+
paramText = content.slice(k, j + 1);
|
|
744
|
+
}
|
|
745
|
+
else if (j >= 0 && /[A-Za-z_$]/.test(content[j])) {
|
|
746
|
+
// Single bare identifier param: `x =>`.
|
|
747
|
+
let k = j;
|
|
748
|
+
while (k >= 0 && /[\w$]/.test(content[k]))
|
|
749
|
+
k--;
|
|
750
|
+
paramText = `(${content.slice(k + 1, j + 1)})`;
|
|
751
|
+
}
|
|
752
|
+
if (paramText !== null) {
|
|
753
|
+
const b = skipWs(content, i + 2);
|
|
754
|
+
let end;
|
|
755
|
+
if (content[b] === '{') {
|
|
756
|
+
const blk = readBalanced(content, b, '{', '}');
|
|
757
|
+
end = blk ? b + blk.text.length : b;
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
// Concise body inside an arg list ends at the comma before the next arg.
|
|
761
|
+
end = b + readExprBody(content, b, true).length;
|
|
762
|
+
}
|
|
763
|
+
out.push({ paramSlots: paramSlotsOf(paramText), start: b, end });
|
|
764
|
+
}
|
|
765
|
+
i = content.indexOf('=>', i + 2);
|
|
766
|
+
}
|
|
767
|
+
return out;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* The subset of a resolver's `taintedNames` that is NOT shadowed at `callOffset`
|
|
771
|
+
* by a parameter binding inside the resolver body. A nested
|
|
772
|
+
* `function f(args) {…}`, an assigned arrow `const g = (args) => …`, OR an inline
|
|
773
|
+
* callback `items.map(args => …)` all rebind `args`, so a sink call inside them
|
|
774
|
+
* uses the inner param — not the resolver's client input — and crediting resolver
|
|
775
|
+
* taint there is a false positive (adversarial-review FP-1 + Codex inline-callback
|
|
776
|
+
* follow-up). `callOffset` is an absolute offset into the same (masked) content
|
|
777
|
+
* `extractResolvers` was given.
|
|
778
|
+
*/
|
|
779
|
+
/**
|
|
780
|
+
* The subset of `names` NOT shadowed by a nested fn/arrow scope enclosing `callOffset`.
|
|
781
|
+
* `bodyText` is the enclosing scope's (masked) source and `base` its absolute offset.
|
|
782
|
+
* Shared by the resolver and NestJS passes so both ignore a request-source name that a
|
|
783
|
+
* nested callback parameter rebinds — e.g. `items.map(body => this.run(body.id))`, where
|
|
784
|
+
* the inner `body` is the callback's local, not the request body.
|
|
785
|
+
*/
|
|
786
|
+
export function unshadowedAt(bodyText, base, names, callOffset) {
|
|
787
|
+
if (names.length === 0)
|
|
788
|
+
return names;
|
|
789
|
+
// Shadow scopes: nested declared/assigned fns AND inline arrow callbacks.
|
|
790
|
+
const scopes = [];
|
|
791
|
+
for (const nf of extractFunctions(bodyText)) {
|
|
792
|
+
scopes.push({ start: base + nf.bodyOpenIdx, end: base + nf.bodyOpenIdx + nf.bodyText.length, paramSlots: nf.paramSlots });
|
|
793
|
+
}
|
|
794
|
+
for (const ar of inlineArrowScopes(bodyText)) {
|
|
795
|
+
scopes.push({ start: base + ar.start, end: base + ar.end, paramSlots: ar.paramSlots });
|
|
796
|
+
}
|
|
797
|
+
if (scopes.length === 0)
|
|
798
|
+
return names;
|
|
799
|
+
return names.filter((name) => {
|
|
800
|
+
for (const s of scopes) {
|
|
801
|
+
if (callOffset >= s.start && callOffset < s.end && s.paramSlots.some((slot) => slot.includes(name))) {
|
|
802
|
+
return false; // the call site is inside a scope that rebinds `name`
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return true;
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
export function unshadowedResolverTaint(resolver, callOffset) {
|
|
809
|
+
return unshadowedAt(resolver.fn.bodyText, resolver.bodyStart, resolver.taintedNames, callOffset);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Sink call signatures. Each must end with `(` so the argument list can be
|
|
813
|
+
* read. `.exec(`/`.query(` member forms are intentionally NOT matched as
|
|
814
|
+
* commands (they collide with `RegExp.exec` etc.); bare `exec`/`spawn`
|
|
815
|
+
* (child_process via destructuring) are.
|
|
816
|
+
*/
|
|
817
|
+
export const SINK_SPECS = [
|
|
818
|
+
{ kind: 'eval', re: /(?<![.\w])eval\s*\(/g },
|
|
819
|
+
{ kind: 'eval', re: /\bnew\s+Function\s*\(/g },
|
|
820
|
+
{ kind: 'sql', re: /\$(?:queryRawUnsafe|executeRawUnsafe)\s*\(/g },
|
|
821
|
+
{ kind: 'sql', re: /\.(?:query|execute|raw)\s*\(/g },
|
|
822
|
+
{ kind: 'command', re: /(?<![.\w])exec(?:Sync|File)?\s*\(/g },
|
|
823
|
+
{ kind: 'command', re: /(?<![.\w])spawn(?:Sync)?\s*\(/g },
|
|
824
|
+
{
|
|
825
|
+
kind: 'fs-path',
|
|
826
|
+
re: /\bfs(?:\.promises)?\s*\.\s*(?:readFile|readFileSync|writeFile|writeFileSync|appendFile|appendFileSync|createReadStream|createWriteStream|unlink|unlinkSync|readdir|readdirSync|open|openSync|rm|rmSync)\s*\(/g,
|
|
827
|
+
},
|
|
828
|
+
{ kind: 'fs-path', re: /\.(?:sendFile|download)\s*\(/g },
|
|
829
|
+
{ kind: 'redirect', re: /\.redirect\s*\(/g },
|
|
830
|
+
// SSRF: a request-tainted URL into an outbound HTTP client. Specific call
|
|
831
|
+
// shapes only (not a bare `.get(`) so it fires on real network sinks.
|
|
832
|
+
{ kind: 'ssrf', re: /(?<![.\w])fetch\s*\(/g },
|
|
833
|
+
{ kind: 'ssrf', re: /\baxios\s*(?:\.\s*(?:get|post|put|patch|delete|head|request))?\s*\(/g },
|
|
834
|
+
{ kind: 'ssrf', re: /\b(?:https?)\s*\.\s*(?:request|get)\s*\(/g },
|
|
835
|
+
{ kind: 'ssrf', re: /(?<![.\w])got\s*(?:\.\s*(?:get|post|put|patch|delete|head|stream))?\s*\(/g },
|
|
836
|
+
];
|
|
837
|
+
/** True if `ident` appears as a standalone (non-member) identifier in `text`. */
|
|
838
|
+
export function identAppears(text, ident) {
|
|
839
|
+
const re = new RegExp(`(?<![.\\w])${ident.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\w])`);
|
|
840
|
+
return re.test(text);
|
|
841
|
+
}
|
|
842
|
+
const URL_BASE_ACCESSOR = /^(?:url|origin|href|host|hostname|protocol|port)$/;
|
|
843
|
+
/**
|
|
844
|
+
* For redirect sinks: a request param used only as a URL base — `new URL(path,
|
|
845
|
+
* req.url)` / `req.origin` — isn't the redirect target (the literal path is).
|
|
846
|
+
* Returns true when every appearance of `ident` is such a base accessor, so it
|
|
847
|
+
* shouldn't be treated as a tainted redirect target. A bare use or a user-input
|
|
848
|
+
* accessor (`.query`/`.nextUrl`/…) returns false → still flagged.
|
|
849
|
+
*/
|
|
850
|
+
export function appearsOnlyAsUrlBase(text, ident) {
|
|
851
|
+
const re = new RegExp(`(?<![.\\w])${ident.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b\\s*(?:\\.\\s*([A-Za-z_$][\\w$]*))?`, 'g');
|
|
852
|
+
let m;
|
|
853
|
+
let appeared = false;
|
|
854
|
+
while ((m = re.exec(text)) !== null) {
|
|
855
|
+
appeared = true;
|
|
856
|
+
if (!m[1] || !URL_BASE_ACCESSOR.test(m[1]))
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
return appeared;
|
|
860
|
+
}
|
|
861
|
+
/** Find every (parameter-slot → sink) pair inside one function body. */
|
|
862
|
+
export function sinksUsingParams(fn, fileContent) {
|
|
863
|
+
const out = [];
|
|
864
|
+
const body = fn.bodyText;
|
|
865
|
+
const seen = new Set();
|
|
866
|
+
for (const spec of SINK_SPECS) {
|
|
867
|
+
const re = new RegExp(spec.re.source, 'g');
|
|
868
|
+
let m;
|
|
869
|
+
while ((m = re.exec(body)) !== null) {
|
|
870
|
+
const parenInBody = m.index + m[0].length - 1;
|
|
871
|
+
const args = readBalanced(body, parenInBody, '(', ')');
|
|
872
|
+
if (!args)
|
|
873
|
+
continue;
|
|
874
|
+
// For SQL sinks only the first argument (the query string) is injectable.
|
|
875
|
+
// A tainted value sitting in the bound-params slot — `conn.execute(sql, [id])`,
|
|
876
|
+
// `$queryRawUnsafe(sql, id)` — is parameterized and safe, so don't scan it.
|
|
877
|
+
// maskNonCode keeps `${…}` interpolation, so a real `\`...${id}\`` / `"..."+id`
|
|
878
|
+
// query still shows the ident in arg[0].
|
|
879
|
+
let searchText = args.text;
|
|
880
|
+
if (spec.kind === 'sql') {
|
|
881
|
+
const arg0 = (splitArgs(args.text)[0] ?? '').trim();
|
|
882
|
+
// Raw SQL takes a query string as arg[0]. An object-literal first arg means
|
|
883
|
+
// a structured `.execute({...})` — an HTTP/queue/postback adapter, not SQL.
|
|
884
|
+
if (arg0.startsWith('{'))
|
|
885
|
+
continue;
|
|
886
|
+
searchText = arg0;
|
|
887
|
+
}
|
|
888
|
+
for (let slot = 0; slot < fn.paramSlots.length; slot++) {
|
|
889
|
+
for (const ident of fn.paramSlots[slot]) {
|
|
890
|
+
if (!ident)
|
|
891
|
+
continue;
|
|
892
|
+
if (identAppears(searchText, ident)) {
|
|
893
|
+
// A request param used only as a URL base in a redirect (e.g.
|
|
894
|
+
// `new URL("/login", req.url)`) is not the redirect target.
|
|
895
|
+
if (spec.kind === 'redirect' && appearsOnlyAsUrlBase(searchText, ident))
|
|
896
|
+
continue;
|
|
897
|
+
const key = `${slot}:${spec.kind}`;
|
|
898
|
+
if (seen.has(key))
|
|
899
|
+
continue;
|
|
900
|
+
seen.add(key);
|
|
901
|
+
const sinkOffset = fn.bodyOpenIdx + m.index;
|
|
902
|
+
const sinkLine = lineOf(fileContent, sinkOffset);
|
|
903
|
+
out.push({ slot, paramName: ident, kind: spec.kind, sinkLine, sinkOffset });
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return out;
|
|
910
|
+
}
|
|
911
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
912
|
+
// Taint at call sites
|
|
913
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
914
|
+
/** Collect local variables assigned directly from request input within a file. */
|
|
915
|
+
export function taintedLocals(content) {
|
|
916
|
+
const vars = new Set();
|
|
917
|
+
const decl = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*|\{[^}]*\}|\[[^\]]*\])\s*=\s*([^;\n]+)/g;
|
|
918
|
+
let m;
|
|
919
|
+
while ((m = decl.exec(content)) !== null) {
|
|
920
|
+
if (!REQUEST_RE.test(m[2]))
|
|
921
|
+
continue;
|
|
922
|
+
const target = m[1].trim();
|
|
923
|
+
if (target.startsWith('{') || target.startsWith('[')) {
|
|
924
|
+
for (const id of target.match(/[A-Za-z_$][\w$]*/g) ?? [])
|
|
925
|
+
vars.add(id);
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
vars.add(target);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return vars;
|
|
932
|
+
}
|
|
933
|
+
export function isTainted(argText, locals) {
|
|
934
|
+
if (REQUEST_RE.test(argText))
|
|
935
|
+
return true;
|
|
936
|
+
for (const v of locals) {
|
|
937
|
+
if (identAppears(argText, v))
|
|
938
|
+
return true;
|
|
939
|
+
}
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Run the full analysis over an in-memory set of source files.
|
|
944
|
+
* Pure and deterministic — the agent is a thin wrapper around this.
|
|
945
|
+
*/
|
|
946
|
+
export function analyzeTaint(files) {
|
|
947
|
+
// Pass 1 — discover sink wrappers across all files.
|
|
948
|
+
const wrappers = [];
|
|
949
|
+
const byName = new Map();
|
|
950
|
+
for (const file of files) {
|
|
951
|
+
const masked = maskNonCode(file.content);
|
|
952
|
+
for (const fn of extractFunctions(masked)) {
|
|
953
|
+
if (fn.paramSlots.length === 0)
|
|
954
|
+
continue;
|
|
955
|
+
for (const hit of sinksUsingParams(fn, masked)) {
|
|
956
|
+
const w = {
|
|
957
|
+
fnName: fn.name,
|
|
958
|
+
file: file.path,
|
|
959
|
+
declLine: fn.declLine,
|
|
960
|
+
paramName: hit.paramName,
|
|
961
|
+
paramSlot: hit.slot,
|
|
962
|
+
sinkKind: hit.kind,
|
|
963
|
+
sinkLine: hit.sinkLine,
|
|
964
|
+
};
|
|
965
|
+
wrappers.push(w);
|
|
966
|
+
const list = byName.get(fn.name) ?? [];
|
|
967
|
+
list.push(w);
|
|
968
|
+
byName.set(fn.name, list);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
// Pass 1.5 — GraphQL resolvers whose `args` reaches a sink directly. The
|
|
973
|
+
// resolver itself is the wrapper, but its caller is the GraphQL framework
|
|
974
|
+
// (not visible in code), so we'd never find a tainted call site in pass 2.
|
|
975
|
+
// Emit the flow here instead. Only the args slot (positionally 2nd = idx 1)
|
|
976
|
+
// counts as a request source.
|
|
977
|
+
const flows = [];
|
|
978
|
+
const seen = new Set();
|
|
979
|
+
for (const file of files) {
|
|
980
|
+
const masked = maskNonCode(file.content);
|
|
981
|
+
for (const r of extractResolvers(masked)) {
|
|
982
|
+
for (const hit of sinksUsingParams(r.fn, masked)) {
|
|
983
|
+
if (hit.slot !== 1)
|
|
984
|
+
continue;
|
|
985
|
+
const key = `${file.path}:${hit.sinkLine}:${r.fn.name}:${hit.slot}:${hit.kind}:resolver`;
|
|
986
|
+
if (seen.has(key))
|
|
987
|
+
continue;
|
|
988
|
+
seen.add(key);
|
|
989
|
+
flows.push({
|
|
990
|
+
callFile: file.path,
|
|
991
|
+
callLine: hit.sinkLine,
|
|
992
|
+
wrapperFn: r.fn.name,
|
|
993
|
+
paramSlot: hit.slot,
|
|
994
|
+
sinkFile: file.path,
|
|
995
|
+
sinkKind: hit.kind,
|
|
996
|
+
crossFile: false,
|
|
997
|
+
snippet: snippetOf(file.content, hit.sinkOffset),
|
|
998
|
+
resolverDirect: true,
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
// Pass 2 — find tainted call sites of those wrappers.
|
|
1004
|
+
for (const file of files) {
|
|
1005
|
+
const masked = maskNonCode(file.content);
|
|
1006
|
+
const locals = taintedLocals(masked);
|
|
1007
|
+
// Inside a resolver body, the resolver's args-slot identifiers are also
|
|
1008
|
+
// taint sources. Scoping to the body offset range avoids tainting random
|
|
1009
|
+
// `args` locals elsewhere in the file (the original FP shape).
|
|
1010
|
+
const resolvers = extractResolvers(masked);
|
|
1011
|
+
for (const [name, defs] of byName) {
|
|
1012
|
+
const callRe = new RegExp(`(?<![.\\w])${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\(`, 'g');
|
|
1013
|
+
let m;
|
|
1014
|
+
while ((m = callRe.exec(masked)) !== null) {
|
|
1015
|
+
// Skip the wrapper's own declaration site.
|
|
1016
|
+
const before = masked.slice(Math.max(0, m.index - 12), m.index);
|
|
1017
|
+
if (/\bfunction\s+$/.test(before))
|
|
1018
|
+
continue;
|
|
1019
|
+
const parenIdx = m.index + m[0].length - 1;
|
|
1020
|
+
const args = readBalanced(masked, parenIdx, '(', ')');
|
|
1021
|
+
if (!args)
|
|
1022
|
+
continue;
|
|
1023
|
+
const argList = splitArgs(args.text);
|
|
1024
|
+
let resolverTaint = [];
|
|
1025
|
+
for (const r of resolvers) {
|
|
1026
|
+
if (m.index >= r.bodyStart && m.index < r.bodyEnd) {
|
|
1027
|
+
resolverTaint = unshadowedResolverTaint(r, m.index);
|
|
1028
|
+
break;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
for (const def of defs) {
|
|
1032
|
+
const slotArg = argList[def.paramSlot];
|
|
1033
|
+
if (slotArg === undefined)
|
|
1034
|
+
continue;
|
|
1035
|
+
const tainted = isTainted(slotArg, locals)
|
|
1036
|
+
|| (resolverTaint.length > 0 && resolverTaint.some((n) => identAppears(slotArg, n)));
|
|
1037
|
+
if (!tainted)
|
|
1038
|
+
continue;
|
|
1039
|
+
const callLine = lineOf(file.content, m.index);
|
|
1040
|
+
const key = `${file.path}:${callLine}:${name}:${def.paramSlot}:${def.sinkKind}`;
|
|
1041
|
+
if (seen.has(key))
|
|
1042
|
+
continue;
|
|
1043
|
+
seen.add(key);
|
|
1044
|
+
flows.push({
|
|
1045
|
+
callFile: file.path,
|
|
1046
|
+
callLine,
|
|
1047
|
+
wrapperFn: name,
|
|
1048
|
+
paramSlot: def.paramSlot,
|
|
1049
|
+
sinkFile: def.file,
|
|
1050
|
+
sinkKind: def.sinkKind,
|
|
1051
|
+
crossFile: file.path !== def.file,
|
|
1052
|
+
snippet: snippetOf(file.content, m.index),
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
// Pass 3 — NestJS: class-method sink-wrappers reached via SAME-CLASS `this.method()`,
|
|
1059
|
+
// plus request params decorated with @Body/@Query/@Param/… as taint sources.
|
|
1060
|
+
// Deliberately isolated from passes 1-2: class methods are never standalone
|
|
1061
|
+
// wrappers (that collided with ORM `.query()` and was reverted), and call sites are
|
|
1062
|
+
// gated to `this.method(...)` within the same class — a bare `.find(`/`.map(` member
|
|
1063
|
+
// call can never match, so Array.prototype methods don't false-positive.
|
|
1064
|
+
for (const file of files) {
|
|
1065
|
+
const masked = maskNonCode(file.content);
|
|
1066
|
+
const locals = taintedLocals(masked);
|
|
1067
|
+
for (const cls of extractClasses(masked)) {
|
|
1068
|
+
// Same-class sink-wrappers (a method whose param reaches a sink), keyed by name.
|
|
1069
|
+
const classWrappers = new Map();
|
|
1070
|
+
for (const method of cls.methods) {
|
|
1071
|
+
const fnDef = {
|
|
1072
|
+
name: method.name, paramSlots: method.paramSlots,
|
|
1073
|
+
bodyText: method.bodyText, bodyOpenIdx: method.bodyAbsIdx,
|
|
1074
|
+
declLine: lineOf(masked, method.bodyAbsIdx),
|
|
1075
|
+
};
|
|
1076
|
+
const hits = sinksUsingParams(fnDef, masked);
|
|
1077
|
+
if (hits.length > 0)
|
|
1078
|
+
classWrappers.set(method.name, hits.map((h) => ({ slot: h.slot, kind: h.kind })));
|
|
1079
|
+
}
|
|
1080
|
+
for (const method of cls.methods) {
|
|
1081
|
+
const sources = method.sourceParamIds;
|
|
1082
|
+
// NestJS flows REQUIRE a decorated request param (@Body/@Query/@Param/…) on
|
|
1083
|
+
// this method — the distinctive, low-false-positive signal (the class-method
|
|
1084
|
+
// analogue of the resolver-signature gate). A plain utility method that merely
|
|
1085
|
+
// uses request-ish NAMES internally — e.g. an HTTP client's
|
|
1086
|
+
// `concurrent(requests)` calling `this.request(req.url, req.body, …)` where
|
|
1087
|
+
// `req` is an outbound config, not inbound input — is correctly excluded.
|
|
1088
|
+
// (Trade-off: Express-in-a-class `handle(req, res)` without decorators is a
|
|
1089
|
+
// conservative MISS rather than an FP; NestJS, the target, always decorates.)
|
|
1090
|
+
if (sources.length === 0)
|
|
1091
|
+
continue;
|
|
1092
|
+
const fnDef = {
|
|
1093
|
+
name: method.name, paramSlots: method.paramSlots,
|
|
1094
|
+
bodyText: method.bodyText, bodyOpenIdx: method.bodyAbsIdx,
|
|
1095
|
+
declLine: lineOf(masked, method.bodyAbsIdx),
|
|
1096
|
+
};
|
|
1097
|
+
// (a) Direct: a decorated request param reaches a sink in this method's own body.
|
|
1098
|
+
for (const hit of sinksUsingParams(fnDef, masked)) {
|
|
1099
|
+
if (!sources.includes(hit.paramName))
|
|
1100
|
+
continue;
|
|
1101
|
+
// A decorated source rebound by a nested callback at the sink is not the request value.
|
|
1102
|
+
if (unshadowedAt(method.bodyText, method.bodyAbsIdx, [hit.paramName], hit.sinkOffset).length === 0)
|
|
1103
|
+
continue;
|
|
1104
|
+
const callLine = lineOf(file.content, hit.sinkOffset);
|
|
1105
|
+
const key = `${file.path}:${callLine}:${method.name}:${hit.slot}:${hit.kind}:nest-direct`;
|
|
1106
|
+
if (seen.has(key))
|
|
1107
|
+
continue;
|
|
1108
|
+
seen.add(key);
|
|
1109
|
+
flows.push({
|
|
1110
|
+
callFile: file.path, callLine, wrapperFn: method.name, paramSlot: hit.slot,
|
|
1111
|
+
sinkFile: file.path, sinkKind: hit.kind, crossFile: false,
|
|
1112
|
+
snippet: snippetOf(file.content, hit.sinkOffset),
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
// (b) Laundered: `this.wrapper(tainted)` where wrapper is a same-class sink-wrapper.
|
|
1116
|
+
const callRe = /this\s*\.\s*([A-Za-z_$][\w$]*)\s*\(/g;
|
|
1117
|
+
let cm;
|
|
1118
|
+
while ((cm = callRe.exec(method.bodyText)) !== null) {
|
|
1119
|
+
const targets = classWrappers.get(cm[1]);
|
|
1120
|
+
if (!targets)
|
|
1121
|
+
continue;
|
|
1122
|
+
const parenIdx = cm.index + cm[0].length - 1;
|
|
1123
|
+
const args = readBalanced(method.bodyText, parenIdx, '(', ')');
|
|
1124
|
+
if (!args)
|
|
1125
|
+
continue;
|
|
1126
|
+
const argList = splitArgs(args.text);
|
|
1127
|
+
const callAbs = method.bodyAbsIdx + cm.index;
|
|
1128
|
+
// Drop decorated sources that a nested callback enclosing THIS call rebinds
|
|
1129
|
+
// (e.g. `items.map(body => this.run(body.id))` — the inner `body` is local,
|
|
1130
|
+
// not the request body), mirroring the resolver path's shadow handling.
|
|
1131
|
+
const liveSources = unshadowedAt(method.bodyText, method.bodyAbsIdx, sources, callAbs);
|
|
1132
|
+
for (const t of targets) {
|
|
1133
|
+
const arg = argList[t.slot];
|
|
1134
|
+
if (arg === undefined)
|
|
1135
|
+
continue;
|
|
1136
|
+
const tainted = isTainted(arg, locals) || liveSources.some((s) => identAppears(arg, s));
|
|
1137
|
+
if (!tainted)
|
|
1138
|
+
continue;
|
|
1139
|
+
const callLine = lineOf(file.content, callAbs);
|
|
1140
|
+
const key = `${file.path}:${callLine}:${cm[1]}:${t.slot}:${t.kind}:nest`;
|
|
1141
|
+
if (seen.has(key))
|
|
1142
|
+
continue;
|
|
1143
|
+
seen.add(key);
|
|
1144
|
+
flows.push({
|
|
1145
|
+
callFile: file.path, callLine, wrapperFn: cm[1], paramSlot: t.slot,
|
|
1146
|
+
sinkFile: file.path, sinkKind: t.kind, crossFile: false,
|
|
1147
|
+
snippet: snippetOf(file.content, callAbs),
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return { wrappers, flows };
|
|
1155
|
+
}
|
|
1156
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1157
|
+
// Agent
|
|
1158
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1159
|
+
const SINK_LABEL = {
|
|
1160
|
+
sql: 'a raw-SQL',
|
|
1161
|
+
eval: 'a dynamic-code-execution',
|
|
1162
|
+
command: 'a shell-command',
|
|
1163
|
+
'fs-path': 'a filesystem-path',
|
|
1164
|
+
redirect: 'an HTTP-redirect',
|
|
1165
|
+
ssrf: 'an outbound-HTTP (SSRF)',
|
|
1166
|
+
};
|
|
1167
|
+
export class CrossFileTaintTrackerAgent extends BaseAgent {
|
|
1168
|
+
agentId = 112;
|
|
1169
|
+
agentName = 'Cross-File Taint Tracker';
|
|
1170
|
+
async preFlight() {
|
|
1171
|
+
const root = this.config.projectRoot ?? process.cwd();
|
|
1172
|
+
if (!fs.existsSync(root)) {
|
|
1173
|
+
throw new Error(`projectRoot does not exist: ${root}`);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
async execute() {
|
|
1177
|
+
const findings = [];
|
|
1178
|
+
const projectRoot = this.config.projectRoot ?? process.cwd();
|
|
1179
|
+
const filePaths = new Set();
|
|
1180
|
+
const mapPath = path.join(this.runDir, 'evidence', 'connection-map.json');
|
|
1181
|
+
try {
|
|
1182
|
+
const map = JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
|
|
1183
|
+
for (const f of map.files) {
|
|
1184
|
+
if (!f.isTest)
|
|
1185
|
+
filePaths.add(path.join(projectRoot, f.path));
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
catch {
|
|
1189
|
+
for (const f of walk(projectRoot))
|
|
1190
|
+
filePaths.add(f);
|
|
1191
|
+
}
|
|
1192
|
+
const files = [];
|
|
1193
|
+
for (const abs of filePaths) {
|
|
1194
|
+
if (isTestPath(abs, projectRoot))
|
|
1195
|
+
continue;
|
|
1196
|
+
let content;
|
|
1197
|
+
try {
|
|
1198
|
+
content = fs.readFileSync(abs, 'utf-8');
|
|
1199
|
+
}
|
|
1200
|
+
catch {
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
files.push({ path: relativise(abs, projectRoot), content });
|
|
1204
|
+
}
|
|
1205
|
+
const { wrappers, flows } = analyzeTaint(files);
|
|
1206
|
+
this.persistAudit(projectRoot, files.length, wrappers.length, flows);
|
|
1207
|
+
if (flows.length === 0) {
|
|
1208
|
+
findings.push({
|
|
1209
|
+
id: `${this.agentId}-clean`,
|
|
1210
|
+
type: 'infra-issue',
|
|
1211
|
+
severity: 'info',
|
|
1212
|
+
agentId: this.agentId,
|
|
1213
|
+
module: 'taint-tracker',
|
|
1214
|
+
description: `Scanned ${files.length} non-test file(s), ${wrappers.length} sink-wrapper(s) — no request input reaches a dangerous sink through a helper.`,
|
|
1215
|
+
});
|
|
1216
|
+
return findings;
|
|
1217
|
+
}
|
|
1218
|
+
for (const flow of flows.slice(0, 100)) {
|
|
1219
|
+
const description = flow.resolverDirect
|
|
1220
|
+
? `GraphQL resolver \`${flow.wrapperFn}\` at ${flow.callFile}:${flow.callLine} reaches ${SINK_LABEL[flow.sinkKind]} sink with its \`args\` directly. The GraphQL framework supplies \`args\` from the client query — validate or parameterise it before the sink. \`${flow.snippet}\``
|
|
1221
|
+
: (() => {
|
|
1222
|
+
const where = flow.crossFile
|
|
1223
|
+
? `${flow.callFile}:${flow.callLine} passes request data to ${flow.wrapperFn}() (defined in ${flow.sinkFile})`
|
|
1224
|
+
: `${flow.callFile}:${flow.callLine} passes request data to ${flow.wrapperFn}() in the same file`;
|
|
1225
|
+
return `${flow.crossFile ? 'Cross-file' : 'Same-file'} taint flow into ${SINK_LABEL[flow.sinkKind]} sink: ${where}, whose parameter #${flow.paramSlot} reaches the sink unsanitised. Validate or parameterise the input at the boundary before it reaches ${flow.wrapperFn}(). \`${flow.snippet}\``;
|
|
1226
|
+
})();
|
|
1227
|
+
const idSuffix = flow.resolverDirect ? '-resolver' : '';
|
|
1228
|
+
findings.push({
|
|
1229
|
+
id: `${this.agentId}-taint-flow-${flow.callFile.replace(/[^\w]/g, '_')}-${flow.callLine}-${flow.wrapperFn}${idSuffix}`,
|
|
1230
|
+
type: 'code-bug-security',
|
|
1231
|
+
severity: 'high',
|
|
1232
|
+
agentId: this.agentId,
|
|
1233
|
+
module: 'taint-tracker',
|
|
1234
|
+
description,
|
|
1235
|
+
file: flow.callFile,
|
|
1236
|
+
line: flow.callLine,
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
findings.push({
|
|
1240
|
+
id: `${this.agentId}-summary`,
|
|
1241
|
+
type: 'infra-issue',
|
|
1242
|
+
severity: 'info',
|
|
1243
|
+
agentId: this.agentId,
|
|
1244
|
+
module: 'taint-tracker',
|
|
1245
|
+
description: `Taint analysis: ${flows.length} flow(s) (${flows.filter(f => f.crossFile).length} cross-file) through ${wrappers.length} sink-wrapper(s) across ${files.length} file(s).`,
|
|
1246
|
+
});
|
|
1247
|
+
return findings;
|
|
1248
|
+
}
|
|
1249
|
+
persistAudit(projectRoot, filesScanned, wrappers, flows) {
|
|
1250
|
+
const audit = {
|
|
1251
|
+
version: 1,
|
|
1252
|
+
generatedAt: new Date().toISOString(),
|
|
1253
|
+
projectRoot,
|
|
1254
|
+
filesScanned,
|
|
1255
|
+
wrappers,
|
|
1256
|
+
flows,
|
|
1257
|
+
};
|
|
1258
|
+
try {
|
|
1259
|
+
const evidenceDir = path.join(this.runDir, 'evidence');
|
|
1260
|
+
fs.mkdirSync(evidenceDir, { recursive: true });
|
|
1261
|
+
fs.writeFileSync(path.join(evidenceDir, 'taint-analysis.json'), JSON.stringify(audit, null, 2), 'utf-8');
|
|
1262
|
+
this.addEvidence({
|
|
1263
|
+
type: 'report',
|
|
1264
|
+
path: 'evidence/taint-analysis.json',
|
|
1265
|
+
description: `Taint analysis: ${flows.length} flow(s) through ${wrappers} sink-wrapper(s) across ${filesScanned} file(s)`,
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
catch {
|
|
1269
|
+
// non-fatal
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
//# sourceMappingURL=112-taint-tracker.js.map
|