@clear-capabilities/agentic-security-scanner 0.148.1 → 0.148.3
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 +83 -0
- package/bin/agentic-security.js +5 -1
- package/dist/4970.index.js +58 -2
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/ccpa.json +2 -0
- package/dist/compliance-frameworks/eu-ai-act.json +12 -16
- package/dist/compliance-frameworks/gdpr.json +8 -6
- package/dist/compliance-frameworks/hipaa-security-rule.json +9 -9
- package/dist/compliance-frameworks/nist-800-171-r3.json +6 -5
- package/dist/compliance-frameworks/nist-ai-600-1.json +6 -4
- package/dist/compliance-frameworks/nist-csf-2.json +6 -5
- package/dist/compliance-frameworks/nist-privacy-1-1.json +20 -10
- package/dist/compliance-frameworks/owasp-asvs-5.json +2 -0
- package/dist/compliance-frameworks/owasp-llm-top-10.json +10 -11
- package/package.json +4 -3
- package/src/engine.js +14 -0
- package/src/pipeline/assurance-mode.js +58 -2
- package/src/posture/accuracy-scorecard.js +34 -0
- package/src/posture/aibom.js +22 -0
- package/src/posture/artifact-registry.js +2 -0
- package/src/posture/auditor-walkthrough.js +91 -35
- package/src/posture/compliance-frameworks/ccpa.json +2 -0
- package/src/posture/compliance-frameworks/eu-ai-act.json +12 -16
- package/src/posture/compliance-frameworks/gdpr.json +8 -6
- package/src/posture/compliance-frameworks/hipaa-security-rule.json +9 -9
- package/src/posture/compliance-frameworks/nist-800-171-r3.json +6 -5
- package/src/posture/compliance-frameworks/nist-ai-600-1.json +6 -4
- package/src/posture/compliance-frameworks/nist-csf-2.json +6 -5
- package/src/posture/compliance-frameworks/nist-privacy-1-1.json +20 -10
- package/src/posture/compliance-frameworks/owasp-asvs-5.json +2 -0
- package/src/posture/compliance-frameworks/owasp-llm-top-10.json +10 -11
- package/src/posture/verifier.js +70 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,89 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
## 0.148.3 - Dependency currency fix; supersedes 0.148.2, which never published
|
|
14
|
+
|
|
15
|
+
The `v0.148.2` tag was pushed but its release workflow's dependency-currency gate failed on a
|
|
16
|
+
clean CI runner — `@types/node` (dev-only, scanner and ide/vscode) was one patch behind the
|
|
17
|
+
latest published version, with no advisory and no legitimate reason to hold it. `npm publish`
|
|
18
|
+
never ran for 0.148.2; nothing shipped under that version. Fixed by upgrading `@types/node` to
|
|
19
|
+
`26.5.0` in both trees. No other change from 0.148.2's own content — see that entry below for
|
|
20
|
+
what actually shipped.
|
|
21
|
+
|
|
22
|
+
## 0.148.2 - A confusing --assurance strict failure made specific and actionable; adversarial premortem re-run on NIST 800-171
|
|
23
|
+
|
|
24
|
+
A user ran the README's own 5-minute quickstart command and got a confusing, uninformative
|
|
25
|
+
failure — see the last item in this entry for the fix. Separately, 0.148.1's compliance-framework
|
|
26
|
+
fixes were put through a second, independent adversarial pass rather than trusted on their own
|
|
27
|
+
say-so. It found the individual fixes held up, but surfaced two live defects the first pass
|
|
28
|
+
missed entirely, plus six gaps in how the fixes themselves were verified and disclosed.
|
|
29
|
+
|
|
30
|
+
1. `module:verifier` and `module:sigstore-verify` were referenced by real controls in four
|
|
31
|
+
bundled frameworks, but nothing ever wrote either artifact — worse than a self-referential
|
|
32
|
+
mapping (which can at least clear, dishonestly), a mapping that can never clear at all, on
|
|
33
|
+
any project, permanently. `verifier.js` now writes a real record on every
|
|
34
|
+
`agentic-security verify` run, closing `module:verifier` for real — proven end-to-end:
|
|
35
|
+
NIST 800-171 `03.12.01` flips from missing to present after a real `verify` invocation.
|
|
36
|
+
`module:sigstore-verify` is disclosed as an engine gap instead: its real producer is a
|
|
37
|
+
fire-and-forget async annotation with no reliable completion point to hang a writer off,
|
|
38
|
+
a bigger fix than this pass's scope.
|
|
39
|
+
|
|
40
|
+
2. A new writer-existence test (`scanner/test/module-artifact-liveness.test.js`) checks every
|
|
41
|
+
`module:` artifact resolves to a real writer or a real repo file, not just a name on a list.
|
|
42
|
+
Running it once found two more real, previously-unknown defects the manual premortem review
|
|
43
|
+
missed: a malformed `module:privacy-taint:emitDpiaArtifact` typo in `gdpr.json` (the
|
|
44
|
+
evaluator parses everything after `module:` as one literal key — no `:`-suffix syntax
|
|
45
|
+
exists — so this control could never clear), and a dead `module:exploitability-probability`
|
|
46
|
+
reference in `nist-csf-2.json` (that annotator writes no standalone artifact; removed as
|
|
47
|
+
redundant with the surviving `module:attack-taxonomy` leg on the same control). Both fixed.
|
|
48
|
+
The eight structurally self-referential `module:` entries found across this and the prior
|
|
49
|
+
release are now removed from the vocabulary table itself, not merely left unreferenced, so
|
|
50
|
+
there is nothing left to copy-paste back into a future mapping.
|
|
51
|
+
|
|
52
|
+
3. `aibom.json` now auto-persists on every scan. It was only ever reachable through the CLI's
|
|
53
|
+
`--format aibom` report emitter, which prints to stdout — never to `.agentic-security/` —
|
|
54
|
+
so `module:aibom` (EU AI Act Art.11, NIST 800-171 `03.04.10`, NIST AI 600-1 `MG-4.1-001`)
|
|
55
|
+
could never clear on any project unless an operator manually redirected the CLI's output to
|
|
56
|
+
that exact path.
|
|
57
|
+
|
|
58
|
+
4. The scoring-threshold provenance caveat added in 0.148.1 (that NIST 800-171's deep-attestation
|
|
59
|
+
scanner shares AI 600-1's unvalidated weights and thresholds) lived only in a doc and a
|
|
60
|
+
source docstring — never in the actual generated attestation markdown a customer would
|
|
61
|
+
submit. It now prints inline in that document whenever the catalog being scored isn't the
|
|
62
|
+
framework the scoring engine was built for.
|
|
63
|
+
|
|
64
|
+
5. The held-out anchor set added in 0.148.1 to guard the 97 hand-authored `codeTestable`
|
|
65
|
+
ratings covered only 3 of the 9 controls that release's own rationale-expansion pass
|
|
66
|
+
actually touched — the other 6 had no drift protection despite being exactly the work the
|
|
67
|
+
gate exists to protect. Expanded to cover all 9.
|
|
68
|
+
|
|
69
|
+
6. `module:why-fired` (EU AI Act Art.13), left an open, undecided question in 0.148.1's own
|
|
70
|
+
premortem, is now adjudicated: on a full reading of the control text (every other control in
|
|
71
|
+
the same framework uses "the system" to mean the assessed AI product) and of `why-fired.js`'s
|
|
72
|
+
actual purpose (explains this scanner's own detection provenance, not the assessed system's),
|
|
73
|
+
it is the same category error as the other eight — the artifact's content being genuinely
|
|
74
|
+
target-derived doesn't make the claim it was asked to back correct.
|
|
75
|
+
|
|
76
|
+
7. `docs/SCORECARD.md` now tracks, per bundled framework, how many controls carry at least one
|
|
77
|
+
live mapping — not as a gate (a drop is sometimes a correct, honest fix and sometimes a real
|
|
78
|
+
regression, and only a human reading the diff each release can tell which), but so the
|
|
79
|
+
cumulative effect of "always subtract a bad mapping, never invent one" is visible instead of
|
|
80
|
+
assumed fine.
|
|
81
|
+
|
|
82
|
+
8. The PRD's own implementation record (`NIST-800-171r3-PRD.md`) now documents this pass.
|
|
83
|
+
|
|
84
|
+
9. `agentic-security ci <path> --assurance strict` on a directory with no git history — the
|
|
85
|
+
exact result of running the README's own quickstart command against a GitHub "Download ZIP"
|
|
86
|
+
extraction instead of a `git clone` — used to fail with only a bare count: "1210 finding(s)
|
|
87
|
+
have status outside [complete, uncommitted]." The scanner already knew and recorded the real
|
|
88
|
+
reason (`finding.findingProvenance.limitations[0] = 'not a Git repository'`), it just never
|
|
89
|
+
reached the message a user actually sees. The assurance-gate failure now names the dominant
|
|
90
|
+
recorded reason and, for the two most common shapes, gives a specific fix: no git history
|
|
91
|
+
("run `git init && git add -A && git commit`, or scan a real `git clone`") and an unpinned
|
|
92
|
+
dependency / missing lockfile ("this is a permanent, by-design limitation — fix the SCA
|
|
93
|
+
finding or use `--assurance standard`"). `docs/walkthroughs/assurance-modes.md` documents
|
|
94
|
+
both (`scanner/src/pipeline/assurance-mode.js`).
|
|
95
|
+
|
|
13
96
|
|
|
14
97
|
## 0.148.1 - Six real false-positive fixes from a customer bug report, two report-consistency fixes
|
|
15
98
|
|
package/bin/agentic-security.js
CHANGED
|
@@ -1875,7 +1875,7 @@ async function cmdVerify(args) {
|
|
|
1875
1875
|
process.env.AGENTIC_SECURITY_VERIFY_LIVE = '1';
|
|
1876
1876
|
process.env.AGENTIC_SECURITY_VERIFY_TARGET = targetFlag;
|
|
1877
1877
|
}
|
|
1878
|
-
const { annotateVerifierVerdicts, verifierCoverageSummary } = await import('../src/posture/verifier.js');
|
|
1878
|
+
const { annotateVerifierVerdicts, verifierCoverageSummary, recordVerifierRun } = await import('../src/posture/verifier.js');
|
|
1879
1879
|
const filter = args.flags.finding ? findings.filter(f => f.id === args.flags.finding || f.stableId === args.flags.finding) : findings;
|
|
1880
1880
|
if (!filter.length) {
|
|
1881
1881
|
console.error(`No matching findings (use --finding <id>).`);
|
|
@@ -1901,6 +1901,10 @@ async function cmdVerify(args) {
|
|
|
1901
1901
|
const sum = verifierCoverageSummary(filter);
|
|
1902
1902
|
console.log(`Verified ${filter.length} finding(s):`);
|
|
1903
1903
|
for (const [k, v] of Object.entries(sum)) console.log(` ${k}: ${v}`);
|
|
1904
|
+
// Adversarial premortem Q1: a durable record that a security assessment of
|
|
1905
|
+
// findings actually happened, so module:verifier has something real to
|
|
1906
|
+
// point at (see verifier.js's recordVerifierRun header comment).
|
|
1907
|
+
recordVerifierRun(scanRoot, { findingCount: filter.length, live: liveFlag, target: targetFlag || null, summary: sum });
|
|
1904
1908
|
if (args.flags.verbose || args.flags.finding) {
|
|
1905
1909
|
for (const f of filter) {
|
|
1906
1910
|
console.log(` ${f.file}:${f.line} ${f.vuln}`);
|
package/dist/4970.index.js
CHANGED
|
@@ -65,6 +65,62 @@ function _isValidMode(mode) {
|
|
|
65
65
|
return ASSURANCE_MODES.includes(mode);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// A real user hit this: `agentic-security ci <a directory downloaded as a
|
|
69
|
+
// GitHub zip, no .git present> --assurance strict` failed with the bare
|
|
70
|
+
// count this function used to produce alone — "1210 finding(s) have status
|
|
71
|
+
// outside [complete, uncommitted]" — with no indication that all 1210
|
|
72
|
+
// findings failed for the exact same, simple, fixable reason
|
|
73
|
+
// (`coordinator.js`'s `annotateGitProvenance` already knows and records it,
|
|
74
|
+
// in `finding.findingProvenance.limitations[0]`, but that reason never
|
|
75
|
+
// reached this message). A user reading "1210 problems" reasonably assumes
|
|
76
|
+
// their CODE has 1210 problems, not that their DIRECTORY isn't a git
|
|
77
|
+
// repository. This surfaces the dominant recorded reason instead of a bare
|
|
78
|
+
// count, and gives the two most common, fully-fixable reasons ("not a Git
|
|
79
|
+
// repository" from a zip download instead of `git clone`; a shallow CI
|
|
80
|
+
// checkout) a one-line, specific remedy — the same specificity the
|
|
81
|
+
// scanHealth branch above already gives for a stale-EPSS-cache failure.
|
|
82
|
+
function _provenanceFailureReason(badProvenance, totalFindings) {
|
|
83
|
+
const counts = new Map();
|
|
84
|
+
for (const f of badProvenance) {
|
|
85
|
+
const reason = f?.findingProvenance?.limitations?.[0] || f?.findingProvenance?.status || 'unknown';
|
|
86
|
+
counts.set(reason, (counts.get(reason) || 0) + 1);
|
|
87
|
+
}
|
|
88
|
+
const ranked = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
89
|
+
const [topReason, topCount] = ranked[0];
|
|
90
|
+
const allSameReason = ranked.length === 1;
|
|
91
|
+
const base = `strict mode requires complete finding provenance; ${badProvenance.length}/${totalFindings} finding(s) have status outside [complete, uncommitted]`;
|
|
92
|
+
|
|
93
|
+
if (topReason === 'not a Git repository' || topReason === 'repository state unavailable') {
|
|
94
|
+
return `${base} — reason: ${allSameReason ? 'all of them are' : `${topCount} of them are`} "${topReason}". ` +
|
|
95
|
+
`strict mode resolves finding provenance from git history, so it requires a real git repository ` +
|
|
96
|
+
`(a GitHub "Download ZIP" extracts without one). Run \`git init && git add -A && git commit -m init\` in ` +
|
|
97
|
+
`the scanned directory, point the scan at a real \`git clone\`, or drop --assurance strict for standard/advisory.`;
|
|
98
|
+
}
|
|
99
|
+
// engine.js's own comment on this branch: "unpinned_dep / no_lockfile and
|
|
100
|
+
// friends... describe the ABSENCE of a declaration, so 'which commit
|
|
101
|
+
// introduced this version' is not a question that has an answer to defer
|
|
102
|
+
// ... this is a known, disclosed limitation, not a bug... strict mode
|
|
103
|
+
// WILL fail on nearly any real project that has a package.json." That
|
|
104
|
+
// disclosure lived only in a source comment nobody hits this error reads —
|
|
105
|
+
// the README's own quickstart explicitly invites pointing --assurance
|
|
106
|
+
// strict at "your own project," where this is the single most likely
|
|
107
|
+
// outcome. Named here so the person who hits it learns it is expected and
|
|
108
|
+
// permanent, not something to keep investigating.
|
|
109
|
+
const supplyChainCount = ranked.filter(([r]) => r.startsWith('origin resolution does not apply to a')).reduce((s, [, n]) => s + n, 0);
|
|
110
|
+
if (supplyChainCount > 0 && supplyChainCount >= badProvenance.length / 2) {
|
|
111
|
+
return `${base} — ${supplyChainCount} of them describe an ABSENT dependency declaration ` +
|
|
112
|
+
`(an unpinned version, a missing lockfile) that has no origin commit to resolve, by design. This is a ` +
|
|
113
|
+
`known, permanent limitation: strict mode cannot pass while any are present, on any real project with ` +
|
|
114
|
+
`such a dependency. Fix the underlying SCA finding(s) (pin the version / add a lockfile) if you want ` +
|
|
115
|
+
`strict to pass, or use --assurance standard/advisory for a project you don't control the dependencies of.`;
|
|
116
|
+
}
|
|
117
|
+
if (allSameReason) {
|
|
118
|
+
return `${base} — all ${badProvenance.length} share the same reason: "${topReason}".`;
|
|
119
|
+
}
|
|
120
|
+
const breakdown = ranked.slice(0, 5).map(([reason, n]) => `${n}× "${reason}"`).join(', ');
|
|
121
|
+
return `${base} — breakdown: ${breakdown}${ranked.length > 5 ? ', …' : ''}.`;
|
|
122
|
+
}
|
|
123
|
+
|
|
68
124
|
/**
|
|
69
125
|
* @param {string} mode - one of ASSURANCE_MODES; invalid/missing degrades to the default.
|
|
70
126
|
* @param {object|null} scanHealth - the engine's computed scan.scanHealth (FR-206).
|
|
@@ -157,7 +213,7 @@ function evaluateAssuranceMode(mode, scanHealth, findings = []) {
|
|
|
157
213
|
return {
|
|
158
214
|
ok: false,
|
|
159
215
|
mode: 'strict',
|
|
160
|
-
reason:
|
|
216
|
+
reason: _provenanceFailureReason(badProvenance, findings.length),
|
|
161
217
|
conditions,
|
|
162
218
|
};
|
|
163
219
|
}
|
|
@@ -165,7 +221,7 @@ function evaluateAssuranceMode(mode, scanHealth, findings = []) {
|
|
|
165
221
|
return { ok: true, mode: 'strict', reason: null, conditions };
|
|
166
222
|
}
|
|
167
223
|
|
|
168
|
-
const _internals = { _isValidMode };
|
|
224
|
+
const _internals = { _isValidMode, _provenanceFailureReason };
|
|
169
225
|
|
|
170
226
|
|
|
171
227
|
/***/ })
|