okengine 0.2.6 → 0.3.2
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 +33 -30
- package/README.md +74 -340
- package/{spec/manifest.v1.schema.json → manifest.v1.schema.json} +40 -9
- package/package.json +47 -44
- package/site/content/docs/ai/llms-txt.mdx +54 -0
- package/site/content/docs/ai/mcp.mdx +123 -0
- package/site/content/docs/ai/meta.json +5 -0
- package/site/content/docs/ai/skills.mdx +53 -0
- package/site/content/docs/console/access.mdx +29 -0
- package/site/content/docs/console/ai.mdx +35 -0
- package/site/content/docs/console/architecture.mdx +35 -0
- package/site/content/docs/console/channels.mdx +37 -0
- package/site/content/docs/console/clock.mdx +31 -0
- package/site/content/docs/console/flows.mdx +31 -0
- package/site/content/docs/console/gates.mdx +35 -0
- package/site/content/docs/console/manifest-diff.mdx +34 -0
- package/site/content/docs/console/meta.json +23 -0
- package/site/content/docs/console/overview.mdx +40 -0
- package/site/content/docs/console/plugins.mdx +41 -0
- package/site/content/docs/console/privacy.mdx +32 -0
- package/site/content/docs/console/runs.mdx +40 -0
- package/site/content/docs/console/signals.mdx +31 -0
- package/site/content/docs/console/store.mdx +32 -0
- package/site/content/docs/console/tenancy.mdx +32 -0
- package/site/content/docs/console/traces.mdx +34 -0
- package/site/content/docs/console/vault.mdx +37 -0
- package/site/content/docs/elements/ai.mdx +180 -0
- package/site/content/docs/elements/channel.mdx +167 -0
- package/site/content/docs/elements/clock.mdx +182 -0
- package/site/content/docs/elements/flow.mdx +288 -0
- package/site/content/docs/elements/gate.mdx +171 -0
- package/site/content/docs/elements/meta.json +5 -0
- package/site/content/docs/elements/signal.mdx +171 -0
- package/site/content/docs/elements/store.mdx +320 -0
- package/site/content/docs/elements/vault.mdx +263 -0
- package/site/content/docs/get-started/basic-usage.mdx +124 -0
- package/site/content/docs/get-started/comparison.mdx +65 -0
- package/site/content/docs/get-started/installation.mdx +113 -0
- package/site/content/docs/get-started/introduction.mdx +123 -0
- package/site/content/docs/get-started/meta.json +5 -0
- package/site/content/docs/index.mdx +63 -0
- package/site/content/docs/meta.json +5 -0
- package/site/content/docs/plugins/compression.mdx +60 -0
- package/site/content/docs/plugins/cors.mdx +92 -0
- package/site/content/docs/plugins/csrf.mdx +96 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +92 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +101 -0
- package/site/content/docs/plugins/meta.json +15 -0
- package/site/content/docs/plugins/security-headers.mdx +136 -0
- package/site/content/docs/reference/cli.md +101 -0
- package/site/content/docs/reference/configuration.mdx +159 -0
- package/site/content/docs/reference/environment-variables.mdx +87 -0
- package/site/content/docs/reference/errors.mdx +80 -0
- package/site/content/docs/reference/fx.mdx +117 -0
- package/site/content/docs/reference/meta.json +5 -0
- package/site/content/docs/reference/plugins.mdx +249 -0
- package/site/content/docs/reference/security.md +63 -0
- package/src/auth/api-keys.ts +2 -5
- package/src/auth/attenuation.ts +2 -6
- package/src/auth/auth.test.ts +56 -31
- package/src/auth/invoke-as.ts +1 -3
- package/src/auth/operator.ts +8 -26
- package/src/auth/planes.ts +2 -7
- package/src/auth/plugin.ts +4 -7
- package/src/auth/roles.ts +1 -5
- package/src/auth/sessions.ts +10 -39
- package/src/cli/args.ts +1 -5
- package/src/cli/ask-dev-mode.ts +40 -0
- package/src/cli/build.ts +1 -2
- package/src/cli/client-add.test.ts +8 -15
- package/src/cli/client-add.ts +5 -14
- package/src/cli/completion.test.ts +1 -5
- package/src/cli/completion.ts +4 -17
- package/src/cli/db-auto-push.test.ts +64 -0
- package/src/cli/db-auto-push.ts +72 -0
- package/src/cli/db.test.ts +189 -0
- package/src/cli/db.ts +456 -0
- package/src/cli/dev-app-runner.ts +2 -8
- package/src/cli/dev-db-push.test.ts +193 -0
- package/src/cli/dev-mode.test.ts +45 -0
- package/src/cli/dev-mode.ts +78 -0
- package/src/cli/dev-schema-sync.test.ts +66 -0
- package/src/cli/dev-schema-sync.ts +139 -0
- package/src/cli/dev.test.ts +264 -15
- package/src/cli/dev.ts +355 -139
- package/src/cli/doc-staleness.test.ts +69 -0
- package/src/cli/docker-cli.test.ts +27 -8
- package/src/cli/docker.ts +11 -3
- package/src/cli/doctor-diff.test.ts +1 -5
- package/src/cli/doctor-diff.ts +17 -46
- package/src/cli/doctor-pii.ts +2 -7
- package/src/cli/doctor.test.ts +26 -4
- package/src/cli/doctor.ts +35 -11
- package/src/cli/drizzle-env.test.ts +67 -0
- package/src/cli/drizzle-env.ts +78 -0
- package/src/cli/ensure-drizzle-config.ts +50 -0
- package/src/cli/eval.ts +4 -14
- package/src/cli/gates-list.ts +1 -4
- package/src/cli/hero-meta.test.ts +17 -19
- package/src/cli/hero-meta.ts +20 -30
- package/src/cli/images.ts +3 -8
- package/src/cli/index.ts +11 -2
- package/src/cli/load-config.images.test.ts +14 -14
- package/src/cli/load-config.ts +47 -47
- package/src/cli/mcp-from-console.ts +1 -4
- package/src/cli/mode.ts +65 -0
- package/src/cli/openbao-bootstrap.test.ts +147 -0
- package/src/cli/openbao-bootstrap.ts +280 -0
- package/src/cli/openbao-restart.integration.test.ts +136 -0
- package/src/cli/ports.test.ts +7 -14
- package/src/cli/ports.ts +7 -5
- package/src/cli/privacy-erase.ts +1 -3
- package/src/cli/registry.ts +75 -25
- package/src/cli/resolve-dev-sql-env.test.ts +48 -0
- package/src/cli/resolve-dev-sql-env.ts +42 -0
- package/src/cli/schema.ts +3 -10
- package/src/cli/stack.ts +8 -10
- package/src/cli/start.ts +4 -15
- package/src/cli/upgrade.ts +1 -4
- package/src/cli/vault-cmd.ts +63 -0
- package/src/client/budget-entry.ts +1 -6
- package/src/client/create.test.ts +6 -12
- package/src/client/create.ts +2 -8
- package/src/client/errors.ts +1 -4
- package/src/client/index.ts +1 -6
- package/src/client/notes-contract.test.ts +4 -13
- package/src/client/transport.ts +9 -30
- package/src/client/types.ts +15 -30
- package/src/compiler/aot.test.ts +2 -9
- package/src/compiler/aot.ts +1 -4
- package/src/compiler/differential.test.ts +3 -11
- package/src/compiler/dynamic.ts +1 -4
- package/src/compiler/effects-infer.ts +8 -34
- package/src/compiler/emit.ts +1 -3
- package/src/compiler/extract.test.ts +136 -30
- package/src/compiler/extract.ts +485 -157
- package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
- package/src/compiler/fixtures/skyport.expected.json +24 -83
- package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
- package/src/compiler/http-parse.ts +2 -9
- package/src/compiler/index.ts +1 -5
- package/src/compiler/response.ts +16 -7
- package/src/compiler/sucrose.ts +11 -31
- package/src/config/define-config.test.ts +55 -34
- package/src/config/index.ts +177 -46
- package/src/config/resolve-driver.test.ts +28 -17
- package/src/console/budget.test.ts +1 -4
- package/src/console/index.ts +1 -4
- package/src/console/server/access.test.ts +15 -43
- package/src/console/server/access.ts +19 -49
- package/src/console/server/ai.test.ts +3 -11
- package/src/console/server/ai.ts +23 -46
- package/src/console/server/app.ts +16 -45
- package/src/console/server/auth-rate.test.ts +4 -12
- package/src/console/server/bind.ts +1 -5
- package/src/console/server/channels.test.ts +2 -6
- package/src/console/server/channels.ts +10 -35
- package/src/console/server/claim.ts +2 -8
- package/src/console/server/clock.test.ts +2 -6
- package/src/console/server/clock.ts +8 -26
- package/src/console/server/console.test.ts +1 -3
- package/src/console/server/diff.test.ts +5 -17
- package/src/console/server/diff.ts +10 -31
- package/src/console/server/dry-run.audit.test.ts +2 -8
- package/src/console/server/flows.ts +64 -233
- package/src/console/server/gates.test.ts +14 -50
- package/src/console/server/gates.ts +13 -48
- package/src/console/server/index.ts +2 -9
- package/src/console/server/live.ts +3 -12
- package/src/console/server/operator-db.test.ts +4 -15
- package/src/console/server/operator-db.ts +13 -32
- package/src/console/server/plugin.ts +50 -53
- package/src/console/server/plugins.test.ts +2 -6
- package/src/console/server/plugins.ts +17 -56
- package/src/console/server/runs-pii.ts +2 -7
- package/src/console/server/security.gate.test.ts +18 -40
- package/src/console/server/serve.ts +10 -25
- package/src/console/server/signals.test.ts +1 -3
- package/src/console/server/signals.ts +17 -40
- package/src/console/server/state.ts +21 -59
- package/src/console/server/store.test.ts +20 -22
- package/src/console/server/store.ts +31 -76
- package/src/console/server/vault.test.ts +12 -29
- package/src/console/server/vault.ts +44 -66
- package/src/console/ui/access/AccessA11yView.tsx +6 -21
- package/src/console/ui/access/a11y.test.tsx +3 -12
- package/src/console/ui/access/acknowledgement.test.ts +1 -4
- package/src/console/ui/access/acknowledgement.ts +1 -3
- package/src/console/ui/access/blast-radius.ts +1 -3
- package/src/console/ui/access/confirmation.test.ts +1 -5
- package/src/console/ui/access/confirmation.ts +1 -4
- package/src/console/ui/access/fixture.ts +1 -5
- package/src/console/ui/access/grantable.test.ts +1 -3
- package/src/console/ui/access/grantable.ts +2 -7
- package/src/console/ui/access/index.ts +2 -9
- package/src/console/ui/access/provenance.test.ts +1 -3
- package/src/console/ui/access/provenance.ts +2 -6
- package/src/console/ui/access/search.test.ts +1 -5
- package/src/console/ui/access/search.ts +2 -6
- package/src/console/ui/ai/AiA11yView.tsx +6 -17
- package/src/console/ui/ai/a11y.test.tsx +2 -5
- package/src/console/ui/ai/fixture.ts +2 -7
- package/src/console/ui/ai/format.ts +1 -3
- package/src/console/ui/ai/group.test.ts +1 -3
- package/src/console/ui/ai/group.ts +4 -12
- package/src/console/ui/ai/index.ts +2 -9
- package/src/console/ui/ai/promotion.test.ts +2 -7
- package/src/console/ui/ai/promotion.ts +4 -13
- package/src/console/ui/ai/search.test.ts +2 -11
- package/src/console/ui/ai/search.ts +3 -13
- package/src/console/ui/ai/types.ts +1 -8
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
- package/src/console/ui/architecture/a11y.test.tsx +1 -2
- package/src/console/ui/architecture/declared.ts +1 -5
- package/src/console/ui/architecture/index.ts +7 -35
- package/src/console/ui/architecture/layout.ts +1 -3
- package/src/console/ui/architecture/pathologies.ts +10 -19
- package/src/console/ui/architecture/search.ts +6 -15
- package/src/console/ui/architecture/traffic.ts +4 -14
- package/src/console/ui/architecture/types.ts +2 -10
- package/src/console/ui/architecture/view.test.ts +7 -19
- package/src/console/ui/architecture/view.ts +9 -36
- package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
- package/src/console/ui/channels/a11y.test.tsx +1 -2
- package/src/console/ui/channels/confirmation.test.ts +1 -4
- package/src/console/ui/channels/confirmation.ts +1 -4
- package/src/console/ui/channels/findings.ts +1 -5
- package/src/console/ui/channels/index.ts +2 -8
- package/src/console/ui/channels/locale.test.ts +1 -3
- package/src/console/ui/channels/locale.ts +2 -8
- package/src/console/ui/channels/search.ts +4 -13
- package/src/console/ui/channels/taxonomy.ts +1 -3
- package/src/console/ui/clock/ClockA11yView.tsx +9 -26
- package/src/console/ui/clock/a11y.test.tsx +2 -5
- package/src/console/ui/clock/confirmation.test.ts +1 -3
- package/src/console/ui/clock/confirmation.ts +1 -4
- package/src/console/ui/clock/findings.ts +1 -4
- package/src/console/ui/clock/health.ts +2 -7
- package/src/console/ui/clock/index.ts +2 -10
- package/src/console/ui/clock/search.test.ts +1 -3
- package/src/console/ui/clock/search.ts +2 -6
- package/src/console/ui/clock/timeline.test.ts +1 -3
- package/src/console/ui/clock/waiting-on.test.ts +1 -5
- package/src/console/ui/clock/waiting-on.ts +2 -7
- package/src/console/ui/diff/DiffA11yView.tsx +8 -17
- package/src/console/ui/diff/a11y.test.tsx +2 -5
- package/src/console/ui/diff/group.test.ts +3 -13
- package/src/console/ui/diff/group.ts +2 -8
- package/src/console/ui/diff/index.ts +2 -8
- package/src/console/ui/diff/search.test.ts +1 -5
- package/src/console/ui/diff/search.ts +4 -16
- package/src/console/ui/diff/summary.ts +1 -2
- package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
- package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
- package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
- package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
- package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
- package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
- package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
- package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
- package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
- package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
- package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
- package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
- package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
- package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
- package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
- package/src/console/ui/dist/index.html +1 -1
- package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
- package/src/console/ui/flows/a11y.test.tsx +2 -8
- package/src/console/ui/flows/buffer.test.ts +3 -11
- package/src/console/ui/flows/buffer.ts +4 -11
- package/src/console/ui/flows/confirmation.ts +1 -3
- package/src/console/ui/flows/contract.test.ts +3 -7
- package/src/console/ui/flows/contract.ts +12 -36
- package/src/console/ui/flows/graph.test.ts +3 -11
- package/src/console/ui/flows/graph.ts +5 -15
- package/src/console/ui/flows/index.ts +1 -6
- package/src/console/ui/flows/save-as-test.ts +1 -3
- package/src/console/ui/flows/search.ts +8 -25
- package/src/console/ui/flows/tiers.ts +2 -9
- package/src/console/ui/gates/GatesA11yView.tsx +4 -13
- package/src/console/ui/gates/a11y.test.tsx +2 -5
- package/src/console/ui/gates/audit.ts +1 -3
- package/src/console/ui/gates/denial.test.ts +1 -3
- package/src/console/ui/gates/denial.ts +4 -12
- package/src/console/ui/gates/findings.ts +1 -3
- package/src/console/ui/gates/group.test.ts +3 -3
- package/src/console/ui/gates/group.ts +3 -11
- package/src/console/ui/gates/index.ts +2 -10
- package/src/console/ui/gates/search.ts +3 -10
- package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
- package/src/console/ui/overview/a11y.test.tsx +1 -2
- package/src/console/ui/overview/busiest.ts +1 -3
- package/src/console/ui/overview/compose.ts +1 -13
- package/src/console/ui/overview/cost.ts +34 -38
- package/src/console/ui/overview/fixture.ts +21 -26
- package/src/console/ui/overview/golden.ts +4 -14
- package/src/console/ui/overview/index.ts +2 -9
- package/src/console/ui/overview/rank.test.ts +3 -10
- package/src/console/ui/overview/rank.ts +2 -7
- package/src/console/ui/overview/slo.test.ts +5 -9
- package/src/console/ui/overview/slo.ts +6 -23
- package/src/console/ui/overview/verdict.ts +3 -10
- package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
- package/src/console/ui/plugins/a11y.test.tsx +1 -2
- package/src/console/ui/plugins/fixture.ts +2 -4
- package/src/console/ui/plugins/group.test.ts +1 -3
- package/src/console/ui/plugins/group.ts +1 -6
- package/src/console/ui/plugins/index.ts +3 -13
- package/src/console/ui/plugins/search.ts +7 -19
- package/src/console/ui/runs/RunsA11yView.tsx +5 -20
- package/src/console/ui/runs/a11y.test.tsx +2 -5
- package/src/console/ui/runs/explain.ts +2 -6
- package/src/console/ui/runs/group.ts +3 -12
- package/src/console/ui/runs/histogram.test.ts +2 -11
- package/src/console/ui/runs/histogram.ts +1 -4
- package/src/console/ui/runs/index.ts +4 -18
- package/src/console/ui/runs/project.ts +2 -6
- package/src/console/ui/runs/query.test.ts +1 -3
- package/src/console/ui/runs/query.ts +12 -35
- package/src/console/ui/runs/search.ts +5 -19
- package/src/console/ui/runs/trace-link.test.ts +2 -6
- package/src/console/ui/runs/trace-link.ts +4 -16
- package/src/console/ui/runs/types.ts +1 -3
- package/src/console/ui/shell/App.tsx +2 -10
- package/src/console/ui/shell/client.ts +22 -115
- package/src/console/ui/shell/components/ui.tsx +1 -4
- package/src/console/ui/shell/layout/Shell.tsx +2 -4
- package/src/console/ui/shell/main.tsx +31 -96
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
- package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
- package/src/console/ui/shell/setup/Wizard.tsx +3 -5
- package/src/console/ui/shell/styles.css +1 -2
- package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
- package/src/console/ui/signals/a11y.test.tsx +2 -5
- package/src/console/ui/signals/confirmation.test.ts +1 -5
- package/src/console/ui/signals/confirmation.ts +2 -6
- package/src/console/ui/signals/dry-run.ts +1 -3
- package/src/console/ui/signals/durable.ts +1 -3
- package/src/console/ui/signals/findings.ts +1 -3
- package/src/console/ui/signals/group.test.ts +2 -8
- package/src/console/ui/signals/group.ts +2 -10
- package/src/console/ui/signals/index.ts +1 -4
- package/src/console/ui/signals/monitor.ts +2 -7
- package/src/console/ui/signals/schema-form.test.ts +3 -14
- package/src/console/ui/signals/schema-form.ts +1 -4
- package/src/console/ui/signals/search.ts +4 -13
- package/src/console/ui/store/StoreA11yView.tsx +3 -9
- package/src/console/ui/store/a11y.test.tsx +2 -5
- package/src/console/ui/store/confirmation.test.ts +1 -3
- package/src/console/ui/store/confirmation.ts +1 -4
- package/src/console/ui/store/dry-run.ts +2 -5
- package/src/console/ui/store/fixture.ts +1 -2
- package/src/console/ui/store/group.test.ts +1 -6
- package/src/console/ui/store/group.ts +1 -4
- package/src/console/ui/store/index.ts +1 -5
- package/src/console/ui/store/search.ts +2 -6
- package/src/console/ui/store/will-not-fire.ts +1 -2
- package/src/console/ui/traces/TracesA11yView.tsx +10 -29
- package/src/console/ui/traces/a11y.test.tsx +1 -2
- package/src/console/ui/traces/chain.test.ts +5 -19
- package/src/console/ui/traces/chain.ts +1 -3
- package/src/console/ui/traces/critical-path.test.ts +1 -3
- package/src/console/ui/traces/critical-path.ts +2 -6
- package/src/console/ui/traces/filter.test.ts +2 -6
- package/src/console/ui/traces/filter.ts +5 -13
- package/src/console/ui/traces/fold.test.ts +4 -12
- package/src/console/ui/traces/fold.ts +1 -3
- package/src/console/ui/traces/mini.ts +1 -3
- package/src/console/ui/traces/replay.test.ts +1 -3
- package/src/console/ui/traces/replay.ts +1 -3
- package/src/console/ui/traces/sampling.ts +1 -4
- package/src/console/ui/traces/search.ts +4 -17
- package/src/console/ui/traces/tier.ts +1 -3
- package/src/console/ui/vault/VaultA11yView.tsx +5 -17
- package/src/console/ui/vault/a11y.test.tsx +1 -2
- package/src/console/ui/vault/blast-radius.ts +1 -3
- package/src/console/ui/vault/confirmation.test.ts +1 -5
- package/src/console/ui/vault/confirmation.ts +1 -4
- package/src/console/ui/vault/dormant.test.ts +1 -4
- package/src/console/ui/vault/export-safe.test.ts +1 -5
- package/src/console/ui/vault/export-safe.ts +1 -4
- package/src/console/ui/vault/fixture.ts +3 -3
- package/src/console/ui/vault/group.ts +1 -2
- package/src/console/ui/vault/index.ts +2 -10
- package/src/console/ui/vault/search.ts +2 -6
- package/src/console/ui/vault/types.ts +1 -1
- package/src/console/xss.gate.test.ts +1 -3
- package/src/docker/compose.ts +215 -27
- package/src/docker/derive.ts +6 -14
- package/src/docker/docker.test.ts +118 -15
- package/src/docker/dockerfile.integration.test.ts +12 -21
- package/src/docker/helpers.ts +5 -4
- package/src/docker/index.ts +9 -6
- package/src/docker/pin.ts +1 -9
- package/src/docker/recipes/index.ts +6 -6
- package/src/docker/recipes/mailpit.ts +22 -0
- package/src/docker/recipes/openbao.ts +47 -0
- package/src/docker/recipes/postgres.ts +17 -6
- package/src/docker/recipes/redis.ts +11 -2
- package/src/docker/recipes/rustfs.ts +32 -0
- package/src/docker/stack-id.test.ts +73 -5
- package/src/docker/stack-id.ts +124 -33
- package/src/docker/stack.integration.test.ts +6 -21
- package/src/docker/stack.ts +36 -4
- package/src/docker/types.ts +16 -4
- package/src/docs-origin.ts +4 -4
- package/src/drivers/ai-anthropic.ts +5 -14
- package/src/drivers/ai-mock.ts +2 -5
- package/src/drivers/ai-openai-compatible.ts +4 -10
- package/src/drivers/ai-providers.test.ts +6 -13
- package/src/drivers/bun-native-completeness.test.ts +6 -15
- package/src/drivers/channel-console.ts +6 -7
- package/src/drivers/channel-fcm.ts +20 -30
- package/src/drivers/channel-resend.ts +1 -3
- package/src/drivers/channel-smtp.ts +2 -6
- package/src/drivers/channel-types.ts +2 -12
- package/src/drivers/channel-unifonic.ts +2 -6
- package/src/drivers/channel-wa-cloud.ts +2 -6
- package/src/drivers/channel-webpush.ts +21 -60
- package/src/drivers/conformance.test.ts +2 -10
- package/src/drivers/conformance.ts +8 -21
- package/src/drivers/drizzle-dialect.test.ts +20 -0
- package/src/drivers/drizzle-dialect.ts +37 -0
- package/src/drivers/fs.ts +2 -8
- package/src/drivers/index.ts +12 -57
- package/src/drivers/memory.ts +287 -75
- package/src/drivers/pgvector.ts +7 -11
- package/src/drivers/postgres.ts +7 -13
- package/src/drivers/redis.ts +13 -37
- package/src/drivers/s3.ts +15 -18
- package/src/drivers/signal-engine.ts +12 -49
- package/src/drivers/signal-memory.ts +2 -8
- package/src/drivers/signal-nats.ts +4 -9
- package/src/drivers/signal-postgres.ts +62 -129
- package/src/drivers/signal-redis.ts +7 -21
- package/src/drivers/signal-types.ts +1 -4
- package/src/drivers/sqlite.ts +3 -12
- package/src/drivers/types.ts +4 -23
- package/src/drivers/vault-driver-removal.test.ts +55 -0
- package/src/drivers/vault-env.ts +2 -6
- package/src/drivers/vault-infisical.ts +1 -5
- package/src/drivers/vault-managed.ts +1 -5
- package/src/drivers/vault-memory.ts +1 -5
- package/src/drivers/vault-openbao.test.ts +97 -0
- package/src/drivers/vault-openbao.ts +103 -40
- package/src/drivers/vault-types.ts +3 -16
- package/src/elements/ai/declare.ts +4 -14
- package/src/elements/ai/eval.ts +2 -6
- package/src/elements/ai/pii.ts +1 -3
- package/src/elements/ai/runtime.ts +12 -50
- package/src/elements/ai/schema.ts +4 -15
- package/src/elements/ai.test.ts +13 -24
- package/src/elements/ai.ts +1 -5
- package/src/elements/channel/consent.ts +3 -12
- package/src/elements/channel/costs.ts +4 -15
- package/src/elements/channel/declare.ts +1 -4
- package/src/elements/channel/email-auth.ts +1 -4
- package/src/elements/channel/locale.ts +2 -10
- package/src/elements/channel/mask.ts +1 -3
- package/src/elements/channel/mime.ts +2 -12
- package/src/elements/channel/outcomes.test.ts +1 -5
- package/src/elements/channel/outcomes.ts +5 -15
- package/src/elements/channel/receipts.ts +2 -7
- package/src/elements/channel/runtime.ts +28 -74
- package/src/elements/channel/suppression.ts +8 -14
- package/src/elements/channel.test.ts +8 -31
- package/src/elements/channel.ts +3 -14
- package/src/elements/clock/actions.ts +6 -18
- package/src/elements/clock/declare.ts +1 -3
- package/src/elements/clock/dst.ts +1 -5
- package/src/elements/clock/durable.ts +4 -16
- package/src/elements/clock/health.ts +2 -7
- package/src/elements/clock/leader.ts +2 -9
- package/src/elements/clock/reconcile.ts +4 -15
- package/src/elements/clock/runtime.ts +2 -5
- package/src/elements/clock/schedule.ts +4 -20
- package/src/elements/clock.test.ts +2 -7
- package/src/elements/clock.ts +5 -23
- package/src/elements/gate/permissions.ts +1 -4
- package/src/elements/gate/runtime.ts +7 -28
- package/src/elements/gate.ts +1 -5
- package/src/elements/index.ts +9 -7
- package/src/elements/signal/declare.ts +2 -7
- package/src/elements/signal/reconcile.test.ts +1 -4
- package/src/elements/signal/runtime.ts +1 -3
- package/src/elements/signal.test.ts +3 -15
- package/src/elements/signal.ts +2 -8
- package/src/elements/store/cache.test.ts +46 -0
- package/src/elements/store/cache.ts +3 -11
- package/src/elements/store/classify.ts +5 -22
- package/src/elements/store/declare.ts +11 -17
- package/src/elements/store/domain-ddl.test.ts +86 -0
- package/src/elements/store/emit-drizzle.ts +363 -0
- package/src/elements/store/files-policy.test.ts +1 -5
- package/src/elements/store/files-policy.ts +4 -12
- package/src/elements/store/load-plugin-tables.ts +103 -0
- package/src/elements/store/missing-relation.test.ts +83 -0
- package/src/elements/store/missing-relation.ts +38 -0
- package/src/elements/store/replica.ts +1 -4
- package/src/elements/store/resource-list-docs.fixture.ts +56 -0
- package/src/elements/store/resource-list-docs.test.ts +79 -0
- package/src/elements/store/resource.test.ts +253 -0
- package/src/elements/store/resource.ts +786 -0
- package/src/elements/store/runtime.ts +25 -28
- package/src/elements/store/schema-decl.test.ts +227 -0
- package/src/elements/store/schema-decl.ts +516 -0
- package/src/elements/store/sql-condition.test.ts +132 -0
- package/src/elements/store/sql-condition.ts +293 -65
- package/src/elements/store/sql-session.test.ts +135 -0
- package/src/elements/store/sql-session.ts +241 -84
- package/src/elements/store/table.ts +69 -23
- package/src/elements/store.test.ts +17 -39
- package/src/elements/store.ts +53 -31
- package/src/elements/vault/chain.ts +34 -0
- package/src/elements/vault/declare.ts +22 -32
- package/src/elements/vault/fingerprint.ts +2 -7
- package/src/elements/vault/redact.ts +2 -6
- package/src/elements/vault/runtime.ts +19 -33
- package/src/elements/vault.test.ts +12 -55
- package/src/elements/vault.ts +9 -13
- package/src/index.ts +5 -2
- package/src/kernel/adopt-routes.ts +15 -29
- package/src/kernel/app.ts +92 -118
- package/src/kernel/auth-resolve.ts +2 -9
- package/src/kernel/boot-bind/ai.ts +1 -4
- package/src/kernel/boot-bind/channel.test.ts +60 -0
- package/src/kernel/boot-bind/channel.ts +61 -5
- package/src/kernel/boot-bind/clock.ts +2 -6
- package/src/kernel/boot-bind/gate.ts +2 -8
- package/src/kernel/boot-bind/runs.ts +1 -3
- package/src/kernel/boot-bind/signal.ts +3 -10
- package/src/kernel/boot-bind/store.test.ts +27 -18
- package/src/kernel/boot-bind/store.ts +93 -52
- package/src/kernel/boot-bind/vault.ts +2 -8
- package/src/kernel/boot.test.ts +4 -10
- package/src/kernel/boot.ts +30 -77
- package/src/kernel/budget.test.ts +1 -3
- package/src/kernel/capability.ts +3 -10
- package/src/kernel/dry-run.test.ts +1 -4
- package/src/kernel/dry-run.ts +3 -11
- package/src/kernel/edge.test.ts +68 -0
- package/src/kernel/effects.test.ts +1 -5
- package/src/kernel/effects.ts +4 -11
- package/src/kernel/errors.registry.test.ts +2 -8
- package/src/kernel/errors.ts +23 -32
- package/src/kernel/flow.test.ts +12 -25
- package/src/kernel/flow.ts +13 -25
- package/src/kernel/fx.test.ts +31 -40
- package/src/kernel/fx.ts +135 -90
- package/src/kernel/hook-timing.test.ts +3 -6
- package/src/kernel/hook-timing.ts +5 -14
- package/src/kernel/hooks.test.ts +43 -10
- package/src/kernel/hooks.ts +25 -12
- package/src/kernel/index.ts +11 -17
- package/src/kernel/journal.ts +7 -31
- package/src/kernel/on.ts +45 -9
- package/src/kernel/pipeline.test.ts +10 -19
- package/src/kernel/pipeline.ts +5 -17
- package/src/kernel/plug.ts +4 -11
- package/src/kernel/plugin/conflicts.test.ts +13 -25
- package/src/kernel/plugin/decorate.test.ts +8 -13
- package/src/kernel/plugin/scoping.test.ts +4 -11
- package/src/kernel/plugin.ts +81 -27
- package/src/kernel/registry-isolation.test.ts +74 -0
- package/src/kernel/registry.ts +102 -30
- package/src/kernel/router.test.ts +15 -13
- package/src/kernel/router.ts +9 -29
- package/src/kernel/routing-budget.test.ts +1 -3
- package/src/kernel/run-telemetry.ts +1 -3
- package/src/kernel/triggers.ts +63 -21
- package/src/manifest/diff.test.ts +26 -91
- package/src/manifest/diff.ts +95 -225
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/manifest/index.ts +3 -10
- package/src/manifest/types.ts +30 -6
- package/src/manifest/undeclared.test.ts +4 -12
- package/src/manifest/undeclared.ts +1 -2
- package/src/manifest/validate.test.ts +2 -6
- package/src/manifest/validate.ts +5 -15
- package/src/mcp/authorization.ts +8 -27
- package/src/mcp/confirmation.ts +3 -10
- package/src/mcp/data.ts +2 -6
- package/src/mcp/docs-index.ts +4 -7
- package/src/mcp/docs-mcp.test.ts +3 -12
- package/src/mcp/docs-server.ts +7 -27
- package/src/mcp/docs-tools.ts +2 -6
- package/src/mcp/index.ts +1 -6
- package/src/mcp/injection.gate.test.ts +4 -18
- package/src/mcp/mcp.test.ts +18 -49
- package/src/mcp/protocol.ts +3 -10
- package/src/mcp/server.ts +11 -52
- package/src/mcp/session.ts +4 -14
- package/src/mcp/tools.ts +14 -50
- package/src/plugins/catalogue.test.ts +3 -7
- package/src/plugins/catalogue.ts +1 -3
- package/src/plugins/compression.test.ts +127 -0
- package/src/plugins/compression.ts +94 -0
- package/src/plugins/config-source.test.ts +204 -0
- package/src/plugins/config-source.ts +209 -0
- package/src/plugins/cors.test.ts +138 -0
- package/src/plugins/cors.ts +129 -0
- package/src/plugins/csrf.test.ts +102 -0
- package/src/plugins/csrf.ts +86 -0
- package/src/plugins/headers.ts +54 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/ip-allowlist.test.ts +105 -0
- package/src/plugins/ip-allowlist.ts +76 -0
- package/src/plugins/maintenance-mode.test.ts +91 -0
- package/src/plugins/maintenance-mode.ts +85 -0
- package/src/plugins/node-import-scan.ts +2 -5
- package/src/plugins/security-headers.test.ts +243 -0
- package/src/plugins/security-headers.ts +255 -0
- package/src/plugins/supply-chain.ts +5 -17
- package/src/release/exports.ts +3 -8
- package/src/release/measure.exports.test.ts +1 -3
- package/src/release/measure.ts +16 -45
- package/src/runs/bench.test.ts +1 -3
- package/src/runs/collect.ts +8 -25
- package/src/runs/drivers/clickhouse.ts +2 -9
- package/src/runs/drivers/files.ts +9 -31
- package/src/runs/drivers/memory.ts +5 -22
- package/src/runs/drivers/postgres.ts +8 -20
- package/src/runs/duckdb.ts +2 -8
- package/src/runs/index.ts +3 -14
- package/src/runs/outlier.ts +3 -10
- package/src/runs/parquet.ts +8 -26
- package/src/runs/privacy.ts +2 -8
- package/src/runs/runs.test.ts +3 -9
- package/src/runs/runtime.ts +7 -32
- package/src/runs/shred.ts +10 -29
- package/src/runs/types.ts +1 -4
- package/src/runtime/bun.ts +4 -15
- package/src/runtime/cold-start.bench.ts +1 -3
- package/src/runtime/dev-request-log.test.ts +1 -4
- package/src/runtime/dev-request-log.ts +1 -4
- package/src/runtime/index.ts +1 -6
- package/src/runtime/primitives.ts +5 -4
- package/src/runtime/security.ts +3 -13
- package/src/runtime/serve.test.ts +4 -7
- package/src/runtime/types.ts +1 -4
- package/src/runtime/web-standard.ts +3 -15
- package/src/term.test.ts +4 -6
- package/src/term.ts +49 -98
- package/src/test/create-test-app.test.ts +4 -14
- package/src/test/create-test-app.ts +27 -33
- package/src/test/provisions.integration.test.ts +3 -11
- package/src/upgrade/codemods.test.ts +25 -6
- package/src/upgrade/codemods.ts +116 -3
- package/src/validation/standard-schema.test.ts +2 -5
- package/src/validation/standard-schema.ts +13 -33
- package/docs/spec/console.md +0 -739
- package/docs/spec/example.md +0 -1196
- package/docs/spec/four-applications.md +0 -1195
- package/docs/spec/unified-theory.md +0 -489
- package/src/cli/doc-drift.test.ts +0 -146
- package/src/cli/doc-drift.ts +0 -430
- package/src/cli/doctor-diff-examples.ts +0 -92
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
- package/src/drivers/vault-sops.ts +0 -271
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official `maintenance-mode` plugin — drain HTTP traffic with one flag.
|
|
3
|
+
* Uses only the public plugin API (unified-theory §14).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { plugin, type PluginDef } from "../kernel/plugin.ts";
|
|
7
|
+
import {
|
|
8
|
+
isConfigSource,
|
|
9
|
+
pluginConfigSnapshot,
|
|
10
|
+
resolvePluginOptions,
|
|
11
|
+
withConfigTable,
|
|
12
|
+
type ConfigSource,
|
|
13
|
+
} from "./config-source.ts";
|
|
14
|
+
|
|
15
|
+
/** Options for {@link maintenanceMode}. */
|
|
16
|
+
export interface MaintenanceModeOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the mode is active. Default `true` — plugging the plugin turns
|
|
19
|
+
* it on. Drive it from the environment to flip without a code change:
|
|
20
|
+
* `enabled: process.env.MAINTENANCE_MODE === "1"`.
|
|
21
|
+
*/
|
|
22
|
+
readonly enabled?: boolean;
|
|
23
|
+
/** `Retry-After` value in seconds on the 503. Omitted unless provided. */
|
|
24
|
+
readonly retryAfter?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Path prefixes that keep serving while the mode is active (e.g.
|
|
27
|
+
* `["/health"]` so load-balancer checks stay green).
|
|
28
|
+
*/
|
|
29
|
+
readonly allowPaths?: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* Operator bypass: requests carrying this header name (any non-empty
|
|
32
|
+
* value) pass through. Presence-based — an ops convenience, not auth.
|
|
33
|
+
*/
|
|
34
|
+
readonly bypassHeader?: string;
|
|
35
|
+
/** Failure message in the envelope. Default `"Service is under maintenance."` */
|
|
36
|
+
readonly message?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Short-circuit every HTTP invocation at `onRequest` with a 503 failure
|
|
41
|
+
* envelope (`ServiceUnavailable`) plus an optional `Retry-After`. Excluded
|
|
42
|
+
* prefixes and the bypass header keep ops paths alive. The 503 still flows
|
|
43
|
+
* through `onResponse`, so response-shaping plugins (security headers,
|
|
44
|
+
* compression) apply to it too. Non-HTTP triggers no-op — clock flows and
|
|
45
|
+
* signal subscribers keep running.
|
|
46
|
+
*
|
|
47
|
+
* Accepts static options or a {@link ConfigSource} — the flagship use
|
|
48
|
+
* case: flip `enabled` from the database and drain traffic without a
|
|
49
|
+
* redeploy.
|
|
50
|
+
*
|
|
51
|
+
* @param options - Flag, retry hint, allow-list, bypass — or a config source
|
|
52
|
+
*/
|
|
53
|
+
export function maintenanceMode(
|
|
54
|
+
options: MaintenanceModeOptions | ConfigSource<MaintenanceModeOptions> = {},
|
|
55
|
+
): PluginDef {
|
|
56
|
+
const def = plugin("maintenance-mode", {
|
|
57
|
+
version: "0.0.2",
|
|
58
|
+
config: pluginConfigSnapshot(options),
|
|
59
|
+
}).hook("onRequest", (ctx) => {
|
|
60
|
+
const resolved = resolvePluginOptions(options);
|
|
61
|
+
if ((resolved.enabled ?? true) === false || !ctx.request) return;
|
|
62
|
+
const message = resolved.message ?? "Service is under maintenance.";
|
|
63
|
+
|
|
64
|
+
const { pathname } = new URL(ctx.request.url);
|
|
65
|
+
if (resolved.allowPaths?.some((prefix) => pathname.startsWith(prefix))) return;
|
|
66
|
+
|
|
67
|
+
const bypass = resolved.bypassHeader;
|
|
68
|
+
if (bypass !== undefined) {
|
|
69
|
+
const value = ctx.request.headers.get(bypass);
|
|
70
|
+
if (value !== null && value.length > 0) return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const headers: Record<string, string> = {};
|
|
74
|
+
if (resolved.retryAfter !== undefined) headers["retry-after"] = String(resolved.retryAfter);
|
|
75
|
+
return Response.json(
|
|
76
|
+
{
|
|
77
|
+
data: null,
|
|
78
|
+
error: { code: "ServiceUnavailable", data: { retryAfter: resolved.retryAfter }, message },
|
|
79
|
+
},
|
|
80
|
+
{ status: 503, headers },
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return isConfigSource(options) ? withConfigTable(def, options) : def;
|
|
85
|
+
}
|
|
@@ -49,9 +49,7 @@ export function scanNodeImportsBypassingFx(
|
|
|
49
49
|
out.push({
|
|
50
50
|
source: file.path,
|
|
51
51
|
specifier,
|
|
52
|
-
line: typeof node.start === "number"
|
|
53
|
-
? lineAt(file.source, node.start)
|
|
54
|
-
: null,
|
|
52
|
+
line: typeof node.start === "number" ? lineAt(file.source, node.start) : null,
|
|
55
53
|
});
|
|
56
54
|
});
|
|
57
55
|
}
|
|
@@ -77,8 +75,7 @@ function importSpecifier(node: AstNode): string | null {
|
|
|
77
75
|
};
|
|
78
76
|
const callee = call.callee;
|
|
79
77
|
const isRequire =
|
|
80
|
-
callee?.type === "Identifier" &&
|
|
81
|
-
(callee as { name?: string }).name === "require";
|
|
78
|
+
callee?.type === "Identifier" && (callee as { name?: string }).name === "require";
|
|
82
79
|
if (!isRequire) return null;
|
|
83
80
|
const arg0 = call.arguments?.[0];
|
|
84
81
|
if (arg0 && (arg0.type === "Literal" || arg0.type === "StringLiteral")) {
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `security-headers` plugin — defaults on success, presence on failure,
|
|
3
|
+
* and app-wins override semantics through the real pipeline.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
7
|
+
import { oke } from "../kernel/app.ts";
|
|
8
|
+
import { flow, resetFlowSeq } from "../kernel/flow.ts";
|
|
9
|
+
import { fail } from "../kernel/index.ts";
|
|
10
|
+
import { on, resetBindings } from "../kernel/on.ts";
|
|
11
|
+
import { http } from "../kernel/triggers.ts";
|
|
12
|
+
import { defaultCspDirectives, securityHeaders } from "./security-headers.ts";
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
resetBindings();
|
|
16
|
+
resetFlowSeq();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("securityHeaders plugin", () => {
|
|
20
|
+
test("sets the default trio on a successful response", async () => {
|
|
21
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
22
|
+
const app = oke({ name: "sec" }).plug(securityHeaders());
|
|
23
|
+
|
|
24
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
25
|
+
|
|
26
|
+
expect(res.status).toBe(200);
|
|
27
|
+
expect(res.headers.get("x-content-type-options")).toBe("nosniff");
|
|
28
|
+
expect(res.headers.get("x-frame-options")).toBe("DENY");
|
|
29
|
+
expect(res.headers.get("referrer-policy")).toBe("no-referrer");
|
|
30
|
+
expect(res.headers.get("content-security-policy")).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("helmet-parity defaults: agent cluster, dns prefetch, download, cross-domain, xss filter", async () => {
|
|
34
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
35
|
+
const app = oke({ name: "sec-parity" }).plug(securityHeaders());
|
|
36
|
+
|
|
37
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
38
|
+
|
|
39
|
+
expect(res.headers.get("origin-agent-cluster")).toBe("?1");
|
|
40
|
+
expect(res.headers.get("x-dns-prefetch-control")).toBe("off");
|
|
41
|
+
expect(res.headers.get("x-download-options")).toBe("noopen");
|
|
42
|
+
expect(res.headers.get("x-permitted-cross-domain-policies")).toBe("none");
|
|
43
|
+
expect(res.headers.get("x-xss-protection")).toBe("0");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("each parity default can be switched off", async () => {
|
|
47
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
48
|
+
const app = oke({ name: "sec-off" }).plug(
|
|
49
|
+
securityHeaders({
|
|
50
|
+
originAgentCluster: false,
|
|
51
|
+
dnsPrefetchControl: false,
|
|
52
|
+
downloadOptions: false,
|
|
53
|
+
xssProtection: false,
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
58
|
+
|
|
59
|
+
expect(res.headers.get("origin-agent-cluster")).toBeNull();
|
|
60
|
+
expect(res.headers.get("x-dns-prefetch-control")).toBeNull();
|
|
61
|
+
expect(res.headers.get("x-download-options")).toBeNull();
|
|
62
|
+
expect(res.headers.get("x-xss-protection")).toBeNull();
|
|
63
|
+
expect(res.headers.get("x-permitted-cross-domain-policies")).toBe("none");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("dnsPrefetchControl allow, custom cross-domain policy, COEP stamps when configured", async () => {
|
|
67
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
68
|
+
const app = oke({ name: "sec-tuned" }).plug(
|
|
69
|
+
securityHeaders({
|
|
70
|
+
dnsPrefetchControl: { allow: true },
|
|
71
|
+
permittedCrossDomainPolicies: "by-content-type",
|
|
72
|
+
crossOriginEmbedderPolicy: "require-corp",
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
77
|
+
|
|
78
|
+
expect(res.headers.get("x-dns-prefetch-control")).toBe("on");
|
|
79
|
+
expect(res.headers.get("x-permitted-cross-domain-policies")).toBe("by-content-type");
|
|
80
|
+
expect(res.headers.get("cross-origin-embedder-policy")).toBe("require-corp");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("x-powered-by is removed by default; a string sets a decoy; false keeps the app's", async () => {
|
|
84
|
+
const poweredByApp = (options: Parameters<typeof securityHeaders>[0], name: string) => {
|
|
85
|
+
on(
|
|
86
|
+
http.get("/x"),
|
|
87
|
+
flow({
|
|
88
|
+
name: "x.get",
|
|
89
|
+
do: () =>
|
|
90
|
+
new Response("{}", {
|
|
91
|
+
headers: { "content-type": "application/json", "x-powered-by": "Express" },
|
|
92
|
+
}),
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
return oke({ name }).plug(securityHeaders(options));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const removed = await poweredByApp({}, "sec-pb-off").fetch(new Request("http://localhost/x"));
|
|
99
|
+
expect(removed.headers.get("x-powered-by")).toBeNull();
|
|
100
|
+
|
|
101
|
+
const decoy = await poweredByApp(
|
|
102
|
+
{ poweredBy: "PHP 4.2.0", override: true },
|
|
103
|
+
"sec-pb-decoy",
|
|
104
|
+
).fetch(new Request("http://localhost/x"));
|
|
105
|
+
expect(decoy.headers.get("x-powered-by")).toBe("PHP 4.2.0");
|
|
106
|
+
|
|
107
|
+
const kept = await poweredByApp({ poweredBy: false }, "sec-pb-keep").fetch(
|
|
108
|
+
new Request("http://localhost/x"),
|
|
109
|
+
);
|
|
110
|
+
expect(kept.headers.get("x-powered-by")).toBe("Express");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("structured CSP merges over helmet's defaults, camelCase keys, report-only mode", async () => {
|
|
114
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
115
|
+
const app = oke({ name: "sec-csp-builder" }).plug(
|
|
116
|
+
securityHeaders({
|
|
117
|
+
contentSecurityPolicy: {
|
|
118
|
+
directives: { scriptSrc: ["'self'", "https://cdn.example.com"] },
|
|
119
|
+
reportOnly: true,
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
125
|
+
|
|
126
|
+
expect(res.headers.get("content-security-policy")).toBeNull();
|
|
127
|
+
const reportOnly = res.headers.get("content-security-policy-report-only");
|
|
128
|
+
expect(reportOnly).toContain("script-src 'self' https://cdn.example.com");
|
|
129
|
+
expect(reportOnly).toContain("default-src 'self'");
|
|
130
|
+
expect(reportOnly).toContain("upgrade-insecure-requests");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("structured CSP with useDefaults: false emits exactly the given directives", async () => {
|
|
134
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
135
|
+
const app = oke({ name: "sec-csp-bare" }).plug(
|
|
136
|
+
securityHeaders({
|
|
137
|
+
contentSecurityPolicy: { useDefaults: false, directives: { "default-src": ["'none'"] } },
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
142
|
+
expect(res.headers.get("content-security-policy")).toBe("default-src 'none'");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("defaultCspDirectives matches helmet's documented default policy", () => {
|
|
146
|
+
expect(defaultCspDirectives["upgrade-insecure-requests"]).toEqual([]);
|
|
147
|
+
expect(defaultCspDirectives["script-src-attr"]).toEqual(["'none'"]);
|
|
148
|
+
expect(Object.keys(defaultCspDirectives)).toHaveLength(11);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("headers land on failures too (onResponse runs after onError)", async () => {
|
|
152
|
+
on(
|
|
153
|
+
http.get("/deny"),
|
|
154
|
+
flow({ name: "x.deny", do: () => ({ ok: true }) }).hook("beforeHandle", () =>
|
|
155
|
+
fail("Forbidden", {}),
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
const app = oke({ name: "sec-fail" }).plug(securityHeaders());
|
|
159
|
+
|
|
160
|
+
const res = await app.fetch(new Request("http://localhost/deny"));
|
|
161
|
+
|
|
162
|
+
expect(res.status).not.toBe(200);
|
|
163
|
+
expect(res.headers.get("x-content-type-options")).toBe("nosniff");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("adds CSP only when configured", async () => {
|
|
167
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
168
|
+
const app = oke({ name: "sec-csp" }).plug(
|
|
169
|
+
securityHeaders({ contentSecurityPolicy: "default-src 'self'" }),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
173
|
+
|
|
174
|
+
expect(res.headers.get("content-security-policy")).toBe("default-src 'self'");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("HSTS is off by default", async () => {
|
|
178
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
179
|
+
const app = oke({ name: "sec-hsts-off" }).plug(securityHeaders());
|
|
180
|
+
|
|
181
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
182
|
+
|
|
183
|
+
expect(res.headers.get("strict-transport-security")).toBeNull();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("hsts: true stamps a one-year max-age", async () => {
|
|
187
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
188
|
+
const app = oke({ name: "sec-hsts-on" }).plug(securityHeaders({ hsts: true }));
|
|
189
|
+
|
|
190
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
191
|
+
|
|
192
|
+
expect(res.headers.get("strict-transport-security")).toBe("max-age=31536000");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("hsts object tunes max-age, subdomains, preload", async () => {
|
|
196
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
197
|
+
const app = oke({ name: "sec-hsts-tuned" }).plug(
|
|
198
|
+
securityHeaders({ hsts: { maxAge: 63072000, includeSubDomains: true, preload: true } }),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
202
|
+
|
|
203
|
+
expect(res.headers.get("strict-transport-security")).toBe(
|
|
204
|
+
"max-age=63072000; includeSubDomains; preload",
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("permissions-policy and cross-origin policies stamp when configured", async () => {
|
|
209
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
210
|
+
const app = oke({ name: "sec-extra" }).plug(
|
|
211
|
+
securityHeaders({
|
|
212
|
+
permissionsPolicy: "camera=(), microphone=()",
|
|
213
|
+
crossOriginOpenerPolicy: "same-origin",
|
|
214
|
+
crossOriginResourcePolicy: "same-site",
|
|
215
|
+
}),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
219
|
+
|
|
220
|
+
expect(res.headers.get("permissions-policy")).toBe("camera=(), microphone=()");
|
|
221
|
+
expect(res.headers.get("cross-origin-opener-policy")).toBe("same-origin");
|
|
222
|
+
expect(res.headers.get("cross-origin-resource-policy")).toBe("same-site");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("an explicit app-set value wins by default", async () => {
|
|
226
|
+
on(http.get("/x"), flow({ name: "x.get", do: () => ({ ok: true }) }));
|
|
227
|
+
const app = oke({ name: "sec-keep" });
|
|
228
|
+
app.hook("onResponse", (ctx) => {
|
|
229
|
+
if (!ctx.response) return;
|
|
230
|
+
const headers = new Headers(ctx.response.headers);
|
|
231
|
+
headers.set("x-frame-options", "SAMEORIGIN");
|
|
232
|
+
ctx.response = new Response(ctx.response.body, {
|
|
233
|
+
status: ctx.response.status,
|
|
234
|
+
headers,
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
app.plug(securityHeaders());
|
|
238
|
+
|
|
239
|
+
const res = await app.fetch(new Request("http://localhost/x"));
|
|
240
|
+
|
|
241
|
+
expect(res.headers.get("x-frame-options")).toBe("SAMEORIGIN");
|
|
242
|
+
});
|
|
243
|
+
});
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official `security-headers` plugin — the complete secure-headers set on
|
|
3
|
+
* every HTTP response (helmet parity, API-first defaults). Uses only the
|
|
4
|
+
* public plugin API (unified-theory §14).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { plugin, type PluginDef } from "../kernel/plugin.ts";
|
|
8
|
+
import {
|
|
9
|
+
isConfigSource,
|
|
10
|
+
pluginConfigSnapshot,
|
|
11
|
+
resolvePluginOptions,
|
|
12
|
+
withConfigTable,
|
|
13
|
+
type ConfigSource,
|
|
14
|
+
} from "./config-source.ts";
|
|
15
|
+
import { setUnlessPresent, withHeaders } from "./headers.ts";
|
|
16
|
+
|
|
17
|
+
/** HSTS value options (see {@link SecurityHeadersOptions.hsts}). */
|
|
18
|
+
export interface HstsOptions {
|
|
19
|
+
/** `max-age` in seconds. Default `31536000` (one year). */
|
|
20
|
+
readonly maxAge?: number;
|
|
21
|
+
/** Append `includeSubDomains`. Default `false`. */
|
|
22
|
+
readonly includeSubDomains?: boolean;
|
|
23
|
+
/** Append `preload`. Only meaningful with subdomains + a ≥1-year max-age. */
|
|
24
|
+
readonly preload?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Structured CSP (see {@link SecurityHeadersOptions.contentSecurityPolicy}). */
|
|
28
|
+
export interface CspOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Directive map — kebab-case (`script-src`) or camelCase (`scriptSrc`)
|
|
31
|
+
* keys, values as string arrays. An empty array emits a bare directive
|
|
32
|
+
* (e.g. `upgrade-insecure-requests`).
|
|
33
|
+
*/
|
|
34
|
+
readonly directives: Readonly<Record<string, readonly string[]>>;
|
|
35
|
+
/** Merge over {@link defaultCspDirectives}. Default `true`. */
|
|
36
|
+
readonly useDefaults?: boolean;
|
|
37
|
+
/** Emit as `Content-Security-Policy-Report-Only`. Default `false`. */
|
|
38
|
+
readonly reportOnly?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Helmet's default CSP — the baseline `directives` merge over unless
|
|
43
|
+
* `useDefaults: false`.
|
|
44
|
+
*/
|
|
45
|
+
export const defaultCspDirectives: Readonly<Record<string, readonly string[]>> = {
|
|
46
|
+
"default-src": ["'self'"],
|
|
47
|
+
"base-uri": ["'self'"],
|
|
48
|
+
"font-src": ["'self'", "https:", "data:"],
|
|
49
|
+
"form-action": ["'self'"],
|
|
50
|
+
"frame-ancestors": ["'self'"],
|
|
51
|
+
"img-src": ["'self'", "data:"],
|
|
52
|
+
"object-src": ["'none'"],
|
|
53
|
+
"script-src": ["'self'"],
|
|
54
|
+
"script-src-attr": ["'none'"],
|
|
55
|
+
"style-src": ["'self'", "https:", "'unsafe-inline'"],
|
|
56
|
+
"upgrade-insecure-requests": [],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Options for {@link securityHeaders}. */
|
|
60
|
+
export interface SecurityHeadersOptions {
|
|
61
|
+
/**
|
|
62
|
+
* Content-Security-Policy — a raw header string, or a structured
|
|
63
|
+
* {@link CspOptions} (directive builder over helmet's defaults, optional
|
|
64
|
+
* report-only mode). Omitted unless provided.
|
|
65
|
+
*/
|
|
66
|
+
readonly contentSecurityPolicy?: string | CspOptions;
|
|
67
|
+
/** X-Frame-Options value. Default `"DENY"`. */
|
|
68
|
+
readonly frameOptions?: "DENY" | "SAMEORIGIN";
|
|
69
|
+
/** Referrer-Policy value. Default `"no-referrer"`. */
|
|
70
|
+
readonly referrerPolicy?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Strict-Transport-Security. `true` → one-year `max-age`; pass an object
|
|
73
|
+
* to tune. Default off — HSTS is sticky, and local dev is plain HTTP, so
|
|
74
|
+
* enable it deliberately for HTTPS deployments.
|
|
75
|
+
*/
|
|
76
|
+
readonly hsts?: boolean | HstsOptions;
|
|
77
|
+
/** Permissions-Policy value (e.g. `"camera=(), microphone=()"`). Omitted unless provided. */
|
|
78
|
+
readonly permissionsPolicy?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Cross-Origin-Opener-Policy value. Omitted unless provided — opt-in
|
|
81
|
+
* because OKE serves APIs, where cross-origin clients are legitimate
|
|
82
|
+
* (helmet targets web pages and defaults it on).
|
|
83
|
+
*/
|
|
84
|
+
readonly crossOriginOpenerPolicy?: "same-origin" | "same-origin-allow-popups" | "unsafe-none";
|
|
85
|
+
/** Cross-Origin-Resource-Policy value. Omitted unless provided (same API rationale). */
|
|
86
|
+
readonly crossOriginResourcePolicy?: "same-origin" | "same-site" | "cross-origin";
|
|
87
|
+
/** Cross-Origin-Embedder-Policy value. Omitted unless provided (helmet also defaults it off). */
|
|
88
|
+
readonly crossOriginEmbedderPolicy?: "require-corp" | "credentialless";
|
|
89
|
+
/** Origin-Agent-Cluster header. Default `true` → `?1`. */
|
|
90
|
+
readonly originAgentCluster?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* X-DNS-Prefetch-Control. Default `true` → `off` (privacy-preserving);
|
|
93
|
+
* `{ allow: true }` → `on`; `false` omits the header.
|
|
94
|
+
*/
|
|
95
|
+
readonly dnsPrefetchControl?: boolean | { readonly allow: boolean };
|
|
96
|
+
/** X-Download-Options: noopen (legacy IE8 mitigation, helmet parity). Default `true`. */
|
|
97
|
+
readonly downloadOptions?: boolean;
|
|
98
|
+
/** X-Permitted-Cross-Domain-Policies value. Default `"none"`. */
|
|
99
|
+
readonly permittedCrossDomainPolicies?: "none" | "master-only" | "by-content-type" | "all";
|
|
100
|
+
/**
|
|
101
|
+
* X-Powered-By handling. Default `true` → remove the header (it leaks
|
|
102
|
+
* framework fingerprints). A string sets a custom value; `false` keeps it.
|
|
103
|
+
*/
|
|
104
|
+
readonly poweredBy?: boolean | string;
|
|
105
|
+
/**
|
|
106
|
+
* X-XSS-Protection: 0 — disables the legacy, buggy browser XSS auditor
|
|
107
|
+
* (helmet parity; modern defense is CSP). Default `true`.
|
|
108
|
+
*/
|
|
109
|
+
readonly xssProtection?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Replace headers the app already set. Default `false` — an explicit
|
|
112
|
+
* app-level value always wins.
|
|
113
|
+
*/
|
|
114
|
+
readonly override?: boolean;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Render the Strict-Transport-Security header value. */
|
|
118
|
+
function hstsValue(hsts: boolean | HstsOptions): string {
|
|
119
|
+
const opts: HstsOptions = typeof hsts === "object" ? hsts : {};
|
|
120
|
+
let value = `max-age=${opts.maxAge ?? 31536000}`;
|
|
121
|
+
if (opts.includeSubDomains) value += "; includeSubDomains";
|
|
122
|
+
if (opts.preload) value += "; preload";
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** camelCase directive keys → kebab-case (`scriptSrc` → `script-src`). */
|
|
127
|
+
function directiveName(name: string): string {
|
|
128
|
+
return name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Build a CSP header value from a directive map. */
|
|
132
|
+
function buildCsp(directives: Readonly<Record<string, readonly string[]>>): string {
|
|
133
|
+
return Object.entries(directives)
|
|
134
|
+
.map(([name, values]) => {
|
|
135
|
+
const kebab = directiveName(name);
|
|
136
|
+
return values.length === 0 ? kebab : `${kebab} ${values.join(" ")}`;
|
|
137
|
+
})
|
|
138
|
+
.join("; ");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Resolve the CSP option into header name + value. */
|
|
142
|
+
function cspEntry(option: string | CspOptions): { readonly name: string; readonly value: string } {
|
|
143
|
+
if (typeof option === "string") return { name: "content-security-policy", value: option };
|
|
144
|
+
const useDefaults = option.useDefaults ?? true;
|
|
145
|
+
const merged: Record<string, readonly string[]> = useDefaults
|
|
146
|
+
? { ...defaultCspDirectives, ...option.directives }
|
|
147
|
+
: { ...option.directives };
|
|
148
|
+
return {
|
|
149
|
+
name:
|
|
150
|
+
(option.reportOnly ?? false)
|
|
151
|
+
? "content-security-policy-report-only"
|
|
152
|
+
: "content-security-policy",
|
|
153
|
+
value: buildCsp(merged),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Apply the complete secure-headers set to every HTTP response, including
|
|
159
|
+
* failures (runs at `onResponse`, which fires after `onError` too). Covers
|
|
160
|
+
* every helmet.js middleware; API-first deviations (opt-in HSTS, COOP,
|
|
161
|
+
* CORP) are documented per option.
|
|
162
|
+
*
|
|
163
|
+
* Accepts static options or a {@link ConfigSource} for DB-driven config
|
|
164
|
+
* (e.g. flip `hsts` from the database without a redeploy).
|
|
165
|
+
*
|
|
166
|
+
* @param options - Header values, or a config source
|
|
167
|
+
*/
|
|
168
|
+
export function securityHeaders(
|
|
169
|
+
options: SecurityHeadersOptions | ConfigSource<SecurityHeadersOptions> = {},
|
|
170
|
+
): PluginDef {
|
|
171
|
+
const def = plugin("security-headers", {
|
|
172
|
+
version: "0.1.0",
|
|
173
|
+
config: pluginConfigSnapshot(options),
|
|
174
|
+
}).hook("onResponse", (ctx) => {
|
|
175
|
+
if (!ctx.response) return;
|
|
176
|
+
const resolved = resolvePluginOptions(options);
|
|
177
|
+
const override = resolved.override ?? false;
|
|
178
|
+
|
|
179
|
+
ctx.response = withHeaders(ctx.response, (headers) => {
|
|
180
|
+
setUnlessPresent(headers, "x-content-type-options", "nosniff", override);
|
|
181
|
+
setUnlessPresent(headers, "x-frame-options", resolved.frameOptions ?? "DENY", override);
|
|
182
|
+
setUnlessPresent(
|
|
183
|
+
headers,
|
|
184
|
+
"referrer-policy",
|
|
185
|
+
resolved.referrerPolicy ?? "no-referrer",
|
|
186
|
+
override,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (resolved.originAgentCluster ?? true) {
|
|
190
|
+
setUnlessPresent(headers, "origin-agent-cluster", "?1", override);
|
|
191
|
+
}
|
|
192
|
+
const dns = resolved.dnsPrefetchControl ?? true;
|
|
193
|
+
if (dns !== false) {
|
|
194
|
+
const allow = typeof dns === "object" ? dns.allow : false;
|
|
195
|
+
setUnlessPresent(headers, "x-dns-prefetch-control", allow ? "on" : "off", override);
|
|
196
|
+
}
|
|
197
|
+
if (resolved.downloadOptions ?? true) {
|
|
198
|
+
setUnlessPresent(headers, "x-download-options", "noopen", override);
|
|
199
|
+
}
|
|
200
|
+
setUnlessPresent(
|
|
201
|
+
headers,
|
|
202
|
+
"x-permitted-cross-domain-policies",
|
|
203
|
+
resolved.permittedCrossDomainPolicies ?? "none",
|
|
204
|
+
override,
|
|
205
|
+
);
|
|
206
|
+
if (resolved.xssProtection ?? true) {
|
|
207
|
+
setUnlessPresent(headers, "x-xss-protection", "0", override);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const poweredBy = resolved.poweredBy ?? true;
|
|
211
|
+
if (poweredBy === true) {
|
|
212
|
+
headers.delete("x-powered-by");
|
|
213
|
+
} else if (typeof poweredBy === "string") {
|
|
214
|
+
setUnlessPresent(headers, "x-powered-by", poweredBy, override);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (resolved.contentSecurityPolicy !== undefined) {
|
|
218
|
+
const csp = cspEntry(resolved.contentSecurityPolicy);
|
|
219
|
+
setUnlessPresent(headers, csp.name, csp.value, override);
|
|
220
|
+
}
|
|
221
|
+
if (resolved.hsts !== undefined && resolved.hsts !== false) {
|
|
222
|
+
setUnlessPresent(headers, "strict-transport-security", hstsValue(resolved.hsts), override);
|
|
223
|
+
}
|
|
224
|
+
if (resolved.permissionsPolicy !== undefined) {
|
|
225
|
+
setUnlessPresent(headers, "permissions-policy", resolved.permissionsPolicy, override);
|
|
226
|
+
}
|
|
227
|
+
if (resolved.crossOriginOpenerPolicy !== undefined) {
|
|
228
|
+
setUnlessPresent(
|
|
229
|
+
headers,
|
|
230
|
+
"cross-origin-opener-policy",
|
|
231
|
+
resolved.crossOriginOpenerPolicy,
|
|
232
|
+
override,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (resolved.crossOriginResourcePolicy !== undefined) {
|
|
236
|
+
setUnlessPresent(
|
|
237
|
+
headers,
|
|
238
|
+
"cross-origin-resource-policy",
|
|
239
|
+
resolved.crossOriginResourcePolicy,
|
|
240
|
+
override,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (resolved.crossOriginEmbedderPolicy !== undefined) {
|
|
244
|
+
setUnlessPresent(
|
|
245
|
+
headers,
|
|
246
|
+
"cross-origin-embedder-policy",
|
|
247
|
+
resolved.crossOriginEmbedderPolicy,
|
|
248
|
+
override,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
return isConfigSource(options) ? withConfigTable(def, options) : def;
|
|
255
|
+
}
|
|
@@ -248,9 +248,7 @@ export function projectSupplyChainSync(
|
|
|
248
248
|
};
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
function projectLifecycle(
|
|
252
|
-
pkg: PackageJsonProbe | null,
|
|
253
|
-
): LifecycleScriptsSignal {
|
|
251
|
+
function projectLifecycle(pkg: PackageJsonProbe | null): LifecycleScriptsSignal {
|
|
254
252
|
if (!pkg) {
|
|
255
253
|
return {
|
|
256
254
|
state: "unknown",
|
|
@@ -335,9 +333,7 @@ function projectNodeScan(
|
|
|
335
333
|
};
|
|
336
334
|
}
|
|
337
335
|
|
|
338
|
-
async function projectProvenance(
|
|
339
|
-
options: ProjectSupplyChainOptions,
|
|
340
|
-
): Promise<NpmProvenanceSignal> {
|
|
336
|
+
async function projectProvenance(options: ProjectSupplyChainOptions): Promise<NpmProvenanceSignal> {
|
|
341
337
|
if (options.provenance === "attested") {
|
|
342
338
|
return { state: "pass", detail: "npm provenance attestation present" };
|
|
343
339
|
}
|
|
@@ -385,10 +381,7 @@ function projectBootConflicts(
|
|
|
385
381
|
};
|
|
386
382
|
}
|
|
387
383
|
|
|
388
|
-
async function readPackageJson(
|
|
389
|
-
cwd: string,
|
|
390
|
-
packageName: string,
|
|
391
|
-
): Promise<PackageJsonProbe | null> {
|
|
384
|
+
async function readPackageJson(cwd: string, packageName: string): Promise<PackageJsonProbe | null> {
|
|
392
385
|
const path = resolve(cwd, "node_modules", ...packageName.split("/"), "package.json");
|
|
393
386
|
try {
|
|
394
387
|
const file = Bun.file(path);
|
|
@@ -422,9 +415,7 @@ async function fetchPublishedAt(
|
|
|
422
415
|
}
|
|
423
416
|
}
|
|
424
417
|
|
|
425
|
-
async function fetchNpmProvenance(
|
|
426
|
-
packageName: string,
|
|
427
|
-
): Promise<NpmProvenanceSignal> {
|
|
418
|
+
async function fetchNpmProvenance(packageName: string): Promise<NpmProvenanceSignal> {
|
|
428
419
|
try {
|
|
429
420
|
const res = await fetch(`https://registry.npmjs.org/${packageName}`, {
|
|
430
421
|
headers: { Accept: "application/json" },
|
|
@@ -437,10 +428,7 @@ async function fetchNpmProvenance(
|
|
|
437
428
|
}
|
|
438
429
|
const body = (await res.json()) as {
|
|
439
430
|
"dist-tags"?: { latest?: string };
|
|
440
|
-
versions?: Record<
|
|
441
|
-
string,
|
|
442
|
-
{ dist?: { attestations?: unknown; signatures?: unknown } }
|
|
443
|
-
>;
|
|
431
|
+
versions?: Record<string, { dist?: { attestations?: unknown; signatures?: unknown } }>;
|
|
444
432
|
};
|
|
445
433
|
const latest = body["dist-tags"]?.latest;
|
|
446
434
|
const dist = latest ? body.versions?.[latest]?.dist : undefined;
|