okengine 0.1.4
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/AGENTS.md +91 -0
- package/README.md +327 -0
- package/docs/spec/complete-example.md +1188 -0
- package/docs/spec/console.md +739 -0
- package/docs/spec/four-applications.md +1188 -0
- package/docs/spec/unified-theory.md +489 -0
- package/package.json +152 -0
- package/spec/manifest.v1.schema.json +624 -0
- package/src/auth/api-keys.ts +168 -0
- package/src/auth/attenuation.ts +116 -0
- package/src/auth/auth.test.ts +273 -0
- package/src/auth/cross-plane.ts +98 -0
- package/src/auth/index.ts +112 -0
- package/src/auth/invites.ts +66 -0
- package/src/auth/invoke-as.ts +49 -0
- package/src/auth/operator.ts +178 -0
- package/src/auth/planes.ts +102 -0
- package/src/auth/plugin.ts +44 -0
- package/src/auth/roles.ts +84 -0
- package/src/auth/sessions.ts +418 -0
- package/src/auth/tables.ts +131 -0
- package/src/cli/args.ts +50 -0
- package/src/cli/branch.ts +75 -0
- package/src/cli/build.ts +86 -0
- package/src/cli/client-add.test.ts +140 -0
- package/src/cli/client-add.ts +164 -0
- package/src/cli/completion.test.ts +58 -0
- package/src/cli/completion.ts +296 -0
- package/src/cli/dev-app-runner.ts +77 -0
- package/src/cli/dev.test.ts +435 -0
- package/src/cli/dev.ts +594 -0
- package/src/cli/doc-drift.test.ts +146 -0
- package/src/cli/doc-drift.ts +397 -0
- package/src/cli/docker-cli.test.ts +221 -0
- package/src/cli/docker.ts +127 -0
- package/src/cli/doctor-diff-examples.ts +92 -0
- package/src/cli/doctor-diff.test.ts +91 -0
- package/src/cli/doctor-diff.ts +274 -0
- package/src/cli/doctor-pii.test.ts +98 -0
- package/src/cli/doctor-pii.ts +151 -0
- package/src/cli/doctor.test.ts +150 -0
- package/src/cli/doctor.ts +223 -0
- package/src/cli/eval.ts +122 -0
- package/src/cli/exit.ts +19 -0
- package/src/cli/gates-list.test.ts +47 -0
- package/src/cli/gates-list.ts +84 -0
- package/src/cli/images.ts +277 -0
- package/src/cli/index.ts +99 -0
- package/src/cli/json-out.test.ts +129 -0
- package/src/cli/load-config.ts +71 -0
- package/src/cli/mcp-from-console.ts +50 -0
- package/src/cli/privacy-erase.ts +73 -0
- package/src/cli/registry.ts +455 -0
- package/src/cli/safe-defaults.test.ts +92 -0
- package/src/cli/schema.ts +196 -0
- package/src/cli/stack.ts +82 -0
- package/src/cli/start.ts +106 -0
- package/src/cli/upgrade.ts +105 -0
- package/src/cli/vault-cmd.ts +140 -0
- package/src/client/budget-entry.ts +34 -0
- package/src/client/budget.test.ts +17 -0
- package/src/client/create.test.ts +159 -0
- package/src/client/create.ts +152 -0
- package/src/client/errors.ts +60 -0
- package/src/client/index.ts +35 -0
- package/src/client/notes-contract.test.ts +289 -0
- package/src/client/transport.test.ts +123 -0
- package/src/client/transport.ts +254 -0
- package/src/client/types.ts +282 -0
- package/src/compiler/aot.test.ts +248 -0
- package/src/compiler/aot.ts +182 -0
- package/src/compiler/differential.test.ts +241 -0
- package/src/compiler/dynamic.ts +54 -0
- package/src/compiler/effects-infer.ts +541 -0
- package/src/compiler/emit.ts +40 -0
- package/src/compiler/extract.test.ts +177 -0
- package/src/compiler/extract.ts +1368 -0
- package/src/compiler/fixtures/raw/raw-unannotated.ts +28 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +24 -0
- package/src/compiler/fixtures/skyport/src/ai.ts +15 -0
- package/src/compiler/fixtures/skyport/src/app.ts +10 -0
- package/src/compiler/fixtures/skyport/src/channels.ts +6 -0
- package/src/compiler/fixtures/skyport/src/core.ts +3 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +57 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +11 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +14 -0
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +20 -0
- package/src/compiler/fixtures/skyport/src/gates.ts +14 -0
- package/src/compiler/fixtures/skyport/src/journeys.ts +9 -0
- package/src/compiler/fixtures/skyport/src/schema.ts +3 -0
- package/src/compiler/fixtures/skyport/src/vault.ts +6 -0
- package/src/compiler/fixtures/skyport.expected.json +241 -0
- package/src/compiler/fixtures/triggers/five-triggers.ts +69 -0
- package/src/compiler/http-parse.ts +214 -0
- package/src/compiler/index.ts +67 -0
- package/src/compiler/response.ts +89 -0
- package/src/compiler/sucrose.ts +238 -0
- package/src/config/index.ts +167 -0
- package/src/console/budget.test.ts +73 -0
- package/src/console/index.ts +30 -0
- package/src/console/server/access.test.ts +370 -0
- package/src/console/server/access.ts +777 -0
- package/src/console/server/ai.test.ts +172 -0
- package/src/console/server/ai.ts +632 -0
- package/src/console/server/app.ts +294 -0
- package/src/console/server/auth-rate.test.ts +81 -0
- package/src/console/server/auth-rate.ts +72 -0
- package/src/console/server/bind.ts +25 -0
- package/src/console/server/channels.test.ts +179 -0
- package/src/console/server/channels.ts +469 -0
- package/src/console/server/claim.test.ts +66 -0
- package/src/console/server/claim.ts +134 -0
- package/src/console/server/clock.test.ts +239 -0
- package/src/console/server/clock.ts +390 -0
- package/src/console/server/console.test.ts +338 -0
- package/src/console/server/diff.test.ts +299 -0
- package/src/console/server/diff.ts +352 -0
- package/src/console/server/dry-run.audit.test.ts +118 -0
- package/src/console/server/flows-invoke.test.ts +148 -0
- package/src/console/server/flows.ts +3633 -0
- package/src/console/server/gates.test.ts +360 -0
- package/src/console/server/gates.ts +869 -0
- package/src/console/server/index.ts +141 -0
- package/src/console/server/live.test.ts +30 -0
- package/src/console/server/live.ts +109 -0
- package/src/console/server/plugin.ts +50 -0
- package/src/console/server/plugins.test.ts +131 -0
- package/src/console/server/plugins.ts +429 -0
- package/src/console/server/runs-pii.ts +98 -0
- package/src/console/server/security-headers.ts +97 -0
- package/src/console/server/security.gate.test.ts +286 -0
- package/src/console/server/serve.ts +319 -0
- package/src/console/server/signals.test.ts +88 -0
- package/src/console/server/signals.ts +282 -0
- package/src/console/server/state.ts +982 -0
- package/src/console/server/store.test.ts +233 -0
- package/src/console/server/store.ts +861 -0
- package/src/console/server/structural.ts +95 -0
- package/src/console/server/vault.test.ts +251 -0
- package/src/console/server/vault.ts +430 -0
- package/src/console/ui/access/AccessA11yView.tsx +237 -0
- package/src/console/ui/access/a11y.test.tsx +122 -0
- package/src/console/ui/access/acknowledgement.test.ts +13 -0
- package/src/console/ui/access/acknowledgement.ts +35 -0
- package/src/console/ui/access/blast-radius.test.ts +26 -0
- package/src/console/ui/access/blast-radius.ts +40 -0
- package/src/console/ui/access/confirmation.test.ts +38 -0
- package/src/console/ui/access/confirmation.ts +36 -0
- package/src/console/ui/access/fixture.ts +203 -0
- package/src/console/ui/access/grantable.test.ts +17 -0
- package/src/console/ui/access/grantable.ts +36 -0
- package/src/console/ui/access/hygiene.test.ts +12 -0
- package/src/console/ui/access/hygiene.ts +39 -0
- package/src/console/ui/access/index.ts +54 -0
- package/src/console/ui/access/provenance.test.ts +12 -0
- package/src/console/ui/access/provenance.ts +29 -0
- package/src/console/ui/access/search.test.ts +17 -0
- package/src/console/ui/access/search.ts +72 -0
- package/src/console/ui/access/types.ts +111 -0
- package/src/console/ui/ai/AiA11yView.tsx +265 -0
- package/src/console/ui/ai/a11y.test.tsx +99 -0
- package/src/console/ui/ai/findings.ts +41 -0
- package/src/console/ui/ai/fixture.ts +182 -0
- package/src/console/ui/ai/format.ts +90 -0
- package/src/console/ui/ai/group.test.ts +37 -0
- package/src/console/ui/ai/group.ts +88 -0
- package/src/console/ui/ai/index.ts +66 -0
- package/src/console/ui/ai/promotion.test.ts +77 -0
- package/src/console/ui/ai/promotion.ts +150 -0
- package/src/console/ui/ai/search.test.ts +40 -0
- package/src/console/ui/ai/search.ts +89 -0
- package/src/console/ui/ai/types.ts +139 -0
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +163 -0
- package/src/console/ui/architecture/a11y.test.tsx +97 -0
- package/src/console/ui/architecture/boundary.ts +42 -0
- package/src/console/ui/architecture/declared.ts +165 -0
- package/src/console/ui/architecture/fixture.ts +74 -0
- package/src/console/ui/architecture/index.ts +84 -0
- package/src/console/ui/architecture/layers.ts +64 -0
- package/src/console/ui/architecture/layout.ts +68 -0
- package/src/console/ui/architecture/pathologies.ts +260 -0
- package/src/console/ui/architecture/search.ts +145 -0
- package/src/console/ui/architecture/traffic.ts +102 -0
- package/src/console/ui/architecture/types.ts +144 -0
- package/src/console/ui/architecture/view.test.ts +165 -0
- package/src/console/ui/architecture/view.ts +516 -0
- package/src/console/ui/channels/ChannelsA11yView.tsx +180 -0
- package/src/console/ui/channels/a11y.test.tsx +96 -0
- package/src/console/ui/channels/confirmation.test.ts +37 -0
- package/src/console/ui/channels/confirmation.ts +27 -0
- package/src/console/ui/channels/fallback.test.ts +15 -0
- package/src/console/ui/channels/fallback.ts +20 -0
- package/src/console/ui/channels/findings.ts +38 -0
- package/src/console/ui/channels/fixture.ts +134 -0
- package/src/console/ui/channels/group.ts +25 -0
- package/src/console/ui/channels/index.ts +57 -0
- package/src/console/ui/channels/locale.test.ts +17 -0
- package/src/console/ui/channels/locale.ts +31 -0
- package/src/console/ui/channels/mask.test.ts +14 -0
- package/src/console/ui/channels/mask.ts +12 -0
- package/src/console/ui/channels/search.ts +58 -0
- package/src/console/ui/channels/taxonomy.test.ts +17 -0
- package/src/console/ui/channels/taxonomy.ts +49 -0
- package/src/console/ui/channels/types.ts +117 -0
- package/src/console/ui/clock/ClockA11yView.tsx +182 -0
- package/src/console/ui/clock/a11y.test.tsx +99 -0
- package/src/console/ui/clock/confirmation.test.ts +32 -0
- package/src/console/ui/clock/confirmation.ts +34 -0
- package/src/console/ui/clock/findings.ts +40 -0
- package/src/console/ui/clock/fixture.ts +111 -0
- package/src/console/ui/clock/health.test.ts +25 -0
- package/src/console/ui/clock/health.ts +61 -0
- package/src/console/ui/clock/index.ts +49 -0
- package/src/console/ui/clock/search.test.ts +39 -0
- package/src/console/ui/clock/search.ts +76 -0
- package/src/console/ui/clock/timeline.test.ts +44 -0
- package/src/console/ui/clock/timeline.ts +41 -0
- package/src/console/ui/clock/types.ts +78 -0
- package/src/console/ui/clock/waiting-on.test.ts +34 -0
- package/src/console/ui/clock/waiting-on.ts +83 -0
- package/src/console/ui/confirmation.unify.test.ts +92 -0
- package/src/console/ui/diff/DiffA11yView.tsx +85 -0
- package/src/console/ui/diff/a11y.test.tsx +99 -0
- package/src/console/ui/diff/fixture.ts +85 -0
- package/src/console/ui/diff/group.test.ts +58 -0
- package/src/console/ui/diff/group.ts +97 -0
- package/src/console/ui/diff/index.ts +38 -0
- package/src/console/ui/diff/search.test.ts +32 -0
- package/src/console/ui/diff/search.ts +75 -0
- package/src/console/ui/diff/summary.ts +74 -0
- package/src/console/ui/diff/types.ts +48 -0
- package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
- package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
- package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
- package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
- package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
- package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
- package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
- package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
- package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
- package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
- package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
- package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
- package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
- package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
- package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
- package/src/console/ui/dist/index.html +14 -0
- package/src/console/ui/flows/FlowsA11yView.tsx +155 -0
- package/src/console/ui/flows/a11y.test.tsx +105 -0
- package/src/console/ui/flows/buffer.test.ts +91 -0
- package/src/console/ui/flows/buffer.ts +118 -0
- package/src/console/ui/flows/confirmation.ts +131 -0
- package/src/console/ui/flows/contract.test.ts +89 -0
- package/src/console/ui/flows/contract.ts +353 -0
- package/src/console/ui/flows/fixture.ts +81 -0
- package/src/console/ui/flows/graph.test.ts +101 -0
- package/src/console/ui/flows/graph.ts +549 -0
- package/src/console/ui/flows/index.ts +87 -0
- package/src/console/ui/flows/save-as-test.test.ts +47 -0
- package/src/console/ui/flows/save-as-test.ts +99 -0
- package/src/console/ui/flows/search.ts +255 -0
- package/src/console/ui/flows/tiers.ts +140 -0
- package/src/console/ui/gates/GatesA11yView.tsx +192 -0
- package/src/console/ui/gates/a11y.test.tsx +116 -0
- package/src/console/ui/gates/audit.test.ts +40 -0
- package/src/console/ui/gates/audit.ts +63 -0
- package/src/console/ui/gates/denial.test.ts +36 -0
- package/src/console/ui/gates/denial.ts +51 -0
- package/src/console/ui/gates/findings.ts +19 -0
- package/src/console/ui/gates/fixture.ts +189 -0
- package/src/console/ui/gates/group.test.ts +30 -0
- package/src/console/ui/gates/group.ts +102 -0
- package/src/console/ui/gates/index.ts +47 -0
- package/src/console/ui/gates/search.test.ts +49 -0
- package/src/console/ui/gates/search.ts +115 -0
- package/src/console/ui/gates/types.ts +121 -0
- package/src/console/ui/overview/OverviewA11yView.tsx +152 -0
- package/src/console/ui/overview/a11y.test.tsx +113 -0
- package/src/console/ui/overview/busiest.ts +26 -0
- package/src/console/ui/overview/compose.ts +133 -0
- package/src/console/ui/overview/cost.test.ts +39 -0
- package/src/console/ui/overview/cost.ts +138 -0
- package/src/console/ui/overview/fixture.ts +161 -0
- package/src/console/ui/overview/golden.ts +57 -0
- package/src/console/ui/overview/index.ts +59 -0
- package/src/console/ui/overview/rank.test.ts +136 -0
- package/src/console/ui/overview/rank.ts +210 -0
- package/src/console/ui/overview/slo.test.ts +73 -0
- package/src/console/ui/overview/slo.ts +229 -0
- package/src/console/ui/overview/types.ts +121 -0
- package/src/console/ui/overview/verdict.ts +131 -0
- package/src/console/ui/plugins/PluginsA11yView.tsx +227 -0
- package/src/console/ui/plugins/a11y.test.tsx +97 -0
- package/src/console/ui/plugins/command.test.ts +22 -0
- package/src/console/ui/plugins/command.ts +27 -0
- package/src/console/ui/plugins/findings.ts +39 -0
- package/src/console/ui/plugins/fixture.ts +233 -0
- package/src/console/ui/plugins/group.test.ts +38 -0
- package/src/console/ui/plugins/group.ts +67 -0
- package/src/console/ui/plugins/index.ts +40 -0
- package/src/console/ui/plugins/search.ts +67 -0
- package/src/console/ui/plugins/types.ts +115 -0
- package/src/console/ui/runs/RunsA11yView.tsx +267 -0
- package/src/console/ui/runs/a11y.test.tsx +99 -0
- package/src/console/ui/runs/explain.test.ts +23 -0
- package/src/console/ui/runs/explain.ts +32 -0
- package/src/console/ui/runs/fixture.ts +189 -0
- package/src/console/ui/runs/group.test.ts +24 -0
- package/src/console/ui/runs/group.ts +85 -0
- package/src/console/ui/runs/histogram.test.ts +31 -0
- package/src/console/ui/runs/histogram.ts +85 -0
- package/src/console/ui/runs/index.ts +75 -0
- package/src/console/ui/runs/project.ts +122 -0
- package/src/console/ui/runs/query.test.ts +50 -0
- package/src/console/ui/runs/query.ts +283 -0
- package/src/console/ui/runs/search.test.ts +45 -0
- package/src/console/ui/runs/search.ts +141 -0
- package/src/console/ui/runs/trace-link.test.ts +30 -0
- package/src/console/ui/runs/trace-link.ts +99 -0
- package/src/console/ui/runs/types.ts +137 -0
- package/src/console/ui/shell/App.tsx +56 -0
- package/src/console/ui/shell/client.ts +1715 -0
- package/src/console/ui/shell/components/ui.tsx +110 -0
- package/src/console/ui/shell/index.html +12 -0
- package/src/console/ui/shell/layout/Shell.tsx +95 -0
- package/src/console/ui/shell/main.tsx +281 -0
- package/src/console/ui/shell/panels/Access.tsx +5 -0
- package/src/console/ui/shell/panels/Ai.tsx +5 -0
- package/src/console/ui/shell/panels/Architecture.tsx +5 -0
- package/src/console/ui/shell/panels/Channels.tsx +5 -0
- package/src/console/ui/shell/panels/Clock.tsx +5 -0
- package/src/console/ui/shell/panels/Diff.tsx +5 -0
- package/src/console/ui/shell/panels/Flows.tsx +5 -0
- package/src/console/ui/shell/panels/Gates.tsx +5 -0
- package/src/console/ui/shell/panels/Overview.tsx +6 -0
- package/src/console/ui/shell/panels/Plugins.tsx +5 -0
- package/src/console/ui/shell/panels/Runs.tsx +5 -0
- package/src/console/ui/shell/panels/Signals.tsx +5 -0
- package/src/console/ui/shell/panels/Store.tsx +5 -0
- package/src/console/ui/shell/panels/Traces.tsx +5 -0
- package/src/console/ui/shell/panels/Vault.tsx +5 -0
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +813 -0
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +563 -0
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +377 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +454 -0
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +479 -0
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +275 -0
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +301 -0
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +350 -0
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +502 -0
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +482 -0
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +428 -0
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +413 -0
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +649 -0
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +840 -0
- package/src/console/ui/shell/panels/store/StorePanel.tsx +787 -0
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +539 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +449 -0
- package/src/console/ui/shell/plugin/Sandbox.tsx +29 -0
- package/src/console/ui/shell/setup/Wizard.tsx +189 -0
- package/src/console/ui/shell/styles.css +75 -0
- package/src/console/ui/signals/SignalsA11yView.tsx +276 -0
- package/src/console/ui/signals/a11y.test.tsx +99 -0
- package/src/console/ui/signals/confirmation.test.ts +39 -0
- package/src/console/ui/signals/confirmation.ts +63 -0
- package/src/console/ui/signals/dry-run.test.ts +18 -0
- package/src/console/ui/signals/dry-run.ts +36 -0
- package/src/console/ui/signals/durable.test.ts +22 -0
- package/src/console/ui/signals/durable.ts +41 -0
- package/src/console/ui/signals/findings.ts +33 -0
- package/src/console/ui/signals/fixture.ts +242 -0
- package/src/console/ui/signals/group.test.ts +32 -0
- package/src/console/ui/signals/group.ts +49 -0
- package/src/console/ui/signals/index.ts +52 -0
- package/src/console/ui/signals/monitor.test.ts +30 -0
- package/src/console/ui/signals/monitor.ts +79 -0
- package/src/console/ui/signals/schema-form.test.ts +34 -0
- package/src/console/ui/signals/schema-form.ts +112 -0
- package/src/console/ui/signals/search.ts +102 -0
- package/src/console/ui/signals/types.ts +71 -0
- package/src/console/ui/store/StoreA11yView.tsx +199 -0
- package/src/console/ui/store/a11y.test.tsx +99 -0
- package/src/console/ui/store/cache-view.test.ts +13 -0
- package/src/console/ui/store/cache-view.ts +36 -0
- package/src/console/ui/store/confirmation.test.ts +38 -0
- package/src/console/ui/store/confirmation.ts +55 -0
- package/src/console/ui/store/dry-run.test.ts +15 -0
- package/src/console/ui/store/dry-run.ts +29 -0
- package/src/console/ui/store/fixture.ts +163 -0
- package/src/console/ui/store/group.test.ts +21 -0
- package/src/console/ui/store/group.ts +41 -0
- package/src/console/ui/store/index.ts +40 -0
- package/src/console/ui/store/search.ts +78 -0
- package/src/console/ui/store/types.ts +65 -0
- package/src/console/ui/store/will-not-fire.test.ts +19 -0
- package/src/console/ui/store/will-not-fire.ts +40 -0
- package/src/console/ui/traces/TracesA11yView.tsx +200 -0
- package/src/console/ui/traces/a11y.test.tsx +97 -0
- package/src/console/ui/traces/chain.test.ts +61 -0
- package/src/console/ui/traces/chain.ts +144 -0
- package/src/console/ui/traces/critical-path.test.ts +24 -0
- package/src/console/ui/traces/critical-path.ts +59 -0
- package/src/console/ui/traces/filter.test.ts +78 -0
- package/src/console/ui/traces/filter.ts +112 -0
- package/src/console/ui/traces/fixture.ts +126 -0
- package/src/console/ui/traces/fold.test.ts +67 -0
- package/src/console/ui/traces/fold.ts +170 -0
- package/src/console/ui/traces/index.ts +64 -0
- package/src/console/ui/traces/mini.ts +65 -0
- package/src/console/ui/traces/replay.test.ts +24 -0
- package/src/console/ui/traces/replay.ts +35 -0
- package/src/console/ui/traces/sampling.test.ts +26 -0
- package/src/console/ui/traces/sampling.ts +66 -0
- package/src/console/ui/traces/search.ts +148 -0
- package/src/console/ui/traces/tier.ts +72 -0
- package/src/console/ui/traces/types.ts +125 -0
- package/src/console/ui/tsconfig.json +20 -0
- package/src/console/ui/vault/VaultA11yView.tsx +177 -0
- package/src/console/ui/vault/a11y.test.tsx +97 -0
- package/src/console/ui/vault/blast-radius.test.ts +35 -0
- package/src/console/ui/vault/blast-radius.ts +55 -0
- package/src/console/ui/vault/confirmation.test.ts +41 -0
- package/src/console/ui/vault/confirmation.ts +39 -0
- package/src/console/ui/vault/dormant.test.ts +37 -0
- package/src/console/ui/vault/dormant.ts +28 -0
- package/src/console/ui/vault/export-safe.test.ts +30 -0
- package/src/console/ui/vault/export-safe.ts +65 -0
- package/src/console/ui/vault/fixture.ts +75 -0
- package/src/console/ui/vault/group.test.ts +25 -0
- package/src/console/ui/vault/group.ts +50 -0
- package/src/console/ui/vault/index.ts +45 -0
- package/src/console/ui/vault/search.ts +62 -0
- package/src/console/ui/vault/types.ts +57 -0
- package/src/console/ui/vite.config.ts +84 -0
- package/src/console/xss.gate.test.ts +118 -0
- package/src/docker/compose.ts +226 -0
- package/src/docker/credentials.ts +25 -0
- package/src/docker/derive.ts +83 -0
- package/src/docker/docker.test.ts +185 -0
- package/src/docker/dockerfile.integration.test.ts +170 -0
- package/src/docker/dockerfile.ts +39 -0
- package/src/docker/helpers.ts +103 -0
- package/src/docker/index.ts +52 -0
- package/src/docker/pin.ts +90 -0
- package/src/docker/recipes/index.ts +33 -0
- package/src/docker/recipes/postgres.ts +18 -0
- package/src/docker/recipes/redis.ts +18 -0
- package/src/docker/stack.integration.test.ts +140 -0
- package/src/docker/stack.ts +62 -0
- package/src/docker/types.ts +134 -0
- package/src/drivers/ai-anthropic.ts +128 -0
- package/src/drivers/ai-mock.ts +82 -0
- package/src/drivers/ai-openai-compatible.ts +139 -0
- package/src/drivers/ai-providers.test.ts +163 -0
- package/src/drivers/ai-types.ts +83 -0
- package/src/drivers/bun-native-completeness.test.ts +155 -0
- package/src/drivers/channel-console.ts +118 -0
- package/src/drivers/channel-fcm.ts +96 -0
- package/src/drivers/channel-resend.ts +27 -0
- package/src/drivers/channel-smtp.ts +33 -0
- package/src/drivers/channel-types.ts +160 -0
- package/src/drivers/channel-unifonic.ts +79 -0
- package/src/drivers/channel-wa-cloud.ts +84 -0
- package/src/drivers/channel-webpush.ts +317 -0
- package/src/drivers/conformance.test.ts +89 -0
- package/src/drivers/conformance.ts +162 -0
- package/src/drivers/fs.ts +73 -0
- package/src/drivers/index.ts +194 -0
- package/src/drivers/kv-lua.ts +222 -0
- package/src/drivers/memory.ts +472 -0
- package/src/drivers/pgvector.ts +130 -0
- package/src/drivers/postgres.ts +221 -0
- package/src/drivers/redis.ts +254 -0
- package/src/drivers/s3.ts +129 -0
- package/src/drivers/signal-engine.ts +690 -0
- package/src/drivers/signal-memory.ts +27 -0
- package/src/drivers/signal-nats.ts +131 -0
- package/src/drivers/signal-postgres.ts +1089 -0
- package/src/drivers/signal-redis.ts +275 -0
- package/src/drivers/signal-types.ts +328 -0
- package/src/drivers/sqlite.ts +53 -0
- package/src/drivers/types.ts +334 -0
- package/src/drivers/vault-dotenv-parse.ts +56 -0
- package/src/drivers/vault-env.ts +101 -0
- package/src/drivers/vault-infisical.ts +61 -0
- package/src/drivers/vault-managed.ts +42 -0
- package/src/drivers/vault-memory.ts +38 -0
- package/src/drivers/vault-openbao.ts +80 -0
- package/src/drivers/vault-sops.ts +252 -0
- package/src/drivers/vault-types.ts +84 -0
- package/src/elements/ai/declare.ts +206 -0
- package/src/elements/ai/eval.ts +113 -0
- package/src/elements/ai/pii.ts +108 -0
- package/src/elements/ai/runtime.ts +540 -0
- package/src/elements/ai/schema.ts +194 -0
- package/src/elements/ai.test.ts +393 -0
- package/src/elements/ai.ts +56 -0
- package/src/elements/channel/consent.ts +81 -0
- package/src/elements/channel/costs.test.ts +62 -0
- package/src/elements/channel/costs.ts +130 -0
- package/src/elements/channel/declare.ts +141 -0
- package/src/elements/channel/email-auth.test.ts +47 -0
- package/src/elements/channel/email-auth.ts +109 -0
- package/src/elements/channel/locale.ts +97 -0
- package/src/elements/channel/mask.test.ts +19 -0
- package/src/elements/channel/mask.ts +60 -0
- package/src/elements/channel/mime.ts +23 -0
- package/src/elements/channel/outcomes.test.ts +40 -0
- package/src/elements/channel/outcomes.ts +126 -0
- package/src/elements/channel/receipts.ts +121 -0
- package/src/elements/channel/runtime.ts +500 -0
- package/src/elements/channel/suppression.ts +136 -0
- package/src/elements/channel.test.ts +232 -0
- package/src/elements/channel.ts +109 -0
- package/src/elements/clock/actions.ts +170 -0
- package/src/elements/clock/declare.ts +58 -0
- package/src/elements/clock/dst.ts +197 -0
- package/src/elements/clock/durable.ts +140 -0
- package/src/elements/clock/duration.ts +29 -0
- package/src/elements/clock/health.test.ts +103 -0
- package/src/elements/clock/health.ts +112 -0
- package/src/elements/clock/leader.ts +127 -0
- package/src/elements/clock/reconcile.ts +241 -0
- package/src/elements/clock/runtime.ts +243 -0
- package/src/elements/clock/schedule.ts +262 -0
- package/src/elements/clock/time-travel.ts +53 -0
- package/src/elements/clock.test.ts +365 -0
- package/src/elements/clock.ts +80 -0
- package/src/elements/gate/declare.ts +134 -0
- package/src/elements/gate/permissions.ts +104 -0
- package/src/elements/gate/runtime.ts +177 -0
- package/src/elements/gate/strategies.ts +388 -0
- package/src/elements/gate.test.ts +145 -0
- package/src/elements/gate.ts +41 -0
- package/src/elements/index.ts +169 -0
- package/src/elements/signal/chaos-child.ts +47 -0
- package/src/elements/signal/declare.ts +77 -0
- package/src/elements/signal/dry-run-replay.test.ts +95 -0
- package/src/elements/signal/dry-run-write-isolation.test.ts +94 -0
- package/src/elements/signal/reconcile.test.ts +23 -0
- package/src/elements/signal/reconcile.ts +122 -0
- package/src/elements/signal/runtime.ts +94 -0
- package/src/elements/signal.test.ts +457 -0
- package/src/elements/signal.ts +26 -0
- package/src/elements/store/cache.ts +197 -0
- package/src/elements/store/classify.ts +125 -0
- package/src/elements/store/declare.ts +200 -0
- package/src/elements/store/files-policy.test.ts +31 -0
- package/src/elements/store/files-policy.ts +70 -0
- package/src/elements/store/replica.ts +68 -0
- package/src/elements/store/runtime.ts +393 -0
- package/src/elements/store/sql-condition.ts +205 -0
- package/src/elements/store/sql-session.ts +579 -0
- package/src/elements/store/table.ts +282 -0
- package/src/elements/store.test.ts +370 -0
- package/src/elements/store.ts +99 -0
- package/src/elements/vault/declare.ts +143 -0
- package/src/elements/vault/fingerprint.ts +30 -0
- package/src/elements/vault/redact.ts +52 -0
- package/src/elements/vault/runtime.ts +461 -0
- package/src/elements/vault.test.ts +296 -0
- package/src/elements/vault.ts +44 -0
- package/src/index.ts +183 -0
- package/src/kernel/adopt-routes.ts +194 -0
- package/src/kernel/app.ts +1068 -0
- package/src/kernel/auth-resolve.ts +93 -0
- package/src/kernel/boot.test.ts +173 -0
- package/src/kernel/boot.ts +439 -0
- package/src/kernel/budget-entry.ts +55 -0
- package/src/kernel/budget.test.ts +19 -0
- package/src/kernel/call.test.ts +83 -0
- package/src/kernel/capability.ts +113 -0
- package/src/kernel/dry-run.test.ts +64 -0
- package/src/kernel/dry-run.ts +171 -0
- package/src/kernel/effects.test.ts +89 -0
- package/src/kernel/effects.ts +140 -0
- package/src/kernel/errors.registry.test.ts +39 -0
- package/src/kernel/errors.ts +225 -0
- package/src/kernel/flow.test.ts +154 -0
- package/src/kernel/flow.ts +290 -0
- package/src/kernel/fx.test.ts +245 -0
- package/src/kernel/fx.ts +996 -0
- package/src/kernel/hook-timing.test.ts +72 -0
- package/src/kernel/hook-timing.ts +190 -0
- package/src/kernel/hooks.test.ts +181 -0
- package/src/kernel/hooks.ts +275 -0
- package/src/kernel/index.ts +278 -0
- package/src/kernel/journal.ts +405 -0
- package/src/kernel/journey.ts +75 -0
- package/src/kernel/on.ts +74 -0
- package/src/kernel/pipeline.test.ts +349 -0
- package/src/kernel/pipeline.ts +245 -0
- package/src/kernel/plug.ts +82 -0
- package/src/kernel/plugin/capabilities.test.ts +84 -0
- package/src/kernel/plugin/conflicts.test.ts +106 -0
- package/src/kernel/plugin/decorate.test.ts +111 -0
- package/src/kernel/plugin/scoping.test.ts +138 -0
- package/src/kernel/plugin.ts +485 -0
- package/src/kernel/rate-limit.ts +25 -0
- package/src/kernel/registry.ts +398 -0
- package/src/kernel/router.test.ts +114 -0
- package/src/kernel/router.ts +556 -0
- package/src/kernel/routing-budget.test.ts +19 -0
- package/src/kernel/run-telemetry.ts +70 -0
- package/src/kernel/triggers.ts +324 -0
- package/src/kernel/unit.ts +88 -0
- package/src/manifest/diff.test.ts +866 -0
- package/src/manifest/diff.ts +1670 -0
- package/src/manifest/fixtures/base.manifest.json +112 -0
- package/src/manifest/fixtures/skyport.excerpt.json +51 -0
- package/src/manifest/fixtures/skyport.manifest.json +153 -0
- package/src/manifest/index.ts +27 -0
- package/src/manifest/types.ts +358 -0
- package/src/manifest/undeclared.test.ts +82 -0
- package/src/manifest/undeclared.ts +65 -0
- package/src/manifest/validate.test.ts +105 -0
- package/src/manifest/validate.ts +174 -0
- package/src/mcp/authorization.ts +340 -0
- package/src/mcp/confirmation.ts +174 -0
- package/src/mcp/data.ts +90 -0
- package/src/mcp/index.ts +80 -0
- package/src/mcp/injection.gate.test.ts +176 -0
- package/src/mcp/mcp.test.ts +348 -0
- package/src/mcp/protocol.ts +172 -0
- package/src/mcp/server.ts +283 -0
- package/src/mcp/session.ts +139 -0
- package/src/mcp/tools.ts +391 -0
- package/src/plugins/catalogue.test.ts +48 -0
- package/src/plugins/catalogue.ts +113 -0
- package/src/plugins/node-import-scan.test.ts +28 -0
- package/src/plugins/node-import-scan.ts +98 -0
- package/src/plugins/supply-chain.test.ts +48 -0
- package/src/plugins/supply-chain.ts +460 -0
- package/src/release/index.ts +30 -0
- package/src/release/limits.ts +20 -0
- package/src/release/measure.ts +443 -0
- package/src/release/publish.ts +56 -0
- package/src/runs/bench.test.ts +93 -0
- package/src/runs/collect.ts +144 -0
- package/src/runs/drivers/clickhouse.ts +108 -0
- package/src/runs/drivers/files.ts +201 -0
- package/src/runs/drivers/memory.ts +94 -0
- package/src/runs/drivers/postgres.ts +110 -0
- package/src/runs/duckdb.ts +99 -0
- package/src/runs/index.ts +82 -0
- package/src/runs/outlier.ts +180 -0
- package/src/runs/parquet.ts +210 -0
- package/src/runs/privacy.ts +49 -0
- package/src/runs/runs.test.ts +324 -0
- package/src/runs/runtime.ts +229 -0
- package/src/runs/shred.ts +243 -0
- package/src/runs/telemetry.ts +10 -0
- package/src/runs/types.ts +179 -0
- package/src/runtime/bun.ts +140 -0
- package/src/runtime/cold-start.bench.ts +20 -0
- package/src/runtime/index.ts +46 -0
- package/src/runtime/primitives.ts +242 -0
- package/src/runtime/security.test.ts +170 -0
- package/src/runtime/security.ts +168 -0
- package/src/runtime/serve.test.ts +223 -0
- package/src/runtime/types.ts +217 -0
- package/src/runtime/web-standard.ts +77 -0
- package/src/test/create-test-app.test.ts +161 -0
- package/src/test/create-test-app.ts +533 -0
- package/src/test/index.ts +21 -0
- package/src/test/provisions.integration.test.ts +174 -0
- package/src/upgrade/codemods.test.ts +58 -0
- package/src/upgrade/codemods.ts +83 -0
- package/src/validation/index.ts +23 -0
- package/src/validation/standard-schema.test.ts +96 -0
- package/src/validation/standard-schema.ts +316 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dormant secret detection (console §9.8 · §9.16).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { DORMANT_MS, dormantSecrets } from "./dormant.ts";
|
|
7
|
+
import { VAULT_FIXTURE } from "./fixture.ts";
|
|
8
|
+
import type { VaultRecord } from "./types.ts";
|
|
9
|
+
|
|
10
|
+
describe("dormantSecrets", () => {
|
|
11
|
+
test("flags never-read secrets and ignores config", () => {
|
|
12
|
+
const now = 1_700_000_000_000;
|
|
13
|
+
const neverRead: VaultRecord = {
|
|
14
|
+
...VAULT_FIXTURE[0]!,
|
|
15
|
+
name: "LEGACY_WEBHOOK_SECRET",
|
|
16
|
+
lastReadAt: null,
|
|
17
|
+
readers: [],
|
|
18
|
+
};
|
|
19
|
+
const dormant = dormantSecrets(
|
|
20
|
+
[...VAULT_FIXTURE, neverRead],
|
|
21
|
+
now,
|
|
22
|
+
);
|
|
23
|
+
expect(dormant.some((s) => s.name === "LEGACY_WEBHOOK_SECRET")).toBe(true);
|
|
24
|
+
expect(dormant.some((s) => s.kind === "config")).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("flags secrets unread for 90+ days", () => {
|
|
28
|
+
const now = 1_700_000_000_000;
|
|
29
|
+
const stale: VaultRecord = {
|
|
30
|
+
...VAULT_FIXTURE[0]!,
|
|
31
|
+
name: "OLD_KEY",
|
|
32
|
+
lastReadAt: now - DORMANT_MS - 1,
|
|
33
|
+
};
|
|
34
|
+
const dormant = dormantSecrets([stale], now);
|
|
35
|
+
expect(dormant.map((s) => s.name)).toEqual(["OLD_KEY"]);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dormant (dead) secrets — last-read timestamps expose attack surface
|
|
3
|
+
* that grows by forgetting (console §9.8 · §9.16).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { VaultRecord } from "./types.ts";
|
|
7
|
+
|
|
8
|
+
/** Secrets unread for this long (or never) are dormant. */
|
|
9
|
+
export const DORMANT_MS = 90 * 86_400_000;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Secrets with no recent read — never read, or idle ≥ 90 days.
|
|
13
|
+
*
|
|
14
|
+
* Config values are excluded; only secrets grow attack surface by neglect.
|
|
15
|
+
*
|
|
16
|
+
* @param secrets - Vault panel rows
|
|
17
|
+
* @param now - Clock (epoch ms)
|
|
18
|
+
*/
|
|
19
|
+
export function dormantSecrets(
|
|
20
|
+
secrets: readonly VaultRecord[],
|
|
21
|
+
now: number,
|
|
22
|
+
): readonly VaultRecord[] {
|
|
23
|
+
return secrets.filter((s) => {
|
|
24
|
+
if (s.kind !== "secret") return false;
|
|
25
|
+
if (s.lastReadAt == null) return true;
|
|
26
|
+
return now - s.lastReadAt >= DORMANT_MS;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
assertExportHasNoSecrets,
|
|
4
|
+
exportSafeList,
|
|
5
|
+
exportSafeRow,
|
|
6
|
+
} from "./export-safe.ts";
|
|
7
|
+
import { FIXTURE_SECRET_VALUE, VAULT_FIXTURE } from "./fixture.ts";
|
|
8
|
+
|
|
9
|
+
describe("exportSafe", () => {
|
|
10
|
+
test("never includes secret cleartext; config cleartext is allowed", () => {
|
|
11
|
+
const secret = VAULT_FIXTURE[0]!;
|
|
12
|
+
const config = VAULT_FIXTURE[1]!;
|
|
13
|
+
const leaked = {
|
|
14
|
+
...secret,
|
|
15
|
+
// Simulate a buggy row that somehow held cleartext — export must scrub.
|
|
16
|
+
cleartext: FIXTURE_SECRET_VALUE,
|
|
17
|
+
};
|
|
18
|
+
const safe = exportSafeRow(leaked);
|
|
19
|
+
expect(safe.cleartext).toBeNull();
|
|
20
|
+
expect(JSON.stringify(safe)).not.toContain(FIXTURE_SECRET_VALUE);
|
|
21
|
+
|
|
22
|
+
const configSafe = exportSafeRow(config);
|
|
23
|
+
expect(configSafe.cleartext).toBe("https://app.example.com");
|
|
24
|
+
|
|
25
|
+
const list = exportSafeList(VAULT_FIXTURE);
|
|
26
|
+
assertExportHasNoSecrets(list, [FIXTURE_SECRET_VALUE]);
|
|
27
|
+
expect(list).toContain("sha256:aaaaaaaaaaaaaaaa");
|
|
28
|
+
expect(list).toContain("https://app.example.com");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export / log safety — Vault panel payloads must never carry secret values.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { VaultRecord } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Project a row for export/clipboard — fingerprints only for secrets;
|
|
9
|
+
* config cleartext is allowed.
|
|
10
|
+
*
|
|
11
|
+
* @param row - Vault row
|
|
12
|
+
*/
|
|
13
|
+
export function exportSafeRow(row: VaultRecord): Record<string, unknown> {
|
|
14
|
+
return {
|
|
15
|
+
name: row.name,
|
|
16
|
+
kind: row.kind,
|
|
17
|
+
sensitive: row.sensitive,
|
|
18
|
+
description: row.description ?? null,
|
|
19
|
+
rotate: row.rotate ?? null,
|
|
20
|
+
fingerprints: { ...row.fingerprints },
|
|
21
|
+
fingerprint: row.fingerprint,
|
|
22
|
+
cleartext: row.sensitive ? null : row.cleartext,
|
|
23
|
+
winner: row.winner,
|
|
24
|
+
resolution: row.resolution.map((s) => ({ ...s })),
|
|
25
|
+
readers: [...row.readers],
|
|
26
|
+
blastRadius: {
|
|
27
|
+
count: row.blastRadius.count,
|
|
28
|
+
longestWakeAt: row.blastRadius.longestWakeAt,
|
|
29
|
+
longestOutstandingMs: row.blastRadius.longestOutstandingMs,
|
|
30
|
+
runIds: [...row.blastRadius.runIds],
|
|
31
|
+
},
|
|
32
|
+
lastReadAt: row.lastReadAt,
|
|
33
|
+
sharedFingerprintEnvs: [...row.sharedFingerprintEnvs],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Serialize list for export — never includes secret cleartext.
|
|
39
|
+
*
|
|
40
|
+
* @param rows - Vault rows
|
|
41
|
+
*/
|
|
42
|
+
export function exportSafeList(rows: readonly VaultRecord[]): string {
|
|
43
|
+
return JSON.stringify(
|
|
44
|
+
rows.map((r) => exportSafeRow(r)),
|
|
45
|
+
null,
|
|
46
|
+
2,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Assert a serialized payload does not contain known secret values.
|
|
52
|
+
*
|
|
53
|
+
* @param payload - Serialized text
|
|
54
|
+
* @param knownSecrets - Cleartext secrets that must not appear
|
|
55
|
+
*/
|
|
56
|
+
export function assertExportHasNoSecrets(
|
|
57
|
+
payload: string,
|
|
58
|
+
knownSecrets: readonly string[],
|
|
59
|
+
): void {
|
|
60
|
+
for (const secret of knownSecrets) {
|
|
61
|
+
if (secret.length > 0 && payload.includes(secret)) {
|
|
62
|
+
throw new Error("vault export leaked a secret value");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault panel fixture — fingerprints only; never real secret values.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { VaultListResponse, VaultRecord } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
/** Fixture secret used only for leak-detection tests (never in panel rows). */
|
|
8
|
+
export const FIXTURE_SECRET_VALUE = "sk_live_DO_NOT_LEAK_fixture";
|
|
9
|
+
|
|
10
|
+
/** One secret + one config for axe / unit tests. */
|
|
11
|
+
export const VAULT_FIXTURE: readonly VaultRecord[] = [
|
|
12
|
+
{
|
|
13
|
+
name: "STRIPE_KEY",
|
|
14
|
+
kind: "secret",
|
|
15
|
+
sensitive: true,
|
|
16
|
+
description: "Payments gateway key",
|
|
17
|
+
rotate: "90d",
|
|
18
|
+
fingerprints: {
|
|
19
|
+
dev: "sha256:aaaaaaaaaaaaaaaa",
|
|
20
|
+
staging: "sha256:aaaaaaaaaaaaaaaa",
|
|
21
|
+
prod: "sha256:bbbbbbbbbbbbbbbb",
|
|
22
|
+
},
|
|
23
|
+
fingerprint: "sha256:aaaaaaaaaaaaaaaa",
|
|
24
|
+
cleartext: null,
|
|
25
|
+
winner: ".env.local",
|
|
26
|
+
resolution: [
|
|
27
|
+
{ source: "process.env", present: false, won: false },
|
|
28
|
+
{ source: ".env.local", present: true, won: true },
|
|
29
|
+
{ source: ".env.stack", present: false, won: false },
|
|
30
|
+
{ source: "driver", present: false, won: false },
|
|
31
|
+
{ source: "dev-fallback", present: false, won: false },
|
|
32
|
+
],
|
|
33
|
+
readers: ["payments.charge", "payments.refund"],
|
|
34
|
+
blastRadius: {
|
|
35
|
+
count: 2,
|
|
36
|
+
longestWakeAt: 1_800_000_000_000,
|
|
37
|
+
longestOutstandingMs: 86_400_000,
|
|
38
|
+
runIds: ["run_sleep_1", "run_sleep_2"],
|
|
39
|
+
},
|
|
40
|
+
lastReadAt: 1_700_000_000_000,
|
|
41
|
+
sharedFingerprintEnvs: ["staging"],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "PUBLIC_APP_URL",
|
|
45
|
+
kind: "config",
|
|
46
|
+
sensitive: false,
|
|
47
|
+
description: "Public site origin",
|
|
48
|
+
fingerprints: {},
|
|
49
|
+
fingerprint: null,
|
|
50
|
+
cleartext: "https://app.example.com",
|
|
51
|
+
winner: "process.env",
|
|
52
|
+
resolution: [
|
|
53
|
+
{ source: "process.env", present: true, won: true },
|
|
54
|
+
{ source: ".env.local", present: false, won: false },
|
|
55
|
+
{ source: ".env.stack", present: false, won: false },
|
|
56
|
+
{ source: "driver", present: false, won: false },
|
|
57
|
+
{ source: "dev-fallback", present: false, won: false },
|
|
58
|
+
],
|
|
59
|
+
readers: ["site.render"],
|
|
60
|
+
blastRadius: {
|
|
61
|
+
count: 0,
|
|
62
|
+
longestWakeAt: null,
|
|
63
|
+
longestOutstandingMs: null,
|
|
64
|
+
runIds: [],
|
|
65
|
+
},
|
|
66
|
+
lastReadAt: null,
|
|
67
|
+
sharedFingerprintEnvs: [],
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/** List response matching `console.vault.list`. */
|
|
72
|
+
export const VAULT_LIST_FIXTURE: VaultListResponse = {
|
|
73
|
+
secrets: VAULT_FIXTURE,
|
|
74
|
+
env: "dev",
|
|
75
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { VAULT_FIXTURE } from "./fixture.ts";
|
|
3
|
+
import { groupByKind, matchesQuery } from "./group.ts";
|
|
4
|
+
|
|
5
|
+
describe("groupByKind", () => {
|
|
6
|
+
test("groups secrets and config", () => {
|
|
7
|
+
const groups = groupByKind(VAULT_FIXTURE);
|
|
8
|
+
expect(groups.map((g) => g.kind)).toEqual(["secret", "config"]);
|
|
9
|
+
expect(groups[0]?.secrets[0]?.name).toBe("STRIPE_KEY");
|
|
10
|
+
expect(groups[1]?.secrets[0]?.name).toBe("PUBLIC_APP_URL");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("filters by query", () => {
|
|
14
|
+
const groups = groupByKind(VAULT_FIXTURE, "public");
|
|
15
|
+
expect(groups).toHaveLength(1);
|
|
16
|
+
expect(groups[0]?.kind).toBe("config");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("matchesQuery", () => {
|
|
21
|
+
test("matches name case-insensitively", () => {
|
|
22
|
+
expect(matchesQuery(VAULT_FIXTURE[0]!, "stripe")).toBe(true);
|
|
23
|
+
expect(matchesQuery(VAULT_FIXTURE[0]!, "nope")).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Group vault contracts — secrets vs non-sensitive config (console §9.8).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { VaultKindGroup, VaultRecord } from "./types.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Group secrets and config; optional case-insensitive name/description filter.
|
|
9
|
+
*
|
|
10
|
+
* @param secrets - Vault rows
|
|
11
|
+
* @param query - Optional filter
|
|
12
|
+
*/
|
|
13
|
+
export function groupByKind(
|
|
14
|
+
secrets: readonly VaultRecord[],
|
|
15
|
+
query = "",
|
|
16
|
+
): readonly VaultKindGroup[] {
|
|
17
|
+
const q = query.trim().toLowerCase();
|
|
18
|
+
const filtered = q
|
|
19
|
+
? secrets.filter(
|
|
20
|
+
(s) =>
|
|
21
|
+
s.name.toLowerCase().includes(q) ||
|
|
22
|
+
(s.description?.toLowerCase().includes(q) ?? false),
|
|
23
|
+
)
|
|
24
|
+
: secrets;
|
|
25
|
+
|
|
26
|
+
const order: Array<"secret" | "config"> = ["secret", "config"];
|
|
27
|
+
const labels = { secret: "Secrets", config: "Config" } as const;
|
|
28
|
+
return order
|
|
29
|
+
.map((kind) => ({
|
|
30
|
+
kind,
|
|
31
|
+
label: labels[kind],
|
|
32
|
+
secrets: filtered.filter((s) => s.kind === kind),
|
|
33
|
+
}))
|
|
34
|
+
.filter((g) => g.secrets.length > 0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Case-insensitive name match helper.
|
|
39
|
+
*
|
|
40
|
+
* @param secret - Row
|
|
41
|
+
* @param query - Filter
|
|
42
|
+
*/
|
|
43
|
+
export function matchesQuery(secret: VaultRecord, query: string): boolean {
|
|
44
|
+
const q = query.trim().toLowerCase();
|
|
45
|
+
if (!q) return true;
|
|
46
|
+
return (
|
|
47
|
+
secret.name.toLowerCase().includes(q) ||
|
|
48
|
+
(secret.description?.toLowerCase().includes(q) ?? false)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault panel pure modules (console §9.8).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
VaultBlastRadius,
|
|
7
|
+
VaultKindGroup,
|
|
8
|
+
VaultListResponse,
|
|
9
|
+
VaultRecord,
|
|
10
|
+
VaultResolutionSource,
|
|
11
|
+
VaultResolutionStep,
|
|
12
|
+
} from "./types.ts";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
VAULT_FIXTURE,
|
|
16
|
+
VAULT_LIST_FIXTURE,
|
|
17
|
+
FIXTURE_SECRET_VALUE,
|
|
18
|
+
} from "./fixture.ts";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
parseVaultSearch,
|
|
22
|
+
serializeVaultSearch,
|
|
23
|
+
openVault,
|
|
24
|
+
closeVault,
|
|
25
|
+
type VaultSearch,
|
|
26
|
+
} from "./search.ts";
|
|
27
|
+
|
|
28
|
+
export { groupByKind, matchesQuery } from "./group.ts";
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
setConfirmation,
|
|
32
|
+
rotateConfirmation,
|
|
33
|
+
validateTypedConfirm,
|
|
34
|
+
type ConfirmationPattern,
|
|
35
|
+
} from "./confirmation.ts";
|
|
36
|
+
|
|
37
|
+
export { formatBlastRadius, formatDuration } from "./blast-radius.ts";
|
|
38
|
+
|
|
39
|
+
export { dormantSecrets, DORMANT_MS } from "./dormant.ts";
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
exportSafeRow,
|
|
43
|
+
exportSafeList,
|
|
44
|
+
assertExportHasNoSecrets,
|
|
45
|
+
} from "./export-safe.ts";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL search state for the Vault panel.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const VaultSearchSchema = z.object({
|
|
8
|
+
q: z.string().optional(),
|
|
9
|
+
name: z.string().optional(),
|
|
10
|
+
/** Active write action in the detail pane. */
|
|
11
|
+
action: z.enum(["set", "rotate"]).optional(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/** Parsed Vault URL search. */
|
|
15
|
+
export type VaultSearch = z.infer<typeof VaultSearchSchema>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse Vault panel search params.
|
|
19
|
+
*
|
|
20
|
+
* @param search - Raw router search
|
|
21
|
+
*/
|
|
22
|
+
export function parseVaultSearch(
|
|
23
|
+
search: Record<string, unknown>,
|
|
24
|
+
): VaultSearch {
|
|
25
|
+
const parsed = VaultSearchSchema.safeParse(search);
|
|
26
|
+
return parsed.success ? parsed.data : {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Serialize Vault search for navigation (omit empties).
|
|
31
|
+
*
|
|
32
|
+
* @param search - Search state
|
|
33
|
+
*/
|
|
34
|
+
export function serializeVaultSearch(
|
|
35
|
+
search: VaultSearch,
|
|
36
|
+
): Record<string, string> {
|
|
37
|
+
const out: Record<string, string> = {};
|
|
38
|
+
if (search.q) out.q = search.q;
|
|
39
|
+
if (search.name) out.name = search.name;
|
|
40
|
+
if (search.action) out.action = search.action;
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Open a vault contract in the URL.
|
|
46
|
+
*
|
|
47
|
+
* @param search - Current search
|
|
48
|
+
* @param name - Contract name
|
|
49
|
+
*/
|
|
50
|
+
export function openVault(search: VaultSearch, name: string): VaultSearch {
|
|
51
|
+
return { ...search, name, action: undefined };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Close the open vault detail.
|
|
56
|
+
*
|
|
57
|
+
* @param search - Current search
|
|
58
|
+
*/
|
|
59
|
+
export function closeVault(search: VaultSearch): VaultSearch {
|
|
60
|
+
const { name: _n, action: _a, ...rest } = search;
|
|
61
|
+
return rest;
|
|
62
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault panel types (console §9.8).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Resolution layer id — matches VaultRuntime sources. */
|
|
6
|
+
export type VaultResolutionSource =
|
|
7
|
+
| "process.env"
|
|
8
|
+
| ".env.local"
|
|
9
|
+
| ".env.stack"
|
|
10
|
+
| "driver"
|
|
11
|
+
| "dev-fallback";
|
|
12
|
+
|
|
13
|
+
/** One step in the full resolution chain. */
|
|
14
|
+
export interface VaultResolutionStep {
|
|
15
|
+
readonly source: VaultResolutionSource;
|
|
16
|
+
readonly present: boolean;
|
|
17
|
+
readonly won: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Journal-queried rotation blast radius. */
|
|
21
|
+
export interface VaultBlastRadius {
|
|
22
|
+
readonly count: number;
|
|
23
|
+
readonly longestWakeAt: number | null;
|
|
24
|
+
readonly longestOutstandingMs: number | null;
|
|
25
|
+
readonly runIds: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One vault contract in the operator list. */
|
|
29
|
+
export interface VaultRecord {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly kind: "secret" | "config";
|
|
32
|
+
readonly sensitive: boolean;
|
|
33
|
+
readonly description?: string;
|
|
34
|
+
readonly rotate?: string;
|
|
35
|
+
readonly fingerprints: Readonly<Record<string, string>>;
|
|
36
|
+
readonly fingerprint: string | null;
|
|
37
|
+
readonly cleartext: string | null;
|
|
38
|
+
readonly winner: VaultResolutionSource | null;
|
|
39
|
+
readonly resolution: readonly VaultResolutionStep[];
|
|
40
|
+
readonly readers: readonly string[];
|
|
41
|
+
readonly blastRadius: VaultBlastRadius;
|
|
42
|
+
readonly lastReadAt: number | null;
|
|
43
|
+
readonly sharedFingerprintEnvs: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `console.vault.list` response. */
|
|
47
|
+
export interface VaultListResponse {
|
|
48
|
+
readonly secrets: readonly VaultRecord[];
|
|
49
|
+
readonly env: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Grouped list section (secrets vs config). */
|
|
53
|
+
export interface VaultKindGroup {
|
|
54
|
+
readonly kind: "secret" | "config";
|
|
55
|
+
readonly label: string;
|
|
56
|
+
readonly secrets: readonly VaultRecord[];
|
|
57
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite build for the Console SPA shell (console §7).
|
|
3
|
+
* Output is shipped inside the package and served by Bun on 6533.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
7
|
+
import react from "@vitejs/plugin-react";
|
|
8
|
+
import { dirname, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { defineConfig } from "vite";
|
|
11
|
+
|
|
12
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Panel chunk id from a module path, or `undefined` for the shell entry.
|
|
16
|
+
*
|
|
17
|
+
* Every lazy panel must resolve to `panel-<id>` so:
|
|
18
|
+
* 1. budget measurement can exclude them from the initial load
|
|
19
|
+
* 2. `modulePreload` can refuse to eagerly fetch them
|
|
20
|
+
*
|
|
21
|
+
* @param id - Rollup module id
|
|
22
|
+
*/
|
|
23
|
+
function panelChunkOf(id: string): string | undefined {
|
|
24
|
+
const panels: ReadonlyArray<readonly [string, string]> = [
|
|
25
|
+
["flows", "panel-flows"],
|
|
26
|
+
["traces", "panel-traces"],
|
|
27
|
+
["runs", "panel-runs"],
|
|
28
|
+
["signals", "panel-signals"],
|
|
29
|
+
["store", "panel-store"],
|
|
30
|
+
["clock", "panel-clock"],
|
|
31
|
+
["vault", "panel-vault"],
|
|
32
|
+
["ai", "panel-ai"],
|
|
33
|
+
["diff", "panel-diff"],
|
|
34
|
+
["architecture", "panel-architecture"],
|
|
35
|
+
["access", "panel-access"],
|
|
36
|
+
["plugins", "panel-plugins"],
|
|
37
|
+
["channels", "panel-channels"],
|
|
38
|
+
["gates", "panel-gates"],
|
|
39
|
+
["overview", "panel-overview"],
|
|
40
|
+
];
|
|
41
|
+
for (const [name, chunk] of panels) {
|
|
42
|
+
const pascal = name[0]!.toUpperCase() + name.slice(1);
|
|
43
|
+
if (
|
|
44
|
+
id.includes(`/console/ui/shell/panels/${name}/`) ||
|
|
45
|
+
id.includes(`/console/ui/shell/panels/${pascal}.`) ||
|
|
46
|
+
id.includes(`/console/ui/${name}/`) ||
|
|
47
|
+
(name === "flows" && id.includes("@codemirror/"))
|
|
48
|
+
) {
|
|
49
|
+
return chunk;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
root: resolve(here, "shell"),
|
|
57
|
+
plugins: [react(), tailwindcss()],
|
|
58
|
+
build: {
|
|
59
|
+
outDir: resolve(here, "dist"),
|
|
60
|
+
emptyOutDir: true,
|
|
61
|
+
sourcemap: false,
|
|
62
|
+
target: "esnext",
|
|
63
|
+
cssCodeSplit: false,
|
|
64
|
+
// Lazy panels must stay lazy on the wire — Vite's default modulepreload
|
|
65
|
+
// of every dynamic import was pulling all 17 panel chunks into the first
|
|
66
|
+
// navigation (the unexplained 89→138 kB class of "initial" measurements).
|
|
67
|
+
modulePreload: {
|
|
68
|
+
resolveDependencies(_filename, deps) {
|
|
69
|
+
return deps.filter((dep) => !dep.includes("panel-"));
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
rollupOptions: {
|
|
73
|
+
output: {
|
|
74
|
+
manualChunks(id) {
|
|
75
|
+
return panelChunkOf(id);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
server: {
|
|
81
|
+
port: 6534,
|
|
82
|
+
strictPort: true,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XSS build gate — no `dangerouslySetInnerHTML` / raw HTML injection in Console UI.
|
|
3
|
+
*
|
|
4
|
+
* console §10.2: this is a build gate, not a review convention.
|
|
5
|
+
*
|
|
6
|
+
* The scan walks all of `src/console/ui` recursively (not a hard-coded panel
|
|
7
|
+
* list from Prompt 16). The panel-directory assertion below fails the build
|
|
8
|
+
* if a new `ui/<panel>` folder appears without being covered by that walk.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, expect, test } from "bun:test";
|
|
12
|
+
import { readdir } from "node:fs/promises";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
|
|
15
|
+
const UI_ROOT = `${import.meta.dir}/ui`;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Panel feature directories that must be covered by the XSS scan today.
|
|
19
|
+
* Update this list when a 18th panel lands — the recursive walk already
|
|
20
|
+
* covers it; the list makes coverage explicit in CI output.
|
|
21
|
+
*/
|
|
22
|
+
export const CONSOLE_UI_PANEL_DIRS = [
|
|
23
|
+
"access",
|
|
24
|
+
"ai",
|
|
25
|
+
"architecture",
|
|
26
|
+
"channels",
|
|
27
|
+
"clock",
|
|
28
|
+
"diff",
|
|
29
|
+
"flows",
|
|
30
|
+
"gates",
|
|
31
|
+
"overview",
|
|
32
|
+
"plugins",
|
|
33
|
+
"runs",
|
|
34
|
+
"signals",
|
|
35
|
+
"store",
|
|
36
|
+
"traces",
|
|
37
|
+
"vault",
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
/** Patterns that fail the gate. */
|
|
41
|
+
const FORBIDDEN = [
|
|
42
|
+
/dangerouslySetInnerHTML/,
|
|
43
|
+
/\.innerHTML\s*=/,
|
|
44
|
+
/document\.write\s*\(/,
|
|
45
|
+
/insertAdjacentHTML\s*\(/,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Collect source files under the Console UI.
|
|
50
|
+
*
|
|
51
|
+
* @param dir - Directory
|
|
52
|
+
*/
|
|
53
|
+
async function collectSources(dir: string): Promise<string[]> {
|
|
54
|
+
const out: string[] = [];
|
|
55
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const path = join(dir, entry.name);
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
if (entry.name === "dist" || entry.name === "node_modules") continue;
|
|
60
|
+
out.push(...(await collectSources(path)));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (/\.(tsx?|jsx?|html|css)$/.test(entry.name)) {
|
|
64
|
+
out.push(path);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Scan source text for forbidden XSS sinks.
|
|
72
|
+
*
|
|
73
|
+
* @param text - Source
|
|
74
|
+
*/
|
|
75
|
+
export function findXssViolations(text: string): string[] {
|
|
76
|
+
return FORBIDDEN.filter((pattern) => pattern.test(text)).map(String);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
describe("console XSS gate", () => {
|
|
80
|
+
test("scan glob covers every ui/<panel> directory that exists today", async () => {
|
|
81
|
+
const entries = await readdir(UI_ROOT, { withFileTypes: true });
|
|
82
|
+
const dirs = entries
|
|
83
|
+
.filter((e) => e.isDirectory())
|
|
84
|
+
.map((e) => e.name)
|
|
85
|
+
.filter((name) => name !== "dist" && name !== "shell" && name !== "node_modules")
|
|
86
|
+
.sort();
|
|
87
|
+
expect(dirs).toEqual([...CONSOLE_UI_PANEL_DIRS].sort());
|
|
88
|
+
|
|
89
|
+
const files = await collectSources(UI_ROOT);
|
|
90
|
+
for (const panel of CONSOLE_UI_PANEL_DIRS) {
|
|
91
|
+
const hit = files.some((f) => f.includes(`/ui/${panel}/`) || f.includes(`\\ui\\${panel}\\`));
|
|
92
|
+
expect(hit).toBe(true);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("no dangerouslySetInnerHTML or raw HTML sinks in Console UI", async () => {
|
|
97
|
+
const files = await collectSources(UI_ROOT);
|
|
98
|
+
expect(files.length).toBeGreaterThan(0);
|
|
99
|
+
const violations: string[] = [];
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
const text = await Bun.file(file).text();
|
|
102
|
+
for (const pattern of FORBIDDEN) {
|
|
103
|
+
if (pattern.test(text)) {
|
|
104
|
+
violations.push(`${file}: ${pattern}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
expect(violations).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("gate fails a PR that introduces raw HTML", () => {
|
|
112
|
+
const bad = `export function Evil() { return <div dangerouslySetInnerHTML={{ __html: x }} />; }`;
|
|
113
|
+
expect(findXssViolations(bad).length).toBeGreaterThan(0);
|
|
114
|
+
expect(findXssViolations(`el.innerHTML = payload`)).toContain(
|
|
115
|
+
String(/\.innerHTML\s*=/),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|