Repository navigation
fix(attachments): base64url-encode source URLs to dodge Akamai WAF 403 (#1931 follow-up) - #2189
Merged
Merged
Conversation
Release 1.21.0 → PROD
promote: rebuild-content phantom-step fix (#2121) to main
Release 1.22.0 — What's New refresh + version bump
…#1931 follow-up) All tutorial attachment download links return 403 at Akamai edge because the query value u= contains encoded 'raw.githubusercontent.com/<path>', which triggers a WAF signature. The fix encodes the source URL as base64url in the client link (no colons = WAF-safe) and decodes server-side in the handler. Changes: - hugo/layouts/_default/_markup/render-link.html: base64url-encode the dest URL via Hugo's base64Encode + character replacement - srv/lib/attachment-warm-utils.js: add resolveAttachmentSourceUrl() helper to detect and decode either base64url or URL-encoded params; update extractAttachmentUrls() to use it - srv/lib/attachment-source-handler.js: call resolveAttachmentSourceUrl() to decode the u= param before use; strict decode = return 400 on malformed input - test/unit/attachment-warm-utils.test.js: new unit tests for the resolver and extractor (9 tests, all green) - test/unit/attachment-source-endpoint.test.js: add base64url encode case + improve malformed-param test Store key remains the raw URL (no re-warm needed). Wire format only changes. All 5491 unit tests pass (1 pre-existing failure unrelated). Verified on PROD: u=<plain URL-encoded> still works (backward compat); u=<base64url> reaches origin (404 until warmed).
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.
Problem
Every tutorial attachment download link (
/content/attachment-source?u=<encoded raw.githubusercontent.com URL>) returns 403 Access Denied from AkamaiGHost on PROD — the request never reaches our origin.Root cause (verified live 2026-09-07): an Akamai WAF signature fires when a query-string value contains an encoded
https://raw.githubusercontent.com/<path>. Bisected on PROD:u=https%3A%2F%2F2.zoppoz.workers.dev%3A443%2Fhttps%2Fexample.com%2Fxand…github.com%2Fx→ reach origin (404)u=https%3A%2F%2F2.zoppoz.workers.dev%3A443%2Fhttps%2Fraw.githubusercontent.com%2Fx→ 403raw.githubusercontent.comwith no path → passesOur CAP handler and origin are correct (no-query returns our own 400 with
x-vcap-request-id). Same edge-WAF class as the.well-knownOAuth 403 and the bare-PATCH/DELETE blocks.Fix
Encode the source URL as base64url in the client link (no
:, no., noraw.githubusercontent.comliteral → dodges the WAF). Decode server-side back to the raw URL so the attachment-store key is unchanged (no re-warm).hugo/layouts/_default/_markup/render-link.html— base64url-encode the dest URL via Hugobase64Encode++/=→-_replacementsrv/lib/attachment-warm-utils.js— newresolveAttachmentSourceUrl()helper; detects base64url vs URL-encoded (presence of:/%3A) and decodes;extractAttachmentUrls()uses itsrv/lib/attachment-source-handler.js— decodeu=via the helper before use; strict decode → 400 on malformedattachment-warm-utils.test.js(resolver + extractor, 9 tests) + base64url case inattachment-source-endpoint.test.jsBackward compatible: legacy plain URL-encoded
u=params still work (detected by:/%3A), so already-published HTML and the warm/backfill path keep functioning during the transition.Testing
u=reaches origin; base64urlu=reaches origin (404 until warmed) — no longer 403Follow-up
Proper root fix is still an Akamai WAF exception for
/content/attachment-source(edge team, same owner as the.well-known403). This code change makes downloads work today without waiting on that.