@clear-capabilities/agentic-security-scanner 0.150.1 → 0.151.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 +137 -0
- package/bin/agentic-security.js +73 -10
- package/dist/1122.index.js +16 -16
- package/dist/1208.index.js +23 -0
- package/dist/1301.index.js +3 -3
- package/dist/1379.index.js +3 -3
- package/dist/1444.index.js +3 -3
- package/dist/1660.index.js +1 -1
- package/dist/1700.index.js +5 -5
- package/dist/1905.index.js +12 -12
- package/dist/1920.index.js +10 -10
- package/dist/2238.index.js +4 -4
- package/dist/2271.index.js +3 -3
- package/dist/2432.index.js +8 -8
- package/dist/2659.index.js +1 -1
- package/dist/2826.index.js +2 -2
- package/dist/2830.index.js +3 -3
- package/dist/2923.index.js +1 -1
- package/dist/3001.index.js +4 -4
- package/dist/3117.index.js +4 -4
- package/dist/3180.index.js +9 -9
- package/dist/3276.index.js +1 -1
- package/dist/3415.index.js +1 -1
- package/dist/3499.index.js +1 -1
- package/dist/3518.index.js +4 -4
- package/dist/3736.index.js +7 -7
- package/dist/3839.index.js +4 -4
- package/dist/4113.index.js +441 -418
- package/dist/4265.index.js +4 -4
- package/dist/4384.index.js +8 -3
- package/dist/4399.index.js +29 -3
- package/dist/4547.index.js +2 -2
- package/dist/4863.index.js +6 -6
- package/dist/4970.index.js +2 -2
- package/dist/5051.index.js +15 -15
- package/dist/5144.index.js +4 -4
- package/dist/5333.index.js +8 -8
- package/dist/5343.index.js +2 -2
- package/dist/5350.index.js +6 -6
- package/dist/5561.index.js +1 -1
- package/dist/5637.index.js +10 -5
- package/dist/5724.index.js +21642 -0
- package/dist/5756.index.js +99 -489
- package/dist/5830.index.js +3 -3
- package/dist/6257.index.js +21 -8
- package/dist/6626.index.js +5 -5
- package/dist/6662.index.js +1 -1
- package/dist/6675.index.js +4 -4
- package/dist/6730.index.js +10 -10
- package/dist/6829.index.js +4 -4
- package/dist/6944.index.js +2 -2
- package/dist/6994.index.js +5 -5
- package/dist/7039.index.js +83 -19
- package/dist/7178.index.js +11 -6
- package/dist/7227.index.js +3 -3
- package/dist/7552.index.js +2 -2
- package/dist/7709.index.js +2 -2
- package/dist/7838.index.js +1037 -0
- package/dist/8218.index.js +4 -4
- package/dist/8476.index.js +4 -4
- package/dist/8513.index.js +8 -8
- package/dist/8520.index.js +1 -1
- package/dist/8752.index.js +4 -4
- package/dist/9207.index.js +2 -2
- package/dist/9220.index.js +5 -5
- package/dist/9390.index.js +6 -6
- package/dist/9503.index.js +2 -2
- package/dist/9560.index.js +3232 -0
- package/dist/957.index.js +4 -4
- package/dist/9801.index.js +1 -1
- package/dist/9824.index.js +5 -5
- package/dist/agentic-security.mjs +23 -23
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +21 -3
- package/src/dataflow/catalog.js +52 -0
- package/src/egress/redact.js +1 -1
- package/src/engine.js +13 -2
- package/src/lineage/source-registry.js +8 -0
- package/src/llm-validator/agent-loop.js +85 -5
- package/src/llm-validator/fix-proposal.js +16 -3
- package/src/llm-validator/model-capabilities.js +26 -1
- package/src/llm-validator/model-probe.js +46 -8
- package/src/llm-validator/ollama-provider.js +26 -1
- package/src/llm-validator/oom-feedback.js +69 -0
- package/src/llm-validator/providers.js +50 -0
- package/src/llm-validator/redact.js +177 -11
- package/src/posture/deterministic-fix.js +11 -0
- package/src/report/index.js +14 -3
- package/src/sast/java-ast-folding.js +18 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
205f60e669498ecd7e51d8e4849419dcbbf4afd6a1d55504c8ac06ab77458327 agentic-security.mjs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.151.0",
|
|
4
4
|
"description": "Scanner engine for the agentic-security Claude Code plugin — SAST, SCA (function-level reachability + CISA KEV), secrets, IaC, prompt-injection, MCP/agent-tool audit, auth/authZ deep analysis, attack chains, PoC generation, business logic, toxic-combinations scoring, SBOM, pipeline integrity, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -66,10 +66,11 @@
|
|
|
66
66
|
"test": "node ../scripts/run-unit-tests.mjs && AGENTIC_SECURITY_CPP_DATAFLOW=1 node --test test/cpp-dataflow.test.js && node --experimental-test-module-mocks --test test/fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection-k8s-admission.test.js && node --experimental-test-module-mocks --test test/lineage-fault-injection.test.js && npm run test:python",
|
|
67
67
|
"test:python": "node ../scripts/run-python-tests.mjs",
|
|
68
68
|
"test:smoke": "node --test test/smoke.test.js test/demo-app.test.js",
|
|
69
|
+
"test:ollama-e2e": "AGENTIC_SECURITY_OLLAMA_E2E=1 node --test test/ollama-e2e.test.js",
|
|
69
70
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
70
71
|
"test:sast": "node --test test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js test/sast/manifest-line-tracking.test.js",
|
|
71
|
-
"test:posture": "node --test test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/release-workflow.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/ollama-provider.test.js test/ollama-capabilities.test.js test/ollama-offline-egress.test.js test/ollama-fix-proposal.test.js test/ollama-explain-proposal.test.js test/ollama-poc-proposal.test.js test/ollama-model-probe.test.js test/ollama-agent-tools.test.js test/ollama-agent-loop.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/module-artifact-liveness.test.js test/supply-chain-provenance-completeness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/cli/attest-obligations.test.js test/cli/models.test.js test/cli/setup-llm.test.js test/cli/compliance-walkthrough-lineage.test.js test/cli/dataflow-export-privacy.test.js test/cli/dataflow-export-briefing.test.js test/cli/lineage-snapshot-persist.test.js test/cli/dataflow-diff.test.js test/cli/dataflow-watch.test.js test/cli/dataflow-recipients.test.js test/cli/dataflow-coverage.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js test/posture/remediation-ledger.test.js",
|
|
72
|
-
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js test/dataflow-progress.test.js test/scan-progress.test.js",
|
|
72
|
+
"test:posture": "node --test test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/release-workflow.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/ollama-provider.test.js test/ollama-capabilities.test.js test/ollama-offline-egress.test.js test/ollama-fix-proposal.test.js test/ollama-explain-proposal.test.js test/ollama-poc-proposal.test.js test/ollama-model-probe.test.js test/ollama-agent-tools.test.js test/ollama-agent-loop.test.js test/ollama-e2e.test.js test/ollama-oom-feedback.test.js test/ollama-doc-drift.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/module-artifact-liveness.test.js test/supply-chain-provenance-completeness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/cli/attest-obligations.test.js test/cli/models.test.js test/cli/setup-llm.test.js test/cli/fix-retry.test.js test/cli/compliance-walkthrough-lineage.test.js test/cli/dataflow-export-privacy.test.js test/cli/dataflow-export-briefing.test.js test/cli/lineage-snapshot-persist.test.js test/cli/dataflow-diff.test.js test/cli/dataflow-watch.test.js test/cli/dataflow-recipients.test.js test/cli/dataflow-coverage.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js test/posture/remediation-ledger.test.js",
|
|
73
|
+
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js test/dataflow-progress.test.js test/scan-progress.test.js test/sard-leakage-pipeline.test.js",
|
|
73
74
|
"test:mcp": "node --test test/mcp.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js test/mcp-dataflow-tools.test.js test/cli/governance-propose-edit.test.js test/cli/remediation-open-update.test.js test/cli/remediation-verify-reopen.test.js test/cli/dataflow-observations.test.js test/cli/federate-declare-list.test.js",
|
|
74
75
|
"test:report": "node --test test/report-scan-invariants.test.js test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/scan-health.test.js test/finding-schema.test.js test/pipeline/finding-schema-provenance.test.js test/evidence-provenance.test.js test/report/provenance-output.test.js test/report/provenance-format-parity.test.js test/legacy-compat.test.js test/annotator-runner.test.js test/detector-runner.test.js test/detector-errors.test.js test/analyzer-supervisor.test.js test/cascade-worker-pool.test.js test/cascade-pool-wiring.test.js test/coverage-ledger.test.js test/coverage-ledger-completeness.test.js test/assurance-mode.test.js test/artifact-registry.test.js test/artifact-registry-completeness.test.js test/retention-policy.test.js test/state-lifecycle-report.test.js test/legal-hold.test.js test/production-feedback.test.js test/calibration-feedback.test.js test/encryption-provider.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js test/cli-fix-apply.test.js test/apply-fix-service.test.js test/high-impact-approval-gate.test.js test/approver-registry.test.js test/suppression-exceptions.test.js test/fix-history.test.js test/producer-registry.test.js test/producer-collector.test.js test/enrichment-completion.test.js",
|
|
75
76
|
"test:lineage": "node --test test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js",
|
|
@@ -128,6 +129,23 @@
|
|
|
128
129
|
"bench:sca-replay:reachability": "node ../bench/sca-replay/reachability.mjs",
|
|
129
130
|
"bench:sca-replay:fetch": "node ../bench/sca-replay/fetch.mjs",
|
|
130
131
|
"bench:sca-replay:label": "node ../bench/sca-replay/label.mjs",
|
|
132
|
+
"bench:sard:score": "node ../bench/sard/scripts/macro-score.mjs",
|
|
133
|
+
"bench:sard:leakage-audit": "node ../bench/sard/scripts/leakage-audit.mjs",
|
|
134
|
+
"bench:sard:java": "node test/benchmark/realworld/bench-realworld.js --app sard-juliet-java-strict --blind --scramble-identifiers --json | node ../bench/sard/scripts/macro-score.mjs",
|
|
135
|
+
"bench:sard:csharp": "node test/benchmark/realworld/bench-realworld.js --app sard-juliet-csharp-strict --blind --scramble-identifiers --json | node ../bench/sard/scripts/macro-score.mjs",
|
|
136
|
+
"bench:sard:java:fast-unsafe": "node test/benchmark/realworld/bench-realworld.js --app sard-juliet-java-strict --blind --json | node ../bench/sard/scripts/macro-score.mjs",
|
|
137
|
+
"bench:sard:split": "node ../bench/sard/scripts/split.mjs",
|
|
138
|
+
"bench:sard:split:check": "node ../bench/sard/scripts/split.mjs --verify",
|
|
139
|
+
"bench:sard:update-baseline": "node ../bench/sard/scripts/compare-baseline.mjs --update-baseline",
|
|
140
|
+
"bench:sard:check-baseline": "node ../bench/sard/scripts/compare-baseline.mjs --check-baseline",
|
|
141
|
+
"bench:sard:holdout-update-baseline": "node ../bench/sard/scripts/holdout-check.mjs --update-baseline",
|
|
142
|
+
"bench:sard:holdout-check": "node ../bench/sard/scripts/holdout-check.mjs --check-baseline",
|
|
143
|
+
"bench:sard:smoke": "node test/benchmark/realworld/bench-realworld.js --app sard-juliet-java-strict --blind --scramble-identifiers --cwe CWE-89,CWE-78,CWE-23,CWE-80,CWE-327 --json | node ../bench/sard/scripts/macro-score.mjs",
|
|
144
|
+
"bench:sard:mutate": "node ../bench/sard/scripts/mutate.mjs --app sard-juliet-java-strict --cwe 89,78,23,80,327 --limit 60",
|
|
145
|
+
"bench:sard:verify-fixes": "node ../bench/sard/scripts/verify-fixes.mjs --app sard-juliet-java-strict --cwe 89,78,23,80,327,328 --limit 200",
|
|
146
|
+
"bench:sard:analyze-errors": "node ../bench/sard/scripts/analyze-errors.mjs",
|
|
147
|
+
"bench:sard:php-ingest": "node ../bench/sard/scripts/ingest-php.mjs",
|
|
148
|
+
"bench:sard:php-score": "node ../bench/sard/scripts/score-php.mjs",
|
|
131
149
|
"bench:layer-recall": "node ../bench/layer-recall/runner.mjs",
|
|
132
150
|
"bench:layer-recall:check": "node ../bench/layer-recall/runner.mjs --check",
|
|
133
151
|
"bench:layer-recall:update-baseline": "node ../bench/layer-recall/runner.mjs --update-baseline",
|
package/src/dataflow/catalog.js
CHANGED
|
@@ -217,6 +217,32 @@ export const CATALOG = [
|
|
|
217
217
|
{ kind: 'source', id: 'java-request-getReader', language: 'java', framework: 'servlet', match: { type: 'call', callee: 'getReader' }, label: 'request.getReader' },
|
|
218
218
|
{ kind: 'source', id: 'java-system-getenv', language: 'java', framework: 'stdlib', match: { type: 'call', callee: 'getenv' }, label: 'System.getenv' },
|
|
219
219
|
{ kind: 'source', id: 'java-system-getProperty', language: 'java', framework: 'stdlib', match: { type: 'call', callee: 'getProperty' }, label: 'System.getProperty' },
|
|
220
|
+
// Found via SARD_AGENTIC_SECURITY_PRD.md bench work: bench/sard/scripts/
|
|
221
|
+
// analyze-errors.mjs clustered Java false negatives by Juliet's own
|
|
222
|
+
// source-descriptor filename segment (evidence-based — see that script's
|
|
223
|
+
// header for why this is legitimate without reading raw corpus content)
|
|
224
|
+
// and found "console_readLine" and "database" among the largest clusters,
|
|
225
|
+
// spanning multiple unrelated CWEs uniformly — the signature of a missing
|
|
226
|
+
// SOURCE model, not a per-CWE bug. Confirmed by grep: this catalog had no
|
|
227
|
+
// entry for either before now. Both are standard real-world taint sources
|
|
228
|
+
// (interactive stdin input; a value read back out of a database result
|
|
229
|
+
// set — classic second-order injection), not SARD-specific.
|
|
230
|
+
{ kind: 'source', id: 'java-io-readline', language: 'java', framework: 'stdlib', match: { type: 'call', callee: 'readLine' }, label: 'BufferedReader.readLine() / Console.readLine()' },
|
|
231
|
+
// `getString`/`getObject` are common enough method names on unrelated
|
|
232
|
+
// classes that this SHOULD be scoped to a resolved ResultSet-shaped
|
|
233
|
+
// receiver, and receiverTypeIn is written here expressing that intent —
|
|
234
|
+
// but verified (via a dedicated precision test, see java-taint-flow.
|
|
235
|
+
// test.js) that it is currently INERT for Java: `class-hierarchy.js`'s
|
|
236
|
+
// own module header states its CHA/classOfVar is JS/TS-only (built by
|
|
237
|
+
// walking Babel ASTs), so `_receiverTypeFor` always returns null for a
|
|
238
|
+
// Java call site and `_receiverTypeAllowed`'s "unknown != clean" rule
|
|
239
|
+
// (documented in this file, `_receiverTypeAllowed`'s own header) then
|
|
240
|
+
// permits the match unconditionally — identical to a bare unscoped entry
|
|
241
|
+
// today. Left in place (correct once Java CHA support exists, and
|
|
242
|
+
// documents the intended precision boundary) rather than removed, but
|
|
243
|
+
// do not assume it is actually gating anything yet.
|
|
244
|
+
{ kind: 'source', id: 'java-resultset-getstring', language: 'java', framework: 'jdbc', match: { type: 'call', callee: 'getString', receiverTypeIn: ['resultset'] }, label: 'ResultSet.getString' },
|
|
245
|
+
{ kind: 'source', id: 'java-resultset-getobject', language: 'java', framework: 'jdbc', match: { type: 'call', callee: 'getObject', receiverTypeIn: ['resultset'] }, label: 'ResultSet.getObject' },
|
|
220
246
|
|
|
221
247
|
// ─── SOURCES (Annotation/Decorator-shaped) ────────────────────────────────
|
|
222
248
|
// R14(a): annotation/decorator-shaped framework sources (Spring @RequestParam,
|
|
@@ -356,6 +382,20 @@ export const CATALOG = [
|
|
|
356
382
|
{ kind: 'source', id: 'php-post', language: 'php', framework: 'core', match: { type: 'global', name: '_POST' }, label: '$_POST' },
|
|
357
383
|
{ kind: 'source', id: 'php-cookie', language: 'php', framework: 'core', match: { type: 'global', name: '_COOKIE' }, label: '$_COOKIE' },
|
|
358
384
|
{ kind: 'source', id: 'php-server', language: 'php', framework: 'core', match: { type: 'global', name: '_SERVER' }, label: '$_SERVER' },
|
|
385
|
+
// $_SESSION and $_ENV were absent from this catalog entirely (only
|
|
386
|
+
// _REQUEST/_GET/_POST/_COOKIE/_SERVER were modeled) — found via
|
|
387
|
+
// SARD_AGENTIC_SECURITY_PRD.md bench work on the NIST SARD PHP corpus,
|
|
388
|
+
// whose templates use $_SESSION as one of several source stand-ins.
|
|
389
|
+
// $_SESSION isn't purely attacker-controlled the way $_GET is (session
|
|
390
|
+
// data usually originates from server-side writes), but a common,
|
|
391
|
+
// real-world-relevant subclass IS attacker-controlled: any value the app
|
|
392
|
+
// itself copies into $_SESSION from $_GET/$_POST/$_COOKIE without
|
|
393
|
+
// validation. Modeling it as a source (same as the existing four) is the
|
|
394
|
+
// conservative, general choice — flagging that class rather than missing
|
|
395
|
+
// it entirely, consistent with how $_COOKIE (also partly server-set) is
|
|
396
|
+
// already modeled.
|
|
397
|
+
{ kind: 'source', id: 'php-session', language: 'php', framework: 'core', match: { type: 'global', name: '_SESSION' }, label: '$_SESSION' },
|
|
398
|
+
{ kind: 'source', id: 'php-env', language: 'php', framework: 'core', match: { type: 'global', name: '_ENV' }, label: '$_ENV' },
|
|
359
399
|
{ kind: 'source', id: 'php-symfony-query', language: 'php', framework: 'symfony', match: { type: 'member', object: '$request', prop: 'query' }, label: '$request->query (Symfony)' },
|
|
360
400
|
{ kind: 'source', id: 'php-symfony-request', language: 'php', framework: 'symfony', match: { type: 'member', object: '$request', prop: 'request' }, label: '$request->request (Symfony)' },
|
|
361
401
|
{ kind: 'source', id: 'php-symfony-cookies', language: 'php', framework: 'symfony', match: { type: 'member', object: '$request', prop: 'cookies' }, label: '$request->cookies (Symfony)' },
|
|
@@ -444,6 +484,18 @@ export const CATALOG = [
|
|
|
444
484
|
{ kind: 'sink', id: 'php-mysqli-query', language: 'php', framework: 'mysqli', match: { type: 'call', callee: 'mysqli_query' }, argIndex: 1,
|
|
445
485
|
vuln: { name: 'SQL Injection (mysqli_query)', severity: 'critical', cwe: 'CWE-89',
|
|
446
486
|
remediation: 'Use prepared statements: $stmt = $conn->prepare("SELECT * WHERE id = ?"); $stmt->bind_param("i", $id);' } },
|
|
487
|
+
// Legacy mysql_* extension (deprecated PHP 5.5, removed 7.0) — still widely
|
|
488
|
+
// present in real-world PHP and, per SARD_AGENTIC_SECURITY_PRD.md bench
|
|
489
|
+
// work, in the NIST SARD PHP Vulnerability Test Suite corpus. Only
|
|
490
|
+
// php.js's same-line structural regex (`sqlInjectionStructural`) covered
|
|
491
|
+
// this call before; a variable assigned on one line and passed to
|
|
492
|
+
// mysql_query() on a later line (the dominant real-world and SARD shape)
|
|
493
|
+
// was invisible to the taint dataflow engine entirely. Query is argIndex 0
|
|
494
|
+
// (the optional link-identifier connection is argIndex 1), unlike
|
|
495
|
+
// mysqli_query's argIndex 1.
|
|
496
|
+
{ kind: 'sink', id: 'php-mysql-query', language: 'php', framework: 'mysql', match: { type: 'call', callee: 'mysql_query' }, argIndex: 0,
|
|
497
|
+
vuln: { name: 'SQL Injection (mysql_query)', severity: 'critical', cwe: 'CWE-89',
|
|
498
|
+
remediation: 'The mysql_* extension is removed in PHP 7+; migrate to mysqli or PDO with prepared statements: $stmt = $mysqli->prepare("SELECT * WHERE id = ?"); $stmt->bind_param("i", $id).' } },
|
|
447
499
|
{ kind: 'sink', id: 'php-pdo-query', language: 'php', framework: 'pdo', match: { type: 'call', callee: 'query' }, argIndex: 0,
|
|
448
500
|
vuln: { name: 'SQL Injection (PDO::query)', severity: 'critical', cwe: 'CWE-89',
|
|
449
501
|
remediation: 'Use PDO::prepare with bound parameters.' } },
|
package/src/egress/redact.js
CHANGED
|
@@ -156,7 +156,7 @@ export function redactPayload({ text, filePath = null, scanRoot = null, taxonomy
|
|
|
156
156
|
|
|
157
157
|
let out = text;
|
|
158
158
|
|
|
159
|
-
const secretResult = redactSecrets(out);
|
|
159
|
+
const secretResult = redactSecrets(out, { filePath });
|
|
160
160
|
out = secretResult.text;
|
|
161
161
|
categories.secrets = secretResult.redactions;
|
|
162
162
|
|
package/src/engine.js
CHANGED
|
@@ -191,7 +191,7 @@ import { annotateExploitability, detectProjectContext } from './posture/exploita
|
|
|
191
191
|
import { applyFeedback as applyLearnedFeedback } from './posture/learning.js';
|
|
192
192
|
import { validateMany as llmValidateMany, applyValidatorVerdicts } from './llm-validator/index.js';
|
|
193
193
|
import { MODEL_STATUS, stageSummaryFromModelStatus } from './llm-validator/model-status.js';
|
|
194
|
-
import { resolveProvider as resolveLlmProvider } from './llm-validator/providers.js';
|
|
194
|
+
import { resolveProvider as resolveLlmProvider, otherRemoteRoles as llmOtherRemoteRoles, NO_CLOUD_FALLBACK } from './llm-validator/providers.js';
|
|
195
195
|
import { scanCrossLangOpenAPI } from './posture/cross-lang-openapi.js';
|
|
196
196
|
import { scanCrossLangGrpc } from './posture/cross-lang-grpc.js';
|
|
197
197
|
import { scanCrossLangGraphql } from './posture/cross-lang-graphql.js';
|
|
@@ -9993,12 +9993,23 @@ function _deterministicFileTimings(timings) {
|
|
|
9993
9993
|
const status = finalFindings.llmValidatorStatus;
|
|
9994
9994
|
if (status && status.counts[MODEL_STATUS.DISABLED] !== status.total) {
|
|
9995
9995
|
const resolved = resolveLlmProvider({ role: 'validate' });
|
|
9996
|
+
// Adversarial-review finding (Ollama offline PRD premortem, 2026-09):
|
|
9997
|
+
// this block only ever checked the `validate` role, but printed an
|
|
9998
|
+
// unqualified "LLM inference was loopback-only" claim a reader could
|
|
9999
|
+
// reasonably take as describing the WHOLE scan — while `verify`/
|
|
10000
|
+
// `explain`/`fix`/`poc`/`logic`/`hunt` each have their own
|
|
10001
|
+
// independent per-role provider override (providers.js's `_forRole`)
|
|
10002
|
+
// and could genuinely be pointed at a cloud vendor at the same time.
|
|
10003
|
+
// `otherRolesRemote` makes that visible instead of silent.
|
|
10004
|
+
const otherRolesRemote = llmOtherRemoteRoles('validate');
|
|
9996
10005
|
_aiAssistance = {
|
|
10006
|
+
scopeRole: 'validate',
|
|
9997
10007
|
provider: resolved.ok ? resolved.config.provider : null,
|
|
9998
10008
|
model: resolved.ok ? resolved.config.model : null,
|
|
9999
10009
|
endpoint: resolved.ok ? resolved.config.endpoint : null,
|
|
10000
10010
|
egress: resolved.ok ? resolved.config.egress : null,
|
|
10001
|
-
cloudFallback:
|
|
10011
|
+
cloudFallback: !NO_CLOUD_FALLBACK,
|
|
10012
|
+
otherRolesRemote,
|
|
10002
10013
|
stages: { validate: stageSummaryFromModelStatus(status) },
|
|
10003
10014
|
};
|
|
10004
10015
|
}
|
|
@@ -153,6 +153,12 @@ export const NO_PROVENANCE_OVERRIDES = Object.freeze({
|
|
|
153
153
|
// Java / stdlib
|
|
154
154
|
'java-system-getenv': 'env-value',
|
|
155
155
|
'java-system-getProperty': 'env-value', // JVM system properties, env-adjacent
|
|
156
|
+
'java-io-readline': 'user-input', // BufferedReader.readLine() / Console.readLine() — interactive stdin
|
|
157
|
+
// Java / JDBC — a value read back out of a database result set (classic
|
|
158
|
+
// second-order injection); `database-read` is SOURCE_CATEGORIES' own
|
|
159
|
+
// vocabulary for exactly this shape.
|
|
160
|
+
'java-resultset-getstring': 'database-read',
|
|
161
|
+
'java-resultset-getobject': 'database-read',
|
|
156
162
|
// Java / Spring annotations
|
|
157
163
|
'java-spring-requestparam': 'http-query',
|
|
158
164
|
'java-spring-pathvariable': 'http-route',
|
|
@@ -209,6 +215,8 @@ export const NO_PROVENANCE_OVERRIDES = Object.freeze({
|
|
|
209
215
|
'php-post': 'http-body',
|
|
210
216
|
'php-cookie': 'http-cookie',
|
|
211
217
|
'php-server': 'http-header',
|
|
218
|
+
'php-session': 'http-cookie', // $_SESSION is cookie-backed by default, same convention as rb-rails-session
|
|
219
|
+
'php-env': 'env-value',
|
|
212
220
|
'php-symfony-query': 'http-query',
|
|
213
221
|
'php-symfony-request': 'http-body',
|
|
214
222
|
'php-symfony-cookies': 'http-cookie',
|
|
@@ -26,6 +26,7 @@ import { evaluateEgress } from '../egress/policy.js';
|
|
|
26
26
|
import { TOOL_DEFINITIONS, TOOL_ERROR, runTool } from './agent-tools.js';
|
|
27
27
|
import { getModelCapabilities } from './model-probe.js';
|
|
28
28
|
import { statePath as defaultStatePath } from '../posture/state-dir.js';
|
|
29
|
+
import { priorOOMFor } from './oom-feedback.js';
|
|
29
30
|
|
|
30
31
|
export const AGENT_LOOP_ERROR = Object.freeze({
|
|
31
32
|
NOT_CONFIGURED: 'agent-loop-not-configured',
|
|
@@ -37,6 +38,35 @@ export const AGENT_LOOP_ERROR = Object.freeze({
|
|
|
37
38
|
export const DEFAULT_MAX_TOOL_ITERATIONS = 12;
|
|
38
39
|
const DEFAULT_WALL_CLOCK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
39
40
|
|
|
41
|
+
// Adversarial-review finding (2026-09), confirmed against a real, slow
|
|
42
|
+
// (CPU-only) Ollama server: the wall-clock deadline used to be checked ONLY
|
|
43
|
+
// at the top of each iteration, never around the in-flight callOllamaChat
|
|
44
|
+
// itself. `docs/guides/ollama.md` tells users to raise
|
|
45
|
+
// AGENTIC_SECURITY_LLM_TIMEOUT_MS for a cold-loading model — that value
|
|
46
|
+
// flows into the PER-CALL requestTimeoutMs, which used to be entirely
|
|
47
|
+
// independent of this loop's own wall-clock budget. A single call already
|
|
48
|
+
// running when the wall clock expires would complete anyway (its own
|
|
49
|
+
// timeout could be minutes longer), and only the NEXT iteration's top-of-
|
|
50
|
+
// loop check would notice — reporting `wall-clock-timeout` after exactly
|
|
51
|
+
// one useful call, no matter how high the per-call timeout was raised,
|
|
52
|
+
// which made the documented remedy for slow models actively useless.
|
|
53
|
+
//
|
|
54
|
+
// Fix: cap the PER-CALL requestTimeoutMs at whatever wall-clock budget
|
|
55
|
+
// actually remains, every iteration. A slow call now gets cut off by ITS
|
|
56
|
+
// OWN timeout mechanism (producing the same clean `{ok:false,
|
|
57
|
+
// code:'ollama-timeout'}` every other caller already handles) at exactly
|
|
58
|
+
// the moment the wall clock would have run out anyway — never later. This
|
|
59
|
+
// also makes the two settings coherent for the first time: raising
|
|
60
|
+
// AGENTIC_SECURITY_LLM_TIMEOUT_MS now genuinely helps, as long as the loop's
|
|
61
|
+
// OWN budget (wallClockTimeoutMs / AGENTIC_SECURITY_LLM_AGENT_TIMEOUT_MS)
|
|
62
|
+
// is raised enough to give it room.
|
|
63
|
+
function _cappedTimeouts(baseTimeouts, remainingMs) {
|
|
64
|
+
if (!baseTimeouts) return { requestTimeoutMs: Math.max(1, remainingMs) };
|
|
65
|
+
const base = Number(baseTimeouts.requestTimeoutMs);
|
|
66
|
+
const capped = Number.isFinite(base) ? Math.min(base, remainingMs) : remainingMs;
|
|
67
|
+
return { ...baseTimeouts, requestTimeoutMs: Math.max(1, capped) };
|
|
68
|
+
}
|
|
69
|
+
|
|
40
70
|
function systemPrompt(scanRoot) {
|
|
41
71
|
return [
|
|
42
72
|
'You are a security-scan assistant with READ-ONLY access to the scanned',
|
|
@@ -54,14 +84,49 @@ function systemPrompt(scanRoot) {
|
|
|
54
84
|
* maxToolIterations?:number, wallClockTimeoutMs?:number}} opts
|
|
55
85
|
* `statePath` defaults to posture/state-dir.js's real implementation;
|
|
56
86
|
* overridable only for tests that need a fixture-scoped state dir.
|
|
87
|
+
* `wallClockTimeoutMs`, when not passed explicitly, falls back to
|
|
88
|
+
* `AGENTIC_SECURITY_LLM_AGENT_TIMEOUT_MS` — a SEPARATE setting from
|
|
89
|
+
* `AGENTIC_SECURITY_LLM_TIMEOUT_MS` (the per-call timeout) on purpose: the
|
|
90
|
+
* two used to be incoherent (raising the per-call setting alone did nothing
|
|
91
|
+
* for a loop that could still time out after one call), so a caller who
|
|
92
|
+
* genuinely needs a longer overall budget for a slow model must raise BOTH.
|
|
57
93
|
* @returns {{ok:true, finalText, iterations, toolCalls, stopReason} |
|
|
58
94
|
* {ok:false, code, reason}}
|
|
59
95
|
*/
|
|
60
|
-
export async function runAgentLoop({
|
|
96
|
+
export async function runAgentLoop(opts = {}) {
|
|
97
|
+
const result = await _runAgentLoopCore(opts);
|
|
98
|
+
// Adversarial-review fix (2026-09, second pass): Round 1's original OOM-
|
|
99
|
+
// feedback fix only surfaced `priorOOMWarning` in `models doctor`'s
|
|
100
|
+
// advisory output — a user who never happens to run `doctor` would OOM
|
|
101
|
+
// again on the exact same model via `ask` with no warning at all, since
|
|
102
|
+
// `recommendAdmission` (where the warning lives) is never consulted on
|
|
103
|
+
// this real call path. Surface it here too, on any outcome where a real
|
|
104
|
+
// call was actually attempted (a pure config/capability refusal before
|
|
105
|
+
// any call has nothing useful to warn about).
|
|
106
|
+
const attemptedARealCall = result.ok || result.code === AGENT_LOOP_ERROR.FAILED;
|
|
107
|
+
if (attemptedARealCall) {
|
|
108
|
+
const resolved = resolveProvider({ role: 'hunt', env: opts.env || process.env });
|
|
109
|
+
const prior = resolved.ok ? priorOOMFor(resolved.config.model) : null;
|
|
110
|
+
if (prior) {
|
|
111
|
+
return {
|
|
112
|
+
...result,
|
|
113
|
+
priorOOMWarning: `'${resolved.config.model}' has previously failed with an out-of-memory error on this machine ` +
|
|
114
|
+
`(${prior.count} time${prior.count === 1 ? '' : 's'}, most recently ${new Date(prior.lastAt).toISOString()}).`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function _runAgentLoopCore({
|
|
61
122
|
goal, scanRoot, env = process.env, statePath = defaultStatePath,
|
|
62
|
-
maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, wallClockTimeoutMs
|
|
123
|
+
maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, wallClockTimeoutMs,
|
|
63
124
|
} = {}) {
|
|
64
125
|
const boundedIterations = Math.max(1, Math.min(maxToolIterations, DEFAULT_MAX_TOOL_ITERATIONS));
|
|
126
|
+
if (wallClockTimeoutMs === undefined) {
|
|
127
|
+
const fromEnv = Number(env.AGENTIC_SECURITY_LLM_AGENT_TIMEOUT_MS);
|
|
128
|
+
wallClockTimeoutMs = Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : DEFAULT_WALL_CLOCK_TIMEOUT_MS;
|
|
129
|
+
}
|
|
65
130
|
|
|
66
131
|
const resolved = resolveProvider({ role: 'hunt', env });
|
|
67
132
|
if (!resolved.ok || resolved.config.provider !== 'ollama') {
|
|
@@ -97,15 +162,30 @@ export async function runAgentLoop({
|
|
|
97
162
|
const deadline = Date.now() + boundedTimeoutMs;
|
|
98
163
|
|
|
99
164
|
for (let iteration = 0; iteration < boundedIterations; iteration++) {
|
|
100
|
-
|
|
165
|
+
const remainingMs = deadline - Date.now();
|
|
166
|
+
if (remainingMs <= 0) {
|
|
101
167
|
return { ok: true, finalText: null, iterations: iteration, toolCalls: toolCallLog, stopReason: 'wall-clock-timeout' };
|
|
102
168
|
}
|
|
103
169
|
|
|
170
|
+
// Cap this call's own timeout at whatever wall-clock budget remains, so
|
|
171
|
+
// a slow call can never silently outlive the loop's overall bound (see
|
|
172
|
+
// the header comment above _cappedTimeouts for the incident this fixes).
|
|
173
|
+
const callTimeouts = _cappedTimeouts(timeouts, remainingMs);
|
|
174
|
+
const deadlineWasBinding = timeouts && Number(timeouts.requestTimeoutMs) > remainingMs;
|
|
104
175
|
const r = await callOllamaChat({
|
|
105
176
|
host: resolved.config.endpoint, model: resolved.config.model, messages,
|
|
106
|
-
tools: TOOL_DEFINITIONS, keepAlive: oc?.keepAlive, timeouts,
|
|
177
|
+
tools: TOOL_DEFINITIONS, keepAlive: oc?.keepAlive, timeouts: callTimeouts,
|
|
107
178
|
});
|
|
108
|
-
if (!r.ok)
|
|
179
|
+
if (!r.ok) {
|
|
180
|
+
// A timeout caused by the WALL CLOCK (not the operator's own per-call
|
|
181
|
+
// setting) is this loop doing exactly what it's supposed to, not an
|
|
182
|
+
// unexpected error — report it the same way the pre-flight check
|
|
183
|
+
// above does, rather than as a hard failure.
|
|
184
|
+
if (r.code === 'ollama-timeout' && deadlineWasBinding) {
|
|
185
|
+
return { ok: true, finalText: null, iterations: iteration, toolCalls: toolCallLog, stopReason: 'wall-clock-timeout' };
|
|
186
|
+
}
|
|
187
|
+
return { ok: false, code: AGENT_LOOP_ERROR.FAILED, reason: r.reason || r.code };
|
|
188
|
+
}
|
|
109
189
|
|
|
110
190
|
const toolCalls = r.result.toolCalls || [];
|
|
111
191
|
if (toolCalls.length === 0) {
|
|
@@ -52,7 +52,7 @@ export const FIX_PROPOSAL_ERROR = Object.freeze({
|
|
|
52
52
|
* through the same redaction pipeline llm-validator/index.js's renderPrompt
|
|
53
53
|
* uses, and is framed as data the model must never treat as instructions.
|
|
54
54
|
*/
|
|
55
|
-
export function buildFixPrompt(finding, fileContent, scanRoot) {
|
|
55
|
+
export function buildFixPrompt(finding, fileContent, scanRoot, rejectionFeedback) {
|
|
56
56
|
const sterileContent = redactPayload({ text: String(fileContent || ''), filePath: finding.file, scanRoot }).text;
|
|
57
57
|
return [
|
|
58
58
|
'You are a security patch-synthesis component. You PROPOSE a fix; a separate',
|
|
@@ -71,6 +71,19 @@ export function buildFixPrompt(finding, fileContent, scanRoot) {
|
|
|
71
71
|
sterileContent,
|
|
72
72
|
'--- END-UNTRUSTED-FILE-CONTENT ---',
|
|
73
73
|
'',
|
|
74
|
+
// Adversarial-review fix (2026-09): at temperature 0 with the SAME
|
|
75
|
+
// prompt, a rejected patch would very likely just reproduce itself on
|
|
76
|
+
// retry — this is the one place cmdFix's bounded one-time retry
|
|
77
|
+
// (bin/agentic-security.js) feeds the deterministic gate's OWN rejection
|
|
78
|
+
// reason back in, so the second attempt has an actual reason to differ
|
|
79
|
+
// rather than repeating the first attempt's exact mistake.
|
|
80
|
+
...(rejectionFeedback ? [
|
|
81
|
+
'Your previous proposal for this exact finding was REJECTED by the',
|
|
82
|
+
'deterministic verification gate below. Propose a DIFFERENT fix that',
|
|
83
|
+
'avoids this specific problem — do not repeat the same patch:',
|
|
84
|
+
` ${String(rejectionFeedback).slice(0, 500)}`,
|
|
85
|
+
'',
|
|
86
|
+
] : []),
|
|
74
87
|
'Propose a minimal, targeted fix for the finding above. Reply with ONLY a',
|
|
75
88
|
'single JSON object, no other text:',
|
|
76
89
|
'{"target_file": "<must exactly equal the File given above>", ' +
|
|
@@ -94,7 +107,7 @@ function validateFixResponse(obj, { file }) {
|
|
|
94
107
|
* @returns {{ok:true, replacement, rationale, expectedSecurityEffect,
|
|
95
108
|
* testsToRun, model} | {ok:false, code, reason}}
|
|
96
109
|
*/
|
|
97
|
-
export async function proposeOllamaFix({ finding, fileContent, scanRoot, env = process.env }) {
|
|
110
|
+
export async function proposeOllamaFix({ finding, fileContent, scanRoot, env = process.env, rejectionFeedback } = {}) {
|
|
98
111
|
const resolved = resolveProvider({ role: 'fix', env });
|
|
99
112
|
if (!resolved.ok || resolved.config.provider !== 'ollama') {
|
|
100
113
|
return {
|
|
@@ -112,7 +125,7 @@ export async function proposeOllamaFix({ finding, fileContent, scanRoot, env = p
|
|
|
112
125
|
return { ok: false, code: FIX_PROPOSAL_ERROR.POLICY_BLOCKED, reason: decision.reason, egressDecision: decision };
|
|
113
126
|
}
|
|
114
127
|
|
|
115
|
-
const prompt = buildFixPrompt(finding, fileContent, scanRoot);
|
|
128
|
+
const prompt = buildFixPrompt(finding, fileContent, scanRoot, rejectionFeedback);
|
|
116
129
|
const oc = resolved.config.ollama;
|
|
117
130
|
const r = await callOllamaStructured({
|
|
118
131
|
host: resolved.config.endpoint,
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// memory to the byte.
|
|
19
19
|
|
|
20
20
|
import * as os from 'node:os';
|
|
21
|
+
import { priorOOMFor } from './oom-feedback.js';
|
|
21
22
|
|
|
22
23
|
// PRD §12/§13 FR-1203 — non-authoritative family hint from a model name.
|
|
23
24
|
// Longest/most-specific pattern first so `qwen3.5:4b` doesn't fall through to
|
|
@@ -200,7 +201,31 @@ export function evaluateMemoryAdmission({
|
|
|
200
201
|
* `{admitted:false}` with a human-readable explanation, which callers treat
|
|
201
202
|
* as "run deterministic-only" (PRD §23.4).
|
|
202
203
|
*/
|
|
203
|
-
export function recommendAdmission(
|
|
204
|
+
export function recommendAdmission(opts = {}) {
|
|
205
|
+
const result = _recommendAdmissionCore(opts);
|
|
206
|
+
// Adversarial-review fix (2026-09): a memory-admission ESTIMATE that
|
|
207
|
+
// actually caused a real OOM (ollama-provider.js's callOllamaChat now
|
|
208
|
+
// records this via oom-feedback.js) used to have no way to affect a
|
|
209
|
+
// future admission decision for the SAME model on the SAME machine — the
|
|
210
|
+
// exact same "admitted: true" would repeat forever. This does not
|
|
211
|
+
// recalibrate the underlying estimate (that needs real hardware variety
|
|
212
|
+
// one machine's observed failures can't substitute for); it attaches an
|
|
213
|
+
// honest warning so the decision is no longer presented with unqualified
|
|
214
|
+
// confidence.
|
|
215
|
+
const chosenModel = result.model || opts.requestedModel;
|
|
216
|
+
const prior = chosenModel ? priorOOMFor(chosenModel) : null;
|
|
217
|
+
if (prior) {
|
|
218
|
+
return {
|
|
219
|
+
...result,
|
|
220
|
+
priorOOMWarning: `'${chosenModel}' has previously failed with an out-of-memory error on this machine ` +
|
|
221
|
+
`(${prior.count} time${prior.count === 1 ? '' : 's'}, most recently ${new Date(prior.lastAt).toISOString()}). ` +
|
|
222
|
+
'The memory estimate below may be optimistic for your hardware.',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function _recommendAdmissionCore({ profile, freeBytes, requestedContextTokens, requestedModel } = {}) {
|
|
204
229
|
const p = MEMORY_PROFILES[profile];
|
|
205
230
|
if (!p) return { admitted: false, reason: `unknown memory profile '${profile}'` };
|
|
206
231
|
|
|
@@ -18,10 +18,21 @@
|
|
|
18
18
|
// CACHE KEY = Ollama version + model digest + model name (PRD §13.2 exactly).
|
|
19
19
|
// Digest is load-bearing: `ollama pull` replacing a tag's underlying weights
|
|
20
20
|
// must invalidate the cache even though the name/tag string is unchanged.
|
|
21
|
-
//
|
|
22
|
-
// entry — a version/digest bump makes a new key, not a stale hit on the old
|
|
23
|
-
// one. Same disk-cache directory convention as sca/sigstore-verify.js and
|
|
21
|
+
// Same disk-cache directory convention as sca/sigstore-verify.js and
|
|
24
22
|
// engine.js's OSV cache (`~/.claude/agentic-security/<name>/`).
|
|
23
|
+
//
|
|
24
|
+
// TTL + force-reprobe (adversarial-review fix, 2026-09). The key-based
|
|
25
|
+
// invalidation above is real but not complete: this module's own comment
|
|
26
|
+
// used to claim the entry is safe "forever" because the key changes when
|
|
27
|
+
// the model does — but `/api/show` doesn't expose a digest on every Ollama
|
|
28
|
+
// version (falls back to model NAME alone then, a few lines below), so a
|
|
29
|
+
// same-tag re-pull, or simply an unlucky single-trial probe the first time
|
|
30
|
+
// (see probeStructuredOutput/probeToolCalling's own single-call design),
|
|
31
|
+
// had no way to ever self-correct short of a user manually deleting a file
|
|
32
|
+
// under `~/.claude/agentic-security/`. Two independent fixes, since either
|
|
33
|
+
// alone leaves a real gap: a default TTL as a safety net for the case
|
|
34
|
+
// nobody notices, and an explicit `force` option (`models test --force`)
|
|
35
|
+
// for the case someone DOES suspect a stale answer and wants it right now.
|
|
25
36
|
|
|
26
37
|
import * as fs from 'node:fs';
|
|
27
38
|
import * as path from 'node:path';
|
|
@@ -32,18 +43,38 @@ import { capabilitiesFromFamilyHint } from './model-capabilities.js';
|
|
|
32
43
|
|
|
33
44
|
const CACHE_DIR = path.join(os.homedir(), '.claude', 'agentic-security', 'ollama-capability-cache');
|
|
34
45
|
|
|
46
|
+
// Default safety-net TTL: 30 days. Not the primary invalidation mechanism
|
|
47
|
+
// (the key is) — a backstop for the cases the key can't see: a same-tag
|
|
48
|
+
// re-pull on an Ollama version that doesn't expose a digest, or a single
|
|
49
|
+
// unlucky probe trial that happened to pass/fail against the model's true
|
|
50
|
+
// behavior. Overridable for anyone who wants a tighter or looser bound.
|
|
51
|
+
export const DEFAULT_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
52
|
+
|
|
35
53
|
function _ensureCacheDir() { try { fs.mkdirSync(CACHE_DIR, { recursive: true }); } catch {} }
|
|
36
54
|
function _cacheKey(ollamaVersion, modelDigest, modelName) {
|
|
37
55
|
return crypto.createHash('sha256').update(`${ollamaVersion}::${modelDigest}::${modelName}`).digest('hex');
|
|
38
56
|
}
|
|
39
57
|
function _cachePath(key) { return path.join(CACHE_DIR, key + '.json'); }
|
|
40
58
|
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
/**
|
|
60
|
+
* @returns {object|null} the cached probe RESULT (not the envelope), or
|
|
61
|
+
* `null` on a miss, a parse failure, OR an entry older than `ttlMs`.
|
|
62
|
+
*/
|
|
63
|
+
function _readProbeCache(key, ttlMs) {
|
|
64
|
+
let envelope;
|
|
65
|
+
try { envelope = JSON.parse(fs.readFileSync(_cachePath(key), 'utf8')); } catch { return null; }
|
|
66
|
+
// Backward-compatible with a pre-TTL cache file that was just the bare
|
|
67
|
+
// result object (no `probedAt`) — treat an entry with no timestamp as
|
|
68
|
+
// fresh rather than discarding every cache written before this fix.
|
|
69
|
+
if (envelope && typeof envelope === 'object' && 'probedAt' in envelope && 'result' in envelope) {
|
|
70
|
+
if (Number.isFinite(ttlMs) && ttlMs > 0 && Date.now() - envelope.probedAt > ttlMs) return null;
|
|
71
|
+
return envelope.result;
|
|
72
|
+
}
|
|
73
|
+
return envelope;
|
|
43
74
|
}
|
|
44
75
|
function _writeProbeCache(key, value) {
|
|
45
76
|
_ensureCacheDir();
|
|
46
|
-
try { fs.writeFileSync(_cachePath(key), JSON.stringify(value)); } catch {}
|
|
77
|
+
try { fs.writeFileSync(_cachePath(key), JSON.stringify({ probedAt: Date.now(), result: value })); } catch {}
|
|
47
78
|
}
|
|
48
79
|
|
|
49
80
|
/**
|
|
@@ -146,9 +177,16 @@ function _mergeLayer(base, overlay, sourceFlag) {
|
|
|
146
177
|
* should use, since Layer C spends real inference time on the user's
|
|
147
178
|
* machine.
|
|
148
179
|
*
|
|
180
|
+
* `force: true` (adversarial-review fix, 2026-09 — `models test --force`)
|
|
181
|
+
* skips reading the cache — always runs a fresh probe and overwrites
|
|
182
|
+
* whatever was there. `ttlMs` (default 30 days, `DEFAULT_CACHE_TTL_MS`)
|
|
183
|
+
* bounds how long a cached entry is trusted without either; pass `0`/
|
|
184
|
+
* `Infinity` to disable the TTL safety net entirely and rely on the key
|
|
185
|
+
* alone, matching this module's original design intent.
|
|
186
|
+
*
|
|
149
187
|
* @returns {{ok:true, capabilities:object, cached:boolean} | {ok:false, code, reason}}
|
|
150
188
|
*/
|
|
151
|
-
export async function getModelCapabilities({ host, model, env = process.env, probe = false, timeouts, keepAlive } = {}) {
|
|
189
|
+
export async function getModelCapabilities({ host, model, env = process.env, probe = false, force = false, ttlMs = DEFAULT_CACHE_TTL_MS, timeouts, keepAlive } = {}) {
|
|
152
190
|
let capabilities = capabilitiesFromFamilyHint(model);
|
|
153
191
|
|
|
154
192
|
const show = await showOllamaModel({ host, model, timeouts });
|
|
@@ -169,7 +207,7 @@ export async function getModelCapabilities({ host, model, env = process.env, pro
|
|
|
169
207
|
const modelDigest = show.ok && show.details?.digest ? show.details.digest : 'unknown-digest';
|
|
170
208
|
const cacheKey = _cacheKey(ollamaVersion, modelDigest, model);
|
|
171
209
|
|
|
172
|
-
const cached = _readProbeCache(cacheKey);
|
|
210
|
+
const cached = force ? null : _readProbeCache(cacheKey, ttlMs);
|
|
173
211
|
if (cached) {
|
|
174
212
|
return { ok: true, capabilities: _mergeLayer(capabilities, cached, 'runtimeProbe'), cached: true };
|
|
175
213
|
}
|