Skip to content

fix(attachments): correct base64url encode in render-link hook (#3098) - #3101

Merged
jung-thomas merged 1 commit into
DEVfrom
worktree-fix-download-url-3098
Nov 11, 2026
Merged

jung-thomas merged 1 commit into
DEVfrom
worktree-fix-download-url-3098

Conversation

@jung-thomas

Copy link
Copy Markdown
Contributor

Fixes #3098 — tutorial file downloads return {"error":"Attachment unavailable"}.

Root cause

The render-link hook (hugo/layouts/_default/_markup/render-link.html) base64url-encodes attachment source URLs into the /content/attachment-source?u= param to dodge the Akamai WAF (#1931). It did so with a pipe chain:

{{ $b64 | replace "+" "-" | replace "/" "_" | replace "=" "" }}

But Hugo's replace is replace INPUT OLD NEW. Piping appends $b64 as the third argument (NEW), so each step became replace("+", "-", $b64) — i.e. it searched the literal string "+" and discarded the base64 entirely. The emitted param collapsed to the garbage u=/=/, the server base64url-decoded it to an empty/invalid URL, ingest failed, and the endpoint returned 404 for every download link.

This has been broken since the base64url WAF fix (b82e4bb21) — it never worked, which is why both old (hana-cloud-cap-create-database-cds) and new tutorials are affected.

Fix

Use nested replace() calls with the string as the first positional arg:

{{ $b64url := replace (replace (replace $b64 "+" "-") "/" "_") "=" "" }}

Test

The old test only string-grepped the template and never rendered Hugo — that's why the bug shipped. Added test/unit/render-link-attachment-encode.test.ts, which spins up a throwaway Hugo site with the real hook, renders an attachment link, and asserts the emitted u= param base64url-decodes back to the exact source URL (and is free of +///=). Verified failing on the old template, passing on the fix.

Deploy note

This is a Hugo-layout change, so it needs a content rebuild to take effect on served tutorials (the u= param is baked at build time). A full/affected rebuild after merge will regenerate all download links.

🤖 Generated with Claude Code

Hugo's `replace` is `replace INPUT OLD NEW`, so the pipe chain
`$b64 | replace "+" "-" | replace "/" "_" | replace "=" ""`
appended $b64 as the third (NEW) argument at each step, searching the
literal "+"/"/"/"=" and discarding the base64 entirely. The emitted
attachment-source param collapsed to "u=/=/", which the server
base64url-decoded to an invalid URL — ingest failed and every tutorial
download link returned 404 "Attachment unavailable" (old and new
tutorials alike).

Use nested replace() calls with the string as the first positional arg.
Add a behavioral regression test that renders the real hook through Hugo
and asserts the u= param base64url-decodes back to the exact source URL;
the previous string-grep test never rendered Hugo, which is why the bug
shipped.
@jung-thomas
jung-thomas merged commit 2d3c536 into DEV Nov 11, 2026
5 checks passed
@jung-thomas
jung-thomas deleted the worktree-fix-download-url-3098 branch November 11, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant