circle-ir-ai 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/LICENSE +15 -0
- package/README.md +336 -0
- package/dist/action-queue/aggregator.d.ts +40 -0
- package/dist/action-queue/aggregator.d.ts.map +1 -0
- package/dist/action-queue/aggregator.js +375 -0
- package/dist/action-queue/aggregator.js.map +1 -0
- package/dist/action-queue/index.d.ts +14 -0
- package/dist/action-queue/index.d.ts.map +1 -0
- package/dist/action-queue/index.js +17 -0
- package/dist/action-queue/index.js.map +1 -0
- package/dist/action-queue/queue.d.ts +74 -0
- package/dist/action-queue/queue.d.ts.map +1 -0
- package/dist/action-queue/queue.js +433 -0
- package/dist/action-queue/queue.js.map +1 -0
- package/dist/action-queue/types.d.ts +162 -0
- package/dist/action-queue/types.d.ts.map +1 -0
- package/dist/action-queue/types.js +44 -0
- package/dist/action-queue/types.js.map +1 -0
- package/dist/agents/enrichment-agent.d.ts +16 -0
- package/dist/agents/enrichment-agent.d.ts.map +1 -0
- package/dist/agents/enrichment-agent.js +102 -0
- package/dist/agents/enrichment-agent.js.map +1 -0
- package/dist/agents/index.d.ts +12 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +15 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/mastra/agents.d.ts +373 -0
- package/dist/agents/mastra/agents.d.ts.map +1 -0
- package/dist/agents/mastra/agents.js +347 -0
- package/dist/agents/mastra/agents.js.map +1 -0
- package/dist/agents/mastra/index.d.ts +12 -0
- package/dist/agents/mastra/index.d.ts.map +1 -0
- package/dist/agents/mastra/index.js +17 -0
- package/dist/agents/mastra/index.js.map +1 -0
- package/dist/agents/mastra/instance.d.ts +383 -0
- package/dist/agents/mastra/instance.d.ts.map +1 -0
- package/dist/agents/mastra/instance.js +37 -0
- package/dist/agents/mastra/instance.js.map +1 -0
- package/dist/agents/mastra/steps.d.ts +300 -0
- package/dist/agents/mastra/steps.d.ts.map +1 -0
- package/dist/agents/mastra/steps.js +468 -0
- package/dist/agents/mastra/steps.js.map +1 -0
- package/dist/agents/mastra/swarm.d.ts +106 -0
- package/dist/agents/mastra/swarm.d.ts.map +1 -0
- package/dist/agents/mastra/swarm.js +501 -0
- package/dist/agents/mastra/swarm.js.map +1 -0
- package/dist/agents/mastra/workflow.d.ts +81 -0
- package/dist/agents/mastra/workflow.d.ts.map +1 -0
- package/dist/agents/mastra/workflow.js +460 -0
- package/dist/agents/mastra/workflow.js.map +1 -0
- package/dist/agents/multi/agents/security.d.ts +29 -0
- package/dist/agents/multi/agents/security.d.ts.map +1 -0
- package/dist/agents/multi/agents/security.js +830 -0
- package/dist/agents/multi/agents/security.js.map +1 -0
- package/dist/agents/multi/extractor.d.ts +21 -0
- package/dist/agents/multi/extractor.d.ts.map +1 -0
- package/dist/agents/multi/extractor.js +483 -0
- package/dist/agents/multi/extractor.js.map +1 -0
- package/dist/agents/multi/index.d.ts +32 -0
- package/dist/agents/multi/index.d.ts.map +1 -0
- package/dist/agents/multi/index.js +34 -0
- package/dist/agents/multi/index.js.map +1 -0
- package/dist/agents/multi/runner.d.ts +79 -0
- package/dist/agents/multi/runner.d.ts.map +1 -0
- package/dist/agents/multi/runner.js +323 -0
- package/dist/agents/multi/runner.js.map +1 -0
- package/dist/agents/security-agent.d.ts +16 -0
- package/dist/agents/security-agent.d.ts.map +1 -0
- package/dist/agents/security-agent.js +299 -0
- package/dist/agents/security-agent.js.map +1 -0
- package/dist/agents/types.d.ts +373 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +14 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/agents/verification-agent.d.ts +23 -0
- package/dist/agents/verification-agent.d.ts.map +1 -0
- package/dist/agents/verification-agent.js +217 -0
- package/dist/agents/verification-agent.js.map +1 -0
- package/dist/agents/workflow.d.ts +30 -0
- package/dist/agents/workflow.d.ts.map +1 -0
- package/dist/agents/workflow.js +79 -0
- package/dist/agents/workflow.js.map +1 -0
- package/dist/analysis/enriched.d.ts +16 -0
- package/dist/analysis/enriched.d.ts.map +1 -0
- package/dist/analysis/enriched.js +297 -0
- package/dist/analysis/enriched.js.map +1 -0
- package/dist/analysis/llm-correlated-predicates.d.ts +80 -0
- package/dist/analysis/llm-correlated-predicates.d.ts.map +1 -0
- package/dist/analysis/llm-correlated-predicates.js +255 -0
- package/dist/analysis/llm-correlated-predicates.js.map +1 -0
- package/dist/analysis/llm-cross-file-taint.d.ts +86 -0
- package/dist/analysis/llm-cross-file-taint.d.ts.map +1 -0
- package/dist/analysis/llm-cross-file-taint.js +264 -0
- package/dist/analysis/llm-cross-file-taint.js.map +1 -0
- package/dist/analysis/pattern-discovery.d.ts +79 -0
- package/dist/analysis/pattern-discovery.d.ts.map +1 -0
- package/dist/analysis/pattern-discovery.js +447 -0
- package/dist/analysis/pattern-discovery.js.map +1 -0
- package/dist/cache/file-cache.d.ts +89 -0
- package/dist/cache/file-cache.d.ts.map +1 -0
- package/dist/cache/file-cache.js +208 -0
- package/dist/cache/file-cache.js.map +1 -0
- package/dist/cache/index.d.ts +6 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cli/args.d.ts +52 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +422 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/colors.d.ts +31 -0
- package/dist/cli/colors.d.ts.map +1 -0
- package/dist/cli/colors.js +80 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/commands/analyze-skill.d.ts +33 -0
- package/dist/cli/commands/analyze-skill.d.ts.map +1 -0
- package/dist/cli/commands/analyze-skill.js +217 -0
- package/dist/cli/commands/analyze-skill.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +18 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +30 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/benchmark-runner.d.ts +42 -0
- package/dist/cli/commands/benchmark-runner.d.ts.map +1 -0
- package/dist/cli/commands/benchmark-runner.js +18 -0
- package/dist/cli/commands/benchmark-runner.js.map +1 -0
- package/dist/cli/commands/benchmark.d.ts +11 -0
- package/dist/cli/commands/benchmark.d.ts.map +1 -0
- package/dist/cli/commands/benchmark.js +90 -0
- package/dist/cli/commands/benchmark.js.map +1 -0
- package/dist/cli/commands/dead-code.d.ts +11 -0
- package/dist/cli/commands/dead-code.d.ts.map +1 -0
- package/dist/cli/commands/dead-code.js +65 -0
- package/dist/cli/commands/dead-code.js.map +1 -0
- package/dist/cli/commands/generate-spec.d.ts +11 -0
- package/dist/cli/commands/generate-spec.d.ts.map +1 -0
- package/dist/cli/commands/generate-spec.js +67 -0
- package/dist/cli/commands/generate-spec.js.map +1 -0
- package/dist/cli/commands/health.d.ts +11 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +67 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/project.d.ts +21 -0
- package/dist/cli/commands/project.d.ts.map +1 -0
- package/dist/cli/commands/project.js +92 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/commands/scan.d.ts +11 -0
- package/dist/cli/commands/scan.d.ts.map +1 -0
- package/dist/cli/commands/scan.js +68 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/commands/secrets.d.ts +11 -0
- package/dist/cli/commands/secrets.d.ts.map +1 -0
- package/dist/cli/commands/secrets.js +71 -0
- package/dist/cli/commands/secrets.js.map +1 -0
- package/dist/cli/commands/swarm.d.ts +20 -0
- package/dist/cli/commands/swarm.d.ts.map +1 -0
- package/dist/cli/commands/swarm.js +174 -0
- package/dist/cli/commands/swarm.js.map +1 -0
- package/dist/cli/config.d.ts +103 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +307 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/discovery.d.ts +31 -0
- package/dist/cli/discovery.d.ts.map +1 -0
- package/dist/cli/discovery.js +212 -0
- package/dist/cli/discovery.js.map +1 -0
- package/dist/cli/formatters/index.d.ts +15 -0
- package/dist/cli/formatters/index.d.ts.map +1 -0
- package/dist/cli/formatters/index.js +51 -0
- package/dist/cli/formatters/index.js.map +1 -0
- package/dist/cli/formatters/json.d.ts +11 -0
- package/dist/cli/formatters/json.d.ts.map +1 -0
- package/dist/cli/formatters/json.js +12 -0
- package/dist/cli/formatters/json.js.map +1 -0
- package/dist/cli/formatters/project-json.d.ts +11 -0
- package/dist/cli/formatters/project-json.d.ts.map +1 -0
- package/dist/cli/formatters/project-json.js +12 -0
- package/dist/cli/formatters/project-json.js.map +1 -0
- package/dist/cli/formatters/project-sarif.d.ts +11 -0
- package/dist/cli/formatters/project-sarif.d.ts.map +1 -0
- package/dist/cli/formatters/project-sarif.js +127 -0
- package/dist/cli/formatters/project-sarif.js.map +1 -0
- package/dist/cli/formatters/project-summary.d.ts +11 -0
- package/dist/cli/formatters/project-summary.d.ts.map +1 -0
- package/dist/cli/formatters/project-summary.js +202 -0
- package/dist/cli/formatters/project-summary.js.map +1 -0
- package/dist/cli/formatters/sarif-shared.d.ts +101 -0
- package/dist/cli/formatters/sarif-shared.d.ts.map +1 -0
- package/dist/cli/formatters/sarif-shared.js +57 -0
- package/dist/cli/formatters/sarif-shared.js.map +1 -0
- package/dist/cli/formatters/sarif.d.ts +12 -0
- package/dist/cli/formatters/sarif.d.ts.map +1 -0
- package/dist/cli/formatters/sarif.js +92 -0
- package/dist/cli/formatters/sarif.js.map +1 -0
- package/dist/cli/formatters/summary.d.ts +11 -0
- package/dist/cli/formatters/summary.d.ts.map +1 -0
- package/dist/cli/formatters/summary.js +240 -0
- package/dist/cli/formatters/summary.js.map +1 -0
- package/dist/cli/formatters/two-phase-summary.d.ts +11 -0
- package/dist/cli/formatters/two-phase-summary.d.ts.map +1 -0
- package/dist/cli/formatters/two-phase-summary.js +188 -0
- package/dist/cli/formatters/two-phase-summary.js.map +1 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +555 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/components/clustering.d.ts +60 -0
- package/dist/components/clustering.d.ts.map +1 -0
- package/dist/components/clustering.js +129 -0
- package/dist/components/clustering.js.map +1 -0
- package/dist/components/enrichment.d.ts +45 -0
- package/dist/components/enrichment.d.ts.map +1 -0
- package/dist/components/enrichment.js +193 -0
- package/dist/components/enrichment.js.map +1 -0
- package/dist/components/index.d.ts +29 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +56 -0
- package/dist/components/index.js.map +1 -0
- package/dist/dead-code/detector.d.ts +200 -0
- package/dist/dead-code/detector.d.ts.map +1 -0
- package/dist/dead-code/detector.js +1003 -0
- package/dist/dead-code/detector.js.map +1 -0
- package/dist/dead-code/index.d.ts +7 -0
- package/dist/dead-code/index.d.ts.map +1 -0
- package/dist/dead-code/index.js +7 -0
- package/dist/dead-code/index.js.map +1 -0
- package/dist/extractors/index.d.ts +15 -0
- package/dist/extractors/index.d.ts.map +1 -0
- package/dist/extractors/index.js +14 -0
- package/dist/extractors/index.js.map +1 -0
- package/dist/extractors/natural-language.d.ts +46 -0
- package/dist/extractors/natural-language.d.ts.map +1 -0
- package/dist/extractors/natural-language.js +228 -0
- package/dist/extractors/natural-language.js.map +1 -0
- package/dist/extractors/tree-sitter.d.ts +33 -0
- package/dist/extractors/tree-sitter.d.ts.map +1 -0
- package/dist/extractors/tree-sitter.js +69 -0
- package/dist/extractors/tree-sitter.js.map +1 -0
- package/dist/extractors/types.d.ts +62 -0
- package/dist/extractors/types.d.ts.map +1 -0
- package/dist/extractors/types.js +54 -0
- package/dist/extractors/types.js.map +1 -0
- package/dist/health-score/calculator.d.ts +123 -0
- package/dist/health-score/calculator.d.ts.map +1 -0
- package/dist/health-score/calculator.js +444 -0
- package/dist/health-score/calculator.js.map +1 -0
- package/dist/health-score/index.d.ts +12 -0
- package/dist/health-score/index.d.ts.map +1 -0
- package/dist/health-score/index.js +14 -0
- package/dist/health-score/index.js.map +1 -0
- package/dist/health-score/metrics.d.ts +142 -0
- package/dist/health-score/metrics.d.ts.map +1 -0
- package/dist/health-score/metrics.js +332 -0
- package/dist/health-score/metrics.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/ax-client.d.ts +477 -0
- package/dist/llm/ax-client.d.ts.map +1 -0
- package/dist/llm/ax-client.js +1641 -0
- package/dist/llm/ax-client.js.map +1 -0
- package/dist/llm/config.d.ts +58 -0
- package/dist/llm/config.d.ts.map +1 -0
- package/dist/llm/config.js +97 -0
- package/dist/llm/config.js.map +1 -0
- package/dist/llm/discovery.d.ts +123 -0
- package/dist/llm/discovery.d.ts.map +1 -0
- package/dist/llm/discovery.js +505 -0
- package/dist/llm/discovery.js.map +1 -0
- package/dist/llm/enrichment.d.ts +108 -0
- package/dist/llm/enrichment.d.ts.map +1 -0
- package/dist/llm/enrichment.js +312 -0
- package/dist/llm/enrichment.js.map +1 -0
- package/dist/llm/index.d.ts +13 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +22 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/language-context.d.ts +64 -0
- package/dist/llm/language-context.d.ts.map +1 -0
- package/dist/llm/language-context.js +492 -0
- package/dist/llm/language-context.js.map +1 -0
- package/dist/llm/pattern-verification.d.ts +39 -0
- package/dist/llm/pattern-verification.d.ts.map +1 -0
- package/dist/llm/pattern-verification.js +127 -0
- package/dist/llm/pattern-verification.js.map +1 -0
- package/dist/llm/prompt-security.d.ts +120 -0
- package/dist/llm/prompt-security.d.ts.map +1 -0
- package/dist/llm/prompt-security.js +301 -0
- package/dist/llm/prompt-security.js.map +1 -0
- package/dist/llm/prompts/index.d.ts +31 -0
- package/dist/llm/prompts/index.d.ts.map +1 -0
- package/dist/llm/prompts/index.js +92 -0
- package/dist/llm/prompts/index.js.map +1 -0
- package/dist/llm/prompts/rust.d.ts +30 -0
- package/dist/llm/prompts/rust.d.ts.map +1 -0
- package/dist/llm/prompts/rust.js +121 -0
- package/dist/llm/prompts/rust.js.map +1 -0
- package/dist/llm/schemas.d.ts +892 -0
- package/dist/llm/schemas.d.ts.map +1 -0
- package/dist/llm/schemas.js +258 -0
- package/dist/llm/schemas.js.map +1 -0
- package/dist/llm/verification.d.ts +127 -0
- package/dist/llm/verification.d.ts.map +1 -0
- package/dist/llm/verification.js +394 -0
- package/dist/llm/verification.js.map +1 -0
- package/dist/project/analyzer.d.ts +30 -0
- package/dist/project/analyzer.d.ts.map +1 -0
- package/dist/project/analyzer.js +358 -0
- package/dist/project/analyzer.js.map +1 -0
- package/dist/project/call-graph.d.ts +22 -0
- package/dist/project/call-graph.d.ts.map +1 -0
- package/dist/project/call-graph.js +246 -0
- package/dist/project/call-graph.js.map +1 -0
- package/dist/project/index.d.ts +18 -0
- package/dist/project/index.d.ts.map +1 -0
- package/dist/project/index.js +20 -0
- package/dist/project/index.js.map +1 -0
- package/dist/project/taint-paths.d.ts +22 -0
- package/dist/project/taint-paths.d.ts.map +1 -0
- package/dist/project/taint-paths.js +265 -0
- package/dist/project/taint-paths.js.map +1 -0
- package/dist/project/two-phase-analyzer.d.ts +143 -0
- package/dist/project/two-phase-analyzer.d.ts.map +1 -0
- package/dist/project/two-phase-analyzer.js +646 -0
- package/dist/project/two-phase-analyzer.js.map +1 -0
- package/dist/project/type-hierarchy.d.ts +28 -0
- package/dist/project/type-hierarchy.d.ts.map +1 -0
- package/dist/project/type-hierarchy.js +218 -0
- package/dist/project/type-hierarchy.js.map +1 -0
- package/dist/secret-scan/index.d.ts +12 -0
- package/dist/secret-scan/index.d.ts.map +1 -0
- package/dist/secret-scan/index.js +14 -0
- package/dist/secret-scan/index.js.map +1 -0
- package/dist/secret-scan/patterns.d.ts +38 -0
- package/dist/secret-scan/patterns.d.ts.map +1 -0
- package/dist/secret-scan/patterns.js +473 -0
- package/dist/secret-scan/patterns.js.map +1 -0
- package/dist/secret-scan/scanner.d.ts +162 -0
- package/dist/secret-scan/scanner.d.ts.map +1 -0
- package/dist/secret-scan/scanner.js +511 -0
- package/dist/secret-scan/scanner.js.map +1 -0
- package/dist/security-scan/index.d.ts +12 -0
- package/dist/security-scan/index.d.ts.map +1 -0
- package/dist/security-scan/index.js +15 -0
- package/dist/security-scan/index.js.map +1 -0
- package/dist/security-scan/owasp-mapping.d.ts +29 -0
- package/dist/security-scan/owasp-mapping.d.ts.map +1 -0
- package/dist/security-scan/owasp-mapping.js +246 -0
- package/dist/security-scan/owasp-mapping.js.map +1 -0
- package/dist/security-scan/scanner.d.ts +204 -0
- package/dist/security-scan/scanner.d.ts.map +1 -0
- package/dist/security-scan/scanner.js +693 -0
- package/dist/security-scan/scanner.js.map +1 -0
- package/dist/security-scan/trend-tracker.d.ts +150 -0
- package/dist/security-scan/trend-tracker.d.ts.map +1 -0
- package/dist/security-scan/trend-tracker.js +299 -0
- package/dist/security-scan/trend-tracker.js.map +1 -0
- package/dist/skills/bundle-loader.d.ts +26 -0
- package/dist/skills/bundle-loader.d.ts.map +1 -0
- package/dist/skills/bundle-loader.js +284 -0
- package/dist/skills/bundle-loader.js.map +1 -0
- package/dist/skills/capability-mismatch.d.ts +21 -0
- package/dist/skills/capability-mismatch.d.ts.map +1 -0
- package/dist/skills/capability-mismatch.js +188 -0
- package/dist/skills/capability-mismatch.js.map +1 -0
- package/dist/skills/index.d.ts +10 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +9 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/skill-analyzer.d.ts +16 -0
- package/dist/skills/skill-analyzer.d.ts.map +1 -0
- package/dist/skills/skill-analyzer.js +361 -0
- package/dist/skills/skill-analyzer.js.map +1 -0
- package/dist/skills/types.d.ts +195 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +7 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/specifica/conflict-resolver.d.ts +23 -0
- package/dist/specifica/conflict-resolver.d.ts.map +1 -0
- package/dist/specifica/conflict-resolver.js +129 -0
- package/dist/specifica/conflict-resolver.js.map +1 -0
- package/dist/specifica/evidence-aggregator.d.ts +33 -0
- package/dist/specifica/evidence-aggregator.d.ts.map +1 -0
- package/dist/specifica/evidence-aggregator.js +236 -0
- package/dist/specifica/evidence-aggregator.js.map +1 -0
- package/dist/specifica/evidence-extractor.d.ts +13 -0
- package/dist/specifica/evidence-extractor.d.ts.map +1 -0
- package/dist/specifica/evidence-extractor.js +431 -0
- package/dist/specifica/evidence-extractor.js.map +1 -0
- package/dist/specifica/feature-clustering.d.ts +19 -0
- package/dist/specifica/feature-clustering.d.ts.map +1 -0
- package/dist/specifica/feature-clustering.js +231 -0
- package/dist/specifica/feature-clustering.js.map +1 -0
- package/dist/specifica/generator.d.ts +16 -0
- package/dist/specifica/generator.d.ts.map +1 -0
- package/dist/specifica/generator.js +277 -0
- package/dist/specifica/generator.js.map +1 -0
- package/dist/specifica/index.d.ts +15 -0
- package/dist/specifica/index.d.ts.map +1 -0
- package/dist/specifica/index.js +18 -0
- package/dist/specifica/index.js.map +1 -0
- package/dist/specifica/prompts.d.ts +21 -0
- package/dist/specifica/prompts.d.ts.map +1 -0
- package/dist/specifica/prompts.js +196 -0
- package/dist/specifica/prompts.js.map +1 -0
- package/dist/specifica/spec-generator.d.ts +22 -0
- package/dist/specifica/spec-generator.d.ts.map +1 -0
- package/dist/specifica/spec-generator.js +229 -0
- package/dist/specifica/spec-generator.js.map +1 -0
- package/dist/specifica/types.d.ts +213 -0
- package/dist/specifica/types.d.ts.map +1 -0
- package/dist/specifica/types.js +7 -0
- package/dist/specifica/types.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +51 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continuous Security Scanner
|
|
3
|
+
*
|
|
4
|
+
* Provides automated security scanning for repositories with:
|
|
5
|
+
* - Git clone → file walker → Circle-IR analysis pipeline
|
|
6
|
+
* - OWASP Top 10 category mapping
|
|
7
|
+
* - Summary report with severity, location, confidence
|
|
8
|
+
* - Trend tracking (store results, compare across runs)
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { execSync } from 'child_process';
|
|
13
|
+
import { initAnalyzer, isAnalyzerInitialized, analyze, generateFindings, } from 'circle-ir';
|
|
14
|
+
import { getOWASPMapping } from './owasp-mapping.js';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Scanner Implementation
|
|
17
|
+
// ============================================================================
|
|
18
|
+
export class SecurityScanner {
|
|
19
|
+
workDir;
|
|
20
|
+
verbose;
|
|
21
|
+
onProgress;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.workDir = '';
|
|
24
|
+
this.verbose = false;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Run a security scan on a repository or local path.
|
|
28
|
+
*/
|
|
29
|
+
async scan(options) {
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
this.verbose = options.verbose ?? false;
|
|
32
|
+
this.onProgress = options.onProgress;
|
|
33
|
+
// Ensure analyzer is initialized
|
|
34
|
+
if (!isAnalyzerInitialized()) {
|
|
35
|
+
await initAnalyzer();
|
|
36
|
+
}
|
|
37
|
+
// Phase 1: Clone or locate repository
|
|
38
|
+
this.reportProgress({ phase: 'clone', filesProcessed: 0, totalFiles: 0, findingsCount: 0 });
|
|
39
|
+
const repoPath = await this.prepareRepository(options);
|
|
40
|
+
// Phase 2: Discover files
|
|
41
|
+
this.reportProgress({ phase: 'discover', filesProcessed: 0, totalFiles: 0, findingsCount: 0 });
|
|
42
|
+
const files = this.discoverFiles(repoPath, options);
|
|
43
|
+
// Phase 3: Analyze files
|
|
44
|
+
this.reportProgress({ phase: 'analyze', filesProcessed: 0, totalFiles: files.length, findingsCount: 0 });
|
|
45
|
+
const fileResults = await this.analyzeFiles(files, options);
|
|
46
|
+
// Phase 4: Generate report
|
|
47
|
+
this.reportProgress({ phase: 'report', filesProcessed: files.length, totalFiles: files.length, findingsCount: 0 });
|
|
48
|
+
const result = this.generateReport(fileResults, options, startTime);
|
|
49
|
+
// Save report if output directory specified
|
|
50
|
+
if (options.outputDir) {
|
|
51
|
+
await this.saveReport(result, options.outputDir);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Prepare repository for scanning.
|
|
57
|
+
*/
|
|
58
|
+
async prepareRepository(options) {
|
|
59
|
+
const { target, branch, commit } = options;
|
|
60
|
+
// Check if target is a URL (Git clone needed)
|
|
61
|
+
if (target.startsWith('http://') || target.startsWith('https://') || target.startsWith('git@')) {
|
|
62
|
+
return this.cloneRepository(target, branch, commit);
|
|
63
|
+
}
|
|
64
|
+
// Target is a local path
|
|
65
|
+
if (!fs.existsSync(target)) {
|
|
66
|
+
throw new Error(`Target path does not exist: ${target}`);
|
|
67
|
+
}
|
|
68
|
+
return path.resolve(target);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Clone a Git repository.
|
|
72
|
+
*/
|
|
73
|
+
cloneRepository(url, branch, commit) {
|
|
74
|
+
// Create temp directory for clone
|
|
75
|
+
const tmpDir = fs.mkdtempSync(path.join('/tmp', 'circle-scan-'));
|
|
76
|
+
this.workDir = tmpDir;
|
|
77
|
+
if (this.verbose) {
|
|
78
|
+
console.log(`Cloning ${url} to ${tmpDir}...`);
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
// Clone with shallow depth for speed
|
|
82
|
+
const branchArg = branch ? `--branch ${branch}` : '';
|
|
83
|
+
execSync(`git clone --depth 1 ${branchArg} "${url}" "${tmpDir}"`, {
|
|
84
|
+
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
85
|
+
});
|
|
86
|
+
// Checkout specific commit if specified
|
|
87
|
+
if (commit) {
|
|
88
|
+
execSync(`git -C "${tmpDir}" fetch --depth 1 origin ${commit}`, {
|
|
89
|
+
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
90
|
+
});
|
|
91
|
+
execSync(`git -C "${tmpDir}" checkout ${commit}`, {
|
|
92
|
+
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return tmpDir;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Clean up on error
|
|
99
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
100
|
+
throw new Error(`Failed to clone repository: ${error}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Discover files to scan.
|
|
105
|
+
*/
|
|
106
|
+
discoverFiles(repoPath, options) {
|
|
107
|
+
const { languages, maxFiles = 1000, includePatterns, excludePatterns, } = options;
|
|
108
|
+
const files = [];
|
|
109
|
+
const defaultExcludes = [
|
|
110
|
+
'**/node_modules/**',
|
|
111
|
+
'**/vendor/**',
|
|
112
|
+
'**/target/**',
|
|
113
|
+
'**/build/**',
|
|
114
|
+
'**/dist/**',
|
|
115
|
+
'**/.git/**',
|
|
116
|
+
'**/test/**',
|
|
117
|
+
'**/tests/**',
|
|
118
|
+
'**/__tests__/**',
|
|
119
|
+
'**/*.test.*',
|
|
120
|
+
'**/*.spec.*',
|
|
121
|
+
'**/.*',
|
|
122
|
+
];
|
|
123
|
+
const excludes = [...defaultExcludes, ...(excludePatterns || [])];
|
|
124
|
+
const languageExtensions = {
|
|
125
|
+
java: ['.java'],
|
|
126
|
+
c: ['.c', '.h'],
|
|
127
|
+
cpp: ['.cpp', '.cc', '.cxx', '.hpp', '.hxx'],
|
|
128
|
+
javascript: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
129
|
+
typescript: ['.ts', '.tsx', '.mts', '.cts'],
|
|
130
|
+
python: ['.py'],
|
|
131
|
+
rust: ['.rs'],
|
|
132
|
+
};
|
|
133
|
+
// Determine which extensions to look for
|
|
134
|
+
let extensions = [];
|
|
135
|
+
if (languages && languages.length > 0) {
|
|
136
|
+
for (const lang of languages) {
|
|
137
|
+
extensions.push(...(languageExtensions[lang] || []));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Auto-detect: include all supported languages
|
|
142
|
+
extensions = Object.values(languageExtensions).flat();
|
|
143
|
+
}
|
|
144
|
+
// Walk directory tree
|
|
145
|
+
const walk = (dir, depth = 0) => {
|
|
146
|
+
if (depth > 30 || files.length >= maxFiles)
|
|
147
|
+
return;
|
|
148
|
+
try {
|
|
149
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
if (files.length >= maxFiles)
|
|
152
|
+
break;
|
|
153
|
+
const fullPath = path.join(dir, entry.name);
|
|
154
|
+
const relativePath = path.relative(repoPath, fullPath);
|
|
155
|
+
// Check excludes
|
|
156
|
+
if (this.matchesPattern(relativePath, excludes)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (entry.isDirectory()) {
|
|
160
|
+
walk(fullPath, depth + 1);
|
|
161
|
+
}
|
|
162
|
+
else if (entry.isFile()) {
|
|
163
|
+
// Check extension
|
|
164
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
165
|
+
if (!extensions.includes(ext))
|
|
166
|
+
continue;
|
|
167
|
+
// Check includes if specified
|
|
168
|
+
if (includePatterns && includePatterns.length > 0) {
|
|
169
|
+
if (!this.matchesPattern(relativePath, includePatterns)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
files.push(fullPath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Skip directories we can't read
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
walk(repoPath);
|
|
182
|
+
if (this.verbose) {
|
|
183
|
+
console.log(`Discovered ${files.length} files to scan`);
|
|
184
|
+
}
|
|
185
|
+
return files;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Check if a path matches any of the given glob patterns.
|
|
189
|
+
*/
|
|
190
|
+
matchesPattern(filePath, patterns) {
|
|
191
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
192
|
+
for (const pattern of patterns) {
|
|
193
|
+
// Simple glob matching (supports * and **)
|
|
194
|
+
const regex = pattern
|
|
195
|
+
.replace(/\*\*/g, '{{DOUBLESTAR}}')
|
|
196
|
+
.replace(/\*/g, '[^/]*')
|
|
197
|
+
.replace(/{{DOUBLESTAR}}/g, '.*')
|
|
198
|
+
.replace(/\?/g, '.');
|
|
199
|
+
if (new RegExp(`^${regex}$`).test(normalizedPath)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Analyze files.
|
|
207
|
+
*/
|
|
208
|
+
async analyzeFiles(files, options) {
|
|
209
|
+
const { parallel = true, maxConcurrency = 10, minSeverity = 'low' } = options;
|
|
210
|
+
const results = [];
|
|
211
|
+
let filesProcessed = 0;
|
|
212
|
+
let totalFindings = 0;
|
|
213
|
+
const repoPath = this.workDir || path.dirname(files[0] || '');
|
|
214
|
+
if (parallel) {
|
|
215
|
+
// Parallel analysis
|
|
216
|
+
const queue = [...files];
|
|
217
|
+
const workers = [];
|
|
218
|
+
for (let i = 0; i < Math.min(maxConcurrency, files.length); i++) {
|
|
219
|
+
workers.push((async () => {
|
|
220
|
+
while (queue.length > 0) {
|
|
221
|
+
const file = queue.shift();
|
|
222
|
+
if (!file)
|
|
223
|
+
break;
|
|
224
|
+
const result = await this.analyzeFile(file, repoPath, minSeverity);
|
|
225
|
+
results.push(result);
|
|
226
|
+
filesProcessed++;
|
|
227
|
+
totalFindings += result.findings.length;
|
|
228
|
+
this.reportProgress({
|
|
229
|
+
phase: 'analyze',
|
|
230
|
+
currentFile: result.path,
|
|
231
|
+
filesProcessed,
|
|
232
|
+
totalFiles: files.length,
|
|
233
|
+
findingsCount: totalFindings,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
})());
|
|
237
|
+
}
|
|
238
|
+
await Promise.all(workers);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// Sequential analysis
|
|
242
|
+
for (const file of files) {
|
|
243
|
+
const result = await this.analyzeFile(file, repoPath, minSeverity);
|
|
244
|
+
results.push(result);
|
|
245
|
+
filesProcessed++;
|
|
246
|
+
totalFindings += result.findings.length;
|
|
247
|
+
this.reportProgress({
|
|
248
|
+
phase: 'analyze',
|
|
249
|
+
currentFile: result.path,
|
|
250
|
+
filesProcessed,
|
|
251
|
+
totalFiles: files.length,
|
|
252
|
+
findingsCount: totalFindings,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return results;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Analyze a single file.
|
|
260
|
+
*/
|
|
261
|
+
async analyzeFile(filePath, repoPath, minSeverity) {
|
|
262
|
+
const startTime = Date.now();
|
|
263
|
+
const relativePath = path.relative(repoPath, filePath);
|
|
264
|
+
const language = this.detectLanguage(filePath);
|
|
265
|
+
const findings = [];
|
|
266
|
+
try {
|
|
267
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
268
|
+
const ir = await analyze(content, path.basename(filePath), language);
|
|
269
|
+
// Generate findings from taint analysis
|
|
270
|
+
const irFindings = generateFindings(ir.taint.sources, ir.taint.sinks, ir.dfg, path.basename(filePath));
|
|
271
|
+
// Convert findings to ScanFinding with OWASP mapping
|
|
272
|
+
for (const finding of irFindings) {
|
|
273
|
+
// Filter by severity
|
|
274
|
+
if (!this.meetsMinSeverity(finding.severity, minSeverity)) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const owasp = getOWASPMapping(finding.cwe);
|
|
278
|
+
findings.push({
|
|
279
|
+
...finding,
|
|
280
|
+
owasp,
|
|
281
|
+
filePath: relativePath,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
path: relativePath,
|
|
286
|
+
language,
|
|
287
|
+
loc: ir.meta.loc,
|
|
288
|
+
findings,
|
|
289
|
+
analysisTimeMs: Date.now() - startTime,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
if (this.verbose) {
|
|
294
|
+
console.error(`Error analyzing ${relativePath}: ${error}`);
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
path: relativePath,
|
|
298
|
+
language,
|
|
299
|
+
loc: 0,
|
|
300
|
+
findings: [],
|
|
301
|
+
analysisTimeMs: Date.now() - startTime,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Detect language from file extension.
|
|
307
|
+
*/
|
|
308
|
+
detectLanguage(filePath) {
|
|
309
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
310
|
+
switch (ext) {
|
|
311
|
+
case '.java':
|
|
312
|
+
return 'java';
|
|
313
|
+
case '.c':
|
|
314
|
+
case '.h':
|
|
315
|
+
return 'c';
|
|
316
|
+
case '.cpp':
|
|
317
|
+
case '.cc':
|
|
318
|
+
case '.cxx':
|
|
319
|
+
case '.hpp':
|
|
320
|
+
case '.hxx':
|
|
321
|
+
return 'cpp';
|
|
322
|
+
case '.js':
|
|
323
|
+
case '.jsx':
|
|
324
|
+
case '.mjs':
|
|
325
|
+
case '.cjs':
|
|
326
|
+
return 'javascript';
|
|
327
|
+
case '.ts':
|
|
328
|
+
case '.tsx':
|
|
329
|
+
case '.mts':
|
|
330
|
+
case '.cts':
|
|
331
|
+
return 'typescript';
|
|
332
|
+
case '.py':
|
|
333
|
+
return 'python';
|
|
334
|
+
case '.rs':
|
|
335
|
+
return 'rust';
|
|
336
|
+
default:
|
|
337
|
+
return 'java';
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Check if severity meets minimum threshold.
|
|
342
|
+
*/
|
|
343
|
+
meetsMinSeverity(severity, minSeverity) {
|
|
344
|
+
const severityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
345
|
+
const actualLevel = severityOrder[severity] || 0;
|
|
346
|
+
const minLevel = severityOrder[minSeverity];
|
|
347
|
+
return actualLevel >= minLevel;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Generate scan report.
|
|
351
|
+
*/
|
|
352
|
+
generateReport(fileResults, options, startTime) {
|
|
353
|
+
// Collect all findings
|
|
354
|
+
const allFindings = [];
|
|
355
|
+
for (const file of fileResults) {
|
|
356
|
+
allFindings.push(...file.findings);
|
|
357
|
+
}
|
|
358
|
+
// Group by OWASP category
|
|
359
|
+
const byOWASP = {};
|
|
360
|
+
for (const finding of allFindings) {
|
|
361
|
+
const category = finding.owasp.category;
|
|
362
|
+
if (!byOWASP[category]) {
|
|
363
|
+
byOWASP[category] = {
|
|
364
|
+
count: 0,
|
|
365
|
+
critical: 0,
|
|
366
|
+
high: 0,
|
|
367
|
+
medium: 0,
|
|
368
|
+
low: 0,
|
|
369
|
+
findings: [],
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
byOWASP[category].count++;
|
|
373
|
+
byOWASP[category][finding.severity]++;
|
|
374
|
+
byOWASP[category].findings.push(finding);
|
|
375
|
+
}
|
|
376
|
+
// Calculate summary
|
|
377
|
+
const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
378
|
+
const byType = {};
|
|
379
|
+
for (const finding of allFindings) {
|
|
380
|
+
bySeverity[finding.severity]++;
|
|
381
|
+
byType[finding.type] = (byType[finding.type] || 0) + 1;
|
|
382
|
+
}
|
|
383
|
+
// Find top vulnerable files
|
|
384
|
+
const fileVulnCounts = fileResults
|
|
385
|
+
.map((f) => ({ file: f.path, count: f.findings.length }))
|
|
386
|
+
.filter((f) => f.count > 0)
|
|
387
|
+
.sort((a, b) => b.count - a.count)
|
|
388
|
+
.slice(0, 5);
|
|
389
|
+
const totalLOC = fileResults.reduce((sum, f) => sum + f.loc, 0);
|
|
390
|
+
// Get commit info if available
|
|
391
|
+
let commit;
|
|
392
|
+
let branch;
|
|
393
|
+
if (this.workDir && fs.existsSync(path.join(this.workDir, '.git'))) {
|
|
394
|
+
try {
|
|
395
|
+
commit = execSync(`git -C "${this.workDir}" rev-parse HEAD`, { encoding: 'utf-8' }).trim();
|
|
396
|
+
branch = execSync(`git -C "${this.workDir}" rev-parse --abbrev-ref HEAD`, { encoding: 'utf-8' }).trim();
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// Ignore git errors
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
meta: {
|
|
404
|
+
target: options.target,
|
|
405
|
+
branch: options.branch || branch,
|
|
406
|
+
commit: options.commit || commit,
|
|
407
|
+
timestamp: new Date().toISOString(),
|
|
408
|
+
durationMs: Date.now() - startTime,
|
|
409
|
+
version: '1.0.0', // TODO: Get from package.json
|
|
410
|
+
},
|
|
411
|
+
findings: allFindings,
|
|
412
|
+
byOWASP,
|
|
413
|
+
summary: {
|
|
414
|
+
totalFiles: fileResults.length,
|
|
415
|
+
totalLOC,
|
|
416
|
+
totalFindings: allFindings.length,
|
|
417
|
+
bySeverity,
|
|
418
|
+
byType,
|
|
419
|
+
topVulnerableFiles: fileVulnCounts,
|
|
420
|
+
},
|
|
421
|
+
files: fileResults,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Save report to output directory.
|
|
426
|
+
*/
|
|
427
|
+
async saveReport(result, outputDir) {
|
|
428
|
+
// Ensure output directory exists
|
|
429
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
430
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
431
|
+
// Save JSON report
|
|
432
|
+
const jsonPath = path.join(outputDir, `scan-${timestamp}.json`);
|
|
433
|
+
fs.writeFileSync(jsonPath, JSON.stringify(result, null, 2));
|
|
434
|
+
// Save SARIF report
|
|
435
|
+
const sarifPath = path.join(outputDir, `scan-${timestamp}.sarif`);
|
|
436
|
+
fs.writeFileSync(sarifPath, JSON.stringify(this.toSARIF(result), null, 2));
|
|
437
|
+
// Save summary report
|
|
438
|
+
const summaryPath = path.join(outputDir, `scan-${timestamp}.txt`);
|
|
439
|
+
fs.writeFileSync(summaryPath, this.toTextSummary(result));
|
|
440
|
+
if (this.verbose) {
|
|
441
|
+
console.log(`Reports saved to ${outputDir}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Convert result to SARIF format.
|
|
446
|
+
*/
|
|
447
|
+
toSARIF(result) {
|
|
448
|
+
return {
|
|
449
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
450
|
+
version: '2.1.0',
|
|
451
|
+
runs: [
|
|
452
|
+
{
|
|
453
|
+
tool: {
|
|
454
|
+
driver: {
|
|
455
|
+
name: 'Circle-IR',
|
|
456
|
+
version: result.meta.version,
|
|
457
|
+
informationUri: 'https://github.com/cognium/circle-ir',
|
|
458
|
+
rules: Object.entries(result.summary.byType).map(([type, _count]) => ({
|
|
459
|
+
id: type,
|
|
460
|
+
name: type.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
461
|
+
helpUri: `https://cwe.mitre.org/data/definitions/${type}.html`,
|
|
462
|
+
})),
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
results: result.findings.map((f) => ({
|
|
466
|
+
ruleId: f.type,
|
|
467
|
+
level: f.severity === 'critical' || f.severity === 'high' ? 'error' : 'warning',
|
|
468
|
+
message: {
|
|
469
|
+
text: f.explanation || `${f.type} vulnerability detected`,
|
|
470
|
+
},
|
|
471
|
+
locations: [
|
|
472
|
+
{
|
|
473
|
+
physicalLocation: {
|
|
474
|
+
artifactLocation: {
|
|
475
|
+
uri: f.filePath,
|
|
476
|
+
},
|
|
477
|
+
region: {
|
|
478
|
+
startLine: f.sink?.line || f.source?.line || 1,
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
properties: {
|
|
484
|
+
cwe: f.cwe,
|
|
485
|
+
owasp: f.owasp.category,
|
|
486
|
+
severity: f.severity,
|
|
487
|
+
confidence: f.confidence,
|
|
488
|
+
},
|
|
489
|
+
})),
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Convert result to text summary.
|
|
496
|
+
*/
|
|
497
|
+
toTextSummary(result) {
|
|
498
|
+
const lines = [
|
|
499
|
+
'='.repeat(70),
|
|
500
|
+
'SECURITY SCAN REPORT',
|
|
501
|
+
'='.repeat(70),
|
|
502
|
+
'',
|
|
503
|
+
`Target: ${result.meta.target}`,
|
|
504
|
+
`Branch: ${result.meta.branch || 'N/A'}`,
|
|
505
|
+
`Commit: ${result.meta.commit || 'N/A'}`,
|
|
506
|
+
`Timestamp: ${result.meta.timestamp}`,
|
|
507
|
+
`Duration: ${(result.meta.durationMs / 1000).toFixed(2)}s`,
|
|
508
|
+
'',
|
|
509
|
+
'-'.repeat(70),
|
|
510
|
+
'SUMMARY',
|
|
511
|
+
'-'.repeat(70),
|
|
512
|
+
'',
|
|
513
|
+
`Files Scanned: ${result.summary.totalFiles}`,
|
|
514
|
+
`Lines of Code: ${result.summary.totalLOC.toLocaleString()}`,
|
|
515
|
+
`Total Findings: ${result.summary.totalFindings}`,
|
|
516
|
+
'',
|
|
517
|
+
'Severity Breakdown:',
|
|
518
|
+
` Critical: ${result.summary.bySeverity.critical}`,
|
|
519
|
+
` High: ${result.summary.bySeverity.high}`,
|
|
520
|
+
` Medium: ${result.summary.bySeverity.medium}`,
|
|
521
|
+
` Low: ${result.summary.bySeverity.low}`,
|
|
522
|
+
'',
|
|
523
|
+
'-'.repeat(70),
|
|
524
|
+
'OWASP TOP 10 BREAKDOWN',
|
|
525
|
+
'-'.repeat(70),
|
|
526
|
+
'',
|
|
527
|
+
];
|
|
528
|
+
// Sort OWASP categories by rank
|
|
529
|
+
const sortedOWASP = Object.entries(result.byOWASP)
|
|
530
|
+
.sort(([, a], [, b]) => b.count - a.count);
|
|
531
|
+
for (const [category, data] of sortedOWASP) {
|
|
532
|
+
if (data.count === 0)
|
|
533
|
+
continue;
|
|
534
|
+
lines.push(`${category}: ${data.count} findings`);
|
|
535
|
+
lines.push(` Critical: ${data.critical}, High: ${data.high}, Medium: ${data.medium}, Low: ${data.low}`);
|
|
536
|
+
lines.push('');
|
|
537
|
+
}
|
|
538
|
+
if (result.summary.topVulnerableFiles.length > 0) {
|
|
539
|
+
lines.push('-'.repeat(70));
|
|
540
|
+
lines.push('TOP VULNERABLE FILES');
|
|
541
|
+
lines.push('-'.repeat(70));
|
|
542
|
+
lines.push('');
|
|
543
|
+
for (const file of result.summary.topVulnerableFiles) {
|
|
544
|
+
lines.push(` ${file.file}: ${file.count} findings`);
|
|
545
|
+
}
|
|
546
|
+
lines.push('');
|
|
547
|
+
}
|
|
548
|
+
lines.push('='.repeat(70));
|
|
549
|
+
return lines.join('\n');
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Report progress.
|
|
553
|
+
*/
|
|
554
|
+
reportProgress(progress) {
|
|
555
|
+
if (this.onProgress) {
|
|
556
|
+
this.onProgress(progress);
|
|
557
|
+
}
|
|
558
|
+
if (this.verbose) {
|
|
559
|
+
const { phase, currentFile, filesProcessed, totalFiles, findingsCount } = progress;
|
|
560
|
+
if (currentFile) {
|
|
561
|
+
console.log(`[${phase}] ${filesProcessed}/${totalFiles} - ${currentFile} (${findingsCount} findings)`);
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
console.log(`[${phase}] ${filesProcessed}/${totalFiles}`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Clean up temporary files.
|
|
570
|
+
*/
|
|
571
|
+
cleanup() {
|
|
572
|
+
if (this.workDir && fs.existsSync(this.workDir)) {
|
|
573
|
+
fs.rmSync(this.workDir, { recursive: true, force: true });
|
|
574
|
+
this.workDir = '';
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// ============================================================================
|
|
579
|
+
// Convenience Functions
|
|
580
|
+
// ============================================================================
|
|
581
|
+
/**
|
|
582
|
+
* Run a security scan on a repository or local path.
|
|
583
|
+
*/
|
|
584
|
+
export async function scanRepository(options) {
|
|
585
|
+
const scanner = new SecurityScanner();
|
|
586
|
+
try {
|
|
587
|
+
return await scanner.scan(options);
|
|
588
|
+
}
|
|
589
|
+
finally {
|
|
590
|
+
scanner.cleanup();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Scan a local directory.
|
|
595
|
+
*/
|
|
596
|
+
export async function scanDirectory(directory, options) {
|
|
597
|
+
return scanRepository({ target: directory, ...options });
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Quick scan with default options.
|
|
601
|
+
*/
|
|
602
|
+
export async function quickScan(target) {
|
|
603
|
+
return scanRepository({
|
|
604
|
+
target,
|
|
605
|
+
parallel: true,
|
|
606
|
+
maxConcurrency: 10,
|
|
607
|
+
minSeverity: 'medium',
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Format scan result as a human-readable report.
|
|
612
|
+
*/
|
|
613
|
+
export function formatScanReport(result) {
|
|
614
|
+
const lines = [];
|
|
615
|
+
lines.push('═'.repeat(60));
|
|
616
|
+
lines.push('SECURITY SCAN REPORT');
|
|
617
|
+
lines.push('═'.repeat(60));
|
|
618
|
+
lines.push('');
|
|
619
|
+
lines.push(`Target: ${result.meta.target}`);
|
|
620
|
+
lines.push(`Timestamp: ${result.meta.timestamp}`);
|
|
621
|
+
lines.push(`Duration: ${(result.meta.durationMs / 1000).toFixed(2)}s`);
|
|
622
|
+
lines.push('');
|
|
623
|
+
// Summary
|
|
624
|
+
lines.push('─'.repeat(40));
|
|
625
|
+
lines.push('SUMMARY');
|
|
626
|
+
lines.push('─'.repeat(40));
|
|
627
|
+
lines.push(`Files scanned: ${result.summary.totalFiles}`);
|
|
628
|
+
lines.push(`Lines of code: ${result.summary.totalLOC.toLocaleString()}`);
|
|
629
|
+
lines.push(`Total findings: ${result.summary.totalFindings}`);
|
|
630
|
+
lines.push('');
|
|
631
|
+
// By severity
|
|
632
|
+
lines.push('By Severity:');
|
|
633
|
+
const { bySeverity } = result.summary;
|
|
634
|
+
if (bySeverity.critical > 0)
|
|
635
|
+
lines.push(` 🔴 Critical: ${bySeverity.critical}`);
|
|
636
|
+
if (bySeverity.high > 0)
|
|
637
|
+
lines.push(` 🟠 High: ${bySeverity.high}`);
|
|
638
|
+
if (bySeverity.medium > 0)
|
|
639
|
+
lines.push(` 🟡 Medium: ${bySeverity.medium}`);
|
|
640
|
+
if (bySeverity.low > 0)
|
|
641
|
+
lines.push(` 🟢 Low: ${bySeverity.low}`);
|
|
642
|
+
lines.push('');
|
|
643
|
+
// OWASP Top 10
|
|
644
|
+
const owaspEntries = Object.entries(result.byOWASP).filter(([_, v]) => v.count > 0);
|
|
645
|
+
if (owaspEntries.length > 0) {
|
|
646
|
+
lines.push('─'.repeat(40));
|
|
647
|
+
lines.push('OWASP TOP 10');
|
|
648
|
+
lines.push('─'.repeat(40));
|
|
649
|
+
for (const [category, data] of owaspEntries.sort((a, b) => b[1].count - a[1].count)) {
|
|
650
|
+
lines.push(`${category}: ${data.count} findings`);
|
|
651
|
+
if (data.critical > 0)
|
|
652
|
+
lines.push(` Critical: ${data.critical}`);
|
|
653
|
+
if (data.high > 0)
|
|
654
|
+
lines.push(` High: ${data.high}`);
|
|
655
|
+
if (data.medium > 0)
|
|
656
|
+
lines.push(` Medium: ${data.medium}`);
|
|
657
|
+
}
|
|
658
|
+
lines.push('');
|
|
659
|
+
}
|
|
660
|
+
// Top vulnerable files
|
|
661
|
+
if (result.summary.topVulnerableFiles.length > 0) {
|
|
662
|
+
lines.push('─'.repeat(40));
|
|
663
|
+
lines.push('TOP VULNERABLE FILES');
|
|
664
|
+
lines.push('─'.repeat(40));
|
|
665
|
+
for (const file of result.summary.topVulnerableFiles.slice(0, 5)) {
|
|
666
|
+
lines.push(` ${file.file}: ${file.count} findings`);
|
|
667
|
+
}
|
|
668
|
+
lines.push('');
|
|
669
|
+
}
|
|
670
|
+
// Detailed findings
|
|
671
|
+
if (result.findings.length > 0) {
|
|
672
|
+
lines.push('─'.repeat(40));
|
|
673
|
+
lines.push('FINDINGS');
|
|
674
|
+
lines.push('─'.repeat(40));
|
|
675
|
+
for (const finding of result.findings.slice(0, 20)) {
|
|
676
|
+
const sev = finding.severity?.toUpperCase() || 'INFO';
|
|
677
|
+
const icon = sev === 'CRITICAL' ? '🔴' : sev === 'HIGH' ? '🟠' : sev === 'MEDIUM' ? '🟡' : '🟢';
|
|
678
|
+
lines.push(`${icon} [${finding.cwe || 'N/A'}] ${finding.type}`);
|
|
679
|
+
lines.push(` File: ${finding.filePath}:${finding.source?.line || '?'}`);
|
|
680
|
+
if (finding.explanation) {
|
|
681
|
+
lines.push(` ${finding.explanation.substring(0, 80)}${finding.explanation.length > 80 ? '...' : ''}`);
|
|
682
|
+
}
|
|
683
|
+
lines.push('');
|
|
684
|
+
}
|
|
685
|
+
if (result.findings.length > 20) {
|
|
686
|
+
lines.push(`... and ${result.findings.length - 20} more findings`);
|
|
687
|
+
lines.push('');
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
lines.push('═'.repeat(60));
|
|
691
|
+
return lines.join('\n');
|
|
692
|
+
}
|
|
693
|
+
//# sourceMappingURL=scanner.js.map
|