@clear-capabilities/agentic-security-scanner 0.162.0 → 0.163.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 +34 -0
- package/bin/agentic-security.js +87 -0
- package/dist/1340.index.js +1871 -0
- package/dist/3477.index.js +727 -0
- package/dist/3499.index.js +23 -3
- package/dist/4113.index.js +1 -1
- package/dist/5014.index.js +833 -0
- package/dist/5756.index.js +2 -2
- package/dist/6257.index.js +1 -1
- package/dist/6907.index.js +248 -0
- package/dist/6994.index.js +1 -1
- package/dist/7033.index.js +33 -5
- package/dist/7439.index.js +1822 -0
- package/dist/7838.index.js +1 -1
- package/dist/9560.index.js +2318 -23
- package/dist/957.index.js +1 -1
- package/dist/9999.index.js +43 -7
- package/dist/agentic-security.mjs +12 -12
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +33 -6
- package/src/capabilities/CLAUDE.md +114 -0
- package/src/capabilities/address.js +73 -0
- package/src/capabilities/attack-coverage.js +93 -0
- package/src/capabilities/decide.js +256 -0
- package/src/capabilities/delegate.js +69 -0
- package/src/capabilities/egress.js +20 -0
- package/src/capabilities/hook-advice.js +50 -0
- package/src/capabilities/index.js +32 -0
- package/src/capabilities/manifest.js +344 -0
- package/src/capabilities/outbound.js +189 -0
- package/src/capabilities/paths.js +57 -0
- package/src/capabilities/probes.js +211 -0
- package/src/capabilities/proxy.js +182 -0
- package/src/capabilities/reasons.js +67 -0
- package/src/capabilities/receipts.js +356 -0
- package/src/capabilities/records.js +58 -0
- package/src/capabilities/recovery.js +367 -0
- package/src/capabilities/report.js +81 -0
- package/src/capabilities/runner.js +236 -0
- package/src/capabilities/secrets.js +26 -0
- package/src/capabilities/tool-gate.js +119 -0
- package/src/capabilities/tool-registry.js +60 -0
- package/src/dataflow/CLAUDE.md +30 -0
- package/src/dataflow/catalog.js +42 -1
- package/src/dataflow/engine.js +67 -2
- package/src/dataflow/guard-dominance.js +352 -0
- package/src/discovery/CLAUDE.md +9 -0
- package/src/discovery/index.js +15 -1
- package/src/discovery/lenses.js +18 -0
- package/src/engine.js +80 -15
- package/src/integrations/index.js +5 -2
- package/src/ir/CLAUDE.md +14 -0
- package/src/ir/implicit-handler-params.js +74 -0
- package/src/ir/parser-cs.js +20 -0
- package/src/ir/parser-go.js +30 -13
- package/src/ir/parser-java.js +11 -0
- package/src/language/nix-eval-isolation.js +33 -5
- package/src/lineage/CLAUDE.md +31 -0
- package/src/lineage/deployment/adapter-cloud.js +202 -0
- package/src/lineage/deployment/adapter-compose.js +90 -0
- package/src/lineage/deployment/adapter-kit.js +142 -0
- package/src/lineage/deployment/adapter-kubernetes.js +247 -0
- package/src/lineage/deployment/attack-paths.js +443 -0
- package/src/lineage/deployment/boundaries-run.js +223 -0
- package/src/lineage/deployment/boundary-graph.js +567 -0
- package/src/lineage/deployment/cross-repo.js +253 -0
- package/src/lineage/deployment/drift.js +383 -0
- package/src/lineage/deployment/ingest.js +221 -0
- package/src/lineage/deployment/projection.js +244 -0
- package/src/lineage/deployment/trace-correlation.js +541 -0
- package/src/lineage/source-registry.js +19 -0
- package/src/mcp/CLAUDE.md +5 -2
- package/src/mcp/invariant-tools.js +64 -0
- package/src/mcp/portfolio-tools.js +79 -0
- package/src/mcp/redact.js +15 -0
- package/src/mcp/server.js +24 -3
- package/src/mcp/tools.js +13 -1
- package/src/posture/CLAUDE.md +332 -1
- package/src/posture/accuracy-scorecard.js +95 -4
- package/src/posture/assurance/rollback.js +214 -0
- package/src/posture/assurance/rollout.js +200 -0
- package/src/posture/business-logic.js +26 -4
- package/src/posture/evaluation/bakeoff.js +148 -0
- package/src/posture/evaluation/custody.js +200 -0
- package/src/posture/evaluation/deployment-ablation.js +416 -0
- package/src/posture/evaluation/dev-cases.js +65 -0
- package/src/posture/evaluation/economics.js +85 -0
- package/src/posture/evaluation/gates.js +161 -0
- package/src/posture/evaluation/grouping.js +101 -0
- package/src/posture/evaluation/interval.js +132 -0
- package/src/posture/evaluation/invariant-ablation.js +325 -0
- package/src/posture/evaluation/labels.js +200 -0
- package/src/posture/evaluation/protocol.js +308 -0
- package/src/posture/evaluation/release-gate.js +209 -0
- package/src/posture/evaluation/report.js +158 -0
- package/src/posture/evaluation/runner.js +273 -0
- package/src/posture/evaluation/scan-child.js +80 -0
- package/src/posture/evaluation/score.js +174 -0
- package/src/posture/evaluation/synthetic.js +90 -0
- package/src/posture/evaluation/why-missed.js +219 -0
- package/src/posture/fleet.js +11 -2
- package/src/posture/invariants/cli.js +95 -0
- package/src/posture/invariants/coverage.js +164 -0
- package/src/posture/invariants/export.js +178 -0
- package/src/posture/invariants/expressions.js +46 -0
- package/src/posture/invariants/lifecycle.js +289 -0
- package/src/posture/invariants/project-input.js +55 -0
- package/src/posture/invariants/repair.js +340 -0
- package/src/posture/invariants/run.js +45 -0
- package/src/posture/invariants/scenarios.js +311 -0
- package/src/posture/invariants/schema.js +167 -0
- package/src/posture/invariants/shrink.js +407 -0
- package/src/posture/invariants/state-assertions.js +165 -0
- package/src/posture/logic-claims.js +12 -0
- package/src/posture/model-routing.js +40 -0
- package/src/posture/oracles/adapters.js +172 -1
- package/src/posture/oracles/conformance.js +3 -2
- package/src/posture/oracles/oracle.js +23 -3
- package/src/posture/oracles/registry.js +1 -1
- package/src/posture/portfolio/backend.js +279 -0
- package/src/posture/portfolio/bundle.js +314 -0
- package/src/posture/portfolio/cli.js +142 -0
- package/src/posture/portfolio/manifest.js +263 -0
- package/src/posture/portfolio/progress.js +267 -0
- package/src/posture/portfolio/resume.js +130 -0
- package/src/posture/portfolio/retention.js +207 -0
- package/src/posture/portfolio/scheduler.js +488 -0
- package/src/posture/portfolio/signing.js +155 -0
- package/src/posture/portfolio/wording.js +59 -0
- package/src/posture/portfolio/work-units.js +405 -0
- package/src/posture/replay/replay.js +1 -0
- package/src/posture/routing/adapter.js +140 -0
- package/src/posture/routing/calibration.js +278 -0
- package/src/posture/routing/canary.js +141 -0
- package/src/posture/routing/control.js +35 -0
- package/src/posture/routing/decide.js +211 -0
- package/src/posture/routing/drift.js +137 -0
- package/src/posture/routing/economics.js +185 -0
- package/src/posture/routing/feedback.js +207 -0
- package/src/posture/routing/index.js +31 -0
- package/src/posture/routing/outcomes.js +281 -0
- package/src/posture/routing/promotion.js +291 -0
- package/src/posture/routing/receipts.js +59 -0
- package/src/posture/routing/report.js +142 -0
- package/src/posture/routing/shadow.js +53 -0
- package/src/posture/specification-mining.js +26 -0
- package/src/posture/verification/patch-negative.js +16 -4
- package/src/posture/verification/patch-promotion.js +10 -2
- package/src/report/index.js +51 -1
- package/src/sandbox/CLAUDE.md +10 -0
- package/src/sandbox/backend-userspace.js +91 -13
- package/dist/5613.index.js +0 -500
- package/dist/6239.index.js +0 -469
package/dist/6239.index.js
DELETED
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
export const id = 6239;
|
|
2
|
-
export const ids = [6239];
|
|
3
|
-
export const modules = {
|
|
4
|
-
|
|
5
|
-
/***/ 56239:
|
|
6
|
-
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
-
|
|
8
|
-
// ESM COMPAT FLAG
|
|
9
|
-
__webpack_require__.r(__webpack_exports__);
|
|
10
|
-
|
|
11
|
-
// EXPORTS
|
|
12
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
13
|
-
MANIFEST_SCHEMA: () => (/* binding */ MANIFEST_SCHEMA),
|
|
14
|
-
getOracle: () => (/* binding */ getOracle),
|
|
15
|
-
listOracles: () => (/* binding */ listOracles),
|
|
16
|
-
manifestEntry: () => (/* binding */ manifestEntry),
|
|
17
|
-
oracleManifest: () => (/* binding */ oracleManifest)
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
// EXTERNAL MODULE: ./src/posture/oracles/oracle.js + 4 modules
|
|
21
|
-
var oracle = __webpack_require__(80219);
|
|
22
|
-
// EXTERNAL MODULE: ./src/posture/assurance/identity.js
|
|
23
|
-
var identity = __webpack_require__(41877);
|
|
24
|
-
;// CONCATENATED MODULE: ./src/posture/oracles/adapters.js
|
|
25
|
-
// The five class-specific oracle adapters (X-202.AC01).
|
|
26
|
-
//
|
|
27
|
-
// Each adapter is data plus three small functions, all verifier-authored:
|
|
28
|
-
// harnessSource the script the boundary runs. It imports the target, drives it
|
|
29
|
-
// with a benign CONTROL input first and the hostile input second,
|
|
30
|
-
// and writes a result document. It never decides a verdict.
|
|
31
|
-
// prepare builds the harness input from the request inputs plus a
|
|
32
|
-
// per-run token the target cannot know in advance.
|
|
33
|
-
// interpret forms `{ satisfied, preconditionsHeld, observed, reason }` from
|
|
34
|
-
// what the run left behind, read by the verifier after the whole
|
|
35
|
-
// process tree is dead. `satisfied: true` means the hypothesised
|
|
36
|
-
// effect was observed; `false` with `preconditionsHeld: true`
|
|
37
|
-
// means it was not observed although the control proved the
|
|
38
|
-
// target was exercised (a refutation); anything else leaves the
|
|
39
|
-
// verdict open.
|
|
40
|
-
//
|
|
41
|
-
// Changing what an adapter asserts means bumping its `version`: the version and
|
|
42
|
-
// the harness text feed `logicDigest`, which replay manifests pin.
|
|
43
|
-
//
|
|
44
|
-
// Platform statements are about what this repository's tests have executed. The
|
|
45
|
-
// development host is macOS, so Linux is declared `unverified` everywhere: on
|
|
46
|
-
// Linux the boundary currently refuses to run (read denial and supervised
|
|
47
|
-
// tree termination are not implemented on the namespace backend), which the
|
|
48
|
-
// runner reports as `unsupported`, and no Linux outcome is claimed.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const PLATFORMS = Object.freeze({
|
|
53
|
-
darwin: { status: 'supported', note: 'executed by this repository\'s tests on the macOS development host (userspace confinement backend)' },
|
|
54
|
-
linux: { status: 'unverified', note: 'no Linux outcome is claimed: the boundary refuses to run on the namespace backend until read denial and supervised tree termination are implemented and executed there; the run reports unsupported' },
|
|
55
|
-
win32: { status: 'unsupported', note: 'no confinement backend exists for Windows' },
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const COMMON_BUDGETS = Object.freeze({ timeoutMs: 8000, graceMs: 1000, maxOutputBytes: 65536, maxFileBytes: 262144, maxFiles: 32 });
|
|
59
|
-
|
|
60
|
-
const PROLOGUE = `import fs from 'node:fs';
|
|
61
|
-
const INPUT = __INPUT__;
|
|
62
|
-
const ENTRY = __ENTRY__;
|
|
63
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
64
|
-
const text = (e) => String((e && e.message) || e).slice(0, 160);
|
|
65
|
-
const out = {};
|
|
66
|
-
function finish(extra) {
|
|
67
|
-
Object.assign(out, extra);
|
|
68
|
-
try { fs.writeFileSync('__oracle_result.json', JSON.stringify(out)); } catch {}
|
|
69
|
-
process.exit(0);
|
|
70
|
-
}
|
|
71
|
-
let mod;
|
|
72
|
-
try { mod = await import('./' + ENTRY); } catch (e) { finish({ loadFailed: true }); }
|
|
73
|
-
const fn = mod && mod[INPUT.export];
|
|
74
|
-
if (typeof fn !== 'function') finish({ loadFailed: true });
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
|
-
const isId = (s) => typeof s === 'string' && /^[A-Za-z_$][\w$]{0,63}$/.test(s);
|
|
78
|
-
const isStr = (s, max = 2000) => typeof s === 'string' && s.length > 0 && s.length <= max;
|
|
79
|
-
|
|
80
|
-
// ------------------------------------------------------------------ injection execution
|
|
81
|
-
|
|
82
|
-
const injection = (0,oracle/* defineOracle */.IL)({
|
|
83
|
-
id: 'injection-execution', class: 'injection-execution', version: '1',
|
|
84
|
-
description: 'Does attacker-controlled input reach a command interpreter and run? The harness calls the target with a benign control, then with a payload whose effect is a token-named file; the verifier looks for that file after the process tree is dead.',
|
|
85
|
-
prerequisites: ['node-runtime', 'confinement-backend', 'posix-shell'],
|
|
86
|
-
platforms: PLATFORMS, budgets: COMMON_BUDGETS,
|
|
87
|
-
negativeControls: [{ id: 'argv-array-call', description: 'the same handler passing the input as a separate argv element (no shell) must not produce the effect', expectedOutcome: 'refuted' }],
|
|
88
|
-
fixtures: { dir: 'test/fixtures/oracles/injection-execution', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
89
|
-
limitations: ['covers command injection through a shell only; SQL, template and code injection are not asserted here', 'the target is imported into the harness process, so it runs with the harness; a target written to recognise the payload and forge the effect file is not distinguishable from a real injection', 'asynchronous handlers that spawn without being awaited are given settleMs to finish, not guaranteed to'],
|
|
90
|
-
harnessSource: `${PROLOGUE}
|
|
91
|
-
try { await Promise.resolve(fn(INPUT.benign)); out.controlOk = true; } catch (e) { out.controlOk = false; }
|
|
92
|
-
if (out.controlOk) {
|
|
93
|
-
try { await Promise.resolve(fn(INPUT.attack)); out.attackReturned = true; } catch (e) { out.attackReturned = false; }
|
|
94
|
-
await sleep(INPUT.settleMs);
|
|
95
|
-
}
|
|
96
|
-
finish({});
|
|
97
|
-
`,
|
|
98
|
-
validateInputs(i) {
|
|
99
|
-
if (!isId(i.export)) return 'export must be an identifier';
|
|
100
|
-
if (!isStr(i.benign, 500)) return 'benign must be a short string';
|
|
101
|
-
if (!isStr(i.attackTemplate, 500) || !i.attackTemplate.includes('{TOKEN}')) return 'attackTemplate must be a short string containing {TOKEN}';
|
|
102
|
-
if (i.settleMs !== undefined && !(Number.isInteger(i.settleMs) && i.settleMs >= 0 && i.settleMs <= 2000)) return 'settleMs must be 0 to 2000';
|
|
103
|
-
return null;
|
|
104
|
-
},
|
|
105
|
-
prepare: (i, token) => ({ export: i.export, benign: i.benign, attack: i.attackTemplate.split('{TOKEN}').join(token), settleMs: i.settleMs ?? 300 }),
|
|
106
|
-
interpret({ token, result, read }) {
|
|
107
|
-
const effect = read(`effect-${token}`, 256) === token;
|
|
108
|
-
const controlOk = result?.controlOk === true;
|
|
109
|
-
const observed = { effectObserved: effect, controlCompleted: controlOk, targetLoaded: result ? result.loadFailed !== true : null };
|
|
110
|
-
if (effect) return { satisfied: true, preconditionsHeld: true, observed, reason: 'the injected command ran: its token-named effect file exists' };
|
|
111
|
-
if (controlOk) return { satisfied: false, preconditionsHeld: true, observed, reason: 'the handler ran its benign control and the injected command left no effect' };
|
|
112
|
-
return { satisfied: null, preconditionsHeld: false, observed, reason: result?.loadFailed ? 'the target failed to load, so the payload was never delivered' : 'the benign control did not complete, so the payload was never delivered' };
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// ------------------------------------------------------------------ authorization decision
|
|
117
|
-
|
|
118
|
-
const DECISIONS = ['allow', 'deny', 'error', 'indeterminate'];
|
|
119
|
-
|
|
120
|
-
const authorization = (0,oracle/* defineOracle */.IL)({
|
|
121
|
-
id: 'authorization-decision', class: 'authorization-decision', version: '1',
|
|
122
|
-
description: 'Does an authorization function allow an actor it should deny? The harness evaluates declared control cases (the owner, expected allow) and attack cases (another tenant or a lower role, expected deny); the verifier recomputes the verdict from the raw decisions.',
|
|
123
|
-
prerequisites: ['node-runtime', 'confinement-backend'],
|
|
124
|
-
platforms: PLATFORMS, budgets: COMMON_BUDGETS,
|
|
125
|
-
negativeControls: [{ id: 'tenant-checked', description: 'the same function comparing tenant ids must deny the cross-tenant attack case', expectedOutcome: 'refuted' }],
|
|
126
|
-
fixtures: { dir: 'test/fixtures/oracles/authorization-decision', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
127
|
-
limitations: ['asserts the decision function in isolation; a route or middleware that enforces authorization elsewhere is not exercised', 'the cases describe the scenario; the oracle does not infer which actors should be denied', 'a thrown error is not read as a denial'],
|
|
128
|
-
harnessSource: `${PROLOGUE}
|
|
129
|
-
const norm = (r) => (r === true || r === 'allow' || (r && r.allowed === true)) ? 'allow'
|
|
130
|
-
: (r === false || r === 'deny' || (r && r.allowed === false)) ? 'deny' : 'indeterminate';
|
|
131
|
-
const decisions = {};
|
|
132
|
-
for (const c of INPUT.cases) {
|
|
133
|
-
try { decisions[c.id] = norm(await Promise.resolve(fn(...c.args))); } catch (e) { decisions[c.id] = 'error'; }
|
|
134
|
-
}
|
|
135
|
-
finish({ decisions });
|
|
136
|
-
`,
|
|
137
|
-
validateInputs(i) {
|
|
138
|
-
if (!isId(i.export)) return 'export must be an identifier';
|
|
139
|
-
if (!Array.isArray(i.cases) || i.cases.length < 2 || i.cases.length > 16) return 'cases must hold 2 to 16 entries';
|
|
140
|
-
const ids = new Set();
|
|
141
|
-
for (const c of i.cases) {
|
|
142
|
-
if (!c || !isId(String(c.id).replace(/-/g, '_')) || ids.has(c.id)) return 'every case needs a unique id';
|
|
143
|
-
ids.add(c.id);
|
|
144
|
-
if (!['control', 'attack'].includes(c.role)) return 'case role must be control or attack';
|
|
145
|
-
if (!Array.isArray(c.args) || c.args.length > 6) return 'case args must be an array of at most 6';
|
|
146
|
-
}
|
|
147
|
-
if (!i.cases.some((c) => c.role === 'control') || !i.cases.some((c) => c.role === 'attack')) return 'at least one control and one attack case are required';
|
|
148
|
-
return null;
|
|
149
|
-
},
|
|
150
|
-
prepare: (i) => ({ export: i.export, cases: i.cases.map((c) => ({ id: c.id, args: c.args })) }),
|
|
151
|
-
interpret({ inputs, result }) {
|
|
152
|
-
const got = result && result.decisions && typeof result.decisions === 'object' ? result.decisions : null;
|
|
153
|
-
if (!got || result.loadFailed) return { satisfied: null, preconditionsHeld: false, observed: { targetLoaded: false }, reason: 'the target did not report decisions' };
|
|
154
|
-
const decisions = {};
|
|
155
|
-
for (const c of inputs.cases) decisions[c.id] = DECISIONS.includes(got[c.id]) ? got[c.id] : 'indeterminate';
|
|
156
|
-
const controls = inputs.cases.filter((c) => c.role === 'control');
|
|
157
|
-
const attacks = inputs.cases.filter((c) => c.role === 'attack');
|
|
158
|
-
const controlsHeld = controls.every((c) => decisions[c.id] === 'allow');
|
|
159
|
-
const allowed = attacks.filter((c) => decisions[c.id] === 'allow').map((c) => c.id);
|
|
160
|
-
const allDenied = attacks.every((c) => decisions[c.id] === 'deny');
|
|
161
|
-
const observed = { decisions, controlsAllowed: controlsHeld, attackCasesAllowed: allowed };
|
|
162
|
-
if (allowed.length) return { satisfied: true, preconditionsHeld: controlsHeld, observed, reason: `the function allowed attack case(s): ${allowed.join(', ')}` };
|
|
163
|
-
if (allDenied && controlsHeld) return { satisfied: false, preconditionsHeld: true, observed, reason: 'every attack case was denied and every control case was allowed' };
|
|
164
|
-
return { satisfied: null, preconditionsHeld: false, observed, reason: controlsHeld ? 'an attack case returned neither allow nor deny' : 'a control case was not allowed, so the function was not shown to be deciding' };
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// ------------------------------------------------------------------ state transition
|
|
169
|
-
|
|
170
|
-
const stateTransition = (0,oracle/* defineOracle */.IL)({
|
|
171
|
-
id: 'state-transition', class: 'state-transition', version: '1',
|
|
172
|
-
description: 'Does a state machine accept an event sequence that must be refused? The harness drives a fresh machine through the legal sequence (control) and a fresh one through the attack sequence; the verifier compares the final states.',
|
|
173
|
-
prerequisites: ['node-runtime', 'confinement-backend'],
|
|
174
|
-
platforms: PLATFORMS, budgets: COMMON_BUDGETS,
|
|
175
|
-
negativeControls: [{ id: 'payment-required', description: 'the same machine refusing shipment before payment must not reach the forbidden state', expectedOutcome: 'refuted' }],
|
|
176
|
-
fixtures: { dir: 'test/fixtures/oracles/state-transition', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
177
|
-
limitations: ['covers one factory and finite event lists; it is not a model checker', 'durable effects outside the machine object (a database row, a queued job) are not observed', 'the forbidden state is stated by the scenario; the oracle does not infer business rules'],
|
|
178
|
-
harnessSource: `${PROLOGUE}
|
|
179
|
-
async function drive(events) {
|
|
180
|
-
const m = await Promise.resolve(fn());
|
|
181
|
-
const accepted = [];
|
|
182
|
-
for (const e of events) {
|
|
183
|
-
try { const r = await Promise.resolve(m.send(e)); accepted.push(r !== false); } catch (err) { accepted.push(false); }
|
|
184
|
-
}
|
|
185
|
-
return { state: String(m.state).slice(0, 80), accepted };
|
|
186
|
-
}
|
|
187
|
-
try { const c = await drive(INPUT.legal); out.controlState = c.state; } catch (e) { out.controlState = null; }
|
|
188
|
-
try { const a = await drive(INPUT.attack); out.attackState = a.state; out.attackAccepted = a.accepted; } catch (e) { out.attackState = null; }
|
|
189
|
-
finish({});
|
|
190
|
-
`,
|
|
191
|
-
validateInputs(i) {
|
|
192
|
-
if (!isId(i.factory)) return 'factory must be an identifier';
|
|
193
|
-
const evs = (a) => Array.isArray(a) && a.length >= 1 && a.length <= 16 && a.every((e) => isStr(e, 64));
|
|
194
|
-
if (!evs(i.legal) || !evs(i.attack)) return 'legal and attack must be lists of 1 to 16 short event names';
|
|
195
|
-
if (!isStr(i.expectedFinal, 64) || !isStr(i.forbiddenState, 64)) return 'expectedFinal and forbiddenState are required';
|
|
196
|
-
return null;
|
|
197
|
-
},
|
|
198
|
-
prepare: (i) => ({ export: i.factory, legal: i.legal, attack: i.attack }),
|
|
199
|
-
interpret({ inputs, result }) {
|
|
200
|
-
if (!result || result.loadFailed) return { satisfied: null, preconditionsHeld: false, observed: { targetLoaded: false }, reason: 'the target failed to load' };
|
|
201
|
-
const controlHeld = result.controlState === inputs.expectedFinal;
|
|
202
|
-
const reached = result.attackState === inputs.forbiddenState;
|
|
203
|
-
const observed = { controlState: result.controlState ?? null, controlReachedExpected: controlHeld, attackState: result.attackState ?? null, attackAccepted: Array.isArray(result.attackAccepted) ? result.attackAccepted.slice(0, 16).map((b) => b === true) : [] };
|
|
204
|
-
if (reached) return { satisfied: true, preconditionsHeld: controlHeld, observed, reason: `the attack sequence reached the forbidden state '${inputs.forbiddenState}'` };
|
|
205
|
-
if (controlHeld) return { satisfied: false, preconditionsHeld: true, observed, reason: 'the legal sequence reached its expected state and the attack sequence did not reach the forbidden one' };
|
|
206
|
-
return { satisfied: null, preconditionsHeld: false, observed, reason: 'the legal sequence did not reach its expected state, so the machine was not shown to be working' };
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// ------------------------------------------------------------------ side-effect reachability
|
|
211
|
-
|
|
212
|
-
const sideEffect = (0,oracle/* defineOracle */.IL)({
|
|
213
|
-
id: 'side-effect-reachability', class: 'side-effect-reachability', version: '1',
|
|
214
|
-
description: 'Does attacker-chosen input reach a privileged side-effecting operation? The harness injects a recording stand-in for the sink, calls the handler with a benign value (control) and an attacker value, and the verifier checks which values reached the sink.',
|
|
215
|
-
prerequisites: ['node-runtime', 'confinement-backend'],
|
|
216
|
-
platforms: PLATFORMS, budgets: COMMON_BUDGETS,
|
|
217
|
-
negativeControls: [{ id: 'allowlisted-target', description: 'the same handler with a destination allowlist must not pass the attacker value to the sink', expectedOutcome: 'refuted' }],
|
|
218
|
-
fixtures: { dir: 'test/fixtures/oracles/side-effect-reachability', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
219
|
-
limitations: ['the sink is a recording stand-in injected as the handler\'s second argument; handlers that import their sink directly are not covered', 'reaching the sink shows the operation would be invoked with the attacker value, not that the real operation would succeed', 'no network or privileged call is ever made'],
|
|
220
|
-
harnessSource: `${PROLOGUE}
|
|
221
|
-
const calls = [];
|
|
222
|
-
const deps = { [INPUT.sink]: (...args) => { try { calls.push(JSON.stringify(args).slice(0, 600)); } catch {} return Promise.resolve({ ok: true, status: 200, text: async () => '' }); } };
|
|
223
|
-
const reached = (v) => calls.some((c) => c.includes(JSON.stringify(v).slice(1, -1)));
|
|
224
|
-
try { await Promise.resolve(fn(INPUT.benignValue, deps)); } catch (e) {}
|
|
225
|
-
out.benignReached = reached(INPUT.benignValue);
|
|
226
|
-
calls.length = 0;
|
|
227
|
-
try { await Promise.resolve(fn(INPUT.attackerValue, deps)); } catch (e) {}
|
|
228
|
-
out.attackReached = reached(INPUT.attackerValue);
|
|
229
|
-
finish({});
|
|
230
|
-
`,
|
|
231
|
-
validateInputs(i) {
|
|
232
|
-
if (!isId(i.export) || !isId(i.sink)) return 'export and sink must be identifiers';
|
|
233
|
-
if (!isStr(i.benignValue, 500) || !isStr(i.attackerValue, 500) || i.benignValue === i.attackerValue) return 'benignValue and attackerValue must be distinct short strings';
|
|
234
|
-
return null;
|
|
235
|
-
},
|
|
236
|
-
prepare: (i) => ({ export: i.export, sink: i.sink, benignValue: i.benignValue, attackerValue: i.attackerValue }),
|
|
237
|
-
interpret({ result }) {
|
|
238
|
-
if (!result || result.loadFailed) return { satisfied: null, preconditionsHeld: false, observed: { targetLoaded: false }, reason: 'the target failed to load' };
|
|
239
|
-
const benign = result.benignReached === true;
|
|
240
|
-
const attack = result.attackReached === true;
|
|
241
|
-
const observed = { benignReachedSink: benign, attackerValueReachedSink: attack };
|
|
242
|
-
if (attack) return { satisfied: true, preconditionsHeld: benign, observed, reason: 'the attacker-chosen value reached the side-effecting sink' };
|
|
243
|
-
if (benign) return { satisfied: false, preconditionsHeld: true, observed, reason: 'the benign value reached the sink and the attacker value did not' };
|
|
244
|
-
return { satisfied: null, preconditionsHeld: false, observed, reason: 'even the benign value never reached the sink, so the handler was not shown to use it' };
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// ------------------------------------------------------------------ parser / resource behaviour
|
|
249
|
-
|
|
250
|
-
const parserResource = (0,oracle/* defineOracle */.IL)({
|
|
251
|
-
id: 'parser-resource', class: 'parser-resource', version: '1',
|
|
252
|
-
description: 'Does a parser or matcher exhaust its budget on a bounded hostile input? The harness completes a benign control (marker file), then feeds the hostile input; the verifier\'s own supervisor deadline is the measurement, so no self-reported timing is trusted.',
|
|
253
|
-
prerequisites: ['node-runtime', 'confinement-backend'],
|
|
254
|
-
platforms: PLATFORMS, budgets: { ...COMMON_BUDGETS, timeoutMs: 5000 },
|
|
255
|
-
negativeControls: [{ id: 'linear-pattern', description: 'the same matcher written without nested quantifiers must finish the hostile input inside the budget', expectedOutcome: 'refuted' }],
|
|
256
|
-
fixtures: { dir: 'test/fixtures/oracles/parser-resource', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
257
|
-
limitations: ['the hostile input is bounded (at most 64 KiB) and the run is cut at the supervisor deadline; this shows a budget was exceeded, not how large the blow-up is', 'a hard crash without a deadline or output-cap hit is left inconclusive rather than called exhaustion', 'memory is not measured: address-space limits are not enforceable on the macOS backend'],
|
|
258
|
-
harnessSource: `${PROLOGUE}
|
|
259
|
-
const mark = (name) => { try { fs.writeFileSync(name, INPUT.token); } catch {} };
|
|
260
|
-
try { await Promise.resolve(fn(INPUT.benign)); mark('__oracle_benign_done'); } catch (e) { finish({ benignFailed: true }); }
|
|
261
|
-
try { await Promise.resolve(fn(INPUT.hostile)); } catch (e) {}
|
|
262
|
-
mark('__oracle_hostile_done');
|
|
263
|
-
finish({});
|
|
264
|
-
`,
|
|
265
|
-
validateInputs(i) {
|
|
266
|
-
if (!isId(i.export)) return 'export must be an identifier';
|
|
267
|
-
if (!isStr(i.benign, 500)) return 'benign must be a short string';
|
|
268
|
-
const h = i.hostile;
|
|
269
|
-
if (!h || !isStr(h.unit, 16) || !Number.isInteger(h.count) || h.count < 1 || h.count > 65536 || (h.suffix !== undefined && !isStr(h.suffix, 16))) return 'hostile needs unit (short), count (1 to 65536) and an optional short suffix';
|
|
270
|
-
return null;
|
|
271
|
-
},
|
|
272
|
-
prepare: (i, token) => ({ export: i.export, benign: i.benign, hostile: i.hostile.unit.repeat(i.hostile.count) + (i.hostile.suffix || ''), token }),
|
|
273
|
-
interpret({ token, run, read }) {
|
|
274
|
-
const benignDone = read('__oracle_benign_done', 128) === token;
|
|
275
|
-
const hostileDone = read('__oracle_hostile_done', 128) === token;
|
|
276
|
-
const observed = { benignCompleted: benignDone, hostileCompleted: hostileDone, deadlineHit: run.timedOut, outputCapHit: run.outputCapped };
|
|
277
|
-
if (!benignDone) return { satisfied: null, preconditionsHeld: false, observed, reason: 'the benign control did not complete, so the target was not shown to be working' };
|
|
278
|
-
if (hostileDone) return { satisfied: false, preconditionsHeld: true, observed, reason: 'the hostile input was handled inside the budget' };
|
|
279
|
-
if (run.timedOut || run.outputCapped) return { satisfied: true, preconditionsHeld: true, observed, reason: run.timedOut ? 'the hostile input exhausted the time budget (the supervisor deadline fired)' : 'the hostile input exhausted the output budget' };
|
|
280
|
-
return { satisfied: null, preconditionsHeld: true, observed, reason: 'the run ended without finishing the hostile input and without hitting a budget, so the cause is unknown' };
|
|
281
|
-
},
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// ------------------------------------------------------------------ replay / idempotency (X-205)
|
|
285
|
-
|
|
286
|
-
const replayIdempotency = (0,oracle/* defineOracle */.IL)({
|
|
287
|
-
id: 'replay-idempotency', class: 'replay-idempotency', version: '1',
|
|
288
|
-
description: 'Does delivering the same request again repeat its side effect? The harness builds a fresh handler around a recording stand-in for the effect, delivers a request and then a DISTINCT request (control: one effect each), then on a second fresh handler delivers the same request repeatedly; the verifier counts the effects.',
|
|
289
|
-
prerequisites: ['node-runtime', 'confinement-backend'],
|
|
290
|
-
platforms: PLATFORMS, budgets: COMMON_BUDGETS,
|
|
291
|
-
negativeControls: [{ id: 'idempotency-key-honoured', description: 'the same handler remembering the idempotency key must produce exactly one effect for the repeated request', expectedOutcome: 'refuted' }],
|
|
292
|
-
fixtures: { dir: 'test/fixtures/oracles/replay-idempotency', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
293
|
-
limitations: ['the target is a factory that receives a recording stand-in for its effect and returns a handler; code that imports its effect directly is not covered', 'deliveries are sequential: a race between concurrent deliveries is not exercised', 'durable deduplication in an external store is not observed, only the handler instance the factory returns', 'reaching the effect twice shows the handler would repeat it, not that the real operation would succeed'],
|
|
294
|
-
harnessSource: `${PROLOGUE}
|
|
295
|
-
const mk = () => { const calls = []; return { calls, deps: { [INPUT.sink]: (...a) => { calls.push(1); return Promise.resolve({ ok: true }); } } }; };
|
|
296
|
-
async function build() { const m = mk(); const h = await Promise.resolve(fn(m.deps)); if (typeof h !== 'function') throw new Error('not a handler'); return { m, h }; }
|
|
297
|
-
try {
|
|
298
|
-
const c = await build();
|
|
299
|
-
try { await Promise.resolve(c.h(INPUT.request)); } catch (e) {}
|
|
300
|
-
out.firstEffects = c.m.calls.length;
|
|
301
|
-
try { await Promise.resolve(c.h(INPUT.distinctRequest)); } catch (e) {}
|
|
302
|
-
out.distinctEffects = c.m.calls.length;
|
|
303
|
-
} catch (e) { out.controlFailed = true; }
|
|
304
|
-
try {
|
|
305
|
-
const a = await build();
|
|
306
|
-
for (let i = 0; i < INPUT.deliveries; i++) { try { await Promise.resolve(a.h(INPUT.request)); } catch (e) {} }
|
|
307
|
-
out.replayEffects = a.m.calls.length;
|
|
308
|
-
} catch (e) { out.replayFailed = true; }
|
|
309
|
-
finish({});
|
|
310
|
-
`,
|
|
311
|
-
validateInputs(i) {
|
|
312
|
-
if (!isId(i.export) || !isId(i.sink)) return 'export and sink must be identifiers';
|
|
313
|
-
const small = (v) => { try { const s = JSON.stringify(v); return typeof s === 'string' && s.length <= 2000 && v !== null && typeof v === 'object' && !Array.isArray(v); } catch { return false; } };
|
|
314
|
-
if (!small(i.request) || !small(i.distinctRequest)) return 'request and distinctRequest must be small JSON objects';
|
|
315
|
-
if ((0,identity/* digestOf */.ol)(i.request) === (0,identity/* digestOf */.ol)(i.distinctRequest)) return 'distinctRequest must differ from request';
|
|
316
|
-
if (!(Number.isInteger(i.deliveries) && i.deliveries >= 2 && i.deliveries <= 6)) return 'deliveries must be 2 to 6';
|
|
317
|
-
return null;
|
|
318
|
-
},
|
|
319
|
-
prepare: (i) => ({ export: i.export, sink: i.sink, request: i.request, distinctRequest: i.distinctRequest, deliveries: i.deliveries }),
|
|
320
|
-
interpret({ result }) {
|
|
321
|
-
if (!result || result.loadFailed) return { satisfied: null, preconditionsHeld: false, observed: { targetLoaded: false }, reason: 'the target failed to load' };
|
|
322
|
-
const first = Number.isInteger(result.firstEffects) ? result.firstEffects : null;
|
|
323
|
-
const distinct = Number.isInteger(result.distinctEffects) ? result.distinctEffects : null;
|
|
324
|
-
const replay = Number.isInteger(result.replayEffects) ? result.replayEffects : null;
|
|
325
|
-
const controlHeld = first === 1 && distinct === 2;
|
|
326
|
-
const observed = { targetLoaded: true, firstDeliveryEffects: first, distinctDeliveryTotalEffects: distinct, repeatedDeliveryTotalEffects: replay, controlHeld };
|
|
327
|
-
if (replay !== null && replay > 1) return { satisfied: true, preconditionsHeld: controlHeld, observed, reason: `the same request delivered repeatedly produced ${replay} effects` };
|
|
328
|
-
if (controlHeld && replay === 1) return { satisfied: false, preconditionsHeld: true, observed, reason: 'a distinct request produced its own effect and the repeated request produced exactly one' };
|
|
329
|
-
return { satisfied: null, preconditionsHeld: false, observed, reason: controlHeld ? 'the repeated deliveries did not report' : 'the handler did not produce exactly one effect per first delivery, so it was not shown to be working' };
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// ------------------------------------------------------------------ functional regression (X-204)
|
|
334
|
-
|
|
335
|
-
const functionalRegression = (0,oracle/* defineOracle */.IL)({
|
|
336
|
-
id: 'functional-regression', class: 'functional-regression', version: '1',
|
|
337
|
-
description: 'Does a revision still behave as intended? The harness calls the exported function with declared cases and records each result; the verifier compares them with the declared expectations. The hypothesis is that the behaviour REGRESSED, so a patch is clean when this is refuted.',
|
|
338
|
-
prerequisites: ['node-runtime', 'confinement-backend'],
|
|
339
|
-
platforms: PLATFORMS, budgets: COMMON_BUDGETS,
|
|
340
|
-
negativeControls: [{ id: 'behaviour-preserved', description: 'a revision that returns the expected value for every declared case must show no regression', expectedOutcome: 'refuted' }],
|
|
341
|
-
fixtures: { dir: 'test/fixtures/oracles/functional-regression', positive: 'positive', negative: 'negative', inconclusive: 'inconclusive' },
|
|
342
|
-
limitations: ['only the declared cases are checked: behaviour outside them is not observed, and a patch that breaks an undeclared path is not detected', 'results are compared as JSON values, so identity, ordering of unordered data and timing are not compared', 'the expectations are supplied by the requester and are not inferred; expectations that are wrong for the ORIGINAL revision are reported by the baseline check, not hidden'],
|
|
343
|
-
harnessSource: `${PROLOGUE}
|
|
344
|
-
const results = {};
|
|
345
|
-
for (const c of INPUT.cases) {
|
|
346
|
-
try { const v = await Promise.resolve(fn(...c.args)); results[c.id] = { ok: true, value: v === undefined ? null : JSON.parse(JSON.stringify(v)) }; }
|
|
347
|
-
catch (e) { results[c.id] = { ok: false }; }
|
|
348
|
-
}
|
|
349
|
-
finish({ results });
|
|
350
|
-
`,
|
|
351
|
-
validateInputs(i) {
|
|
352
|
-
if (!isId(i.export)) return 'export must be an identifier';
|
|
353
|
-
if (!Array.isArray(i.cases) || i.cases.length < 1 || i.cases.length > 16) return 'cases must hold 1 to 16 entries';
|
|
354
|
-
const ids = new Set();
|
|
355
|
-
for (const c of i.cases) {
|
|
356
|
-
if (!c || typeof c.id !== 'string' || !/^[a-z0-9-]{1,40}$/.test(c.id) || ids.has(c.id)) return 'every case needs a unique slug id';
|
|
357
|
-
ids.add(c.id);
|
|
358
|
-
if (!Array.isArray(c.args) || c.args.length > 6) return 'case args must be an array of at most 6';
|
|
359
|
-
if (!('expected' in c)) return 'every case needs an expected value';
|
|
360
|
-
try { if (JSON.stringify(c.expected) === undefined) return 'expected must be JSON'; } catch { return 'expected must be JSON'; }
|
|
361
|
-
}
|
|
362
|
-
return null;
|
|
363
|
-
},
|
|
364
|
-
prepare: (i) => ({ export: i.export, cases: i.cases.map((c) => ({ id: c.id, args: c.args })) }),
|
|
365
|
-
interpret({ inputs, result }) {
|
|
366
|
-
const got = result && result.results && typeof result.results === 'object' ? result.results : null;
|
|
367
|
-
if (!got || result.loadFailed) return { satisfied: null, preconditionsHeld: false, observed: { targetLoaded: false }, reason: 'the target did not report results' };
|
|
368
|
-
const regressions = [];
|
|
369
|
-
let reported = 0;
|
|
370
|
-
for (const c of inputs.cases) {
|
|
371
|
-
const r = got[c.id];
|
|
372
|
-
if (!r || typeof r !== 'object') continue;
|
|
373
|
-
reported++;
|
|
374
|
-
if (r.ok !== true || (0,identity/* digestOf */.ol)(r.value) !== (0,identity/* digestOf */.ol)(c.expected)) regressions.push(c.id);
|
|
375
|
-
}
|
|
376
|
-
const complete = reported === inputs.cases.length;
|
|
377
|
-
const observed = { targetLoaded: true, casesDeclared: inputs.cases.length, casesReported: reported, regressedCases: regressions };
|
|
378
|
-
if (regressions.length) return { satisfied: true, preconditionsHeld: complete, observed, reason: `behaviour differs from the declared expectation in case(s): ${regressions.join(', ')}` };
|
|
379
|
-
if (complete) return { satisfied: false, preconditionsHeld: true, observed, reason: 'every declared case returned its expected value' };
|
|
380
|
-
return { satisfied: null, preconditionsHeld: false, observed, reason: 'not every declared case reported a result' };
|
|
381
|
-
},
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
const ADAPTERS = Object.freeze([injection, authorization, stateTransition, sideEffect, parserResource, replayIdempotency, functionalRegression]);
|
|
385
|
-
|
|
386
|
-
// EXTERNAL MODULE: ./src/posture/oracles/scenario-classes.js
|
|
387
|
-
var scenario_classes = __webpack_require__(57430);
|
|
388
|
-
;// CONCATENATED MODULE: ./src/posture/oracles/registry.js
|
|
389
|
-
// The oracle registry and its manifest (X-202).
|
|
390
|
-
//
|
|
391
|
-
// The registry is built once, at module load, from the static adapter list and
|
|
392
|
-
// frozen. There is no registration function: a candidate worker (or anything at
|
|
393
|
-
// run time) cannot add, replace or edit an adapter, which is half of "workers
|
|
394
|
-
// cannot alter adapter logic". The other half is the harness digest check in
|
|
395
|
-
// `runOracle`.
|
|
396
|
-
//
|
|
397
|
-
// `oracleManifest()` is the machine-readable description X-208 gates a release on:
|
|
398
|
-
// per adapter, its class, declared prerequisites, platforms (with an honest status
|
|
399
|
-
// per platform), budgets, negative controls, fixture locations, limitations and
|
|
400
|
-
// the logic digest a replay manifest pins. It contains no function bodies and no
|
|
401
|
-
// host details, and its order is fixed, so it is byte-stable across runs.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const BY_ID = new Map(ADAPTERS.map((a) => [a.id, a]));
|
|
407
|
-
if (BY_ID.size !== ADAPTERS.length) throw new Error('duplicate oracle adapter id');
|
|
408
|
-
|
|
409
|
-
function getOracle(id) {
|
|
410
|
-
return typeof id === 'string' && BY_ID.has(id) ? BY_ID.get(id) : null;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function listOracles() {
|
|
414
|
-
return [...ADAPTERS].sort((a, b) => a.id.localeCompare(b.id));
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const MANIFEST_SCHEMA = 'agentic-security/oracle-manifest';
|
|
418
|
-
|
|
419
|
-
/** The manifest entry for one adapter: data only. */
|
|
420
|
-
function manifestEntry(a) {
|
|
421
|
-
return {
|
|
422
|
-
id: a.id, class: a.class, version: a.version, logicDigest: a.logicDigest, description: a.description,
|
|
423
|
-
prerequisites: a.prerequisites.map((id) => ({ id, description: oracle/* PREREQUISITES */.sD[id].description })),
|
|
424
|
-
platforms: a.platforms, budgets: a.budgets,
|
|
425
|
-
negativeControls: a.negativeControls, fixtures: a.fixtures, limitations: a.limitations,
|
|
426
|
-
receipts: 'issued by the verifier domain only (runOracle); a caller-supplied receipt is not valid',
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// X-205: the supported-class manifest for non-taint vulnerabilities. Each class names the adapter that executes it, whether
|
|
431
|
-
// that adapter was reused, its fixtures and the user-visible report text (prerequisites, platforms, limitations).
|
|
432
|
-
function scenarioClassEntries(adapters) {
|
|
433
|
-
return scenario_classes/* SCENARIO_CLASSES */.ML.map((c) => {
|
|
434
|
-
const a = adapters.find((x) => x.id === c.adapterId) || null;
|
|
435
|
-
return {
|
|
436
|
-
id: c.id, title: c.title, claim: c.claim, oracle: c.adapterId, adapterReuse: c.adapterReuse,
|
|
437
|
-
requires: c.requires, limitations: c.limitations, fixtures: c.fixtures,
|
|
438
|
-
prerequisites: a ? a.prerequisites.map((id) => ({ id, description: oracle/* PREREQUISITES */.sD[id].description })) : [],
|
|
439
|
-
platforms: a ? a.platforms : null,
|
|
440
|
-
report: (0,scenario_classes/* scenarioClassReportLines */.Uq)(c, a),
|
|
441
|
-
};
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
function oracleManifest(adapters = listOracles()) {
|
|
446
|
-
const entries = adapters.map(manifestEntry);
|
|
447
|
-
return {
|
|
448
|
-
schema: MANIFEST_SCHEMA, schemaVersion: '1.0.0',
|
|
449
|
-
classes: [...oracle/* ORACLE_CLASSES */.aI],
|
|
450
|
-
oracles: entries,
|
|
451
|
-
// Every class the PRD names must have an adapter; a missing one is stated, never implied.
|
|
452
|
-
uncoveredClasses: oracle/* ORACLE_CLASSES */.aI.filter((c) => !adapters.some((a) => a.class === c)),
|
|
453
|
-
problems: [
|
|
454
|
-
...adapters.flatMap((a) => (0,oracle/* validateOracleSpec */.GX)(a).map((p) => `${a.id}: ${p}`)),
|
|
455
|
-
...scenario_classes/* SCENARIO_CLASSES */.ML.filter((c) => !adapters.some((a) => a.id === c.adapterId)).map((c) => `scenario class ${c.id}: its oracle '${c.adapterId}' is not registered`),
|
|
456
|
-
],
|
|
457
|
-
nonTaint: {
|
|
458
|
-
schema: scenario_classes/* NON_TAINT_SCHEMA */.RF, schemaVersion: '1.0.0',
|
|
459
|
-
classes: scenarioClassEntries(adapters),
|
|
460
|
-
unsupported: scenario_classes/* UNSUPPORTED_NON_TAINT */.bm.map((u) => ({ id: u.id, cwes: u.cwes, reason: u.reason })),
|
|
461
|
-
rules: ['taint absence never refutes a non-taint hypothesis', 'an unsupported or unrun case is never counted in the trusted-negative denominator'],
|
|
462
|
-
},
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
/***/ })
|
|
468
|
-
|
|
469
|
-
};
|