fix(website-lambda): gate .plain.html and .md variants of private pages - #163
Merged
Merged
Conversation
AEM serves every page as head-less variants (<page>.plain.html, <page>.md) that carry no <head>, so the audience meta gate could not see that the page was private and served it to anonymous visitors. The audience block filter also missed both variants (no <main> in .plain.html; .md not filtered). - gate.js: getCanonicalPagePath maps variants to their page; .md is now page-like (gate) instead of default-allow. - index.js: for anonymous variant requests, fetch the canonical page and run isPrivateHtml on it before proxying; fail closed (404) on private, non-200/redirect, non-HTML, or fetch error. - audience.js: strip audience blocks from .plain.html markup, and add filterAudienceMarkdown for .md block tables (plus orphaned image refs). - Tests for gate, audience, and a new index.test.js with mocked fetch. - README: document variant gating. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve conflicts with the gated-ETag / sitemap work on main: export isPageLike (now including .md variants), keep main's 206 fail-closed and gated 304 handling alongside the canonical-page check, move the variant routing tests to index.variants.test.js, and bump GATE_ETAG_VERSION to 2 since the audience filtering changed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
marissahuysentruyt
approved these changes
Oct 1, 2026
This branch was successfully deployed
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.
Description
Severity: SEV2. Private pages and
audience-privateblocks leaked to signed-out visitors through AEM's head-less page variants.The website Lambda decided whether a page is private by scanning
<head>for<meta name="audience" content="private">. AEM's.plain.htmland.mdvariants have no<head>, so for a private page like/support/developer-overview:/support/developer-overviewcorrectly returned 404 to signed-out visitors./support/developer-overview.plain.htmland/support/developer-overview.mdreturned 200 with the full page content./index.plain.htmland/index.md) still includedaudience-privateblocks.This PR closes both leaks in
workers/website-lambda/:.plain.htmlor.mdvariant, the Lambda fetches the canonical page (/foo.plain.html→/foo,/index.plain.html→/) and runs the existingisPrivateHtmlcheck on it. A private page returns 404. Any lookup failure (non-200, redirect or network error) also returns 404, so the check fails closed.filterAudienceBlocksnow matches the block markup that.plain.htmluses.filterAudienceMarkdownremovesaudience-privateblock tables from.md, including headers with inline formatting such as**Cards (audience private)**and the image reference definitions left behind by removed blocks.GATE_ETAG_VERSIONis bumped to 2, so copies filtered by the old gate are never confirmed with a 304.The Cloudflare worker (
workers/website/) is unaffected because it denies signed-out visitors by default.