@clear-capabilities/agentic-security-scanner 0.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1580 -0
- package/bin/.agentic-security/findings.json +1577 -0
- package/bin/.agentic-security/last-scan.json +1577 -0
- package/bin/.agentic-security/last-scan.json.sig +1 -0
- package/bin/.agentic-security/scan-history.json +465 -0
- package/bin/.agentic-security/streak.json +25 -0
- package/bin/agentic-security-audit.js +198 -0
- package/bin/agentic-security-consistency.js +80 -0
- package/bin/agentic-security-diff.js +136 -0
- package/bin/agentic-security-lsp.js +12 -0
- package/bin/agentic-security-mcp.js +40 -0
- package/bin/agentic-security-rule.js +153 -0
- package/bin/agentic-security.js +1683 -0
- package/dist/117.index.js +207 -0
- package/dist/178.index.js +250 -0
- package/dist/218.index.js +793 -0
- package/dist/227.index.js +192 -0
- package/dist/301.index.js +167 -0
- package/dist/384.index.js +18 -0
- package/dist/476.index.js +126 -0
- package/dist/513.index.js +373 -0
- package/dist/520.index.js +13 -0
- package/dist/601.index.js +1038 -0
- package/dist/634.index.js +1892 -0
- package/dist/637.index.js +216 -0
- package/dist/660.index.js +131 -0
- package/dist/675.index.js +451 -0
- package/dist/826.index.js +188 -0
- package/dist/830.index.js +133 -0
- package/dist/agentic-security.mjs +272 -0
- package/dist/agentic-security.mjs.sha256 +1 -0
- package/dist/calibration-seed.json +27 -0
- package/package.json +77 -0
- package/src/.agentic-security/findings.json +80844 -0
- package/src/.agentic-security/last-scan.json +80844 -0
- package/src/.agentic-security/last-scan.json.sig +1 -0
- package/src/.agentic-security/scan-history.json +8408 -0
- package/src/.agentic-security/streak.json +26 -0
- package/src/badge.js +188 -0
- package/src/compare.js +203 -0
- package/src/dataflow/.agentic-security/findings.json +3487 -0
- package/src/dataflow/.agentic-security/last-scan.json +3487 -0
- package/src/dataflow/.agentic-security/last-scan.json.sig +1 -0
- package/src/dataflow/.agentic-security/scan-history.json +735 -0
- package/src/dataflow/.agentic-security/streak.json +24 -0
- package/src/dataflow/CLAUDE.md +38 -0
- package/src/dataflow/access-paths.js +172 -0
- package/src/dataflow/async-sequencing.js +177 -0
- package/src/dataflow/backward.js +201 -0
- package/src/dataflow/catalog-expanded.js +485 -0
- package/src/dataflow/catalog.js +659 -0
- package/src/dataflow/cross-repo.js +219 -0
- package/src/dataflow/engine.js +588 -0
- package/src/dataflow/exception-flow.js +116 -0
- package/src/dataflow/exploit-prover.js +187 -0
- package/src/dataflow/higher-order.js +221 -0
- package/src/dataflow/ifds.js +347 -0
- package/src/dataflow/implicit-flow.js +129 -0
- package/src/dataflow/incremental.js +229 -0
- package/src/dataflow/index.js +181 -0
- package/src/dataflow/numeric-domain.js +192 -0
- package/src/dataflow/path-feasibility.js +114 -0
- package/src/dataflow/points-to.js +337 -0
- package/src/dataflow/polyglot.js +190 -0
- package/src/dataflow/proven-clean.js +159 -0
- package/src/dataflow/receiver-context.js +76 -0
- package/src/dataflow/sanitizer-proof.js +154 -0
- package/src/dataflow/soft-taint.js +140 -0
- package/src/dataflow/string-domain.js +234 -0
- package/src/dataflow/stub-aware-filter.js +100 -0
- package/src/dataflow/summaries.js +132 -0
- package/src/dataflow/symbolic-exec.js +238 -0
- package/src/dataflow/tabulation.js +135 -0
- package/src/engine.js +7763 -0
- package/src/history-scan.js +229 -0
- package/src/index.js +3 -0
- package/src/integrations/.agentic-security/findings.json +1504 -0
- package/src/integrations/.agentic-security/last-scan.json +1504 -0
- package/src/integrations/.agentic-security/scan-history.json +40 -0
- package/src/integrations/.agentic-security/streak.json +21 -0
- package/src/integrations/index.js +321 -0
- package/src/integrations/tickets.js +200 -0
- package/src/ir/.agentic-security/findings.json +3036 -0
- package/src/ir/.agentic-security/last-scan.json +3036 -0
- package/src/ir/.agentic-security/last-scan.json.sig +1 -0
- package/src/ir/.agentic-security/scan-history.json +364 -0
- package/src/ir/.agentic-security/streak.json +23 -0
- package/src/ir/CLAUDE.md +172 -0
- package/src/ir/callgraph.js +73 -0
- package/src/ir/class-hierarchy.js +195 -0
- package/src/ir/index.js +152 -0
- package/src/ir/parser-cs.js +260 -0
- package/src/ir/parser-java.js +286 -0
- package/src/ir/parser-js.js +413 -0
- package/src/ir/parser-kt.js +258 -0
- package/src/ir/parser-py-cst.js +136 -0
- package/src/ir/parser-py.helper.py +501 -0
- package/src/ir/parser-py.js +312 -0
- package/src/ir/ssa.js +315 -0
- package/src/ir/type-stubs.js +288 -0
- package/src/leaderboard.js +152 -0
- package/src/llm-validator/.agentic-security/findings.json +1891 -0
- package/src/llm-validator/.agentic-security/last-scan.json +1891 -0
- package/src/llm-validator/.agentic-security/last-scan.json.sig +1 -0
- package/src/llm-validator/.agentic-security/scan-history.json +168 -0
- package/src/llm-validator/.agentic-security/streak.json +20 -0
- package/src/llm-validator/consistency.js +141 -0
- package/src/llm-validator/index.js +437 -0
- package/src/lsp/.agentic-security/findings.json +28 -0
- package/src/lsp/.agentic-security/last-scan.json +28 -0
- package/src/lsp/.agentic-security/scan-history.json +79 -0
- package/src/lsp/.agentic-security/streak.json +22 -0
- package/src/lsp/server.js +275 -0
- package/src/mcp/.agentic-security/findings.json +8358 -0
- package/src/mcp/.agentic-security/last-scan.json +8358 -0
- package/src/mcp/.agentic-security/last-scan.json.sig +1 -0
- package/src/mcp/.agentic-security/scan-history.json +1125 -0
- package/src/mcp/.agentic-security/streak.json +22 -0
- package/src/mcp/CLAUDE.md +54 -0
- package/src/mcp/audit.js +136 -0
- package/src/mcp/redact.js +75 -0
- package/src/mcp/server.js +158 -0
- package/src/mcp/stdio.js +83 -0
- package/src/mcp/tools.js +940 -0
- package/src/mcp/validate.js +49 -0
- package/src/personality.js +164 -0
- package/src/poc-video.js +239 -0
- package/src/posture/.agentic-security/findings.json +51239 -0
- package/src/posture/.agentic-security/last-scan.json +51239 -0
- package/src/posture/.agentic-security/last-scan.json.sig +1 -0
- package/src/posture/.agentic-security/scan-history.json +5557 -0
- package/src/posture/.agentic-security/streak.json +24 -0
- package/src/posture/CLAUDE.md +42 -0
- package/src/posture/adversarial-self-test.js +114 -0
- package/src/posture/adversary-agent.js +204 -0
- package/src/posture/agents-memory.js +135 -0
- package/src/posture/ai-code-fingerprint.js +171 -0
- package/src/posture/aibom.js +284 -0
- package/src/posture/api-inventory.js +96 -0
- package/src/posture/attack-playbooks.js +305 -0
- package/src/posture/auditor-agent.js +115 -0
- package/src/posture/auth-posture-import.js +135 -0
- package/src/posture/baseline-compare.js +114 -0
- package/src/posture/blast-radius.js +836 -0
- package/src/posture/bounty-prediction.js +141 -0
- package/src/posture/business-logic.js +239 -0
- package/src/posture/calibration-drift.js +93 -0
- package/src/posture/calibration-seed.json +27 -0
- package/src/posture/calibration.js +204 -0
- package/src/posture/clustering.js +75 -0
- package/src/posture/concurrency-checker.js +265 -0
- package/src/posture/confidence.js +65 -0
- package/src/posture/container-runtime.js +149 -0
- package/src/posture/counterfactual.js +109 -0
- package/src/posture/cross-lang-graphql.js +165 -0
- package/src/posture/cross-lang-grpc.js +166 -0
- package/src/posture/cross-lang-meta.js +101 -0
- package/src/posture/cross-lang-openapi.js +187 -0
- package/src/posture/cross-lang-orm.js +153 -0
- package/src/posture/cross-lang-queues.js +210 -0
- package/src/posture/crown-jewels.js +110 -0
- package/src/posture/custom-rules.js +361 -0
- package/src/posture/cve-alert-daemon.js +433 -0
- package/src/posture/cve-lookup.js +129 -0
- package/src/posture/dead-code.js +430 -0
- package/src/posture/defender-agent.js +158 -0
- package/src/posture/deploy-platform.js +204 -0
- package/src/posture/detector-fuzz.js +61 -0
- package/src/posture/deterministic.js +99 -0
- package/src/posture/drift.js +165 -0
- package/src/posture/epss.js +156 -0
- package/src/posture/exploitability-probability.js +212 -0
- package/src/posture/exploitability.js +121 -0
- package/src/posture/feature-flags.js +110 -0
- package/src/posture/finding-defaults.js +132 -0
- package/src/posture/fix-history.js +411 -0
- package/src/posture/fix-plan.js +121 -0
- package/src/posture/fix-verify-loop.js +157 -0
- package/src/posture/fix-verify.js +130 -0
- package/src/posture/flow-narration.js +105 -0
- package/src/posture/grader-calibration.js +156 -0
- package/src/posture/harness-discovery.js +113 -0
- package/src/posture/holdout-eval.js +144 -0
- package/src/posture/iac-reachability.js +163 -0
- package/src/posture/iam-policy.js +128 -0
- package/src/posture/integrity.js +97 -0
- package/src/posture/learning.js +166 -0
- package/src/posture/license-policy.js +109 -0
- package/src/posture/llm-redteam-prompts.js +418 -0
- package/src/posture/llm-redteam.js +303 -0
- package/src/posture/material-change.js +163 -0
- package/src/posture/mitigation-composite.js +55 -0
- package/src/posture/mttr.js +91 -0
- package/src/posture/network-policy-import.js +126 -0
- package/src/posture/path-predicates.js +99 -0
- package/src/posture/persona-prioritization.js +153 -0
- package/src/posture/poc-cwe-map.js +51 -0
- package/src/posture/poc-generator.js +500 -0
- package/src/posture/policy-gate.js +174 -0
- package/src/posture/pre-incident-archaeology.js +110 -0
- package/src/posture/profile.js +93 -0
- package/src/posture/reachability-filter.js +42 -0
- package/src/posture/regression-test-gen.js +200 -0
- package/src/posture/reverse-blast-radius.js +110 -0
- package/src/posture/router.js +109 -0
- package/src/posture/rule-overrides.js +198 -0
- package/src/posture/rule-pack-signing.js +209 -0
- package/src/posture/rule-packs.js +143 -0
- package/src/posture/rule-synthesis.js +108 -0
- package/src/posture/ruleset-version.js +71 -0
- package/src/posture/sbom.js +129 -0
- package/src/posture/schema-aware-bridge.js +207 -0
- package/src/posture/security-trend.js +87 -0
- package/src/posture/semantic-clone.js +114 -0
- package/src/posture/specification-mining.js +170 -0
- package/src/posture/stable-id.js +75 -0
- package/src/posture/stack-playbook.js +229 -0
- package/src/posture/streak.js +249 -0
- package/src/posture/suppressions.js +135 -0
- package/src/posture/telemetry-ingest.js +112 -0
- package/src/posture/threat-model.js +145 -0
- package/src/posture/three-agent-pipeline.js +74 -0
- package/src/posture/triage.js +146 -0
- package/src/posture/trust-boundary-diagram.js +115 -0
- package/src/posture/type-narrowing.js +129 -0
- package/src/posture/validator-metrics.js +179 -0
- package/src/posture/verifier-ephemeral.js +118 -0
- package/src/posture/verifier-target.js +147 -0
- package/src/posture/verifier.js +257 -0
- package/src/posture/version.js +75 -0
- package/src/posture/waf-ingest.js +200 -0
- package/src/posture/why-fired.js +141 -0
- package/src/pr-comment.js +172 -0
- package/src/pr-delta.js +198 -0
- package/src/report/.agentic-security/findings.json +79 -0
- package/src/report/.agentic-security/last-scan.json +79 -0
- package/src/report/.agentic-security/last-scan.json.sig +1 -0
- package/src/report/.agentic-security/scan-history.json +332 -0
- package/src/report/.agentic-security/streak.json +23 -0
- package/src/report/index.js +1136 -0
- package/src/report/mascot.js +42 -0
- package/src/runScan.js +141 -0
- package/src/sast/.agentic-security/findings.json +5051 -0
- package/src/sast/.agentic-security/last-scan.json +5051 -0
- package/src/sast/.agentic-security/last-scan.json.sig +1 -0
- package/src/sast/.agentic-security/scan-history.json +788 -0
- package/src/sast/.agentic-security/streak.json +23 -0
- package/src/sast/CLAUDE.md +39 -0
- package/src/sast/_comment-strip.js +46 -0
- package/src/sast/agent-tool-escalation.js +131 -0
- package/src/sast/auth-provider.js +171 -0
- package/src/sast/authz.js +236 -0
- package/src/sast/bench-shape/.agentic-security/findings.json +28 -0
- package/src/sast/bench-shape/.agentic-security/last-scan.json +28 -0
- package/src/sast/bench-shape/.agentic-security/scan-history.json +24 -0
- package/src/sast/bench-shape/.agentic-security/streak.json +22 -0
- package/src/sast/bench-shape/index.js +62 -0
- package/src/sast/claude-hook-injection.js +199 -0
- package/src/sast/claude-md-prompt-injection.js +170 -0
- package/src/sast/claude-settings.js +165 -0
- package/src/sast/client-side.js +149 -0
- package/src/sast/cpp-bench-extras.js +122 -0
- package/src/sast/cpp-dataflow.js +430 -0
- package/src/sast/cpp.js +248 -0
- package/src/sast/csharp.js +152 -0
- package/src/sast/csrf.js +82 -0
- package/src/sast/dart-flutter.js +173 -0
- package/src/sast/db-rls.js +147 -0
- package/src/sast/db-taint.js +215 -0
- package/src/sast/defi-deep.js +242 -0
- package/src/sast/deserialization-gadgets.js +113 -0
- package/src/sast/django-hardening.js +230 -0
- package/src/sast/env-hygiene.js +125 -0
- package/src/sast/fastapi-hardening.js +145 -0
- package/src/sast/go-extended.js +84 -0
- package/src/sast/host-header.js +106 -0
- package/src/sast/index.js +17 -0
- package/src/sast/java-ast-folding.js +561 -0
- package/src/sast/java-bench-extras.js +708 -0
- package/src/sast/java-collection-passthrough.js +178 -0
- package/src/sast/java-constant-fold.js +244 -0
- package/src/sast/java-deserialization.js +125 -0
- package/src/sast/jndi.js +104 -0
- package/src/sast/juliet-shape.js +324 -0
- package/src/sast/jwt-exp.js +104 -0
- package/src/sast/kotlin.js +82 -0
- package/src/sast/laravel-hardening.js +198 -0
- package/src/sast/ldap-injection.js +100 -0
- package/src/sast/llm-owasp.js +465 -0
- package/src/sast/llm-stored-prompt.js +103 -0
- package/src/sast/llm-trading-agent.js +161 -0
- package/src/sast/llm.js +308 -0
- package/src/sast/logic.js +140 -0
- package/src/sast/mass-assignment.js +101 -0
- package/src/sast/mcp-audit.js +242 -0
- package/src/sast/mobile-manifest.js +195 -0
- package/src/sast/model-load.js +164 -0
- package/src/sast/mutation-xss.js +87 -0
- package/src/sast/nosql-injection.js +82 -0
- package/src/sast/open-redirect.js +119 -0
- package/src/sast/php.js +91 -0
- package/src/sast/pipeline.js +122 -0
- package/src/sast/primary-cwe-java.js +155 -0
- package/src/sast/prompt-firewall.js +151 -0
- package/src/sast/prompt-template.js +157 -0
- package/src/sast/prototype-pollution.js +112 -0
- package/src/sast/python-sinks.js +195 -0
- package/src/sast/quarkus-hardening.js +102 -0
- package/src/sast/rag-poisoning.js +118 -0
- package/src/sast/rate-limit.js +128 -0
- package/src/sast/response-splitting.js +138 -0
- package/src/sast/ruby.js +108 -0
- package/src/sast/rust.js +105 -0
- package/src/sast/solidity.js +167 -0
- package/src/sast/springboot-hardening.js +186 -0
- package/src/sast/ssrf-cloud-metadata.js +80 -0
- package/src/sast/ssti.js +116 -0
- package/src/sast/swift.js +162 -0
- package/src/sast/toctou.js +95 -0
- package/src/sast/webhook.js +101 -0
- package/src/sast/xpath-injection.js +51 -0
- package/src/sast/xxe.js +140 -0
- package/src/sast/zip-slip.js +200 -0
- package/src/sca/base-images.json +45 -0
- package/src/sca/container.js +107 -0
- package/src/sca/dep-confusion.js +134 -0
- package/src/sca/index.js +6 -0
- package/src/sca/popular-packages.json +41 -0
- package/src/sca/sarif-ingest.js +187 -0
- package/src/sca/vuln-function-hints.json +89 -0
- package/src/secrets/index.js +4 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CLI front-end for the LLM-validator consistency harness.
|
|
3
|
+
//
|
|
4
|
+
// Reads .agentic-security/last-scan.json from the project root, picks the
|
|
5
|
+
// top-N findings with precise file:line locations (validator preflight
|
|
6
|
+
// requires them), and runs N trials of the validator on the same finding
|
|
7
|
+
// set. Reports pass^N (unanimous-verdict rate) and per-finding flap detail.
|
|
8
|
+
//
|
|
9
|
+
// The validator endpoint is configured via the usual env vars:
|
|
10
|
+
// AGENTIC_SECURITY_LLM_VALIDATE=1
|
|
11
|
+
// AGENTIC_SECURITY_LLM_ENDPOINT=https://...
|
|
12
|
+
// AGENTIC_SECURITY_LLM_API_KEY=...
|
|
13
|
+
// AGENTIC_SECURITY_LLM_MODEL=...
|
|
14
|
+
//
|
|
15
|
+
// When the validator is off (env unset), every verdict will be 'unvalidated'
|
|
16
|
+
// and pass^N is trivially 100%. That's an honest signal; deploy with the
|
|
17
|
+
// validator on if you care about its behavior.
|
|
18
|
+
//
|
|
19
|
+
// Usage:
|
|
20
|
+
// agentic-security-consistency # uses last-scan, 5 trials, 5 findings
|
|
21
|
+
// agentic-security-consistency --trials 10
|
|
22
|
+
// agentic-security-consistency --top 3
|
|
23
|
+
// agentic-security-consistency --json
|
|
24
|
+
'use strict';
|
|
25
|
+
import * as fs from 'node:fs';
|
|
26
|
+
import * as path from 'node:path';
|
|
27
|
+
import { measureConsistency, summarize } from '../src/llm-validator/consistency.js';
|
|
28
|
+
|
|
29
|
+
function args() {
|
|
30
|
+
const a = process.argv.slice(2);
|
|
31
|
+
const out = { trials: 5, top: 5, json: false, root: process.cwd() };
|
|
32
|
+
for (let i = 0; i < a.length; i++) {
|
|
33
|
+
if (a[i] === '--trials') out.trials = parseInt(a[++i], 10);
|
|
34
|
+
else if (a[i] === '--top') out.top = parseInt(a[++i], 10);
|
|
35
|
+
else if (a[i] === '--json') out.json = true;
|
|
36
|
+
else if (a[i] === '--root') out.root = a[++i];
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
const opts = args();
|
|
43
|
+
const scanFile = path.join(opts.root, '.agentic-security', 'last-scan.json');
|
|
44
|
+
if (!fs.existsSync(scanFile)) {
|
|
45
|
+
console.error(`no last-scan.json at ${scanFile} — run a scan first`);
|
|
46
|
+
process.exit(2);
|
|
47
|
+
}
|
|
48
|
+
let scan;
|
|
49
|
+
try { scan = JSON.parse(fs.readFileSync(scanFile, 'utf8')); }
|
|
50
|
+
catch (e) { console.error(`failed to parse last-scan.json: ${e.message}`); process.exit(2); }
|
|
51
|
+
// Pick findings the validator can actually grade: must have precise file+line.
|
|
52
|
+
const candidates = (scan.findings || [])
|
|
53
|
+
.filter(f => f && typeof f.file === 'string' && f.file.length > 0 &&
|
|
54
|
+
typeof f.line === 'number' && f.line > 0)
|
|
55
|
+
.slice(0, opts.top);
|
|
56
|
+
if (!candidates.length) {
|
|
57
|
+
console.error('no findings with precise locations in last-scan.json');
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
// Build fileContents map from scan.fc if present, otherwise read from disk.
|
|
61
|
+
const fileContents = (scan.fc && typeof scan.fc === 'object') ? scan.fc : {};
|
|
62
|
+
for (const f of candidates) {
|
|
63
|
+
if (fileContents[f.file]) continue;
|
|
64
|
+
try {
|
|
65
|
+
const fp = path.join(opts.root, f.file);
|
|
66
|
+
if (fs.existsSync(fp)) fileContents[f.file] = fs.readFileSync(fp, 'utf8');
|
|
67
|
+
} catch { /* skip */ }
|
|
68
|
+
}
|
|
69
|
+
const r = await measureConsistency({
|
|
70
|
+
findings: candidates, fileContents,
|
|
71
|
+
scanRoot: opts.root, trials: opts.trials,
|
|
72
|
+
});
|
|
73
|
+
if (opts.json) {
|
|
74
|
+
console.log(JSON.stringify(r, null, 2));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log(summarize(r));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main().catch(e => { console.error(e); process.exit(1); });
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// FR-SDLC-10 — Differential scanner CLI.
|
|
3
|
+
//
|
|
4
|
+
// Run two versions of the scanner against the same target and emit the
|
|
5
|
+
// delta. Use cases:
|
|
6
|
+
// 1. PR-time regression detection — catch when an upgrade of the scanner
|
|
7
|
+
// itself loses or adds findings on a stable codebase.
|
|
8
|
+
// 2. Migration confidence — let a customer dry-run a scanner upgrade
|
|
9
|
+
// before bumping their pinned version.
|
|
10
|
+
//
|
|
11
|
+
// Usage:
|
|
12
|
+
// agentic-security-diff --baseline ./dist/agentic-security.mjs \
|
|
13
|
+
// --candidate ./dist/agentic-security.mjs.next \
|
|
14
|
+
// [--root .]
|
|
15
|
+
//
|
|
16
|
+
// Both `--baseline` and `--candidate` paths must point to runnable scanner
|
|
17
|
+
// bundles. The CLI invokes each via child_process with `--format json` and
|
|
18
|
+
// computes the diff on the resulting findings list (keyed by `stableId`
|
|
19
|
+
// when present, else by `(file, line, family)`).
|
|
20
|
+
|
|
21
|
+
import { spawnSync } from 'node:child_process';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
import * as fs from 'node:fs';
|
|
24
|
+
|
|
25
|
+
function parseArgs(argv) {
|
|
26
|
+
const opts = { root: '.', baseline: null, candidate: null, format: 'cli' };
|
|
27
|
+
for (let i = 2; i < argv.length; i++) {
|
|
28
|
+
const a = argv[i];
|
|
29
|
+
if (a === '--baseline') opts.baseline = argv[++i];
|
|
30
|
+
else if (a === '--candidate') opts.candidate = argv[++i];
|
|
31
|
+
else if (a === '--root') opts.root = argv[++i];
|
|
32
|
+
else if (a === '--format') opts.format = argv[++i];
|
|
33
|
+
else if (a === '--help' || a === '-h') opts.help = true;
|
|
34
|
+
}
|
|
35
|
+
return opts;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function runScan(binPath, root) {
|
|
39
|
+
if (!fs.existsSync(binPath)) {
|
|
40
|
+
return { error: `binary not found: ${binPath}`, findings: [] };
|
|
41
|
+
}
|
|
42
|
+
const res = spawnSync(process.execPath, [binPath, 'scan', root, '--format', 'json'], {
|
|
43
|
+
encoding: 'utf8',
|
|
44
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
45
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
46
|
+
});
|
|
47
|
+
if (res.status !== 0 && !res.stdout) {
|
|
48
|
+
return { error: `scanner exited ${res.status}: ${(res.stderr || '').slice(0, 200)}`, findings: [] };
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const data = JSON.parse(res.stdout);
|
|
52
|
+
const findings = Array.isArray(data) ? data : (data.findings || []);
|
|
53
|
+
return { findings };
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return { error: `failed to parse JSON: ${e.message}`, findings: [] };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function keyOf(f) {
|
|
60
|
+
if (f.stableId) return `sid:${f.stableId}`;
|
|
61
|
+
return `pos:${f.file}:${f.line}:${f.family || f.vuln || '?'}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function diff(baseline, candidate) {
|
|
65
|
+
const byKey = (arr) => {
|
|
66
|
+
const m = new Map();
|
|
67
|
+
for (const f of arr) m.set(keyOf(f), f);
|
|
68
|
+
return m;
|
|
69
|
+
};
|
|
70
|
+
const a = byKey(baseline);
|
|
71
|
+
const b = byKey(candidate);
|
|
72
|
+
const added = [], removed = [], changed = [];
|
|
73
|
+
for (const [k, f] of b) {
|
|
74
|
+
if (!a.has(k)) added.push(f);
|
|
75
|
+
else {
|
|
76
|
+
const prev = a.get(k);
|
|
77
|
+
if (prev.severity !== f.severity || prev.vuln !== f.vuln) changed.push({ before: prev, after: f });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const [k, f] of a) if (!b.has(k)) removed.push(f);
|
|
81
|
+
return { added, removed, changed };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function summarize(d) {
|
|
85
|
+
const lines = [];
|
|
86
|
+
lines.push('# Differential scan report');
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push(`Added: ${d.added.length}`);
|
|
89
|
+
lines.push(`Removed: ${d.removed.length}`);
|
|
90
|
+
lines.push(`Changed: ${d.changed.length}`);
|
|
91
|
+
lines.push('');
|
|
92
|
+
if (d.added.length) {
|
|
93
|
+
lines.push('## Added (candidate found, baseline did not)');
|
|
94
|
+
for (const f of d.added.slice(0, 25)) lines.push(` + [${f.severity}] ${f.vuln || ''} at ${f.file}:${f.line}`);
|
|
95
|
+
if (d.added.length > 25) lines.push(` + ... ${d.added.length - 25} more`);
|
|
96
|
+
lines.push('');
|
|
97
|
+
}
|
|
98
|
+
if (d.removed.length) {
|
|
99
|
+
lines.push('## Removed (baseline found, candidate did not)');
|
|
100
|
+
for (const f of d.removed.slice(0, 25)) lines.push(` - [${f.severity}] ${f.vuln || ''} at ${f.file}:${f.line}`);
|
|
101
|
+
if (d.removed.length > 25) lines.push(` - ... ${d.removed.length - 25} more`);
|
|
102
|
+
lines.push('');
|
|
103
|
+
}
|
|
104
|
+
if (d.changed.length) {
|
|
105
|
+
lines.push('## Changed (same finding, different severity or wording)');
|
|
106
|
+
for (const c of d.changed.slice(0, 15)) {
|
|
107
|
+
lines.push(` ~ [${c.before.severity}→${c.after.severity}] ${c.after.file}:${c.after.line}`);
|
|
108
|
+
}
|
|
109
|
+
lines.push('');
|
|
110
|
+
}
|
|
111
|
+
return lines.join('\n');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function main() {
|
|
115
|
+
const opts = parseArgs(process.argv);
|
|
116
|
+
if (opts.help || !opts.baseline || !opts.candidate) {
|
|
117
|
+
console.log('Usage: agentic-security-diff --baseline <bin> --candidate <bin> [--root .] [--format cli|json]');
|
|
118
|
+
process.exit(opts.help ? 0 : 1);
|
|
119
|
+
}
|
|
120
|
+
const root = path.resolve(opts.root);
|
|
121
|
+
const baseline = runScan(opts.baseline, root);
|
|
122
|
+
const candidate = runScan(opts.candidate, root);
|
|
123
|
+
if (baseline.error) { console.error('baseline:', baseline.error); process.exit(2); }
|
|
124
|
+
if (candidate.error) { console.error('candidate:', candidate.error); process.exit(2); }
|
|
125
|
+
const d = diff(baseline.findings, candidate.findings);
|
|
126
|
+
if (opts.format === 'json') {
|
|
127
|
+
process.stdout.write(JSON.stringify(d, null, 2));
|
|
128
|
+
} else {
|
|
129
|
+
process.stdout.write(summarize(d));
|
|
130
|
+
}
|
|
131
|
+
// Exit 0 if no delta, 1 if delta, 2 if errors (already exited above).
|
|
132
|
+
const hasDelta = d.added.length || d.removed.length || d.changed.length;
|
|
133
|
+
process.exit(hasDelta ? 1 : 0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// agentic-security LSP server — stdio entry point.
|
|
3
|
+
//
|
|
4
|
+
// Speaks the Language Server Protocol (vscode-jsonrpc framing) on stdin/stdout.
|
|
5
|
+
// Used by the JetBrains plugin (via LSP4IJ) and the Neovim plugin (via
|
|
6
|
+
// built-in LSP). Diagnostics emitted to publishDiagnostics on save/open.
|
|
7
|
+
//
|
|
8
|
+
// Usage (typically invoked by an editor, not directly):
|
|
9
|
+
// node bin/agentic-security-lsp.js
|
|
10
|
+
//
|
|
11
|
+
import { startLspServer } from '../src/lsp/server.js';
|
|
12
|
+
startLspServer();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// agentic-security MCP server — stdio entry point.
|
|
3
|
+
//
|
|
4
|
+
// Speaks JSON-RPC 2.0 over NDJSON on stdin/stdout. stderr is for logging.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// node bin/agentic-security-mcp.js [--root <path>]
|
|
8
|
+
//
|
|
9
|
+
// Session root resolution (highest precedence first):
|
|
10
|
+
// 1. --root <path> CLI arg
|
|
11
|
+
// 2. AGENTIC_SECURITY_MCP_ROOT env var
|
|
12
|
+
// 3. process.cwd()
|
|
13
|
+
//
|
|
14
|
+
// The session root confines every tool: paths in tool arguments must resolve
|
|
15
|
+
// inside it, and apply_fix refuses any finding whose file field escapes it.
|
|
16
|
+
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
import { runStdio } from '../src/mcp/stdio.js';
|
|
19
|
+
|
|
20
|
+
function _parseRoot() {
|
|
21
|
+
const argv = process.argv.slice(2);
|
|
22
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23
|
+
if (argv[i] === '--root' && argv[i + 1]) return argv[i + 1];
|
|
24
|
+
if (argv[i].startsWith('--root=')) return argv[i].slice('--root='.length);
|
|
25
|
+
}
|
|
26
|
+
return process.env.AGENTIC_SECURITY_MCP_ROOT || process.cwd();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Operator kill-switch (OWASP MCP09 Shadow MCP Servers): bin exits
|
|
30
|
+
// immediately if disabled. Tool calls would also be refused server-side,
|
|
31
|
+
// but exiting early prevents the process from staying resident as an
|
|
32
|
+
// invisible attack surface on machines where the env var is set.
|
|
33
|
+
if (process.env.AGENTIC_SECURITY_MCP_DISABLED === '1') {
|
|
34
|
+
process.stderr.write('mcp: disabled via AGENTIC_SECURITY_MCP_DISABLED=1 — exiting.\n');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const sessionRoot = path.resolve(_parseRoot());
|
|
39
|
+
process.stderr.write(`mcp: session root = ${sessionRoot}\n`);
|
|
40
|
+
runStdio({ sessionRoot });
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// agentic-security-rule — CLI for signing / verifying custom rule packs.
|
|
3
|
+
//
|
|
4
|
+
// Subcommands:
|
|
5
|
+
// keygen Generate an Ed25519 key pair (PRIVATE KEY printed
|
|
6
|
+
// to stdout; handle with care).
|
|
7
|
+
// sign <rule-yml> Sign the rule file. Writes <rule-yml>.sig.
|
|
8
|
+
// Reads the private key from $AGENTIC_SECURITY_PRIVATE_KEY
|
|
9
|
+
// or --key <base64>.
|
|
10
|
+
// verify <rule-yml> Verify against the project's trusted-keys.json
|
|
11
|
+
// (or bundled official keys). Honors revocation.
|
|
12
|
+
//
|
|
13
|
+
// First-time setup walkthrough:
|
|
14
|
+
//
|
|
15
|
+
// 1) Generate a key pair OUTSIDE the project tree:
|
|
16
|
+
// mkdir -p ~/.config/agentic-security/keys
|
|
17
|
+
// agentic-security-rule keygen --out ~/.config/agentic-security/keys/MY_KEY.json
|
|
18
|
+
// (the file is written 0600. KEEP the private key SECRET — do not commit it,
|
|
19
|
+
// do not put it in a cloud-synced directory.)
|
|
20
|
+
//
|
|
21
|
+
// 2) Add the public key to .agentic-security/trusted-keys.json:
|
|
22
|
+
// {
|
|
23
|
+
// "keys": [
|
|
24
|
+
// { "id": "my-team-2026", "alg": "ed25519",
|
|
25
|
+
// "publicKey": "<paste publicKey from step 1>" }
|
|
26
|
+
// ]
|
|
27
|
+
// }
|
|
28
|
+
//
|
|
29
|
+
// 3) Tell the scanner to trust project-local keys (audit-logged):
|
|
30
|
+
// export AGENTIC_SECURITY_ALLOW_PROJECT_KEYS=1
|
|
31
|
+
//
|
|
32
|
+
// 4) Author a custom rule at .agentic-security/rules/my-rule.yml.
|
|
33
|
+
//
|
|
34
|
+
// 5) Sign it:
|
|
35
|
+
// export AGENTIC_SECURITY_PRIVATE_KEY="<paste privateKey from step 1>"
|
|
36
|
+
// agentic-security-rule sign .agentic-security/rules/my-rule.yml
|
|
37
|
+
//
|
|
38
|
+
// 6) Verify before commit:
|
|
39
|
+
// agentic-security-rule verify .agentic-security/rules/my-rule.yml
|
|
40
|
+
//
|
|
41
|
+
// CAUTION: the private key in step 1 is a SECRET. Anyone with it can sign
|
|
42
|
+
// rules that will execute in your CI. Store in a password manager / KMS,
|
|
43
|
+
// never in source control or shell history. Use --rotate to retire keys.
|
|
44
|
+
|
|
45
|
+
import { keygen, signRulePack, verifyRulePack, loadTrustedKeys } from '../src/posture/rule-pack-signing.js';
|
|
46
|
+
import * as fs from 'node:fs';
|
|
47
|
+
import * as path from 'node:path';
|
|
48
|
+
|
|
49
|
+
const cmd = process.argv[2];
|
|
50
|
+
const args = process.argv.slice(3);
|
|
51
|
+
|
|
52
|
+
function die(msg, code = 1) {
|
|
53
|
+
process.stderr.write(msg + '\n');
|
|
54
|
+
process.exit(code);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pickArg(flag) {
|
|
58
|
+
const i = args.indexOf(flag);
|
|
59
|
+
return i >= 0 ? args[i + 1] : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (cmd === 'keygen') {
|
|
63
|
+
// Premortem 3R3.2 / 3R-5: keygen output contains a PRIVATE KEY. Three
|
|
64
|
+
// safety rails:
|
|
65
|
+
// 1. If --out <path> is supplied AND the path resolves under the project's
|
|
66
|
+
// .agentic-security/ directory, REFUSE. That directory tends to be
|
|
67
|
+
// cloud-synced / git-checked / editor-recent. The right place for
|
|
68
|
+
// private keys is outside source control.
|
|
69
|
+
// 2. If stdout is not a TTY (redirected to a file or pipe) AND no --out
|
|
70
|
+
// was specified, WARN that the operator is about to write a private
|
|
71
|
+
// key somewhere we can't see; suggest --out so we can validate.
|
|
72
|
+
// 3. Operators who really mean it can pass --i-understand-private-keys
|
|
73
|
+
// to silence the warnings.
|
|
74
|
+
const outArg = pickArg('--out');
|
|
75
|
+
const understandFlag = args.includes('--i-understand-private-keys');
|
|
76
|
+
if (outArg) {
|
|
77
|
+
const projectAgenticDir = path.resolve(process.cwd(), '.agentic-security') + path.sep;
|
|
78
|
+
const absOut = path.resolve(outArg);
|
|
79
|
+
if (absOut.startsWith(projectAgenticDir)) {
|
|
80
|
+
die(
|
|
81
|
+
`Refusing to write a private key into ${absOut}.\n` +
|
|
82
|
+
` .agentic-security/ is typically git-tracked, cloud-synced, or editor-indexed.\n` +
|
|
83
|
+
` Choose a location OUTSIDE the project tree (e.g. ~/.config/agentic-security/keys/).\n` +
|
|
84
|
+
` Override with --i-understand-private-keys if you really mean it.`,
|
|
85
|
+
2);
|
|
86
|
+
}
|
|
87
|
+
} else if (!process.stdout.isTTY && !understandFlag) {
|
|
88
|
+
process.stderr.write(
|
|
89
|
+
'agentic-security-rule: WARNING — stdout is being redirected, but no --out was specified.\n' +
|
|
90
|
+
' This command emits a PRIVATE KEY. We cannot validate where it ends up.\n' +
|
|
91
|
+
' Re-run with --out <path-outside-project> so we can check the destination,\n' +
|
|
92
|
+
' or with --i-understand-private-keys to silence this warning.\n');
|
|
93
|
+
process.exit(3);
|
|
94
|
+
}
|
|
95
|
+
const kp = keygen();
|
|
96
|
+
const out = {
|
|
97
|
+
note: 'STORE THE privateKey SECURELY. Do not commit it to source control. Anyone with this key can sign rules that execute in your CI.',
|
|
98
|
+
id: `key-${new Date().toISOString().slice(0, 10)}-${Math.random().toString(36).slice(2, 6)}`,
|
|
99
|
+
alg: 'ed25519',
|
|
100
|
+
issuedAt: new Date().toISOString(),
|
|
101
|
+
publicKey: kp.publicKey,
|
|
102
|
+
privateKey: kp.privateKey,
|
|
103
|
+
};
|
|
104
|
+
const payload = JSON.stringify(out, null, 2) + '\n';
|
|
105
|
+
if (outArg) {
|
|
106
|
+
fs.writeFileSync(outArg, payload, { mode: 0o600 });
|
|
107
|
+
process.stderr.write(`\nagentic-security-rule: keypair written to ${outArg} (mode 0600).\n`);
|
|
108
|
+
} else {
|
|
109
|
+
process.stdout.write(payload);
|
|
110
|
+
}
|
|
111
|
+
process.stderr.write(' · Add publicKey to .agentic-security/trusted-keys.json (this IS git-trackable)\n');
|
|
112
|
+
process.stderr.write(' · Store privateKey in a password manager / KMS\n');
|
|
113
|
+
process.stderr.write(' · Set AGENTIC_SECURITY_ALLOW_PROJECT_KEYS=1 so the scanner trusts project-local keys\n');
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (cmd === 'sign') {
|
|
118
|
+
const target = args.find(a => !a.startsWith('--'));
|
|
119
|
+
if (!target) die('Usage: agentic-security-rule sign <rule-yml> [--key <base64>]');
|
|
120
|
+
if (!fs.existsSync(target)) die(`File not found: ${target}`);
|
|
121
|
+
const key = pickArg('--key') || process.env.AGENTIC_SECURITY_PRIVATE_KEY;
|
|
122
|
+
if (!key) die('No private key. Set AGENTIC_SECURITY_PRIVATE_KEY or pass --key <base64>.');
|
|
123
|
+
try {
|
|
124
|
+
signRulePack(target, key);
|
|
125
|
+
process.stdout.write(`Signed: ${target}.sig\n`);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
die(`Sign failed: ${e.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (cmd === 'verify') {
|
|
133
|
+
const target = args.find(a => !a.startsWith('--'));
|
|
134
|
+
if (!target) die('Usage: agentic-security-rule verify <rule-yml>');
|
|
135
|
+
if (!fs.existsSync(target)) die(`File not found: ${target}`);
|
|
136
|
+
const scanRoot = path.resolve('.');
|
|
137
|
+
const keys = loadTrustedKeys(scanRoot);
|
|
138
|
+
if (keys.length === 0) {
|
|
139
|
+
process.stderr.write('agentic-security-rule: no trusted keys configured.\n');
|
|
140
|
+
process.stderr.write(' Add keys to .agentic-security/trusted-keys.json and set AGENTIC_SECURITY_ALLOW_PROJECT_KEYS=1.\n');
|
|
141
|
+
process.exit(2);
|
|
142
|
+
}
|
|
143
|
+
const r = verifyRulePack(target, keys);
|
|
144
|
+
if (r.ok) {
|
|
145
|
+
process.stdout.write(`OK — signed by ${r.keyId}\n`);
|
|
146
|
+
process.exit(0);
|
|
147
|
+
} else {
|
|
148
|
+
process.stderr.write(`FAILED: ${r.reason}${r.keyId ? ` (key ${r.keyId})` : ''}\n`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
die(`Usage: agentic-security-rule <keygen | sign <rule.yml> | verify <rule.yml>>`);
|