@jimhoyd/urlcode 0.4.2 → 0.4.7
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/.claude/skills/urlcode-authoring/SKILL.md +57 -14
- package/.claude/skills/urlcode-operations/SKILL.md +4 -0
- package/README.md +42 -18
- package/SECURITY.md +5 -3
- package/dist/BUILD-MANIFEST.json +30 -27
- package/dist/agent-context.js +82 -0
- package/dist/agents-guide.js +42 -42
- package/dist/authoring.js +12 -2
- package/dist/body-schema.js +159 -0
- package/dist/build-cloudflare.js +2 -0
- package/dist/capabilities.js +1 -1
- package/dist/cli.js +28 -12
- package/dist/config.js +74 -6
- package/dist/context.js +4 -6
- package/dist/errors.js +3 -1
- package/dist/examples.js +1 -1
- package/dist/extensions.js +60 -2
- package/dist/http-policy.js +19 -4
- package/dist/http-response.js +2 -2
- package/dist/init-with.js +71 -9
- package/dist/mcp.js +15 -2
- package/dist/pattern-guard.js +32 -0
- package/dist/policies/security.js +0 -0
- package/dist/project-tests.js +35 -11
- package/dist/readiness.js +184 -32
- package/dist/recipes.js +1 -1
- package/dist/router.js +17 -0
- package/dist/runtime.js +26 -3
- package/dist/scaffold.js +0 -0
- package/dist/server.js +26 -3
- package/dist/site.js +0 -0
- package/dist/tooling.js +2 -1
- package/dist/types/agent-context.d.ts +44 -0
- package/dist/types/authoring.d.ts +3 -1
- package/dist/types/body-schema.d.ts +58 -0
- package/dist/types/config.d.ts +10 -2
- package/dist/types/context.d.ts +1 -1
- package/dist/types/errors.d.ts +9 -1
- package/dist/types/examples.d.ts +1 -1
- package/dist/types/extensions.d.ts +47 -2
- package/dist/types/http-policy.d.ts +3 -0
- package/dist/types/init-with.d.ts +10 -1
- package/dist/types/pattern-guard.d.ts +10 -0
- package/dist/types/project-tests.d.ts +9 -0
- package/dist/types/readiness.d.ts +68 -0
- package/dist/types/recipes.d.ts +1 -1
- package/dist/types/runtime.d.ts +4 -0
- package/dist/types/server.d.ts +9 -1
- package/dist/types/tooling.d.ts +1 -0
- package/dist/types/types.d.ts +13 -0
- package/dist/types.js +10 -2
- package/dist/typescript-authoring.js +5 -3
- package/dist/verify-deployment.js +16 -10
- package/examples/body-validation/README.md +16 -0
- package/examples/body-validation/example.yaml +17 -0
- package/examples/body-validation/tests/requests.json +15 -0
- package/examples/body-validation/urlcode.yaml +34 -0
- package/examples/coverage-waiver/README.md +8 -0
- package/examples/coverage-waiver/example.yaml +16 -0
- package/examples/coverage-waiver/functions/notes.mjs +2 -0
- package/examples/coverage-waiver/tests/requests.json +3 -0
- package/examples/coverage-waiver/urlcode.yaml +10 -0
- package/examples/data-dir/README.md +39 -0
- package/examples/data-dir/data/welcome.txt +1 -0
- package/examples/data-dir/example.yaml +22 -0
- package/examples/data-dir/functions/note.mjs +18 -0
- package/examples/data-dir/tests/requests.json +6 -0
- package/examples/data-dir/urlcode.yaml +6 -0
- package/examples/lifecycle/README.md +15 -0
- package/examples/lifecycle/example.yaml +19 -0
- package/examples/lifecycle/functions/notes.mjs +29 -0
- package/examples/lifecycle/tests/requests.json +12 -0
- package/examples/lifecycle/urlcode.yaml +29 -0
- package/examples/not-found/README.md +10 -0
- package/examples/not-found/example.yaml +17 -0
- package/examples/not-found/public/404.html +3 -0
- package/examples/not-found/public/index.html +3 -0
- package/examples/not-found/tests/requests.json +7 -0
- package/examples/not-found/urlcode.yaml +7 -0
- package/examples/shared-blocks/README.md +11 -0
- package/examples/shared-blocks/example.yaml +17 -0
- package/examples/shared-blocks/tests/requests.json +7 -0
- package/examples/shared-blocks/urlcode.yaml +37 -0
- package/llms-full.txt +364 -49
- package/llms.txt +44 -6
- package/package.json +15 -26
- package/recipes/static-page/README.md +9 -0
- package/recipes/static-page/public/index.html +11 -0
- package/recipes/static-page/recipe.yaml +21 -0
- package/recipes/static-page/tests/requests.json +22 -0
- package/recipes/static-page/urlcode.yaml +7 -0
- package/recipes/static-plus-api/README.md +6 -0
- package/recipes/static-plus-api/urlcode.yaml +4 -0
- package/recipes/store-crud/README.md +53 -0
- package/recipes/store-crud/recipe.yaml +31 -0
- package/recipes/store-crud/tests/requests.json +18 -0
- package/recipes/store-crud/urlcode.yaml +18 -0
- package/schemas/urlcode.schema.json +125 -60
- package/skills/urlcode/SKILL.md +47 -27
- package/starters/default/AGENTS.md +43 -43
- package/starters/page/README.md +14 -0
- package/starters/page/public/index.html +12 -0
- package/starters/page/tests/requests.json +17 -0
- package/starters/page/urlcode.yaml +6 -0
- package/.claude-plugin/marketplace.json +0 -18
- package/CONTRIBUTING.md +0 -148
- package/ROADMAP.md +0 -70
- package/docs/AI-AUTHORING.md +0 -339
- package/docs/ASSETS.md +0 -107
- package/docs/AUTH-BACKUP.md +0 -32
- package/docs/AWS.md +0 -86
- package/docs/BEST-PRACTICES.md +0 -276
- package/docs/BULK.md +0 -79
- package/docs/CAPABILITIES.md +0 -192
- package/docs/CAPACITY.md +0 -305
- package/docs/CI-FOLLOWUP-2026-09-19.md +0 -97
- package/docs/CI-RELEASE-AUDIT-2026-09-19.md +0 -322
- package/docs/CI.md +0 -147
- package/docs/CLOUDFLARE.md +0 -109
- package/docs/CODEBASE-AUDIT-2026-09-20.md +0 -284
- package/docs/COMPLIANCE.md +0 -239
- package/docs/COMPOSING-A-SITE.md +0 -287
- package/docs/CONDITIONS.md +0 -74
- package/docs/CONTAINER-PROMOTION.md +0 -74
- package/docs/DEPLOYMENT-CHECKS.md +0 -108
- package/docs/DEVELOPMENT-PIPELINE.md +0 -304
- package/docs/EGRESS.md +0 -125
- package/docs/EXTENSIONS.md +0 -403
- package/docs/FRAMEWORK.md +0 -217
- package/docs/FUNCTION-SECURITY.md +0 -251
- package/docs/HTTP.md +0 -129
- package/docs/INSTALL.md +0 -128
- package/docs/INTERCHANGE.md +0 -134
- package/docs/LOAD-TESTING.md +0 -91
- package/docs/LOCAL-DEVELOPMENT.md +0 -102
- package/docs/MIDDLEWARE-EXAMPLES.md +0 -75
- package/docs/MIDDLEWARE.md +0 -102
- package/docs/MONITORING.md +0 -115
- package/docs/OBSERVABILITY.md +0 -222
- package/docs/OPEN-DECISIONS.md +0 -149
- package/docs/OPERATIONAL-PROOF.md +0 -41
- package/docs/OPERATIONS.md +0 -201
- package/docs/ORGANIZATION.md +0 -135
- package/docs/PERFORMANCE.md +0 -72
- package/docs/PLUGINS.md +0 -271
- package/docs/POLICIES.md +0 -211
- package/docs/PRERENDER.md +0 -245
- package/docs/PROJECT-DIRECTION.md +0 -118
- package/docs/PROVIDER-VERIFICATION.md +0 -84
- package/docs/READINESS.md +0 -153
- package/docs/README.md +0 -93
- package/docs/RECIPES.md +0 -99
- package/docs/RELEASE-0.4.0-alpha.3.md +0 -50
- package/docs/RELEASE-0.4.1.md +0 -73
- package/docs/RELEASE-0.4.2.md +0 -30
- package/docs/RELEASE-READINESS.md +0 -146
- package/docs/RELEASE-SECURITY.md +0 -100
- package/docs/RESILIENCE.md +0 -161
- package/docs/ROUTING.md +0 -92
- package/docs/SANDBOX-REVIEW.md +0 -72
- package/docs/SCAFFOLDING.md +0 -70
- package/docs/SECURITY-AUDIT.md +0 -164
- package/docs/SITE.md +0 -150
- package/docs/SPECIFICATION.md +0 -359
- package/docs/SPIKE-AI-FRAMEWORK-BENCHMARK.md +0 -288
- package/docs/SPIKE-BUSINESS-SUITE.md +0 -1029
- package/docs/SPIKE-CORE-LAYERING.md +0 -368
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +0 -207
- package/docs/STANDARDS.md +0 -311
- package/docs/STARTERS.md +0 -83
- package/docs/STATIC.md +0 -105
- package/docs/TOOLING.md +0 -298
- package/docs/TUNNELS.md +0 -72
- package/docs/TYPESCRIPT-AUTHORING.md +0 -87
- package/docs/TYPESCRIPT.md +0 -123
- package/docs/VERCEL.md +0 -114
- package/docs/VERSION-ALIGNMENT.md +0 -88
- package/docs/YAML-GUIDE.md +0 -57
- package/docs/YAML-REFERENCE.md +0 -449
- package/docs/archive/2026-09-19/EXTENSION-IMPLEMENTATION.md +0 -68
- package/docs/archive/2026-09-19/MANAGEMENT-SECURITY.md +0 -102
- package/docs/archive/2026-09-19/NEXT-PHASE-PLAN.md +0 -108
- package/docs/archive/2026-09-19/NEXT-STEPS.md +0 -646
- package/docs/archive/2026-09-19/OPEN-DECISIONS.md +0 -277
- package/docs/archive/2026-09-19/RELEASE-SECURITY.md +0 -186
- package/docs/archive/2026-09-19/ROADMAP.md +0 -387
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +0 -430
- package/docs/archive/2026-09-19/SPIKE-EXTENSIONS.md +0 -492
- package/docs/archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md +0 -365
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +0 -778
- package/docs/archive/2026-09-19/USABILITY-REVIEW.md +0 -139
- package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +0 -116
- package/docs/archive/README.md +0 -29
- package/docs/policies/agents.md +0 -182
- package/docs/policies/cache.md +0 -152
- package/docs/policies/compression.md +0 -169
- package/docs/policies/contract.md +0 -52
- package/docs/policies/hardened.md +0 -56
- package/docs/policies/interoperability.md +0 -169
- package/docs/policies/operations.md +0 -45
- package/docs/policies/security.md +0 -161
- package/docs/policies/throttle.md +0 -103
- package/docs/yaml/assets.md +0 -36
- package/docs/yaml/conditions.md +0 -20
- package/docs/yaml/functions.md +0 -168
- package/docs/yaml/middleware.md +0 -31
- package/docs/yaml/organization.md +0 -74
- package/docs/yaml/policies.md +0 -37
- package/docs/yaml/redirects.md +0 -64
- package/docs/yaml/responses.md +0 -57
- package/docs/yaml/site.md +0 -24
- package/packaging/claude-plugin/.claude-plugin/plugin.json +0 -19
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +0 -130
- package/packaging/claude-plugin/skills/urlcode-operations/SKILL.md +0 -108
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
<!-- trust-model-prose: historical-file -->
|
|
2
|
-
<!-- This report quotes the defective wording it is reporting -- including the
|
|
3
|
-
pre-trusted-default claims in finding 4 -- so the prose gate would read the
|
|
4
|
-
quotations as the guidance itself. The file is a dated review of one
|
|
5
|
-
commit and is not edited as the defects are fixed. -->
|
|
6
|
-
|
|
7
|
-
# Codebase, tooling and documentation audit — 2026-09-20
|
|
8
|
-
|
|
9
|
-
Reviewed commit: `bca8ac7` (core 0.4.0-alpha.2, auth 0.1.0-alpha.5,
|
|
10
|
-
admin 0.1.0-alpha.4, UI 0.1.0-alpha.6). Local environment: macOS,
|
|
11
|
-
Node 26.8.2. Date is UTC. This report records findings; it does not change
|
|
12
|
-
runtime behavior or remove supported features.
|
|
13
|
-
|
|
14
|
-
The trusted default is implemented correctly in the dispatch paths inspected:
|
|
15
|
-
only a route declaring `sandbox: true` enters the QuickJS pool. Ordinary
|
|
16
|
-
project functions and middleware run in Node. Host access by trusted code is
|
|
17
|
-
intentional, not an audit finding. Binding grants scope injected context;
|
|
18
|
-
they do not confine trusted code's independent filesystem, environment or
|
|
19
|
-
network access. Opt-in sandbox isolation and provider refusals remain necessary.
|
|
20
|
-
|
|
21
|
-
## Scope and method
|
|
22
|
-
|
|
23
|
-
Inventoried 966 tracked files. Reviewed the implemented specification,
|
|
24
|
-
contribution/security instructions, runtime activation/dispatch, module loading,
|
|
25
|
-
TypeScript authoring, inspection/manifests/diffs, extension hooks, workspace
|
|
26
|
-
contracts, package/build/check scripts, CI/release configuration, starter,
|
|
27
|
-
example/recipe coverage and documentation indexes. Used targeted source review,
|
|
28
|
-
the complete local verification suite and synthetic reproductions. This is a
|
|
29
|
-
repository-wide engineering audit, not a claim that every line or execution
|
|
30
|
-
path received independent security review.
|
|
31
|
-
|
|
32
|
-
Scanned tracked non-archive/non-spike Markdown for local links and YAML fences:
|
|
33
|
-
64 YAML fences, 49 route/project-shaped candidates. Added only an omitted
|
|
34
|
-
format version or the documented route wrapper for schema checks. Classified
|
|
35
|
-
partial illustrations separately from complete examples. Semantic constraints,
|
|
36
|
-
external services and prose fragments require more than schema validation.
|
|
37
|
-
|
|
38
|
-
Searched existing repository issues before filing. Five new issues and new
|
|
39
|
-
evidence on existing issue 168 retain all actionable findings below.
|
|
40
|
-
|
|
41
|
-
## Findings, ordered by practical impact
|
|
42
|
-
|
|
43
|
-
### 1. Inspection hides middleware execution-mode changes — P2
|
|
44
|
-
|
|
45
|
-
[Issue 199](https://github.com/jimhoyd-com/urlcode/issues/199).
|
|
46
|
-
|
|
47
|
-
For a `respond` route with `middleware: [mw.mjs]`, switching `sandbox` from
|
|
48
|
-
true to false produces identical manifest route records and an empty route
|
|
49
|
-
diff. This changes execution from QuickJS to full Node without appearing in
|
|
50
|
-
those review surfaces. The overall manifest digest can change; it does not
|
|
51
|
-
explain the change to reviewers.
|
|
52
|
-
|
|
53
|
-
`src/explain.ts` adds sandbox information only inside the function-handler
|
|
54
|
-
case. `src/manifest.ts` carries that handler object forward, while
|
|
55
|
-
`src/readiness.ts` and `src/route-diff.ts` omit execution mode from inventory.
|
|
56
|
-
Context already exposes it at route level.
|
|
57
|
-
|
|
58
|
-
Reproduction: create the same middleware/native route in both modes, call
|
|
59
|
-
`buildManifest`, obtain `createRuntime(...).testPlan()`, then call `diffRoutes`.
|
|
60
|
-
Observed diff: `{"added":[],"removed":[],"changed":[]}`. Add route-level
|
|
61
|
-
execution mode/reason to explain, manifest and inventory, with compatibility
|
|
62
|
-
handling for older inventory files and regressions for both handler kinds.
|
|
63
|
-
|
|
64
|
-
### 2. Auth/admin hook reactivation serves stale entry code — P2
|
|
65
|
-
|
|
66
|
-
[Issue 198](https://github.com/jimhoyd-com/urlcode/issues/198).
|
|
67
|
-
|
|
68
|
-
Both `packages/auth/src/lifecycle-hooks.ts` and
|
|
69
|
-
`packages/admin/src/admin-hooks.ts` import an unchanged file URL. Loading a
|
|
70
|
-
hook, editing its entry file, then loading it again in the same process returns
|
|
71
|
-
the old hook. A synthetic decision changed on disk from allow/v1 to deny/v2;
|
|
72
|
-
both loaders still returned allow/v1 after reactivation.
|
|
73
|
-
|
|
74
|
-
Native trusted routes already give entry modules a new URL per activation.
|
|
75
|
-
Apply an explicit hook reload policy and test it, or clearly require process
|
|
76
|
-
restart for hook changes. This reproduction exercised the loaders directly,
|
|
77
|
-
not the end-to-end development watcher and account flow. Transitive Node module
|
|
78
|
-
caching is a separate, already documented limitation. Keep trusted execution
|
|
79
|
-
as the default and reject unsupported sandbox hooks explicitly.
|
|
80
|
-
|
|
81
|
-
### 3. TypeScript authoring rejects valid mixed-trust module reuse — P2
|
|
82
|
-
|
|
83
|
-
[Issue 197](https://github.com/jimhoyd-com/urlcode/issues/197).
|
|
84
|
-
|
|
85
|
-
`src/typescript-authoring.ts:34` assigns each source one trust mode and rejects
|
|
86
|
-
reuse from a different mode. Two routes sharing a pure `f.ts`, one trusted and
|
|
87
|
-
one sandboxed, fail with “Module is imported by both a sandboxed and a trusted
|
|
88
|
-
route”. The equivalent JavaScript project activates and both routes return
|
|
89
|
-
200/ok. Shared transitive helpers have the same restriction.
|
|
90
|
-
|
|
91
|
-
Separate emission deduplication from sandbox reachability validation. A helper
|
|
92
|
-
reachable by a sandboxed route must still satisfy all sandbox import/source
|
|
93
|
-
budgets; trusted execution must not be downgraded or unnecessarily restricted.
|
|
94
|
-
Also reconcile the documented unlimited trusted source size with the builder's
|
|
95
|
-
16 MiB per-file authoring cap.
|
|
96
|
-
|
|
97
|
-
### 4. Live guidance still contradicts trust by default — P2
|
|
98
|
-
|
|
99
|
-
[Issue 196](https://github.com/jimhoyd-com/urlcode/issues/196).
|
|
100
|
-
|
|
101
|
-
- `docs/yaml/functions.md:57` denies Node/npm/network/filesystem APIs immediately
|
|
102
|
-
after an ordinary trusted route example.
|
|
103
|
-
- `docs/yaml/middleware.md:26` describes a mandatory shared sandbox.
|
|
104
|
-
- `docs/MIDDLEWARE.md:49` applies guest clone/body restrictions without scoping
|
|
105
|
-
them to sandboxed execution. Reproduced: trusted middleware reads a native
|
|
106
|
-
response body and returns its uppercase text; the sandboxed version rejects
|
|
107
|
-
that operation.
|
|
108
|
-
- `docs/SPECIFICATION.md` says `.js` ESM behavior is independent of package
|
|
109
|
-
settings. Reproduced: ESM `f.js` under `type: commonjs` fails trusted activation
|
|
110
|
-
and works in the sandbox. Ordinary Node resolution is the intended default;
|
|
111
|
-
the documentation should explain `.mjs` and package type accurately.
|
|
112
|
-
- `README.md` and `docs/FRAMEWORK.md` overstate binding grants as the only way
|
|
113
|
-
secrets reach code. Scope that statement to injected bindings.
|
|
114
|
-
- `docs/READINESS.md` calls the activated runtime isolated; the first test in
|
|
115
|
-
`test/typescript-authoring.test.ts` claims QuickJS execution but omits sandbox.
|
|
116
|
-
|
|
117
|
-
The prose guards pass this revision despite these contradictions. Strengthen
|
|
118
|
-
specific regression fixtures and fix the guidance; do not impose old sandbox
|
|
119
|
-
restrictions on trusted code to make the prose true.
|
|
120
|
-
|
|
121
|
-
The POST/body-based sandbox advisory is a related cleanup candidate. It is
|
|
122
|
-
non-blocking and documented, but suggests isolation from request shape rather
|
|
123
|
-
than evidence about code trust. Review whether to remove or reword it while
|
|
124
|
-
retaining explicit `sandboxReason` information. This is a recommendation, not
|
|
125
|
-
a discovered isolation failure.
|
|
126
|
-
|
|
127
|
-
### 5. Copyable documentation examples fail validation — P2
|
|
128
|
-
|
|
129
|
-
[New evidence on issue 168](https://github.com/jimhoyd-com/urlcode/issues/168#issuecomment-5747024306).
|
|
130
|
-
|
|
131
|
-
- `docs/yaml/policies.md:18` uses `page.source`; the field is `page.file`.
|
|
132
|
-
- `docs/policies/compression.md:35` uses `secret: api-key`, rejected by the
|
|
133
|
-
external binding-name grammar; an identifier such as `api_key` is admitted.
|
|
134
|
-
- `docs/AI-AUTHORING.md:249` omits the leading slash on `webhooks/stripe`.
|
|
135
|
-
This also illustrates why schema checks alone cannot replace semantic route
|
|
136
|
-
validation. The same section still lists the retired `link` handler.
|
|
137
|
-
|
|
138
|
-
The first two are complete fenced projects and fail `validateDocument`.
|
|
139
|
-
The partial profile illustration in `docs/POLICIES.md:169` was excluded from
|
|
140
|
-
findings because it deliberately omits a handler. Add classified executable
|
|
141
|
-
snippets: complete projects, context-dependent fragments and intentionally
|
|
142
|
-
invalid examples. Existing cookbook tests do not validate every prose example.
|
|
143
|
-
|
|
144
|
-
### 6. Monorepo ownership, navigation and release prose need cleanup — P3
|
|
145
|
-
|
|
146
|
-
[Issue 200](https://github.com/jimhoyd-com/urlcode/issues/200).
|
|
147
|
-
|
|
148
|
-
- Package AGENTS files still direct findings to former package repositories.
|
|
149
|
-
- Framework/status pages retain former source homes, old alpha combinations,
|
|
150
|
-
private cross-repository credential instructions and peer-pin language.
|
|
151
|
-
- Two admin README links target nonexistent package-local release workflows.
|
|
152
|
-
- Documentation navigation duplicates cookbook entries with conflicting 25/40
|
|
153
|
-
counts; llms.txt repeats semantics and TypeScript entries.
|
|
154
|
-
- Hook comments claim core lacks a sandbox primitive although SandboxPool is
|
|
155
|
-
exported. The packages still explicitly reject sandboxed hooks; update the
|
|
156
|
-
reason and tracked work rather than claiming they already support them.
|
|
157
|
-
|
|
158
|
-
Use local links and one canonical version/ownership inventory for live material;
|
|
159
|
-
keep dated release evidence clearly historical. Consolidate the duplicate
|
|
160
|
-
SQLite preflight predicates through development tooling without removing auth's
|
|
161
|
-
runtime gate. Shorten repeated issue-history narration in implementation comments
|
|
162
|
-
where a contract explanation and issue link are sufficient.
|
|
163
|
-
|
|
164
|
-
Do not remove the supported UI primitive fallback, opt-in sandbox path, provider
|
|
165
|
-
compatibility refusals, licenses or regression fixtures. No dead production module
|
|
166
|
-
was established with enough evidence to recommend deleting it in this audit.
|
|
167
|
-
|
|
168
|
-
## Verification evidence
|
|
169
|
-
|
|
170
|
-
The final sequential `npm run verify` passed, including lint, strict typecheck,
|
|
171
|
-
syntax/JSON/catalog checks, generated-document checks, workspace links, release
|
|
172
|
-
checks, build, core tests and all workspace suites:
|
|
173
|
-
|
|
174
|
-
| Suite | Passed | Skipped | Failed/cancelled |
|
|
175
|
-
|---|---:|---:|---:|
|
|
176
|
-
| Core | 518 | 1 | 0 |
|
|
177
|
-
| UI | 57 | 0 | 0 |
|
|
178
|
-
| Auth | 207 | 0 | 0 |
|
|
179
|
-
| Admin | 68 | 0 | 0 |
|
|
180
|
-
| Workspace scaffold integration | 1 | 0 | 0 |
|
|
181
|
-
|
|
182
|
-
Total: 851 passed, one intentional HTTPS-deployment test skip. The integration
|
|
183
|
-
test checks scaffold composition; it is not a live provider deployment.
|
|
184
|
-
|
|
185
|
-
`npm run test:package` passed installation of the packed archive and
|
|
186
|
-
starter/cookbook/authoring-consumer checks. Separate CLI HTTP fixture runs passed
|
|
187
|
-
for the starter, assets, cookbook, Vercel, AWS, Cloudflare, conditions (with its
|
|
188
|
-
documented public origin) and prerender source. The cookbook count audit passed
|
|
189
|
-
with 40 routes. Extension, fake-egress, monitoring, provider-conformance and
|
|
190
|
-
tunnel examples are also exercised by their dedicated core tests.
|
|
191
|
-
|
|
192
|
-
`npm audit --omit=dev --json`
|
|
193
|
-
reported zero known runtime advisories at audit time. Neither substitutes for
|
|
194
|
-
source review or establishes absence of unknown vulnerabilities.
|
|
195
|
-
|
|
196
|
-
The initial restricted-environment run could not run local HTTP tests. A first
|
|
197
|
-
unrestricted run had five test-file cancellations; those files passed alone.
|
|
198
|
-
An overlapping build also caused transient missing-declaration errors in a
|
|
199
|
-
workspace run. The final sequential full run passed without cancellations.
|
|
200
|
-
These intermediate results are not reported as confirmed product defects.
|
|
201
|
-
|
|
202
|
-
## Remaining evidence boundaries
|
|
203
|
-
|
|
204
|
-
No provider account was deployed, no release published, no production system
|
|
205
|
-
probed, and no container/Windows/Linux/Node 22 or 24 run was performed locally.
|
|
206
|
-
Live email/OIDC/passkey services, accessibility/browser/device assessment,
|
|
207
|
-
operational recovery/soak evidence and independent sandbox review remain separate.
|
|
208
|
-
Existing issues 58, 173, 174 and 185 already retain relevant acceptance,
|
|
209
|
-
model-benchmark, schema-discovery and release/CI-evidence work; this audit does
|
|
210
|
-
not close those gates.
|
|
211
|
-
|
|
212
|
-
## Follow-up: dead-code reachability
|
|
213
|
-
|
|
214
|
-
[Issue 203](https://github.com/jimhoyd-com/urlcode/issues/203) records a dedicated
|
|
215
|
-
unused-code pass requested after the initial audit. Runtime source remains the
|
|
216
|
-
same as the reviewed revision; the intervening commit only added this report.
|
|
217
|
-
|
|
218
|
-
A conservative relative-reference graph rooted at package exports and CLI entry
|
|
219
|
-
points reached all 157 tracked production TypeScript modules. Every direct
|
|
220
|
-
runtime dependency has a production source reference, and every root script
|
|
221
|
-
has a named reference elsewhere in the repository. No whole production file,
|
|
222
|
-
runtime dependency or root script was established as removable.
|
|
223
|
-
|
|
224
|
-
A TypeScript symbol/reference pass excluded 501 symbols exposed by public
|
|
225
|
-
package entrypoints. Candidates were then checked with repository-wide search
|
|
226
|
-
and manual inspection, including local uses, CLI imports, namespace dispatch,
|
|
227
|
-
worker URLs and dynamically loaded agent-list code. The confirmed small removals
|
|
228
|
-
are:
|
|
229
|
-
|
|
230
|
-
| Declaration/plumbing | Evidence | Proposed cleanup |
|
|
231
|
-
|---|---|---|
|
|
232
|
-
| `src/capability-query.ts:47`, `capabilityNameList()` | Declaration only; no caller or public entry export | Delete the unused wrapper |
|
|
233
|
-
| `src/catalog.ts:39`, `metadataFiles` | Declaration only; no reader or public entry export | Delete the unused constant |
|
|
234
|
-
| `src/trusted-functions.ts`, `log` option/property | Assigned but never read | Remove this unused executor plumbing, preserving runtime observer/logging behavior |
|
|
235
|
-
| `src/mcp-authoring.ts:57`, `expandHandler(path, handler)` | `path` is never read | Remove the argument and update callers |
|
|
236
|
-
| `src/policies/cache.ts:201`, `revalidate(state, req, result)` | `state` is never read | Remove the argument and update callers |
|
|
237
|
-
|
|
238
|
-
Several live implementation helpers are unnecessarily exported: admin's
|
|
239
|
-
`activeKit`, core's `routeState`, `forbiddenHeaders`, `normalizeRoute`,
|
|
240
|
-
`manifestFileName`, and local scaffold/render/name helpers in `init-with.ts`.
|
|
241
|
-
These are candidates for removing export modifiers, not deleting their bodies.
|
|
242
|
-
Check declaration dependencies before changing exported types.
|
|
243
|
-
|
|
244
|
-
An additional TypeScript check with `--noUnusedLocals --noUnusedParameters`
|
|
245
|
-
reported six unused parameters: the two production helpers above and four test
|
|
246
|
-
callbacks. It reported no unused local declarations. This stricter exploratory
|
|
247
|
-
check is separate from the normal passing typecheck.
|
|
248
|
-
|
|
249
|
-
Public APIs with no internal callers, types used in public signatures, registry
|
|
250
|
-
policy hooks, dynamic imports, supported UI fallback rendering and opt-in sandbox
|
|
251
|
-
execution are not dead code. This reachability analysis does not prove that
|
|
252
|
-
every branch executes. No production code was removed by this follow-up.
|
|
253
|
-
|
|
254
|
-
## Follow-up: unnecessary files and distribution weight
|
|
255
|
-
|
|
256
|
-
[File-level evidence on issue 200](https://github.com/jimhoyd-com/urlcode/issues/200#issuecomment-5747116642).
|
|
257
|
-
|
|
258
|
-
One package-local script is obsolete: `packages/admin/scripts/peer-revisions.mjs`.
|
|
259
|
-
It has no workflow/package-script caller and reads the deleted admin `peers.json`;
|
|
260
|
-
executing it fails with ENOENT. Its only other named reference is historical
|
|
261
|
-
monorepo prose. Remove it; workspace linking replaced its cross-repository
|
|
262
|
-
revision-output mechanism. The earlier root-script scan did not cover this
|
|
263
|
-
package-local leftover.
|
|
264
|
-
|
|
265
|
-
The three package `CODE_OF_CONDUCT.md` files are byte-identical to the root copy
|
|
266
|
-
(1,062 bytes each). Consolidate their links to the root policy before deleting
|
|
267
|
-
the duplicates. Package governance also repeats repository-wide controls and
|
|
268
|
-
can link to the root while retaining any package-specific information. Package
|
|
269
|
-
licenses and attribution notices serve a different purpose and must stay.
|
|
270
|
-
|
|
271
|
-
A dry-run npm package inventory includes 11 archived documentation files
|
|
272
|
-
(179,666 bytes) and six design-spike files (184,792 bytes): about 356 KiB
|
|
273
|
-
uncompressed combined. The audit report also ships because package.json includes
|
|
274
|
-
all of docs. Consider excluding maintainer/history/design records from npm
|
|
275
|
-
while retaining them in Git and keeping their references navigable. These
|
|
276
|
-
figures are content sizes, not estimates of compressed download savings.
|
|
277
|
-
|
|
278
|
-
The duplicate Claude plugin skills are intentional, checked distribution copies;
|
|
279
|
-
cookbook/recipe copies make each project independently usable; upstream UI
|
|
280
|
-
snapshots preserve attribution/provenance; the benchmark baseline is consumed
|
|
281
|
-
by its gate. None is established as unwanted. No tracked build output,
|
|
282
|
-
node_modules, tarballs, logs, backup files or TypeScript build-info files were
|
|
283
|
-
found. Ignored local dependencies/build products are regenerable development
|
|
284
|
-
output, not tracked repository clutter. No files were deleted in this audit.
|
package/docs/COMPLIANCE.md
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
# Compliance rules
|
|
2
|
-
|
|
3
|
-
A compliance rule is a standards-referenced check over what a project declares
|
|
4
|
-
and what the runtime knows about it: the parsed YAML, the route inventory and
|
|
5
|
-
policy table from `testPlan()`, the effective policy per route, the public
|
|
6
|
-
origin and the logging settings the operator states for the deployment. The
|
|
7
|
-
runtime ships three built-in profiles; an operator adds, overrides or disables
|
|
8
|
-
rules with code kept outside the project, the same trust boundary as
|
|
9
|
-
[plugins](PLUGINS.md) and the `--policy` binding grant; `urlcode audit` prints
|
|
10
|
-
every finding with the reference it rests on and sets the exit code.
|
|
11
|
-
|
|
12
|
-
## What it is and is not
|
|
13
|
-
|
|
14
|
-
- It checks **declared configuration and runtime facts**. No rule sends a
|
|
15
|
-
request, reads a binding, runs guest code or inspects a running deployment.
|
|
16
|
-
`oshp/hsts-origin` cannot know your TLS terminator; it knows the origin you
|
|
17
|
-
declared with `--origin`, so declare it truthfully.
|
|
18
|
-
- A passing run is **not a certification**, an audit opinion or evidence that
|
|
19
|
-
a deployment is secure or lawful. It says the declared configuration matches
|
|
20
|
-
a rule set whose references are listed beside each finding. Deployment,
|
|
21
|
-
soak and recovery proof live in [release readiness](RELEASE-READINESS.md).
|
|
22
|
-
- Rules never change behavior. The runtime serves the same responses whether a
|
|
23
|
-
finding exists or not; a rule set is an operator's review checklist in code.
|
|
24
|
-
- The built-in rules only check what the runtime can see. Anything a rule
|
|
25
|
-
cannot verify (an undeclared log level, an unknown origin) is reported as an
|
|
26
|
-
`info` finding saying so, never assumed to pass.
|
|
27
|
-
|
|
28
|
-
## The rule contract
|
|
29
|
-
|
|
30
|
-
```js
|
|
31
|
-
export const rules = [{
|
|
32
|
-
id: 'acme/redirect-hosts', // ^[a-z][a-z0-9-]{0,31}/[a-z][a-z0-9-]{0,63}$, unique per run
|
|
33
|
-
title: 'Redirects only leave for approved hosts',
|
|
34
|
-
standard: { name: 'ACME link policy', reference: 'https://example.com/policies/links', section: 'Outbound' },
|
|
35
|
-
severity: 'high', // high | medium | low | info
|
|
36
|
-
appliesTo: 'route', // project | route
|
|
37
|
-
check(context) { return []; }, // findings[]; may be async
|
|
38
|
-
}];
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
`standard.reference` is a URL, an RFC number (`RFC 9110`) or a path under
|
|
42
|
-
`docs/`. `check` returns an array of findings (an empty array, `undefined`,
|
|
43
|
-
a single finding or an array); a thrown error fails the run with the rule
|
|
44
|
-
named, so a rule cannot silently pass by crashing.
|
|
45
|
-
|
|
46
|
-
A **project** rule runs once with:
|
|
47
|
-
|
|
48
|
-
| Field | Value |
|
|
49
|
-
|---|---|
|
|
50
|
-
| `document` | The parsed and validated `urlcode.yaml` (includes are merged into `routes`) |
|
|
51
|
-
| `routes` | Route configuration by pattern, as written in YAML |
|
|
52
|
-
| `plan` | `testPlan()`: `inventory[]` (`path`, `handler`, `methods`, `middleware`, `policies`, `state`), `policies` (the per-route describe map) |
|
|
53
|
-
| `policies` | `effectivePolicies(document, route)` by pattern: the merged configuration of every policy on each route |
|
|
54
|
-
| `origin` | The declared public origin, or `null` |
|
|
55
|
-
| `target` | `node` unless the caller states another |
|
|
56
|
-
| `host` | `{ requestLog }` as declared for the deployment; `null` where undeclared |
|
|
57
|
-
|
|
58
|
-
A **route** rule runs once per inventory entry and additionally receives
|
|
59
|
-
`route` (the inventory entry), `config` (that route's YAML), `policy` (the
|
|
60
|
-
runtime's describe map for the route: `security.emits`, `agents.deny[].revision`,
|
|
61
|
-
`cache.cacheControl`, …) and `effective` (the merged policy configuration).
|
|
62
|
-
|
|
63
|
-
A **finding** is `{ rule, severity, route?, message, remediation, standard }`.
|
|
64
|
-
`severity` defaults to the rule's own; a check may lower or raise it for one
|
|
65
|
-
finding (the privacy rules report an undeclared setting as `info`). The
|
|
66
|
-
runtime fills `rule` and `standard` and the route pattern for route rules.
|
|
67
|
-
|
|
68
|
-
## Built-in profiles
|
|
69
|
-
|
|
70
|
-
`strict` contains every `baseline` rule plus its own; `privacy` stands alone;
|
|
71
|
-
`none` runs only operator rules. Check derivations reference the runtime's
|
|
72
|
-
own code: the security profile tables in `src/policies/security.ts`, the
|
|
73
|
-
cache and compression secrets handling in `src/policies/cache.ts` and
|
|
74
|
-
`src/policies/compression.ts`, the `no-store` default in
|
|
75
|
-
`src/http-response.ts`, the 16 KiB header cap in `src/http-policy.ts`.
|
|
76
|
-
|
|
77
|
-
### `baseline`
|
|
78
|
-
|
|
79
|
-
| Rule | Standard | Severity | Checks | Remediation |
|
|
80
|
-
|---|---|---|---|---|
|
|
81
|
-
| `oshp/security-headers` | [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/) | medium | Every active route has an effective `policies.security` | Declare `policies.security: { headers: oshp }` on the project or the route |
|
|
82
|
-
| `oshp/hsts-origin` | OWASP Secure Headers Project, Strict-Transport-Security | low | When any active route's security profile emits HSTS, the declared origin is `https:`; the runtime emits HSTS only then | Serve behind TLS and declare `--origin https://…` |
|
|
83
|
-
| `breach/secrets-compression` | [BREACH](https://www.breachattack.com/) | high | No route binding `secrets` sets `policies.compression.allowWithSecrets` | Remove `allowWithSecrets` (the runtime then skips compression on secret routes) |
|
|
84
|
-
| `rfc9111/secrets-no-store` | [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111) §5.2.2.5, §5.2.2.7 | medium | A route binding `secrets` declares no cache strategy, Cache-Control or handler `cacheControl` other than `no-store`/`private` | Use `no-store` or `private` on secret routes |
|
|
85
|
-
| `rfc9111/cache-control-declared` | RFC 9111 §5.2 | low | `respond` and `redirect` routes declare a `Cache-Control` header or a cache policy; asset handlers declare `cacheControl` (otherwise the runtime defaults `no-store` / assets `no-cache`) | State the intent in `response.headers`, the handler or `policies.cache` |
|
|
86
|
-
| `rfc6585/throttle-functions` | [RFC 6585](https://www.rfc-editor.org/rfc/rfc6585) §4 | medium | Every active function or middleware route has an effective `policies.throttle` | Declare a throttle on the route or the project |
|
|
87
|
-
| `rfc9309/robots` | [RFC 9309](https://www.rfc-editor.org/rfc/rfc9309) | low | Some active route declares `policies.agents`, or an active `/robots.txt` `respond` route exists | Add a `/robots.txt` route or an agents deny list |
|
|
88
|
-
| `rfc9110/expired-routes` | [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110) §15.5.11 | info | Lists routes past `expires` that still answer 410 | Remove them once the 410 window has served its purpose |
|
|
89
|
-
|
|
90
|
-
### `strict` (baseline plus)
|
|
91
|
-
|
|
92
|
-
| Rule | Standard | Severity | Checks | Remediation |
|
|
93
|
-
|---|---|---|---|---|
|
|
94
|
-
| `oshp/csp` | OWASP Secure Headers Project, Content-Security-Policy | medium | Every active route emits `Content-Security-Policy` (security profile minus `unset`, plus `set`, or a YAML response header) | Use the `oshp` profile or set the header |
|
|
95
|
-
| `rfc6585/throttle-all` | RFC 6585 §4 | medium | Every active non-function route has an effective throttle (function routes are covered by `throttle-functions`) | Declare `policies.throttle` at the project level |
|
|
96
|
-
| `agents/lists-pinned` | [Bundled agent lists](policies/agents.md) | low | Every agents deny/allow list is a bundled list that carries an upstream revision; project `.json` list files report `revision: project` | Prefer bundled lists or record the revision a list was built from |
|
|
97
|
-
| `rfc9110/redirect-https` | RFC 9110 §15.4 | medium | No active `redirect.url` starts with `http:` | Redirect to https |
|
|
98
|
-
| `http/header-budget` | RFC 9110 §5.4 | low | YAML `response.headers` bytes plus the security profile's static bytes stay at or under 8192, half the runtime's 16 KiB response header cap | Trim headers or the security set |
|
|
99
|
-
|
|
100
|
-
### `privacy`
|
|
101
|
-
|
|
102
|
-
These rules check deployment settings, so `audit` takes `--request-log` to
|
|
103
|
-
declare the level the deployment uses (the audit process itself always logs
|
|
104
|
-
nothing); embedders pass `host` themselves. The references are the runtime's
|
|
105
|
-
own [logging guarantees](MONITORING.md): records carry no URL, query, header,
|
|
106
|
-
body or binding, and `detailed` adds only the method and route pattern.
|
|
107
|
-
|
|
108
|
-
| Rule | Standard | Severity | Checks | Remediation |
|
|
109
|
-
|---|---|---|---|---|
|
|
110
|
-
| `privacy/request-log-minimal` | [Monitoring](MONITORING.md), Log records | medium (`info` when undeclared) | `host.requestLog` is `minimal` | Use the default log unless per-route rates are required |
|
|
111
|
-
| `privacy/detailed-log-parameters` | [Monitoring](MONITORING.md), Log records | low | With `detailed` logging, no active route takes parameters (records name the pattern and method, never values) | Keep `minimal` on parameterised deployments |
|
|
112
|
-
|
|
113
|
-
## Writing custom rules
|
|
114
|
-
|
|
115
|
-
[`examples/compliance/rules.mjs`](../examples/compliance/rules.mjs) is a
|
|
116
|
-
complete operator module; it runs against the cookbook:
|
|
117
|
-
|
|
118
|
-
```sh
|
|
119
|
-
node src/cli.ts audit --project examples/cookbook \
|
|
120
|
-
--compliance baseline --compliance-rules "$PWD/examples/compliance/rules.mjs" --compliance-warn
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
A module exports up to three names:
|
|
124
|
-
|
|
125
|
-
```js
|
|
126
|
-
export const rules = [ /* rules to add; an id already in the profile is an error */ ];
|
|
127
|
-
export const disable = ['rfc9110/expired-routes']; // remove built-in or added rules by id
|
|
128
|
-
export const override = { 'oshp/security-headers': { severity: 'high' } }; // shallow-merge fields of an existing rule
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Order is fixed: profile rules, then `rules` added, then `override` applied,
|
|
132
|
-
then `disable` removed, then `--compliance-ignore`. The result is validated
|
|
133
|
-
as one set, so an override cannot produce an invalid rule.
|
|
134
|
-
|
|
135
|
-
The module must be an absolute path to an `.mjs`/`.js` file **outside** the
|
|
136
|
-
audited project, checked the way `--policy` is (`realpath` of both, the file
|
|
137
|
-
may not resolve inside the project root). It is imported as trusted operator
|
|
138
|
-
code with the host's privileges, the same standing as a plugin: a project
|
|
139
|
-
cannot ship its own rule set and grade itself. Rules read the context they
|
|
140
|
-
are given; a rule that reaches for the filesystem or the network is a plugin
|
|
141
|
-
in the wrong place.
|
|
142
|
-
|
|
143
|
-
## CLI
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
urlcode audit [--project dir] [--compliance baseline|strict|privacy|none]
|
|
147
|
-
[--compliance-rules /absolute/rules.mjs] [--compliance-ignore id,id]
|
|
148
|
-
[--compliance-warn] [--origin https://links.example] [--request-log minimal|detailed]
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
Without any compliance flag the audit report is unchanged apart from
|
|
152
|
-
`compliance: null`. With one, the report gains a `compliance` section:
|
|
153
|
-
|
|
154
|
-
```json
|
|
155
|
-
{ "profile": "baseline", "rules": 9, "ruleIds": ["oshp/security-headers", "…"], "ignored": [],
|
|
156
|
-
"findings": [{ "rule": "rfc6585/throttle-functions", "severity": "medium", "route": "/hello/{name}",
|
|
157
|
-
"message": "…", "remediation": "…", "standard": { "name": "RFC 6585 …", "reference": "…", "section": "…" } }],
|
|
158
|
-
"counts": { "high": 0, "medium": 4, "low": 11, "info": 1 }, "pass": true,
|
|
159
|
-
"evidence": { "routes": 21, "active": 19, "policies": ["agents", "cache", "security", "throttle"],
|
|
160
|
-
"files": ["urlcode.yaml", "routes/code.yaml"], "origin": null, "target": "node",
|
|
161
|
-
"host": { "requestLog": "minimal" },
|
|
162
|
-
"scope": "declared configuration and runtime facts; not a deployment or certification" } }
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
`--compliance-rules` alone implies `--compliance baseline`. `--origin` and
|
|
166
|
-
`--request-log` describe the deployment under review and are echoed in
|
|
167
|
-
`evidence`. Findings are sorted by severity, rule and route.
|
|
168
|
-
|
|
169
|
-
### Exit codes
|
|
170
|
-
|
|
171
|
-
| Condition | Exit |
|
|
172
|
-
|---|---|
|
|
173
|
-
| Readiness failed (`ready: false`) | 1, as before |
|
|
174
|
-
| A `high` finding and no `--compliance-warn` | 1 |
|
|
175
|
-
| `--compliance-warn`: findings are printed, `pass` is still `false` | 0 unless readiness failed |
|
|
176
|
-
| Unknown profile, rules file inside the project, malformed rule or ignore id | 1 with an `error` event on stderr |
|
|
177
|
-
|
|
178
|
-
## Programmatic API
|
|
179
|
-
|
|
180
|
-
```js
|
|
181
|
-
import { runCompliance, builtinProfiles, validateRules, resolveRules, loadComplianceRules } from '@jimhoyd/urlcode/compliance';
|
|
182
|
-
import { createRuntime } from '@jimhoyd/urlcode';
|
|
183
|
-
|
|
184
|
-
const runtime = await createRuntime('./site');
|
|
185
|
-
const report = await runCompliance(runtime, {
|
|
186
|
-
profile: 'strict', // baseline | strict | privacy | none
|
|
187
|
-
rules: [], override: {}, disable: [], // as a rules module would export them
|
|
188
|
-
ignore: ['rfc9110/expired-routes'],
|
|
189
|
-
origin: 'https://links.example',
|
|
190
|
-
host: { requestLog: 'minimal' }, // what the deployment is configured with
|
|
191
|
-
});
|
|
192
|
-
await runtime.close();
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
The declarations ship with the package: `ComplianceRule` (with `ProjectRule`
|
|
196
|
-
and `RouteRule`, and `ProjectContext`/`RouteContext` for what `check`
|
|
197
|
-
receives), `RawFinding` and `Finding`, `ComplianceOptions`, `ComplianceReport`
|
|
198
|
-
and `ComplianceProfileName` are all exported from `@jimhoyd/urlcode/compliance`, so a
|
|
199
|
-
rules module written in TypeScript is checked against the same contract the
|
|
200
|
-
runtime validates at load time:
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
import type { ComplianceRule, ComplianceReport } from '@jimhoyd/urlcode/compliance';
|
|
204
|
-
import { runCompliance } from '@jimhoyd/urlcode/compliance';
|
|
205
|
-
|
|
206
|
-
export const rules: ComplianceRule[] = [{
|
|
207
|
-
id: 'acme/redirect-hosts',
|
|
208
|
-
title: 'Redirects only leave for approved hosts',
|
|
209
|
-
standard: { name: 'ACME link policy', reference: 'https://example.com/policies/links', section: 'Outbound' },
|
|
210
|
-
severity: 'high',
|
|
211
|
-
appliesTo: 'route',
|
|
212
|
-
check(context) {
|
|
213
|
-
if (context.config.redirect?.url.startsWith('https://acme.example/')) return [];
|
|
214
|
-
return [{ message: `${context.route.path} redirects outside the approved hosts`, remediation: 'Point the redirect at an approved host' }];
|
|
215
|
-
},
|
|
216
|
-
}];
|
|
217
|
-
const report: ComplianceReport = await runCompliance(runtime, { profile: 'strict', rules });
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
`runCompliance` accepts a started server from `startServer` or a runtime from
|
|
221
|
-
`createRuntime`; it re-reads the YAML from the runtime's `root` and takes the
|
|
222
|
-
plan from `testPlan()`, so rules see what the runtime compiled.
|
|
223
|
-
`auditProject(app, { compliance })` runs the same and attaches the report
|
|
224
|
-
under `compliance`. `builtinProfiles` maps profile names to their frozen rule
|
|
225
|
-
arrays for reuse or inspection; `validateRules` checks a rule array;
|
|
226
|
-
`resolveRules` builds the final set from a profile and operator additions.
|
|
227
|
-
|
|
228
|
-
## Extending with a plugin-style workflow
|
|
229
|
-
|
|
230
|
-
Keep rule modules where you keep plugins: in the operator application, under
|
|
231
|
-
version control, reviewed like code, outside every audited project. A shared
|
|
232
|
-
module can export rules that read the same policy tables a plugin's
|
|
233
|
-
`onActivate(runtime)` sees through `testPlan()`, so one review of "what does
|
|
234
|
-
this runtime enforce" serves both. Compose organisation rules on top of a
|
|
235
|
-
built-in profile with `override` for stricter severities and `disable` for
|
|
236
|
-
rules that a documented decision replaces, and run `urlcode audit
|
|
237
|
-
--compliance strict --compliance-rules …` in CI with the exit code as the
|
|
238
|
-
gate. Record the report beside the readiness and benchmark evidence for the
|
|
239
|
-
revision; a report proves what was declared at that commit, nothing more.
|