@agentic-qe/v3 3.0.0-alpha.6 → 3.0.0-alpha.7
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/assets/agents/v3/subagents/v3-qe-code-reviewer.md +339 -0
- package/assets/agents/v3/subagents/v3-qe-integration-reviewer.md +344 -0
- package/assets/agents/v3/subagents/v3-qe-performance-reviewer.md +351 -0
- package/assets/agents/v3/subagents/v3-qe-security-reviewer.md +374 -0
- package/assets/agents/v3/subagents/v3-qe-tdd-green.md +334 -0
- package/assets/agents/v3/subagents/v3-qe-tdd-red.md +329 -0
- package/assets/agents/v3/subagents/v3-qe-tdd-refactor.md +361 -0
- package/assets/agents/v3/v3-qe-accessibility-auditor.md +266 -0
- package/assets/agents/v3/v3-qe-bdd-generator.md +279 -0
- package/assets/agents/v3/v3-qe-chaos-engineer.md +265 -0
- package/assets/agents/v3/v3-qe-code-complexity.md +298 -0
- package/assets/agents/v3/v3-qe-code-intelligence.md +262 -0
- package/assets/agents/v3/v3-qe-contract-validator.md +267 -0
- package/assets/agents/v3/v3-qe-coverage-specialist.md +227 -0
- package/assets/agents/v3/v3-qe-defect-predictor.md +251 -0
- package/assets/agents/v3/v3-qe-dependency-mapper.md +277 -0
- package/assets/agents/v3/v3-qe-deployment-advisor.md +275 -0
- package/assets/agents/v3/v3-qe-flaky-hunter.md +248 -0
- package/assets/agents/v3/v3-qe-fleet-commander.md +293 -0
- package/assets/agents/v3/v3-qe-gap-detector.md +260 -0
- package/assets/agents/v3/v3-qe-graphql-tester.md +308 -0
- package/assets/agents/v3/v3-qe-impact-analyzer.md +299 -0
- package/assets/agents/v3/v3-qe-integration-tester.md +238 -0
- package/assets/agents/v3/v3-qe-kg-builder.md +273 -0
- package/assets/agents/v3/v3-qe-learning-coordinator.md +226 -0
- package/assets/agents/v3/v3-qe-load-tester.md +280 -0
- package/assets/agents/v3/v3-qe-metrics-optimizer.md +300 -0
- package/assets/agents/v3/v3-qe-mutation-tester.md +301 -0
- package/assets/agents/v3/v3-qe-parallel-executor.md +240 -0
- package/assets/agents/v3/v3-qe-pattern-learner.md +271 -0
- package/assets/agents/v3/v3-qe-performance-tester.md +262 -0
- package/assets/agents/v3/v3-qe-property-tester.md +247 -0
- package/assets/agents/v3/v3-qe-quality-gate.md +218 -0
- package/assets/agents/v3/v3-qe-queen-coordinator.md +214 -0
- package/assets/agents/v3/v3-qe-qx-partner.md +313 -0
- package/assets/agents/v3/v3-qe-regression-analyzer.md +322 -0
- package/assets/agents/v3/v3-qe-requirements-validator.md +360 -0
- package/assets/agents/v3/v3-qe-responsive-tester.md +311 -0
- package/assets/agents/v3/v3-qe-retry-handler.md +256 -0
- package/assets/agents/v3/v3-qe-risk-assessor.md +273 -0
- package/assets/agents/v3/v3-qe-root-cause-analyzer.md +286 -0
- package/assets/agents/v3/v3-qe-security-auditor.md +299 -0
- package/assets/agents/v3/v3-qe-security-scanner.md +235 -0
- package/assets/agents/v3/v3-qe-tdd-specialist.md +239 -0
- package/assets/agents/v3/v3-qe-test-architect.md +233 -0
- package/assets/agents/v3/v3-qe-transfer-specialist.md +295 -0
- package/assets/agents/v3/v3-qe-visual-tester.md +232 -0
- package/assets/skills/accessibility-testing/SKILL.md +216 -0
- package/assets/skills/agentdb-advanced/SKILL.md +550 -0
- package/assets/skills/agentdb-learning/SKILL.md +545 -0
- package/assets/skills/agentdb-memory-patterns/SKILL.md +339 -0
- package/assets/skills/agentdb-optimization/SKILL.md +509 -0
- package/assets/skills/agentdb-vector-search/SKILL.md +339 -0
- package/assets/skills/agentic-jujutsu/SKILL.md +645 -0
- package/assets/skills/agentic-quality-engineering/SKILL.md +335 -0
- package/assets/skills/api-testing-patterns/SKILL.md +294 -0
- package/assets/skills/aqe-v2-v3-migration/skill.md +322 -0
- package/assets/skills/brutal-honesty-review/README.md +218 -0
- package/assets/skills/brutal-honesty-review/SKILL.md +235 -0
- package/assets/skills/brutal-honesty-review/resources/assessment-rubrics.md +295 -0
- package/assets/skills/brutal-honesty-review/resources/review-template.md +102 -0
- package/assets/skills/brutal-honesty-review/scripts/assess-code.sh +179 -0
- package/assets/skills/brutal-honesty-review/scripts/assess-tests.sh +223 -0
- package/assets/skills/bug-reporting-excellence/SKILL.md +225 -0
- package/assets/skills/chaos-engineering-resilience/SKILL.md +158 -0
- package/assets/skills/cicd-pipeline-qe-orchestrator/README.md +304 -0
- package/assets/skills/cicd-pipeline-qe-orchestrator/SKILL.md +315 -0
- package/assets/skills/cicd-pipeline-qe-orchestrator/resources/workflows/microservice-pipeline.md +239 -0
- package/assets/skills/cicd-pipeline-qe-orchestrator/resources/workflows/mobile-pipeline.md +375 -0
- package/assets/skills/cicd-pipeline-qe-orchestrator/resources/workflows/monolith-pipeline.md +268 -0
- package/assets/skills/code-review-quality/SKILL.md +227 -0
- package/assets/skills/compatibility-testing/SKILL.md +205 -0
- package/assets/skills/compliance-testing/SKILL.md +225 -0
- package/assets/skills/consultancy-practices/SKILL.md +202 -0
- package/assets/skills/context-driven-testing/SKILL.md +196 -0
- package/assets/skills/contract-testing/SKILL.md +222 -0
- package/assets/skills/database-testing/SKILL.md +244 -0
- package/assets/skills/exploratory-testing-advanced/SKILL.md +201 -0
- package/assets/skills/flow-nexus-neural/SKILL.md +738 -0
- package/assets/skills/flow-nexus-platform/SKILL.md +1157 -0
- package/assets/skills/flow-nexus-swarm/SKILL.md +610 -0
- package/assets/skills/github-code-review/SKILL.md +1140 -0
- package/assets/skills/github-multi-repo/SKILL.md +874 -0
- package/assets/skills/github-project-management/SKILL.md +1277 -0
- package/assets/skills/github-release-management/SKILL.md +1081 -0
- package/assets/skills/github-workflow-automation/SKILL.md +1065 -0
- package/assets/skills/hive-mind-advanced/SKILL.md +712 -0
- package/assets/skills/holistic-testing-pact/SKILL.md +171 -0
- package/assets/skills/hooks-automation/SKILL.md +1201 -0
- package/assets/skills/localization-testing/SKILL.md +221 -0
- package/assets/skills/mobile-testing/SKILL.md +219 -0
- package/assets/skills/mutation-testing/SKILL.md +229 -0
- package/assets/skills/n8n-expression-testing/SKILL.md +434 -0
- package/assets/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
- package/assets/skills/n8n-security-testing/SKILL.md +599 -0
- package/assets/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
- package/assets/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
- package/assets/skills/pair-programming/SKILL.md +1202 -0
- package/assets/skills/performance-analysis/SKILL.md +563 -0
- package/assets/skills/performance-testing/SKILL.md +310 -0
- package/assets/skills/quality-metrics/SKILL.md +225 -0
- package/assets/skills/reasoningbank-agentdb/SKILL.md +446 -0
- package/assets/skills/reasoningbank-intelligence/SKILL.md +201 -0
- package/assets/skills/refactoring-patterns/SKILL.md +205 -0
- package/assets/skills/regression-testing/SKILL.md +227 -0
- package/assets/skills/risk-based-testing/SKILL.md +206 -0
- package/assets/skills/security-testing/SKILL.md +306 -0
- package/assets/skills/sherlock-review/SKILL.md +250 -0
- package/assets/skills/shift-left-testing/SKILL.md +225 -0
- package/assets/skills/shift-right-testing/SKILL.md +227 -0
- package/assets/skills/six-thinking-hats/README.md +190 -0
- package/assets/skills/six-thinking-hats/SKILL.md +280 -0
- package/assets/skills/six-thinking-hats/resources/examples/api-testing-example.md +345 -0
- package/assets/skills/six-thinking-hats/resources/templates/solo-session-template.md +167 -0
- package/assets/skills/six-thinking-hats/resources/templates/team-session-template.md +336 -0
- package/assets/skills/skill-builder/SKILL.md +910 -0
- package/assets/skills/sparc-methodology/SKILL.md +1115 -0
- package/assets/skills/stream-chain/SKILL.md +563 -0
- package/assets/skills/swarm-advanced/SKILL.md +973 -0
- package/assets/skills/swarm-orchestration/SKILL.md +179 -0
- package/assets/skills/tdd-london-chicago/SKILL.md +244 -0
- package/assets/skills/technical-writing/SKILL.md +178 -0
- package/assets/skills/test-automation-strategy/SKILL.md +230 -0
- package/assets/skills/test-data-management/SKILL.md +270 -0
- package/assets/skills/test-design-techniques/SKILL.md +244 -0
- package/assets/skills/test-environment-management/SKILL.md +243 -0
- package/assets/skills/test-reporting-analytics/SKILL.md +214 -0
- package/assets/skills/testability-scoring/README.md +71 -0
- package/assets/skills/testability-scoring/SKILL.md +346 -0
- package/assets/skills/testability-scoring/resources/templates/config.template.js +84 -0
- package/assets/skills/testability-scoring/resources/templates/testability-scoring.spec.template.js +532 -0
- package/assets/skills/testability-scoring/scripts/generate-html-report.js +1007 -0
- package/assets/skills/testability-scoring/scripts/run-assessment.sh +70 -0
- package/assets/skills/v3-qe-chaos-resilience/SKILL.md +238 -0
- package/assets/skills/v3-qe-code-intelligence/SKILL.md +209 -0
- package/assets/skills/v3-qe-contract-testing/SKILL.md +218 -0
- package/assets/skills/v3-qe-coverage-analysis/SKILL.md +187 -0
- package/assets/skills/v3-qe-defect-intelligence/SKILL.md +205 -0
- package/assets/skills/v3-qe-learning-optimization/SKILL.md +238 -0
- package/assets/skills/v3-qe-quality-assessment/SKILL.md +213 -0
- package/assets/skills/v3-qe-requirements-validation/SKILL.md +248 -0
- package/assets/skills/v3-qe-test-execution/SKILL.md +182 -0
- package/assets/skills/v3-qe-test-generation/SKILL.md +141 -0
- package/assets/skills/v3-qe-visual-accessibility/SKILL.md +242 -0
- package/assets/skills/verification-quality/SKILL.md +649 -0
- package/assets/skills/visual-testing-advanced/SKILL.md +219 -0
- package/assets/skills/xp-practices/SKILL.md +229 -0
- package/dist/cli/bundle.js +8 -8
- package/dist/init/agents-installer.js +4 -4
- package/dist/init/agents-installer.js.map +1 -1
- package/dist/init/skills-installer.js +4 -4
- package/dist/init/skills-installer.js.map +1 -1
- package/package.json +7 -1
- package/docs/analysis/V3-INIT-REQUIREMENTS-ANALYSIS.md +0 -352
- package/implementation/README.md +0 -90
- package/implementation/adrs/ADR-030-coherence-gated-quality-gates.md +0 -312
- package/implementation/adrs/ADR-031-strange-loop-self-awareness.md +0 -484
- package/implementation/adrs/ADR-032-time-crystal-scheduling.md +0 -530
- package/implementation/adrs/ADR-033-early-exit-testing.md +0 -634
- package/implementation/adrs/ADR-034-neural-topology-optimizer.md +0 -589
- package/implementation/adrs/ADR-035-causal-discovery.md +0 -610
- package/implementation/adrs/ADR-036-result-persistence.md +0 -326
- package/implementation/adrs/ADR-037-v3-qe-agent-naming.md +0 -105
- package/implementation/adrs/ADR-038-v3-qe-memory-unification.md +0 -154
- package/implementation/adrs/ADR-039-v3-qe-mcp-optimization.md +0 -179
- package/implementation/adrs/ADR-040-v3-qe-agentic-flow-integration.md +0 -240
- package/implementation/adrs/ADR-041-v3-qe-cli-enhancement.md +0 -296
- package/implementation/adrs/ADR-042-v3-qe-token-tracking-integration.md +0 -517
- package/implementation/adrs/v3-adrs.md +0 -2783
- package/implementation/planning/AQE-V3-MASTER-PLAN.md +0 -815
- package/security-scan-report-2026-01-11.md +0 -410
- package/security-verification-report-2026-01-11.md +0 -278
- package/src/benchmarks/performance-benchmarks.ts +0 -646
- package/src/benchmarks/run-benchmarks.ts +0 -324
- package/src/causal-discovery/causal-graph.ts +0 -450
- package/src/causal-discovery/discovery-engine.ts +0 -438
- package/src/causal-discovery/index.ts +0 -117
- package/src/causal-discovery/types.ts +0 -456
- package/src/causal-discovery/weight-matrix.ts +0 -453
- package/src/cli/commands/qe-tools.ts +0 -634
- package/src/cli/index.ts +0 -1976
- package/src/compatibility/agent-mapper.ts +0 -291
- package/src/compatibility/cli-adapter.ts +0 -277
- package/src/compatibility/config-migrator.ts +0 -334
- package/src/compatibility/index.ts +0 -112
- package/src/compatibility/mcp-adapter.ts +0 -248
- package/src/compatibility/types.ts +0 -156
- package/src/coordination/claims/claim-repository.ts +0 -636
- package/src/coordination/claims/claim-service.ts +0 -675
- package/src/coordination/claims/handoff-manager.ts +0 -535
- package/src/coordination/claims/index.ts +0 -276
- package/src/coordination/claims/interfaces.ts +0 -687
- package/src/coordination/claims/work-stealing.ts +0 -436
- package/src/coordination/cross-domain-router.ts +0 -492
- package/src/coordination/index.ts +0 -127
- package/src/coordination/interfaces.ts +0 -691
- package/src/coordination/protocol-executor.ts +0 -760
- package/src/coordination/protocols/code-intelligence-index.ts +0 -855
- package/src/coordination/protocols/defect-investigation.ts +0 -1184
- package/src/coordination/protocols/index.ts +0 -11
- package/src/coordination/protocols/learning-consolidation.ts +0 -1181
- package/src/coordination/protocols/morning-sync.ts +0 -1055
- package/src/coordination/protocols/quality-gate.ts +0 -1566
- package/src/coordination/protocols/security-audit.ts +0 -1587
- package/src/coordination/queen-coordinator.ts +0 -1176
- package/src/coordination/result-saver.ts +0 -780
- package/src/coordination/task-executor.ts +0 -1146
- package/src/coordination/workflow-orchestrator.ts +0 -1917
- package/src/domains/chaos-resilience/coordinator.ts +0 -1032
- package/src/domains/chaos-resilience/index.ts +0 -143
- package/src/domains/chaos-resilience/interfaces.ts +0 -659
- package/src/domains/chaos-resilience/plugin.ts +0 -691
- package/src/domains/chaos-resilience/services/chaos-engineer.ts +0 -1097
- package/src/domains/chaos-resilience/services/index.ts +0 -19
- package/src/domains/chaos-resilience/services/load-tester.ts +0 -799
- package/src/domains/chaos-resilience/services/performance-profiler.ts +0 -792
- package/src/domains/code-intelligence/coordinator.ts +0 -631
- package/src/domains/code-intelligence/index.ts +0 -86
- package/src/domains/code-intelligence/interfaces.ts +0 -162
- package/src/domains/code-intelligence/plugin.ts +0 -451
- package/src/domains/code-intelligence/services/impact-analyzer.ts +0 -567
- package/src/domains/code-intelligence/services/index.ts +0 -26
- package/src/domains/code-intelligence/services/knowledge-graph.ts +0 -1067
- package/src/domains/code-intelligence/services/semantic-analyzer.ts +0 -901
- package/src/domains/contract-testing/coordinator.ts +0 -1038
- package/src/domains/contract-testing/index.ts +0 -122
- package/src/domains/contract-testing/interfaces.ts +0 -458
- package/src/domains/contract-testing/plugin.ts +0 -746
- package/src/domains/contract-testing/services/api-compatibility.ts +0 -748
- package/src/domains/contract-testing/services/contract-validator.ts +0 -1700
- package/src/domains/contract-testing/services/index.ts +0 -19
- package/src/domains/contract-testing/services/schema-validator.ts +0 -1102
- package/src/domains/coverage-analysis/coordinator.ts +0 -485
- package/src/domains/coverage-analysis/index.ts +0 -114
- package/src/domains/coverage-analysis/interfaces.ts +0 -142
- package/src/domains/coverage-analysis/plugin.ts +0 -172
- package/src/domains/coverage-analysis/services/coverage-analyzer.ts +0 -449
- package/src/domains/coverage-analysis/services/coverage-embedder.ts +0 -733
- package/src/domains/coverage-analysis/services/coverage-parser.ts +0 -753
- package/src/domains/coverage-analysis/services/gap-detector.ts +0 -592
- package/src/domains/coverage-analysis/services/hnsw-index.ts +0 -728
- package/src/domains/coverage-analysis/services/index.ts +0 -61
- package/src/domains/coverage-analysis/services/risk-scorer.ts +0 -540
- package/src/domains/coverage-analysis/services/sublinear-analyzer.ts +0 -747
- package/src/domains/defect-intelligence/coordinator.ts +0 -635
- package/src/domains/defect-intelligence/index.ts +0 -83
- package/src/domains/defect-intelligence/interfaces.ts +0 -152
- package/src/domains/defect-intelligence/plugin.ts +0 -483
- package/src/domains/defect-intelligence/services/causal-root-cause-analyzer.ts +0 -494
- package/src/domains/defect-intelligence/services/defect-predictor.ts +0 -852
- package/src/domains/defect-intelligence/services/index.ts +0 -37
- package/src/domains/defect-intelligence/services/pattern-learner.ts +0 -738
- package/src/domains/defect-intelligence/services/root-cause-analyzer.ts +0 -637
- package/src/domains/domain-interface.ts +0 -77
- package/src/domains/index.ts +0 -23
- package/src/domains/learning-optimization/coordinator.ts +0 -1215
- package/src/domains/learning-optimization/index.ts +0 -127
- package/src/domains/learning-optimization/interfaces.ts +0 -570
- package/src/domains/learning-optimization/plugin.ts +0 -851
- package/src/domains/learning-optimization/services/index.ts +0 -29
- package/src/domains/learning-optimization/services/learning-coordinator.ts +0 -972
- package/src/domains/learning-optimization/services/metrics-optimizer.ts +0 -915
- package/src/domains/learning-optimization/services/production-intel.ts +0 -971
- package/src/domains/learning-optimization/services/transfer-specialist.ts +0 -723
- package/src/domains/quality-assessment/coherence/gate-controller.ts +0 -549
- package/src/domains/quality-assessment/coherence/index.ts +0 -211
- package/src/domains/quality-assessment/coherence/lambda-calculator.ts +0 -384
- package/src/domains/quality-assessment/coherence/partition-detector.ts +0 -469
- package/src/domains/quality-assessment/coherence/types.ts +0 -384
- package/src/domains/quality-assessment/coordinator.ts +0 -605
- package/src/domains/quality-assessment/index.ts +0 -97
- package/src/domains/quality-assessment/interfaces.ts +0 -152
- package/src/domains/quality-assessment/plugin.ts +0 -496
- package/src/domains/quality-assessment/services/coherence-gate.ts +0 -358
- package/src/domains/quality-assessment/services/deployment-advisor.ts +0 -571
- package/src/domains/quality-assessment/services/index.ts +0 -34
- package/src/domains/quality-assessment/services/quality-analyzer.ts +0 -670
- package/src/domains/quality-assessment/services/quality-gate.ts +0 -384
- package/src/domains/requirements-validation/coordinator.ts +0 -812
- package/src/domains/requirements-validation/index.ts +0 -92
- package/src/domains/requirements-validation/interfaces.ts +0 -303
- package/src/domains/requirements-validation/plugin.ts +0 -576
- package/src/domains/requirements-validation/services/bdd-scenario-writer.ts +0 -676
- package/src/domains/requirements-validation/services/index.ts +0 -20
- package/src/domains/requirements-validation/services/requirements-validator.ts +0 -559
- package/src/domains/requirements-validation/services/testability-scorer.ts +0 -639
- package/src/domains/security-compliance/coordinator.ts +0 -757
- package/src/domains/security-compliance/index.ts +0 -120
- package/src/domains/security-compliance/interfaces.ts +0 -434
- package/src/domains/security-compliance/plugin.ts +0 -509
- package/src/domains/security-compliance/services/compliance-validator.ts +0 -1226
- package/src/domains/security-compliance/services/index.ts +0 -31
- package/src/domains/security-compliance/services/security-auditor.ts +0 -2227
- package/src/domains/security-compliance/services/security-scanner.ts +0 -2354
- package/src/domains/security-compliance/services/semgrep-integration.ts +0 -289
- package/src/domains/test-execution/coordinator.ts +0 -426
- package/src/domains/test-execution/index.ts +0 -76
- package/src/domains/test-execution/interfaces.ts +0 -119
- package/src/domains/test-execution/plugin.ts +0 -208
- package/src/domains/test-execution/services/flaky-detector.ts +0 -1240
- package/src/domains/test-execution/services/index.ts +0 -8
- package/src/domains/test-execution/services/retry-handler.ts +0 -820
- package/src/domains/test-execution/services/test-executor.ts +0 -885
- package/src/domains/test-generation/coordinator.ts +0 -656
- package/src/domains/test-generation/index.ts +0 -77
- package/src/domains/test-generation/interfaces.ts +0 -118
- package/src/domains/test-generation/plugin.ts +0 -397
- package/src/domains/test-generation/services/index.ts +0 -23
- package/src/domains/test-generation/services/pattern-matcher.ts +0 -1725
- package/src/domains/test-generation/services/test-generator.ts +0 -2750
- package/src/domains/visual-accessibility/coordinator.ts +0 -860
- package/src/domains/visual-accessibility/index.ts +0 -116
- package/src/domains/visual-accessibility/interfaces.ts +0 -435
- package/src/domains/visual-accessibility/plugin.ts +0 -568
- package/src/domains/visual-accessibility/services/accessibility-tester.ts +0 -982
- package/src/domains/visual-accessibility/services/axe-core-audit.ts +0 -630
- package/src/domains/visual-accessibility/services/index.ts +0 -28
- package/src/domains/visual-accessibility/services/responsive-tester.ts +0 -934
- package/src/domains/visual-accessibility/services/visual-tester.ts +0 -458
- package/src/early-exit/early-exit-controller.ts +0 -490
- package/src/early-exit/early-exit-decision.ts +0 -391
- package/src/early-exit/index.ts +0 -115
- package/src/early-exit/quality-signal.ts +0 -389
- package/src/early-exit/speculative-executor.ts +0 -505
- package/src/early-exit/types.ts +0 -407
- package/src/feedback/coverage-learner.ts +0 -456
- package/src/feedback/feedback-loop.ts +0 -426
- package/src/feedback/index.ts +0 -72
- package/src/feedback/pattern-promotion.ts +0 -373
- package/src/feedback/quality-score-calculator.ts +0 -334
- package/src/feedback/test-outcome-tracker.ts +0 -450
- package/src/feedback/types.ts +0 -497
- package/src/index.ts +0 -224
- package/src/init/agents-installer.ts +0 -536
- package/src/init/index.ts +0 -80
- package/src/init/init-wizard.ts +0 -1061
- package/src/init/project-analyzer.ts +0 -696
- package/src/init/self-configurator.ts +0 -488
- package/src/init/skills-installer.ts +0 -467
- package/src/init/types.ts +0 -432
- package/src/integrations/ruvector/ast-complexity.ts +0 -470
- package/src/integrations/ruvector/coverage-router.ts +0 -594
- package/src/integrations/ruvector/diff-risk-classifier.ts +0 -759
- package/src/integrations/ruvector/fallback.ts +0 -942
- package/src/integrations/ruvector/graph-boundaries.ts +0 -809
- package/src/integrations/ruvector/index.ts +0 -363
- package/src/integrations/ruvector/interfaces.ts +0 -609
- package/src/integrations/ruvector/q-learning-router.ts +0 -550
- package/src/kernel/agent-coordinator.ts +0 -165
- package/src/kernel/agentdb-backend.ts +0 -504
- package/src/kernel/event-bus.ts +0 -129
- package/src/kernel/hybrid-backend.ts +0 -538
- package/src/kernel/index.ts +0 -28
- package/src/kernel/interfaces.ts +0 -257
- package/src/kernel/kernel.ts +0 -285
- package/src/kernel/memory-backend.ts +0 -169
- package/src/kernel/memory-factory.ts +0 -293
- package/src/kernel/plugin-loader.ts +0 -179
- package/src/learning/index.ts +0 -219
- package/src/learning/pattern-store.ts +0 -990
- package/src/learning/qe-guidance.ts +0 -832
- package/src/learning/qe-hooks.ts +0 -644
- package/src/learning/qe-patterns.ts +0 -449
- package/src/learning/qe-reasoning-bank.ts +0 -951
- package/src/learning/real-embeddings.ts +0 -277
- package/src/learning/real-qe-reasoning-bank.ts +0 -833
- package/src/learning/sqlite-persistence.ts +0 -554
- package/src/mcp/entry.ts +0 -59
- package/src/mcp/handlers/agent-handlers.ts +0 -285
- package/src/mcp/handlers/core-handlers.ts +0 -317
- package/src/mcp/handlers/domain-handlers.ts +0 -1444
- package/src/mcp/handlers/index.ts +0 -57
- package/src/mcp/handlers/memory-handlers.ts +0 -338
- package/src/mcp/handlers/task-handlers.ts +0 -363
- package/src/mcp/index.ts +0 -30
- package/src/mcp/metrics/index.ts +0 -14
- package/src/mcp/metrics/metrics-collector.ts +0 -503
- package/src/mcp/protocol-server.ts +0 -752
- package/src/mcp/security/cve-prevention.ts +0 -742
- package/src/mcp/security/index.ts +0 -356
- package/src/mcp/security/oauth21-provider.ts +0 -821
- package/src/mcp/security/rate-limiter.ts +0 -615
- package/src/mcp/security/sampling-server.ts +0 -662
- package/src/mcp/security/schema-validator.ts +0 -855
- package/src/mcp/server.ts +0 -657
- package/src/mcp/tool-registry.ts +0 -391
- package/src/mcp/tools/base.ts +0 -399
- package/src/mcp/tools/chaos-resilience/inject.ts +0 -699
- package/src/mcp/tools/code-intelligence/analyze.ts +0 -745
- package/src/mcp/tools/contract-testing/validate.ts +0 -708
- package/src/mcp/tools/coverage-analysis/index.ts +0 -770
- package/src/mcp/tools/defect-intelligence/predict.ts +0 -466
- package/src/mcp/tools/index.ts +0 -214
- package/src/mcp/tools/learning-optimization/optimize.ts +0 -772
- package/src/mcp/tools/quality-assessment/evaluate.ts +0 -385
- package/src/mcp/tools/registry.ts +0 -248
- package/src/mcp/tools/requirements-validation/validate.ts +0 -394
- package/src/mcp/tools/security-compliance/scan.ts +0 -365
- package/src/mcp/tools/test-execution/execute.ts +0 -291
- package/src/mcp/tools/test-generation/generate.ts +0 -544
- package/src/mcp/tools/visual-accessibility/index.ts +0 -791
- package/src/mcp/transport/index.ts +0 -31
- package/src/mcp/transport/stdio.ts +0 -318
- package/src/mcp/types.ts +0 -543
- package/src/neural-optimizer/index.ts +0 -111
- package/src/neural-optimizer/replay-buffer.ts +0 -455
- package/src/neural-optimizer/swarm-topology.ts +0 -508
- package/src/neural-optimizer/topology-optimizer.ts +0 -828
- package/src/neural-optimizer/types.ts +0 -481
- package/src/neural-optimizer/value-network.ts +0 -351
- package/src/optimization/auto-tuner.ts +0 -817
- package/src/optimization/index.ts +0 -77
- package/src/optimization/metric-collectors.ts +0 -474
- package/src/optimization/qe-workers.ts +0 -704
- package/src/optimization/tuning-algorithm.ts +0 -401
- package/src/optimization/types.ts +0 -314
- package/src/routing/index.ts +0 -51
- package/src/routing/qe-agent-registry.ts +0 -963
- package/src/routing/qe-task-router.ts +0 -564
- package/src/routing/routing-feedback.ts +0 -365
- package/src/routing/types.ts +0 -406
- package/src/shared/embeddings/embedding-cache.ts +0 -157
- package/src/shared/embeddings/index.ts +0 -50
- package/src/shared/embeddings/nomic-embedder.ts +0 -404
- package/src/shared/embeddings/ollama-client.ts +0 -195
- package/src/shared/embeddings/types.ts +0 -147
- package/src/shared/entities/agent.ts +0 -141
- package/src/shared/entities/base-entity.ts +0 -79
- package/src/shared/entities/index.ts +0 -6
- package/src/shared/events/domain-events.ts +0 -259
- package/src/shared/events/index.ts +0 -5
- package/src/shared/git/git-analyzer.ts +0 -656
- package/src/shared/git/index.ts +0 -11
- package/src/shared/http/http-client.ts +0 -420
- package/src/shared/http/index.ts +0 -13
- package/src/shared/index.ts +0 -41
- package/src/shared/io/file-reader.ts +0 -525
- package/src/shared/io/index.ts +0 -25
- package/src/shared/llm/cache.ts +0 -473
- package/src/shared/llm/circuit-breaker.ts +0 -369
- package/src/shared/llm/cost-tracker.ts +0 -460
- package/src/shared/llm/index.ts +0 -140
- package/src/shared/llm/interfaces.ts +0 -629
- package/src/shared/llm/provider-manager.ts +0 -685
- package/src/shared/llm/providers/claude.ts +0 -524
- package/src/shared/llm/providers/index.ts +0 -8
- package/src/shared/llm/providers/ollama.ts +0 -575
- package/src/shared/llm/providers/openai.ts +0 -609
- package/src/shared/metrics/code-metrics.ts +0 -520
- package/src/shared/metrics/index.ts +0 -23
- package/src/shared/metrics/system-metrics.ts +0 -353
- package/src/shared/parsers/index.ts +0 -6
- package/src/shared/parsers/typescript-parser.ts +0 -841
- package/src/shared/security/compliance-patterns.ts +0 -666
- package/src/shared/security/index.ts +0 -30
- package/src/shared/security/osv-client.ts +0 -468
- package/src/shared/types/index.ts +0 -150
- package/src/shared/value-objects/index.ts +0 -273
- package/src/strange-loop/healing-controller.ts +0 -833
- package/src/strange-loop/index.ts +0 -104
- package/src/strange-loop/self-model.ts +0 -494
- package/src/strange-loop/strange-loop.ts +0 -446
- package/src/strange-loop/swarm-observer.ts +0 -448
- package/src/strange-loop/topology-analyzer.ts +0 -565
- package/src/strange-loop/types.ts +0 -640
- package/src/time-crystal/default-phases.ts +0 -520
- package/src/time-crystal/index.ts +0 -164
- package/src/time-crystal/oscillator.ts +0 -425
- package/src/time-crystal/phase-executor.ts +0 -521
- package/src/time-crystal/scheduler.ts +0 -1025
- package/src/time-crystal/test-runner.ts +0 -787
- package/src/time-crystal/types.ts +0 -421
- package/src/workers/base-worker.ts +0 -304
- package/src/workers/daemon.ts +0 -264
- package/src/workers/index.ts +0 -119
- package/src/workers/interfaces.ts +0 -393
- package/src/workers/worker-manager.ts +0 -424
- package/src/workers/workers/compliance-checker.ts +0 -445
- package/src/workers/workers/coverage-tracker.ts +0 -344
- package/src/workers/workers/defect-predictor.ts +0 -375
- package/src/workers/workers/flaky-detector.ts +0 -390
- package/src/workers/workers/index.ts +0 -17
- package/src/workers/workers/learning-consolidation.ts +0 -442
- package/src/workers/workers/performance-baseline.ts +0 -434
- package/src/workers/workers/quality-gate.ts +0 -419
- package/src/workers/workers/regression-monitor.ts +0 -357
- package/src/workers/workers/security-scan.ts +0 -349
- package/src/workers/workers/test-health.ts +0 -359
- package/tests/integration/code-intelligence/knowledge-graph-real.test.ts +0 -540
- package/tests/integration/coordination/cross-domain-router.test.ts +0 -403
- package/tests/integration/coordination/protocol-executor.test.ts +0 -454
- package/tests/integration/coordination/workflow-orchestrator.test.ts +0 -418
- package/tests/integration/feedback/feedback-loop-integration.test.ts +0 -560
- package/tests/integration/migration/v2-to-v3-migration.test.ts +0 -471
- package/tests/integration/parsers/typescript-parser.test.ts +0 -463
- package/tests/integration/security/vulnerability-detection.test.ts +0 -628
- package/tests/integration/test-execution/coordinator.test.ts +0 -410
- package/tests/integration/test-generation/coordinator.test.ts +0 -361
- package/tests/mocks/index.ts +0 -228
- package/tests/time-crystal/default-phases.test.ts +0 -476
- package/tests/time-crystal/oscillator.test.ts +0 -541
- package/tests/time-crystal/phase-executor.test.ts +0 -653
- package/tests/time-crystal/scheduler.test.ts +0 -626
- package/tests/time-crystal/test-runner.test.ts +0 -594
- package/tests/unit/causal-discovery/causal-graph.test.ts +0 -504
- package/tests/unit/causal-discovery/causal-root-cause-analyzer.test.ts +0 -347
- package/tests/unit/causal-discovery/discovery-engine.test.ts +0 -435
- package/tests/unit/causal-discovery/weight-matrix.test.ts +0 -328
- package/tests/unit/cli/cli.test.ts +0 -341
- package/tests/unit/cli/commands.test.ts +0 -414
- package/tests/unit/cli/init-command.test.ts +0 -274
- package/tests/unit/cli/migrate-command.test.ts +0 -396
- package/tests/unit/coordination/claims/claim-service.test.ts +0 -949
- package/tests/unit/coordination/claims/handoff-manager.test.ts +0 -773
- package/tests/unit/coordination/claims/work-stealing.test.ts +0 -492
- package/tests/unit/coordination/queen-coordinator.test.ts +0 -966
- package/tests/unit/coordination/result-saver.test.ts +0 -653
- package/tests/unit/coordination/task-executor.test.ts +0 -810
- package/tests/unit/domains/chaos-resilience/chaos-engineer.test.ts +0 -484
- package/tests/unit/domains/chaos-resilience/load-tester.test.ts +0 -559
- package/tests/unit/domains/chaos-resilience/performance-profiler.test.ts +0 -490
- package/tests/unit/domains/code-intelligence/impact-analyzer.test.ts +0 -560
- package/tests/unit/domains/code-intelligence/knowledge-graph.test.ts +0 -460
- package/tests/unit/domains/code-intelligence/semantic-analyzer.test.ts +0 -584
- package/tests/unit/domains/contract-testing/api-compatibility.test.ts +0 -483
- package/tests/unit/domains/contract-testing/contract-validator.test.ts +0 -370
- package/tests/unit/domains/contract-testing/schema-validator.test.ts +0 -610
- package/tests/unit/domains/coverage-analysis/coverage-embedder.test.ts +0 -298
- package/tests/unit/domains/coverage-analysis/hnsw-index.test.ts +0 -292
- package/tests/unit/domains/coverage-analysis/sublinear-analyzer.test.ts +0 -506
- package/tests/unit/domains/defect-intelligence/defect-predictor.test.ts +0 -370
- package/tests/unit/domains/defect-intelligence/pattern-learner.test.ts +0 -546
- package/tests/unit/domains/defect-intelligence/root-cause-analyzer.test.ts +0 -534
- package/tests/unit/domains/learning-optimization/learning-coordinator.test.ts +0 -541
- package/tests/unit/domains/learning-optimization/metrics-optimizer.test.ts +0 -552
- package/tests/unit/domains/learning-optimization/production-intel.test.ts +0 -589
- package/tests/unit/domains/learning-optimization/transfer-specialist.test.ts +0 -453
- package/tests/unit/domains/quality-assessment/coherence-gate.test.ts +0 -1006
- package/tests/unit/domains/quality-assessment/deployment-advisor.test.ts +0 -515
- package/tests/unit/domains/quality-assessment/quality-analyzer.test.ts +0 -401
- package/tests/unit/domains/quality-assessment/quality-gate.test.ts +0 -324
- package/tests/unit/domains/requirements-validation/bdd-scenario-writer.test.ts +0 -479
- package/tests/unit/domains/requirements-validation/requirements-validator.test.ts +0 -452
- package/tests/unit/domains/requirements-validation/testability-scorer.test.ts +0 -505
- package/tests/unit/domains/security-compliance/compliance-validator.test.ts +0 -500
- package/tests/unit/domains/security-compliance/security-auditor.test.ts +0 -498
- package/tests/unit/domains/security-compliance/security-scanner.test.ts +0 -412
- package/tests/unit/domains/visual-accessibility/accessibility-tester.test.ts +0 -432
- package/tests/unit/domains/visual-accessibility/responsive-tester.test.ts +0 -506
- package/tests/unit/domains/visual-accessibility/visual-tester.test.ts +0 -412
- package/tests/unit/early-exit/early-exit-controller.test.ts +0 -548
- package/tests/unit/early-exit/early-exit-decision.test.ts +0 -617
- package/tests/unit/early-exit/index.test.ts +0 -254
- package/tests/unit/early-exit/quality-signal.test.ts +0 -589
- package/tests/unit/early-exit/speculative-executor.test.ts +0 -453
- package/tests/unit/feedback/coverage-learner.test.ts +0 -288
- package/tests/unit/feedback/feedback-loop.test.ts +0 -458
- package/tests/unit/feedback/pattern-promotion.test.ts +0 -390
- package/tests/unit/feedback/quality-score-calculator.test.ts +0 -364
- package/tests/unit/feedback/test-outcome-tracker.test.ts +0 -243
- package/tests/unit/init/init-wizard.test.ts +0 -881
- package/tests/unit/init/project-analyzer.test.ts +0 -807
- package/tests/unit/init/self-configurator.test.ts +0 -493
- package/tests/unit/integrations/ruvector/ast-complexity.test.ts +0 -240
- package/tests/unit/integrations/ruvector/coverage-router.test.ts +0 -366
- package/tests/unit/integrations/ruvector/diff-risk-classifier.test.ts +0 -340
- package/tests/unit/integrations/ruvector/graph-boundaries.test.ts +0 -355
- package/tests/unit/integrations/ruvector/q-learning-router.test.ts +0 -314
- package/tests/unit/kernel/agent-coordinator.test.ts +0 -220
- package/tests/unit/kernel/event-bus.test.ts +0 -197
- package/tests/unit/learning/qe-reasoning-bank.test.ts +0 -666
- package/tests/unit/learning/real-qe-reasoning-bank.benchmark.test.ts +0 -415
- package/tests/unit/mcp/mcp-server.test.ts +0 -544
- package/tests/unit/mcp/metrics/metrics-collector.test.ts +0 -340
- package/tests/unit/mcp/security/cve-prevention.test.ts +0 -512
- package/tests/unit/mcp/security/oauth21-provider.test.ts +0 -624
- package/tests/unit/mcp/security/rate-limiter.test.ts +0 -410
- package/tests/unit/mcp/security/sampling-server.test.ts +0 -420
- package/tests/unit/mcp/security/schema-validator.test.ts +0 -494
- package/tests/unit/mcp/tools/base.test.ts +0 -336
- package/tests/unit/mcp/tools/domain-tools.test.ts +0 -759
- package/tests/unit/mcp/tools/registry.test.ts +0 -240
- package/tests/unit/neural-optimizer/replay-buffer.test.ts +0 -403
- package/tests/unit/neural-optimizer/swarm-topology.test.ts +0 -473
- package/tests/unit/neural-optimizer/topology-optimizer.test.ts +0 -595
- package/tests/unit/neural-optimizer/value-network.test.ts +0 -343
- package/tests/unit/optimization/auto-tuner.test.ts +0 -506
- package/tests/unit/optimization/metric-collectors.test.ts +0 -352
- package/tests/unit/optimization/qe-workers.test.ts +0 -407
- package/tests/unit/optimization/tuning-algorithm.test.ts +0 -467
- package/tests/unit/routing/qe-agent-registry.test.ts +0 -229
- package/tests/unit/routing/qe-task-router.test.ts +0 -390
- package/tests/unit/routing/routing-feedback.test.ts +0 -339
- package/tests/unit/shared/embeddings/nomic-embedder.test.ts +0 -419
- package/tests/unit/shared/http/http-client.test.ts +0 -719
- package/tests/unit/shared/io/file-reader.test.ts +0 -511
- package/tests/unit/shared/llm/cache.test.ts +0 -391
- package/tests/unit/shared/llm/circuit-breaker.test.ts +0 -293
- package/tests/unit/shared/llm/cost-tracker.test.ts +0 -431
- package/tests/unit/shared/llm/provider-manager.test.ts +0 -550
- package/tests/unit/shared/llm/providers.test.ts +0 -532
- package/tests/unit/shared/parsers/typescript-parser.test.ts +0 -693
- package/tests/unit/shared/value-objects.test.ts +0 -184
- package/tests/unit/strange-loop/strange-loop.test.ts +0 -1170
- package/tests/unit/workers/base-worker.test.ts +0 -341
- package/tests/unit/workers/daemon.test.ts +0 -291
- package/tests/unit/workers/worker-manager.test.ts +0 -284
- package/tsconfig.json +0 -32
- package/vitest.config.ts +0 -27
|
@@ -1,2227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agentic QE v3 - Security Auditor Service
|
|
3
|
-
* Provides comprehensive security audit functionality
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
7
|
-
import * as fs from 'fs/promises';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { Result, ok, err } from '../../../shared/types/index.js';
|
|
10
|
-
import type { MemoryBackend } from '../../../kernel/interfaces.js';
|
|
11
|
-
import type { FilePath, RiskScore } from '../../../shared/value-objects/index.js';
|
|
12
|
-
|
|
13
|
-
// ============================================================================
|
|
14
|
-
// Package.json Types
|
|
15
|
-
// ============================================================================
|
|
16
|
-
|
|
17
|
-
interface PackageJson {
|
|
18
|
-
name?: string;
|
|
19
|
-
version?: string;
|
|
20
|
-
dependencies?: Record<string, string>;
|
|
21
|
-
devDependencies?: Record<string, string>;
|
|
22
|
-
peerDependencies?: Record<string, string>;
|
|
23
|
-
optionalDependencies?: Record<string, string>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// OSV (Open Source Vulnerabilities) API Types
|
|
28
|
-
// ============================================================================
|
|
29
|
-
|
|
30
|
-
interface OSVQueryRequest {
|
|
31
|
-
package: {
|
|
32
|
-
name: string;
|
|
33
|
-
ecosystem: string;
|
|
34
|
-
};
|
|
35
|
-
version: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface OSVVulnerability {
|
|
39
|
-
id: string;
|
|
40
|
-
summary?: string;
|
|
41
|
-
details?: string;
|
|
42
|
-
aliases?: string[];
|
|
43
|
-
severity?: Array<{
|
|
44
|
-
type: string;
|
|
45
|
-
score: string;
|
|
46
|
-
}>;
|
|
47
|
-
affected?: Array<{
|
|
48
|
-
package?: {
|
|
49
|
-
name: string;
|
|
50
|
-
ecosystem: string;
|
|
51
|
-
};
|
|
52
|
-
ranges?: Array<{
|
|
53
|
-
type: string;
|
|
54
|
-
events?: Array<{
|
|
55
|
-
introduced?: string;
|
|
56
|
-
fixed?: string;
|
|
57
|
-
}>;
|
|
58
|
-
}>;
|
|
59
|
-
versions?: string[];
|
|
60
|
-
}>;
|
|
61
|
-
references?: Array<{
|
|
62
|
-
type: string;
|
|
63
|
-
url: string;
|
|
64
|
-
}>;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
interface OSVQueryResponse {
|
|
68
|
-
vulns?: OSVVulnerability[];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ============================================================================
|
|
72
|
-
// HTTP Client for API calls
|
|
73
|
-
// ============================================================================
|
|
74
|
-
|
|
75
|
-
interface HttpResponse<T> {
|
|
76
|
-
ok: boolean;
|
|
77
|
-
status: number;
|
|
78
|
-
data: T;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function httpPost<T, R>(url: string, body: T): Promise<HttpResponse<R>> {
|
|
82
|
-
const response = await fetch(url, {
|
|
83
|
-
method: 'POST',
|
|
84
|
-
headers: {
|
|
85
|
-
'Content-Type': 'application/json',
|
|
86
|
-
},
|
|
87
|
-
body: JSON.stringify(body),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const data = await response.json() as R;
|
|
91
|
-
return {
|
|
92
|
-
ok: response.ok,
|
|
93
|
-
status: response.status,
|
|
94
|
-
data,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function httpGet<R>(url: string): Promise<HttpResponse<R>> {
|
|
99
|
-
const response = await fetch(url, {
|
|
100
|
-
method: 'GET',
|
|
101
|
-
headers: {
|
|
102
|
-
'Accept': 'application/json',
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const data = await response.json() as R;
|
|
107
|
-
return {
|
|
108
|
-
ok: response.ok,
|
|
109
|
-
status: response.status,
|
|
110
|
-
data,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
import type {
|
|
114
|
-
IDependencySecurityService,
|
|
115
|
-
DependencyScanResult,
|
|
116
|
-
PackageSecurityInfo,
|
|
117
|
-
UpgradeRecommendation,
|
|
118
|
-
Vulnerability,
|
|
119
|
-
VulnerabilitySeverity,
|
|
120
|
-
VulnerabilityCategory,
|
|
121
|
-
VulnerabilityLocation,
|
|
122
|
-
DependencyInfo,
|
|
123
|
-
OutdatedPackage,
|
|
124
|
-
RemediationAdvice,
|
|
125
|
-
ScanSummary,
|
|
126
|
-
DetectedSecret,
|
|
127
|
-
SecretScanResult,
|
|
128
|
-
SecurityAuditOptions,
|
|
129
|
-
SecurityAuditReport,
|
|
130
|
-
SASTResult,
|
|
131
|
-
DASTResult,
|
|
132
|
-
} from '../interfaces.js';
|
|
133
|
-
|
|
134
|
-
// ============================================================================
|
|
135
|
-
// Service Interface
|
|
136
|
-
// ============================================================================
|
|
137
|
-
|
|
138
|
-
export interface ISecurityAuditorService extends IDependencySecurityService {
|
|
139
|
-
/**
|
|
140
|
-
* Run comprehensive security audit
|
|
141
|
-
*/
|
|
142
|
-
runAudit(options: SecurityAuditOptions): Promise<Result<SecurityAuditReport>>;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Scan for secrets/credentials in code
|
|
146
|
-
*/
|
|
147
|
-
scanSecrets(files: FilePath[]): Promise<Result<SecretScanResult>>;
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Get security posture summary
|
|
151
|
-
*/
|
|
152
|
-
getSecurityPosture(): Promise<Result<SecurityPostureSummary>>;
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Triage vulnerabilities by priority
|
|
156
|
-
*/
|
|
157
|
-
triageVulnerabilities(
|
|
158
|
-
vulnerabilities: Vulnerability[]
|
|
159
|
-
): Promise<Result<TriagedVulnerabilities>>;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export interface SecurityPostureSummary {
|
|
163
|
-
overallScore: number;
|
|
164
|
-
trend: 'improving' | 'stable' | 'declining';
|
|
165
|
-
criticalIssues: number;
|
|
166
|
-
highIssues: number;
|
|
167
|
-
openVulnerabilities: number;
|
|
168
|
-
resolvedLastWeek: number;
|
|
169
|
-
averageResolutionTime: number;
|
|
170
|
-
lastAuditDate: Date;
|
|
171
|
-
recommendations: string[];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export interface TriagedVulnerabilities {
|
|
175
|
-
immediate: Vulnerability[];
|
|
176
|
-
shortTerm: Vulnerability[];
|
|
177
|
-
mediumTerm: Vulnerability[];
|
|
178
|
-
longTerm: Vulnerability[];
|
|
179
|
-
accepted: Vulnerability[];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ============================================================================
|
|
183
|
-
// Configuration
|
|
184
|
-
// ============================================================================
|
|
185
|
-
|
|
186
|
-
export interface SecurityAuditorConfig {
|
|
187
|
-
secretPatterns: RegExp[];
|
|
188
|
-
excludePatterns: string[];
|
|
189
|
-
maxFileSizeKb: number;
|
|
190
|
-
enableHistoricalAnalysis: boolean;
|
|
191
|
-
riskThreshold: number;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const DEFAULT_CONFIG: SecurityAuditorConfig = {
|
|
195
|
-
secretPatterns: [
|
|
196
|
-
/(?:api[_-]?key|apikey)['":\s]*['"=]?\s*['"]?([a-zA-Z0-9_\-]{20,})['"]?/gi,
|
|
197
|
-
/(?:password|passwd|pwd)['":\s]*['"=]?\s*['"]?([^\s'"]{8,})['"]?/gi,
|
|
198
|
-
/(?:secret|token)['":\s]*['"=]?\s*['"]?([a-zA-Z0-9_\-]{16,})['"]?/gi,
|
|
199
|
-
/(?:aws[_-]?access[_-]?key|aws[_-]?secret)['":\s]*['"=]?\s*['"]?([A-Z0-9]{20,})['"]?/gi,
|
|
200
|
-
/-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/gi,
|
|
201
|
-
/ghp_[a-zA-Z0-9]{36}/g, // GitHub personal access token
|
|
202
|
-
/gho_[a-zA-Z0-9]{36}/g, // GitHub OAuth access token
|
|
203
|
-
/sk-[a-zA-Z0-9]{48}/g, // OpenAI API key
|
|
204
|
-
],
|
|
205
|
-
excludePatterns: ['node_modules', 'dist', 'build', '.git', '*.test.*', '*.spec.*'],
|
|
206
|
-
maxFileSizeKb: 1024,
|
|
207
|
-
enableHistoricalAnalysis: true,
|
|
208
|
-
riskThreshold: 0.7,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// ============================================================================
|
|
212
|
-
// Security Auditor Service Implementation
|
|
213
|
-
// ============================================================================
|
|
214
|
-
|
|
215
|
-
export class SecurityAuditorService implements ISecurityAuditorService {
|
|
216
|
-
private readonly config: SecurityAuditorConfig;
|
|
217
|
-
|
|
218
|
-
constructor(
|
|
219
|
-
private readonly memory: MemoryBackend,
|
|
220
|
-
config: Partial<SecurityAuditorConfig> = {}
|
|
221
|
-
) {
|
|
222
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// ==========================================================================
|
|
226
|
-
// IDependencySecurityService Implementation
|
|
227
|
-
// ==========================================================================
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Scan dependencies for vulnerabilities
|
|
231
|
-
*/
|
|
232
|
-
async scanDependencies(manifestPath: FilePath): Promise<Result<DependencyScanResult>> {
|
|
233
|
-
try {
|
|
234
|
-
const manifest = manifestPath.value;
|
|
235
|
-
const ecosystem = this.detectEcosystem(manifest);
|
|
236
|
-
|
|
237
|
-
if (!ecosystem) {
|
|
238
|
-
return err(new Error(`Unknown manifest format: ${manifest}`));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const vulnerabilities: Vulnerability[] = [];
|
|
242
|
-
const outdatedPackages: OutdatedPackage[] = [];
|
|
243
|
-
|
|
244
|
-
// Parse dependencies from manifest file
|
|
245
|
-
const dependencies = await this.parseDependencies(manifest, ecosystem);
|
|
246
|
-
|
|
247
|
-
for (const dep of dependencies) {
|
|
248
|
-
// Check for known vulnerabilities
|
|
249
|
-
const vulns = await this.checkDependencyVulnerabilities(dep, ecosystem);
|
|
250
|
-
vulnerabilities.push(...vulns);
|
|
251
|
-
|
|
252
|
-
// Check for outdated packages
|
|
253
|
-
const outdated = await this.checkOutdated(dep);
|
|
254
|
-
if (outdated) {
|
|
255
|
-
outdatedPackages.push(outdated);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const summary = this.createDependencySummary(vulnerabilities, dependencies.length);
|
|
260
|
-
|
|
261
|
-
// Store results
|
|
262
|
-
await this.memory.set(
|
|
263
|
-
`security:deps:${manifestPath.filename}`,
|
|
264
|
-
{ vulnerabilities, outdatedPackages, summary },
|
|
265
|
-
{ namespace: 'security-compliance', ttl: 86400 } // 24 hours
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
return ok({
|
|
269
|
-
vulnerabilities,
|
|
270
|
-
outdatedPackages,
|
|
271
|
-
summary,
|
|
272
|
-
});
|
|
273
|
-
} catch (error) {
|
|
274
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Check specific package for security issues
|
|
280
|
-
*/
|
|
281
|
-
async checkPackage(
|
|
282
|
-
name: string,
|
|
283
|
-
version: string,
|
|
284
|
-
ecosystem: DependencyInfo['ecosystem']
|
|
285
|
-
): Promise<Result<PackageSecurityInfo>> {
|
|
286
|
-
try {
|
|
287
|
-
// Query OSV vulnerability database
|
|
288
|
-
const vulnerabilities = await this.queryVulnerabilityDatabase(
|
|
289
|
-
name,
|
|
290
|
-
version,
|
|
291
|
-
ecosystem
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
const latestVersion = await this.getLatestVersion(name, ecosystem);
|
|
295
|
-
const isDeprecated = await this.checkDeprecation(name, ecosystem);
|
|
296
|
-
|
|
297
|
-
return ok({
|
|
298
|
-
name,
|
|
299
|
-
version,
|
|
300
|
-
vulnerabilities,
|
|
301
|
-
latestVersion,
|
|
302
|
-
isDeprecated,
|
|
303
|
-
});
|
|
304
|
-
} catch (error) {
|
|
305
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Get upgrade recommendations for vulnerabilities
|
|
311
|
-
*/
|
|
312
|
-
async getUpgradeRecommendations(
|
|
313
|
-
vulnerabilities: Vulnerability[]
|
|
314
|
-
): Promise<Result<UpgradeRecommendation[]>> {
|
|
315
|
-
try {
|
|
316
|
-
const recommendations: UpgradeRecommendation[] = [];
|
|
317
|
-
|
|
318
|
-
// Group vulnerabilities by package
|
|
319
|
-
const byPackage = new Map<string, Vulnerability[]>();
|
|
320
|
-
for (const vuln of vulnerabilities) {
|
|
321
|
-
const dep = vuln.location.dependency;
|
|
322
|
-
if (dep) {
|
|
323
|
-
const key = `${dep.ecosystem}:${dep.name}`;
|
|
324
|
-
const existing = byPackage.get(key) || [];
|
|
325
|
-
existing.push(vuln);
|
|
326
|
-
byPackage.set(key, existing);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Generate recommendations for each package
|
|
331
|
-
for (const [key, vulns] of byPackage) {
|
|
332
|
-
const [ecosystem, name] = key.split(':');
|
|
333
|
-
const currentVersion = vulns[0].location.dependency?.version || 'unknown';
|
|
334
|
-
|
|
335
|
-
const latestVersion = await this.getLatestVersion(
|
|
336
|
-
name,
|
|
337
|
-
ecosystem as DependencyInfo['ecosystem']
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
const fixedVersions = vulns
|
|
341
|
-
.filter((v) => v.id)
|
|
342
|
-
.map((v) => v.id);
|
|
343
|
-
|
|
344
|
-
recommendations.push({
|
|
345
|
-
package: name,
|
|
346
|
-
fromVersion: currentVersion,
|
|
347
|
-
toVersion: latestVersion,
|
|
348
|
-
fixesVulnerabilities: fixedVersions,
|
|
349
|
-
breakingChanges: this.hasBreakingChanges(currentVersion, latestVersion),
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Sort by number of vulnerabilities fixed
|
|
354
|
-
recommendations.sort(
|
|
355
|
-
(a, b) => b.fixesVulnerabilities.length - a.fixesVulnerabilities.length
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
return ok(recommendations);
|
|
359
|
-
} catch (error) {
|
|
360
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ==========================================================================
|
|
365
|
-
// Audit Functionality
|
|
366
|
-
// ==========================================================================
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Run comprehensive security audit
|
|
370
|
-
*/
|
|
371
|
-
async runAudit(options: SecurityAuditOptions): Promise<Result<SecurityAuditReport>> {
|
|
372
|
-
const auditId = uuidv4();
|
|
373
|
-
const timestamp = new Date();
|
|
374
|
-
|
|
375
|
-
try {
|
|
376
|
-
let sastResults: SASTResult | undefined;
|
|
377
|
-
let dastResults: DASTResult | undefined;
|
|
378
|
-
let dependencyResults: DependencyScanResult | undefined;
|
|
379
|
-
let secretScanResults: SecretScanResult | undefined;
|
|
380
|
-
|
|
381
|
-
// Note: Actual SAST/DAST scanning would be delegated to SecurityScannerService
|
|
382
|
-
// This is orchestration-level code that combines results
|
|
383
|
-
|
|
384
|
-
if (options.includeSAST) {
|
|
385
|
-
// SAST scanning delegated to SecurityScannerService
|
|
386
|
-
sastResults = await this.performSASTScan();
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if (options.includeDAST && options.targetUrl) {
|
|
390
|
-
// DAST scanning delegated to SecurityScannerService
|
|
391
|
-
dastResults = await this.performDASTScan(options.targetUrl);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (options.includeDependencies) {
|
|
395
|
-
// Scan package manifests for vulnerabilities
|
|
396
|
-
dependencyResults = await this.performDependencyScan();
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (options.includeSecrets) {
|
|
400
|
-
// Scan source files for secrets
|
|
401
|
-
secretScanResults = await this.performSecretScan();
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Calculate overall risk score
|
|
405
|
-
const overallRiskScore = this.calculateOverallRisk(
|
|
406
|
-
sastResults,
|
|
407
|
-
dastResults,
|
|
408
|
-
dependencyResults,
|
|
409
|
-
secretScanResults
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
// Generate recommendations
|
|
413
|
-
const recommendations = this.generateRecommendations(
|
|
414
|
-
sastResults,
|
|
415
|
-
dastResults,
|
|
416
|
-
dependencyResults,
|
|
417
|
-
secretScanResults
|
|
418
|
-
);
|
|
419
|
-
|
|
420
|
-
const report: SecurityAuditReport = {
|
|
421
|
-
auditId,
|
|
422
|
-
timestamp,
|
|
423
|
-
sastResults,
|
|
424
|
-
dastResults,
|
|
425
|
-
dependencyResults,
|
|
426
|
-
secretScanResults,
|
|
427
|
-
overallRiskScore,
|
|
428
|
-
recommendations,
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
// Store audit report
|
|
432
|
-
await this.memory.set(
|
|
433
|
-
`security:audit:${auditId}`,
|
|
434
|
-
report,
|
|
435
|
-
{ namespace: 'security-compliance', persist: true }
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
return ok(report);
|
|
439
|
-
} catch (error) {
|
|
440
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Scan for secrets in code
|
|
446
|
-
*/
|
|
447
|
-
async scanSecrets(files: FilePath[]): Promise<Result<SecretScanResult>> {
|
|
448
|
-
try {
|
|
449
|
-
const secretsFound: DetectedSecret[] = [];
|
|
450
|
-
let filesScanned = 0;
|
|
451
|
-
|
|
452
|
-
for (const file of files) {
|
|
453
|
-
// Skip excluded patterns
|
|
454
|
-
if (this.shouldExclude(file.value)) {
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
filesScanned++;
|
|
459
|
-
|
|
460
|
-
// Read and scan file for secrets using regex patterns
|
|
461
|
-
const secrets = await this.scanFileForSecrets(file);
|
|
462
|
-
secretsFound.push(...secrets);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return ok({
|
|
466
|
-
secretsFound,
|
|
467
|
-
filesScanned,
|
|
468
|
-
});
|
|
469
|
-
} catch (error) {
|
|
470
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Get security posture summary
|
|
476
|
-
*/
|
|
477
|
-
async getSecurityPosture(): Promise<Result<SecurityPostureSummary>> {
|
|
478
|
-
try {
|
|
479
|
-
// Get historical audit data
|
|
480
|
-
const audits = await this.getRecentAudits();
|
|
481
|
-
|
|
482
|
-
// Calculate metrics
|
|
483
|
-
const latestAudit = audits[0];
|
|
484
|
-
const previousAudit = audits[1];
|
|
485
|
-
|
|
486
|
-
const criticalIssues = this.countBySeverity(latestAudit, 'critical');
|
|
487
|
-
const highIssues = this.countBySeverity(latestAudit, 'high');
|
|
488
|
-
const openVulnerabilities = this.countOpenVulnerabilities(latestAudit);
|
|
489
|
-
|
|
490
|
-
// Calculate trend
|
|
491
|
-
const trend = this.calculateTrend(latestAudit, previousAudit);
|
|
492
|
-
|
|
493
|
-
// Calculate overall score (0-100)
|
|
494
|
-
const overallScore = this.calculatePostureScore(
|
|
495
|
-
criticalIssues,
|
|
496
|
-
highIssues,
|
|
497
|
-
openVulnerabilities
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
// Generate recommendations
|
|
501
|
-
const recommendations = this.generatePostureRecommendations(
|
|
502
|
-
criticalIssues,
|
|
503
|
-
highIssues,
|
|
504
|
-
overallScore
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
return ok({
|
|
508
|
-
overallScore,
|
|
509
|
-
trend,
|
|
510
|
-
criticalIssues,
|
|
511
|
-
highIssues,
|
|
512
|
-
openVulnerabilities,
|
|
513
|
-
resolvedLastWeek: await this.countResolvedLastWeek(),
|
|
514
|
-
averageResolutionTime: await this.calculateAverageResolutionTime(),
|
|
515
|
-
lastAuditDate: latestAudit?.timestamp ?? new Date(),
|
|
516
|
-
recommendations,
|
|
517
|
-
});
|
|
518
|
-
} catch (error) {
|
|
519
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Triage vulnerabilities by priority
|
|
525
|
-
*/
|
|
526
|
-
async triageVulnerabilities(
|
|
527
|
-
vulnerabilities: Vulnerability[]
|
|
528
|
-
): Promise<Result<TriagedVulnerabilities>> {
|
|
529
|
-
try {
|
|
530
|
-
const triaged: TriagedVulnerabilities = {
|
|
531
|
-
immediate: [],
|
|
532
|
-
shortTerm: [],
|
|
533
|
-
mediumTerm: [],
|
|
534
|
-
longTerm: [],
|
|
535
|
-
accepted: [],
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
for (const vuln of vulnerabilities) {
|
|
539
|
-
const bucket = this.determinePriorityBucket(vuln);
|
|
540
|
-
triaged[bucket].push(vuln);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Sort each bucket by severity
|
|
544
|
-
const severityOrder = ['critical', 'high', 'medium', 'low', 'informational'];
|
|
545
|
-
for (const bucket of Object.values(triaged)) {
|
|
546
|
-
bucket.sort(
|
|
547
|
-
(a: Vulnerability, b: Vulnerability) =>
|
|
548
|
-
severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity)
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return ok(triaged);
|
|
553
|
-
} catch (error) {
|
|
554
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// ==========================================================================
|
|
559
|
-
// Private Helper Methods
|
|
560
|
-
// ==========================================================================
|
|
561
|
-
|
|
562
|
-
private detectEcosystem(manifest: string): DependencyInfo['ecosystem'] | null {
|
|
563
|
-
if (manifest.includes('package.json')) return 'npm';
|
|
564
|
-
if (manifest.includes('requirements.txt') || manifest.includes('Pipfile')) return 'pip';
|
|
565
|
-
if (manifest.includes('pom.xml') || manifest.includes('build.gradle')) return 'maven';
|
|
566
|
-
if (manifest.includes('Cargo.toml')) return 'cargo';
|
|
567
|
-
if (manifest.includes('.csproj') || manifest.includes('packages.config')) return 'nuget';
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
private async parseDependencies(
|
|
572
|
-
manifest: string,
|
|
573
|
-
ecosystem: DependencyInfo['ecosystem']
|
|
574
|
-
): Promise<DependencyInfo[]> {
|
|
575
|
-
const dependencies: DependencyInfo[] = [];
|
|
576
|
-
|
|
577
|
-
try {
|
|
578
|
-
if (ecosystem === 'npm') {
|
|
579
|
-
// Read and parse package.json
|
|
580
|
-
const content = await fs.readFile(manifest, 'utf-8');
|
|
581
|
-
const packageJson: PackageJson = JSON.parse(content);
|
|
582
|
-
|
|
583
|
-
// Collect all dependency types
|
|
584
|
-
const allDeps: Record<string, string> = {
|
|
585
|
-
...packageJson.dependencies,
|
|
586
|
-
...packageJson.devDependencies,
|
|
587
|
-
...packageJson.peerDependencies,
|
|
588
|
-
...packageJson.optionalDependencies,
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
// Convert to DependencyInfo array
|
|
592
|
-
for (const [name, versionSpec] of Object.entries(allDeps)) {
|
|
593
|
-
// Clean version specifiers (^, ~, >=, etc.)
|
|
594
|
-
const version = this.cleanVersionSpec(versionSpec);
|
|
595
|
-
dependencies.push({ name, version, ecosystem: 'npm' });
|
|
596
|
-
}
|
|
597
|
-
} else if (ecosystem === 'pip') {
|
|
598
|
-
// Parse requirements.txt or Pipfile
|
|
599
|
-
const content = await fs.readFile(manifest, 'utf-8');
|
|
600
|
-
const lines = content.split('\n');
|
|
601
|
-
|
|
602
|
-
for (const line of lines) {
|
|
603
|
-
const trimmed = line.trim();
|
|
604
|
-
// Skip comments and empty lines
|
|
605
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
606
|
-
|
|
607
|
-
// Parse package==version or package>=version patterns
|
|
608
|
-
const match = trimmed.match(/^([a-zA-Z0-9_-]+)(?:[=<>~!]+(.+))?$/);
|
|
609
|
-
if (match) {
|
|
610
|
-
dependencies.push({
|
|
611
|
-
name: match[1],
|
|
612
|
-
version: match[2] || 'latest',
|
|
613
|
-
ecosystem: 'pip',
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
// Additional ecosystems (maven, cargo, nuget) would be implemented similarly
|
|
619
|
-
} catch (error) {
|
|
620
|
-
// If file reading fails, return empty array rather than throwing
|
|
621
|
-
console.error(`Failed to parse dependencies from ${manifest}:`, error);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
return dependencies;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* Clean version specifier to extract actual version
|
|
629
|
-
*/
|
|
630
|
-
private cleanVersionSpec(versionSpec: string): string {
|
|
631
|
-
// Remove leading specifiers like ^, ~, >=, <=, >, <, =
|
|
632
|
-
return versionSpec.replace(/^[\^~>=<]+/, '').trim();
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
private async checkDependencyVulnerabilities(
|
|
636
|
-
dep: DependencyInfo,
|
|
637
|
-
ecosystem: DependencyInfo['ecosystem']
|
|
638
|
-
): Promise<Vulnerability[]> {
|
|
639
|
-
const vulnerabilities: Vulnerability[] = [];
|
|
640
|
-
|
|
641
|
-
try {
|
|
642
|
-
// Map ecosystem to OSV ecosystem name
|
|
643
|
-
const osvEcosystem = this.mapToOSVEcosystem(ecosystem);
|
|
644
|
-
|
|
645
|
-
// Query OSV API for vulnerabilities
|
|
646
|
-
const osvQuery: OSVQueryRequest = {
|
|
647
|
-
package: {
|
|
648
|
-
name: dep.name,
|
|
649
|
-
ecosystem: osvEcosystem,
|
|
650
|
-
},
|
|
651
|
-
version: dep.version,
|
|
652
|
-
};
|
|
653
|
-
|
|
654
|
-
const response = await httpPost<OSVQueryRequest, OSVQueryResponse>(
|
|
655
|
-
'https://api.osv.dev/v1/query',
|
|
656
|
-
osvQuery
|
|
657
|
-
);
|
|
658
|
-
|
|
659
|
-
if (response.ok && response.data.vulns && response.data.vulns.length > 0) {
|
|
660
|
-
for (const osvVuln of response.data.vulns) {
|
|
661
|
-
const vulnerability = this.mapOSVVulnerability(osvVuln, dep);
|
|
662
|
-
vulnerabilities.push(vulnerability);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
} catch (error) {
|
|
666
|
-
// Log error but don't fail the entire scan
|
|
667
|
-
console.error(`Failed to check vulnerabilities for ${dep.name}@${dep.version}:`, error);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
return vulnerabilities;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* Map internal ecosystem to OSV ecosystem name
|
|
675
|
-
*/
|
|
676
|
-
private mapToOSVEcosystem(ecosystem: DependencyInfo['ecosystem']): string {
|
|
677
|
-
const ecosystemMap: Record<DependencyInfo['ecosystem'], string> = {
|
|
678
|
-
npm: 'npm',
|
|
679
|
-
pip: 'PyPI',
|
|
680
|
-
maven: 'Maven',
|
|
681
|
-
nuget: 'NuGet',
|
|
682
|
-
cargo: 'crates.io',
|
|
683
|
-
};
|
|
684
|
-
return ecosystemMap[ecosystem];
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* Map OSV vulnerability to our Vulnerability type
|
|
689
|
-
*/
|
|
690
|
-
private mapOSVVulnerability(osvVuln: OSVVulnerability, dep: DependencyInfo): Vulnerability {
|
|
691
|
-
// Extract CVE ID from aliases
|
|
692
|
-
const cveId = osvVuln.aliases?.find((alias) => alias.startsWith('CVE-'));
|
|
693
|
-
|
|
694
|
-
// Determine severity from OSV severity scores
|
|
695
|
-
const severity = this.mapOSVSeverity(osvVuln.severity);
|
|
696
|
-
|
|
697
|
-
// Extract fixed version from affected ranges
|
|
698
|
-
const fixedVersion = this.extractFixedVersion(osvVuln);
|
|
699
|
-
|
|
700
|
-
// Build references list
|
|
701
|
-
const references = osvVuln.references?.map((ref) => ref.url) || [];
|
|
702
|
-
|
|
703
|
-
const location: VulnerabilityLocation = {
|
|
704
|
-
file: 'package.json',
|
|
705
|
-
dependency: dep,
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
const remediation: RemediationAdvice = {
|
|
709
|
-
description: fixedVersion
|
|
710
|
-
? `Upgrade ${dep.name} to version ${fixedVersion} or higher`
|
|
711
|
-
: `Review and address vulnerability in ${dep.name}`,
|
|
712
|
-
fixExample: fixedVersion ? `"${dep.name}": "^${fixedVersion}"` : undefined,
|
|
713
|
-
estimatedEffort: fixedVersion ? 'trivial' : 'moderate',
|
|
714
|
-
automatable: !!fixedVersion,
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
return {
|
|
718
|
-
id: uuidv4(),
|
|
719
|
-
cveId,
|
|
720
|
-
title: osvVuln.summary || `Vulnerability in ${dep.name}`,
|
|
721
|
-
description: osvVuln.details || osvVuln.summary || 'No description available',
|
|
722
|
-
severity,
|
|
723
|
-
category: this.categorizeVulnerability(osvVuln),
|
|
724
|
-
location,
|
|
725
|
-
remediation,
|
|
726
|
-
references,
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
* Map OSV severity to our severity levels
|
|
732
|
-
*/
|
|
733
|
-
private mapOSVSeverity(
|
|
734
|
-
severityScores?: OSVVulnerability['severity']
|
|
735
|
-
): VulnerabilitySeverity {
|
|
736
|
-
if (!severityScores || severityScores.length === 0) {
|
|
737
|
-
return 'medium'; // Default to medium if no severity info
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// Find CVSS score
|
|
741
|
-
const cvssScore = severityScores.find(
|
|
742
|
-
(s) => s.type === 'CVSS_V3' || s.type === 'CVSS_V2'
|
|
743
|
-
);
|
|
744
|
-
|
|
745
|
-
if (cvssScore) {
|
|
746
|
-
const score = parseFloat(cvssScore.score);
|
|
747
|
-
if (score >= 9.0) return 'critical';
|
|
748
|
-
if (score >= 7.0) return 'high';
|
|
749
|
-
if (score >= 4.0) return 'medium';
|
|
750
|
-
if (score > 0) return 'low';
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
return 'medium';
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Extract fixed version from OSV vulnerability data
|
|
758
|
-
*/
|
|
759
|
-
private extractFixedVersion(osvVuln: OSVVulnerability): string | undefined {
|
|
760
|
-
if (!osvVuln.affected) return undefined;
|
|
761
|
-
|
|
762
|
-
for (const affected of osvVuln.affected) {
|
|
763
|
-
if (affected.ranges) {
|
|
764
|
-
for (const range of affected.ranges) {
|
|
765
|
-
if (range.events) {
|
|
766
|
-
const fixedEvent = range.events.find((event) => event.fixed);
|
|
767
|
-
if (fixedEvent?.fixed) {
|
|
768
|
-
return fixedEvent.fixed;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
return undefined;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
/**
|
|
779
|
-
* Categorize vulnerability based on OSV data
|
|
780
|
-
*/
|
|
781
|
-
private categorizeVulnerability(osvVuln: OSVVulnerability): VulnerabilityCategory {
|
|
782
|
-
const summary = (osvVuln.summary || '').toLowerCase();
|
|
783
|
-
const details = (osvVuln.details || '').toLowerCase();
|
|
784
|
-
const combined = `${summary} ${details}`;
|
|
785
|
-
|
|
786
|
-
// Check for common vulnerability patterns
|
|
787
|
-
if (combined.includes('injection') || combined.includes('sql')) {
|
|
788
|
-
return 'injection';
|
|
789
|
-
}
|
|
790
|
-
if (combined.includes('xss') || combined.includes('cross-site scripting')) {
|
|
791
|
-
return 'xss';
|
|
792
|
-
}
|
|
793
|
-
if (combined.includes('authentication') || combined.includes('auth bypass')) {
|
|
794
|
-
return 'broken-auth';
|
|
795
|
-
}
|
|
796
|
-
if (combined.includes('sensitive data') || combined.includes('exposure')) {
|
|
797
|
-
return 'sensitive-data';
|
|
798
|
-
}
|
|
799
|
-
if (combined.includes('xxe') || combined.includes('xml external')) {
|
|
800
|
-
return 'xxe';
|
|
801
|
-
}
|
|
802
|
-
if (combined.includes('access control') || combined.includes('authorization')) {
|
|
803
|
-
return 'access-control';
|
|
804
|
-
}
|
|
805
|
-
if (combined.includes('deseriali')) {
|
|
806
|
-
return 'insecure-deserialization';
|
|
807
|
-
}
|
|
808
|
-
if (combined.includes('prototype pollution') || combined.includes('dependency')) {
|
|
809
|
-
return 'vulnerable-components';
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// Default to vulnerable-components for dependency vulnerabilities
|
|
813
|
-
return 'vulnerable-components';
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
private async checkOutdated(dep: DependencyInfo): Promise<OutdatedPackage | null> {
|
|
817
|
-
// Check if package is outdated by comparing with latest version from registry
|
|
818
|
-
const latestVersion = await this.getLatestVersion(dep.name, dep.ecosystem);
|
|
819
|
-
|
|
820
|
-
if (latestVersion !== dep.version) {
|
|
821
|
-
return {
|
|
822
|
-
name: dep.name,
|
|
823
|
-
currentVersion: dep.version,
|
|
824
|
-
latestVersion,
|
|
825
|
-
updateType: this.determineUpdateType(dep.version, latestVersion),
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
return null;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
private async queryVulnerabilityDatabase(
|
|
833
|
-
name: string,
|
|
834
|
-
version: string,
|
|
835
|
-
ecosystem: DependencyInfo['ecosystem']
|
|
836
|
-
): Promise<Vulnerability[]> {
|
|
837
|
-
// Query OSV API for vulnerabilities
|
|
838
|
-
const dep: DependencyInfo = { name, version, ecosystem };
|
|
839
|
-
return this.checkDependencyVulnerabilities(dep, ecosystem);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
private async getLatestVersion(
|
|
843
|
-
name: string,
|
|
844
|
-
ecosystem: DependencyInfo['ecosystem']
|
|
845
|
-
): Promise<string> {
|
|
846
|
-
try {
|
|
847
|
-
if (ecosystem === 'npm') {
|
|
848
|
-
// Query npm registry for latest version
|
|
849
|
-
const response = await httpGet<{ 'dist-tags'?: { latest?: string }; version?: string }>(
|
|
850
|
-
`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`
|
|
851
|
-
);
|
|
852
|
-
|
|
853
|
-
if (response.ok && response.data.version) {
|
|
854
|
-
return response.data.version;
|
|
855
|
-
}
|
|
856
|
-
} else if (ecosystem === 'pip') {
|
|
857
|
-
// Query PyPI for latest version
|
|
858
|
-
const response = await httpGet<{ info?: { version?: string } }>(
|
|
859
|
-
`https://pypi.org/pypi/${encodeURIComponent(name)}/json`
|
|
860
|
-
);
|
|
861
|
-
|
|
862
|
-
if (response.ok && response.data.info?.version) {
|
|
863
|
-
return response.data.info.version;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
// Additional registries (Maven, NuGet, Cargo) would be implemented similarly
|
|
867
|
-
} catch (error) {
|
|
868
|
-
console.error(`Failed to get latest version for ${name}:`, error);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
return 'unknown';
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
private async checkDeprecation(
|
|
875
|
-
name: string,
|
|
876
|
-
ecosystem: DependencyInfo['ecosystem']
|
|
877
|
-
): Promise<boolean> {
|
|
878
|
-
try {
|
|
879
|
-
if (ecosystem === 'npm') {
|
|
880
|
-
// Query npm registry for deprecation notice
|
|
881
|
-
const response = await httpGet<{ deprecated?: string }>(
|
|
882
|
-
`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
if (response.ok && response.data.deprecated) {
|
|
886
|
-
return true;
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
// Additional registries would be implemented similarly
|
|
890
|
-
} catch (error) {
|
|
891
|
-
console.error(`Failed to check deprecation for ${name}:`, error);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
return false;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
private determineUpdateType(
|
|
898
|
-
current: string,
|
|
899
|
-
latest: string
|
|
900
|
-
): 'major' | 'minor' | 'patch' {
|
|
901
|
-
const currentParts = current.split('.').map(Number);
|
|
902
|
-
const latestParts = latest.split('.').map(Number);
|
|
903
|
-
|
|
904
|
-
if (latestParts[0] > currentParts[0]) return 'major';
|
|
905
|
-
if (latestParts[1] > currentParts[1]) return 'minor';
|
|
906
|
-
return 'patch';
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
private hasBreakingChanges(current: string, latest: string): boolean {
|
|
910
|
-
const currentMajor = parseInt(current.split('.')[0]);
|
|
911
|
-
const latestMajor = parseInt(latest.split('.')[0]);
|
|
912
|
-
return latestMajor > currentMajor;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
private createDependencySummary(
|
|
916
|
-
vulnerabilities: Vulnerability[],
|
|
917
|
-
totalDeps: number
|
|
918
|
-
): ScanSummary {
|
|
919
|
-
// Count vulnerabilities by severity
|
|
920
|
-
let critical = 0;
|
|
921
|
-
let high = 0;
|
|
922
|
-
let medium = 0;
|
|
923
|
-
let low = 0;
|
|
924
|
-
let informational = 0;
|
|
925
|
-
|
|
926
|
-
for (const vuln of vulnerabilities) {
|
|
927
|
-
switch (vuln.severity) {
|
|
928
|
-
case 'critical':
|
|
929
|
-
critical++;
|
|
930
|
-
break;
|
|
931
|
-
case 'high':
|
|
932
|
-
high++;
|
|
933
|
-
break;
|
|
934
|
-
case 'medium':
|
|
935
|
-
medium++;
|
|
936
|
-
break;
|
|
937
|
-
case 'low':
|
|
938
|
-
low++;
|
|
939
|
-
break;
|
|
940
|
-
case 'informational':
|
|
941
|
-
informational++;
|
|
942
|
-
break;
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
return {
|
|
947
|
-
critical,
|
|
948
|
-
high,
|
|
949
|
-
medium,
|
|
950
|
-
low,
|
|
951
|
-
informational,
|
|
952
|
-
totalFiles: totalDeps,
|
|
953
|
-
scanDurationMs: 0,
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
private shouldExclude(filePath: string): boolean {
|
|
958
|
-
return this.config.excludePatterns.some((pattern) => {
|
|
959
|
-
if (pattern.startsWith('*')) {
|
|
960
|
-
return filePath.endsWith(pattern.slice(1));
|
|
961
|
-
}
|
|
962
|
-
return filePath.includes(pattern);
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
private async scanFileForSecrets(file: FilePath): Promise<DetectedSecret[]> {
|
|
967
|
-
const secrets: DetectedSecret[] = [];
|
|
968
|
-
|
|
969
|
-
try {
|
|
970
|
-
// Read the file content
|
|
971
|
-
const content = await fs.readFile(file.value, 'utf-8');
|
|
972
|
-
const lines = content.split('\n');
|
|
973
|
-
|
|
974
|
-
// Comprehensive secret detection patterns
|
|
975
|
-
const secretPatterns: Array<{
|
|
976
|
-
name: string;
|
|
977
|
-
type: DetectedSecret['type'];
|
|
978
|
-
regex: RegExp;
|
|
979
|
-
entropyThreshold?: number;
|
|
980
|
-
}> = [
|
|
981
|
-
// AWS Keys
|
|
982
|
-
{
|
|
983
|
-
name: 'AWS Access Key ID',
|
|
984
|
-
type: 'api-key',
|
|
985
|
-
regex: /AKIA[0-9A-Z]{16}/g,
|
|
986
|
-
},
|
|
987
|
-
{
|
|
988
|
-
name: 'AWS Secret Access Key',
|
|
989
|
-
type: 'api-key',
|
|
990
|
-
regex: /(?:aws[_-]?secret[_-]?access[_-]?key|AWS_SECRET_ACCESS_KEY)['"]?\s*[:=]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/gi,
|
|
991
|
-
entropyThreshold: 4.0,
|
|
992
|
-
},
|
|
993
|
-
// GitHub Tokens
|
|
994
|
-
{
|
|
995
|
-
name: 'GitHub Personal Access Token',
|
|
996
|
-
type: 'token',
|
|
997
|
-
regex: /ghp_[A-Za-z0-9]{36}/g,
|
|
998
|
-
},
|
|
999
|
-
{
|
|
1000
|
-
name: 'GitHub OAuth Access Token',
|
|
1001
|
-
type: 'token',
|
|
1002
|
-
regex: /gho_[A-Za-z0-9]{36}/g,
|
|
1003
|
-
},
|
|
1004
|
-
{
|
|
1005
|
-
name: 'GitHub App Token',
|
|
1006
|
-
type: 'token',
|
|
1007
|
-
regex: /(?:ghu|ghs)_[A-Za-z0-9]{36}/g,
|
|
1008
|
-
},
|
|
1009
|
-
// Generic API Keys
|
|
1010
|
-
{
|
|
1011
|
-
name: 'Generic API Key',
|
|
1012
|
-
type: 'api-key',
|
|
1013
|
-
regex: /(?:api[_-]?key|apikey|api_secret)['"]?\s*[:=]\s*['"]([A-Za-z0-9_\-]{20,})['"]?/gi,
|
|
1014
|
-
entropyThreshold: 3.5,
|
|
1015
|
-
},
|
|
1016
|
-
// OpenAI API Key
|
|
1017
|
-
{
|
|
1018
|
-
name: 'OpenAI API Key',
|
|
1019
|
-
type: 'api-key',
|
|
1020
|
-
regex: /sk-[A-Za-z0-9]{48}/g,
|
|
1021
|
-
},
|
|
1022
|
-
// Stripe Keys
|
|
1023
|
-
{
|
|
1024
|
-
name: 'Stripe Secret Key',
|
|
1025
|
-
type: 'api-key',
|
|
1026
|
-
regex: /sk_live_[A-Za-z0-9]{24,}/g,
|
|
1027
|
-
},
|
|
1028
|
-
{
|
|
1029
|
-
name: 'Stripe Publishable Key',
|
|
1030
|
-
type: 'api-key',
|
|
1031
|
-
regex: /pk_live_[A-Za-z0-9]{24,}/g,
|
|
1032
|
-
},
|
|
1033
|
-
// Private Keys
|
|
1034
|
-
{
|
|
1035
|
-
name: 'RSA Private Key',
|
|
1036
|
-
type: 'private-key',
|
|
1037
|
-
regex: /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/gi,
|
|
1038
|
-
},
|
|
1039
|
-
{
|
|
1040
|
-
name: 'PGP Private Key',
|
|
1041
|
-
type: 'private-key',
|
|
1042
|
-
regex: /-----BEGIN\s+PGP\s+PRIVATE\s+KEY\s+BLOCK-----/gi,
|
|
1043
|
-
},
|
|
1044
|
-
{
|
|
1045
|
-
name: 'SSH Private Key',
|
|
1046
|
-
type: 'private-key',
|
|
1047
|
-
regex: /-----BEGIN\s+(?:OPENSSH|DSA|EC)?\s*PRIVATE\s+KEY-----/gi,
|
|
1048
|
-
},
|
|
1049
|
-
// Passwords
|
|
1050
|
-
{
|
|
1051
|
-
name: 'Password Assignment',
|
|
1052
|
-
type: 'password',
|
|
1053
|
-
regex: /(?:password|passwd|pwd|secret)['"]?\s*[:=]\s*['"]([^'"\s]{8,})['"]?/gi,
|
|
1054
|
-
entropyThreshold: 3.0,
|
|
1055
|
-
},
|
|
1056
|
-
// Database Connection Strings
|
|
1057
|
-
{
|
|
1058
|
-
name: 'Database Connection String',
|
|
1059
|
-
type: 'password',
|
|
1060
|
-
regex: /(?:mongodb|postgres|mysql|redis):\/\/[^:]+:[^@]+@[^\s'"]+/gi,
|
|
1061
|
-
},
|
|
1062
|
-
// JWT Tokens
|
|
1063
|
-
{
|
|
1064
|
-
name: 'JWT Token',
|
|
1065
|
-
type: 'token',
|
|
1066
|
-
regex: /eyJ[A-Za-z0-9_-]*\.eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*/g,
|
|
1067
|
-
entropyThreshold: 4.0,
|
|
1068
|
-
},
|
|
1069
|
-
// Slack Tokens
|
|
1070
|
-
{
|
|
1071
|
-
name: 'Slack Token',
|
|
1072
|
-
type: 'token',
|
|
1073
|
-
regex: /xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
1074
|
-
},
|
|
1075
|
-
// Google API Key
|
|
1076
|
-
{
|
|
1077
|
-
name: 'Google API Key',
|
|
1078
|
-
type: 'api-key',
|
|
1079
|
-
regex: /AIza[A-Za-z0-9_-]{35}/g,
|
|
1080
|
-
},
|
|
1081
|
-
// Twilio
|
|
1082
|
-
{
|
|
1083
|
-
name: 'Twilio API Key',
|
|
1084
|
-
type: 'api-key',
|
|
1085
|
-
regex: /SK[A-Za-z0-9]{32}/g,
|
|
1086
|
-
},
|
|
1087
|
-
// SendGrid
|
|
1088
|
-
{
|
|
1089
|
-
name: 'SendGrid API Key',
|
|
1090
|
-
type: 'api-key',
|
|
1091
|
-
regex: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g,
|
|
1092
|
-
},
|
|
1093
|
-
// Certificates
|
|
1094
|
-
{
|
|
1095
|
-
name: 'X.509 Certificate',
|
|
1096
|
-
type: 'certificate',
|
|
1097
|
-
regex: /-----BEGIN\s+CERTIFICATE-----/gi,
|
|
1098
|
-
},
|
|
1099
|
-
];
|
|
1100
|
-
|
|
1101
|
-
// Scan each line for secrets
|
|
1102
|
-
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
1103
|
-
const line = lines[lineIndex];
|
|
1104
|
-
const lineNumber = lineIndex + 1;
|
|
1105
|
-
|
|
1106
|
-
for (const pattern of secretPatterns) {
|
|
1107
|
-
// Reset regex lastIndex for global patterns
|
|
1108
|
-
pattern.regex.lastIndex = 0;
|
|
1109
|
-
|
|
1110
|
-
let match: RegExpExecArray | null;
|
|
1111
|
-
while ((match = pattern.regex.exec(line)) !== null) {
|
|
1112
|
-
const matchedValue = match[1] || match[0];
|
|
1113
|
-
|
|
1114
|
-
// Calculate entropy if threshold is specified
|
|
1115
|
-
const entropy = this.calculateEntropy(matchedValue);
|
|
1116
|
-
|
|
1117
|
-
// Skip low-entropy matches if threshold is specified
|
|
1118
|
-
if (pattern.entropyThreshold && entropy < pattern.entropyThreshold) {
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
// Create masked snippet (show context but mask the actual secret)
|
|
1123
|
-
const snippet = this.createMaskedSnippet(line, match.index, matchedValue.length);
|
|
1124
|
-
|
|
1125
|
-
const location: VulnerabilityLocation = {
|
|
1126
|
-
file: file.value,
|
|
1127
|
-
line: lineNumber,
|
|
1128
|
-
column: match.index + 1,
|
|
1129
|
-
snippet,
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
secrets.push({
|
|
1133
|
-
type: pattern.type,
|
|
1134
|
-
location,
|
|
1135
|
-
entropy,
|
|
1136
|
-
isValid: this.validateSecret(pattern.type, matchedValue),
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
} catch (error) {
|
|
1142
|
-
// File reading error - skip this file
|
|
1143
|
-
console.error(`Failed to scan file for secrets: ${file.value}`, error);
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
return secrets;
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
/**
|
|
1150
|
-
* Calculate Shannon entropy of a string
|
|
1151
|
-
*/
|
|
1152
|
-
private calculateEntropy(str: string): number {
|
|
1153
|
-
if (!str || str.length === 0) return 0;
|
|
1154
|
-
|
|
1155
|
-
const charFrequency: Record<string, number> = {};
|
|
1156
|
-
|
|
1157
|
-
for (const char of str) {
|
|
1158
|
-
charFrequency[char] = (charFrequency[char] || 0) + 1;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
let entropy = 0;
|
|
1162
|
-
const len = str.length;
|
|
1163
|
-
|
|
1164
|
-
for (const char in charFrequency) {
|
|
1165
|
-
const probability = charFrequency[char] / len;
|
|
1166
|
-
entropy -= probability * Math.log2(probability);
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
return entropy;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
/**
|
|
1173
|
-
* Create a masked snippet for display
|
|
1174
|
-
*/
|
|
1175
|
-
private createMaskedSnippet(line: string, matchIndex: number, matchLength: number): string {
|
|
1176
|
-
const contextBefore = 20;
|
|
1177
|
-
const contextAfter = 10;
|
|
1178
|
-
|
|
1179
|
-
const start = Math.max(0, matchIndex - contextBefore);
|
|
1180
|
-
const end = Math.min(line.length, matchIndex + matchLength + contextAfter);
|
|
1181
|
-
|
|
1182
|
-
let snippet = line.substring(start, end);
|
|
1183
|
-
|
|
1184
|
-
// Mask the secret value (show first 4 and last 4 characters)
|
|
1185
|
-
const secretStart = matchIndex - start;
|
|
1186
|
-
const secretInSnippet = snippet.substring(secretStart, secretStart + matchLength);
|
|
1187
|
-
|
|
1188
|
-
if (secretInSnippet.length > 8) {
|
|
1189
|
-
const masked = secretInSnippet.substring(0, 4) + '...' + secretInSnippet.substring(secretInSnippet.length - 4);
|
|
1190
|
-
snippet = snippet.substring(0, secretStart) + masked + snippet.substring(secretStart + matchLength);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
if (start > 0) snippet = '...' + snippet;
|
|
1194
|
-
if (end < line.length) snippet = snippet + '...';
|
|
1195
|
-
|
|
1196
|
-
return snippet;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
/**
|
|
1200
|
-
* Validate if a detected secret is likely valid (basic validation)
|
|
1201
|
-
*/
|
|
1202
|
-
private validateSecret(type: DetectedSecret['type'], value: string): boolean {
|
|
1203
|
-
// Basic validation - in production, could do more sophisticated checks
|
|
1204
|
-
switch (type) {
|
|
1205
|
-
case 'api-key':
|
|
1206
|
-
// Check minimum length and character variety
|
|
1207
|
-
return value.length >= 20 && /[a-z]/.test(value) && /[A-Z0-9]/.test(value);
|
|
1208
|
-
case 'token':
|
|
1209
|
-
return value.length >= 20;
|
|
1210
|
-
case 'password':
|
|
1211
|
-
// Likely not a placeholder password
|
|
1212
|
-
return !['password', 'secret', 'changeme', '12345678', 'qwerty'].includes(value.toLowerCase());
|
|
1213
|
-
case 'private-key':
|
|
1214
|
-
return true; // Private key headers are already specific
|
|
1215
|
-
case 'certificate':
|
|
1216
|
-
return true;
|
|
1217
|
-
default:
|
|
1218
|
-
return true;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
/**
|
|
1223
|
-
* Perform SAST scan using AST-based analysis for JavaScript/TypeScript
|
|
1224
|
-
* Scans for common vulnerability patterns: XSS, SQL injection, command injection, path traversal
|
|
1225
|
-
*/
|
|
1226
|
-
private async performSASTScan(): Promise<SASTResult> {
|
|
1227
|
-
const scanId = uuidv4();
|
|
1228
|
-
const startTime = Date.now();
|
|
1229
|
-
const vulnerabilities: Vulnerability[] = [];
|
|
1230
|
-
let filesScanned = 0;
|
|
1231
|
-
let linesScanned = 0;
|
|
1232
|
-
|
|
1233
|
-
try {
|
|
1234
|
-
// Find source files to scan
|
|
1235
|
-
const sourceFiles = await this.findSourceFiles(process.cwd());
|
|
1236
|
-
|
|
1237
|
-
// Define vulnerability patterns for AST-like analysis
|
|
1238
|
-
const vulnerabilityPatterns: Array<{
|
|
1239
|
-
id: string;
|
|
1240
|
-
pattern: RegExp;
|
|
1241
|
-
title: string;
|
|
1242
|
-
description: string;
|
|
1243
|
-
severity: VulnerabilitySeverity;
|
|
1244
|
-
category: VulnerabilityCategory;
|
|
1245
|
-
remediation: string;
|
|
1246
|
-
fixExample?: string;
|
|
1247
|
-
cweId: string;
|
|
1248
|
-
}> = [
|
|
1249
|
-
// SQL Injection patterns
|
|
1250
|
-
{
|
|
1251
|
-
id: 'sqli-concat',
|
|
1252
|
-
pattern: /(?:query|execute|exec|run)\s*\(\s*(?:['"`].*?\s*\+|`[^`]*\$\{)/gi,
|
|
1253
|
-
title: 'SQL Injection via String Concatenation',
|
|
1254
|
-
description: 'SQL query constructed using string concatenation with potentially untrusted input',
|
|
1255
|
-
severity: 'critical',
|
|
1256
|
-
category: 'injection',
|
|
1257
|
-
remediation: 'Use parameterized queries or prepared statements',
|
|
1258
|
-
fixExample: 'db.query("SELECT * FROM users WHERE id = $1", [userId])',
|
|
1259
|
-
cweId: 'CWE-89',
|
|
1260
|
-
},
|
|
1261
|
-
// XSS patterns
|
|
1262
|
-
{
|
|
1263
|
-
id: 'xss-innerhtml',
|
|
1264
|
-
pattern: /\.innerHTML\s*=\s*(?!['"`])/g,
|
|
1265
|
-
title: 'XSS via innerHTML Assignment',
|
|
1266
|
-
description: 'Direct innerHTML assignment with potentially unsanitized content',
|
|
1267
|
-
severity: 'high',
|
|
1268
|
-
category: 'xss',
|
|
1269
|
-
remediation: 'Use textContent for text, or sanitize HTML with DOMPurify',
|
|
1270
|
-
fixExample: 'element.textContent = userInput;',
|
|
1271
|
-
cweId: 'CWE-79',
|
|
1272
|
-
},
|
|
1273
|
-
{
|
|
1274
|
-
id: 'xss-document-write',
|
|
1275
|
-
pattern: /document\.write\s*\([^)]+\)/g,
|
|
1276
|
-
title: 'XSS via document.write',
|
|
1277
|
-
description: 'document.write() can execute scripts from untrusted data',
|
|
1278
|
-
severity: 'high',
|
|
1279
|
-
category: 'xss',
|
|
1280
|
-
remediation: 'Avoid document.write(); use DOM manipulation methods',
|
|
1281
|
-
cweId: 'CWE-79',
|
|
1282
|
-
},
|
|
1283
|
-
{
|
|
1284
|
-
id: 'xss-eval',
|
|
1285
|
-
pattern: /(?<!\.)\beval\s*\([^)]+\)/g,
|
|
1286
|
-
title: 'Code Injection via eval()',
|
|
1287
|
-
description: 'eval() executes arbitrary code and is a major security risk',
|
|
1288
|
-
severity: 'critical',
|
|
1289
|
-
category: 'xss',
|
|
1290
|
-
remediation: 'Never use eval(); use JSON.parse() for JSON data',
|
|
1291
|
-
fixExample: 'JSON.parse(jsonString)',
|
|
1292
|
-
cweId: 'CWE-95',
|
|
1293
|
-
},
|
|
1294
|
-
{
|
|
1295
|
-
id: 'xss-dangerous-react',
|
|
1296
|
-
pattern: /dangerouslySetInnerHTML\s*=\s*\{/g,
|
|
1297
|
-
title: 'React dangerouslySetInnerHTML Usage',
|
|
1298
|
-
description: 'dangerouslySetInnerHTML bypasses React XSS protections',
|
|
1299
|
-
severity: 'medium',
|
|
1300
|
-
category: 'xss',
|
|
1301
|
-
remediation: 'Sanitize HTML content with DOMPurify before use',
|
|
1302
|
-
fixExample: 'dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}',
|
|
1303
|
-
cweId: 'CWE-79',
|
|
1304
|
-
},
|
|
1305
|
-
// Command Injection patterns
|
|
1306
|
-
{
|
|
1307
|
-
id: 'cmd-injection-exec',
|
|
1308
|
-
pattern: /(?:child_process\.)?exec\s*\(\s*(?:[^,)]*\s*\+|`[^`]*\$\{)/g,
|
|
1309
|
-
title: 'Command Injection via exec()',
|
|
1310
|
-
description: 'Shell command execution with unsanitized input',
|
|
1311
|
-
severity: 'critical',
|
|
1312
|
-
category: 'injection',
|
|
1313
|
-
remediation: 'Use execFile() with argument array instead of exec()',
|
|
1314
|
-
fixExample: 'execFile("command", [arg1, arg2], callback)',
|
|
1315
|
-
cweId: 'CWE-78',
|
|
1316
|
-
},
|
|
1317
|
-
{
|
|
1318
|
-
id: 'cmd-injection-spawn-shell',
|
|
1319
|
-
pattern: /spawn\s*\([^)]+,\s*\{[^}]*shell\s*:\s*true/g,
|
|
1320
|
-
title: 'Dangerous Shell Option in spawn()',
|
|
1321
|
-
description: 'spawn() with shell: true can enable command injection',
|
|
1322
|
-
severity: 'high',
|
|
1323
|
-
category: 'injection',
|
|
1324
|
-
remediation: 'Avoid shell: true option; use direct command execution',
|
|
1325
|
-
cweId: 'CWE-78',
|
|
1326
|
-
},
|
|
1327
|
-
// Path Traversal patterns
|
|
1328
|
-
{
|
|
1329
|
-
id: 'path-traversal-readfile',
|
|
1330
|
-
pattern: /(?:readFile|readFileSync)\s*\([^)]*\+/g,
|
|
1331
|
-
title: 'Path Traversal via File Read',
|
|
1332
|
-
description: 'File read operation with concatenated path may allow directory traversal',
|
|
1333
|
-
severity: 'high',
|
|
1334
|
-
category: 'access-control',
|
|
1335
|
-
remediation: 'Validate and sanitize file paths; use path.resolve() and check against base directory',
|
|
1336
|
-
fixExample: 'const safePath = path.resolve(baseDir, path.basename(userInput))',
|
|
1337
|
-
cweId: 'CWE-22',
|
|
1338
|
-
},
|
|
1339
|
-
{
|
|
1340
|
-
id: 'path-traversal-writefile',
|
|
1341
|
-
pattern: /(?:writeFile|writeFileSync)\s*\([^)]*\+/g,
|
|
1342
|
-
title: 'Path Traversal via File Write',
|
|
1343
|
-
description: 'File write operation with concatenated path may allow directory traversal',
|
|
1344
|
-
severity: 'high',
|
|
1345
|
-
category: 'access-control',
|
|
1346
|
-
remediation: 'Validate file paths before writing; ensure path is within allowed directory',
|
|
1347
|
-
cweId: 'CWE-22',
|
|
1348
|
-
},
|
|
1349
|
-
// Hardcoded secrets
|
|
1350
|
-
{
|
|
1351
|
-
id: 'secret-private-key',
|
|
1352
|
-
pattern: /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----/g,
|
|
1353
|
-
title: 'Private Key Detected in Source',
|
|
1354
|
-
description: 'Private key found in source code',
|
|
1355
|
-
severity: 'critical',
|
|
1356
|
-
category: 'sensitive-data',
|
|
1357
|
-
remediation: 'Store private keys in secure key management systems, not in code',
|
|
1358
|
-
cweId: 'CWE-798',
|
|
1359
|
-
},
|
|
1360
|
-
// Insecure configurations
|
|
1361
|
-
{
|
|
1362
|
-
id: 'config-tls-disabled',
|
|
1363
|
-
pattern: /rejectUnauthorized\s*:\s*false/g,
|
|
1364
|
-
title: 'TLS Certificate Validation Disabled',
|
|
1365
|
-
description: 'Disabling TLS certificate validation exposes to MITM attacks',
|
|
1366
|
-
severity: 'high',
|
|
1367
|
-
category: 'security-misconfiguration',
|
|
1368
|
-
remediation: 'Always enable TLS certificate validation in production',
|
|
1369
|
-
cweId: 'CWE-295',
|
|
1370
|
-
},
|
|
1371
|
-
{
|
|
1372
|
-
id: 'config-cors-wildcard',
|
|
1373
|
-
pattern: /cors\s*\(\s*\{[^}]*origin\s*:\s*['"]\*['"]/gi,
|
|
1374
|
-
title: 'Permissive CORS Configuration',
|
|
1375
|
-
description: 'CORS allows all origins (*) which may expose sensitive data',
|
|
1376
|
-
severity: 'medium',
|
|
1377
|
-
category: 'security-misconfiguration',
|
|
1378
|
-
remediation: 'Restrict CORS to specific trusted origins',
|
|
1379
|
-
fixExample: 'cors({ origin: ["https://trusted-domain.com"] })',
|
|
1380
|
-
cweId: 'CWE-942',
|
|
1381
|
-
},
|
|
1382
|
-
];
|
|
1383
|
-
|
|
1384
|
-
for (const filePath of sourceFiles) {
|
|
1385
|
-
if (this.shouldExclude(filePath)) {
|
|
1386
|
-
continue;
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
// Only scan JS/TS files
|
|
1390
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
1391
|
-
if (!['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(ext)) {
|
|
1392
|
-
continue;
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
try {
|
|
1396
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
1397
|
-
const lines = content.split('\n');
|
|
1398
|
-
filesScanned++;
|
|
1399
|
-
linesScanned += lines.length;
|
|
1400
|
-
|
|
1401
|
-
// Check each vulnerability pattern
|
|
1402
|
-
for (const vulnPattern of vulnerabilityPatterns) {
|
|
1403
|
-
// Reset regex state
|
|
1404
|
-
vulnPattern.pattern.lastIndex = 0;
|
|
1405
|
-
let match: RegExpExecArray | null;
|
|
1406
|
-
|
|
1407
|
-
while ((match = vulnPattern.pattern.exec(content)) !== null) {
|
|
1408
|
-
// Calculate line and column
|
|
1409
|
-
const beforeMatch = content.substring(0, match.index);
|
|
1410
|
-
const linesBefore = beforeMatch.split('\n');
|
|
1411
|
-
const lineNumber = linesBefore.length;
|
|
1412
|
-
const column = linesBefore[linesBefore.length - 1].length + 1;
|
|
1413
|
-
|
|
1414
|
-
// Check if in comment
|
|
1415
|
-
const currentLine = lines[lineNumber - 1] || '';
|
|
1416
|
-
if (currentLine.trimStart().startsWith('//') || currentLine.trimStart().startsWith('*')) {
|
|
1417
|
-
continue;
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
// Check for nosec annotation
|
|
1421
|
-
if (currentLine.includes('// nosec') || currentLine.includes('// security-ignore')) {
|
|
1422
|
-
continue;
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
// Extract snippet with context
|
|
1426
|
-
const startLine = Math.max(0, lineNumber - 2);
|
|
1427
|
-
const endLine = Math.min(lines.length, lineNumber + 1);
|
|
1428
|
-
const snippet = lines.slice(startLine, endLine).join('\n');
|
|
1429
|
-
|
|
1430
|
-
const location: VulnerabilityLocation = {
|
|
1431
|
-
file: filePath,
|
|
1432
|
-
line: lineNumber,
|
|
1433
|
-
column,
|
|
1434
|
-
snippet,
|
|
1435
|
-
};
|
|
1436
|
-
|
|
1437
|
-
const remediation: RemediationAdvice = {
|
|
1438
|
-
description: vulnPattern.remediation,
|
|
1439
|
-
fixExample: vulnPattern.fixExample,
|
|
1440
|
-
estimatedEffort: vulnPattern.severity === 'critical' ? 'moderate' : 'minor',
|
|
1441
|
-
automatable: vulnPattern.severity === 'low' || vulnPattern.severity === 'medium',
|
|
1442
|
-
};
|
|
1443
|
-
|
|
1444
|
-
vulnerabilities.push({
|
|
1445
|
-
id: uuidv4(),
|
|
1446
|
-
cveId: undefined,
|
|
1447
|
-
title: vulnPattern.title,
|
|
1448
|
-
description: `${vulnPattern.description} [${vulnPattern.cweId}]`,
|
|
1449
|
-
severity: vulnPattern.severity,
|
|
1450
|
-
category: vulnPattern.category,
|
|
1451
|
-
location,
|
|
1452
|
-
remediation,
|
|
1453
|
-
references: [
|
|
1454
|
-
`https://cwe.mitre.org/data/definitions/${vulnPattern.cweId.replace('CWE-', '')}.html`,
|
|
1455
|
-
],
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
} catch {
|
|
1460
|
-
// Skip files that can't be read
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
} catch (error) {
|
|
1464
|
-
console.error('SAST scan failed:', error);
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
const scanDurationMs = Date.now() - startTime;
|
|
1468
|
-
|
|
1469
|
-
// Calculate summary
|
|
1470
|
-
let critical = 0, high = 0, medium = 0, low = 0, informational = 0;
|
|
1471
|
-
for (const vuln of vulnerabilities) {
|
|
1472
|
-
switch (vuln.severity) {
|
|
1473
|
-
case 'critical': critical++; break;
|
|
1474
|
-
case 'high': high++; break;
|
|
1475
|
-
case 'medium': medium++; break;
|
|
1476
|
-
case 'low': low++; break;
|
|
1477
|
-
case 'informational': informational++; break;
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
return {
|
|
1482
|
-
scanId,
|
|
1483
|
-
vulnerabilities,
|
|
1484
|
-
summary: {
|
|
1485
|
-
critical,
|
|
1486
|
-
high,
|
|
1487
|
-
medium,
|
|
1488
|
-
low,
|
|
1489
|
-
informational,
|
|
1490
|
-
totalFiles: filesScanned,
|
|
1491
|
-
scanDurationMs,
|
|
1492
|
-
},
|
|
1493
|
-
coverage: {
|
|
1494
|
-
filesScanned,
|
|
1495
|
-
linesScanned,
|
|
1496
|
-
rulesApplied: 12, // Number of vulnerability patterns
|
|
1497
|
-
},
|
|
1498
|
-
};
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
/**
|
|
1502
|
-
* Perform DAST scan using HTTP requests to test for vulnerabilities
|
|
1503
|
-
* Tests for common web vulnerabilities: XSS, SQL injection, security headers, etc.
|
|
1504
|
-
*/
|
|
1505
|
-
private async performDASTScan(targetUrl: string): Promise<DASTResult> {
|
|
1506
|
-
const scanId = uuidv4();
|
|
1507
|
-
const startTime = Date.now();
|
|
1508
|
-
const vulnerabilities: Vulnerability[] = [];
|
|
1509
|
-
let crawledUrls = 0;
|
|
1510
|
-
|
|
1511
|
-
try {
|
|
1512
|
-
// Validate URL
|
|
1513
|
-
let parsedUrl: URL;
|
|
1514
|
-
try {
|
|
1515
|
-
parsedUrl = new URL(targetUrl);
|
|
1516
|
-
} catch {
|
|
1517
|
-
return {
|
|
1518
|
-
scanId,
|
|
1519
|
-
targetUrl,
|
|
1520
|
-
vulnerabilities: [{
|
|
1521
|
-
id: uuidv4(),
|
|
1522
|
-
title: 'Invalid Target URL',
|
|
1523
|
-
description: 'The provided target URL is not valid',
|
|
1524
|
-
severity: 'informational',
|
|
1525
|
-
category: 'security-misconfiguration',
|
|
1526
|
-
location: { file: targetUrl },
|
|
1527
|
-
remediation: { description: 'Provide a valid URL', estimatedEffort: 'trivial', automatable: false },
|
|
1528
|
-
references: [],
|
|
1529
|
-
}],
|
|
1530
|
-
summary: { critical: 0, high: 0, medium: 0, low: 0, informational: 1, totalFiles: 0, scanDurationMs: Date.now() - startTime },
|
|
1531
|
-
crawledUrls: 0,
|
|
1532
|
-
};
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
// Perform HTTP request to check security headers and response characteristics
|
|
1536
|
-
const controller = new AbortController();
|
|
1537
|
-
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
|
|
1538
|
-
|
|
1539
|
-
try {
|
|
1540
|
-
const response = await fetch(targetUrl, {
|
|
1541
|
-
method: 'GET',
|
|
1542
|
-
headers: {
|
|
1543
|
-
'User-Agent': 'AgenticQE-SecurityScanner/3.0',
|
|
1544
|
-
'Accept': 'text/html,application/json,*/*',
|
|
1545
|
-
},
|
|
1546
|
-
signal: controller.signal,
|
|
1547
|
-
redirect: 'follow',
|
|
1548
|
-
});
|
|
1549
|
-
|
|
1550
|
-
clearTimeout(timeoutId);
|
|
1551
|
-
crawledUrls = 1;
|
|
1552
|
-
|
|
1553
|
-
// Check security headers
|
|
1554
|
-
const headers = response.headers;
|
|
1555
|
-
|
|
1556
|
-
// Check for missing security headers
|
|
1557
|
-
const securityHeaderChecks: Array<{
|
|
1558
|
-
header: string;
|
|
1559
|
-
title: string;
|
|
1560
|
-
description: string;
|
|
1561
|
-
severity: VulnerabilitySeverity;
|
|
1562
|
-
remediation: string;
|
|
1563
|
-
}> = [
|
|
1564
|
-
{
|
|
1565
|
-
header: 'strict-transport-security',
|
|
1566
|
-
title: 'Missing HTTP Strict Transport Security (HSTS)',
|
|
1567
|
-
description: 'HSTS header is missing, allowing downgrade attacks',
|
|
1568
|
-
severity: 'medium',
|
|
1569
|
-
remediation: 'Add Strict-Transport-Security header with appropriate max-age',
|
|
1570
|
-
},
|
|
1571
|
-
{
|
|
1572
|
-
header: 'x-content-type-options',
|
|
1573
|
-
title: 'Missing X-Content-Type-Options Header',
|
|
1574
|
-
description: 'X-Content-Type-Options header is missing, allowing MIME sniffing attacks',
|
|
1575
|
-
severity: 'low',
|
|
1576
|
-
remediation: 'Add X-Content-Type-Options: nosniff header',
|
|
1577
|
-
},
|
|
1578
|
-
{
|
|
1579
|
-
header: 'x-frame-options',
|
|
1580
|
-
title: 'Missing X-Frame-Options Header',
|
|
1581
|
-
description: 'X-Frame-Options header is missing, allowing clickjacking attacks',
|
|
1582
|
-
severity: 'medium',
|
|
1583
|
-
remediation: 'Add X-Frame-Options: DENY or SAMEORIGIN header',
|
|
1584
|
-
},
|
|
1585
|
-
{
|
|
1586
|
-
header: 'content-security-policy',
|
|
1587
|
-
title: 'Missing Content Security Policy',
|
|
1588
|
-
description: 'CSP header is missing, increasing XSS attack surface',
|
|
1589
|
-
severity: 'medium',
|
|
1590
|
-
remediation: 'Implement a Content-Security-Policy header',
|
|
1591
|
-
},
|
|
1592
|
-
{
|
|
1593
|
-
header: 'x-xss-protection',
|
|
1594
|
-
title: 'Missing X-XSS-Protection Header',
|
|
1595
|
-
description: 'X-XSS-Protection header is missing (legacy XSS filter)',
|
|
1596
|
-
severity: 'low',
|
|
1597
|
-
remediation: 'Add X-XSS-Protection: 1; mode=block header',
|
|
1598
|
-
},
|
|
1599
|
-
];
|
|
1600
|
-
|
|
1601
|
-
for (const check of securityHeaderChecks) {
|
|
1602
|
-
if (!headers.get(check.header)) {
|
|
1603
|
-
vulnerabilities.push({
|
|
1604
|
-
id: uuidv4(),
|
|
1605
|
-
title: check.title,
|
|
1606
|
-
description: check.description,
|
|
1607
|
-
severity: check.severity,
|
|
1608
|
-
category: 'security-misconfiguration',
|
|
1609
|
-
location: {
|
|
1610
|
-
file: targetUrl,
|
|
1611
|
-
snippet: `Response Headers: ${check.header} not present`,
|
|
1612
|
-
},
|
|
1613
|
-
remediation: {
|
|
1614
|
-
description: check.remediation,
|
|
1615
|
-
estimatedEffort: 'minor',
|
|
1616
|
-
automatable: true,
|
|
1617
|
-
},
|
|
1618
|
-
references: [
|
|
1619
|
-
'https://owasp.org/www-project-secure-headers/',
|
|
1620
|
-
],
|
|
1621
|
-
});
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
// Check for insecure cookies
|
|
1626
|
-
const cookies = headers.get('set-cookie');
|
|
1627
|
-
if (cookies) {
|
|
1628
|
-
if (!cookies.toLowerCase().includes('secure')) {
|
|
1629
|
-
vulnerabilities.push({
|
|
1630
|
-
id: uuidv4(),
|
|
1631
|
-
title: 'Cookie Without Secure Flag',
|
|
1632
|
-
description: 'Session cookie is set without the Secure flag',
|
|
1633
|
-
severity: 'medium',
|
|
1634
|
-
category: 'sensitive-data',
|
|
1635
|
-
location: { file: targetUrl, snippet: `Set-Cookie: ${cookies.substring(0, 50)}...` },
|
|
1636
|
-
remediation: { description: 'Add Secure flag to cookies', estimatedEffort: 'trivial', automatable: true },
|
|
1637
|
-
references: ['https://owasp.org/www-community/controls/SecureCookieAttribute'],
|
|
1638
|
-
});
|
|
1639
|
-
}
|
|
1640
|
-
if (!cookies.toLowerCase().includes('httponly')) {
|
|
1641
|
-
vulnerabilities.push({
|
|
1642
|
-
id: uuidv4(),
|
|
1643
|
-
title: 'Cookie Without HttpOnly Flag',
|
|
1644
|
-
description: 'Session cookie is set without the HttpOnly flag, making it accessible to JavaScript',
|
|
1645
|
-
severity: 'medium',
|
|
1646
|
-
category: 'sensitive-data',
|
|
1647
|
-
location: { file: targetUrl, snippet: `Set-Cookie: ${cookies.substring(0, 50)}...` },
|
|
1648
|
-
remediation: { description: 'Add HttpOnly flag to session cookies', estimatedEffort: 'trivial', automatable: true },
|
|
1649
|
-
references: ['https://owasp.org/www-community/HttpOnly'],
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
if (!cookies.toLowerCase().includes('samesite')) {
|
|
1653
|
-
vulnerabilities.push({
|
|
1654
|
-
id: uuidv4(),
|
|
1655
|
-
title: 'Cookie Without SameSite Attribute',
|
|
1656
|
-
description: 'Cookie is set without the SameSite attribute, potentially vulnerable to CSRF',
|
|
1657
|
-
severity: 'low',
|
|
1658
|
-
category: 'broken-auth',
|
|
1659
|
-
location: { file: targetUrl, snippet: `Set-Cookie: ${cookies.substring(0, 50)}...` },
|
|
1660
|
-
remediation: { description: 'Add SameSite=Strict or SameSite=Lax to cookies', estimatedEffort: 'trivial', automatable: true },
|
|
1661
|
-
references: ['https://owasp.org/www-community/SameSite'],
|
|
1662
|
-
});
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
// Check for server information disclosure
|
|
1667
|
-
const server = headers.get('server');
|
|
1668
|
-
if (server && /[0-9]+\.[0-9]+/.test(server)) {
|
|
1669
|
-
vulnerabilities.push({
|
|
1670
|
-
id: uuidv4(),
|
|
1671
|
-
title: 'Server Version Disclosure',
|
|
1672
|
-
description: `Server header reveals version information: ${server}`,
|
|
1673
|
-
severity: 'low',
|
|
1674
|
-
category: 'security-misconfiguration',
|
|
1675
|
-
location: { file: targetUrl, snippet: `Server: ${server}` },
|
|
1676
|
-
remediation: { description: 'Remove or obfuscate server version information', estimatedEffort: 'trivial', automatable: true },
|
|
1677
|
-
references: ['https://owasp.org/www-project-web-security-testing-guide/'],
|
|
1678
|
-
});
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
// Check for HTTPS
|
|
1682
|
-
if (parsedUrl.protocol === 'http:') {
|
|
1683
|
-
vulnerabilities.push({
|
|
1684
|
-
id: uuidv4(),
|
|
1685
|
-
title: 'Insecure HTTP Protocol',
|
|
1686
|
-
description: 'Target is using HTTP instead of HTTPS, exposing data in transit',
|
|
1687
|
-
severity: 'high',
|
|
1688
|
-
category: 'sensitive-data',
|
|
1689
|
-
location: { file: targetUrl },
|
|
1690
|
-
remediation: { description: 'Use HTTPS with valid TLS certificate', estimatedEffort: 'moderate', automatable: false },
|
|
1691
|
-
references: ['https://owasp.org/www-project-web-security-testing-guide/'],
|
|
1692
|
-
});
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
// Try common test endpoints for additional checks
|
|
1696
|
-
const testEndpoints = [
|
|
1697
|
-
{ path: '/.git/config', vuln: 'Git Repository Exposed', severity: 'high' as VulnerabilitySeverity },
|
|
1698
|
-
{ path: '/.env', vuln: 'Environment File Exposed', severity: 'critical' as VulnerabilitySeverity },
|
|
1699
|
-
{ path: '/phpinfo.php', vuln: 'PHP Info Exposed', severity: 'medium' as VulnerabilitySeverity },
|
|
1700
|
-
{ path: '/wp-config.php.bak', vuln: 'WordPress Config Backup Exposed', severity: 'critical' as VulnerabilitySeverity },
|
|
1701
|
-
];
|
|
1702
|
-
|
|
1703
|
-
for (const endpoint of testEndpoints) {
|
|
1704
|
-
try {
|
|
1705
|
-
const testUrl = new URL(endpoint.path, parsedUrl.origin).toString();
|
|
1706
|
-
const testResponse = await fetch(testUrl, {
|
|
1707
|
-
method: 'GET',
|
|
1708
|
-
signal: AbortSignal.timeout(5000),
|
|
1709
|
-
});
|
|
1710
|
-
|
|
1711
|
-
if (testResponse.ok && testResponse.status === 200) {
|
|
1712
|
-
const text = await testResponse.text();
|
|
1713
|
-
// Verify it's actually the sensitive content, not a custom 404
|
|
1714
|
-
if (text.length > 10 && !text.toLowerCase().includes('not found')) {
|
|
1715
|
-
crawledUrls++;
|
|
1716
|
-
vulnerabilities.push({
|
|
1717
|
-
id: uuidv4(),
|
|
1718
|
-
title: endpoint.vuln,
|
|
1719
|
-
description: `Sensitive file found at ${endpoint.path}`,
|
|
1720
|
-
severity: endpoint.severity,
|
|
1721
|
-
category: 'sensitive-data',
|
|
1722
|
-
location: { file: testUrl },
|
|
1723
|
-
remediation: { description: 'Remove or restrict access to sensitive files', estimatedEffort: 'trivial', automatable: true },
|
|
1724
|
-
references: ['https://owasp.org/www-project-web-security-testing-guide/'],
|
|
1725
|
-
});
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
} catch {
|
|
1729
|
-
// Endpoint not accessible, which is expected/good
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
} catch (fetchError) {
|
|
1734
|
-
clearTimeout(timeoutId);
|
|
1735
|
-
const errorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
1736
|
-
|
|
1737
|
-
// Network errors might indicate issues
|
|
1738
|
-
if (errorMessage.includes('certificate') || errorMessage.includes('SSL') || errorMessage.includes('TLS')) {
|
|
1739
|
-
vulnerabilities.push({
|
|
1740
|
-
id: uuidv4(),
|
|
1741
|
-
title: 'TLS/SSL Certificate Issue',
|
|
1742
|
-
description: `Certificate error: ${errorMessage}`,
|
|
1743
|
-
severity: 'high',
|
|
1744
|
-
category: 'security-misconfiguration',
|
|
1745
|
-
location: { file: targetUrl },
|
|
1746
|
-
remediation: { description: 'Fix TLS certificate configuration', estimatedEffort: 'moderate', automatable: false },
|
|
1747
|
-
references: ['https://owasp.org/www-project-web-security-testing-guide/'],
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
} catch (error) {
|
|
1753
|
-
console.error('DAST scan failed:', error);
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
const scanDurationMs = Date.now() - startTime;
|
|
1757
|
-
|
|
1758
|
-
// Calculate summary
|
|
1759
|
-
let critical = 0, high = 0, medium = 0, low = 0, informational = 0;
|
|
1760
|
-
for (const vuln of vulnerabilities) {
|
|
1761
|
-
switch (vuln.severity) {
|
|
1762
|
-
case 'critical': critical++; break;
|
|
1763
|
-
case 'high': high++; break;
|
|
1764
|
-
case 'medium': medium++; break;
|
|
1765
|
-
case 'low': low++; break;
|
|
1766
|
-
case 'informational': informational++; break;
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
|
-
return {
|
|
1771
|
-
scanId,
|
|
1772
|
-
targetUrl,
|
|
1773
|
-
vulnerabilities,
|
|
1774
|
-
summary: {
|
|
1775
|
-
critical,
|
|
1776
|
-
high,
|
|
1777
|
-
medium,
|
|
1778
|
-
low,
|
|
1779
|
-
informational,
|
|
1780
|
-
totalFiles: 1,
|
|
1781
|
-
scanDurationMs,
|
|
1782
|
-
},
|
|
1783
|
-
crawledUrls,
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
/**
|
|
1788
|
-
* Perform dependency scan using OSV API
|
|
1789
|
-
*/
|
|
1790
|
-
private async performDependencyScan(): Promise<DependencyScanResult> {
|
|
1791
|
-
const startTime = Date.now();
|
|
1792
|
-
const vulnerabilities: Vulnerability[] = [];
|
|
1793
|
-
const outdatedPackages: OutdatedPackage[] = [];
|
|
1794
|
-
|
|
1795
|
-
try {
|
|
1796
|
-
// Look for package.json in current working directory
|
|
1797
|
-
const manifestPath = path.join(process.cwd(), 'package.json');
|
|
1798
|
-
const dependencies = await this.parseDependencies(manifestPath, 'npm');
|
|
1799
|
-
|
|
1800
|
-
for (const dep of dependencies) {
|
|
1801
|
-
// Check for vulnerabilities via OSV API
|
|
1802
|
-
const vulns = await this.checkDependencyVulnerabilities(dep, dep.ecosystem);
|
|
1803
|
-
vulnerabilities.push(...vulns);
|
|
1804
|
-
|
|
1805
|
-
// Check for outdated packages
|
|
1806
|
-
const outdated = await this.checkOutdated(dep);
|
|
1807
|
-
if (outdated) {
|
|
1808
|
-
outdatedPackages.push(outdated);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
const scanDurationMs = Date.now() - startTime;
|
|
1813
|
-
const baseSummary = this.createDependencySummary(vulnerabilities, dependencies.length);
|
|
1814
|
-
|
|
1815
|
-
return {
|
|
1816
|
-
vulnerabilities,
|
|
1817
|
-
outdatedPackages,
|
|
1818
|
-
summary: {
|
|
1819
|
-
...baseSummary,
|
|
1820
|
-
scanDurationMs,
|
|
1821
|
-
},
|
|
1822
|
-
};
|
|
1823
|
-
} catch (error) {
|
|
1824
|
-
console.error('Dependency scan failed:', error);
|
|
1825
|
-
return {
|
|
1826
|
-
vulnerabilities: [],
|
|
1827
|
-
outdatedPackages: [],
|
|
1828
|
-
summary: {
|
|
1829
|
-
critical: 0,
|
|
1830
|
-
high: 0,
|
|
1831
|
-
medium: 0,
|
|
1832
|
-
low: 0,
|
|
1833
|
-
informational: 0,
|
|
1834
|
-
totalFiles: 0,
|
|
1835
|
-
scanDurationMs: Date.now() - startTime,
|
|
1836
|
-
},
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
}
|
|
1840
|
-
|
|
1841
|
-
/**
|
|
1842
|
-
* Perform secret scan on source files
|
|
1843
|
-
*/
|
|
1844
|
-
private async performSecretScan(): Promise<SecretScanResult> {
|
|
1845
|
-
const secretsFound: DetectedSecret[] = [];
|
|
1846
|
-
let filesScanned = 0;
|
|
1847
|
-
|
|
1848
|
-
try {
|
|
1849
|
-
// Get list of source files to scan
|
|
1850
|
-
const sourceFiles = await this.findSourceFiles(process.cwd());
|
|
1851
|
-
|
|
1852
|
-
for (const filePath of sourceFiles) {
|
|
1853
|
-
if (this.shouldExclude(filePath)) {
|
|
1854
|
-
continue;
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
filesScanned++;
|
|
1858
|
-
const filePathObj = { value: filePath } as FilePath;
|
|
1859
|
-
const secrets = await this.scanFileForSecrets(filePathObj);
|
|
1860
|
-
secretsFound.push(...secrets);
|
|
1861
|
-
}
|
|
1862
|
-
} catch (error) {
|
|
1863
|
-
console.error('Secret scan failed:', error);
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
return {
|
|
1867
|
-
secretsFound,
|
|
1868
|
-
filesScanned,
|
|
1869
|
-
};
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
/**
|
|
1873
|
-
* Find source files in a directory (recursively)
|
|
1874
|
-
*/
|
|
1875
|
-
private async findSourceFiles(dir: string, files: string[] = []): Promise<string[]> {
|
|
1876
|
-
const sourceExtensions = ['.ts', '.js', '.tsx', '.jsx', '.json', '.env', '.yaml', '.yml', '.config'];
|
|
1877
|
-
|
|
1878
|
-
try {
|
|
1879
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
1880
|
-
|
|
1881
|
-
for (const entry of entries) {
|
|
1882
|
-
const fullPath = path.join(dir, entry.name);
|
|
1883
|
-
|
|
1884
|
-
// Skip excluded directories
|
|
1885
|
-
if (this.shouldExclude(fullPath)) {
|
|
1886
|
-
continue;
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
if (entry.isDirectory()) {
|
|
1890
|
-
await this.findSourceFiles(fullPath, files);
|
|
1891
|
-
} else if (entry.isFile()) {
|
|
1892
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
1893
|
-
if (sourceExtensions.includes(ext) || entry.name.startsWith('.env')) {
|
|
1894
|
-
files.push(fullPath);
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
} catch (error) {
|
|
1899
|
-
// Silently skip directories we can't read
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
return files;
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
private calculateOverallRisk(
|
|
1906
|
-
sast?: SASTResult,
|
|
1907
|
-
dast?: DASTResult,
|
|
1908
|
-
deps?: DependencyScanResult,
|
|
1909
|
-
secrets?: SecretScanResult
|
|
1910
|
-
): RiskScore {
|
|
1911
|
-
let riskValue = 0;
|
|
1912
|
-
let weights = 0;
|
|
1913
|
-
|
|
1914
|
-
if (sast) {
|
|
1915
|
-
riskValue +=
|
|
1916
|
-
this.calculateScanRisk(sast.summary) * 0.35;
|
|
1917
|
-
weights += 0.35;
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
if (dast) {
|
|
1921
|
-
riskValue +=
|
|
1922
|
-
this.calculateScanRisk(dast.summary) * 0.25;
|
|
1923
|
-
weights += 0.25;
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
if (deps) {
|
|
1927
|
-
riskValue +=
|
|
1928
|
-
this.calculateScanRisk(deps.summary) * 0.25;
|
|
1929
|
-
weights += 0.25;
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
|
-
if (secrets) {
|
|
1933
|
-
const secretRisk = secrets.secretsFound.length > 0 ? 0.9 : 0.1;
|
|
1934
|
-
riskValue += secretRisk * 0.15;
|
|
1935
|
-
weights += 0.15;
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
const normalizedRisk = weights > 0 ? riskValue / weights : 0;
|
|
1939
|
-
|
|
1940
|
-
// Return RiskScore-compatible object
|
|
1941
|
-
return {
|
|
1942
|
-
value: Math.min(1, normalizedRisk),
|
|
1943
|
-
percentage: normalizedRisk * 100,
|
|
1944
|
-
level: normalizedRisk >= 0.8 ? 'critical' :
|
|
1945
|
-
normalizedRisk >= 0.6 ? 'high' :
|
|
1946
|
-
normalizedRisk >= 0.3 ? 'medium' : 'low',
|
|
1947
|
-
} as unknown as RiskScore;
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
private calculateScanRisk(summary: ScanSummary): number {
|
|
1951
|
-
const weights = {
|
|
1952
|
-
critical: 1.0,
|
|
1953
|
-
high: 0.7,
|
|
1954
|
-
medium: 0.4,
|
|
1955
|
-
low: 0.1,
|
|
1956
|
-
informational: 0.02,
|
|
1957
|
-
};
|
|
1958
|
-
|
|
1959
|
-
const totalIssues =
|
|
1960
|
-
summary.critical +
|
|
1961
|
-
summary.high +
|
|
1962
|
-
summary.medium +
|
|
1963
|
-
summary.low +
|
|
1964
|
-
summary.informational;
|
|
1965
|
-
|
|
1966
|
-
if (totalIssues === 0) return 0;
|
|
1967
|
-
|
|
1968
|
-
const weightedSum =
|
|
1969
|
-
summary.critical * weights.critical +
|
|
1970
|
-
summary.high * weights.high +
|
|
1971
|
-
summary.medium * weights.medium +
|
|
1972
|
-
summary.low * weights.low +
|
|
1973
|
-
summary.informational * weights.informational;
|
|
1974
|
-
|
|
1975
|
-
return Math.min(1, weightedSum / 10);
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
private generateRecommendations(
|
|
1979
|
-
sast?: SASTResult,
|
|
1980
|
-
dast?: DASTResult,
|
|
1981
|
-
deps?: DependencyScanResult,
|
|
1982
|
-
secrets?: SecretScanResult
|
|
1983
|
-
): string[] {
|
|
1984
|
-
const recommendations: string[] = [];
|
|
1985
|
-
|
|
1986
|
-
if (sast && sast.summary.critical > 0) {
|
|
1987
|
-
recommendations.push(
|
|
1988
|
-
`Address ${sast.summary.critical} critical vulnerabilities found in static analysis`
|
|
1989
|
-
);
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
if (dast && dast.summary.high > 0) {
|
|
1993
|
-
recommendations.push(
|
|
1994
|
-
`Fix ${dast.summary.high} high-severity issues found in dynamic testing`
|
|
1995
|
-
);
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
if (deps && deps.outdatedPackages.length > 5) {
|
|
1999
|
-
recommendations.push(
|
|
2000
|
-
`Update ${deps.outdatedPackages.length} outdated dependencies`
|
|
2001
|
-
);
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
if (secrets && secrets.secretsFound.length > 0) {
|
|
2005
|
-
recommendations.push(
|
|
2006
|
-
`Remove ${secrets.secretsFound.length} exposed secrets and rotate credentials`
|
|
2007
|
-
);
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
if (recommendations.length === 0) {
|
|
2011
|
-
recommendations.push('Security posture is good. Continue regular scanning.');
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
return recommendations;
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
private async getRecentAudits(): Promise<SecurityAuditReport[]> {
|
|
2018
|
-
// Query recent audits from memory storage
|
|
2019
|
-
const keys = await this.memory.search('security:audit:*', 10);
|
|
2020
|
-
const audits: SecurityAuditReport[] = [];
|
|
2021
|
-
|
|
2022
|
-
for (const key of keys) {
|
|
2023
|
-
const audit = await this.memory.get<SecurityAuditReport>(key);
|
|
2024
|
-
if (audit) {
|
|
2025
|
-
audits.push(audit);
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
|
|
2029
|
-
return audits.sort(
|
|
2030
|
-
(a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
2031
|
-
);
|
|
2032
|
-
}
|
|
2033
|
-
|
|
2034
|
-
private countBySeverity(
|
|
2035
|
-
audit: SecurityAuditReport | undefined,
|
|
2036
|
-
severity: 'critical' | 'high'
|
|
2037
|
-
): number {
|
|
2038
|
-
if (!audit) return 0;
|
|
2039
|
-
|
|
2040
|
-
let count = 0;
|
|
2041
|
-
if (audit.sastResults) count += audit.sastResults.summary[severity];
|
|
2042
|
-
if (audit.dastResults) count += audit.dastResults.summary[severity];
|
|
2043
|
-
if (audit.dependencyResults) count += audit.dependencyResults.summary[severity];
|
|
2044
|
-
|
|
2045
|
-
return count;
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
private countOpenVulnerabilities(audit: SecurityAuditReport | undefined): number {
|
|
2049
|
-
if (!audit) return 0;
|
|
2050
|
-
|
|
2051
|
-
let count = 0;
|
|
2052
|
-
if (audit.sastResults) count += audit.sastResults.vulnerabilities.length;
|
|
2053
|
-
if (audit.dastResults) count += audit.dastResults.vulnerabilities.length;
|
|
2054
|
-
if (audit.dependencyResults) count += audit.dependencyResults.vulnerabilities.length;
|
|
2055
|
-
|
|
2056
|
-
return count;
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
private calculateTrend(
|
|
2060
|
-
latest: SecurityAuditReport | undefined,
|
|
2061
|
-
previous: SecurityAuditReport | undefined
|
|
2062
|
-
): 'improving' | 'stable' | 'declining' {
|
|
2063
|
-
if (!latest || !previous) return 'stable';
|
|
2064
|
-
|
|
2065
|
-
const latestScore = this.countOpenVulnerabilities(latest);
|
|
2066
|
-
const previousScore = this.countOpenVulnerabilities(previous);
|
|
2067
|
-
|
|
2068
|
-
if (latestScore < previousScore * 0.9) return 'improving';
|
|
2069
|
-
if (latestScore > previousScore * 1.1) return 'declining';
|
|
2070
|
-
return 'stable';
|
|
2071
|
-
}
|
|
2072
|
-
|
|
2073
|
-
private calculatePostureScore(
|
|
2074
|
-
critical: number,
|
|
2075
|
-
high: number,
|
|
2076
|
-
open: number
|
|
2077
|
-
): number {
|
|
2078
|
-
// Start at 100, deduct points for issues
|
|
2079
|
-
let score = 100;
|
|
2080
|
-
score -= critical * 20;
|
|
2081
|
-
score -= high * 10;
|
|
2082
|
-
score -= open * 2;
|
|
2083
|
-
return Math.max(0, Math.min(100, score));
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
private generatePostureRecommendations(
|
|
2087
|
-
critical: number,
|
|
2088
|
-
high: number,
|
|
2089
|
-
score: number
|
|
2090
|
-
): string[] {
|
|
2091
|
-
const recommendations: string[] = [];
|
|
2092
|
-
|
|
2093
|
-
if (critical > 0) {
|
|
2094
|
-
recommendations.push('Immediately address all critical vulnerabilities');
|
|
2095
|
-
}
|
|
2096
|
-
|
|
2097
|
-
if (high > 3) {
|
|
2098
|
-
recommendations.push('Prioritize fixing high-severity issues');
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
if (score < 50) {
|
|
2102
|
-
recommendations.push('Consider a comprehensive security review');
|
|
2103
|
-
recommendations.push('Implement automated security scanning in CI/CD');
|
|
2104
|
-
}
|
|
2105
|
-
|
|
2106
|
-
if (score >= 80) {
|
|
2107
|
-
recommendations.push('Maintain current security practices');
|
|
2108
|
-
recommendations.push('Consider penetration testing for deeper analysis');
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
return recommendations;
|
|
2112
|
-
}
|
|
2113
|
-
|
|
2114
|
-
/**
|
|
2115
|
-
* Count vulnerabilities resolved in the last week
|
|
2116
|
-
* Calculates by comparing consecutive audit reports
|
|
2117
|
-
*/
|
|
2118
|
-
private async countResolvedLastWeek(): Promise<number> {
|
|
2119
|
-
try {
|
|
2120
|
-
const audits = await this.getRecentAudits();
|
|
2121
|
-
if (audits.length < 2) return 0;
|
|
2122
|
-
|
|
2123
|
-
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
2124
|
-
|
|
2125
|
-
// Find audits from this week
|
|
2126
|
-
const recentAudits = audits.filter(
|
|
2127
|
-
(audit) => new Date(audit.timestamp) >= oneWeekAgo
|
|
2128
|
-
);
|
|
2129
|
-
|
|
2130
|
-
if (recentAudits.length < 2) return 0;
|
|
2131
|
-
|
|
2132
|
-
// Compare vulnerability counts between first and last audit of the week
|
|
2133
|
-
const oldest = recentAudits[recentAudits.length - 1];
|
|
2134
|
-
const newest = recentAudits[0];
|
|
2135
|
-
|
|
2136
|
-
const oldCount = this.countOpenVulnerabilities(oldest);
|
|
2137
|
-
const newCount = this.countOpenVulnerabilities(newest);
|
|
2138
|
-
|
|
2139
|
-
// Resolved = old count - new count (if positive)
|
|
2140
|
-
return Math.max(0, oldCount - newCount);
|
|
2141
|
-
} catch (error) {
|
|
2142
|
-
console.error('Failed to count resolved vulnerabilities:', error);
|
|
2143
|
-
return 0;
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
/**
|
|
2148
|
-
* Calculate average resolution time from historical audit data
|
|
2149
|
-
*/
|
|
2150
|
-
private async calculateAverageResolutionTime(): Promise<number> {
|
|
2151
|
-
try {
|
|
2152
|
-
const audits = await this.getRecentAudits();
|
|
2153
|
-
if (audits.length < 2) return 72; // Default to 72 hours
|
|
2154
|
-
|
|
2155
|
-
// Track vulnerabilities across audits to calculate resolution time
|
|
2156
|
-
const vulnFirstSeen = new Map<string, Date>();
|
|
2157
|
-
const resolutionTimes: number[] = [];
|
|
2158
|
-
|
|
2159
|
-
// Process audits from oldest to newest
|
|
2160
|
-
const sortedAudits = [...audits].reverse();
|
|
2161
|
-
|
|
2162
|
-
for (const audit of sortedAudits) {
|
|
2163
|
-
const currentVulnIds = new Set<string>();
|
|
2164
|
-
|
|
2165
|
-
// Collect all vulnerability IDs in this audit
|
|
2166
|
-
if (audit.sastResults) {
|
|
2167
|
-
audit.sastResults.vulnerabilities.forEach((v) => currentVulnIds.add(v.id));
|
|
2168
|
-
}
|
|
2169
|
-
if (audit.dastResults) {
|
|
2170
|
-
audit.dastResults.vulnerabilities.forEach((v) => currentVulnIds.add(v.id));
|
|
2171
|
-
}
|
|
2172
|
-
if (audit.dependencyResults) {
|
|
2173
|
-
audit.dependencyResults.vulnerabilities.forEach((v) => currentVulnIds.add(v.id));
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
// Track new vulnerabilities
|
|
2177
|
-
for (const vulnId of currentVulnIds) {
|
|
2178
|
-
if (!vulnFirstSeen.has(vulnId)) {
|
|
2179
|
-
vulnFirstSeen.set(vulnId, audit.timestamp);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
// Check for resolved vulnerabilities
|
|
2184
|
-
for (const [vulnId, firstSeen] of vulnFirstSeen) {
|
|
2185
|
-
if (!currentVulnIds.has(vulnId)) {
|
|
2186
|
-
const resolutionTime = (audit.timestamp.getTime() - firstSeen.getTime()) / (1000 * 60 * 60);
|
|
2187
|
-
resolutionTimes.push(resolutionTime);
|
|
2188
|
-
vulnFirstSeen.delete(vulnId);
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
if (resolutionTimes.length === 0) return 72; // Default
|
|
2194
|
-
|
|
2195
|
-
// Calculate average
|
|
2196
|
-
const sum = resolutionTimes.reduce((acc, time) => acc + time, 0);
|
|
2197
|
-
return Math.round(sum / resolutionTimes.length);
|
|
2198
|
-
} catch (error) {
|
|
2199
|
-
console.error('Failed to calculate average resolution time:', error);
|
|
2200
|
-
return 72; // Default to 72 hours
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
|
-
private determinePriorityBucket(
|
|
2205
|
-
vuln: Vulnerability
|
|
2206
|
-
): keyof TriagedVulnerabilities {
|
|
2207
|
-
const effort = vuln.remediation.estimatedEffort;
|
|
2208
|
-
|
|
2209
|
-
if (vuln.severity === 'critical') {
|
|
2210
|
-
return 'immediate';
|
|
2211
|
-
}
|
|
2212
|
-
|
|
2213
|
-
if (vuln.severity === 'high') {
|
|
2214
|
-
return effort === 'trivial' || effort === 'minor' ? 'immediate' : 'shortTerm';
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
if (vuln.severity === 'medium') {
|
|
2218
|
-
return effort === 'major' ? 'longTerm' : 'mediumTerm';
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
if (vuln.severity === 'low' || vuln.severity === 'informational') {
|
|
2222
|
-
return 'longTerm';
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
return 'accepted';
|
|
2226
|
-
}
|
|
2227
|
-
}
|