@clear-capabilities/agentic-security-scanner 0.74.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 +1580 -0
- package/bin/.agentic-security/findings.json +1577 -0
- package/bin/.agentic-security/last-scan.json +1577 -0
- package/bin/.agentic-security/last-scan.json.sig +1 -0
- package/bin/.agentic-security/scan-history.json +465 -0
- package/bin/.agentic-security/streak.json +25 -0
- package/bin/agentic-security-audit.js +198 -0
- package/bin/agentic-security-consistency.js +80 -0
- package/bin/agentic-security-diff.js +136 -0
- package/bin/agentic-security-lsp.js +12 -0
- package/bin/agentic-security-mcp.js +40 -0
- package/bin/agentic-security-rule.js +153 -0
- package/bin/agentic-security.js +1683 -0
- package/dist/117.index.js +207 -0
- package/dist/178.index.js +250 -0
- package/dist/218.index.js +793 -0
- package/dist/227.index.js +192 -0
- package/dist/301.index.js +167 -0
- package/dist/384.index.js +18 -0
- package/dist/476.index.js +126 -0
- package/dist/513.index.js +373 -0
- package/dist/520.index.js +13 -0
- package/dist/601.index.js +1038 -0
- package/dist/634.index.js +1892 -0
- package/dist/637.index.js +216 -0
- package/dist/660.index.js +131 -0
- package/dist/675.index.js +451 -0
- package/dist/826.index.js +188 -0
- package/dist/830.index.js +133 -0
- package/dist/agentic-security.mjs +272 -0
- package/dist/agentic-security.mjs.sha256 +1 -0
- package/dist/calibration-seed.json +27 -0
- package/package.json +77 -0
- package/src/.agentic-security/findings.json +80844 -0
- package/src/.agentic-security/last-scan.json +80844 -0
- package/src/.agentic-security/last-scan.json.sig +1 -0
- package/src/.agentic-security/scan-history.json +8408 -0
- package/src/.agentic-security/streak.json +26 -0
- package/src/badge.js +188 -0
- package/src/compare.js +203 -0
- package/src/dataflow/.agentic-security/findings.json +3487 -0
- package/src/dataflow/.agentic-security/last-scan.json +3487 -0
- package/src/dataflow/.agentic-security/last-scan.json.sig +1 -0
- package/src/dataflow/.agentic-security/scan-history.json +735 -0
- package/src/dataflow/.agentic-security/streak.json +24 -0
- package/src/dataflow/CLAUDE.md +38 -0
- package/src/dataflow/access-paths.js +172 -0
- package/src/dataflow/async-sequencing.js +177 -0
- package/src/dataflow/backward.js +201 -0
- package/src/dataflow/catalog-expanded.js +485 -0
- package/src/dataflow/catalog.js +659 -0
- package/src/dataflow/cross-repo.js +219 -0
- package/src/dataflow/engine.js +588 -0
- package/src/dataflow/exception-flow.js +116 -0
- package/src/dataflow/exploit-prover.js +187 -0
- package/src/dataflow/higher-order.js +221 -0
- package/src/dataflow/ifds.js +347 -0
- package/src/dataflow/implicit-flow.js +129 -0
- package/src/dataflow/incremental.js +229 -0
- package/src/dataflow/index.js +181 -0
- package/src/dataflow/numeric-domain.js +192 -0
- package/src/dataflow/path-feasibility.js +114 -0
- package/src/dataflow/points-to.js +337 -0
- package/src/dataflow/polyglot.js +190 -0
- package/src/dataflow/proven-clean.js +159 -0
- package/src/dataflow/receiver-context.js +76 -0
- package/src/dataflow/sanitizer-proof.js +154 -0
- package/src/dataflow/soft-taint.js +140 -0
- package/src/dataflow/string-domain.js +234 -0
- package/src/dataflow/stub-aware-filter.js +100 -0
- package/src/dataflow/summaries.js +132 -0
- package/src/dataflow/symbolic-exec.js +238 -0
- package/src/dataflow/tabulation.js +135 -0
- package/src/engine.js +7763 -0
- package/src/history-scan.js +229 -0
- package/src/index.js +3 -0
- package/src/integrations/.agentic-security/findings.json +1504 -0
- package/src/integrations/.agentic-security/last-scan.json +1504 -0
- package/src/integrations/.agentic-security/scan-history.json +40 -0
- package/src/integrations/.agentic-security/streak.json +21 -0
- package/src/integrations/index.js +321 -0
- package/src/integrations/tickets.js +200 -0
- package/src/ir/.agentic-security/findings.json +3036 -0
- package/src/ir/.agentic-security/last-scan.json +3036 -0
- package/src/ir/.agentic-security/last-scan.json.sig +1 -0
- package/src/ir/.agentic-security/scan-history.json +364 -0
- package/src/ir/.agentic-security/streak.json +23 -0
- package/src/ir/CLAUDE.md +172 -0
- package/src/ir/callgraph.js +73 -0
- package/src/ir/class-hierarchy.js +195 -0
- package/src/ir/index.js +152 -0
- package/src/ir/parser-cs.js +260 -0
- package/src/ir/parser-java.js +286 -0
- package/src/ir/parser-js.js +413 -0
- package/src/ir/parser-kt.js +258 -0
- package/src/ir/parser-py-cst.js +136 -0
- package/src/ir/parser-py.helper.py +501 -0
- package/src/ir/parser-py.js +312 -0
- package/src/ir/ssa.js +315 -0
- package/src/ir/type-stubs.js +288 -0
- package/src/leaderboard.js +152 -0
- package/src/llm-validator/.agentic-security/findings.json +1891 -0
- package/src/llm-validator/.agentic-security/last-scan.json +1891 -0
- package/src/llm-validator/.agentic-security/last-scan.json.sig +1 -0
- package/src/llm-validator/.agentic-security/scan-history.json +168 -0
- package/src/llm-validator/.agentic-security/streak.json +20 -0
- package/src/llm-validator/consistency.js +141 -0
- package/src/llm-validator/index.js +437 -0
- package/src/lsp/.agentic-security/findings.json +28 -0
- package/src/lsp/.agentic-security/last-scan.json +28 -0
- package/src/lsp/.agentic-security/scan-history.json +79 -0
- package/src/lsp/.agentic-security/streak.json +22 -0
- package/src/lsp/server.js +275 -0
- package/src/mcp/.agentic-security/findings.json +8358 -0
- package/src/mcp/.agentic-security/last-scan.json +8358 -0
- package/src/mcp/.agentic-security/last-scan.json.sig +1 -0
- package/src/mcp/.agentic-security/scan-history.json +1125 -0
- package/src/mcp/.agentic-security/streak.json +22 -0
- package/src/mcp/CLAUDE.md +54 -0
- package/src/mcp/audit.js +136 -0
- package/src/mcp/redact.js +75 -0
- package/src/mcp/server.js +158 -0
- package/src/mcp/stdio.js +83 -0
- package/src/mcp/tools.js +940 -0
- package/src/mcp/validate.js +49 -0
- package/src/personality.js +164 -0
- package/src/poc-video.js +239 -0
- package/src/posture/.agentic-security/findings.json +51239 -0
- package/src/posture/.agentic-security/last-scan.json +51239 -0
- package/src/posture/.agentic-security/last-scan.json.sig +1 -0
- package/src/posture/.agentic-security/scan-history.json +5557 -0
- package/src/posture/.agentic-security/streak.json +24 -0
- package/src/posture/CLAUDE.md +42 -0
- package/src/posture/adversarial-self-test.js +114 -0
- package/src/posture/adversary-agent.js +204 -0
- package/src/posture/agents-memory.js +135 -0
- package/src/posture/ai-code-fingerprint.js +171 -0
- package/src/posture/aibom.js +284 -0
- package/src/posture/api-inventory.js +96 -0
- package/src/posture/attack-playbooks.js +305 -0
- package/src/posture/auditor-agent.js +115 -0
- package/src/posture/auth-posture-import.js +135 -0
- package/src/posture/baseline-compare.js +114 -0
- package/src/posture/blast-radius.js +836 -0
- package/src/posture/bounty-prediction.js +141 -0
- package/src/posture/business-logic.js +239 -0
- package/src/posture/calibration-drift.js +93 -0
- package/src/posture/calibration-seed.json +27 -0
- package/src/posture/calibration.js +204 -0
- package/src/posture/clustering.js +75 -0
- package/src/posture/concurrency-checker.js +265 -0
- package/src/posture/confidence.js +65 -0
- package/src/posture/container-runtime.js +149 -0
- package/src/posture/counterfactual.js +109 -0
- package/src/posture/cross-lang-graphql.js +165 -0
- package/src/posture/cross-lang-grpc.js +166 -0
- package/src/posture/cross-lang-meta.js +101 -0
- package/src/posture/cross-lang-openapi.js +187 -0
- package/src/posture/cross-lang-orm.js +153 -0
- package/src/posture/cross-lang-queues.js +210 -0
- package/src/posture/crown-jewels.js +110 -0
- package/src/posture/custom-rules.js +361 -0
- package/src/posture/cve-alert-daemon.js +433 -0
- package/src/posture/cve-lookup.js +129 -0
- package/src/posture/dead-code.js +430 -0
- package/src/posture/defender-agent.js +158 -0
- package/src/posture/deploy-platform.js +204 -0
- package/src/posture/detector-fuzz.js +61 -0
- package/src/posture/deterministic.js +99 -0
- package/src/posture/drift.js +165 -0
- package/src/posture/epss.js +156 -0
- package/src/posture/exploitability-probability.js +212 -0
- package/src/posture/exploitability.js +121 -0
- package/src/posture/feature-flags.js +110 -0
- package/src/posture/finding-defaults.js +132 -0
- package/src/posture/fix-history.js +411 -0
- package/src/posture/fix-plan.js +121 -0
- package/src/posture/fix-verify-loop.js +157 -0
- package/src/posture/fix-verify.js +130 -0
- package/src/posture/flow-narration.js +105 -0
- package/src/posture/grader-calibration.js +156 -0
- package/src/posture/harness-discovery.js +113 -0
- package/src/posture/holdout-eval.js +144 -0
- package/src/posture/iac-reachability.js +163 -0
- package/src/posture/iam-policy.js +128 -0
- package/src/posture/integrity.js +97 -0
- package/src/posture/learning.js +166 -0
- package/src/posture/license-policy.js +109 -0
- package/src/posture/llm-redteam-prompts.js +418 -0
- package/src/posture/llm-redteam.js +303 -0
- package/src/posture/material-change.js +163 -0
- package/src/posture/mitigation-composite.js +55 -0
- package/src/posture/mttr.js +91 -0
- package/src/posture/network-policy-import.js +126 -0
- package/src/posture/path-predicates.js +99 -0
- package/src/posture/persona-prioritization.js +153 -0
- package/src/posture/poc-cwe-map.js +51 -0
- package/src/posture/poc-generator.js +500 -0
- package/src/posture/policy-gate.js +174 -0
- package/src/posture/pre-incident-archaeology.js +110 -0
- package/src/posture/profile.js +93 -0
- package/src/posture/reachability-filter.js +42 -0
- package/src/posture/regression-test-gen.js +200 -0
- package/src/posture/reverse-blast-radius.js +110 -0
- package/src/posture/router.js +109 -0
- package/src/posture/rule-overrides.js +198 -0
- package/src/posture/rule-pack-signing.js +209 -0
- package/src/posture/rule-packs.js +143 -0
- package/src/posture/rule-synthesis.js +108 -0
- package/src/posture/ruleset-version.js +71 -0
- package/src/posture/sbom.js +129 -0
- package/src/posture/schema-aware-bridge.js +207 -0
- package/src/posture/security-trend.js +87 -0
- package/src/posture/semantic-clone.js +114 -0
- package/src/posture/specification-mining.js +170 -0
- package/src/posture/stable-id.js +75 -0
- package/src/posture/stack-playbook.js +229 -0
- package/src/posture/streak.js +249 -0
- package/src/posture/suppressions.js +135 -0
- package/src/posture/telemetry-ingest.js +112 -0
- package/src/posture/threat-model.js +145 -0
- package/src/posture/three-agent-pipeline.js +74 -0
- package/src/posture/triage.js +146 -0
- package/src/posture/trust-boundary-diagram.js +115 -0
- package/src/posture/type-narrowing.js +129 -0
- package/src/posture/validator-metrics.js +179 -0
- package/src/posture/verifier-ephemeral.js +118 -0
- package/src/posture/verifier-target.js +147 -0
- package/src/posture/verifier.js +257 -0
- package/src/posture/version.js +75 -0
- package/src/posture/waf-ingest.js +200 -0
- package/src/posture/why-fired.js +141 -0
- package/src/pr-comment.js +172 -0
- package/src/pr-delta.js +198 -0
- package/src/report/.agentic-security/findings.json +79 -0
- package/src/report/.agentic-security/last-scan.json +79 -0
- package/src/report/.agentic-security/last-scan.json.sig +1 -0
- package/src/report/.agentic-security/scan-history.json +332 -0
- package/src/report/.agentic-security/streak.json +23 -0
- package/src/report/index.js +1136 -0
- package/src/report/mascot.js +42 -0
- package/src/runScan.js +141 -0
- package/src/sast/.agentic-security/findings.json +5051 -0
- package/src/sast/.agentic-security/last-scan.json +5051 -0
- package/src/sast/.agentic-security/last-scan.json.sig +1 -0
- package/src/sast/.agentic-security/scan-history.json +788 -0
- package/src/sast/.agentic-security/streak.json +23 -0
- package/src/sast/CLAUDE.md +39 -0
- package/src/sast/_comment-strip.js +46 -0
- package/src/sast/agent-tool-escalation.js +131 -0
- package/src/sast/auth-provider.js +171 -0
- package/src/sast/authz.js +236 -0
- package/src/sast/bench-shape/.agentic-security/findings.json +28 -0
- package/src/sast/bench-shape/.agentic-security/last-scan.json +28 -0
- package/src/sast/bench-shape/.agentic-security/scan-history.json +24 -0
- package/src/sast/bench-shape/.agentic-security/streak.json +22 -0
- package/src/sast/bench-shape/index.js +62 -0
- package/src/sast/claude-hook-injection.js +199 -0
- package/src/sast/claude-md-prompt-injection.js +170 -0
- package/src/sast/claude-settings.js +165 -0
- package/src/sast/client-side.js +149 -0
- package/src/sast/cpp-bench-extras.js +122 -0
- package/src/sast/cpp-dataflow.js +430 -0
- package/src/sast/cpp.js +248 -0
- package/src/sast/csharp.js +152 -0
- package/src/sast/csrf.js +82 -0
- package/src/sast/dart-flutter.js +173 -0
- package/src/sast/db-rls.js +147 -0
- package/src/sast/db-taint.js +215 -0
- package/src/sast/defi-deep.js +242 -0
- package/src/sast/deserialization-gadgets.js +113 -0
- package/src/sast/django-hardening.js +230 -0
- package/src/sast/env-hygiene.js +125 -0
- package/src/sast/fastapi-hardening.js +145 -0
- package/src/sast/go-extended.js +84 -0
- package/src/sast/host-header.js +106 -0
- package/src/sast/index.js +17 -0
- package/src/sast/java-ast-folding.js +561 -0
- package/src/sast/java-bench-extras.js +708 -0
- package/src/sast/java-collection-passthrough.js +178 -0
- package/src/sast/java-constant-fold.js +244 -0
- package/src/sast/java-deserialization.js +125 -0
- package/src/sast/jndi.js +104 -0
- package/src/sast/juliet-shape.js +324 -0
- package/src/sast/jwt-exp.js +104 -0
- package/src/sast/kotlin.js +82 -0
- package/src/sast/laravel-hardening.js +198 -0
- package/src/sast/ldap-injection.js +100 -0
- package/src/sast/llm-owasp.js +465 -0
- package/src/sast/llm-stored-prompt.js +103 -0
- package/src/sast/llm-trading-agent.js +161 -0
- package/src/sast/llm.js +308 -0
- package/src/sast/logic.js +140 -0
- package/src/sast/mass-assignment.js +101 -0
- package/src/sast/mcp-audit.js +242 -0
- package/src/sast/mobile-manifest.js +195 -0
- package/src/sast/model-load.js +164 -0
- package/src/sast/mutation-xss.js +87 -0
- package/src/sast/nosql-injection.js +82 -0
- package/src/sast/open-redirect.js +119 -0
- package/src/sast/php.js +91 -0
- package/src/sast/pipeline.js +122 -0
- package/src/sast/primary-cwe-java.js +155 -0
- package/src/sast/prompt-firewall.js +151 -0
- package/src/sast/prompt-template.js +157 -0
- package/src/sast/prototype-pollution.js +112 -0
- package/src/sast/python-sinks.js +195 -0
- package/src/sast/quarkus-hardening.js +102 -0
- package/src/sast/rag-poisoning.js +118 -0
- package/src/sast/rate-limit.js +128 -0
- package/src/sast/response-splitting.js +138 -0
- package/src/sast/ruby.js +108 -0
- package/src/sast/rust.js +105 -0
- package/src/sast/solidity.js +167 -0
- package/src/sast/springboot-hardening.js +186 -0
- package/src/sast/ssrf-cloud-metadata.js +80 -0
- package/src/sast/ssti.js +116 -0
- package/src/sast/swift.js +162 -0
- package/src/sast/toctou.js +95 -0
- package/src/sast/webhook.js +101 -0
- package/src/sast/xpath-injection.js +51 -0
- package/src/sast/xxe.js +140 -0
- package/src/sast/zip-slip.js +200 -0
- package/src/sca/base-images.json +45 -0
- package/src/sca/container.js +107 -0
- package/src/sca/dep-confusion.js +134 -0
- package/src/sca/index.js +6 -0
- package/src/sca/popular-packages.json +41 -0
- package/src/sca/sarif-ingest.js +187 -0
- package/src/sca/vuln-function-hints.json +89 -0
- package/src/secrets/index.js +4 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Patch — terminal mascot expressions for the ship-verdict renderer.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the ALERT (02) and APPROVE (05) expressions from
|
|
4
|
+
// docs/brand/patch-mascot.html, scaled down to a four-line monospace face.
|
|
5
|
+
// The caller passes `color` (the same flag toShipVerdict already gates on);
|
|
6
|
+
// NO_COLOR additionally forces a plain render.
|
|
7
|
+
//
|
|
8
|
+
// Kept self-contained so the bundled scanner has no extra import surface.
|
|
9
|
+
|
|
10
|
+
const ON = {
|
|
11
|
+
FROG: '\x1b[38;2;255;107;44m',
|
|
12
|
+
DEEP: '\x1b[38;2;201;52;20m',
|
|
13
|
+
BOLD: '\x1b[1m',
|
|
14
|
+
RESET: '\x1b[0m',
|
|
15
|
+
};
|
|
16
|
+
const OFF = { FROG: '', DEEP: '', BOLD: '', RESET: '' };
|
|
17
|
+
|
|
18
|
+
function paint(enabled) {
|
|
19
|
+
return (enabled === false || process.env.NO_COLOR) ? OFF : ON;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Dilated wide pupils + small "O" mouth: scanner caught something.
|
|
23
|
+
export function alertFace({ color } = {}) {
|
|
24
|
+
const { FROG, DEEP, BOLD, RESET } = paint(color);
|
|
25
|
+
return [
|
|
26
|
+
` ${FROG}╭───╮ ╭───╮${RESET}`,
|
|
27
|
+
` ${FROG}│ ${BOLD}⊙${RESET}${FROG} │ │ ${BOLD}⊙${RESET}${FROG} │${RESET}`,
|
|
28
|
+
` ${FROG}╰───╯ ╰───╯${RESET}`,
|
|
29
|
+
` ${DEEP}${BOLD}◯${RESET}`,
|
|
30
|
+
].join('\n');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Happy closed eyes + smile: safe to deploy.
|
|
34
|
+
export function approveFace({ color } = {}) {
|
|
35
|
+
const { FROG, BOLD, RESET } = paint(color);
|
|
36
|
+
return [
|
|
37
|
+
` ${FROG}╭───╮ ╭───╮${RESET}`,
|
|
38
|
+
` ${FROG}│ ${BOLD}‿${RESET}${FROG} │ │ ${BOLD}‿${RESET}${FROG} │${RESET}`,
|
|
39
|
+
` ${FROG}╰───╯ ╰───╯${RESET}`,
|
|
40
|
+
` ${BOLD}‿‿${RESET}`,
|
|
41
|
+
].join('\n');
|
|
42
|
+
}
|
package/src/runScan.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Filesystem driver: reads a directory, builds the fileContents/depFileContents
|
|
2
|
+
// maps the engine expects, and invokes runFullScan.
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as cp from 'node:child_process';
|
|
6
|
+
import fg from 'fast-glob';
|
|
7
|
+
import { runFullScan, shouldScan } from './engine.js';
|
|
8
|
+
import { appendScanSnapshot } from './posture/security-trend.js';
|
|
9
|
+
import { recover as recoverFixHistory } from './posture/fix-history.js';
|
|
10
|
+
import { stampScan } from './posture/ruleset-version.js';
|
|
11
|
+
|
|
12
|
+
const DEP_FILE_NAMES = new Set([
|
|
13
|
+
'package.json','package-lock.json','yarn.lock','pnpm-lock.yaml',
|
|
14
|
+
'requirements.txt','pyproject.toml','poetry.lock','Pipfile.lock',
|
|
15
|
+
'composer.json','composer.lock','Gemfile','Gemfile.lock',
|
|
16
|
+
'go.mod','Cargo.toml','Cargo.lock',
|
|
17
|
+
'pom.xml','build.gradle','build.gradle.kts',
|
|
18
|
+
'pubspec.yaml','pubspec.lock',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const DEFAULT_IGNORE = [
|
|
22
|
+
'**/node_modules/**','**/.git/**','**/__pycache__/**','**/vendor/**',
|
|
23
|
+
'**/dist/**','**/build/**','**/.next/**','**/venv/**','**/env/**','**/.venv/**',
|
|
24
|
+
'**/target/**','**/bin/**','**/obj/**','**/.cache/**','**/coverage/**',
|
|
25
|
+
'**/bower_components/**','**/tests/**','**/test/**','**/__tests__/**','**/spec/**','**/mocks/**',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export async function readTree(root, { ignore = [] } = {}) {
|
|
29
|
+
const entries = await fg('**/*', {
|
|
30
|
+
cwd: root, dot: true, onlyFiles: true,
|
|
31
|
+
ignore: [...DEFAULT_IGNORE, ...ignore], followSymbolicLinks: false,
|
|
32
|
+
suppressErrors: true,
|
|
33
|
+
});
|
|
34
|
+
const fileContents = {};
|
|
35
|
+
const depFileContents = {};
|
|
36
|
+
for (const rel of entries) {
|
|
37
|
+
const abs = path.join(root, rel);
|
|
38
|
+
let stat;
|
|
39
|
+
try { stat = await fs.stat(abs); } catch { continue; }
|
|
40
|
+
if (stat.size > 500_000) continue;
|
|
41
|
+
let content;
|
|
42
|
+
try { content = await fs.readFile(abs, 'utf8'); } catch { continue; }
|
|
43
|
+
const base = path.basename(rel);
|
|
44
|
+
if (DEP_FILE_NAMES.has(base)) depFileContents[rel] = content;
|
|
45
|
+
// Cross-language taint module needs to see openapi/swagger specs even
|
|
46
|
+
// though they aren't "code" per se. Stash them in depFileContents so
|
|
47
|
+
// they ride through to runFullScan without polluting the SAST loop.
|
|
48
|
+
if (/(?:openapi|swagger)\.(?:ya?ml|json)$/i.test(base)) depFileContents[rel] = content;
|
|
49
|
+
else if (/\.proto$/i.test(base)) depFileContents[rel] = content;
|
|
50
|
+
else if (/\.(?:graphql|gql)$/i.test(base)) depFileContents[rel] = content;
|
|
51
|
+
else if (/\.tf$/i.test(base)) depFileContents[rel] = content;
|
|
52
|
+
if (shouldScan(rel)) fileContents[rel] = content;
|
|
53
|
+
// Auxiliary files: .properties files are referenced by Java rules
|
|
54
|
+
// (e.g. OWASP Benchmark's benchmark.properties resolves algorithm
|
|
55
|
+
// aliases). They are not scannable for vulns themselves, but the
|
|
56
|
+
// project index parses key=value lines for cross-file lookup.
|
|
57
|
+
else if (/\.properties$/i.test(rel)) fileContents[rel] = content;
|
|
58
|
+
}
|
|
59
|
+
return { fileContents, depFileContents };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Feat-10: incremental scan via `--changed-since <git-ref>`. Returns the set of
|
|
63
|
+
// repo-relative paths modified since the ref, or null if git is unavailable.
|
|
64
|
+
export function changedSince(root, gitRef) {
|
|
65
|
+
if (!gitRef) return null;
|
|
66
|
+
try {
|
|
67
|
+
const out = cp.execFileSync('git', ['diff', '--name-only', `${gitRef}...HEAD`], {
|
|
68
|
+
cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
69
|
+
});
|
|
70
|
+
const set = new Set(out.split('\n').filter(Boolean));
|
|
71
|
+
// Also include uncommitted changes
|
|
72
|
+
try {
|
|
73
|
+
const dirty = cp.execFileSync('git', ['status', '--porcelain'], {
|
|
74
|
+
cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
75
|
+
});
|
|
76
|
+
for (const line of dirty.split('\n')) {
|
|
77
|
+
const f = line.slice(3).trim();
|
|
78
|
+
if (f) set.add(f);
|
|
79
|
+
}
|
|
80
|
+
} catch {}
|
|
81
|
+
return set;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function runScan(rootDir, opts = {}) {
|
|
88
|
+
const root = path.resolve(rootDir);
|
|
89
|
+
const startedAt = new Date().toISOString();
|
|
90
|
+
const t0 = Date.now();
|
|
91
|
+
// Premortem 2R4.1: reconcile any pending entries from a crash mid-apply_fix
|
|
92
|
+
// before we do anything else. Idempotent + best-effort; logs to stderr.
|
|
93
|
+
try {
|
|
94
|
+
const recovered = await recoverFixHistory(root);
|
|
95
|
+
if (Array.isArray(recovered) && recovered.length) {
|
|
96
|
+
for (const r of recovered) {
|
|
97
|
+
process.stderr.write(`agentic-security: recovered fix-history entry ${r.id} → status=${r.status}${r.error ? ' (' + r.error + ')' : ''}\n`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (_) { /* best-effort */ }
|
|
101
|
+
// Caller may pre-build fileContents (used by the MCP server's scan_diff to
|
|
102
|
+
// scope a scan to a specific file list without walking the whole tree).
|
|
103
|
+
let fileContents, depFileContents;
|
|
104
|
+
if (opts.fileContents) {
|
|
105
|
+
fileContents = opts.fileContents;
|
|
106
|
+
depFileContents = opts.depFileContents || {};
|
|
107
|
+
} else {
|
|
108
|
+
({ fileContents, depFileContents } = await readTree(root, opts));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Feat-10: incremental mode — restrict the scan to files changed since a git ref
|
|
112
|
+
if (opts.changedSince) {
|
|
113
|
+
const changed = changedSince(root, opts.changedSince);
|
|
114
|
+
if (changed) {
|
|
115
|
+
const filtered = {};
|
|
116
|
+
for (const f of Object.keys(fileContents)) {
|
|
117
|
+
if (changed.has(f)) filtered[f] = fileContents[f];
|
|
118
|
+
}
|
|
119
|
+
fileContents = filtered;
|
|
120
|
+
} else if (opts.onProgress) {
|
|
121
|
+
opts.onProgress({ phase: 'warning', file: 'changedSince ignored: not a git repo or invalid ref', current: 0, total: 0 });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const scan = await runFullScan({ fileContents, depFileContents, scanRoot: root }, opts.onProgress || (()=>{}));
|
|
126
|
+
// Premortem 2R4.2: stamp ruleset version + source on the scan result, and
|
|
127
|
+
// notify if the operator pinned a different version than what's installed.
|
|
128
|
+
try { stampScan(root, scan); } catch {}
|
|
129
|
+
// Append snapshot to history for /security-trend (non-blocking, never throws)
|
|
130
|
+
try { appendScanSnapshot(scan, root); } catch {}
|
|
131
|
+
return {
|
|
132
|
+
scan,
|
|
133
|
+
meta: { scanId: cryptoUUID(), startedAt, durationMs: Date.now() - t0, root, mode: opts.changedSince ? 'incremental' : 'full' },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const scanPath = runScan;
|
|
138
|
+
|
|
139
|
+
function cryptoUUID(){
|
|
140
|
+
return globalThis.crypto?.randomUUID?.() || `scan-${Date.now().toString(36)}`;
|
|
141
|
+
}
|