@clear-capabilities/agentic-security-scanner 0.144.0 → 0.147.0
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 +390 -0
- package/bin/agentic-security.js +3813 -83
- package/dist/1122.index.js +702 -0
- package/dist/{301.index.js → 1301.index.js} +2 -2
- package/dist/1379.index.js +591 -0
- package/dist/{444.index.js → 1444.index.js} +13 -4
- package/dist/{660.index.js → 1660.index.js} +2 -2
- package/dist/{700.index.js → 1700.index.js} +2 -2
- package/dist/{905.index.js → 1905.index.js} +3 -3
- package/dist/{920.index.js → 1920.index.js} +3 -3
- package/dist/{238.index.js → 2238.index.js} +3 -3
- package/dist/2271.index.js +165 -0
- package/dist/{985.index.js → 2376.index.js} +1260 -340
- package/dist/2432.index.js +793 -0
- package/dist/2659.index.js +93 -0
- package/dist/{826.index.js → 2826.index.js} +2 -2
- package/dist/{830.index.js → 2830.index.js} +2 -2
- package/dist/2923.index.js +298 -0
- package/dist/{1.index.js → 3001.index.js} +5 -5
- package/dist/{117.index.js → 3117.index.js} +3 -3
- package/dist/3180.index.js +307 -0
- package/dist/3276.index.js +117 -0
- package/dist/{415.index.js → 3415.index.js} +2 -2
- package/dist/{499.index.js → 3499.index.js} +2 -2
- package/dist/3518.index.js +450 -0
- package/dist/{526.index.js → 3526.index.js} +14 -6
- package/dist/{736.index.js → 3736.index.js} +4 -4
- package/dist/{839.index.js → 3839.index.js} +4 -4
- package/dist/{113.index.js → 4113.index.js} +14 -6
- package/dist/{265.index.js → 4265.index.js} +2 -2
- package/dist/{384.index.js → 4384.index.js} +3 -3
- package/dist/4547.index.js +268 -0
- package/dist/4863.index.js +422 -0
- package/dist/{970.index.js → 4970.index.js} +67 -3
- package/dist/5051.index.js +770 -0
- package/dist/{144.index.js → 5144.index.js} +5 -5
- package/dist/{333.index.js → 5333.index.js} +3 -3
- package/dist/5343.index.js +185 -0
- package/dist/5350.index.js +866 -0
- package/dist/5561.index.js +436 -0
- package/dist/{637.index.js → 5637.index.js} +29 -7
- package/dist/{449.index.js → 5830.index.js} +78 -14
- package/dist/6626.index.js +532 -0
- package/dist/6662.index.js +297 -0
- package/dist/{675.index.js → 6675.index.js} +5 -5
- package/dist/{730.index.js → 6730.index.js} +6 -6
- package/dist/6829.index.js +225 -0
- package/dist/6944.index.js +130 -0
- package/dist/{178.index.js → 7178.index.js} +26 -8
- package/dist/{227.index.js → 7227.index.js} +2 -2
- package/dist/7310.index.js +520 -0
- package/dist/{552.index.js → 7552.index.js} +4 -4
- package/dist/7709.index.js +78 -0
- package/dist/8218.index.js +160 -0
- package/dist/{476.index.js → 8476.index.js} +4 -4
- package/dist/{513.index.js → 8513.index.js} +5 -5
- package/dist/{520.index.js → 8520.index.js} +2 -2
- package/dist/{718.index.js → 8718.index.js} +2 -2
- package/dist/{752.index.js → 8752.index.js} +2 -2
- package/dist/8846.index.js +100 -0
- package/dist/{435.index.js → 9091.index.js} +580 -187
- package/dist/{207.index.js → 9207.index.js} +2 -2
- package/dist/{220.index.js → 9220.index.js} +2 -2
- package/dist/9390.index.js +163 -0
- package/dist/{503.index.js → 9503.index.js} +2 -2
- package/dist/{801.index.js → 9801.index.js} +2 -2
- package/dist/{824.index.js → 9824.index.js} +2 -2
- package/dist/agentic-security.mjs +16 -16
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/hipaa-security-rule.json +3 -2
- package/package.json +23 -10
- package/src/compare.js +6 -1
- package/src/dataflow/CLAUDE.md +2 -2
- package/src/dataflow/catalog.js +42 -0
- package/src/dataflow/orm-write-catalog.js +175 -0
- package/src/engine.js +580 -30
- package/src/fix/apply-fix-service.js +1 -0
- package/src/history-scan.js +22 -5
- package/src/ir/CLAUDE.md +2 -1
- package/src/ir/chrome-probe.mjs +150 -0
- package/src/ir/parser-js.js +94 -7
- package/src/lineage/CLAUDE.md +1203 -0
- package/src/lineage/DESIGN_DESTINATION_RESOLVER.md +156 -0
- package/src/lineage/DESIGN_GRAPH_BUILDER.md +938 -0
- package/src/lineage/DESIGN_HANDLING_ANALYZER.md +355 -0
- package/src/lineage/DESIGN_INTRAPROCEDURAL.md +628 -0
- package/src/lineage/DESIGN_PATH_PROVENANCE.md +3451 -0
- package/src/lineage/DESIGN_QUEUE_DETAIL.md +120 -0
- package/src/lineage/DESIGN_REGISTRIES.md +880 -0
- package/src/lineage/DESIGN_STORE_DETAIL.md +143 -0
- package/src/lineage/DESIGN_TRANSIT_PROTECTION.md +245 -0
- package/src/lineage/classification.js +56 -0
- package/src/lineage/coverage.js +658 -0
- package/src/lineage/cross-repo-link.js +107 -0
- package/src/lineage/dataflow-graph.schema.json +184 -0
- package/src/lineage/decision-story.js +206 -0
- package/src/lineage/drift-policy.js +279 -0
- package/src/lineage/driver.js +135 -0
- package/src/lineage/engine.js +992 -0
- package/src/lineage/export-briefing.js +628 -0
- package/src/lineage/export-csv.js +62 -0
- package/src/lineage/export-json.js +238 -0
- package/src/lineage/export-privacy.js +258 -0
- package/src/lineage/federation-loader.js +111 -0
- package/src/lineage/field-identity.js +78 -0
- package/src/lineage/fixtures/build-flagship-fixture.mjs +272 -0
- package/src/lineage/fixtures/flagship-graph.json +1453 -0
- package/src/lineage/flow-grade.js +221 -0
- package/src/lineage/governance-edit.js +169 -0
- package/src/lineage/graph-builder.js +1114 -0
- package/src/lineage/graph-diff.js +431 -0
- package/src/lineage/graph-snapshot.js +180 -0
- package/src/lineage/handling-analyzer.js +168 -0
- package/src/lineage/ids.js +349 -0
- package/src/lineage/impact-assessment.js +76 -0
- package/src/lineage/impact-engine.js +268 -0
- package/src/lineage/index.js +281 -0
- package/src/lineage/language-coverage-tiers.js +58 -0
- package/src/lineage/obligation-mapping.js +126 -0
- package/src/lineage/obligation-predicates.js +235 -0
- package/src/lineage/observation-adapters.js +282 -0
- package/src/lineage/observation-correlation.js +622 -0
- package/src/lineage/observation-store.js +497 -0
- package/src/lineage/path-query.js +410 -0
- package/src/lineage/path-store.js +400 -0
- package/src/lineage/protection.js +53 -0
- package/src/lineage/recipient-profile.js +192 -0
- package/src/lineage/recipient-registry.js +394 -0
- package/src/lineage/redact-graph.js +224 -0
- package/src/lineage/remediation.js +417 -0
- package/src/lineage/resolve-destination.js +91 -0
- package/src/lineage/runtime-observation.js +464 -0
- package/src/lineage/scenario-diff.js +84 -0
- package/src/lineage/scenario-engine.js +251 -0
- package/src/lineage/scenario.js +101 -0
- package/src/lineage/schema.js +167 -0
- package/src/lineage/sink-registry.js +427 -0
- package/src/lineage/source-registry.js +357 -0
- package/src/lineage/source-seeding.js +212 -0
- package/src/lineage/summaries.js +590 -0
- package/src/lineage/transform-catalog.js +397 -0
- package/src/lineage/transit-protection.js +150 -0
- package/src/lineage/validate.js +285 -0
- package/src/lsp/server.js +49 -2
- package/src/mcp/CLAUDE.md +7 -1
- package/src/mcp/dataflow-tools.js +160 -0
- package/src/mcp/server.js +1 -1
- package/src/mcp/tools.js +22 -1
- package/src/pipeline/assurance-mode.js +64 -1
- package/src/pipeline/finding-schema.js +8 -1
- package/src/pipeline/scan-health.js +19 -1
- package/src/posture/CLAUDE.md +140 -0
- package/src/posture/accuracy-scorecard.js +60 -0
- package/src/posture/artifact-registry.js +76 -0
- package/src/posture/auditor-walkthrough.js +192 -13
- package/src/posture/compliance-frameworks/hipaa-security-rule.json +3 -2
- package/src/posture/compliance-policy.js +12 -2
- package/src/posture/cross-repo-memory.js +7 -2
- package/src/posture/fix-history.js +25 -2
- package/src/posture/fix-verify.js +9 -1
- package/src/posture/fleet.js +0 -0
- package/src/posture/git-history.js +13 -5
- package/src/posture/material-change.js +21 -2
- package/src/posture/mttr.js +75 -12
- package/src/posture/obligation-evidence-pack.js +202 -0
- package/src/posture/pre-incident-archaeology.js +39 -7
- package/src/posture/privacy-framework.js +14 -0
- package/src/posture/provenance/ai-authorship.js +68 -0
- package/src/posture/provenance/branch-entry.js +80 -0
- package/src/posture/provenance/cache.js +143 -0
- package/src/posture/provenance/confidence.js +36 -0
- package/src/posture/provenance/coordinator.js +786 -0
- package/src/posture/provenance/dag-walk.js +249 -0
- package/src/posture/provenance/evidence-attribution.js +59 -0
- package/src/posture/provenance/git-evidence.js +310 -0
- package/src/posture/provenance/lifecycle.js +208 -0
- package/src/posture/provenance/missing-control-resolver.js +137 -0
- package/src/posture/provenance/origin-resolver.js +342 -0
- package/src/posture/provenance/predicate-replay.js +133 -0
- package/src/posture/provenance/providers/config.js +39 -0
- package/src/posture/provenance/providers/github.js +62 -0
- package/src/posture/provenance/providers/gitlab.js +58 -0
- package/src/posture/provenance/repo-lineage.js +74 -0
- package/src/posture/provenance/sca-origin.js +139 -0
- package/src/posture/provenance/schema.js +255 -0
- package/src/posture/provenance/transitive-sca.js +147 -0
- package/src/posture/provenance/validate.js +30 -0
- package/src/posture/provenance-evidence-bundle.js +144 -0
- package/src/posture/remediation-ledger.js +337 -0
- package/src/posture/sbom-diff.js +15 -2
- package/src/posture/secret-history.js +10 -2
- package/src/posture/state-dir.js +38 -14
- package/src/posture/vuln-archaeology.js +8 -2
- package/src/pr-delta.js +25 -4
- package/src/report/index.js +197 -3
- package/src/runScan.js +34 -5
- package/src/sast/rate-limit.js +33 -3
- package/src/server/CLAUDE.md +47 -0
- package/src/server/graph-loader.js +141 -0
- package/src/server/http-server.js +325 -0
- package/src/server/routes.js +129 -0
- package/src/server/security.js +111 -0
- package/src/server/static-assets.js +139 -0
- package/src/util/git-hardening.js +128 -0
- package/dist/11.index.js +0 -353
- package/dist/259.index.js +0 -975
- package/dist/317.index.js +0 -300
- package/dist/609.index.js +0 -741
- package/dist/838.index.js +0 -152
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
export const id = 3180;
|
|
2
|
+
export const ids = [3180];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 3180:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// EXPORTS
|
|
10
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
11
|
+
generateHtmlReport: () => (/* binding */ generateHtmlReport)
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// EXTERNAL MODULE: external "node:fs"
|
|
15
|
+
var external_node_fs_ = __webpack_require__(3024);
|
|
16
|
+
// EXTERNAL MODULE: external "node:path"
|
|
17
|
+
var external_node_path_ = __webpack_require__(6760);
|
|
18
|
+
// EXTERNAL MODULE: external "node:url"
|
|
19
|
+
var external_node_url_ = __webpack_require__(3136);
|
|
20
|
+
;// CONCATENATED MODULE: ./scripts/bundle-frontend.mjs
|
|
21
|
+
// bundle-frontend.mjs — Milestone 4, sub-project Self-contained HTML
|
|
22
|
+
// report. A minimal, hand-rolled ES-module bundler, deliberately NOT a
|
|
23
|
+
// general one — see this sub-project's own scoping doc for why a real
|
|
24
|
+
// bundler dependency (esbuild etc.) was considered and NOT chosen:
|
|
25
|
+
// frontend/src/'s real module graph (confirmed by a full-tree grep before
|
|
26
|
+
// this file was written) uses ONLY named imports/exports, no default
|
|
27
|
+
// exports, no `export *`, no namespace or side-effect-only imports. This
|
|
28
|
+
// bundler supports exactly that subset and throws a clear error on
|
|
29
|
+
// anything else, rather than silently mishandling an unsupported form.
|
|
30
|
+
//
|
|
31
|
+
// Algorithm: parse each file's own import specifiers + exported top-level
|
|
32
|
+
// names via regex (safe here because the supported subset's syntax is
|
|
33
|
+
// simple and unambiguous — a full parser is not needed for this bounded,
|
|
34
|
+
// fully-enumerated 21-file input); topologically order every reachable
|
|
35
|
+
// module; wrap EVERY NON-ENTRY module in its own IIFE (this is what
|
|
36
|
+
// prevents two dependency files' same-named PRIVATE top-level bindings
|
|
37
|
+
// from colliding when concatenated — a real risk with naive flat
|
|
38
|
+
// concatenation, proven by this file's own test suite); each module's
|
|
39
|
+
// IIFE destructures its own imports from its dependencies' already-
|
|
40
|
+
// evaluated namespace objects (`const { a, b } = __NS_shared;`) and
|
|
41
|
+
// returns its own exports as a plain object.
|
|
42
|
+
//
|
|
43
|
+
// The ENTRY module is deliberately NOT wrapped in an IIFE: its body is
|
|
44
|
+
// emitted directly at the top level of the output (after its own
|
|
45
|
+
// destructured imports), so its top-level declarations — functions the
|
|
46
|
+
// caller needs to invoke directly, e.g. `bootstrap()` in a `<script>`
|
|
47
|
+
// tag, or (as this file's own tests require) a bare exported name called
|
|
48
|
+
// straight off the bundled output — become real top-level bindings
|
|
49
|
+
// rather than being trapped inside a closure whose return value is
|
|
50
|
+
// discarded. Wrapping the entry in an IIFE too (as an earlier draft of
|
|
51
|
+
// this algorithm did) type-checks and looks symmetrical, but it is a
|
|
52
|
+
// correctness bug: it makes every exported name of the entry module
|
|
53
|
+
// unreachable from outside the bundle, which is never what a bundle's
|
|
54
|
+
// caller wants. Only the entry is unwrapped — every other module is
|
|
55
|
+
// still fully IIFE-isolated, so the collision guarantee above is
|
|
56
|
+
// unaffected (there is exactly one unwrapped scope per bundle, by
|
|
57
|
+
// construction, since the entry is the unique DFS root and cannot be
|
|
58
|
+
// re-entered — a cycle back to it is rejected below as a circular
|
|
59
|
+
// import).
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const IMPORT_RE = /import\s*\{([\s\S]*?)\}\s*from\s*['"](\.[^'"]+)['"];?/g;
|
|
65
|
+
const UNSUPPORTED_IMPORT_RE = /^import\s+(?!\{)/m;
|
|
66
|
+
const EXPORT_STAR_RE = /^export\s*\*/m;
|
|
67
|
+
const EXPORT_DEFAULT_RE = /^export\s+default\b/m;
|
|
68
|
+
// Three more real, unsupported forms — added after the final whole-branch
|
|
69
|
+
// review found each builds with NO error (`EXPORT_NAMED_RE` below requires
|
|
70
|
+
// `function`/`class`/`const`/`let`/`var` immediately after `export `, so
|
|
71
|
+
// `export async function` matches none of these guards OR the named-export
|
|
72
|
+
// extractor, and is silently dropped as neither a caught error nor a real
|
|
73
|
+
// export) and only fails later as a page-load SyntaxError, blanking the
|
|
74
|
+
// whole page with no visible error — `export async function` already
|
|
75
|
+
// exists in the real tree today (frontend/src/lib/api-client.js), outside
|
|
76
|
+
// today's bundled entry point's own reachable graph only by luck.
|
|
77
|
+
const EXPORT_ASYNC_RE = /^export\s+async\s+function/m;
|
|
78
|
+
const EXPORT_LIST_RE = /^export\s*\{/m;
|
|
79
|
+
const IMPORT_RENAME_RE = /\bas\b/;
|
|
80
|
+
const EXPORT_NAMED_RE = /^export\s+(?:function\*?|class|const|let|var)\s+([A-Za-z0-9_$]+)/gm;
|
|
81
|
+
|
|
82
|
+
function _nsName(absPath, seen) {
|
|
83
|
+
// A stable, collision-free JS identifier per module, derived from its
|
|
84
|
+
// path. seen is a Map<absPath, name> shared across the whole bundle run.
|
|
85
|
+
if (seen.has(absPath)) return seen.get(absPath);
|
|
86
|
+
const base = absPath.replace(/[^A-Za-z0-9_$]/g, '_');
|
|
87
|
+
const name = `__NS_${base}`;
|
|
88
|
+
seen.set(absPath, name);
|
|
89
|
+
return name;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _parseModule(absPath) {
|
|
93
|
+
const src = external_node_fs_.readFileSync(absPath, 'utf8');
|
|
94
|
+
if (UNSUPPORTED_IMPORT_RE.test(src) || EXPORT_STAR_RE.test(src) || EXPORT_DEFAULT_RE.test(src) || EXPORT_ASYNC_RE.test(src) || EXPORT_LIST_RE.test(src)) {
|
|
95
|
+
throw new Error(`bundleFrontendModules: unsupported import/export form in ${absPath} — only named imports/exports are supported (no default/namespace/side-effect imports, no export */export default/export {}-list/export async function)`);
|
|
96
|
+
}
|
|
97
|
+
const imports = [];
|
|
98
|
+
let m;
|
|
99
|
+
IMPORT_RE.lastIndex = 0;
|
|
100
|
+
while ((m = IMPORT_RE.exec(src))) {
|
|
101
|
+
if (IMPORT_RENAME_RE.test(m[1])) {
|
|
102
|
+
throw new Error(`bundleFrontendModules: unsupported \`import { x as y }\` rename in ${absPath} — only bare named imports are supported`);
|
|
103
|
+
}
|
|
104
|
+
const names = m[1].split(',').map((s) => s.trim()).filter(Boolean);
|
|
105
|
+
const resolved = external_node_path_.resolve(external_node_path_.dirname(absPath), m[2]);
|
|
106
|
+
imports.push({ names, resolved });
|
|
107
|
+
}
|
|
108
|
+
const exportNames = [];
|
|
109
|
+
EXPORT_NAMED_RE.lastIndex = 0;
|
|
110
|
+
while ((m = EXPORT_NAMED_RE.exec(src))) exportNames.push(m[1]);
|
|
111
|
+
// Strip import lines entirely; strip the leading `export ` keyword from
|
|
112
|
+
// each exported declaration (the declaration itself — function/const/
|
|
113
|
+
// class — is kept verbatim, so the body is otherwise untouched).
|
|
114
|
+
const body = src
|
|
115
|
+
.replace(IMPORT_RE, '')
|
|
116
|
+
.replace(/^export\s+(?=(?:function\*?|class|const|let|var)\s)/gm, '');
|
|
117
|
+
return { absPath, body, imports, exportNames };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function bundleFrontendModules(entryAbsPath) {
|
|
121
|
+
const nsNames = new Map();
|
|
122
|
+
const parsed = new Map(); // absPath -> parsed module
|
|
123
|
+
const order = []; // topological, dependency-first
|
|
124
|
+
const visiting = new Set();
|
|
125
|
+
|
|
126
|
+
function visit(absPath) {
|
|
127
|
+
// `visiting` (currently-on-the-DFS-stack) must be checked BEFORE
|
|
128
|
+
// `parsed` (fully-resolved, safe to skip) — found by this task's own
|
|
129
|
+
// review: the original ordering set `parsed` before recursing into a
|
|
130
|
+
// module's own imports, so a cycle back to an ancestor hit the
|
|
131
|
+
// `parsed.has()` check first and returned silently, making the
|
|
132
|
+
// `visiting`-based throw below unreachable for ANY cycle. Reproduced
|
|
133
|
+
// live: a real entry<->b.js cycle built successfully at bundle time
|
|
134
|
+
// and only failed later, as a confusing `ReferenceError` (TDZ) when
|
|
135
|
+
// the broken output was executed, instead of this function's own
|
|
136
|
+
// clear build-time error.
|
|
137
|
+
if (visiting.has(absPath)) {
|
|
138
|
+
throw new Error(`bundleFrontendModules: circular import detected at ${absPath} — this bundler does not support cycles`);
|
|
139
|
+
}
|
|
140
|
+
if (parsed.has(absPath)) return;
|
|
141
|
+
visiting.add(absPath);
|
|
142
|
+
const mod = _parseModule(absPath);
|
|
143
|
+
parsed.set(absPath, mod);
|
|
144
|
+
for (const imp of mod.imports) visit(imp.resolved);
|
|
145
|
+
visiting.delete(absPath);
|
|
146
|
+
order.push(absPath);
|
|
147
|
+
}
|
|
148
|
+
visit(entryAbsPath);
|
|
149
|
+
|
|
150
|
+
const chunks = [];
|
|
151
|
+
for (const absPath of order) {
|
|
152
|
+
const mod = parsed.get(absPath);
|
|
153
|
+
const destructures = mod.imports
|
|
154
|
+
.map((imp) => ` const { ${imp.names.join(', ')} } = ${_nsName(imp.resolved, nsNames)};`)
|
|
155
|
+
.join('\n');
|
|
156
|
+
|
|
157
|
+
if (absPath === entryAbsPath) {
|
|
158
|
+
// Entry: emit unwrapped at top level so its declarations become
|
|
159
|
+
// real top-level bindings the caller can invoke — see the header
|
|
160
|
+
// comment for why this must not be an IIFE like every other module.
|
|
161
|
+
chunks.push(`${destructures}\n${mod.body}`);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const ns = _nsName(absPath, nsNames);
|
|
166
|
+
const returnObj = mod.exportNames.length ? ` return { ${mod.exportNames.join(', ')} };` : ' return {};';
|
|
167
|
+
chunks.push(`const ${ns} = (function() {\n${destructures}\n${mod.body}\n${returnObj}\n})();`);
|
|
168
|
+
}
|
|
169
|
+
const bundled = chunks.join('\n\n');
|
|
170
|
+
|
|
171
|
+
// Real gap found by the final whole-branch review, live-reproduced: the
|
|
172
|
+
// UNSUPPORTED_IMPORT_RE/EXPORT_STAR_RE/EXPORT_DEFAULT_RE guards above
|
|
173
|
+
// only cover THREE specific unsupported forms — real ES syntax this
|
|
174
|
+
// bundler doesn't handle (`export async function`, `export { g };`
|
|
175
|
+
// re-export lists, `import { h as hh }` renames) builds with NO error
|
|
176
|
+
// and only fails later, as a page-load-time SyntaxError that silently
|
|
177
|
+
// blanks the whole page (export-entry.js's own showError() never runs,
|
|
178
|
+
// since the entire inline <script> fails to parse before any of its
|
|
179
|
+
// code executes) — directly contradicting this file's own documented
|
|
180
|
+
// "throws a clear error on anything else" promise. `export async
|
|
181
|
+
// function` already exists in the real tree today
|
|
182
|
+
// (frontend/src/lib/api-client.js) — outside today's bundled entry
|
|
183
|
+
// point's own reachable graph only by luck. Rather than enumerating
|
|
184
|
+
// every unsupported form by name (the exact approach that missed these
|
|
185
|
+
// three), validate the FINAL bundle is syntactically valid JS before
|
|
186
|
+
// returning it — this converts any parse failure, including forms not
|
|
187
|
+
// yet imagined, into a clear build-time error. `new Function` compiles
|
|
188
|
+
// without executing (measured ~1ms on the real ~131KB bundle — cheap).
|
|
189
|
+
try {
|
|
190
|
+
// eslint-disable-next-line no-new-func
|
|
191
|
+
new Function(bundled);
|
|
192
|
+
} catch (e) {
|
|
193
|
+
throw new Error(`bundleFrontendModules: the assembled bundle is not valid JavaScript (${e.message}) — one of the ${order.length} modules likely uses an ES form this bundler doesn't support (only named imports/exports, no async/generator export shorthand ambiguity, no \`export { x };\` lists, no \`import { x as y }\` renames)`);
|
|
194
|
+
}
|
|
195
|
+
return bundled;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// EXTERNAL MODULE: ./src/lineage/export-json.js
|
|
199
|
+
var export_json = __webpack_require__(859);
|
|
200
|
+
;// CONCATENATED MODULE: ./scripts/generate-html-report.mjs
|
|
201
|
+
// generate-html-report.mjs — Milestone 4, sub-project Self-contained
|
|
202
|
+
// HTML report. Assembles ONE offline-safe HTML document: inline CSS,
|
|
203
|
+
// one bundled inline <script> (Task 1's bundler over Task 2's
|
|
204
|
+
// export-entry.js), and the graph embedded via exportGraphJSON
|
|
205
|
+
// (redacted by default) as a global the bundled script reads.
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
const HERE = external_node_path_.dirname((0,external_node_url_.fileURLToPath)(import.meta.url));
|
|
214
|
+
const FRONTEND_ROOT = external_node_path_.resolve(HERE, '../../frontend');
|
|
215
|
+
const STYLES_DIR = external_node_path_.join(FRONTEND_ROOT, 'styles');
|
|
216
|
+
const ENTRY_PATH = external_node_path_.join(FRONTEND_ROOT, 'src', 'export-entry.js');
|
|
217
|
+
|
|
218
|
+
// NOT alphabetical — confirmed this session by reading the real
|
|
219
|
+
// frontend/index.html directly: its own <link> order is
|
|
220
|
+
// tokens.css, shell.css, architecture-view.css, inspector.css,
|
|
221
|
+
// privacy-view.css, trace-view.css, filter-rail.css, inventory-view.css,
|
|
222
|
+
// query-bar.css. `tokens.css` first is load-bearing (CSS custom
|
|
223
|
+
// properties the other 8 files consume via `var(--...)` — an alphabetical
|
|
224
|
+
// sort would put it 8th, after 7 files that reference undefined custom
|
|
225
|
+
// properties on first paint). Matching real cascade order avoids a real
|
|
226
|
+
// specificity/undefined-custom-property regression alphabetical sorting
|
|
227
|
+
// would introduce silently. Re-confirmed against the real index.html
|
|
228
|
+
// directly at implementation time (unchanged from the task-3 brief's own
|
|
229
|
+
// citation).
|
|
230
|
+
const CSS_LOAD_ORDER = [
|
|
231
|
+
'tokens.css', 'shell.css', 'architecture-view.css', 'inspector.css',
|
|
232
|
+
'privacy-view.css', 'trace-view.css', 'filter-rail.css',
|
|
233
|
+
'inventory-view.css', 'query-bar.css',
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
function _inlineCss() {
|
|
237
|
+
const onDisk = new Set(external_node_fs_.readdirSync(STYLES_DIR).filter((f) => f.endsWith('.css')));
|
|
238
|
+
const missing = CSS_LOAD_ORDER.filter((f) => !onDisk.has(f));
|
|
239
|
+
if (missing.length) throw new Error(`generate-html-report: CSS_LOAD_ORDER names files not found in ${STYLES_DIR}: ${missing.join(', ')} — frontend/index.html's own <link> list may have changed; re-sync CSS_LOAD_ORDER with it`);
|
|
240
|
+
const extra = [...onDisk].filter((f) => !CSS_LOAD_ORDER.includes(f));
|
|
241
|
+
if (extra.length) throw new Error(`generate-html-report: real CSS files not in CSS_LOAD_ORDER: ${extra.join(', ')} — a new stylesheet was added to frontend/styles/ without updating this list's load order`);
|
|
242
|
+
return CSS_LOAD_ORDER.map((f) => external_node_fs_.readFileSync(external_node_path_.join(STYLES_DIR, f), 'utf8')).join('\n');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function generateHtmlReport(graph, opts = {}) {
|
|
246
|
+
const exported = (0,export_json.exportGraphJSON)(graph, opts);
|
|
247
|
+
const css = _inlineCss();
|
|
248
|
+
const bundledJs = bundleFrontendModules(ENTRY_PATH);
|
|
249
|
+
// Escape `<` so scanned-source-derived content (a node/flow/data-element
|
|
250
|
+
// label — none of which _redactGraph covers, since labels aren't a
|
|
251
|
+
// secret-shaped surface, but CAN carry arbitrary scanned identifier
|
|
252
|
+
// text) can never contain a literal `</script>` that breaks out of this
|
|
253
|
+
// inline data script and injects a second one. Same mitigation, same
|
|
254
|
+
// reasoning, as the existing SAST/SCA HTML report's own precedent
|
|
255
|
+
// (scanner/src/report/index.js's toHTML) — found missing here and
|
|
256
|
+
// fixed by this sub-project's own final task review, which reproduced
|
|
257
|
+
// the injection live before this fix landed.
|
|
258
|
+
const envelopeJson = JSON.stringify(exported).replace(/</g, '\\u003c');
|
|
259
|
+
return `<!DOCTYPE html>
|
|
260
|
+
<html lang="en" data-theme="dark">
|
|
261
|
+
<head>
|
|
262
|
+
<meta charset="UTF-8" />
|
|
263
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
264
|
+
<title>Data Flow Explorer — self-contained report</title>
|
|
265
|
+
<style>
|
|
266
|
+
${css}
|
|
267
|
+
</style>
|
|
268
|
+
</head>
|
|
269
|
+
<body>
|
|
270
|
+
<div id="app-root"></div>
|
|
271
|
+
<script>
|
|
272
|
+
// The full exportGraphJSON envelope (digest/scope/coverage/limitations/
|
|
273
|
+
// confidential + the redacted graph body) is embedded whole here, under
|
|
274
|
+
// its own name, so this report carries the same tamper-evident/disclosure
|
|
275
|
+
// metadata the JSON exporter produces (PRD §17.5) — never dropped just
|
|
276
|
+
// because this is an HTML report rather than a .json file.
|
|
277
|
+
//
|
|
278
|
+
// __AGENTIC_SECURITY_EXPORTED_GRAPH__ is then bound to the envelope's own
|
|
279
|
+
// \`.graph\` body, NOT the envelope itself — this is a deliberate,
|
|
280
|
+
// necessary departure from an earlier draft of this generator (which
|
|
281
|
+
// assigned the whole envelope directly to that name). frontend/src/
|
|
282
|
+
// export-entry.js (Task 2, already shipped and covered by its own golden
|
|
283
|
+
// DOM-equivalence test, test/bundle-frontend-golden.test.js) reads this
|
|
284
|
+
// EXACT global and passes it straight through to bootstrap(rootEl, graph)
|
|
285
|
+
// -> mountShell(rootEl, graph)/computeFilterFacets(graph), both of which
|
|
286
|
+
// expect a real DataFlowGraph v1 document (top-level nodes/edges/flows/...),
|
|
287
|
+
// never the exportGraphJSON envelope shape (which nests the graph one
|
|
288
|
+
// level down, under .graph, alongside digest/scope/confidential). Handing
|
|
289
|
+
// bootstrap() the envelope instead of the graph would silently break real
|
|
290
|
+
// rendering — exactly what this sub-project's own real-Chrome acceptance
|
|
291
|
+
// proof (task-3 brief, Step 5) exists to catch. Confirmed against
|
|
292
|
+
// export-entry.js's and Task 2's golden test's real source before this
|
|
293
|
+
// generator was written, not assumed.
|
|
294
|
+
window.__AGENTIC_SECURITY_EXPORT_ENVELOPE__ = ${envelopeJson};
|
|
295
|
+
window.__AGENTIC_SECURITY_EXPORTED_GRAPH__ = window.__AGENTIC_SECURITY_EXPORT_ENVELOPE__.graph;
|
|
296
|
+
</script>
|
|
297
|
+
<script>
|
|
298
|
+
${bundledJs}
|
|
299
|
+
</script>
|
|
300
|
+
</body>
|
|
301
|
+
</html>`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
/***/ })
|
|
306
|
+
|
|
307
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export const id = 3276;
|
|
2
|
+
export const ids = [3276];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 3276:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
9
|
+
/* harmony export */ validateScenario: () => (/* binding */ validateScenario)
|
|
10
|
+
/* harmony export */ });
|
|
11
|
+
/* unused harmony exports SCENARIO_VERSION, SCENARIO_OPERATION_KINDS, SCENARIO_OPERATION_REQUIRED_FIELDS */
|
|
12
|
+
// scenario.js — M5 deliverable #3a (FR-502 §10.10, DFG-0xx): the
|
|
13
|
+
// Scenario extension contract — a hypothetical set of graph overrides,
|
|
14
|
+
// NOT a DataFlowGraph v1 entity, mirrors recipient-profile.js's own
|
|
15
|
+
// contract shape exactly (structural-only {valid, errors} validator,
|
|
16
|
+
// zero graph access, per-field evidence typing for the fields a
|
|
17
|
+
// Scenario actually overrides).
|
|
18
|
+
//
|
|
19
|
+
// See docs/superpowers/plans/2026-09-02-data-flow-explorer-m5-whatif-simulator-scoping.md
|
|
20
|
+
// for the full design reasoning, including why EVIDENCE_GRADES gained
|
|
21
|
+
// 'assumed' instead of reusing 'declared', and why this module's own
|
|
22
|
+
// operations catalog excludes synthetic node/edge insertion (deferred).
|
|
23
|
+
|
|
24
|
+
const SCENARIO_VERSION = '1.0.0';
|
|
25
|
+
|
|
26
|
+
// The 6 in-scope hypothetical-change kinds (FR-502's own 7, minus the
|
|
27
|
+
// deferred synthetic-insertion category). Each operation names its
|
|
28
|
+
// target canonical id(s) plus the override value(s); scenario-engine.js
|
|
29
|
+
// is the only consumer that interprets `kind`.
|
|
30
|
+
const SCENARIO_OPERATION_KINDS = Object.freeze([
|
|
31
|
+
'require_transit_protection',
|
|
32
|
+
'apply_handling',
|
|
33
|
+
'remove_entity',
|
|
34
|
+
'replace_recipient_fact',
|
|
35
|
+
'change_storage_fact',
|
|
36
|
+
'change_governance_fact',
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
// Per-operation-kind required fields, beyond the universal `kind`. Kept
|
|
40
|
+
// as data (not inline in validateScenario) for the same reason
|
|
41
|
+
// SCENARIO_OPERATION_KINDS above is — a single source of truth a table
|
|
42
|
+
// can be checked against — though scenario-engine.js does not currently
|
|
43
|
+
// import this table itself; it only mentions it in a prose comment on
|
|
44
|
+
// its own _applyReplaceRecipientFact.
|
|
45
|
+
const SCENARIO_OPERATION_REQUIRED_FIELDS = Object.freeze({
|
|
46
|
+
require_transit_protection: ['targetEdgeId'],
|
|
47
|
+
apply_handling: ['targetEdgeId', 'handling'],
|
|
48
|
+
remove_entity: ['targetNodeId'],
|
|
49
|
+
replace_recipient_fact: ['targetNodeId', 'field', 'value'],
|
|
50
|
+
change_storage_fact: ['targetNodeId', 'field', 'value'],
|
|
51
|
+
change_governance_fact: ['targetFlowId', 'field', 'value'],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function _isNonEmptyString(v) { return typeof v === 'string' && v.length > 0; }
|
|
55
|
+
function _isPlainObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }
|
|
56
|
+
function _isStringArray(v) { return Array.isArray(v) && v.every((x) => typeof x === 'string'); }
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Structural validation only — mirrors validateRecipientProfile's own
|
|
60
|
+
* {valid, errors} shape and "never throws" contract. Does not check
|
|
61
|
+
* that targetEdgeId/targetNodeId/targetFlowId actually exist in any
|
|
62
|
+
* real graph — that is scenario-engine.js's job at apply time, since
|
|
63
|
+
* this module has zero graph access by design.
|
|
64
|
+
*/
|
|
65
|
+
function validateScenario(record) {
|
|
66
|
+
const errors = [];
|
|
67
|
+
const err = (p, message) => errors.push({ path: p, message });
|
|
68
|
+
if (!record || typeof record !== 'object' || Array.isArray(record)) {
|
|
69
|
+
err('$', 'Scenario record must be an object');
|
|
70
|
+
return { valid: false, errors };
|
|
71
|
+
}
|
|
72
|
+
if (!_isNonEmptyString(record.id) || !record.id.startsWith('scenario:')) {
|
|
73
|
+
err('$.id', 'id is required and must start with "scenario:"');
|
|
74
|
+
}
|
|
75
|
+
if (!_isNonEmptyString(record.version)) err('$.version', 'version is required');
|
|
76
|
+
if (!_isNonEmptyString(record.baseGraphId)) err('$.baseGraphId', 'baseGraphId is required');
|
|
77
|
+
if (!_isNonEmptyString(record.baseGraphDigest)) err('$.baseGraphDigest', 'baseGraphDigest is required');
|
|
78
|
+
if (!_isNonEmptyString(record.author)) err('$.author', 'author is required');
|
|
79
|
+
if (!_isNonEmptyString(record.createdAt)) err('$.createdAt', 'createdAt is required');
|
|
80
|
+
if (record.expiration !== null && record.expiration !== undefined && !_isNonEmptyString(record.expiration)) {
|
|
81
|
+
err('$.expiration', 'expiration must be a string or null');
|
|
82
|
+
}
|
|
83
|
+
if (!_isStringArray(record.assumptions ?? [])) err('$.assumptions', 'assumptions must be an array of strings');
|
|
84
|
+
if (!_isStringArray(record.verificationRequirements ?? [])) {
|
|
85
|
+
err('$.verificationRequirements', 'verificationRequirements must be an array of strings');
|
|
86
|
+
}
|
|
87
|
+
if (!Array.isArray(record.operations) || record.operations.length === 0) {
|
|
88
|
+
err('$.operations', 'operations is required and must be a non-empty array');
|
|
89
|
+
} else {
|
|
90
|
+
record.operations.forEach((op, i) => {
|
|
91
|
+
const p = `$.operations[${i}]`;
|
|
92
|
+
if (!_isPlainObject(op)) { err(p, 'each operation must be an object'); return; }
|
|
93
|
+
if (!SCENARIO_OPERATION_KINDS.includes(op.kind)) {
|
|
94
|
+
err(`${p}.kind`, `unrecognized operation kind "${op.kind}" — must be one of ${SCENARIO_OPERATION_KINDS.join('|')}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
for (const field of SCENARIO_OPERATION_REQUIRED_FIELDS[op.kind]) {
|
|
98
|
+
if (op[field] === undefined || op[field] === null || op[field] === '') {
|
|
99
|
+
err(`${p}.${field}`, `operation of kind "${op.kind}" requires "${field}"`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// simulatedDelta is populated by scenario-engine.js after apply, never
|
|
105
|
+
// by a caller constructing the pre-apply record — null is the only
|
|
106
|
+
// valid pre-apply value, an object (scenario-diff.js's own shape) the
|
|
107
|
+
// only valid post-apply value.
|
|
108
|
+
if (record.simulatedDelta !== null && record.simulatedDelta !== undefined && !_isPlainObject(record.simulatedDelta)) {
|
|
109
|
+
err('$.simulatedDelta', 'simulatedDelta must be null (before apply) or an object (after apply)');
|
|
110
|
+
}
|
|
111
|
+
return { valid: errors.length === 0, errors };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/***/ })
|
|
116
|
+
|
|
117
|
+
};
|