fix(github): enforce the redirect allowlist on the client's redirect_uri - #545
Merged
Merged
Conversation
ALLOWED_REDIRECT_HOST_SUFFIXES was checked inside authorizationUrl(), on
the callback URL the runtime builds as `${url.origin}/oauth/callback`. That
value is always this server's own origin, so assertAllowedRedirectUri()
passed unconditionally and rejected nothing. The value an attacker actually
controls is the client's redirect_uri on /authorize, which nothing checked.
@decocms/runtime 3.0.0 validates that value on /authorize and
/oauth/callback, so hand it the same suffix list through the new
allowedRedirectHosts option. The check now sits where it does something.
Drop lib/redirect-allowlist.ts and its test; the runtime's redirect-uri.ts
carries the same label-boundary matching and the same cases.
Pin zod to 4.3.6 alongside the runtime bump. Runtime 1.6.5 depended on
"zod": "^4.0.0" and deduped with this workspace's own ^4.0.0, but 3.0.0
pins 4.3.6 exactly. Left on a range we resolve 4.4.3, get two zod copies,
and every schema crossing the runtime boundary becomes nominally
incompatible: 50 type errors in server/lib/trigger-store.ts and
server/main.ts that have nothing to do with this change. Matching the pin
collapses it back to one instance.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to decocms/studio#6330, which shipped
@decocms/runtime3.0.0.What is this contribution about?
ALLOWED_REDIRECT_HOST_SUFFIXESwas checked insideauthorizationUrl(), against the callback URL the runtime builds as${url.origin}/oauth/callback. That value is always this server's own origin, soassertAllowedRedirectUri()passed unconditionally and rejected nothing. The value an attacker actually controls is the client-suppliedredirect_urion/authorize, and nothing checked that at all.Runtime 3.0.0 validates it on both
/authorizeand/oauth/callback, so this passes the same suffix list in through the newallowedRedirectHostsoption. The check now sits on the attacker-controlled value instead of ours.lib/redirect-allowlist.tsand its test are deleted. The runtime'sredirect-uri.tscarries the same label-boundary matching and the same cases, including theevildecocms.comanddecocms.com.attacker.ionear-misses.Why zod moves too
Runtime 1.6.5 depended on
"zod": "^4.0.0"and deduped with this workspace's own^4.0.0. Runtime 3.0.0 pins4.3.6exactly. Left on a range we resolve 4.4.3, end up with two zod copies, and every schema crossing the runtime boundary becomes nominally incompatible: 50 type errors inserver/lib/trigger-store.tsandserver/main.tsthat have nothing to do with this change. Pinning4.3.6collapses it back to one instance.Worth knowing before the other MCPs are upgraded, since they will all hit this. The cleaner long-term fix is upstream: the runtime ships raw TypeScript and takes zod types across its API boundary, so it should express zod as a range rather than an exact pin.
How did you verify your code works?
bun run check github: 2 passed, 0 failed (coversgithubandgithub-repo-reports). Before the zod pin this reported 50 own-source errors; after, zero.bun test github/: 112 pass, 0 fail across 7 files.tscoutput is 19 errors entirely insidenode_modules(@decocms/runtime/src/triggers.ts,tools.ts, one ajv duplicate).scripts/check.tsfilters those by design.mainalready had 4 of the same kind, so this is a pre-existing condition of the runtime shipping raw source, not a regression from this PR.Runtime-side coverage lives in decocms/studio#6330: 40 tests over the exact-match rules, the allowlist near-misses, and the callback path.
How to Test
bun install && bun run check github && bun test github//authorizewith aredirect_urion a host outsidedecocms.com. Expected: 400 with nolocationheader, where it previously 302 d to GitHub.Migration Notes
allowedRedirectHostsis what makes/authorizework at all on runtime 3.0.0 for a server with nooauth.persistence. Without it the endpoint rejects every request by design, so the config and the version bump have to ship together, which they do here.In-flight authorizations do not survive the deploy: a
stateissued by the running version carries noclient_id, so the new callback gate rejects it. Users retry and it works. Worth deploying off-peak.stateSecret, which seals thestateand the authorization code, is deliberately not set here. It should be a separate, later change, because mid-rollout an instance holding the secret cannot unseal a state written by one without it.Review Checklist
Summary by cubic
Enforces the OAuth redirect allowlist on the client-supplied redirect_uri via
@decocms/runtime3.0.0. Previously we only validated our own callback URL (always this server), so disallowed redirects were never rejected; now the runtime validates the client value on /authorize and /oauth/callback.allowedRedirectHostsfromALLOWED_REDIRECT_HOST_SUFFIXESto the runtime; removes local redirect allowlist code and tests in favor of the runtime’s label-boundary rules.zodto "4.3.6" to match the runtime and avoid duplicate instances causing type mismatches.Rollout/Migration
@decocms/runtime3 should also pinzodto "4.3.6".Written for commit ef50358. Summary will update on new commits.