@legionworks/facet 1.9.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/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +23 -0
- package/README.md +158 -0
- package/dist/gallery/chunk-4g8rem85.css +656 -0
- package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
- package/dist/gallery/frame/artifact.css +8424 -0
- package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
- package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
- package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
- package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
- package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
- package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
- package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
- package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
- package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
- package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
- package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
- package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
- package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
- package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
- package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
- package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
- package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
- package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
- package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
- package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
- package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
- package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
- package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
- package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
- package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
- package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
- package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
- package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
- package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
- package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
- package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
- package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
- package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
- package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
- package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
- package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
- package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
- package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
- package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
- package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
- package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
- package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
- package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
- package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
- package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
- package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
- package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
- package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
- package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
- package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
- package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
- package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
- package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
- package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
- package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
- package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
- package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
- package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
- package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
- package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
- package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
- package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
- package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
- package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
- package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
- package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
- package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
- package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
- package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
- package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
- package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
- package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
- package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
- package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
- package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
- package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
- package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
- package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
- package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
- package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
- package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
- package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
- package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
- package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
- package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
- package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
- package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
- package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
- package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
- package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
- package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
- package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
- package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
- package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
- package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
- package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
- package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
- package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
- package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
- package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
- package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
- package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
- package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
- package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
- package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
- package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
- package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
- package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
- package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
- package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
- package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
- package/dist/gallery/frame/frame.css +183 -0
- package/dist/gallery/frame/runtime/chart.js +42744 -0
- package/dist/gallery/frame/runtime/html.js +14 -0
- package/dist/gallery/frame/runtime/markdown.js +1403 -0
- package/dist/gallery/frame/runtime/mermaid.js +32 -0
- package/dist/gallery/frame/runtime/svg.js +14 -0
- package/dist/gallery/frame/runtime/tsx.js +52 -0
- package/dist/gallery/index.html +104 -0
- package/docs/reference/cli.md +163 -0
- package/docs/reference/export.md +106 -0
- package/docs/reference/html.md +215 -0
- package/docs/reference/http.md +85 -0
- package/docs/reference/mcp.md +78 -0
- package/docs/reference/security.md +119 -0
- package/docs/reference/storage.md +93 -0
- package/docs/reference/tsx.md +101 -0
- package/docs/reference/validation.md +212 -0
- package/package.json +89 -0
- package/scripts/build-gallery.ts +127 -0
- package/scripts/launch-netns.sh +67 -0
- package/skills/facet/SKILL.md +100 -0
- package/src/cli/client.ts +311 -0
- package/src/cli/commands/create.ts +49 -0
- package/src/cli/commands/doctor.ts +249 -0
- package/src/cli/commands/export.ts +179 -0
- package/src/cli/commands/instantiate.ts +35 -0
- package/src/cli/commands/list.ts +49 -0
- package/src/cli/commands/open.ts +75 -0
- package/src/cli/commands/pin.ts +39 -0
- package/src/cli/commands/promote.ts +49 -0
- package/src/cli/commands/publish.ts +122 -0
- package/src/cli/commands/read-back.ts +56 -0
- package/src/cli/commands/status.ts +114 -0
- package/src/cli/commands/watch.ts +146 -0
- package/src/cli/main.ts +522 -0
- package/src/cli/output.ts +106 -0
- package/src/cli/parser.ts +376 -0
- package/src/cli/presenter.ts +212 -0
- package/src/cli/service-metadata.ts +134 -0
- package/src/cli/spawn-service.ts +326 -0
- package/src/css.d.ts +4 -0
- package/src/gallery-web/app.ts +1581 -0
- package/src/gallery-web/export-menu.ts +121 -0
- package/src/gallery-web/export.ts +112 -0
- package/src/gallery-web/favicon.ts +54 -0
- package/src/gallery-web/frame/entries/chart.ts +6 -0
- package/src/gallery-web/frame/entries/html.ts +6 -0
- package/src/gallery-web/frame/entries/markdown.ts +6 -0
- package/src/gallery-web/frame/entries/mermaid.ts +6 -0
- package/src/gallery-web/frame/entries/svg.ts +6 -0
- package/src/gallery-web/frame/entries/tsx.ts +6 -0
- package/src/gallery-web/frame/frame-payload.ts +91 -0
- package/src/gallery-web/frame/renderer-validation.ts +13 -0
- package/src/gallery-web/frame/renderers/chart.ts +182 -0
- package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
- package/src/gallery-web/frame/renderers/html.ts +48 -0
- package/src/gallery-web/frame/renderers/markdown.ts +81 -0
- package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
- package/src/gallery-web/frame/renderers/registry.ts +260 -0
- package/src/gallery-web/frame/renderers/svg.ts +393 -0
- package/src/gallery-web/frame/renderers/tsx.ts +61 -0
- package/src/gallery-web/frame/runtime.ts +501 -0
- package/src/gallery-web/frame/styles/artifact.css +8424 -0
- package/src/gallery-web/frame/styles/frame.css +183 -0
- package/src/gallery-web/frame/styles/html-source.css +23 -0
- package/src/gallery-web/frame/view-box.ts +33 -0
- package/src/gallery-web/frame-html.ts +72 -0
- package/src/gallery-web/index.html +104 -0
- package/src/gallery-web/session.ts +107 -0
- package/src/gallery-web/sse-client.ts +212 -0
- package/src/gallery-web/styles/app.css +420 -0
- package/src/gallery-web/styles/tokens.css +112 -0
- package/src/gallery-web/styles/verdict.css +186 -0
- package/src/gallery-web/swap.ts +64 -0
- package/src/gallery-web/theme.ts +40 -0
- package/src/gallery-web/view-state.ts +93 -0
- package/src/harness-adapters/claude-code/README.md +10 -0
- package/src/harness-adapters/claude-code/facet.sh +4 -0
- package/src/harness-adapters/codex/README.md +10 -0
- package/src/harness-adapters/codex/facet.sh +4 -0
- package/src/harness-adapters/mcp/README.md +13 -0
- package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
- package/src/harness-adapters/mcp/main.ts +15 -0
- package/src/harness-adapters/mcp/server.ts +164 -0
- package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
- package/src/harness-adapters/opencode/README.md +10 -0
- package/src/harness-adapters/opencode/facet.sh +4 -0
- package/src/runtime/compiled-entrypoints.ts +69 -0
- package/src/service/dispatcher.ts +717 -0
- package/src/service/export.ts +218 -0
- package/src/service/http-utils.ts +55 -0
- package/src/service/lexical/expectations.ts +210 -0
- package/src/service/lifecycle/idle-controller.ts +125 -0
- package/src/service/lifecycle/orphan-cleanup.ts +128 -0
- package/src/service/lifecycle/process-lock.ts +235 -0
- package/src/service/main.ts +136 -0
- package/src/service/process.ts +240 -0
- package/src/service/router-guards.ts +239 -0
- package/src/service/router.ts +627 -0
- package/src/service/security/auth.ts +168 -0
- package/src/service/security/host-origin.ts +138 -0
- package/src/service/security/http-guards.ts +13 -0
- package/src/service/security/leases.ts +182 -0
- package/src/service/security/token-store.ts +138 -0
- package/src/service/server.ts +356 -0
- package/src/service/store/database.ts +61 -0
- package/src/service/store/evidence-retention.ts +234 -0
- package/src/service/store/migrations.ts +167 -0
- package/src/service/store/repository-lifecycle.ts +121 -0
- package/src/service/store/repository.ts +732 -0
- package/src/service/store/schema.ts +218 -0
- package/src/service/stored-verdict.ts +97 -0
- package/src/service/stream.ts +301 -0
- package/src/service/verdict-enrichment.ts +45 -0
- package/src/shared/build/frame-bundle-plugins.ts +61 -0
- package/src/shared/build-mode.ts +28 -0
- package/src/shared/config/evidence-read.ts +98 -0
- package/src/shared/config/limits.ts +56 -0
- package/src/shared/config/paths.ts +96 -0
- package/src/shared/contracts/artifact-types.ts +2 -0
- package/src/shared/contracts/artifact.ts +98 -0
- package/src/shared/contracts/commands/_shared.ts +59 -0
- package/src/shared/contracts/commands/guards.ts +86 -0
- package/src/shared/contracts/commands/index.ts +204 -0
- package/src/shared/contracts/commands/names.ts +35 -0
- package/src/shared/contracts/commands/requests.ts +149 -0
- package/src/shared/contracts/commands/results.ts +200 -0
- package/src/shared/contracts/envelope.ts +140 -0
- package/src/shared/contracts/events.ts +41 -0
- package/src/shared/contracts/observed-counts.ts +19 -0
- package/src/shared/contracts/renderers.ts +6 -0
- package/src/shared/contracts/validation.ts +296 -0
- package/src/shared/errors/facet-error.ts +127 -0
- package/src/shared/errors/store-error.ts +68 -0
- package/src/shared/evidence-image.ts +37 -0
- package/src/shared/export.ts +31 -0
- package/src/shared/html/policy.ts +108 -0
- package/src/shared/html/style-vocabulary.ts +352 -0
- package/src/shared/logging/logger.ts +108 -0
- package/src/shared/logging/redact.ts +49 -0
- package/src/shared/security/frozen-csp.ts +13 -0
- package/src/shared/storage-version.ts +2 -0
- package/src/shared/tsx/execution.ts +29 -0
- package/src/shared/tsx/import-policy.ts +94 -0
- package/src/shared/util/dir-permissions.ts +94 -0
- package/src/shared/util/mermaid-nodes.ts +230 -0
- package/src/shared/util/process.ts +67 -0
- package/src/shared/util/time.ts +21 -0
- package/src/shared/version.ts +3 -0
- package/src/validation/sandbox/limits.ts +41 -0
- package/src/validation/sandbox/netns.ts +177 -0
- package/src/validation/tier0/chart.ts +177 -0
- package/src/validation/tier0/dom-shim.ts +54 -0
- package/src/validation/tier0/html.ts +408 -0
- package/src/validation/tier0/markdown.ts +285 -0
- package/src/validation/tier0/mermaid.ts +98 -0
- package/src/validation/tier0/runner.ts +504 -0
- package/src/validation/tier0/svg.ts +321 -0
- package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
- package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
- package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
- package/src/validation/tier0/tsx/ast-policy.ts +181 -0
- package/src/validation/tier0/tsx/compiler.ts +156 -0
- package/src/validation/tier0/worker-dispatch.ts +211 -0
- package/src/validation/tier0/worker-entry.ts +96 -0
- package/src/validation/tier0/worker-input.ts +75 -0
- package/src/validation/tier1/browser-process.ts +147 -0
- package/src/validation/tier1/cdp-pipe.ts +281 -0
- package/src/validation/tier1/entries/chart.ts +5 -0
- package/src/validation/tier1/entries/html.ts +6 -0
- package/src/validation/tier1/entries/markdown.ts +5 -0
- package/src/validation/tier1/entries/mermaid.ts +5 -0
- package/src/validation/tier1/entries/svg.ts +5 -0
- package/src/validation/tier1/entries/tsx.ts +5 -0
- package/src/validation/tier1/frame-target.ts +162 -0
- package/src/validation/tier1/harness-entry.ts +261 -0
- package/src/validation/tier1/harness.ts +215 -0
- package/src/validation/tier1/isolated-probe.ts +94 -0
- package/src/validation/tier1/launcher.ts +177 -0
- package/src/validation/tier1/limits.ts +80 -0
- package/src/validation/tier1/nonce.ts +17 -0
- package/src/validation/tier1/protocol-probe.ts +519 -0
- package/src/validation/tier1/runner.ts +1122 -0
- package/src/validation/tier1/verdict.ts +246 -0
- package/src/validation/tier1/webp.ts +69 -0
- package/templates/README.md +88 -0
- package/templates/bar-compare.vl.json +63 -0
- package/templates/capacity-report.tsx +102 -0
- package/templates/decision-record.md +24 -0
- package/templates/deployment-state.mmd +40 -0
- package/templates/exemplar.md +91 -0
- package/templates/facet-story.tsx +799 -0
- package/templates/fleet-dashboard.html +141 -0
- package/templates/html-release-ledger.html +91 -0
- package/templates/html-status-report.html +114 -0
- package/templates/incident-console.tsx +147 -0
- package/templates/legion-boundaries.mmd +23 -0
- package/templates/legion-flow.mmd +15 -0
- package/templates/legion-sequence.mmd +14 -0
- package/templates/legion-state.mmd +7 -0
- package/templates/metric-card.svg +29 -0
- package/templates/observability-map.svg +101 -0
- package/templates/pipeline-audit.md +108 -0
- package/templates/release-metrics.vl.json +98 -0
- package/templates/service-topology.mmd +47 -0
- package/templates/status-report.md +32 -0
- package/templates/system-map.svg +53 -0
- package/templates/timeseries.vl.json +71 -0
- package/templates/tsx-interactive-counter.tsx +19 -0
- package/templates/tsx-status-report.tsx +31 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolated-world probe — evaluate counts inside the pre-created
|
|
3
|
+
* execution context (`Page.createIsolatedWorld`). The isolated world
|
|
4
|
+
* is OUTSIDE the page's JavaScript scope: a hostile artifact that
|
|
5
|
+
* monkey-patched `document.querySelectorAll` (Test A) cannot reach
|
|
6
|
+
* it. Counts taken here are the third authority channel after
|
|
7
|
+
* `DOMSnapshot` and `DOM.getDocument`.
|
|
8
|
+
*
|
|
9
|
+
* Failure to evaluate returns null — the verifier falls back to
|
|
10
|
+
* `shim_only` instead of `tampered` because a missing isolated
|
|
11
|
+
* world is a probe-level failure, not a page-world divergence.
|
|
12
|
+
*
|
|
13
|
+
* `graphCount` matches the protocol probe: one count per
|
|
14
|
+
* renderer-owned `<svg>`. `mermaidNodeCount` sums the `g.node`
|
|
15
|
+
* descendants. Those renderer counts are marker-scoped; opaque regions
|
|
16
|
+
* deliberately census every owning `<canvas>` in the frame document so
|
|
17
|
+
* smuggled canvases cannot hide outside a marked root. The probe never
|
|
18
|
+
* calls getContext(), which would create an observation.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { ProtocolObservation } from "../../shared/contracts/validation";
|
|
22
|
+
import { HTML_STRUCTURAL_GROUPS } from "../../shared/html/policy";
|
|
23
|
+
|
|
24
|
+
import type { VerifierCdpSession } from "./browser-process";
|
|
25
|
+
|
|
26
|
+
export async function probeIsolatedCounts(
|
|
27
|
+
session: VerifierCdpSession,
|
|
28
|
+
executionContextId: number,
|
|
29
|
+
): Promise<ProtocolObservation | null> {
|
|
30
|
+
try {
|
|
31
|
+
const selectors = Object.fromEntries(
|
|
32
|
+
Object.entries(HTML_STRUCTURAL_GROUPS).map(([group, names]) => [group, names.join(",")]),
|
|
33
|
+
);
|
|
34
|
+
const result = (await session.send("Runtime.evaluate", {
|
|
35
|
+
contextId: executionContextId,
|
|
36
|
+
returnByValue: true,
|
|
37
|
+
expression: [
|
|
38
|
+
"(function(){",
|
|
39
|
+
" var candidates = Array.prototype.slice.call(document.querySelectorAll('[data-facet-renderer-root=\"true\"]'));",
|
|
40
|
+
" var candidateSet = new Set(candidates);",
|
|
41
|
+
" var roots = candidates.filter(function(root){",
|
|
42
|
+
" for (var parent = root.parentElement; parent; parent = parent.parentElement) {",
|
|
43
|
+
" if (candidateSet.has(parent)) return false;",
|
|
44
|
+
" }",
|
|
45
|
+
" return true;",
|
|
46
|
+
" });",
|
|
47
|
+
" var svgRoots = roots.filter(function(root){ return String(root.nodeName).toLowerCase() === 'svg'; });",
|
|
48
|
+
" var graphRoots = svgRoots.filter(function(root){ return root.getAttribute('data-facet-renderer-graph') === 'true'; });",
|
|
49
|
+
" var htmlRoots = roots.filter(function(root){ return String(root.nodeName).toLowerCase() !== 'svg'; });",
|
|
50
|
+
` var selectors = ${JSON.stringify(selectors)};`,
|
|
51
|
+
" var html = htmlRoots.length === 0 ? null : {rendererRootCount:htmlRoots.length,headingCount:0,tableCount:0,listCount:0,imageCount:0,canvasCount:0,externalImageCount:0};",
|
|
52
|
+
" var externalImageCount = 0;",
|
|
53
|
+
" for (var h = 0; h < htmlRoots.length; h++) {",
|
|
54
|
+
" var htmlRoot = htmlRoots[h];",
|
|
55
|
+
" html.headingCount += htmlRoot.querySelectorAll(selectors.headings).length;",
|
|
56
|
+
" html.tableCount += htmlRoot.querySelectorAll(selectors.tables).length;",
|
|
57
|
+
" html.listCount += htmlRoot.querySelectorAll(selectors.lists).length;",
|
|
58
|
+
" var images = Array.prototype.slice.call(htmlRoot.querySelectorAll(selectors.images));",
|
|
59
|
+
" html.imageCount += images.length;",
|
|
60
|
+
" var rootExternal = images.filter(function(image){ try { return new URL(image.getAttribute('src') || '').protocol === 'https:'; } catch (_) { return false; } }).length;",
|
|
61
|
+
" html.externalImageCount += rootExternal;",
|
|
62
|
+
" externalImageCount += rootExternal;",
|
|
63
|
+
" html.canvasCount += htmlRoot.querySelectorAll(selectors.canvases).length;",
|
|
64
|
+
" }",
|
|
65
|
+
" var nodeCount = 0;",
|
|
66
|
+
" var visibleSvgCount = 0;",
|
|
67
|
+
" for (var i = 0; i < svgRoots.length; i++) {",
|
|
68
|
+
" var vb = svgRoots[i].getAttribute('viewBox') || '';",
|
|
69
|
+
" var parts = vb.trim().split(/[\\s,]+/).map(Number.parseFloat);",
|
|
70
|
+
" if (parts.length === 4 && Number.isFinite(parts[2]) && Number.isFinite(parts[3]) && parts[2] > 0 && parts[3] > 0) visibleSvgCount += 1;",
|
|
71
|
+
" }",
|
|
72
|
+
" for (var j = 0; j < graphRoots.length; j++) nodeCount += graphRoots[j].querySelectorAll('g.node').length;",
|
|
73
|
+
" var errorCount = document.querySelectorAll('[data-facet-error]').length;",
|
|
74
|
+
" var opaqueRegionCount = Array.prototype.slice.call(document.querySelectorAll('*')).filter(function(el){ return String(el.nodeName).toLowerCase() === 'canvas'; }).length;",
|
|
75
|
+
" return {",
|
|
76
|
+
" rendererRootSvgCount: svgRoots.length,",
|
|
77
|
+
" graphCount: graphRoots.length,",
|
|
78
|
+
" mermaidNodeCount: nodeCount,",
|
|
79
|
+
" visibleSvgCount: visibleSvgCount,",
|
|
80
|
+
" viewBoxes: [],",
|
|
81
|
+
" errorCount: errorCount,",
|
|
82
|
+
" opaqueRegionCount: opaqueRegionCount,",
|
|
83
|
+
" externalImageCount: externalImageCount,",
|
|
84
|
+
" discriminativeErrors: [],",
|
|
85
|
+
" ...(html === null ? {} : {html: html})",
|
|
86
|
+
" };",
|
|
87
|
+
"})()",
|
|
88
|
+
].join("\n"),
|
|
89
|
+
})) as { result: { value?: ProtocolObservation } };
|
|
90
|
+
return result.result.value ?? null;
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the PINNED chrome-headless-shell binary and the production
|
|
3
|
+
* netns wrapper. Both paths live behind narrow helpers so the runner
|
|
4
|
+
* never inlines a path string and so the egress penetration harness
|
|
5
|
+
* can share the same lookup logic.
|
|
6
|
+
*
|
|
7
|
+
* The wrapper script (`scripts/launch-netns.sh`) is the egress boundary
|
|
8
|
+
* — `unshare --map-current-user --net --` removes every reachable
|
|
9
|
+
* interface, route, and DNS context from the browser process. A future
|
|
10
|
+
* regression that swaps the wrapper for a plain chromium exec would
|
|
11
|
+
* silently re-enable off-loopback traffic; the harness's CI test would
|
|
12
|
+
* catch that, but the path resolution here is the only place that
|
|
13
|
+
* names the wrapper script.
|
|
14
|
+
*
|
|
15
|
+
* The shell binary is pinned per ADR 0001 D3. The cache layout matches
|
|
16
|
+
* what `puppeteer` writes under `~/.cache/puppeteer/chrome-headless-shell/`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
|
|
23
|
+
import { InsecureLevelSchema, type InsecureLevel } from "../../shared/contracts/validation";
|
|
24
|
+
import { TIER1_PINNED_VERSION } from "./limits";
|
|
25
|
+
|
|
26
|
+
const ENV_PINNED_VERSION = "FACET_TIER1_PINNED_VERSION";
|
|
27
|
+
const ENV_CACHE_DIR = "FACET_TIER1_BROWSER_CACHE";
|
|
28
|
+
// Test-only seam: production callers resolve the repository wrapper path.
|
|
29
|
+
const ENV_NETNS_WRAPPER = "FACET_TIER1_NETNS_WRAPPER";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A pair of resolved paths the runner hands to puppeteer-core. The
|
|
33
|
+
* wrapper (`executablePath`) is what puppeteer-core actually `exec`s;
|
|
34
|
+
* `binaryPath` records the absolute path to the shell inside the
|
|
35
|
+
* wrapper's argv so a failure diagnostic can cite it.
|
|
36
|
+
*/
|
|
37
|
+
export interface ResolvedLauncher {
|
|
38
|
+
readonly executablePath: string;
|
|
39
|
+
readonly binaryPath: string;
|
|
40
|
+
readonly pinnedVersion: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type LauncherOverrides = {
|
|
44
|
+
readonly version?: string;
|
|
45
|
+
readonly cacheRoot?: string;
|
|
46
|
+
readonly wrapperPath?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Default lookup locations for the cached shell. The cache directory
|
|
51
|
+
* layout matches what `puppeteer browsers install chrome-headless-shell`
|
|
52
|
+
* writes — pinning that layout here means the project does not need
|
|
53
|
+
* its own installer.
|
|
54
|
+
*/
|
|
55
|
+
function defaultCacheRoot(): string {
|
|
56
|
+
return join(homedir(), ".cache", "puppeteer", "chrome-headless-shell");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Read an env var or return undefined. */
|
|
60
|
+
function readEnv(name: string): string | undefined {
|
|
61
|
+
const value = process.env[name];
|
|
62
|
+
return value !== undefined && value.length > 0 ? value : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Probe the cache layout for the pinned shell. Returns null when no
|
|
67
|
+
* candidate file exists; the caller turns that into a typed
|
|
68
|
+
* `tier1_launcher_missing` error.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveShellBinary(
|
|
71
|
+
overrides: { readonly version?: string; readonly cacheRoot?: string } = {},
|
|
72
|
+
): string | null {
|
|
73
|
+
const version = overrides.version ?? readEnv(ENV_PINNED_VERSION) ?? TIER1_PINNED_VERSION;
|
|
74
|
+
const cacheRoot = overrides.cacheRoot ?? readEnv(ENV_CACHE_DIR) ?? defaultCacheRoot();
|
|
75
|
+
const candidates = [
|
|
76
|
+
join(cacheRoot, version, "chrome-headless-shell-linux64", "chrome-headless-shell"),
|
|
77
|
+
join(cacheRoot, `linux-${version}`, "chrome-headless-shell-linux64", "chrome-headless-shell"),
|
|
78
|
+
join(cacheRoot, version, "chrome-headless-shell"),
|
|
79
|
+
];
|
|
80
|
+
for (const candidate of candidates) {
|
|
81
|
+
if (existsSync(candidate)) return candidate;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Resolve the production netns wrapper script. The wrapper is the
|
|
88
|
+
* only egress boundary — when it is missing, callers surface a typed
|
|
89
|
+
* `tier1_unavailable` instead of falling back to a plain exec.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveNetnsWrapper(overrides: { readonly wrapperPath?: string } = {}): string {
|
|
92
|
+
const override = overrides.wrapperPath ?? readEnv(ENV_NETNS_WRAPPER);
|
|
93
|
+
if (override !== undefined) return override;
|
|
94
|
+
const fromRepo = join(process.cwd(), "scripts", "launch-netns.sh");
|
|
95
|
+
if (existsSync(fromRepo)) return fromRepo;
|
|
96
|
+
const fromSibling = join(import.meta.dir, "..", "..", "..", "scripts", "launch-netns.sh");
|
|
97
|
+
return fromSibling;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The Puppeteer-compatible browser args. Every flag here is required:
|
|
102
|
+
*
|
|
103
|
+
* - `--headless=new` selects the modern headless mode that owns the
|
|
104
|
+
* same renderer as full Chrome.
|
|
105
|
+
* - `--no-sandbox` is OMITTED — we keep Chromium's browser sandbox
|
|
106
|
+
* (the netns wrapper passes `--map-current-user` so the user is
|
|
107
|
+
* not mapped to root, and the renderer retains its sandbox).
|
|
108
|
+
* - `--no-first-run`, `--disable-background-networking`,
|
|
109
|
+
* `--disable-component-update`, `--disable-default-apps`,
|
|
110
|
+
* `--disable-sync` keep first-launch noise and network from
|
|
111
|
+
* contaminating an isolated run.
|
|
112
|
+
* - `--user-data-dir=$tmp/...` plus the verifier's profile-mode
|
|
113
|
+
* (0o700) keeps the on-disk footprint ephemeral.
|
|
114
|
+
*
|
|
115
|
+
* `--single-process --no-zygote` are NOT used. The Phase-0 fidelity
|
|
116
|
+
* spike relied on them to bound the process tree, but the pinned
|
|
117
|
+
* The pinned build aborts the CDP target during `newPage()` when
|
|
118
|
+
* those flags are present (verified empirically against the wrapper).
|
|
119
|
+
* The multi-process model is fine inside an isolated verifier run
|
|
120
|
+
* because every renderer lives under the same netns pid and exits
|
|
121
|
+
* when the wrapper does.
|
|
122
|
+
*
|
|
123
|
+
* CDP transport (`--remote-debugging-pipe`) is added by puppeteer-core
|
|
124
|
+
* itself when `pipe: true` is set on the launch options; the netns
|
|
125
|
+
* wrapper inherits the pipe through `exec`.
|
|
126
|
+
*/
|
|
127
|
+
export function buildBrowserArgs(userDataDir: string): readonly string[] {
|
|
128
|
+
return [
|
|
129
|
+
"--headless=new",
|
|
130
|
+
"--no-first-run",
|
|
131
|
+
"--disable-background-networking",
|
|
132
|
+
"--disable-component-update",
|
|
133
|
+
"--disable-default-apps",
|
|
134
|
+
"--disable-sync",
|
|
135
|
+
"--disable-features=AutofillServerCommunication,OptimizationHints",
|
|
136
|
+
`--user-data-dir=${userDataDir}`,
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Resolve every launcher path needed for a single run. Throws a
|
|
142
|
+
* plain `Error` (not `FacetError`) because the verifier is the only
|
|
143
|
+
* caller and maps the error into a typed `tier1_launcher_missing` or
|
|
144
|
+
* `tier1_unavailable` upstream.
|
|
145
|
+
*/
|
|
146
|
+
export function resolveLauncher(
|
|
147
|
+
level: InsecureLevel,
|
|
148
|
+
overrides?: LauncherOverrides,
|
|
149
|
+
): ResolvedLauncher;
|
|
150
|
+
export function resolveLauncher(overrides?: LauncherOverrides): ResolvedLauncher;
|
|
151
|
+
export function resolveLauncher(
|
|
152
|
+
levelOrOverrides: InsecureLevel | LauncherOverrides = 0,
|
|
153
|
+
passedOverrides: LauncherOverrides = {},
|
|
154
|
+
): ResolvedLauncher {
|
|
155
|
+
const level = typeof levelOrOverrides === "number" ? levelOrOverrides : 0;
|
|
156
|
+
const overrides = typeof levelOrOverrides === "number" ? passedOverrides : levelOrOverrides;
|
|
157
|
+
InsecureLevelSchema.parse(level);
|
|
158
|
+
const binaryPath = resolveShellBinary({
|
|
159
|
+
...(overrides.version !== undefined ? { version: overrides.version } : {}),
|
|
160
|
+
...(overrides.cacheRoot !== undefined ? { cacheRoot: overrides.cacheRoot } : {}),
|
|
161
|
+
});
|
|
162
|
+
if (binaryPath === null) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`pinned chrome-headless-shell ${overrides.version ?? TIER1_PINNED_VERSION} not found in cache`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
executablePath:
|
|
169
|
+
level === 0
|
|
170
|
+
? resolveNetnsWrapper(
|
|
171
|
+
overrides.wrapperPath !== undefined ? { wrapperPath: overrides.wrapperPath } : {},
|
|
172
|
+
)
|
|
173
|
+
: binaryPath,
|
|
174
|
+
binaryPath,
|
|
175
|
+
pinnedVersion: overrides.version ?? TIER1_PINNED_VERSION,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource caps for the Tier 1 verifier.
|
|
3
|
+
*
|
|
4
|
+
* Each cap is named and exposed so the runner, the harness builder,
|
|
5
|
+
* and the penetration test agree on a single value. A failing cap
|
|
6
|
+
* produces a typed `FacetError` with the matching `tier1_*` code.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Wall-clock budget for one Tier 1 verifier invocation. The harness
|
|
11
|
+
* bundles the REAL renderers (mermaid + marked + vega, ~8 MB inline);
|
|
12
|
+
* parse + first render of a 40-node fixture needs real headroom.
|
|
13
|
+
*/
|
|
14
|
+
export const TIER1_TIMEOUT_MS = 60_000;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Path to the pinned `chrome-headless-shell` distribution. The release
|
|
18
|
+
* manifest pins a specific revision; the default resolves the first
|
|
19
|
+
* match under `~/.cache/puppeteer/chrome-headless-shell/`. Tests inject
|
|
20
|
+
* a custom resolver so the path does not become load-bearing on a
|
|
21
|
+
* specific dev workstation.
|
|
22
|
+
*/
|
|
23
|
+
export { TIER1_PINNED_VERSION } from "../../shared/config/limits";
|
|
24
|
+
|
|
25
|
+
/** Network namespace label recorded with each Tier 1 run. */
|
|
26
|
+
export const TIER1_NETWORK_NAMESPACE = "facet-tier1-egress-isolated";
|
|
27
|
+
|
|
28
|
+
/** Lifetime of the ephemeral browser user-data directory (mode 0700). */
|
|
29
|
+
export const TIER1_USER_DATA_DIR_MODE = 0o700;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Per-render timeout the verifier waits for the trusted
|
|
33
|
+
* `render-complete` barrier before classifying the run as `timeout`.
|
|
34
|
+
* Sized for the real mermaid runtime: first-render initialization of
|
|
35
|
+
* the bundled runtime plus a 40-node layout must fit inside.
|
|
36
|
+
*/
|
|
37
|
+
export const TIER1_RENDER_BARRIER_MS = 30_000;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Time between the first interactive TSX observation and its bounded
|
|
41
|
+
* stability re-check. It stays below the headroom after the render barrier so
|
|
42
|
+
* a typed `partial:unstable` verdict remains observable before Tier 1 times out.
|
|
43
|
+
*/
|
|
44
|
+
export const TSX_STABILITY_WINDOW_MS = 1_000;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Ceiling for any single CDP call. Every protocol round-trip in a
|
|
48
|
+
* healthy session completes in well under a second; a call that
|
|
49
|
+
* outlives this budget means the pipe transport silently wedged
|
|
50
|
+
* (observed: a browser spawned immediately after another browser's
|
|
51
|
+
* teardown can have its devtools pipe torn down while the process
|
|
52
|
+
* stays alive — every subsequent call pends forever). The runner
|
|
53
|
+
* treats the typed rejection as retryable and relaunches once.
|
|
54
|
+
*/
|
|
55
|
+
export const TIER1_CDP_CALL_WATCHDOG_MS = 10_000;
|
|
56
|
+
|
|
57
|
+
/** Screenshot evidence has a tighter deadline than the verifier transport watchdog. */
|
|
58
|
+
export const TIER1_SCREENSHOT_CAPTURE_TIMEOUT_MS = 2_000;
|
|
59
|
+
export const TIER1_SCREENSHOT_CAPTURE_ATTEMPTS = 2;
|
|
60
|
+
|
|
61
|
+
/** Deterministic viewport used for Tier 1 layout and screenshot evidence. */
|
|
62
|
+
export const TIER1_VIEWPORT_WIDTH = 1280;
|
|
63
|
+
export const TIER1_VIEWPORT_HEIGHT = 800;
|
|
64
|
+
|
|
65
|
+
/** Maximum CSS axis length for a whole-artifact evidence image. */
|
|
66
|
+
export const TIER1_SCREENSHOT_MAX_AXIS_PX = 4096;
|
|
67
|
+
|
|
68
|
+
/** Maximum decoded pixel count for a whole-artifact evidence image. */
|
|
69
|
+
export const TIER1_SCREENSHOT_MAX_PIXELS = 8_388_608;
|
|
70
|
+
|
|
71
|
+
/** Static evidence uses WebP at this quality. */
|
|
72
|
+
export const TIER1_SCREENSHOT_WEBP_QUALITY = 82;
|
|
73
|
+
|
|
74
|
+
/** Animated evidence samples four frames at fixed browser-time intervals. */
|
|
75
|
+
export const TIER1_ANIMATION_FRAME_COUNT = 4;
|
|
76
|
+
export const TIER1_ANIMATION_FRAME_INTERVAL_MS = 150;
|
|
77
|
+
export const TIER1_ANIMATION_WEBP_QUALITIES = [TIER1_SCREENSHOT_WEBP_QUALITY, 70, 55] as const;
|
|
78
|
+
|
|
79
|
+
/** Whole-artifact evidence above this cap after scaling and quality fallback is screenshot_unavailable. */
|
|
80
|
+
export const TIER1_SCREENSHOT_CAP_BYTES = 8 * 1024 * 1024;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-frame nonce for the Tier 1 verification harness.
|
|
3
|
+
*
|
|
4
|
+
* Cryptographically random; the harness pins it into the CSP meta and
|
|
5
|
+
* the <script nonce> for the same srcdoc so only that inlined bundle
|
|
6
|
+
* executes. A regression that reuses a nonce across builds would let a
|
|
7
|
+
* stale bundle survive a CSP bypass attempt.
|
|
8
|
+
*
|
|
9
|
+
* Kept separate from gallery-web so the gallery frame bundle cannot
|
|
10
|
+
* accidentally pull in any Tier 1 import.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export function freshHarnessNonce(): string {
|
|
14
|
+
const bytes = new Uint8Array(16);
|
|
15
|
+
crypto.getRandomValues(bytes);
|
|
16
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
17
|
+
}
|