@clear-capabilities/agentic-security-scanner 0.122.0 → 0.123.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 +19 -0
- package/dist/agentic-security.mjs +1 -1
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +2 -2
- package/src/report/index.js +39 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
c8cf8da77f8cf7ba57141e8280ea3642843125b66c871e2051e5a99c1894cbbd 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.123.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, SARIF ingest, pipeline integrity, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"test:posture": "node --test test/material-change.test.js test/drift.test.js test/scorecard.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/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/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-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/license-graph.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/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",
|
|
65
65
|
"test:dataflow": "node --test 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/parser-py-cst.test.js test/parser-cs-kt.test.js test/parser-go.test.js test/parser-php-rb.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/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",
|
|
66
66
|
"test:mcp": "node --test test/mcp.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",
|
|
67
|
-
"test:report": "node --test test/sarif-ingest.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/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js",
|
|
67
|
+
"test:report": "node --test test/sarif-ingest.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/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js",
|
|
68
68
|
"test:bench-modules": "node --test test/phase4-harness.test.js test/pipeline.test.js",
|
|
69
69
|
"test:lifecycle": "node --test test/dead-code.test.js test/no-dead-modules.test.js test/stop-hook.test.js test/plugin-self-check.test.js test/skills-registry.test.js test/bodyguard.test.js test/cache-invalidator-guard.test.js && node ../scripts/lint-command-descriptions.mjs",
|
|
70
70
|
"test:eval": "node --test test/independent-eval.test.js",
|
package/src/report/index.js
CHANGED
|
@@ -7,6 +7,28 @@ import { SCANNER_VERSION } from '../posture/version.js';
|
|
|
7
7
|
const SEV_RANK = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
|
8
8
|
const SEV_TO_SARIF = { critical: 'error', high: 'error', medium: 'warning', low: 'note', info: 'none' };
|
|
9
9
|
|
|
10
|
+
// A short "likely lower risk than its label" note for a high/critical finding the
|
|
11
|
+
// reachability / exploitability / confidence pipeline has already marked down.
|
|
12
|
+
// Detector severities are hardcoded per rule, so a `critical` can outrun its real
|
|
13
|
+
// risk; this surfaces that in the rendered output so the label isn't taken at face
|
|
14
|
+
// value. Returns null when the headline severity is fair as-is. (We annotate, not
|
|
15
|
+
// mutate — `severity` stays canonical for SARIF/exit codes/baselines.)
|
|
16
|
+
function riskNote(f) {
|
|
17
|
+
if (!/^(critical|high)$/.test(String(f.severity || ''))) return null;
|
|
18
|
+
const mv = String(f.mitigationVerdict || '').toLowerCase();
|
|
19
|
+
if (f.unreachable === true || f.unreachableInProd === true || mv.includes('unreachable')) {
|
|
20
|
+
return 'likely lower risk — not reachable in prod';
|
|
21
|
+
}
|
|
22
|
+
if (mv.includes('saniti')) return 'likely lower risk — input appears sanitized';
|
|
23
|
+
const et = String(f.exploitabilityTier || '').toLowerCase();
|
|
24
|
+
if (et === 'minimal' || et === 'low') return `likely lower risk — ${et} exploitability`;
|
|
25
|
+
const ct = String(f.confidenceTier || '').toLowerCase();
|
|
26
|
+
if (ct === 'low' || (typeof f.confidence === 'number' && f.confidence > 0 && f.confidence < 0.5)) {
|
|
27
|
+
return 'lower confidence — verify before prioritising';
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
10
32
|
function fingerprint(f){
|
|
11
33
|
const s = `${f.file}:${f.line||f.source?.line||0}:${f.vuln||f.type||''}`;
|
|
12
34
|
return crypto.createHash('sha256').update(s).digest('hex').slice(0, 16);
|
|
@@ -681,7 +703,9 @@ export function toVex(scan, meta = {}) {
|
|
|
681
703
|
}
|
|
682
704
|
|
|
683
705
|
export function toHTML(scan, meta = {}) {
|
|
684
|
-
|
|
706
|
+
// Attach the "likely lower risk" note (computed server-side) so the browser
|
|
707
|
+
// render can badge over-stated high/critical findings.
|
|
708
|
+
const findings = normalizeFindings(scan).map(f => ({ ...f, _riskNote: riskNote(f) }));
|
|
685
709
|
const counts = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
686
710
|
for (const f of findings) counts[f.severity] = (counts[f.severity] || 0) + 1;
|
|
687
711
|
const stride = {};
|
|
@@ -745,6 +769,7 @@ export function toHTML(scan, meta = {}) {
|
|
|
745
769
|
.f-vuln{font-weight:600;flex:1}
|
|
746
770
|
.f-cwe{color:#64748b;font-size:11px;font-family:ui-monospace,monospace}
|
|
747
771
|
.f-epss{font-size:11px;font-weight:600;color:#f59e0b;background:#f59e0b18;padding:1px 6px;border-radius:3px;white-space:nowrap}
|
|
772
|
+
.f-note{font-size:11px;font-weight:600;color:#a3a3a3;background:#a3a3a318;padding:1px 6px;border-radius:3px;white-space:nowrap}
|
|
748
773
|
.f-body{display:none;margin-top:12px;padding-top:12px;border-top:1px solid #1e293b;font-size:12px}
|
|
749
774
|
.f.expanded .f-body{display:block}
|
|
750
775
|
.f-body pre{background:#020617;padding:10px;border-radius:4px;overflow-x:auto;font-size:11px;line-height:1.5}
|
|
@@ -796,6 +821,7 @@ function makeCard(f) {
|
|
|
796
821
|
'<span class="f-vuln">' + esc(f.vuln) + '</span>' +
|
|
797
822
|
'<span class="f-cwe">' + esc(f.cwe||'') + '</span>' +
|
|
798
823
|
epssHtml +
|
|
824
|
+
(f._riskNote ? '<span class="f-note" title="The reachability / confidence pipeline marked this down from its rule-default severity">↓ ' + esc(f._riskNote) + '</span>' : '') +
|
|
799
825
|
'</div>' +
|
|
800
826
|
'<div class="f-body">' +
|
|
801
827
|
(f.snippet ? '<pre>' + esc(f.snippet) + '</pre>' : '') +
|
|
@@ -897,6 +923,8 @@ export function toCLI(scan, { verbose=false, color=true }={}){
|
|
|
897
923
|
? c(` V:${f.validator_verdict}`, DIM)
|
|
898
924
|
: '';
|
|
899
925
|
lines.push(`${sevTag} ${c(f.cwe||' ', DIM)} ${f.file}:${f.line} ${BOLD}${f.vuln}${RESET}${epssTag}${kevTag}${verdictTag}`);
|
|
926
|
+
const rn = riskNote(f);
|
|
927
|
+
if (rn) lines.push(` ${c('↓ ' + rn, '\x1b[2;33m')}`);
|
|
900
928
|
if (f.masked) lines.push(` ${c('value:', DIM)} ${f.masked}`);
|
|
901
929
|
if (verbose && f.fix?.description) {
|
|
902
930
|
lines.push(` ${c('fix:', DIM)} ${f.fix.description}`);
|
|
@@ -1051,6 +1079,14 @@ export function toShipVerdict(scan, options = {}) {
|
|
|
1051
1079
|
} else if (advisoryCount > 0) {
|
|
1052
1080
|
lines.push(c(` ${advisoryCount} advisory item${advisoryCount === 1 ? '' : 's'} — run /security-scan-all --firehose to see them.`, DIM));
|
|
1053
1081
|
}
|
|
1082
|
+
// Discoverability: the depth (per-finding explanation) and the shareable report
|
|
1083
|
+
// exist but aren't obvious from the one-screen verdict — point to them.
|
|
1084
|
+
if (findings.length > 0) {
|
|
1085
|
+
lines.push('');
|
|
1086
|
+
lines.push(c(' Want more detail?', BOLD));
|
|
1087
|
+
lines.push(c(' /triage --explain <id> why it fired, the data-flow trace, and the fix', DIM));
|
|
1088
|
+
lines.push(c(' scan . --format html -o report.html shareable browser report (charts + filters)', DIM));
|
|
1089
|
+
}
|
|
1054
1090
|
// Coverage-honesty line (#5/#6): show the scan's blind spots — which
|
|
1055
1091
|
// languages got flow analysis vs pattern-only, what was skipped, and how
|
|
1056
1092
|
// many dangerous-looking calls had no finding. One concise line, not bloat.
|
|
@@ -1117,6 +1153,8 @@ export function toProTable(scan, options = {}) {
|
|
|
1117
1153
|
else if (columns === 'capec') lines.push(`${sev} ${where} ${capec} ${vuln} ${conf}`);
|
|
1118
1154
|
else if (columns === 'owasp') lines.push(`${sev} ${where} ${cwe} ${owasp} ${vuln} ${conf}`);
|
|
1119
1155
|
else lines.push(`${sev} ${where} ${cwe} ${cvss} ${owasp} ${vuln} ${conf}`);
|
|
1156
|
+
const rn = riskNote(f);
|
|
1157
|
+
if (rn) lines.push(c(' ↓ ' + rn, '\x1b[2;33m'));
|
|
1120
1158
|
}
|
|
1121
1159
|
|
|
1122
1160
|
// Footer counts.
|