@clear-capabilities/agentic-security-scanner 0.134.0 → 0.136.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/bin/agentic-security-audit.js +2 -1
  3. package/bin/agentic-security-consistency.js +2 -1
  4. package/bin/agentic-security.js +269 -44
  5. package/dist/113.index.js +6 -6
  6. package/dist/117.index.js +3 -1
  7. package/dist/178.index.js +1 -1
  8. package/dist/220.index.js +5 -3
  9. package/dist/238.index.js +4 -4
  10. package/dist/317.index.js +270 -0
  11. package/dist/384.index.js +1 -1
  12. package/dist/435.index.js +13 -7
  13. package/dist/513.index.js +7 -3
  14. package/dist/526.index.js +6 -6
  15. package/dist/637.index.js +1 -1
  16. package/dist/675.index.js +7 -5
  17. package/dist/839.index.js +4 -3
  18. package/dist/905.index.js +1173 -0
  19. package/dist/agentic-security.mjs +14 -14
  20. package/dist/agentic-security.mjs.sha256 +1 -1
  21. package/dist/compliance-frameworks/ccpa.json +32 -0
  22. package/dist/compliance-frameworks/eu-ai-act.json +51 -0
  23. package/dist/compliance-frameworks/gdpr.json +45 -0
  24. package/dist/compliance-frameworks/hipaa-security-rule.json +56 -0
  25. package/dist/compliance-frameworks/nist-ai-600-1.json +51 -0
  26. package/dist/compliance-frameworks/nist-csf-2.json +73 -0
  27. package/dist/compliance-frameworks/nist-privacy-1-1.json +846 -0
  28. package/dist/compliance-frameworks/owasp-asvs-5.json +79 -0
  29. package/dist/compliance-frameworks/owasp-llm-top-10.json +69 -0
  30. package/package.json +9 -5
  31. package/src/badge.js +2 -1
  32. package/src/dataflow/cross-service-taint.js +2 -1
  33. package/src/dataflow/ifds-precise.js +6 -4
  34. package/src/dataflow/incremental.js +7 -5
  35. package/src/discovery/CLAUDE.md +10 -0
  36. package/src/discovery/index.js +175 -3
  37. package/src/discovery/llm-invoke.js +90 -1
  38. package/src/discovery/memory.js +163 -0
  39. package/src/engine.js +45 -8
  40. package/src/integrations/tickets.js +7 -6
  41. package/src/ir/ir-stats.js +1 -1
  42. package/src/ir/parser-cpp.js +1 -1
  43. package/src/leaderboard.js +3 -2
  44. package/src/llm-validator/consistency.js +6 -2
  45. package/src/llm-validator/index.js +1 -2
  46. package/src/mcp/audit.js +2 -1
  47. package/src/mcp/tools.js +4 -3
  48. package/src/posture/CLAUDE.md +26 -1
  49. package/src/posture/accuracy-scorecard.js +59 -1
  50. package/src/posture/agents-memory.js +5 -3
  51. package/src/posture/auditor-walkthrough.js +9 -7
  52. package/src/posture/auth-posture-import.js +5 -4
  53. package/src/posture/compliance-frameworks/nist-privacy-1-1.json +846 -0
  54. package/src/posture/compliance-policy.js +7 -9
  55. package/src/posture/custom-rules.js +7 -5
  56. package/src/posture/cve-alert-daemon.js +6 -5
  57. package/src/posture/dep-add-guard.js +2 -1
  58. package/src/posture/deterministic.js +3 -2
  59. package/src/posture/evidence-bundle.js +246 -0
  60. package/src/posture/exploitability-probability.js +2 -1
  61. package/src/posture/feature-flags.js +3 -2
  62. package/src/posture/findings-memory.js +3 -3
  63. package/src/posture/fix-history.js +5 -2
  64. package/src/posture/fix-metrics.js +5 -5
  65. package/src/posture/fix-plan.js +2 -1
  66. package/src/posture/grader-calibration.js +3 -4
  67. package/src/posture/intent-context.js +2 -1
  68. package/src/posture/learning.js +4 -3
  69. package/src/posture/license-attributions.js +5 -7
  70. package/src/posture/license-graph.js +2 -1
  71. package/src/posture/license-policy.js +2 -1
  72. package/src/posture/model-rescan.js +4 -3
  73. package/src/posture/network-policy-import.js +3 -2
  74. package/src/posture/pqc-migration-plan.js +7 -5
  75. package/src/posture/pr-augment.js +8 -5
  76. package/src/posture/privacy-framework.js +262 -0
  77. package/src/posture/risk-dollars.js +2 -2
  78. package/src/posture/router.js +5 -4
  79. package/src/posture/ruleset-version.js +2 -2
  80. package/src/posture/runtime-correlation.js +2 -1
  81. package/src/posture/sbom-diff.js +12 -3
  82. package/src/posture/sca-policy.js +7 -4
  83. package/src/posture/scan-checkpoint.js +15 -0
  84. package/src/posture/state-dir.js +34 -0
  85. package/src/posture/telemetry-ingest.js +4 -3
  86. package/src/posture/threat-model-auto.js +4 -1
  87. package/src/posture/threat-model-grounding.js +11 -1
  88. package/src/posture/time-to-fix.js +3 -2
  89. package/src/posture/triage-memory.js +3 -2
  90. package/src/posture/waf-ingest.js +6 -5
  91. package/src/posture/watch-mode.js +4 -3
  92. package/src/sast/code-injection-multilang.js +29 -0
  93. package/src/sca/dep-confusion.js +2 -1
@@ -0,0 +1,270 @@
1
+ export const id = 317;
2
+ export const ids = [317];
3
+ export const modules = {
4
+
5
+ /***/ 8317:
6
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
+
8
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
+ /* harmony export */ buildEvidenceBundle: () => (/* binding */ buildEvidenceBundle),
10
+ /* harmony export */ ensureKeyPair: () => (/* binding */ ensureKeyPair),
11
+ /* harmony export */ keyPaths: () => (/* binding */ keyPaths),
12
+ /* harmony export */ signEvidenceBundle: () => (/* binding */ signEvidenceBundle),
13
+ /* harmony export */ verifyEvidenceBundle: () => (/* binding */ verifyEvidenceBundle)
14
+ /* harmony export */ });
15
+ /* unused harmony exports BUNDLE_SCHEMA, canonicalBytes, canonicalJson */
16
+ /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
17
+ /* harmony import */ var node_os__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8161);
18
+ /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
19
+ /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7598);
20
+ // Per-finding evidence bundles, signed so a THIRD PARTY can verify them.
21
+ // PRD Phase 2 / D2.
22
+ //
23
+ // WHAT WAS MISSING
24
+ // ----------------
25
+ // `attestation.js` already turns a run into a checkable digest, and it is
26
+ // explicit about its own ceiling: the signature is a per-install SYMMETRIC HMAC,
27
+ // so it is "tamper-evidence for the operator, not third-party non-repudiation."
28
+ // Anyone who can verify it can also forge it. That is fine for detecting local
29
+ // corruption and useless for handing a finding to somebody else.
30
+ //
31
+ // This module closes that gap for the artefact that actually travels: a single
32
+ // finding plus the evidence behind it. A buyer, an auditor, or a downstream
33
+ // consumer can check the bundle with the PUBLIC key alone, having never had
34
+ // access to ours.
35
+ //
36
+ // WHY A SEPARATE MODULE RATHER THAN EXTENDING attestation.js
37
+ // ----------------------------------------------------------
38
+ // They answer different questions and must not be conflated. A run attestation
39
+ // says "this set of findings came from this engine, ruleset and bundle". An
40
+ // evidence bundle says "THIS finding is backed by THIS evidence, and here is
41
+ // proof the claim was not edited after the fact." One is about reproducibility,
42
+ // the other about portability. Merging them would produce an artefact that is
43
+ // worse at both.
44
+ //
45
+ // WHAT A BUNDLE PROVES, AND WHAT IT DOES NOT
46
+ // -------------------------------------------
47
+ // PROVES — the bundle's contents are exactly what the holder of the signing key
48
+ // attested, unmodified. Nothing in it has been added, removed, or edited since.
49
+ //
50
+ // DOES NOT PROVE — that the finding is real. A signature is an integrity claim,
51
+ // never a correctness claim. An `unproven` finding in a signed bundle is still
52
+ // an unproven finding; the signature only stops someone silently upgrading the
53
+ // word `unproven` to `execution-proven` in transit. Both statements are carried
54
+ // INSIDE the bundle so they travel with it and cannot be dropped by a
55
+ // summariser.
56
+ //
57
+ // The evidence chain is deliberately the honest one this engine already
58
+ // computes: the proof tier, what the sandbox observed, which backend ran it, the
59
+ // taint path if there is one. A bundle for a regex finding says so plainly
60
+ // rather than dressing it up.
61
+
62
+
63
+
64
+
65
+
66
+
67
+ const BUNDLE_SCHEMA = 'agentic-security/finding-evidence@1';
68
+
69
+ /** Where the Ed25519 signing key lives. Mirrors integrity.js's key handling. */
70
+ function keyDir() {
71
+ const xdg = process.env.XDG_CONFIG_HOME || node_path__WEBPACK_IMPORTED_MODULE_2__.join(node_os__WEBPACK_IMPORTED_MODULE_1__.homedir(), '.config');
72
+ return node_path__WEBPACK_IMPORTED_MODULE_2__.join(xdg, 'agentic-security');
73
+ }
74
+ const PRIVATE_KEY_FILE = 'attest-key.pem';
75
+ const PUBLIC_KEY_FILE = 'attest-key.pub.pem';
76
+
77
+ function keyPaths(dir = keyDir()) {
78
+ return {
79
+ privateKey: node_path__WEBPACK_IMPORTED_MODULE_2__.join(dir, PRIVATE_KEY_FILE),
80
+ publicKey: node_path__WEBPACK_IMPORTED_MODULE_2__.join(dir, PUBLIC_KEY_FILE),
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Load the signing key pair, generating one on first use.
86
+ *
87
+ * Ed25519 from node:crypto — no new dependency, and deliberately not a
88
+ * transparency-log scheme: those need a network round trip, and this project
89
+ * does not make runtime cloud calls. A log-backed option can layer on later
90
+ * without changing the bundle format.
91
+ */
92
+ function ensureKeyPair(dir = keyDir()) {
93
+ const p = keyPaths(dir);
94
+
95
+ // NO existsSync-then-read. This project's own scanner flagged the first
96
+ // version of this function for TOCTOU (CWE-367) and it was right: between an
97
+ // existence check and the read, an attacker who can write this directory can
98
+ // swap the file, and the thing being swapped is a SIGNING KEY. Winning that
99
+ // race means we sign with a key the attacker controls, which defeats the
100
+ // entire point of the module. Read first and treat absence as the exceptional
101
+ // case — there is then no window between the check and the use, because there
102
+ // is no check.
103
+ try {
104
+ const privateKeyPem = node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(p.privateKey, 'utf8');
105
+ const publicKeyPem = node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(p.publicKey, 'utf8');
106
+ if (privateKeyPem && publicKeyPem) {
107
+ return { privateKeyPem, publicKeyPem, created: false, ...p };
108
+ }
109
+ } catch { /* missing or unreadable — fall through and generate */ }
110
+
111
+ const { privateKey, publicKey } = node_crypto__WEBPACK_IMPORTED_MODULE_3__.generateKeyPairSync('ed25519');
112
+ const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' });
113
+ const publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' });
114
+ node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(dir, { recursive: true, mode: 0o700 });
115
+
116
+ // `wx` — exclusive create. If another process generated a key between our
117
+ // failed read and this write, we must NOT clobber it: every bundle already
118
+ // signed with that key would silently stop verifying. On collision, re-read
119
+ // and use the winner's key.
120
+ try {
121
+ node_fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(p.privateKey, privateKeyPem, { mode: 0o600, flag: 'wx' });
122
+ node_fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(p.publicKey, publicKeyPem, { mode: 0o644, flag: 'w' });
123
+ return { privateKeyPem, publicKeyPem, created: true, ...p };
124
+ } catch (e) {
125
+ if (e.code !== 'EEXIST') throw e;
126
+ return {
127
+ privateKeyPem: node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(p.privateKey, 'utf8'),
128
+ publicKeyPem: node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(p.publicKey, 'utf8'),
129
+ created: false,
130
+ ...p,
131
+ };
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Canonical bytes for signing.
137
+ *
138
+ * An ALLOWLIST, exactly as attestation.js canonicalises: a field that is not
139
+ * named here is not signed, so a future addition cannot silently enter the
140
+ * signed surface. Sorted keys, so serialisation order cannot change the
141
+ * signature.
142
+ */
143
+ function canonicalBytes(bundle) {
144
+ const signed = {
145
+ schema: bundle.schema,
146
+ finding: bundle.finding,
147
+ evidence: bundle.evidence,
148
+ engine: bundle.engine,
149
+ proves: bundle.proves,
150
+ doesNotProve: bundle.doesNotProve,
151
+ };
152
+ return Buffer.from(canonicalJson(signed), 'utf8');
153
+ }
154
+
155
+ /**
156
+ * Deterministic JSON: keys sorted at EVERY level, arrays order-preserving.
157
+ *
158
+ * Written by hand rather than using JSON.stringify's replacer argument, and the
159
+ * reason matters. The first version of this function did
160
+ * `JSON.stringify(signed, Object.keys(signed).sort())`, believing the array
161
+ * argument was a top-level field allowlist. It is not — a replacer ARRAY is a
162
+ * key filter applied at every nesting depth, so every nested object serialised
163
+ * as `{}` and the signature covered nothing but the two prose strings. A bundle
164
+ * whose severity was edited from `high` to `critical` verified as authentic.
165
+ *
166
+ * That is the worst possible failure for this module: a security feature that
167
+ * appears to work, produces a valid-looking signature, and protects nothing. It
168
+ * was caught by tampering with a bundle and expecting verification to fail —
169
+ * which is why the tamper cases in the test file are not optional extras.
170
+ */
171
+ function canonicalJson(value) {
172
+ if (value === null || typeof value !== 'object') return JSON.stringify(value ?? null);
173
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
174
+ const keys = Object.keys(value).sort();
175
+ return `{${keys.map(k => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(',')}}`;
176
+ }
177
+
178
+ const PROVES =
179
+ 'The contents of this bundle are exactly what the signer attested, unmodified.';
180
+ const DOES_NOT_PROVE =
181
+ 'That the finding is real. A signature is an integrity claim, never a correctness ' +
182
+ 'claim — read `evidence.proofTier` for how well-supported the finding actually is. ' +
183
+ 'An unproven finding in a signed bundle is still an unproven finding.';
184
+
185
+ /**
186
+ * Build an unsigned bundle from a finding.
187
+ *
188
+ * Everything here is copied from what the engine already computed. Nothing is
189
+ * inferred, and a missing field stays missing rather than being defaulted into
190
+ * something more confident-looking than the truth.
191
+ */
192
+ function buildEvidenceBundle(finding, { engineVersion, rulesetVersion, bundleSha, commit } = {}) {
193
+ if (!finding || typeof finding !== 'object') return null;
194
+ return {
195
+ schema: BUNDLE_SCHEMA,
196
+ finding: {
197
+ id: finding.id ?? null,
198
+ stableId: finding.stableId ?? null,
199
+ severity: finding.severity ?? null,
200
+ file: finding.file ?? null,
201
+ line: finding.line ?? null,
202
+ vuln: finding.vuln ?? null,
203
+ cwe: finding.cwe ?? null,
204
+ family: finding.family ?? null,
205
+ parser: finding.parser ?? null,
206
+ },
207
+ evidence: {
208
+ // The honest chain, in the vocabulary the engine already uses.
209
+ proofTier: finding.proofTier ?? null,
210
+ proofEvidence: finding.proofEvidence ?? null,
211
+ confidence: finding.confidence ?? null,
212
+ exploitability: finding.exploitability ?? null,
213
+ unreachable: finding.unreachable ?? null,
214
+ // Present only for the layers that produce them; absent means absent.
215
+ taintPath: finding.pathSteps ?? finding.exampleFlows ?? null,
216
+ discovery: finding.discovery ?? null,
217
+ },
218
+ engine: {
219
+ engineVersion: engineVersion ?? null,
220
+ rulesetVersion: rulesetVersion ?? null,
221
+ bundleSha: bundleSha ?? null,
222
+ commit: commit ?? null,
223
+ },
224
+ proves: PROVES,
225
+ doesNotProve: DOES_NOT_PROVE,
226
+ };
227
+ }
228
+
229
+ /** Sign a bundle. Returns a new object; the input is not mutated. */
230
+ function signEvidenceBundle(bundle, privateKeyPem) {
231
+ const sig = node_crypto__WEBPACK_IMPORTED_MODULE_3__.sign(null, canonicalBytes(bundle), privateKeyPem);
232
+ return {
233
+ ...bundle,
234
+ signature: {
235
+ algorithm: 'ed25519',
236
+ canonicalisation: BUNDLE_SCHEMA,
237
+ value: sig.toString('base64'),
238
+ },
239
+ };
240
+ }
241
+
242
+ /**
243
+ * Verify a bundle with a PUBLIC key. This is the whole point: the verifier
244
+ * never needs, and never had, the signing key.
245
+ *
246
+ * Returns {ok, reason}. Never throws — a malformed bundle from an untrusted
247
+ * source is an expected input, not an exceptional one.
248
+ */
249
+ function verifyEvidenceBundle(bundle, publicKeyPem) {
250
+ if (!bundle || typeof bundle !== 'object') return { ok: false, reason: 'bundle is not an object' };
251
+ if (bundle.schema !== BUNDLE_SCHEMA) return { ok: false, reason: `unrecognised schema: ${bundle.schema}` };
252
+ const sig = bundle.signature;
253
+ if (!sig?.value) return { ok: false, reason: 'bundle is unsigned' };
254
+ if (sig.algorithm !== 'ed25519') return { ok: false, reason: `unsupported algorithm: ${sig.algorithm}` };
255
+ if (!publicKeyPem) return { ok: false, reason: 'no public key supplied' };
256
+ let ok = false;
257
+ try {
258
+ ok = node_crypto__WEBPACK_IMPORTED_MODULE_3__.verify(null, canonicalBytes(bundle), publicKeyPem, Buffer.from(sig.value, 'base64'));
259
+ } catch (e) {
260
+ return { ok: false, reason: `verification error: ${e.message}` };
261
+ }
262
+ return ok
263
+ ? { ok: true, reason: null }
264
+ : { ok: false, reason: 'signature does not match the bundle contents — it was modified after signing' };
265
+ }
266
+
267
+
268
+ /***/ })
269
+
270
+ };
package/dist/384.index.js CHANGED
@@ -8,7 +8,7 @@ export const modules = {
8
8
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
9
  /* harmony export */ scanCredentials: () => (/* reexport safe */ _engine_js__WEBPACK_IMPORTED_MODULE_0__.Sv)
10
10
  /* harmony export */ });
11
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5695);
11
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2912);
12
12
  // Secrets submodule view of the engine — credential + entropy + TODO scanning.
13
13
 
14
14
 
package/dist/435.index.js CHANGED
@@ -171,6 +171,8 @@ function redactArgsBlob(s) {
171
171
  return redactString(s);
172
172
  }
173
173
 
174
+ // EXTERNAL MODULE: ./src/posture/state-dir.js
175
+ var state_dir = __webpack_require__(1174);
174
176
  ;// CONCATENATED MODULE: ./src/posture/agents-memory.js
175
177
  // AGENTS.md — writable continual-learning memory (harness-anatomy #2).
176
178
  //
@@ -203,6 +205,7 @@ function redactArgsBlob(s) {
203
205
 
204
206
 
205
207
 
208
+
206
209
  const MEMORY_FILE = '.agentic-security/AGENTS.md';
207
210
  const ARCHIVE_FILE = '.agentic-security/AGENTS.md.archive';
208
211
  const MAX_BYTES = 20 * 1024;
@@ -210,8 +213,8 @@ const MAX_ENTRY_BYTES = 2 * 1024;
210
213
  const ARCHIVE_MAX_BYTES = 200 * 1024;
211
214
  const HEADER = '# AGENTS.md\n\nAgent-authored continual-learning notes. Each entry: timestamp + agent name + one short paragraph. New entries appended at the bottom; oldest entries rotate to AGENTS.md.archive when this file exceeds 20 KB.\n\n';
212
215
 
213
- function _resolve(scanRoot) { return external_node_path_.join(scanRoot, MEMORY_FILE); }
214
- function _archivePath(scanRoot) { return external_node_path_.join(scanRoot, ARCHIVE_FILE); }
216
+ function _resolve(scanRoot) { return (0,state_dir/* statePath */.BQ)(scanRoot, 'AGENTS.md'); }
217
+ function _archivePath(scanRoot) { return (0,state_dir/* statePath */.BQ)(scanRoot, 'AGENTS.md.archive'); }
215
218
 
216
219
  function readAgentsMemory(scanRoot) {
217
220
  const fp = _resolve(scanRoot);
@@ -239,7 +242,8 @@ function appendAgentsMemory(scanRoot, { agent, body }) {
239
242
  const entry = `\n## ${ts} agent: ${agent}\n\n${snippet}\n`;
240
243
  try {
241
244
  const fp = _resolve(scanRoot);
242
- external_node_fs_.mkdirSync(external_node_path_.dirname(fp), { recursive: true });
245
+ if (!(0,state_dir.stateWritesEnabled)()) return;
246
+ external_node_fs_.mkdirSync(external_node_path_.dirname(fp), { recursive: true });
243
247
  if (!external_node_fs_.existsSync(fp)) external_node_fs_.writeFileSync(fp, HEADER);
244
248
  external_node_fs_.appendFileSync(fp, entry);
245
249
  _maybeRotate(scanRoot);
@@ -601,7 +605,7 @@ function _scratchpadAbs(sessionRoot, relPath) {
601
605
  }
602
606
 
603
607
  function _scratchpadTotalBytes(sessionRoot) {
604
- const base = external_node_path_.join(sessionRoot, '.agentic-security', 'agent-scratchpad');
608
+ const base = (0,state_dir/* statePath */.BQ)(sessionRoot, 'agent-scratchpad');
605
609
  if (!external_node_fs_.existsSync(base)) return 0;
606
610
  let total = 0;
607
611
  const walk = (dir) => {
@@ -666,8 +670,8 @@ function _confine(sessionRoot, candidate, label) {
666
670
  }
667
671
 
668
672
  function _readLastScanVerified(sessionRoot, { allowUnsigned = false } = {}) {
669
- const stateDir = external_node_path_.join(sessionRoot, '.agentic-security');
670
- const scanFile = external_node_path_.join(stateDir, 'last-scan.json');
673
+ const stateDirPath = (0,state_dir/* stateDir */.Pn)(sessionRoot);
674
+ const scanFile = external_node_path_.join(stateDirPath, 'last-scan.json');
671
675
  const sigFile = scanFile + '.sig';
672
676
  if (!external_node_fs_.existsSync(scanFile)) return { scan: null, status: 'missing' };
673
677
  const body = external_node_fs_.readFileSync(scanFile, 'utf8');
@@ -1457,6 +1461,7 @@ const read_scratchpad = {
1457
1461
 
1458
1462
 
1459
1463
 
1464
+
1460
1465
  const append_agents_memory = {
1461
1466
  name: 'append_agents_memory',
1462
1467
  description: 'Append a short narrative entry to AGENTS.md — agent-authored continual-learning notes. Use at session end to record "what worked / what didn\'t / what I\'d try differently next time" so the next agent can pick up the lesson. Bounded: 2 KB per entry, 20 KB total before rotation to AGENTS.md.archive. Use sparingly — narrative, not structured data.',
@@ -1754,6 +1759,7 @@ function validate(schema, value, path = 'arguments') {
1754
1759
 
1755
1760
 
1756
1761
 
1762
+
1757
1763
  const MAX_ARG_BYTES = 1024;
1758
1764
  const GENESIS = 'GENESIS';
1759
1765
  const REMOTE_TIMEOUT_MS = 1500;
@@ -1814,7 +1820,7 @@ function auditCall({ sessionRoot, tool, args, outcome, reason }) {
1814
1820
  let hasMarker = false;
1815
1821
  for (const m of MARKERS) { try { if (external_node_fs_.existsSync(external_node_path_.join(sessionRoot, m))) { hasMarker = true; break; } } catch {} }
1816
1822
  if (!hasMarker) return;
1817
- const dir = external_node_path_.join(sessionRoot, '.agentic-security');
1823
+ const dir = (0,state_dir/* stateDir */.Pn)(sessionRoot);
1818
1824
  external_node_fs_.mkdirSync(dir, { recursive: true });
1819
1825
  const logFile = external_node_path_.join(dir, 'mcp-audit.log');
1820
1826
  const entry = {
package/dist/513.index.js CHANGED
@@ -12,6 +12,7 @@ export const modules = {
12
12
  /* unused harmony exports badgeFromScanRoot, renderSvg, _internal */
13
13
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
14
14
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
15
+ /* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
15
16
  // Live SVG badge generator (v0.72).
16
17
  //
17
18
  // Every repo can drop a badge in its README pulling from the latest scan:
@@ -42,6 +43,7 @@ export const modules = {
42
43
 
43
44
 
44
45
 
46
+
45
47
  const COLORS = {
46
48
  critical: '#e05d44', // red
47
49
  high: '#fe7d37', // orange
@@ -56,7 +58,7 @@ const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
56
58
 
57
59
  function _readLastScan(scanRoot) {
58
60
  if (!scanRoot) return null;
59
- const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, '.agentic-security', 'last-scan.json');
61
+ const fp = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'last-scan.json');
60
62
  if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
61
63
  try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); }
62
64
  catch { return null; }
@@ -214,6 +216,7 @@ const _internal = { COLORS, _ageString, _readLastScan };
214
216
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
215
217
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
216
218
  /* harmony import */ var _badge_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3117);
219
+ /* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1174);
217
220
  // Leaderboard backend (v0.72).
218
221
  //
219
222
  // Generates the data shape that powers the future public leaderboard at
@@ -243,6 +246,7 @@ const _internal = { COLORS, _ageString, _readLastScan };
243
246
 
244
247
 
245
248
 
249
+
246
250
  // Grade thresholds. Critical findings dominate; high/medium contribute
247
251
  // secondarily. These numbers are heuristic — calibrate against the
248
252
  // public leaderboard corpus once data lands.
@@ -306,7 +310,7 @@ function _deltaTrend(history) {
306
310
  */
307
311
  function leaderboardRowFor({ scanRoot, repo, badgeBase = 'https://agentic-security.dev/badge' } = {}) {
308
312
  if (!repo) throw new Error('leaderboardRowFor: repo slug is required');
309
- const lastScanPath = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot || '.', '.agentic-security', 'last-scan.json');
313
+ const lastScanPath = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_3__/* .statePath */ .BQ)(scanRoot || '.', 'last-scan.json');
310
314
  let scan = null;
311
315
  try { scan = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(lastScanPath, 'utf8')); } catch {}
312
316
  const summary = (0,_badge_js__WEBPACK_IMPORTED_MODULE_2__/* .summarizeForBadge */ .iI)(scan);
@@ -314,7 +318,7 @@ function leaderboardRowFor({ scanRoot, repo, badgeBase = 'https://agentic-securi
314
318
  const topCwe = _topCwe(scan);
315
319
 
316
320
  // Optional scan history for the trend signal.
317
- const historyPath = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot || '.', '.agentic-security', 'scan-history.jsonl');
321
+ const historyPath = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_3__/* .statePath */ .BQ)(scanRoot || '.', 'scan-history.jsonl');
318
322
  let history = [];
319
323
  if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(historyPath)) {
320
324
  try {
package/dist/526.index.js CHANGED
@@ -52,7 +52,6 @@ export const modules = {
52
52
 
53
53
 
54
54
 
55
- const STATE_DIR = '.agentic-security';
56
55
  const LOG_FILE = 'fix-metrics.jsonl';
57
56
 
58
57
  // Below this many samples a percentile is an artifact of the sample, not a
@@ -65,7 +64,7 @@ const RELIABLE_N = 10;
65
64
  const FIX_STAGES = Object.freeze(['rescan', 'lint', 'tests', 'honesty', 'poc']);
66
65
 
67
66
  function _logPath(scanRoot) {
68
- return node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, STATE_DIR, LOG_FILE);
67
+ return (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, LOG_FILE);
69
68
  }
70
69
 
71
70
  /**
@@ -77,9 +76,10 @@ function _logPath(scanRoot) {
77
76
  function recordFixAttempt(scanRoot, record) {
78
77
  if (!scanRoot || !record || typeof record !== 'object') return false;
79
78
  try {
80
- const dir = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, STATE_DIR);
79
+ const dir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .stateDir */ .Pn)(scanRoot);
81
80
  if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.isSafeStateDir)(dir)) return false;
82
- node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(dir, { recursive: true });
81
+ if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.stateWritesEnabled)()) return;
82
+ node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(dir, { recursive: true });
83
83
  // One writeSync of one newline-terminated line: a concurrent reader sees
84
84
  // whole records or nothing, and a torn tail is dropped on read.
85
85
  node_fs__WEBPACK_IMPORTED_MODULE_0__.appendFileSync(_logPath(scanRoot), JSON.stringify(record) + '\n', 'utf8');
@@ -234,8 +234,8 @@ var external_node_child_process_ = __webpack_require__(1421);
234
234
  var external_node_fs_ = __webpack_require__(3024);
235
235
  // EXTERNAL MODULE: external "node:path"
236
236
  var external_node_path_ = __webpack_require__(6760);
237
- // EXTERNAL MODULE: ./src/engine.js + 501 modules
238
- var engine = __webpack_require__(5695);
237
+ // EXTERNAL MODULE: ./src/engine.js + 499 modules
238
+ var engine = __webpack_require__(2912);
239
239
  ;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
240
240
  // Deterministic honesty gates on fix / finding output (#7).
241
241
  //
package/dist/637.index.js CHANGED
@@ -11,7 +11,7 @@ export const modules = {
11
11
  /* harmony export */ renderPrDeltaText: () => (/* binding */ renderPrDeltaText)
12
12
  /* harmony export */ });
13
13
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
14
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5695);
14
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2912);
15
15
  // Shadowscan / security-DELTA on PR (v0.72).
16
16
  //
17
17
  // Most SAST PR-comment integrations show absolute counts — "12 findings
package/dist/675.index.js CHANGED
@@ -11,6 +11,7 @@ export const modules = {
11
11
  /* unused harmony exports readDeps, _queryOsvForDep, loadState, persistState, loadConfig, formatPayload, _internals */
12
12
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
13
13
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
14
+ /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
14
15
  // Continuous CVE-watch daemon.
15
16
  //
16
17
  // Polls OSV for the project's dependency tree on a schedule (or one-shot),
@@ -40,9 +41,9 @@ export const modules = {
40
41
 
41
42
 
42
43
 
44
+
43
45
  const OSV_API = 'https://api.osv.dev/v1/query';
44
- const CFG_PATH = '.agentic-security/cve-alerts.json';
45
- const STATE_PATH = '.agentic-security/cve-alerts-state.json';
46
+
46
47
 
47
48
  // Multi-ecosystem dep extraction. Each entry: { manifest filename → ecosystem }
48
49
  const ECOSYSTEM_BY_MANIFEST = {
@@ -280,7 +281,7 @@ async function _queryOsvForDep({ name, ecosystem }, { fetchImpl = globalThis.fet
280
281
  // ─── State persistence ───────────────────────────────────────────────────
281
282
 
282
283
  function loadState(scanRoot) {
283
- const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, STATE_PATH);
284
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'cve-alerts-state.json');
284
285
  if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return { known: new Set(), lastRun: null };
285
286
  try {
286
287
  const j = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8'));
@@ -289,7 +290,8 @@ function loadState(scanRoot) {
289
290
  }
290
291
 
291
292
  function persistState(scanRoot, state) {
292
- const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, STATE_PATH);
293
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'cve-alerts-state.json');
294
+ if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.stateWritesEnabled)()) return;
293
295
  node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(node_path__WEBPACK_IMPORTED_MODULE_1__.dirname(fp), { recursive: true });
294
296
  node_fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(fp, JSON.stringify({
295
297
  known: [...state.known].sort(),
@@ -298,7 +300,7 @@ function persistState(scanRoot, state) {
298
300
  }
299
301
 
300
302
  function loadConfig(scanRoot) {
301
- const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, CFG_PATH);
303
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'cve-alerts.json');
302
304
  if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
303
305
  try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); } catch { return null; }
304
306
  }
package/dist/839.index.js CHANGED
@@ -11,6 +11,7 @@ export const modules = {
11
11
  /* unused harmony export _internals */
12
12
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
13
13
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
14
+ /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
14
15
  // Findings memory — natural-language Q&A over the institutional knowledge
15
16
  // the scanner has accumulated. Backs the MCP query_findings_memory tool.
16
17
  //
@@ -28,10 +29,10 @@ export const modules = {
28
29
 
29
30
 
30
31
 
31
- const STATE = '.agentic-security';
32
+
32
33
 
33
34
  function _read(scanRoot, name) {
34
- try { return node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, STATE, name), 'utf8'); } catch { return null; }
35
+ try { return node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync((0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, name), 'utf8'); } catch { return null; }
35
36
  }
36
37
 
37
38
  function _readJson(scanRoot, name) {
@@ -112,7 +113,7 @@ function queryFindingsMemory(scanRoot, query) {
112
113
 
113
114
  // 3. Scan history.
114
115
  try {
115
- const histDir = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot, STATE, 'scan-history');
116
+ const histDir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'scan-history');
116
117
  if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(histDir)) {
117
118
  const files = node_fs__WEBPACK_IMPORTED_MODULE_0__.readdirSync(histDir).filter(f => f.endsWith('.json')).slice(-10);
118
119
  for (const f of files) {