@clear-capabilities/agentic-security-scanner 0.133.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.
- package/CHANGELOG.md +299 -0
- package/bin/agentic-security-audit.js +2 -1
- package/bin/agentic-security-consistency.js +2 -1
- package/bin/agentic-security.js +348 -40
- package/dist/113.index.js +6 -6
- package/dist/117.index.js +3 -1
- package/dist/178.index.js +1 -1
- package/dist/220.index.js +5 -3
- package/dist/238.index.js +4 -4
- package/dist/317.index.js +270 -0
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +13 -7
- package/dist/499.index.js +86 -0
- package/dist/513.index.js +7 -3
- package/dist/526.index.js +6 -6
- package/dist/609.index.js +741 -0
- package/dist/637.index.js +1 -1
- package/dist/675.index.js +7 -5
- package/dist/839.index.js +4 -3
- package/dist/905.index.js +1173 -0
- package/dist/agentic-security.mjs +56 -56
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/ccpa.json +32 -0
- package/dist/compliance-frameworks/eu-ai-act.json +51 -0
- package/dist/compliance-frameworks/gdpr.json +45 -0
- package/dist/compliance-frameworks/hipaa-security-rule.json +56 -0
- package/dist/compliance-frameworks/nist-ai-600-1.json +51 -0
- package/dist/compliance-frameworks/nist-csf-2.json +73 -0
- package/dist/compliance-frameworks/nist-privacy-1-1.json +846 -0
- package/dist/compliance-frameworks/owasp-asvs-5.json +79 -0
- package/dist/compliance-frameworks/owasp-llm-top-10.json +69 -0
- package/package.json +14 -6
- package/src/badge.js +2 -1
- package/src/dataflow/cross-service-taint.js +2 -1
- package/src/dataflow/ifds-precise.js +6 -4
- package/src/dataflow/incremental.js +7 -5
- package/src/discovery/CLAUDE.md +48 -0
- package/src/discovery/confirm.js +47 -0
- package/src/discovery/disprove.js +79 -0
- package/src/discovery/hunter.js +116 -0
- package/src/discovery/index.js +331 -0
- package/src/discovery/judge.js +97 -0
- package/src/discovery/lenses.js +69 -0
- package/src/discovery/llm-invoke.js +120 -0
- package/src/discovery/memory.js +163 -0
- package/src/discovery/partition.js +92 -0
- package/src/engine.js +163 -7
- package/src/integrations/tickets.js +7 -6
- package/src/ir/ir-stats.js +1 -1
- package/src/ir/parser-cpp.js +1 -1
- package/src/leaderboard.js +3 -2
- package/src/llm-validator/consistency.js +6 -2
- package/src/llm-validator/index.js +30 -41
- package/src/llm-validator/providers.js +227 -0
- package/src/mcp/audit.js +2 -1
- package/src/mcp/tools.js +4 -3
- package/src/posture/CLAUDE.md +102 -1
- package/src/posture/accuracy-scorecard.js +59 -1
- package/src/posture/agents-memory.js +5 -3
- package/src/posture/auditor-walkthrough.js +9 -7
- package/src/posture/auth-posture-import.js +5 -4
- package/src/posture/autopilot.js +225 -0
- package/src/posture/comparison.js +181 -0
- package/src/posture/compliance-frameworks/nist-privacy-1-1.json +846 -0
- package/src/posture/compliance-policy.js +7 -9
- package/src/posture/custom-rules.js +7 -5
- package/src/posture/cve-alert-daemon.js +6 -5
- package/src/posture/dep-add-guard.js +2 -1
- package/src/posture/deterministic.js +3 -2
- package/src/posture/evidence-bundle.js +246 -0
- package/src/posture/execution-proof.js +25 -1
- package/src/posture/exploitability-probability.js +2 -1
- package/src/posture/feature-flags.js +3 -2
- package/src/posture/findings-memory.js +3 -3
- package/src/posture/fix-history.js +5 -2
- package/src/posture/fix-metrics.js +5 -5
- package/src/posture/fix-plan.js +2 -1
- package/src/posture/fleet.js +0 -0
- package/src/posture/grader-calibration.js +3 -4
- package/src/posture/intent-context.js +2 -1
- package/src/posture/learning.js +4 -3
- package/src/posture/license-attributions.js +5 -7
- package/src/posture/license-graph.js +2 -1
- package/src/posture/license-policy.js +2 -1
- package/src/posture/logic-claims.js +266 -0
- package/src/posture/model-rescan.js +4 -3
- package/src/posture/network-policy-import.js +3 -2
- package/src/posture/poc-inprocess.js +404 -2
- package/src/posture/pqc-migration-plan.js +7 -5
- package/src/posture/pr-augment.js +8 -5
- package/src/posture/privacy-framework.js +262 -0
- package/src/posture/proof-artifact.js +101 -0
- package/src/posture/prove-findings.js +28 -4
- package/src/posture/risk-dollars.js +2 -2
- package/src/posture/router.js +5 -4
- package/src/posture/ruleset-version.js +2 -2
- package/src/posture/runtime-correlation.js +2 -1
- package/src/posture/sbom-diff.js +12 -3
- package/src/posture/sca-policy.js +7 -4
- package/src/posture/scan-checkpoint.js +15 -0
- package/src/posture/state-dir.js +34 -0
- package/src/posture/telemetry-ingest.js +4 -3
- package/src/posture/threat-model-auto.js +4 -1
- package/src/posture/threat-model-grounding.js +11 -1
- package/src/posture/time-to-fix.js +3 -2
- package/src/posture/triage-memory.js +3 -2
- package/src/posture/waf-ingest.js +6 -5
- package/src/posture/watch-mode.js +4 -3
- package/src/report/index.js +9 -0
- package/src/sast/code-injection-multilang.js +29 -0
- package/src/sca/dep-confusion.js +2 -1
package/dist/220.index.js
CHANGED
|
@@ -15,6 +15,7 @@ export const modules = {
|
|
|
15
15
|
/* harmony import */ var node_fs_promises__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1455);
|
|
16
16
|
/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
|
|
17
17
|
/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
|
|
18
|
+
/* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1174);
|
|
18
19
|
// Watch mode — continuous incremental scan as the developer edits.
|
|
19
20
|
//
|
|
20
21
|
// Spawns a long-running scan watcher that:
|
|
@@ -40,7 +41,7 @@ export const modules = {
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
const STATUS_MD = 'watch-status.md';
|
|
45
46
|
const STATUS_JSON = 'watch-status.json';
|
|
46
47
|
const DEBOUNCE_MS = 350;
|
|
@@ -102,7 +103,8 @@ function renderStatusLine(delta) {
|
|
|
102
103
|
* Persist watch-status.{md,json}. Cheap atomic write (write tmp, rename).
|
|
103
104
|
*/
|
|
104
105
|
function persistStatus(scanRoot, delta) {
|
|
105
|
-
const dir =
|
|
106
|
+
const dir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_3__/* .stateDir */ .Pn)(scanRoot);
|
|
107
|
+
if (!(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_3__.stateWritesEnabled)()) return;
|
|
106
108
|
try { node_fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(dir, { recursive: true }); } catch {}
|
|
107
109
|
const status = {
|
|
108
110
|
ts: new Date().toISOString(),
|
|
@@ -139,7 +141,7 @@ function persistStatus(scanRoot, delta) {
|
|
|
139
141
|
* Read the latest watch-status (returns null if none).
|
|
140
142
|
*/
|
|
141
143
|
function readStatus(scanRoot) {
|
|
142
|
-
return _readJsonSafe(
|
|
144
|
+
return _readJsonSafe(statePath(scanRoot, STATUS_JSON));
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
/**
|
package/dist/238.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
|
|
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 =
|
|
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
|
-
|
|
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');
|
|
@@ -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__(
|
|
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
|
|
214
|
-
function _archivePath(scanRoot) { return
|
|
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
|
-
|
|
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 =
|
|
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
|
|
670
|
-
const scanFile = external_node_path_.join(
|
|
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 =
|
|
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 = {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export const id = 499;
|
|
2
|
+
export const ids = [499];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 3499:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ H: () => (/* binding */ lensByKey),
|
|
10
|
+
/* harmony export */ LENSES: () => (/* binding */ LENSES),
|
|
11
|
+
/* harmony export */ j: () => (/* binding */ buildHunterPrompt)
|
|
12
|
+
/* harmony export */ });
|
|
13
|
+
//
|
|
14
|
+
// The seven hunting lenses. Each hunter run is one (focus area × lens) pair.
|
|
15
|
+
//
|
|
16
|
+
// WHY DIVERSE LENSES RATHER THAN N IDENTICAL HUNTERS: redundancy raises
|
|
17
|
+
// confidence in what was already found and adds nothing to coverage. A lens
|
|
18
|
+
// that is told to look only at authorization asks different questions of the
|
|
19
|
+
// same code than one told to look at crypto, so the union covers failure modes
|
|
20
|
+
// no single prompt reaches. `wildcard` exists because a fixed taxonomy is a
|
|
21
|
+
// ceiling, and the classes worth finding are the ones not on the list.
|
|
22
|
+
const LENSES = Object.freeze([
|
|
23
|
+
{ key: 'injection', title: 'Injection', family: 'injection', cwe: 'CWE-74',
|
|
24
|
+
brief: 'Untrusted input reaching an interpreter: SQL, shell, template, XPath, LDAP, or deserialization. Follow the value, not the function name.' },
|
|
25
|
+
{ key: 'authz', title: 'Authorization', family: 'access-control', cwe: 'CWE-285',
|
|
26
|
+
brief: 'Missing, partial, or bypassable authorization: object references not scoped to the caller, tier checks applied on one path but not another, checks performed after the effect.' },
|
|
27
|
+
{ key: 'crypto', title: 'Cryptography', family: 'crypto', cwe: 'CWE-327',
|
|
28
|
+
brief: 'Misuse rather than choice of primitive: reused nonces, unauthenticated ciphertext, comparisons that are not constant time, keys derived from guessable material.' },
|
|
29
|
+
{ key: 'business-logic', title: 'Business logic', family: 'business-logic', cwe: 'CWE-840',
|
|
30
|
+
brief: 'The code does what it says and what it says is wrong: state machines that accept out-of-order transitions, quantities that may be negative, refunds that exceed charges, limits enforced client side.' },
|
|
31
|
+
{ key: 'feature-abuse', title: 'Feature abuse', family: 'abuse', cwe: 'CWE-799',
|
|
32
|
+
brief: 'A working feature used as a weapon: unbounded fan-out, expensive endpoints with no cost to the caller, invitations or exports that leak across tenants.' },
|
|
33
|
+
{ key: 'chained', title: 'Chained', family: 'attack-chain', cwe: 'CWE-1173',
|
|
34
|
+
brief: 'Two behaviours that are each acceptable alone and unacceptable together. State the chain as an ordered sequence of steps with the attacker capability required at each.' },
|
|
35
|
+
{ key: 'wildcard', title: 'Wildcard', family: 'other', cwe: 'CWE-710',
|
|
36
|
+
brief: 'Anything the other lenses do not cover. Prefer the surprising and specific over the generic; report nothing rather than something already obvious.' },
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
function lensByKey(key) {
|
|
40
|
+
if (typeof key !== 'string') return null;
|
|
41
|
+
return LENSES.find(l => l.key === key) || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_MAX_CHARS = 60_000;
|
|
45
|
+
|
|
46
|
+
function buildHunterPrompt(focusArea, lens, ctx = {}) {
|
|
47
|
+
const maxChars = Number.isInteger(ctx.maxChars) && ctx.maxChars > 0 ? ctx.maxChars : DEFAULT_MAX_CHARS;
|
|
48
|
+
const contents = ctx.fileContents || {};
|
|
49
|
+
const files = (focusArea?.files || []).filter(f => typeof contents[f] === 'string');
|
|
50
|
+
|
|
51
|
+
let budget = maxChars;
|
|
52
|
+
const blocks = [];
|
|
53
|
+
for (const f of files) {
|
|
54
|
+
const src = contents[f];
|
|
55
|
+
const slice = src.length > budget ? src.slice(0, Math.max(0, budget)) : src;
|
|
56
|
+
const truncated = slice.length < src.length;
|
|
57
|
+
blocks.push(`--- ${f}${truncated ? ' (truncated)' : ''} ---\n${slice}`);
|
|
58
|
+
budget -= slice.length;
|
|
59
|
+
if (budget <= 0) break;
|
|
60
|
+
}
|
|
61
|
+
const omitted = files.length - blocks.length;
|
|
62
|
+
|
|
63
|
+
return [
|
|
64
|
+
`You are hunting for security vulnerabilities in one area of a codebase.`,
|
|
65
|
+
`Area: ${focusArea?.label ?? 'unknown'} (${files.length} files)`,
|
|
66
|
+
``,
|
|
67
|
+
`Your lens is ${lens.title}. ${lens.brief}`,
|
|
68
|
+
`Report ONLY through this lens. Another hunter covers the others.`,
|
|
69
|
+
``,
|
|
70
|
+
`Rules:`,
|
|
71
|
+
`- Report a candidate only if you can name the entry point an attacker controls and the effect they achieve.`,
|
|
72
|
+
`- Do not report defence-in-depth gaps, style, or "could be hardened". Those are not candidates.`,
|
|
73
|
+
`- Cite a real file and line from the source below. A candidate with no location is discarded.`,
|
|
74
|
+
``,
|
|
75
|
+
`Return JSON: {"candidates":[{"title","file","line","rationale","entryPoint","sink"}]}`,
|
|
76
|
+
`Return {"candidates":[]} if you find nothing. An empty result is a valid and useful answer.`,
|
|
77
|
+
``,
|
|
78
|
+
omitted > 0 ? `NOTE: ${omitted} file(s) omitted, prompt budget exhausted (truncated context).\n` : ``,
|
|
79
|
+
...blocks,
|
|
80
|
+
].join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
/***/ })
|
|
85
|
+
|
|
86
|
+
};
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 +
|
|
238
|
-
var engine = __webpack_require__(
|
|
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
|
//
|