@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
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
"publisher": "OWASP Foundation",
|
|
5
5
|
"license": "Creative Commons Attribution-ShareAlike 4.0",
|
|
6
6
|
"url": "https://owasp.org/www-project-top-10-for-large-language-model-applications/",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"sourceVerifiedAt": "2026-09-07",
|
|
8
|
+
"sourceVersionDriftDetected": true,
|
|
9
|
+
"sourceVerificationNote": "Verified 2026-09-07 (WebFetch): confirmed page is OWASP Top 10 for LLM Applications, but the CURRENT published edition is now 2026 (2026-08-04), not the 2025 edition this file bundles. See scope field for the disclosure and what remediation this requires.",
|
|
10
|
+
"scope": "FULL TOP-10. All ten categories are present. Each category is a broad class rather than a single testable requirement, so a mapping indicates a detector exists for part of the class, not that the class is fully covered. VERSION DRIFT, disclosed 2026-09-07: this bundle is the 2025 edition. OWASP published a 2026 edition on 2026-08-04 (confirmed live, the project page now archives 2025 and lists 2026 as current); the 10-category taxonomy may have changed. This file has NOT been re-mapped to the 2026 edition, that requires a deliberate review of the new categories against this engine's detector families, not a rushed relabel. Treat this bundle as 2025-accurate only until that review lands.",
|
|
11
|
+
"controlsDigest": "7500b9c4268eba6f",
|
|
9
12
|
"controlCount": 10,
|
|
10
13
|
"controls": [
|
|
11
14
|
{
|
|
@@ -101,11 +104,10 @@
|
|
|
101
104
|
"summary": "Excessive Agency — agent autonomy is bounded by explicit guards.",
|
|
102
105
|
"codeTestable": "partial",
|
|
103
106
|
"evidence": [
|
|
104
|
-
"
|
|
105
|
-
"
|
|
107
|
+
"Apply-fix path requires HMAC-verified scan.",
|
|
108
|
+
"Removed module:mcp-tools after a second-review pass: that path points at THIS scanner's own source file (scanner/src/mcp/tools.js), which only resolves inside this tool's own checkout, never inside an arbitrary assessed project. It cannot evidence that the assessed LLM application bounds ITS OWN agent's autonomy. Category error (self-referential: about this tool, not the target)."
|
|
106
109
|
],
|
|
107
110
|
"mapsTo": [
|
|
108
|
-
"module:mcp-tools",
|
|
109
111
|
"module:apply-fix"
|
|
110
112
|
]
|
|
111
113
|
},
|
|
@@ -114,13 +116,10 @@
|
|
|
114
116
|
"summary": "Overreliance — human review checkpoints exist.",
|
|
115
117
|
"codeTestable": "partial",
|
|
116
118
|
"evidence": [
|
|
117
|
-
"security-fixer agent
|
|
118
|
-
"
|
|
119
|
+
"No automated signal in this engine after a second-review pass removed both prior mappings. module:security-fixer pointed at THIS scanner's own agent definition file (agents/security-fixer.md) and module:pre-edit-bodyguard at THIS scanner's own installed hook — both resolve only inside this tool's own checkout/plugin install, never inside an arbitrary assessed project, and neither evidences that the assessed LLM application itself has human review checkpoints. Category error (self-referential: about this tool, not the target), same class as the 03.03.08 fix in the NIST 800-171 framework.",
|
|
120
|
+
"Reported as an engine gap: human review checkpoints around an LLM application's outputs are code-observable in principle (an approval-gate library, a human-in-the-loop wrapper in the assessed system's own code), but no detector here looks for that in the SCANNED project."
|
|
119
121
|
],
|
|
120
|
-
"mapsTo": [
|
|
121
|
-
"module:security-fixer",
|
|
122
|
-
"module:pre-edit-bodyguard"
|
|
123
|
-
]
|
|
122
|
+
"mapsTo": []
|
|
124
123
|
},
|
|
125
124
|
{
|
|
126
125
|
"id": "LLM10",
|
package/src/posture/verifier.js
CHANGED
|
@@ -32,6 +32,7 @@ import * as path from 'node:path';
|
|
|
32
32
|
import * as os from 'node:os';
|
|
33
33
|
import { runConfined, sandboxAvailable } from '../sandbox/index.js';
|
|
34
34
|
import { isExplicitlyNoPoc } from './poc-cwe-map.js';
|
|
35
|
+
import { isSafeStateDir, stateDir, statePath, stateWritesEnabled } from './state-dir.js';
|
|
35
36
|
|
|
36
37
|
// ─── PoC static validation ──────────────────────────────────────────────────
|
|
37
38
|
//
|
|
@@ -228,5 +229,74 @@ export function verifierCoverageSummary(findings) {
|
|
|
228
229
|
return out;
|
|
229
230
|
}
|
|
230
231
|
|
|
232
|
+
// ─── Run persistence (adversarial premortem Q1, 2026-09-07) ────────────────
|
|
233
|
+
//
|
|
234
|
+
// `compliance-frameworks/*.json` has mapped `module:verifier` to
|
|
235
|
+
// `verifier-runs/` since this framework's control-to-artifact table was
|
|
236
|
+
// written, but nothing ever wrote to it: `cmdVerify` (bin/agentic-security.js)
|
|
237
|
+
// ran the loop and printed a summary, but never persisted one. That made
|
|
238
|
+
// NIST 800-171 `03.12.01` (Security Assessment) and NIST CSF 2.0 `RC.RP`
|
|
239
|
+
// (Recovery plans executed and improved) permanently unable to read
|
|
240
|
+
// 'present' via this leg of their mapping, on any project, ever — worse than
|
|
241
|
+
// a self-referential mapping (which can at least clear, dishonestly), a
|
|
242
|
+
// mapping that can never clear at all. This closes that gap for real, one
|
|
243
|
+
// record per `agentic-security verify` run, same shape as fix-metrics.js's
|
|
244
|
+
// append-per-attempt pattern (that file uses one growing JSONL; this one
|
|
245
|
+
// uses one file per run, because the table entry it satisfies is a
|
|
246
|
+
// directory, not a file, and a per-run file is what a reader would expect
|
|
247
|
+
// under a name like "verifier-runs/").
|
|
248
|
+
const RUN_DIR = 'verifier-runs';
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Persist one record of an `agentic-security verify` invocation. Best-effort
|
|
252
|
+
* and silent on failure, same convention as every other posture writer: a
|
|
253
|
+
* verify run's own findings must never be lost because the record of having
|
|
254
|
+
* run could not be written.
|
|
255
|
+
*
|
|
256
|
+
* @returns {boolean} whether the record was written (for tests, not callers).
|
|
257
|
+
*/
|
|
258
|
+
export function recordVerifierRun(scanRoot, summary) {
|
|
259
|
+
if (!scanRoot || !summary || typeof summary !== 'object') return false;
|
|
260
|
+
try {
|
|
261
|
+
const dir = stateDir(scanRoot);
|
|
262
|
+
if (!isSafeStateDir(dir)) return false;
|
|
263
|
+
if (!stateWritesEnabled()) return false;
|
|
264
|
+
const runsDir = statePath(scanRoot, RUN_DIR);
|
|
265
|
+
fs.mkdirSync(runsDir, { recursive: true });
|
|
266
|
+
const now = new Date();
|
|
267
|
+
// Millisecond Date timestamps collide under back-to-back calls (two
|
|
268
|
+
// `verify` invocations in the same test, or a scripted loop). hrtime is
|
|
269
|
+
// monotonic and nanosecond-resolution within this process, so appending
|
|
270
|
+
// it guarantees both a distinct filename AND correct chronological sort
|
|
271
|
+
// order (alphabetical == call order), which a random suffix alone would
|
|
272
|
+
// not: two records written in the same millisecond would sort by random
|
|
273
|
+
// bytes, not by which call happened first.
|
|
274
|
+
const stamp = now.toISOString().replace(/[^0-9TZ]/g, '-');
|
|
275
|
+
const seq = process.hrtime.bigint().toString().padStart(20, '0');
|
|
276
|
+
const record = { timestamp: now.toISOString(), ...summary };
|
|
277
|
+
// One writeFileSync of one complete file: a concurrent reader sees a
|
|
278
|
+
// whole record or nothing (never a partial one, unlike an append target).
|
|
279
|
+
fs.writeFileSync(path.join(runsDir, `${stamp}-${seq}.json`), JSON.stringify(record, null, 2), 'utf8');
|
|
280
|
+
return true;
|
|
281
|
+
} catch { return false; }
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Read every well-formed run record, oldest first. A file that does not
|
|
286
|
+
* parse is skipped, not thrown on — same tolerant-read convention as
|
|
287
|
+
* fix-metrics.js's readFixAttempts.
|
|
288
|
+
*/
|
|
289
|
+
export function readVerifierRuns(scanRoot) {
|
|
290
|
+
try {
|
|
291
|
+
const runsDir = statePath(scanRoot, RUN_DIR);
|
|
292
|
+
const files = fs.readdirSync(runsDir).filter((f) => f.endsWith('.json')).sort();
|
|
293
|
+
const out = [];
|
|
294
|
+
for (const f of files) {
|
|
295
|
+
try { out.push(JSON.parse(fs.readFileSync(path.join(runsDir, f), 'utf8'))); } catch { /* skip malformed */ }
|
|
296
|
+
}
|
|
297
|
+
return out;
|
|
298
|
+
} catch { return []; }
|
|
299
|
+
}
|
|
300
|
+
|
|
231
301
|
// For tests and the no-dead-modules check.
|
|
232
302
|
export const _internals = { MAX_POC_BYTES, BANNED_HOSTS, SANITIZER_TABLE, runSandboxed };
|