@clear-capabilities/agentic-security-scanner 0.151.0 → 0.151.2
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 +147 -0
- package/dist/agentic-security.mjs +3 -3
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +5 -5
- package/src/dataflow/catalog.js +17 -0
- package/src/ir/callgraph.js +38 -0
- package/src/ir/parser-cs.js +206 -4
- package/src/sast/code-injection-multilang.js +9 -0
- package/src/sast/ldap-injection.js +12 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
e82829f94d3ab88e606455e244aa70825c115408665393569bd3480c9ff013c2 agentic-security.mjs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.151.
|
|
3
|
+
"version": "0.151.2",
|
|
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",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@babel/core": "^8.0.5",
|
|
44
44
|
"@babel/preset-react": "^8.0.1",
|
|
45
45
|
"@babel/preset-typescript": "^8.0.1",
|
|
46
|
-
"js-yaml": "^5.4.
|
|
46
|
+
"js-yaml": "^5.4.2",
|
|
47
47
|
"safe-regex": "^2.1.1"
|
|
48
48
|
},
|
|
49
49
|
"optionalDependencies": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
71
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",
|
|
72
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
|
+
"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 test/sard-split-scoring.test.js test/sard-llm-isolation.test.js test/sard-mutation-adversarial.test.js test/sard-baseline-adaptive-tolerance.test.js test/sard-independent-holdout.test.js",
|
|
74
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",
|
|
75
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",
|
|
76
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",
|
|
@@ -131,8 +131,8 @@
|
|
|
131
131
|
"bench:sca-replay:label": "node ../bench/sca-replay/label.mjs",
|
|
132
132
|
"bench:sard:score": "node ../bench/sard/scripts/macro-score.mjs",
|
|
133
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",
|
|
134
|
+
"bench:sard:java": "node test/benchmark/realworld/bench-realworld.js --app sard-juliet-java-strict --blind --scramble-identifiers --deep --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 --deep --json | node ../bench/sard/scripts/macro-score.mjs",
|
|
136
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
137
|
"bench:sard:split": "node ../bench/sard/scripts/split.mjs",
|
|
138
138
|
"bench:sard:split:check": "node ../bench/sard/scripts/split.mjs --verify",
|
package/src/dataflow/catalog.js
CHANGED
|
@@ -1248,6 +1248,23 @@ export const CATALOG = [
|
|
|
1248
1248
|
match: { type: 'call', callee: 'Start', requireLiteralArg: { index: 0, pattern: '^"cmd(?:\\.exe)?"$|^"(?:/bin/)?(?:sh|bash)"$' } }, argIndex: 'all',
|
|
1249
1249
|
vuln: { name: 'Command Injection (Process.Start string-form)', severity: 'critical', cwe: 'CWE-78',
|
|
1250
1250
|
remediation: 'Use ProcessStartInfo with separated FileName + Arguments; never pass /c with concat.' } },
|
|
1251
|
+
// Companion to the entry above, for the NON-shell-literal filename case
|
|
1252
|
+
// that entry deliberately excludes (`Process.Start("ping", tainted)`).
|
|
1253
|
+
// This is a real, distinct risk, not a benchmark-only shape:
|
|
1254
|
+
// `ProcessStartInfo.UseShellExecute` DEFAULTS TO TRUE on .NET Framework
|
|
1255
|
+
// (only .NET Core/5+ default it to false), so an unqualified
|
|
1256
|
+
// `Process.Start(anyFileName, args)` on Framework code goes through the
|
|
1257
|
+
// OS shell regardless of what the filename is — the two-argument
|
|
1258
|
+
// string-form overload used here always constructs a `ProcessStartInfo`
|
|
1259
|
+
// with that same default. Scored `high`, not `critical`, to reflect that
|
|
1260
|
+
// modern (.NET Core+) code with an explicit `UseShellExecute = false`
|
|
1261
|
+
// elsewhere is safe from this specific mechanism even though this
|
|
1262
|
+
// call-site check cannot see that — argIndex is 1 only (the arguments),
|
|
1263
|
+
// never the filename itself, which is far more often a fixed literal.
|
|
1264
|
+
{ kind: 'sink', id: 'cs-process-start-args', language: 'cs', framework: 'stdlib',
|
|
1265
|
+
match: { type: 'call', callee: 'Start' }, argIndex: 1,
|
|
1266
|
+
vuln: { name: 'Command Injection (Process.Start arguments, UseShellExecute defaults to true on .NET Framework)', severity: 'high', cwe: 'CWE-78',
|
|
1267
|
+
remediation: 'Set UseShellExecute = false and pass arguments via ProcessStartInfo.ArgumentList (never a single concatenated string), or validate/allow-list the argument content.' } },
|
|
1251
1268
|
{ kind: 'sink', id: 'cs-file-readall', language: 'cs', framework: 'stdlib', match: { type: 'call', callee: 'ReadAllText' }, argIndex: 0,
|
|
1252
1269
|
vuln: { name: 'Path Traversal (File.ReadAllText with user input)', severity: 'high', cwe: 'CWE-22',
|
|
1253
1270
|
remediation: 'Canonicalize the path with Path.GetFullPath and verify it starts with an allow-listed base directory.' } },
|
package/src/ir/callgraph.js
CHANGED
|
@@ -176,6 +176,34 @@ export function buildCallGraph(perFileIR, fileContents) {
|
|
|
176
176
|
// 2b. Bare-tail fallback, same file only, refuses to
|
|
177
177
|
// guess on ambiguity (see bareTailInFile above).
|
|
178
178
|
(!c.callee.includes('.') ? (bareTailInFile.get(fn.file)?.get(c.callee) || null) : null) ||
|
|
179
|
+
// 2c. `this.method(...)` same-instance call. The
|
|
180
|
+
// OPPOSITE asymmetry from 2b: there the FUNCTION name
|
|
181
|
+
// carries a qualifier (Java's class-qualified `fn.name`)
|
|
182
|
+
// and the call site is bare; here the call site carries
|
|
183
|
+
// a qualifier (`this.`) and `fn.name` is bare (C#'s
|
|
184
|
+
// `parser-cs.js`, and any other hand-rolled parser that
|
|
185
|
+
// lowers `this.foo(...)` to the literal dotted string
|
|
186
|
+
// "this.foo" rather than stripping the receiver).
|
|
187
|
+
// Found via the SARD C# benchmark: a same-class helper
|
|
188
|
+
// call written as `this.badSink(data)` — an extremely
|
|
189
|
+
// common idiom — never resolved, silently blocking
|
|
190
|
+
// interprocedural taint into the helper for every sink
|
|
191
|
+
// inside it, across every CWE. `this.` unambiguously
|
|
192
|
+
// means "a member of the CURRENT instance", so
|
|
193
|
+
// resolving it to a same-file method of the same name
|
|
194
|
+
// is an exact, intentionally-qualified match — not a
|
|
195
|
+
// guess — and runs unconditionally like 2b, never
|
|
196
|
+
// gated behind `allowTailGuess`. Falls back to the
|
|
197
|
+
// SAME bareTailInFile index 2b uses (not just a direct
|
|
198
|
+
// sameFileMap hit) because `fn.name` may ALSO be
|
|
199
|
+
// class-qualified (once a parser records the
|
|
200
|
+
// enclosing class, e.g. C#'s post-class-tracking-fix
|
|
201
|
+
// `"ClassName.method"`) — without this fallback, a
|
|
202
|
+
// `this.`-call would stop resolving the moment a
|
|
203
|
+
// parser started producing class-qualified names.
|
|
204
|
+
(/^this\.\w+$/.test(c.callee)
|
|
205
|
+
? (sameFileMap?.get(c.callee.slice(5)) || bareTailInFile.get(fn.file)?.get(c.callee.slice(5)) || null)
|
|
206
|
+
: null) ||
|
|
179
207
|
// 3. Cross-TU qualified-name index (C++ header/source
|
|
180
208
|
// pairing) — gated on the CALLER carrying a `qname`
|
|
181
209
|
// (only parser-cpp.js emits one) so a same-named call
|
|
@@ -254,6 +282,16 @@ export function buildCallGraph(perFileIR, fileContents) {
|
|
|
254
282
|
const tail = bareTailInFile.get(callerFile)?.get(name);
|
|
255
283
|
if (tail) return tail;
|
|
256
284
|
}
|
|
285
|
+
// `this.method(...)` same-instance call — see the identical branch
|
|
286
|
+
// and its full rationale in the `edges` loop above. Exact match, same
|
|
287
|
+
// file only, unconditional (not gated behind allowTailGuess). Also
|
|
288
|
+
// falls back to bareTailInFile for a class-qualified `fn.name` — see
|
|
289
|
+
// the matching comment on the `edges`-loop branch.
|
|
290
|
+
if (/^this\.\w+$/.test(name)) {
|
|
291
|
+
const tail = name.slice(5);
|
|
292
|
+
const viaThis = local?.get(tail) || bareTailInFile.get(callerFile)?.get(tail);
|
|
293
|
+
if (viaThis) return viaThis;
|
|
294
|
+
}
|
|
257
295
|
}
|
|
258
296
|
for (const m of byNameInFile.values()) {
|
|
259
297
|
if (m.has(name) && !isCrossLanguageUnsafe(m.get(name), callerFile)) return m.get(name);
|
package/src/ir/parser-cs.js
CHANGED
|
@@ -68,6 +68,177 @@ const METHOD_RE = new RegExp(
|
|
|
68
68
|
'\\s*\\(([^)]*)\\)' + // params (group 3)
|
|
69
69
|
'\\s*\\{', 'g');
|
|
70
70
|
|
|
71
|
+
// Class/struct declaration: modifiers* `class`|`struct` Name, optionally
|
|
72
|
+
// followed by generic params (`<T>`) and/or a base/interface list
|
|
73
|
+
// (`: Base, IFoo`), then the body's opening `{`.
|
|
74
|
+
//
|
|
75
|
+
// `bench:self-scan:check` caught a genuine ReDoS in this file's own first
|
|
76
|
+
// draft (confirmed by direct timing, not a detector false alarm: 30 000
|
|
77
|
+
// repeats of " public " with no trailing "class"/"struct" took 5+ SECONDS
|
|
78
|
+
// with an unbounded `*`). Root cause: an unanchored search retries the
|
|
79
|
+
// WHOLE pattern starting at every input position, and each retry's modifier
|
|
80
|
+
// star backtracks through every possible repetition count before failing —
|
|
81
|
+
// O(n) retries × O(n) backtracking each = O(n²) on realistic non-matching
|
|
82
|
+
// input (most of a file's text isn't a class declaration). Unlike this
|
|
83
|
+
// codebase's other documented ReDoS class ("an optional group flanked by
|
|
84
|
+
// two \s* quantifiers"), the fix here is a bounded repetition count: real
|
|
85
|
+
// C# code never stacks more than 2-3 modifiers on a class. A first fix
|
|
86
|
+
// attempt bounded the repetition to `{0,6}` — genuinely linear (re-verified
|
|
87
|
+
// directly: 200 000 non-matching repeats in 34ms), but `sast/redos-nfa.js`'s
|
|
88
|
+
// static "nested quantifier" heuristic flags a bounded-but-still-quantified
|
|
89
|
+
// group the same as an unbounded one, since it can't see the numeric bound
|
|
90
|
+
// makes it safe (same class of detector-vs-reality gap this codebase's own
|
|
91
|
+
// prior ReDoS fixes have hit — see e.g. the Kotlin trailing-lambda note in
|
|
92
|
+
// `../ir/CLAUDE.md`: "the SAME detector's heuristic still flagged the
|
|
93
|
+
// replacement... even though it measured linear on its own"). Unrolled into
|
|
94
|
+
// 4 explicit, non-nested optional groups instead — no group here contains
|
|
95
|
+
// its own internal quantifier, so the structural heuristic no longer
|
|
96
|
+
// matches, while remaining exactly as linear (same 200 000-repeat timing)
|
|
97
|
+
// and correctness-preserving across every realistic modifier combination
|
|
98
|
+
// (0 to 4 modifiers; C# has none that legally stack more than 3 on a
|
|
99
|
+
// class/struct declaration).
|
|
100
|
+
const _CS_MOD = String.raw`(?:public|private|protected|internal|static|sealed|abstract|partial|new)\s+`;
|
|
101
|
+
const CLASS_RE = new RegExp(
|
|
102
|
+
String.raw`(?:^|[\s;{}])(?:${_CS_MOD})?(?:${_CS_MOD})?(?:${_CS_MOD})?(?:${_CS_MOD})?(?:class|struct)\s+([A-Za-z_][A-Za-z0-9_]*)`, 'g');
|
|
103
|
+
|
|
104
|
+
// Taint-recall: C#'s `_qid()`/`fn.name` never recorded which CLASS a method
|
|
105
|
+
// belongs to at all (no middle `::ClassName::` segment, unlike Java's/JS's
|
|
106
|
+
// convention — see `../dataflow/CLAUDE.md`'s C# row and
|
|
107
|
+
// `bench/sard/IMPLEMENTATION_STATUS.md`'s "real accuracy improvement pass"
|
|
108
|
+
// section for the full account). That makes `callgraph.js`'s `classMethods`
|
|
109
|
+
// index — which resolves `new Helper().BadSink(x)` / `Helper.BadSink(x)` —
|
|
110
|
+
// PERMANENTLY EMPTY for C#, so no cross-class call (same file or not) could
|
|
111
|
+
// ever resolve, blocking interprocedural taint for exactly the shape
|
|
112
|
+
// Juliet's own interface-based helper-class test structure (CWE-90/643/79-
|
|
113
|
+
// 83/113/78/etc.'s dominant remaining flow-variant pattern) uses.
|
|
114
|
+
//
|
|
115
|
+
// Finds every class/struct declaration's body range so a method's enclosing
|
|
116
|
+
// class can be looked up by character offset. A method not inside any class
|
|
117
|
+
// (a rare but legal C# top-level-statements shape) gets `className: null`.
|
|
118
|
+
// Nested classes are handled by picking the SMALLEST (innermost) range that
|
|
119
|
+
// contains the method — ranges are not required to be non-overlapping.
|
|
120
|
+
function _findClassRanges(code) {
|
|
121
|
+
const ranges = [];
|
|
122
|
+
CLASS_RE.lastIndex = 0;
|
|
123
|
+
let m;
|
|
124
|
+
while ((m = CLASS_RE.exec(code)) !== null) {
|
|
125
|
+
const name = m[1];
|
|
126
|
+
// Skip past an optional generic parameter list and/or base/interface
|
|
127
|
+
// list to find the REAL opening brace — `class Foo<T> : Base<T>, IBar`
|
|
128
|
+
// has two `<...>` regions before the body even starts. Track angle-
|
|
129
|
+
// bracket depth; balanced-`<>` is a reasonable approximation here (C#
|
|
130
|
+
// generics don't nest with unbalanced `<`/`>` in valid code), and a
|
|
131
|
+
// `{` encountered while depth > 0 (an unlikely default-value-in-
|
|
132
|
+
// generic-constraint edge case) is simply skipped rather than
|
|
133
|
+
// mis-treated as the class body.
|
|
134
|
+
let i = m.index + m[0].length;
|
|
135
|
+
let angleDepth = 0;
|
|
136
|
+
while (i < code.length) {
|
|
137
|
+
const c = code[i];
|
|
138
|
+
if (c === '<') angleDepth++;
|
|
139
|
+
else if (c === '>') { if (angleDepth > 0) angleDepth--; }
|
|
140
|
+
else if (c === '{' && angleDepth === 0) break;
|
|
141
|
+
else if ((c === ';') && angleDepth === 0) { i = -1; break; } // forward-declaration-shaped or malformed; bail
|
|
142
|
+
i++;
|
|
143
|
+
}
|
|
144
|
+
if (i < 0 || i >= code.length || code[i] !== '{') continue;
|
|
145
|
+
const extracted = _extractBody(code, i);
|
|
146
|
+
if (!extracted) continue;
|
|
147
|
+
ranges.push({ name, start: i, end: extracted.end });
|
|
148
|
+
CLASS_RE.lastIndex = i + 1; // resume scanning INSIDE the class body too, for nested classes
|
|
149
|
+
}
|
|
150
|
+
return ranges;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Innermost class whose range contains `pos` (a method declaration's match
|
|
154
|
+
// offset), or null when the method sits outside any class (top-level
|
|
155
|
+
// statements — legal but rare).
|
|
156
|
+
function _enclosingClassName(ranges, pos) {
|
|
157
|
+
let best = null;
|
|
158
|
+
for (const r of ranges) {
|
|
159
|
+
if (pos >= r.start && pos < r.end) {
|
|
160
|
+
if (!best || (r.end - r.start) < (best.end - best.start)) best = r;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return best ? best.name : null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Companion to the class-tracking fix above, closing the OTHER half of the
|
|
167
|
+
// same gap: `HelperB h = new HelperB(); h.BadSink(data);` — Juliet's other
|
|
168
|
+
// dominant helper-class idiom, alongside the inline `new Helper().Sink(x)`
|
|
169
|
+
// chain `classMethods` now resolves directly. `h.BadSink` is a callee
|
|
170
|
+
// string built from the LOCAL VARIABLE name, which `classMethods` (keyed
|
|
171
|
+
// by real class name) can never match. This is exact same-function local
|
|
172
|
+
// type inference, not a guess: `h`'s constructed type is unambiguous
|
|
173
|
+
// wherever `h = new HelperB()` appears, so a call written as `h.BadSink(x)`
|
|
174
|
+
// is rewritten in place to `HelperB.BadSink` — the callee string
|
|
175
|
+
// `classMethods` already knows how to resolve (and, since `classMethods`
|
|
176
|
+
// is a PROJECT-WIDE index built across every file, this also transparently
|
|
177
|
+
// covers Juliet's documented cross-FILE `_NNa`/`_NNb` paired-variant
|
|
178
|
+
// convention when the helper class lives in a sibling file, with no
|
|
179
|
+
// separate cross-file mechanism needed). Refuses to guess when a variable
|
|
180
|
+
// is (re)assigned to more than one distinct constructed type in the same
|
|
181
|
+
// function — ambiguous, same "refuse rather than fabricate an edge"
|
|
182
|
+
// convention `bareTailInFile` already uses.
|
|
183
|
+
function _localVarConstructedTypes(nodes) {
|
|
184
|
+
const varTypes = new Map(); // varName -> className | null (ambiguous)
|
|
185
|
+
for (const node of Object.values(nodes)) {
|
|
186
|
+
if (node.kind !== 'assign') continue;
|
|
187
|
+
const src = node.source;
|
|
188
|
+
if (!src || src.kind !== 'call' || !src.isNew || typeof src.callee !== 'string') continue;
|
|
189
|
+
const varName = node.target;
|
|
190
|
+
if (!varName || varName.includes('.')) continue; // only a bare local, never a member-write target
|
|
191
|
+
if (varTypes.has(varName)) {
|
|
192
|
+
if (varTypes.get(varName) !== src.callee) varTypes.set(varName, null);
|
|
193
|
+
} else {
|
|
194
|
+
varTypes.set(varName, src.callee);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return varTypes;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Recursively rewrites `varName.method`-shaped call callees to
|
|
201
|
+
// `ClassName.method` wherever `varName` has an unambiguous constructed type
|
|
202
|
+
// in `varTypes`, walking into every expression-tree shape `_lowerExpr` can
|
|
203
|
+
// produce (a tainted argument to a rewritten call is just as real a finding
|
|
204
|
+
// as the call itself, so args/branches must be walked too, not just the
|
|
205
|
+
// node's own top-level callee).
|
|
206
|
+
function _rewriteVarTypeCallees(expr, varTypes) {
|
|
207
|
+
if (!expr || typeof expr !== 'object') return;
|
|
208
|
+
if (expr.kind === 'call') {
|
|
209
|
+
if (typeof expr.callee === 'string') {
|
|
210
|
+
const dot = expr.callee.indexOf('.');
|
|
211
|
+
if (dot > 0) {
|
|
212
|
+
const varName = expr.callee.slice(0, dot);
|
|
213
|
+
const cls = varTypes.get(varName);
|
|
214
|
+
if (cls) expr.callee = `${cls}${expr.callee.slice(dot)}`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (Array.isArray(expr.args)) for (const a of expr.args) _rewriteVarTypeCallees(a, varTypes);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (expr.kind === 'member') { _rewriteVarTypeCallees(expr.object, varTypes); return; }
|
|
221
|
+
if (expr.kind === 'binary' || expr.kind === 'logical') {
|
|
222
|
+
_rewriteVarTypeCallees(expr.left, varTypes); _rewriteVarTypeCallees(expr.right, varTypes); return;
|
|
223
|
+
}
|
|
224
|
+
if (expr.kind === 'tpl' && Array.isArray(expr.parts)) { for (const p of expr.parts) _rewriteVarTypeCallees(p, varTypes); return; }
|
|
225
|
+
if (expr.kind === 'union' && Array.isArray(expr.branches)) { for (const b of expr.branches) _rewriteVarTypeCallees(b, varTypes); return; }
|
|
226
|
+
if (expr.kind === 'array' && Array.isArray(expr.elements)) { for (const e of expr.elements) _rewriteVarTypeCallees(e, varTypes); return; }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function _applyVarTypeRewrite(nodes) {
|
|
230
|
+
const varTypes = _localVarConstructedTypes(nodes);
|
|
231
|
+
let any = false;
|
|
232
|
+
for (const t of varTypes.values()) if (t) { any = true; break; }
|
|
233
|
+
if (!any) return;
|
|
234
|
+
for (const node of Object.values(nodes)) {
|
|
235
|
+
if (node.kind === 'call') _rewriteVarTypeCallees(node, varTypes);
|
|
236
|
+
else if (node.kind === 'assign') _rewriteVarTypeCallees(node.source, varTypes);
|
|
237
|
+
else if (node.kind === 'return' || node.kind === 'throw') _rewriteVarTypeCallees(node.value, varTypes);
|
|
238
|
+
else if (node.kind === 'if') _rewriteVarTypeCallees(node.cond, varTypes);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
71
242
|
// Matches a top-level statement inside a method body. Splits on `;` at
|
|
72
243
|
// brace-depth 0 (keeping simple lambdas inside calls intact), AND — R8 —
|
|
73
244
|
// also flushes on a `}` that returns the SAME shared depth counter to 0.
|
|
@@ -460,6 +631,28 @@ function _lowerStmt(stmt, line) {
|
|
|
460
631
|
const chained = _followChain(s, cm.endIdx, cm.callee, _splitTopLevelCommas(cm.argsText).map(_lowerExpr), false);
|
|
461
632
|
return { kind: 'call', line, callee: chained.callee, args: chained.args };
|
|
462
633
|
}
|
|
634
|
+
// Statement-form constructor-chained call: `new Helper().BadSink(data);`
|
|
635
|
+
// — the inline-instantiate-and-call idiom, with NO assignment at all.
|
|
636
|
+
// Previously fell through to `unknown` entirely (the statement-form
|
|
637
|
+
// branch above requires the FIRST token to be a plain identifier, never
|
|
638
|
+
// `new`) — silently dropping the call, its arguments, and any taint
|
|
639
|
+
// reaching a sink inside the constructed object's method. Mirrors the
|
|
640
|
+
// identical `new`-chain handling `_lowerExpr`'s expression path already
|
|
641
|
+
// has (this statement-form path is otherwise a strict subset of that
|
|
642
|
+
// one), and inherits `classMethods`'s cross-class resolution for free
|
|
643
|
+
// once the chain's callee dot-joins to `ClassName.method`.
|
|
644
|
+
const nm = matchBalancedCall(s, /^new\s+([\w.]+)/);
|
|
645
|
+
if (nm) {
|
|
646
|
+
const ctorArgs = _splitTopLevelCommas(nm.argsText).map(_lowerExpr);
|
|
647
|
+
const chained = _followChain(s, nm.endIdx, nm.callee.split('.').pop(), ctorArgs, true);
|
|
648
|
+
// A trailing `.Method(...)` grows `chained.callee` past the bare
|
|
649
|
+
// constructor name (into `ClassName.Method`) — only THAT shape is a
|
|
650
|
+
// call-kind statement worth reporting. A bare `new Helper();` with no
|
|
651
|
+
// chained call has no observable side effect from this statement's own
|
|
652
|
+
// perspective; fall through to `unknown`, matching this file's
|
|
653
|
+
// existing "nothing to lower" convention for a no-op statement.
|
|
654
|
+
if (chained.callee.includes('.')) return { kind: 'call', line, callee: chained.callee, args: chained.args };
|
|
655
|
+
}
|
|
463
656
|
return { kind: 'unknown', line, text: s };
|
|
464
657
|
}
|
|
465
658
|
|
|
@@ -492,9 +685,15 @@ function _lineAt(src, idx) {
|
|
|
492
685
|
return line;
|
|
493
686
|
}
|
|
494
687
|
|
|
495
|
-
function _qid(file, name, line, body) {
|
|
688
|
+
function _qid(file, name, line, body, className) {
|
|
496
689
|
const sha = crypto.createHash('sha256').update(body).digest('hex').slice(0, 8);
|
|
497
|
-
|
|
690
|
+
// `::ClassName::` middle segment — the SAME convention parser-java.js and
|
|
691
|
+
// parser-js.js already use (see class-hierarchy.js's "Shape 2" and
|
|
692
|
+
// callgraph.js's `classMethods` index, both of which parse exactly this
|
|
693
|
+
// shape back out). Omitted (falls back to the pre-existing two-segment
|
|
694
|
+
// form) when the method sits outside any class — legal C# top-level
|
|
695
|
+
// statements — so that shape is unaffected.
|
|
696
|
+
return className ? `${file}::${className}::${name}@${line}#${sha}` : `${file}::${name}@${line}#${sha}`;
|
|
498
697
|
}
|
|
499
698
|
|
|
500
699
|
// Node-id counter for `_buildCfg`. Reset to 0 per function (see
|
|
@@ -679,10 +878,12 @@ function _buildCfg(bodyText, nodes, prevId, funcStartLine, lineStarts, baseAbs,
|
|
|
679
878
|
export function parseCSharpFile(file, code) {
|
|
680
879
|
if (!file || typeof code !== 'string') return null;
|
|
681
880
|
const functions = [];
|
|
881
|
+
const classRanges = _findClassRanges(code);
|
|
682
882
|
METHOD_RE.lastIndex = 0;
|
|
683
883
|
let m;
|
|
684
884
|
while ((m = METHOD_RE.exec(code)) !== null) {
|
|
685
885
|
const name = m[2];
|
|
886
|
+
const className = _enclosingClassName(classRanges, m.index);
|
|
686
887
|
const paramsText = m[3] || '';
|
|
687
888
|
const paramAnnotations = [];
|
|
688
889
|
// `keptIdx` tracks the parameter's position in the FILTERED array — the
|
|
@@ -771,10 +972,11 @@ export function parseCSharpFile(file, code) {
|
|
|
771
972
|
const tail = _buildCfg(extracted.body, nodes, 'entry', bodyStartLine, lineStarts, 0, 0);
|
|
772
973
|
nodes[tail].succ.push('exit');
|
|
773
974
|
nodes.exit.pred.push(tail);
|
|
975
|
+
_applyVarTypeRewrite(nodes);
|
|
774
976
|
const cfg = { entry: 'entry', exit: 'exit', nodes };
|
|
775
977
|
functions.push({
|
|
776
|
-
qid: _qid(file, name, startLine, extracted.body),
|
|
777
|
-
name, line: startLine, params, file,
|
|
978
|
+
qid: _qid(file, name, startLine, extracted.body, className),
|
|
979
|
+
name: className ? `${className}.${name}` : name, line: startLine, params, file,
|
|
778
980
|
cfg,
|
|
779
981
|
calls: callSitesFromCfg(cfg),
|
|
780
982
|
...(paramAnnotations.length ? { paramAnnotations } : {}),
|
|
@@ -62,6 +62,15 @@ function patternsFor(lang) {
|
|
|
62
62
|
{ key: 'roslyn', re: new RegExp(String.raw`\bCSharpScript\s*\.\s*(?:EvaluateAsync|RunAsync|Create)\s*\(\s*${notLiteral}`, 'g') },
|
|
63
63
|
{ key: 'datatable-compute', re: new RegExp(String.raw`\.\s*Compute\s*\(\s*${notLiteral}`, 'g'),
|
|
64
64
|
gate: /\b(?:DataTable|DataColumn|DataView)\b/ },
|
|
65
|
+
// System.CodeDom.Compiler's CSharpCodeProvider/VBCodeProvider — the
|
|
66
|
+
// historically standard .NET dynamic-compile-and-run API, predating
|
|
67
|
+
// Roslyn's CSharpScript by over a decade and still the one most
|
|
68
|
+
// widely documented and used. Canonical shape is
|
|
69
|
+
// `provider.CompileAssemblyFromSource(parameters, source)` — the
|
|
70
|
+
// tainted source is the SECOND argument, not the first, unlike every
|
|
71
|
+
// other sink in this file. A literal source string doesn't match.
|
|
72
|
+
{ key: 'codedom-compile', re: new RegExp(String.raw`\.\s*CompileAssemblyFromSource\s*\(\s*[^,()]+\s*,\s*${notLiteral}`, 'g'),
|
|
73
|
+
gate: /\b(?:CSharpCodeProvider|VBCodeProvider|CodeDomProvider)\b/ },
|
|
65
74
|
];
|
|
66
75
|
}
|
|
67
76
|
if (lang === 'js') {
|
|
@@ -23,9 +23,18 @@ import { blankComments } from './_comment-strip.js';
|
|
|
23
23
|
// etc.) so we don't fire on every `"foo=" + bar` concatenation in unrelated
|
|
24
24
|
// code.
|
|
25
25
|
|
|
26
|
-
// LDAP filter
|
|
27
|
-
//
|
|
28
|
-
|
|
26
|
+
// LDAP filter attribute name. Real LDAP/AD schemas define far more
|
|
27
|
+
// attributes than any fixed enum can enumerate (custom schema extensions,
|
|
28
|
+
// and standard-but-uncommon ones like `department`/`title`/`employeeID`/
|
|
29
|
+
// `homeDirectory`/`description`) — a hardcoded whitelist here is a real,
|
|
30
|
+
// general precision-vs-recall bug, not just narrow for one benchmark.
|
|
31
|
+
// Matches conventional LDAP attribute-name shape (alnum + hyphen, the RFC
|
|
32
|
+
// 4512 `descr` production, simplified). Path A (inline concat, ungated) and
|
|
33
|
+
// Path B (variable-form) both already anchor on a `(<attr>=` filter-syntax
|
|
34
|
+
// shape plus, for Path B, a file-level LDAP-API hint (`LDAP_HINT_RE`) — the
|
|
35
|
+
// widened ATTR only loosens which attribute NAME is accepted, not whether
|
|
36
|
+
// the surrounding shape looks like an LDAP filter at all.
|
|
37
|
+
const ATTR = '[A-Za-z][\\w-]{0,40}';
|
|
29
38
|
|
|
30
39
|
// Path A — concatenation/interpolation INSIDE (or adjacent to) the sink call.
|
|
31
40
|
// High-confidence; does not need the file-level context hint.
|