@clear-capabilities/agentic-security-scanner 0.185.0 → 0.186.1
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 +22 -0
- package/bin/agentic-security.js +24 -1
- package/dist/1128.index.js +25 -21
- package/dist/2584.index.js +1 -1
- package/dist/4171.index.js +2 -2
- package/dist/5752.index.js +466 -0
- package/dist/7405.index.js +1 -1
- package/dist/8158.index.js +1713 -0
- package/dist/8798.index.js +1 -1
- package/dist/9382.index.js +2 -2
- package/dist/9579.index.js +1 -1
- package/dist/agentic-security.mjs +29 -29
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/{0b301d45b231f144a1b4.json → ca62ee93079be4c24eee.json} +7 -7
- package/package.json +7 -7
- package/src/dataflow/CLAUDE.md +6 -3
- package/src/dataflow/access-paths.js +12 -2
- package/src/dataflow/demand-query.js +279 -40
- package/src/dataflow/dep-fingerprint.js +1 -1
- package/src/dataflow/exploit-prover.js +6 -2
- package/src/dataflow/ir-cache.js +268 -0
- package/src/dataflow/privacy-taint.js +7 -0
- package/src/engine.js +62 -5
- package/src/ir/CLAUDE.md +4 -0
- package/src/ir/callgraph.js +36 -8
- package/src/ir/index.js +18 -4
- package/src/ir/parser-js.js +10 -0
- package/src/language/CLAUDE.md +13 -0
- package/src/language/nix-option-types.js +170 -0
- package/src/language/nix-script-taint.js +46 -3
- package/src/language/nix-secrets.js +20 -2
- package/src/lineage/CLAUDE.md +2 -1
- package/src/lineage/smt-orchestrator.js +47 -11
- package/src/lineage/smt-z3-backend.js +294 -0
- package/src/posture/CLAUDE.md +5 -1
- package/src/posture/business-logic.js +16 -7
- package/src/posture/dag-commands.js +442 -0
- package/src/posture/finding-defaults.js +19 -0
- package/src/posture/git-history.js +91 -1
- package/src/sast/CLAUDE.md +2 -0
- package/src/sast/_call-args.js +70 -0
- package/src/sast/init-scope.js +87 -0
- package/src/sast/listen-bind-address.js +49 -0
- package/src/sast/redirect-target.js +106 -0
- package/src/sast/reflected-input.js +76 -0
- package/src/sast/send-file-root.js +27 -0
- package/src/sast/sensitive-data-logging.js +38 -1
- package/dist/119.index.js +0 -197
- package/dist/128.index.js +0 -129
- package/dist/1293.index.js +0 -5689
- package/dist/1336.index.js +0 -16331
- package/dist/1351.index.js +0 -133
- package/dist/1387.index.js +0 -271
- package/dist/1437.index.js +0 -23
- package/dist/1445.index.js +0 -110
- package/dist/1505.index.js +0 -151
- package/dist/1557.index.js +0 -293
- package/dist/1621.index.js +0 -311
- package/dist/1751.index.js +0 -160
- package/dist/1765.index.js +0 -727
- package/dist/1861.index.js +0 -690
- package/dist/1906.index.js +0 -433
- package/dist/208.index.js +0 -1631
- package/dist/2223.index.js +0 -159
- package/dist/2238.index.js +0 -295
- package/dist/2530.index.js +0 -152
- package/dist/2565.index.js +0 -116
- package/dist/2574.index.js +0 -127
- package/dist/2620.index.js +0 -345
- package/dist/2773.index.js +0 -98
- package/dist/2775.index.js +0 -32
- package/dist/2966.index.js +0 -171
- package/dist/2972.index.js +0 -87
- package/dist/2991.index.js +0 -131
- package/dist/3063.index.js +0 -833
- package/dist/3075.index.js +0 -227
- package/dist/3121.index.js +0 -297
- package/dist/3314.index.js +0 -263
- package/dist/3480.index.js +0 -1822
- package/dist/3481.index.js +0 -24
- package/dist/3506.index.js +0 -268
- package/dist/3533.index.js +0 -591
- package/dist/3653.index.js +0 -450
- package/dist/3740.index.js +0 -722
- package/dist/3822.index.js +0 -131
- package/dist/3868.index.js +0 -292
- package/dist/3918.index.js +0 -192
- package/dist/4090.index.js +0 -288
- package/dist/4113.index.js +0 -1286
- package/dist/4121.index.js +0 -1190
- package/dist/4142.index.js +0 -690
- package/dist/4168.index.js +0 -509
- package/dist/4281.index.js +0 -993
- package/dist/4336.index.js +0 -436
- package/dist/4387.index.js +0 -532
- package/dist/4413.index.js +0 -302
- package/dist/4476.index.js +0 -4510
- package/dist/4491.index.js +0 -133
- package/dist/4538.index.js +0 -185
- package/dist/4684.index.js +0 -319
- package/dist/4740.index.js +0 -165
- package/dist/4780.index.js +0 -209
- package/dist/4802.index.js +0 -225
- package/dist/4812.index.js +0 -379
- package/dist/4835.index.js +0 -126
- package/dist/494.index.js +0 -226
- package/dist/4991.index.js +0 -549
- package/dist/5199.index.js +0 -305
- package/dist/5216.index.js +0 -109
- package/dist/5439.index.js +0 -301
- package/dist/5443.index.js +0 -97
- package/dist/5496.index.js +0 -377
- package/dist/5738.index.js +0 -240
- package/dist/5801.index.js +0 -588
- package/dist/5817.index.js +0 -714
- package/dist/5858.index.js +0 -223
- package/dist/5933.index.js +0 -143
- package/dist/601.index.js +0 -23
- package/dist/6173.index.js +0 -273
- package/dist/6291.index.js +0 -1871
- package/dist/6312.index.js +0 -666
- package/dist/634.index.js +0 -16182
- package/dist/644.index.js +0 -87
- package/dist/6508.index.js +0 -283
- package/dist/6560.index.js +0 -645
- package/dist/6576.index.js +0 -422
- package/dist/6579.index.js +0 -271
- package/dist/6655.index.js +0 -3221
- package/dist/6745.index.js +0 -305
- package/dist/6818.index.js +0 -21642
- package/dist/6932.index.js +0 -1190
- package/dist/6972.index.js +0 -167
- package/dist/6995.index.js +0 -163
- package/dist/7249.index.js +0 -191
- package/dist/7372.index.js +0 -1197
- package/dist/7546.index.js +0 -541
- package/dist/76.index.js +0 -448
- package/dist/7879.index.js +0 -163
- package/dist/8025.index.js +0 -130
- package/dist/8054.index.js +0 -248
- package/dist/808.index.js +0 -1330
- package/dist/8097.index.js +0 -201
- package/dist/8126.index.js +0 -191
- package/dist/8178.index.js +0 -78
- package/dist/8382.index.js +0 -93
- package/dist/8408.index.js +0 -261
- package/dist/8540.index.js +0 -174
- package/dist/8635.index.js +0 -802
- package/dist/8688.index.js +0 -410
- package/dist/8699.index.js +0 -779
- package/dist/8891.index.js +0 -457
- package/dist/8916.index.js +0 -885
- package/dist/9146.index.js +0 -170
- package/dist/9168.index.js +0 -841
- package/dist/9292.index.js +0 -369
- package/dist/9422.index.js +0 -866
- package/dist/9440.index.js +0 -453
- package/dist/9479.index.js +0 -152
- package/dist/9481.index.js +0 -220
- package/dist/9539.index.js +0 -100
- package/dist/9547.index.js +0 -103
- package/dist/9692.index.js +0 -90
- package/dist/9800.index.js +0 -462
- package/dist/9899.index.js +0 -138
- package/dist/9981.index.js +0 -206
- package/dist/999.index.js +0 -117
- package/dist/9999.index.js +0 -1698
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,28 @@
|
|
|
9
9
|
> make the history less accurate, not more.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
## 0.186.1 - Git history annotation blames once per file, which restores scan speed (fix)
|
|
13
|
+
|
|
14
|
+
0.186.0 was tagged but never published: its release workflow stopped at the time-to-first-finding gate (15 to 18 s against a 13 s limit), so the first npm release carrying that work is 0.186.1. Nothing in 0.186.0's content changed apart from the fix below.
|
|
15
|
+
|
|
16
|
+
- **Why it got slower.** Findings that used to carry no line number now carry one, so the git history step, which asks git who introduced each finding, ran for about twice as many findings. It ran one `git blame` per finding and one `git show` per commit, about 14 ms each. Measured on this repository's own source: 7.9 s on 0.185.0, 10.9 s on 0.186.0.
|
|
17
|
+
- **The fix.** One `git blame` per file for all of its lines and one `git log --no-walk` for all commit messages. A file whose batch fails is blamed line by line, so a bad line number cannot lose the others. The annotations are the same; `test/git-history-batch.test.js` checks the batched answers against per-line `git blame`. Measured after the change: 8.3 s locally.
|
|
18
|
+
- **Also in this release.** Two test fixes found by hosted CI: the artifact CLI tests now warm the OSV cache once, because a cold runner has none, and `owned-build/6` accepts a watchdog stop on a slow runner.
|
|
19
|
+
|
|
20
|
+
## 0.186.0 - The graph-precision capabilities are reachable from the CLI, a z3 backend checks path feasibility, and demand queries and incremental analysis got faster (Preview)
|
|
21
|
+
|
|
22
|
+
Everything new is Preview, opt-in and off by default. A default scan's output is unchanged by the new commands and by the solver. No GA capability changed its contract.
|
|
23
|
+
|
|
24
|
+
- **Reachable from the CLI.** `dataflow view`, `dataflow contracts`, `dataflow sink-query`, `dataflow plan-cuts`, `witness build` and `witness verify` expose the typed graph views, boundary contracts, demand reverse taint, min-cut control planner and signed witness bundles. Every report carries a completion status (`complete`, `partial`, `cancelled`, `unavailable`, `invalid`) and `negativeClaimAllowed`; "no modeled path" is never reported as "no vulnerability". `witness build` exits 1 and writes nothing when no modeled path exists. The graph views other than the dataflow view and a tool in the MCP server remain library-only.
|
|
25
|
+
- **A real z3 feasibility backend (`AGENTIC_SECURITY_LINEAGE_SMT=z3`).** Path conditions are translated to SMT-LIB and checked with a per-query timeout and memory cap. Only a proven `unsat` over a verified encoding can reduce a path's exploitability; a timeout, an unknown result, a missing solver or a condition the encoding cannot express changes nothing. The synchronous graph build does not call it: it is reached through the async path-evidence entry points. NaN and loose-equality coercion are not modeled.
|
|
26
|
+
- **Demand queries are faster.** A query with the index built per query is 23.4x faster than full analysis on this repository (was 1.9x), and 3.7x averaged over a 30-query sample that includes fallbacks. Instance-field reads in JS and TS no longer force a full fallback; 75 of 982 sink sites still use the full path, all at the caller-depth limit. Findings equal full analysis on all 982 sites. Call-graph name resolution got a name index that also speeds up the cold engine.
|
|
27
|
+
- **Incremental analysis is faster.** A per-file IR cache (JS and TS, under `AGENTIC_SECURITY_INCREMENTAL`) removes re-parsing. End to end it is now 4.3x on `scanner/src`; on the two synthetic projects it is 2.5x and 2.4x, still short of the 3x target. Projects above about 5,000 functions still run cold at the default summary budget.
|
|
28
|
+
- **Fewer false positives on the labeled samples.** Flagged sites that were true rose from 5 of 25 to 5 of 8 for JavaScript and from 6 of 17 to 6 of 8 for Nix, with recall unchanged (5 of 5 and 6 of 6). Part of the JavaScript gain is a measurement correction: line-less findings were credited to every same-class label in their file. The rules are general (a literal origin prefix on a redirect, request-input-only reflection, module-load blocking calls, confined file serving, bounded Nix option types) and each has a metamorphic test; the held-out split was not independent, which the benchmark summary now says. The multi-service fixture still finds 3 of 8: the misses need the cross-service join in a normal scan and a queue-consumer source model.
|
|
29
|
+
- **The state-machine heuristic is narrowed.** A `.status` write is compared only with status sets declared in the same file, and test files are skipped. Before, the first set found anywhere in a project was applied to every write.
|
|
30
|
+
- **Fixed.** The lineage deadline tests no longer assume which of two valid cancellations wins on a slow runner, and the regression report no longer embeds the exact bundle size, so a rebuild cannot make it stale. `@babel/core` and `z3-solver` moved to their latest releases and `@azure/msal-node` is pinned past an advisory in the extension's dev tooling.
|
|
31
|
+
- **Self-scan baseline.** The scanner's own source falls to 688 findings; the only increases are three low advisories in the two new files that read files from disk.
|
|
32
|
+
- **Not claimed.** Reachability, satisfiability and runtime observation are not exploit proof. A signature on a witness bundle proves integrity, not that the transfer models are right.
|
|
33
|
+
|
|
12
34
|
## 0.185.0 - Graph analysis says when it did not finish, and gains recursion handling, call-site correlation, demand queries, incremental invalidation, boundary contracts, control planning and replayable witnesses (Preview)
|
|
13
35
|
|
|
14
36
|
All new behavior is Preview and is off by default or purely additive. No GA capability changed its contract.
|
package/bin/agentic-security.js
CHANGED
|
@@ -206,6 +206,22 @@ Commands:
|
|
|
206
206
|
dataflow plan-cuts [path] --candidates <file.json> --output <file> [--query <file.json>] [--format json|markdown]
|
|
207
207
|
Plan a minimum-cost, hypothetical control placement (graph cut) over the
|
|
208
208
|
already-scanned lineage graph. Never verifies a remediation.
|
|
209
|
+
The plan carries a completion contract (never a negative claim).
|
|
210
|
+
dataflow view [path] [--node <id> [--direction in|out|both] [--relations a,b] [--origins a,b]] [--output <file>]
|
|
211
|
+
Opt-in typed graph view over the already-scanned lineage graph: identity,
|
|
212
|
+
completion, SCC condensation of the data-flow relation, or one node's neighbours.
|
|
213
|
+
dataflow contracts [path] [--contracts <file>] [--output <file>]
|
|
214
|
+
Opt-in boundary contracts: bind declared HTTP/queue/storage edges to parsed
|
|
215
|
+
consumer sites and seed taint there. A declaration is a modeled input only.
|
|
216
|
+
dataflow sink-query [path] (--list | --file <rel> --line <n>) [--mode demand|full|compare] [--arg-index <n>]
|
|
217
|
+
[--sink-id <id>] [--access-path <p>] [--rule-family <f>] [--max-work <n>]
|
|
218
|
+
[--max-caller-depth <n>] [--on-limit full|partial] [--output <file>]
|
|
219
|
+
Opt-in demand-driven reverse taint for one sink site. Verdicts: flow-found,
|
|
220
|
+
no-flow-in-modeled-scope, unknown. There is no "safe".
|
|
221
|
+
witness build [path] --out <dir> [--sink <id>] | --list-sinks
|
|
222
|
+
witness verify <bundle.json> [--public-key <path>] [--artifacts <dir> --root <project>] [--json]
|
|
223
|
+
Opt-in signed, replayable path witness bundles (Ed25519, same key as attest).
|
|
224
|
+
A valid signature proves integrity only, never that the finding is real.
|
|
209
225
|
dataflow scenario apply [path] --operations <file.json> --output <file> [--format json|markdown]
|
|
210
226
|
Simulate a hypothetical architecture change against the
|
|
211
227
|
already-scanned lineage graph — never mutates the real scan.
|
|
@@ -5162,6 +5178,8 @@ async function cmdDataflowPlanCuts(args) {
|
|
|
5162
5178
|
if (!loaded.ok) { process.stderr.write(`agentic-security dataflow plan-cuts: ${loaded.message}\n`); return 1; }
|
|
5163
5179
|
const { planCuts } = await import('../src/lineage/cut-planner.js');
|
|
5164
5180
|
const plan = planCuts(loaded.graph, query, candidates);
|
|
5181
|
+
// Completion contract: a plan is `complete` only for a finite cut over a graph known complete; never a negative claim.
|
|
5182
|
+
plan.contract = (await import('../src/posture/dag-commands.js')).planContract(plan);
|
|
5165
5183
|
let data;
|
|
5166
5184
|
if (format === 'json') data = JSON.stringify(plan, null, 2);
|
|
5167
5185
|
else {
|
|
@@ -5171,6 +5189,7 @@ async function cmdDataflowPlanCuts(args) {
|
|
|
5171
5189
|
for (const r of plan.validation?.residualPaths ?? []) lines.push(`- to \`${r.sink}\` via ${r.nodes.map((n) => `\`${n}\``).join(' -> ')}`);
|
|
5172
5190
|
lines.push('', '## Limitations', '');
|
|
5173
5191
|
for (const l of plan.limitations) lines.push(`- ${l}`);
|
|
5192
|
+
lines.push('', `Completion: ${plan.contract.status}. A negative claim is never allowed for a hypothetical plan.`);
|
|
5174
5193
|
data = lines.join('\n') + '\n';
|
|
5175
5194
|
}
|
|
5176
5195
|
try {
|
|
@@ -8012,6 +8031,7 @@ async function main() {
|
|
|
8012
8031
|
}
|
|
8013
8032
|
case 'boundaries': process.exit(await cmdBoundaries(args));
|
|
8014
8033
|
case 'explore': process.exit(await cmdExplore(args));
|
|
8034
|
+
case 'witness': process.exit(await (await import('../src/posture/dag-commands.js')).cmdWitness(args));
|
|
8015
8035
|
case 'dataflow': {
|
|
8016
8036
|
const sub = args._[1];
|
|
8017
8037
|
if (sub === 'export') { process.exit(await cmdDataflowExport(args)); }
|
|
@@ -8053,6 +8073,9 @@ async function main() {
|
|
|
8053
8073
|
break;
|
|
8054
8074
|
}
|
|
8055
8075
|
else if (sub === 'plan-cuts') { process.exit(await cmdDataflowPlanCuts(args)); }
|
|
8076
|
+
else if (sub === 'view') { process.exit(await (await import('../src/posture/dag-commands.js')).cmdDataflowView(args)); }
|
|
8077
|
+
else if (sub === 'contracts') { process.exit(await (await import('../src/posture/dag-commands.js')).cmdDataflowContracts(args)); }
|
|
8078
|
+
else if (sub === 'sink-query') { process.exit(await (await import('../src/posture/dag-commands.js')).cmdDataflowSinkQuery(args)); }
|
|
8056
8079
|
else if (sub === 'impact') {
|
|
8057
8080
|
const impactSub = args._[2];
|
|
8058
8081
|
if (impactSub === 'assess') { process.exit(await cmdDataflowImpactAssess(args)); }
|
|
@@ -8073,7 +8096,7 @@ async function main() {
|
|
|
8073
8096
|
break;
|
|
8074
8097
|
}
|
|
8075
8098
|
else if (sub === 'twin') { process.exit(await cmdDataflowTwin(args)); }
|
|
8076
|
-
process.stderr.write(`agentic-security dataflow: unknown subcommand "${sub}" — only "export", "diff", "watch", "scenario", "impact", "observations", and "twin" are supported.\n`);
|
|
8099
|
+
process.stderr.write(`agentic-security dataflow: unknown subcommand "${sub}" — only "export", "diff", "watch", "scenario", "plan-cuts", "view", "contracts", "sink-query", "impact", "observations", and "twin" are supported.\n`);
|
|
8077
8100
|
process.exit(2);
|
|
8078
8101
|
}
|
|
8079
8102
|
case 'governance': {
|
package/dist/1128.index.js
CHANGED
|
@@ -31,10 +31,14 @@ function makeIdentityTokenizer() {
|
|
|
31
31
|
// EXPORTS
|
|
32
32
|
__webpack_require__.d(__webpack_exports__, {
|
|
33
33
|
WITNESS_SCHEMA: () => (/* binding */ WITNESS_SCHEMA),
|
|
34
|
+
buildWitnessBundle: () => (/* binding */ buildWitnessBundle),
|
|
35
|
+
exportProvenanceArtifact: () => (/* binding */ exportProvenanceArtifact),
|
|
36
|
+
parseWitnessBundle: () => (/* binding */ parseWitnessBundle),
|
|
37
|
+
signWitnessBundle: () => (/* binding */ signWitnessBundle),
|
|
34
38
|
verifyWitness: () => (/* binding */ verifyWitness)
|
|
35
39
|
});
|
|
36
40
|
|
|
37
|
-
// UNUSED EXPORTS:
|
|
41
|
+
// UNUSED EXPORTS: replayWitness, transferModelFingerprints, verifyWitnessSignature
|
|
38
42
|
|
|
39
43
|
// EXTERNAL MODULE: external "node:crypto"
|
|
40
44
|
var external_node_crypto_ = __webpack_require__(77598);
|
|
@@ -118,7 +122,7 @@ function iso(v) {
|
|
|
118
122
|
* dropped and only COUNTED (their names could themselves carry a value). Identities are tokenized with the
|
|
119
123
|
* existing keyed-hash convention; a raw identity with no tokenizer is dropped, never kept.
|
|
120
124
|
*/
|
|
121
|
-
function
|
|
125
|
+
function sanitizeCausalGroups(groups, { tokenize = (0,identity_token.makeIdentityTokenizer)() } = {}) {
|
|
122
126
|
const out = []; const rejected = []; let droppedFields = 0;
|
|
123
127
|
const GROUP_KEYS = new Set(['id', 'sessionRef', 'sessionId', 'traceId', 'environment', 'deploymentRef', 'deploymentConfigDigest',
|
|
124
128
|
'identity', 'identityToken', 'firstObservedAt', 'lastObservedAt', 'edgeObservations']);
|
|
@@ -218,7 +222,7 @@ function assessRuntimeSupport({ pathEdgeIds, edgeStates, causalGroups = [], expe
|
|
|
218
222
|
if (!ids.length || !isObj(edgeStates) || !edgeStates.evaluated) {
|
|
219
223
|
return { ...base, status: 'not_consulted', statements: ['No telemetry was consulted for this path. That is not an observation of absence.'] };
|
|
220
224
|
}
|
|
221
|
-
const sanitized =
|
|
225
|
+
const sanitized = sanitizeCausalGroups(causalGroups, { tokenize });
|
|
222
226
|
base.groupsConsidered = sanitized.groups.length; base.rejectedGroups = sanitized.rejected.length; base.droppedFields = sanitized.droppedFields;
|
|
223
227
|
|
|
224
228
|
for (const id of ids) {
|
|
@@ -278,7 +282,7 @@ function assessRuntimeSupport({ pathEdgeIds, edgeStates, causalGroups = [], expe
|
|
|
278
282
|
* R10.6: external proof references are bound to the sink, the value and the configuration. A reference that is
|
|
279
283
|
* stale or mismatched is REJECTED with its reasons; it is never re-attached to the changed graph.
|
|
280
284
|
*/
|
|
281
|
-
function
|
|
285
|
+
function bindExploitProofs(proofs, expected) {
|
|
282
286
|
const bound = []; const rejected = [];
|
|
283
287
|
for (const [i, p] of arr(proofs).entries()) {
|
|
284
288
|
const id = isObj(p) && typeof p.id === 'string' && REF_RE.test(p.id) ? p.id : `proof-${i}`;
|
|
@@ -302,7 +306,7 @@ function witness_runtime_bindExploitProofs(proofs, expected) {
|
|
|
302
306
|
}
|
|
303
307
|
|
|
304
308
|
/** Validate the nullable DAG-04 slot. Returns { ok, errors }. */
|
|
305
|
-
function
|
|
309
|
+
function validateControlEvidence(ce, pathEdgeIds) {
|
|
306
310
|
if (ce === null) return { ok: true, errors: [] };
|
|
307
311
|
const errors = [];
|
|
308
312
|
if (!isObj(ce)) return { ok: false, errors: ['control evidence must be an object or null'] };
|
|
@@ -321,7 +325,7 @@ function witness_runtime_validateControlEvidence(ce, pathEdgeIds) {
|
|
|
321
325
|
* The four statuses, side by side. Returns exactly the four keys in EVIDENCE_STATUS_KEYS and nothing that
|
|
322
326
|
* ranks, scores or combines them.
|
|
323
327
|
*/
|
|
324
|
-
function
|
|
328
|
+
function buildEvidenceStatuses({ pathComplete, controlEvidence, runtime, exploit }) {
|
|
325
329
|
return {
|
|
326
330
|
modeledPath: { status: pathComplete ? 'connected_complete' : 'connected_partial' },
|
|
327
331
|
feasibility: { status: controlEvidence ? controlEvidence.status : 'not_assessed' },
|
|
@@ -420,14 +424,14 @@ const fileOfScope = (scope) => (typeof scope === 'string' ? scope.split('::')[0]
|
|
|
420
424
|
function exportProvenanceArtifact({ hops, incomplete = [] }) {
|
|
421
425
|
const seen = new Map();
|
|
422
426
|
for (const h of witness_bundle_arr(hops)) {
|
|
423
|
-
const norm = Object.fromEntries(HOP_FIELDS.map((f) => [f, h?.[f] ?? null]));
|
|
424
|
-
seen.set(canonicalJson(norm), norm);
|
|
427
|
+
const norm = Object.fromEntries(path_store/* HOP_FIELDS */.KO.map((f) => [f, h?.[f] ?? null]));
|
|
428
|
+
seen.set((0,evidence_bundle/* canonicalJson */.dj)(norm), norm);
|
|
425
429
|
}
|
|
426
430
|
const hopList = [...seen.keys()].sort().map((k) => seen.get(k));
|
|
427
431
|
const truncations = witness_bundle_arr(incomplete)
|
|
428
432
|
.map((u) => ({ scope: u.qid ?? u.scope ?? null, context: u.context ?? null, reason: u.reasons?.[0] ?? u.reason ?? 'analysis-incomplete' }))
|
|
429
|
-
.sort((a, b) => (canonicalJson(a) < canonicalJson(b) ? -1 : 1));
|
|
430
|
-
return { schema: PROVENANCE_ARTIFACT_SCHEMA, hops: hopList, truncations, digest: witness_bundle_sha(canonicalJson({ hops: hopList, truncations })) };
|
|
433
|
+
.sort((a, b) => ((0,evidence_bundle/* canonicalJson */.dj)(a) < (0,evidence_bundle/* canonicalJson */.dj)(b) ? -1 : 1));
|
|
434
|
+
return { schema: PROVENANCE_ARTIFACT_SCHEMA, hops: hopList, truncations, digest: witness_bundle_sha((0,evidence_bundle/* canonicalJson */.dj)({ hops: hopList, truncations })) };
|
|
431
435
|
}
|
|
432
436
|
|
|
433
437
|
function artifactDigest(a) {
|
|
@@ -568,8 +572,8 @@ function buildWitnessBundle(o = {}) {
|
|
|
568
572
|
}
|
|
569
573
|
const budget = { ...result.budget }; delete budget.expansionsUsed;
|
|
570
574
|
const config = { pathQuery: budget, storeLimits: storeLimits.limits, ...(witness_bundle_isObj(o.analysisConfig) ? { extra: o.analysisConfig } : {}) };
|
|
571
|
-
const analysisConfigDigest = witness_bundle_sha(canonicalJson(config));
|
|
572
|
-
const graphDigest = computeGraphDigest(graph);
|
|
575
|
+
const analysisConfigDigest = witness_bundle_sha((0,evidence_bundle/* canonicalJson */.dj)(config));
|
|
576
|
+
const graphDigest = (0,export_json.computeGraphDigest)(graph);
|
|
573
577
|
|
|
574
578
|
const files = [...new Set([srcNode, sinkNode, ...edges.flatMap((e) => [store.getNode(e.fromNodeId), store.getNode(e.toNodeId)])].map((n) => fileOfScope(n?.scope)).filter(Boolean))].sort();
|
|
575
579
|
const sources = []; const unboundSourceFiles = [];
|
|
@@ -608,7 +612,7 @@ function buildWitnessBundle(o = {}) {
|
|
|
608
612
|
const ceCheck = validateControlEvidence(ce, edges.map((e) => e.edgeId));
|
|
609
613
|
if (!ceCheck.ok) return { ok: false, reason: `control evidence: ${ceCheck.errors.join('; ')}` };
|
|
610
614
|
|
|
611
|
-
const tokenize = o.tokenize ?? makeIdentityTokenizer();
|
|
615
|
+
const tokenize = o.tokenize ?? (0,identity_token.makeIdentityTokenizer)();
|
|
612
616
|
let runtime;
|
|
613
617
|
if (o.runtime) {
|
|
614
618
|
const groups = sanitizeCausalGroups(o.runtime.causalGroups, { tokenize });
|
|
@@ -622,7 +626,7 @@ function buildWitnessBundle(o = {}) {
|
|
|
622
626
|
const exploitInputs = witness_bundle_arr(o.exploitProofs).map((p) => sanitizeProof(p));
|
|
623
627
|
const exploit = { inputs: exploitInputs, ...bindExploitProofs(exploitInputs, { sinkRef, dataElementId: edges[0].dataElementId, graphDigest, analysisConfigDigest }) };
|
|
624
628
|
|
|
625
|
-
const sourcePath = redactString(srcNode.path ?? ''); const sinkPath = redactString(sinkNode.path ?? '');
|
|
629
|
+
const sourcePath = (0,redact/* redactString */.rd)(srcNode.path ?? ''); const sinkPath = (0,redact/* redactString */.rd)(sinkNode.path ?? '');
|
|
626
630
|
const redactions = (srcNode.path && sourcePath !== srcNode.path ? 1 : 0) + (sinkNode.path && sinkPath !== sinkNode.path ? 1 : 0);
|
|
627
631
|
|
|
628
632
|
const bundle = jsonSafe({
|
|
@@ -669,7 +673,7 @@ function sanitizeProof(p) {
|
|
|
669
673
|
const ref = (v) => (typeof v === 'string' && /^[A-Za-z0-9._:@/-]{1,160}$/.test(v) ? v : null);
|
|
670
674
|
return {
|
|
671
675
|
id: ref(p.id), kind: ['proof', 'dnr-hunt', 'incident'].includes(p.kind) ? p.kind : null, sinkRef: ref(p.sinkRef), dataElementId: ref(p.dataElementId),
|
|
672
|
-
graphDigest: ref(p.graphDigest), analysisConfigDigest: ref(p.analysisConfigDigest), note: typeof p.note === 'string' ? redactString(p.note).slice(0, 200) : null,
|
|
676
|
+
graphDigest: ref(p.graphDigest), analysisConfigDigest: ref(p.analysisConfigDigest), note: typeof p.note === 'string' ? (0,redact/* redactString */.rd)(p.note).slice(0, 200) : null,
|
|
673
677
|
};
|
|
674
678
|
}
|
|
675
679
|
|
|
@@ -693,9 +697,9 @@ function signedBytes(bundle) {
|
|
|
693
697
|
|
|
694
698
|
/** Sign with the same Ed25519 key and self-issued trust basis as finding evidence bundles. */
|
|
695
699
|
function signWitnessBundle(bundle, privateKeyPem) {
|
|
696
|
-
const publicKeyPem =
|
|
697
|
-
const withIssuance = { ...bundle, issuance: buildLocalIssuance(publicKeyPem) };
|
|
698
|
-
const sig =
|
|
700
|
+
const publicKeyPem = external_node_crypto_.createPublicKey(privateKeyPem).export({ type: 'spki', format: 'pem' });
|
|
701
|
+
const withIssuance = { ...bundle, issuance: (0,evidence_bundle/* buildLocalIssuance */.hc)(publicKeyPem) };
|
|
702
|
+
const sig = external_node_crypto_.sign(null, signedBytes(withIssuance), privateKeyPem);
|
|
699
703
|
return { ...withIssuance, signature: { algorithm: 'ed25519', canonicalisation: WITNESS_SCHEMA, value: sig.toString('base64') } };
|
|
700
704
|
}
|
|
701
705
|
|
|
@@ -874,16 +878,16 @@ function replayWitness(bundle, artifacts, opts = {}) {
|
|
|
874
878
|
const gEdges = new Set(witness_bundle_arr(artifacts.graph.edges).map((e) => e.id));
|
|
875
879
|
if (gb.edgeIds.some((id) => !gEdges.has(id))) fail('graph-binding', 'a bound graph edge is not in the graph artifact');
|
|
876
880
|
}
|
|
877
|
-
const ceCheck =
|
|
881
|
+
const ceCheck = validateControlEvidence(bundle.controlEvidence, edges.map((e) => e.edgeId));
|
|
878
882
|
if (!ceCheck.ok) fail('control-evidence', ceCheck.errors.join('; '));
|
|
879
883
|
const rt = bundle.runtime;
|
|
880
884
|
const reAssessed = witness_bundle_isObj(rt) ? runtimeAssessment(rt, gb ? gb.edgeIds : []) : null;
|
|
881
885
|
if (!reAssessed || !same(reAssessed, rt.assessment)) fail('runtime-inconsistent', 'the recorded runtime assessment does not follow from its recorded inputs');
|
|
882
886
|
if (rt?.inputs && !gb) fail('runtime-inconsistent', 'runtime inputs without a graph binding');
|
|
883
|
-
const reBound =
|
|
887
|
+
const reBound = bindExploitProofs(witness_bundle_arr(bundle.exploit?.inputs), { sinkRef: last, dataElementId: edges[0]?.dataElementId, graphDigest: bundle.graphRef.digest, analysisConfigDigest: bundle.graphRef.analysisConfigDigest });
|
|
884
888
|
if (!same({ status: reBound.status, bound: reBound.bound, rejected: reBound.rejected }, { status: bundle.exploit?.status, bound: bundle.exploit?.bound, rejected: bundle.exploit?.rejected })) fail('proof-binding', 'the recorded proof binding does not follow from the sink, value and configuration it is bound to');
|
|
885
889
|
if (reAssessed && bundle.completion) {
|
|
886
|
-
const ev =
|
|
890
|
+
const ev = buildEvidenceStatuses({ pathComplete: bundle.completion.path.complete && derived.overall === 'complete', controlEvidence: bundle.controlEvidence, runtime: reAssessed, exploit: reBound });
|
|
887
891
|
if (!same(ev, bundle.evidence)) fail('evidence-statuses', 'the four evidence statuses do not follow from their inputs');
|
|
888
892
|
}
|
|
889
893
|
return { ok: failures.length === 0, failures, warnings, derived };
|
package/dist/2584.index.js
CHANGED
|
@@ -1430,7 +1430,7 @@ async function diagnosticsOf(scan) {
|
|
|
1430
1430
|
try { wanted = JSON.parse(process.env.AGENTIC_SECURITY_DIAG_FILES || '[]'); } catch { wanted = []; }
|
|
1431
1431
|
if (wanted.length) {
|
|
1432
1432
|
try {
|
|
1433
|
-
const irMod = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__,
|
|
1433
|
+
const irMod = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 98297));
|
|
1434
1434
|
irMod._resetIrParseFailures();
|
|
1435
1435
|
const ir = await irMod.buildProjectIRAsync(Object.fromEntries(wanted.filter((f) => scan.fc && typeof scan.fc[f] === 'string').map((f) => [f, scan.fc[f]])));
|
|
1436
1436
|
for (const f of wanted) {
|
package/dist/4171.index.js
CHANGED
|
@@ -22,8 +22,8 @@ var nix_build_trust = __webpack_require__(36668);
|
|
|
22
22
|
var discovery = __webpack_require__(30951);
|
|
23
23
|
// EXTERNAL MODULE: ./src/language/haskell-supply.js
|
|
24
24
|
var haskell_supply = __webpack_require__(86349);
|
|
25
|
-
// EXTERNAL MODULE: ./src/language/nix-script-taint.js
|
|
26
|
-
var nix_script_taint = __webpack_require__(
|
|
25
|
+
// EXTERNAL MODULE: ./src/language/nix-script-taint.js + 1 modules
|
|
26
|
+
var nix_script_taint = __webpack_require__(98078);
|
|
27
27
|
// EXTERNAL MODULE: ./src/language/nix-secrets.js
|
|
28
28
|
var nix_secrets = __webpack_require__(69466);
|
|
29
29
|
// EXTERNAL MODULE: ./src/language/nix-parser.js
|