@clear-capabilities/agentic-security-scanner 0.172.1 → 0.173.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.
Files changed (194) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/bin/agentic-security.js +37 -0
  3. package/dist/119.index.js +197 -0
  4. package/dist/128.index.js +129 -0
  5. package/dist/1293.index.js +5689 -0
  6. package/dist/1351.index.js +133 -0
  7. package/dist/1387.index.js +271 -0
  8. package/dist/1437.index.js +23 -0
  9. package/dist/1445.index.js +110 -0
  10. package/dist/1505.index.js +151 -0
  11. package/dist/1557.index.js +293 -0
  12. package/dist/1621.index.js +311 -0
  13. package/dist/1751.index.js +160 -0
  14. package/dist/1765.index.js +727 -0
  15. package/dist/1861.index.js +690 -0
  16. package/dist/1898.index.js +116 -0
  17. package/dist/1906.index.js +433 -0
  18. package/dist/1920.index.js +1 -291
  19. package/dist/208.index.js +1631 -0
  20. package/dist/2199.index.js +0 -239
  21. package/dist/2223.index.js +159 -0
  22. package/dist/2530.index.js +152 -0
  23. package/dist/2565.index.js +116 -0
  24. package/dist/2574.index.js +127 -0
  25. package/dist/2620.index.js +345 -0
  26. package/dist/2773.index.js +98 -0
  27. package/dist/2775.index.js +32 -0
  28. package/dist/2966.index.js +171 -0
  29. package/dist/2972.index.js +87 -0
  30. package/dist/2991.index.js +131 -0
  31. package/dist/3063.index.js +833 -0
  32. package/dist/3075.index.js +227 -0
  33. package/dist/3121.index.js +297 -0
  34. package/dist/3314.index.js +263 -0
  35. package/dist/3480.index.js +1822 -0
  36. package/dist/3481.index.js +24 -0
  37. package/dist/3506.index.js +268 -0
  38. package/dist/3533.index.js +591 -0
  39. package/dist/3653.index.js +450 -0
  40. package/dist/3822.index.js +131 -0
  41. package/dist/3868.index.js +292 -0
  42. package/dist/3917.index.js +152 -0
  43. package/dist/3918.index.js +192 -0
  44. package/dist/4090.index.js +288 -0
  45. package/dist/4121.index.js +1190 -0
  46. package/dist/4142.index.js +0 -239
  47. package/dist/4336.index.js +436 -0
  48. package/dist/4387.index.js +532 -0
  49. package/dist/4413.index.js +302 -0
  50. package/dist/4476.index.js +4510 -0
  51. package/dist/4491.index.js +133 -0
  52. package/dist/4538.index.js +185 -0
  53. package/dist/4684.index.js +319 -0
  54. package/dist/4740.index.js +165 -0
  55. package/dist/4780.index.js +209 -0
  56. package/dist/4802.index.js +225 -0
  57. package/dist/4812.index.js +379 -0
  58. package/dist/4835.index.js +126 -0
  59. package/dist/494.index.js +226 -0
  60. package/dist/4991.index.js +549 -0
  61. package/dist/5199.index.js +305 -0
  62. package/dist/5216.index.js +109 -0
  63. package/dist/5439.index.js +301 -0
  64. package/dist/5443.index.js +97 -0
  65. package/dist/5496.index.js +377 -0
  66. package/dist/550.index.js +2 -1
  67. package/dist/5801.index.js +588 -0
  68. package/dist/5858.index.js +223 -0
  69. package/dist/5933.index.js +143 -0
  70. package/dist/601.index.js +23 -0
  71. package/dist/6173.index.js +273 -0
  72. package/dist/6291.index.js +1871 -0
  73. package/dist/6312.index.js +666 -0
  74. package/dist/634.index.js +16182 -0
  75. package/dist/644.index.js +87 -0
  76. package/dist/6508.index.js +283 -0
  77. package/dist/6560.index.js +645 -0
  78. package/dist/6576.index.js +422 -0
  79. package/dist/6579.index.js +271 -0
  80. package/dist/6745.index.js +305 -0
  81. package/dist/6818.index.js +21642 -0
  82. package/dist/6972.index.js +167 -0
  83. package/dist/6995.index.js +163 -0
  84. package/dist/7249.index.js +191 -0
  85. package/dist/7372.index.js +1197 -0
  86. package/dist/7546.index.js +541 -0
  87. package/dist/76.index.js +448 -0
  88. package/dist/7879.index.js +163 -0
  89. package/dist/8025.index.js +130 -0
  90. package/dist/8054.index.js +248 -0
  91. package/dist/8097.index.js +201 -0
  92. package/dist/8126.index.js +191 -0
  93. package/dist/8178.index.js +78 -0
  94. package/dist/818.index.js +212 -0
  95. package/dist/8382.index.js +93 -0
  96. package/dist/8408.index.js +261 -0
  97. package/dist/8540.index.js +174 -0
  98. package/dist/8635.index.js +802 -0
  99. package/dist/8688.index.js +410 -0
  100. package/dist/8699.index.js +779 -0
  101. package/dist/8891.index.js +457 -0
  102. package/dist/8916.index.js +885 -0
  103. package/dist/9146.index.js +170 -0
  104. package/dist/9168.index.js +841 -0
  105. package/dist/9292.index.js +369 -0
  106. package/dist/9382.index.js +135 -11
  107. package/dist/9422.index.js +866 -0
  108. package/dist/9440.index.js +453 -0
  109. package/dist/9479.index.js +152 -0
  110. package/dist/9481.index.js +220 -0
  111. package/dist/9539.index.js +100 -0
  112. package/dist/9547.index.js +103 -0
  113. package/dist/9560.index.js +41 -61
  114. package/dist/9692.index.js +90 -0
  115. package/dist/9800.index.js +213 -1
  116. package/dist/9899.index.js +138 -0
  117. package/dist/9981.index.js +206 -0
  118. package/dist/999.index.js +117 -0
  119. package/dist/agentic-security.mjs +27 -27
  120. package/dist/agentic-security.mjs.sha256 +1 -1
  121. package/package.json +28 -3
  122. package/src/context-intelligence/candidates.js +2 -1
  123. package/src/context-intelligence/capabilities.js +2 -1
  124. package/src/context-intelligence/cli.js +2 -0
  125. package/src/context-intelligence/config.js +3 -1
  126. package/src/context-intelligence/controls.js +49 -1
  127. package/src/context-intelligence/counterexample.js +200 -0
  128. package/src/context-intelligence/cross-route.js +111 -0
  129. package/src/context-intelligence/findings.js +166 -0
  130. package/src/context-intelligence/invariants.js +197 -0
  131. package/src/context-intelligence/queue.js +6 -1
  132. package/src/context-intelligence/reachability.js +2 -2
  133. package/src/context-intelligence/review.js +68 -4
  134. package/src/dataflow/access-paths.js +11 -0
  135. package/src/dataflow/analysis-limits.js +66 -0
  136. package/src/dataflow/engine.js +272 -20
  137. package/src/dataflow/service-edges.js +174 -0
  138. package/src/dataflow/summaries.js +7 -1
  139. package/src/discovery/judge.js +2 -1
  140. package/src/engine.js +82 -14
  141. package/src/fix/apply-fix-service.js +55 -8
  142. package/src/fix/batch-journal.js +84 -0
  143. package/src/fix/exploit-replay.js +51 -0
  144. package/src/fix/footprint.js +65 -0
  145. package/src/fix/patch-policy.js +49 -0
  146. package/src/fix/secret-persistence.js +39 -0
  147. package/src/fix/test-commands.js +84 -0
  148. package/src/fix/verification-adapters.js +97 -0
  149. package/src/fix/verification-record.js +243 -0
  150. package/src/fix/verify-patch.js +89 -0
  151. package/src/ir/CLAUDE.md +5 -0
  152. package/src/ir/callgraph.js +23 -1
  153. package/src/ir/module-resolver.js +315 -0
  154. package/src/ir/parser-js.js +21 -2
  155. package/src/ir/semantic-graph.js +245 -0
  156. package/src/lineage/build-attested-correlation.js +63 -0
  157. package/src/lineage/deployment-identity.js +76 -0
  158. package/src/mcp/tools.js +13 -34
  159. package/src/pipeline/canonical-output.js +64 -0
  160. package/src/pipeline/detector-runner.js +7 -0
  161. package/src/pipeline/scan-health.js +14 -0
  162. package/src/pipeline/supervised-analyzers.js +125 -0
  163. package/src/pipeline/supervised-child.js +17 -0
  164. package/src/pipeline/telemetry.js +79 -0
  165. package/src/posture/accuracy-scorecard.js +98 -9
  166. package/src/posture/artifact-registry.js +5 -0
  167. package/src/posture/assurance/evidence-registry.js +215 -0
  168. package/src/posture/assurance/release-package-verify.js +135 -0
  169. package/src/posture/assurance/release-package.js +118 -0
  170. package/src/posture/assurance/release-verdict.js +94 -0
  171. package/src/posture/assurance/waiver.js +200 -0
  172. package/src/posture/compliance-truth.js +100 -0
  173. package/src/posture/evaluation/scan-child.js +3 -1
  174. package/src/posture/evaluation/why-missed.js +3 -1
  175. package/src/posture/exploitability-evidence.js +141 -0
  176. package/src/posture/fix-verify.js +4 -2
  177. package/src/posture/fleet/exposure-ranking.js +115 -0
  178. package/src/posture/fleet/service-chain-graph.js +150 -0
  179. package/src/posture/graph-reconciliation.js +154 -0
  180. package/src/posture/provenance/coverage.js +103 -0
  181. package/src/posture/provenance/git-evidence.js +84 -0
  182. package/src/posture/provenance/origin-resolver.js +7 -1
  183. package/src/posture/provenance/predicate-replay.js +14 -4
  184. package/src/posture/runtime-correlation.js +61 -15
  185. package/src/posture/runtime-explanation.js +116 -0
  186. package/src/posture/runtime-trace-ingest.js +194 -0
  187. package/src/posture/scan-checkpoint.js +38 -10
  188. package/src/posture/waf-ingest.js +7 -2
  189. package/src/report/index.js +4 -0
  190. package/src/report/oscal.js +38 -0
  191. package/src/report/perspectives.js +150 -0
  192. package/src/sast/_comment-strip.js +8 -1
  193. package/src/sast/ruby.js +26 -0
  194. package/src/util/string-memo.js +38 -0
@@ -0,0 +1,379 @@
1
+ export const id = 4812;
2
+ export const ids = [4812];
3
+ export const modules = {
4
+
5
+ /***/ 64812:
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__(73024);
16
+ // EXTERNAL MODULE: external "node:path"
17
+ var external_node_path_ = __webpack_require__(76760);
18
+ // EXTERNAL MODULE: external "node:url"
19
+ var external_node_url_ = __webpack_require__(73136);
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__(63406);
200
+ // EXTERNAL MODULE: ./src/shared/frontend-root.js
201
+ var frontend_root = __webpack_require__(35934);
202
+ ;// CONCATENATED MODULE: ./scripts/generate-html-report.mjs
203
+ // generate-html-report.mjs — Milestone 4, sub-project Self-contained
204
+ // HTML report. Assembles ONE offline-safe HTML document: inline CSS,
205
+ // one bundled inline <script> (Task 1's bundler over Task 2's
206
+ // export-entry.js), and the graph embedded via exportGraphJSON
207
+ // (redacted by default) as a global the bundled script reads.
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+ const HERE = external_node_path_.dirname((0,external_node_url_.fileURLToPath)(import.meta.url));
217
+ // See src/shared/frontend-root.js: a hardcoded `../../frontend` resolved
218
+ // correctly for this file's unbundled dev location (scanner/scripts/) but
219
+ // broke for the published package, whose build now copies frontend/ into
220
+ // scanner/dist/frontend/ — a different relative depth from this same
221
+ // module once ncc bundles it into a dist/ chunk.
222
+ const FRONTEND_ROOT = (0,frontend_root/* resolveFrontendRoot */.D)(HERE);
223
+ const STYLES_DIR = external_node_path_.join(FRONTEND_ROOT, 'styles');
224
+ const ENTRY_PATH = external_node_path_.join(FRONTEND_ROOT, 'src', 'export-entry.js');
225
+
226
+ // NOT alphabetical — confirmed this session by reading the real
227
+ // frontend/index.html directly: its own <link> order is
228
+ // tokens.css, shell.css, architecture-view.css, inspector.css,
229
+ // privacy-view.css, trace-view.css, filter-rail.css, inventory-view.css,
230
+ // query-bar.css. `tokens.css` first is load-bearing (CSS custom
231
+ // properties the other 8 files consume via `var(--...)` — an alphabetical
232
+ // sort would put it 8th, after 7 files that reference undefined custom
233
+ // properties on first paint). Matching real cascade order avoids a real
234
+ // specificity/undefined-custom-property regression alphabetical sorting
235
+ // would introduce silently. Re-confirmed against the real index.html
236
+ // directly at implementation time (unchanged from the task-3 brief's own
237
+ // citation).
238
+ const CSS_LOAD_ORDER = [
239
+ 'tokens.css', 'shell.css', 'architecture-view.css', 'inspector.css',
240
+ 'privacy-view.css', 'trace-view.css', 'filter-rail.css',
241
+ 'inventory-view.css', 'query-bar.css',
242
+ ];
243
+
244
+ function _inlineCss() {
245
+ const onDisk = new Set(external_node_fs_.readdirSync(STYLES_DIR).filter((f) => f.endsWith('.css')));
246
+ const missing = CSS_LOAD_ORDER.filter((f) => !onDisk.has(f));
247
+ 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`);
248
+ const extra = [...onDisk].filter((f) => !CSS_LOAD_ORDER.includes(f));
249
+ 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`);
250
+ return CSS_LOAD_ORDER.map((f) => external_node_fs_.readFileSync(external_node_path_.join(STYLES_DIR, f), 'utf8')).join('\n');
251
+ }
252
+
253
+ function generateHtmlReport(graph, opts = {}) {
254
+ const exported = (0,export_json.exportGraphJSON)(graph, opts);
255
+ const css = _inlineCss();
256
+ const bundledJs = bundleFrontendModules(ENTRY_PATH);
257
+ // Escape `<` so scanned-source-derived content (a node/flow/data-element
258
+ // label — none of which _redactGraph covers, since labels aren't a
259
+ // secret-shaped surface, but CAN carry arbitrary scanned identifier
260
+ // text) can never contain a literal `</script>` that breaks out of this
261
+ // inline data script and injects a second one. Same mitigation, same
262
+ // reasoning, as the existing SAST/SCA HTML report's own precedent
263
+ // (scanner/src/report/index.js's toHTML) — found missing here and
264
+ // fixed by this sub-project's own final task review, which reproduced
265
+ // the injection live before this fix landed.
266
+ const envelopeJson = JSON.stringify(exported).replace(/</g, '\\u003c');
267
+ return `<!DOCTYPE html>
268
+ <html lang="en" data-theme="dark">
269
+ <head>
270
+ <meta charset="UTF-8" />
271
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
272
+ <title>Data Flow Explorer — self-contained report</title>
273
+ <style>
274
+ ${css}
275
+ </style>
276
+ </head>
277
+ <body>
278
+ <div id="app-root"></div>
279
+ <script>
280
+ // The full exportGraphJSON envelope (digest/scope/coverage/limitations/
281
+ // confidential + the redacted graph body) is embedded whole here, under
282
+ // its own name, so this report carries the same tamper-evident/disclosure
283
+ // metadata the JSON exporter produces (PRD §17.5) — never dropped just
284
+ // because this is an HTML report rather than a .json file.
285
+ //
286
+ // __AGENTIC_SECURITY_EXPORTED_GRAPH__ is then bound to the envelope's own
287
+ // \`.graph\` body, NOT the envelope itself — this is a deliberate,
288
+ // necessary departure from an earlier draft of this generator (which
289
+ // assigned the whole envelope directly to that name). frontend/src/
290
+ // export-entry.js (Task 2, already shipped and covered by its own golden
291
+ // DOM-equivalence test, test/bundle-frontend-golden.test.js) reads this
292
+ // EXACT global and passes it straight through to bootstrap(rootEl, graph)
293
+ // -> mountShell(rootEl, graph)/computeFilterFacets(graph), both of which
294
+ // expect a real DataFlowGraph v1 document (top-level nodes/edges/flows/...),
295
+ // never the exportGraphJSON envelope shape (which nests the graph one
296
+ // level down, under .graph, alongside digest/scope/confidential). Handing
297
+ // bootstrap() the envelope instead of the graph would silently break real
298
+ // rendering — exactly what this sub-project's own real-Chrome acceptance
299
+ // proof (task-3 brief, Step 5) exists to catch. Confirmed against
300
+ // export-entry.js's and Task 2's golden test's real source before this
301
+ // generator was written, not assumed.
302
+ window.__AGENTIC_SECURITY_EXPORT_ENVELOPE__ = ${envelopeJson};
303
+ window.__AGENTIC_SECURITY_EXPORTED_GRAPH__ = window.__AGENTIC_SECURITY_EXPORT_ENVELOPE__.graph;
304
+ </script>
305
+ <script>
306
+ ${bundledJs}
307
+ </script>
308
+ </body>
309
+ </html>`;
310
+ }
311
+
312
+
313
+ /***/ }),
314
+
315
+ /***/ 35934:
316
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
317
+
318
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
319
+ /* harmony export */ D: () => (/* binding */ resolveFrontendRoot)
320
+ /* harmony export */ });
321
+ /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(73024);
322
+ /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(76760);
323
+ // frontend-root.js — locates the Data Flow Explorer's `frontend/` assets
324
+ // (index.html/src/styles) regardless of whether this code is running:
325
+ // - unbundled, straight out of scanner/src/ or scanner/scripts/ (dev/test —
326
+ // frontend/ is a monorepo sibling of scanner/, 2-3 levels up), or
327
+ // - bundled by `npm run build` (ncc splits a dynamic import() into its own
328
+ // chunk file physically written into scanner/dist/, so import.meta.url
329
+ // inside that chunk reflects dist/'s own location, not the original
330
+ // source file's — a fixed relative-levels-up path that was correct for
331
+ // one depth breaks silently at the other), or
332
+ // - installed from the published npm package, where the build copies
333
+ // frontend/'s servable files into scanner/dist/frontend/ (a sibling of
334
+ // the chunk file itself, i.e. 0 levels up) — see scripts/copy-frontend.mjs.
335
+ //
336
+ // Rather than hardcode one of those depths (the bug this file fixes: every
337
+ // consumer used to hardcode the dev-only depth), search upward from the
338
+ // caller's own directory and take the first candidate that actually has a
339
+ // frontend/index.html on disk. Never guessed silently past that — a caller
340
+ // with no match anywhere gets a clear, actionable error instead of a
341
+ // downstream ENOENT/404 with no indication why.
342
+
343
+
344
+
345
+
346
+ const MAX_LEVELS_UP = 4;
347
+
348
+ /**
349
+ * @param {string} startDir - `path.dirname(fileURLToPath(import.meta.url))`
350
+ * of the CALLING module (not this file) — each caller's own bundled/
351
+ * unbundled location determines which candidate depth resolves.
352
+ * @returns {string} absolute path to a real `frontend/` directory containing
353
+ * `index.html`.
354
+ * @throws if no candidate directory up to MAX_LEVELS_UP contains one.
355
+ */
356
+ function resolveFrontendRoot(startDir) {
357
+ const tried = [];
358
+ for (let up = 0; up <= MAX_LEVELS_UP; up++) {
359
+ const candidate = node_path__WEBPACK_IMPORTED_MODULE_1__.resolve(startDir, ...Array(up).fill('..'), 'frontend');
360
+ tried.push(candidate);
361
+ // Every caller invokes resolveFrontendRoot() once, at module-load time,
362
+ // to compute a top-level FRONTEND_ROOT const (see static-assets.js /
363
+ // generate-html-report.mjs) — never per-request inside the explore
364
+ // server's request handler. At most MAX_LEVELS_UP+1 (5) sync stat calls
365
+ // at process startup is not a request-path DoS surface.
366
+ if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(node_path__WEBPACK_IMPORTED_MODULE_1__.join(candidate, 'index.html'))) return candidate; // agentic-security-ignore: dos-sync-io
367
+ }
368
+ throw new Error(
369
+ `resolveFrontendRoot: no frontend/index.html found searching up from ${startDir}. ` +
370
+ `Tried: ${tried.join(', ')}. If you're running from a source checkout, run \`npm run build\` ` +
371
+ `first (it copies frontend/ into scanner/dist/frontend/); if you're running the published ` +
372
+ `package, this indicates a packaging defect — report it.`
373
+ );
374
+ }
375
+
376
+
377
+ /***/ })
378
+
379
+ };
@@ -0,0 +1,126 @@
1
+ export const id = 4835;
2
+ export const ids = [4835];
3
+ export const modules = {
4
+
5
+ /***/ 34835:
6
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
+
8
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
+ /* harmony export */ synthesizeRules: () => (/* binding */ synthesizeRules)
10
+ /* harmony export */ });
11
+ /* unused harmony export _internals */
12
+ /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(73024);
13
+ /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(76760);
14
+ /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69259);
15
+ // Auto-rule synthesis from repeated FPs (FR-LEARN-6).
16
+ //
17
+ // Reads `.agentic-security/triage-feedback.json` (populated by the /triage
18
+ // slash command). When 5+ findings sharing a similar shape get marked FP,
19
+ // propose a YAML suppression rule and write it to
20
+ // `.agentic-security/rules-proposed/auto-<timestamp>.yml`. The operator
21
+ // reviews and either drops it into `rules/` (active) or deletes it.
22
+ //
23
+ // Honest scope:
24
+ // - We propose, we don't auto-activate. The customer decides.
25
+ // - Similar-shape = same (rule_id or vuln-family) AND same file glob root.
26
+ // - Threshold = 5 occurrences by default (env override).
27
+
28
+
29
+
30
+
31
+
32
+ const DEFAULT_FP_THRESHOLD = 5;
33
+
34
+ function _readTriage(scanRoot) {
35
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, 'triage-feedback.json');
36
+ if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
37
+ try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); } catch { return null; }
38
+ }
39
+
40
+ function _shapeKey(entry) {
41
+ // Group by rule/family + the dir prefix (first two path segments) so we
42
+ // suggest rules scoped to the same module — never project-wide.
43
+ const fam = entry.family || entry.cwe || entry.vuln || 'unknown';
44
+ const file = entry.file || '';
45
+ const dir = file.split('/').slice(0, 2).join('/') || '.';
46
+ return `${fam}::${dir}`;
47
+ }
48
+
49
+ function _summarizeGroup(entries) {
50
+ const e0 = entries[0];
51
+ return {
52
+ family: e0.family || null,
53
+ rule: e0.cwe || e0.vuln || 'unknown',
54
+ dirGlob: (e0.file || '').split('/').slice(0, 2).join('/') + '/**',
55
+ count: entries.length,
56
+ examples: entries.slice(0, 3).map(e => `${e.file}:${e.line}`),
57
+ };
58
+ }
59
+
60
+ function _yamlProposal(group) {
61
+ const ruleId = `auto-suppress-${group.family || group.rule}-${Date.now().toString(36)}`;
62
+ return `# Auto-synthesised suppression proposal (FR-LEARN-6).
63
+ # Generated from ${group.count} false-positive verdicts on ${group.family || group.rule}
64
+ # in ${group.dirGlob}.
65
+ #
66
+ # Examples:
67
+ ${group.examples.map(e => '# - ' + e).join('\n')}
68
+ #
69
+ # Review carefully BEFORE moving into rules/. This is a PROPOSAL.
70
+
71
+ - id: ${ruleId}
72
+ title: "Auto-suppress: ${group.family || group.rule}"
73
+ description: "Repeated FP verdicts in ${group.dirGlob}"
74
+ shadow: true # never blocks CI; safe by default
75
+ match:
76
+ family: ${group.family || group.rule}
77
+ paths:
78
+ - "${group.dirGlob}"
79
+ action: suppress
80
+ `;
81
+ }
82
+
83
+ /**
84
+ * Public entry: scan the triage history and emit a proposal YAML for any
85
+ * group with ≥ threshold FP verdicts. Returns the list of proposals written.
86
+ */
87
+ function synthesizeRules(scanRoot, opts = {}) {
88
+ const threshold = parseInt(opts.threshold || process.env.AGENTIC_SECURITY_RULE_SYNTHESIS_THRESHOLD || String(DEFAULT_FP_THRESHOLD), 10);
89
+ const triage = _readTriage(scanRoot);
90
+ if (!triage) return [];
91
+ // triage format (per v0.46): { verdicts: [{file, line, vuln, family, verdict, ...}] }
92
+ const verdicts = triage.verdicts || [];
93
+ const fps = verdicts.filter(v => v.verdict === 'fp' || v.verdict === 'false-positive');
94
+ if (!fps.length) return [];
95
+ const groups = new Map();
96
+ for (const e of fps) {
97
+ const k = _shapeKey(e);
98
+ if (!groups.has(k)) groups.set(k, []);
99
+ groups.get(k).push(e);
100
+ }
101
+ const proposals = [];
102
+ const dir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, 'rules-proposed');
103
+ if (!opts.dryRun && !(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.isSafeStateDir)(node_path__WEBPACK_IMPORTED_MODULE_1__.dirname(dir))) return [];
104
+ for (const [, group] of groups) {
105
+ if (group.length < threshold) continue;
106
+ const summary = _summarizeGroup(group);
107
+ const yaml = _yamlProposal(summary);
108
+ const name = `auto-${(summary.family || summary.rule).replace(/[^a-zA-Z0-9_-]/g, '-')}-${Date.now().toString(36)}.yml`;
109
+ const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(dir, name);
110
+ if (!opts.dryRun) {
111
+ try {
112
+ node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(dir, { recursive: true });
113
+ node_fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(fp, yaml);
114
+ } catch { /* non-fatal */ }
115
+ }
116
+ proposals.push({ ...summary, file: fp, yaml });
117
+ }
118
+ return proposals;
119
+ }
120
+
121
+ const _internals = { DEFAULT_FP_THRESHOLD };
122
+
123
+
124
+ /***/ })
125
+
126
+ };