From 2c26852d00e93057cccdfb3af4fd4219f249b8da Mon Sep 17 00:00:00 2001 From: universe-ops <177390656+universe-ops@users.noreply.github.com> Date: Thu, 21 May 2026 23:33:31 +0300 Subject: [PATCH] fix(cloudflare/worker): don't merge query params into external redirects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cloudflare worker proxies the upstream Lambda's redirect responses back to the browser. Previously it merged the original request's query string into every redirect target, including external ones like S3 presigned URLs. This broke any service that issues a 3xx to an S3 presigned URL (e.g. storage-service): the extra param (e.g. ?secret=...) changes the canonical query string, so X-Amz-Signature no longer matches and S3 returns "SignatureDoesNotMatch". It also leaked the original request's secrets to whatever third party we redirected to. Move the query-merge inside the same `target.hostname === overrideHost` branch as the hostname rewrite — same-upstream redirects still merge, external redirects pass through untouched. --- pkg/clouds/pulumi/cloudflare/registrar.go | 29 ++++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pkg/clouds/pulumi/cloudflare/registrar.go b/pkg/clouds/pulumi/cloudflare/registrar.go index f361abb1..7f4103ad 100644 --- a/pkg/clouds/pulumi/cloudflare/registrar.go +++ b/pkg/clouds/pulumi/cloudflare/registrar.go @@ -249,22 +249,23 @@ async function handleRequest(origRequest) { if (location) { const target = new URL(location, url); - // merge original query string with redirect's own query params - // redirect params take precedence over original request params - if (url.search) { - const origParams = new URLSearchParams(url.search); - const targetParams = new URLSearchParams(target.search); - for (const [key, value] of origParams) { - if (!targetParams.has(key)) { - targetParams.append(key, value); + // Only rewrite hostname AND merge query params if redirect is to our + // target upstream. External redirects (S3 presigned URLs, OAuth, etc.) + // must pass through unchanged — extra query params break S3 signatures + // and may leak request secrets (e.g. ?secret=) to third parties. + if (target.hostname === overrideHost) { + // merge original query string with redirect's own query params + // redirect params take precedence over original request params + if (url.search) { + const origParams = new URLSearchParams(url.search); + const targetParams = new URLSearchParams(target.search); + for (const [key, value] of origParams) { + if (!targetParams.has(key)) { + targetParams.append(key, value); + } } + target.search = targetParams.toString(); } - target.search = targetParams.toString(); - } - - // Only rewrite hostname if redirect is to our target upstream (overrideHost) - // External redirects (e.g., to Microsoft login) should pass through unchanged - if (target.hostname === overrideHost) { target.hostname = origHost; target.protocol = new URL(request.url).protocol; }