Skip to content

Demo: shescape-based alternative to the H26-318 allowlist fix - #116

Open
junhoef wants to merge 1 commit into
masterfrom
demo/h26-318-shescape
Open

Demo: shescape-based alternative to the H26-318 allowlist fix#116
junhoef wants to merge 1 commit into
masterfrom
demo/h26-318-shescape

Conversation

@junhoef

@junhoef junhoef commented Aug 5, 2026

Copy link
Copy Markdown

What does this PR do?

This is not meant to be merged — it's a side-by-side alternative to #115 for comparison, showing what the H26-318 command-injection fix looks like using a library instead of a hand-rolled character allowlist.

src/writeRedirect.js still validates redirectValue is a genuine https: URL (via the native URL class), but instead of SAFE_PAGE_URL/SAFE_REDIRECT_URL regexes, it uses shescape to quote/escape both Prismic-controlled values before they're embedded in bin/redirects.sh. Since escaping works on the actual string content rather than a pre-approved character set, this sidesteps the exact class of regression found in #115 (a real production redirect using a UTM query string was silently rejected because ?/&/= weren't in the allowlist).

Pinned to shescape@2.1.14 specifically:

  • shescape@3.x dropped CommonJS support entirely (pure ESM) — this package builds to CJS via Babel for require()-based consumers (ssg-hx-uk, ssg-paultonsbreaks, etc.), so a v3 dependency would throw ERR_REQUIRE_ESM at runtime for every downstream consumer.
  • shescape@1.x has known CVEs, including one rated critical.
  • 2.1.14 is the earliest version with all known advisories patched and still ships a require export condition.

What tests does this PR have?

Rewrote test/writeRedirect.test.js (11 unit tests) against the shescape-based implementation:

  • legitimate redirect (plain URL and one with a UTM query string, using the exact real-world value found in ssg-paultonsbreaks's production bin/redirects.sh)
  • command substitution, semicolon, backtick, and embedded-single-quote payloads in the redirect value — asserted as neutralised (quoted literally) rather than rejected, since escaping renders them inert instead of filtering them out
  • shell-metacharacter payload in the page slug
  • non-https and non-URL redirect values rejected
  • uid fallback when slug is absent

How can this be tested?

npm install
npm test

All 19 tests pass (8 pre-existing simulate-request + 11 new writeRedirect).

Also validated against every unique redirect URL and page slug that exists in ssg-paultonsbreaks's real, checked-in bin/redirects.sh (production data) — all pass.

Any tech debt?

None new, but worth flagging for whoever compares this against #115: shescape's quote() wraps the whole value in shell quotes, so the command template had to change from "s3://$BUCKET/${pageUrl}.html" (one big double-quoted string) to "s3://$BUCKET/"${safePageUrl}".html" (adjacent quoted segments concatenated by bash) so $BUCKET still expands at deploy time while the Prismic-controlled segment stays fully quoted. This is a bit less readable than the single-string-with-regex-validation approach in #115.

What gif best describes how you feel?

https://media.giphy.com/media/xUOxf48UtjF3O7HYUE/giphy.gif

For reviewer

  • Would you accept a pull request for a similar issue?
  • Do you understand the risk of this change?

…ist (H26-318)

Alternative to the regex-allowlist fix in #115. Validates redirectValue is a
genuine https URL with the native URL class, then lets shescape quote/escape
both Prismic-controlled values for safe embedding in bin/redirects.sh -
removing the need to enumerate every safe character by hand (which is how
the query-string regression in #115 happened).

Pinned to shescape@2.1.14: v3 dropped CommonJS support (this package builds
to CJS via Babel for require()-based consumers) and v1.x has known CVEs,
including a critical one, in the version ranges below 2.1.14.

@shackpank shackpank left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good, but is sort of a symptom of one project generating shell code for a different project to execute (https://github.com/holidayextras/ssg-toolbox/blob/50bc65dc559532f4e860e54f467d3f0f9a2a8ce3/bin/deployment.sh#L105)?

ie this wouldn't be needed if instead we did something like, write a redirects.json, and ssg-toolbox picks up, uses the S3 javascript sdk to make the requested changes...

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.

2 participants