Skip to content

Anchor jq validators at true string ends so malformed keys fail closed - #117

Merged
thetangstr merged 1 commit into
mainfrom
fix/compose-up-strict-jq
Sep 17, 2026
Merged

thetangstr merged 1 commit into
mainfrom
fix/compose-up-strict-jq

Conversation

@thetangstr

@thetangstr thetangstr commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes an independent-review finding on PR Deploy wrapper: drop node dependency so the systemd unit can restart #116's node-free validator: jq's test() uses Oniguruma, where $ anchors before a trailing newline, and $(...) strips trailing newlines — so a kid or secretBase64 with an escaped trailing \n could pass the wrapper while the container's stricter JS validator rejects it, deferring failure to container startup.
  • All security-relevant patterns now anchor with \A...\z (true string ends), in both the runtime-key filter and the release-pin filter.
  • Canonicality is not claimed by the regex: secretBase64 gets a strict structural pattern (alphabet + padding shape), which also guarantees the extracted value contains no whitespace for $(...) to strip — making the original base64 -d | base64 decode-and-reencode equality check safe to run again. Nonzero pad bits (e.g. ...ZWV=) still fail exactly as the old node validator rejected them.
  • New regression fixtures prove all three malformed variants are rejected before docker is invoked: escaped trailing newline in kid, escaped trailing newline in secretBase64, a noncanonical pad-bit variant at valid apparent length, and a trailing-newline sourceCommit in the release pin (the release_filter got the same \\A\\z hardening).

Test plan

  • node --test infra/test/deploy-assets.test.mjs — 29/29, including all new rejection cases (asserted: nonzero exit, docker never invoked)
  • Root npm test — 625 workspace + 36 infra, 0 fail
  • npm run build, git diff --check clean
  • CI build-test (20), (22)

Generated with Devin

@thetangstr
thetangstr force-pushed the fix/compose-up-strict-jq branch 2 times, most recently from 71803ab to b015d89 Compare September 17, 2026 04:11
jq regexes are Oniguruma, where $ anchors before a trailing newline,
and $(...) strips trailing newlines — so a kid or secretBase64 with an
escaped trailing newline could pass the wrapper and then fail the
stricter JS validator inside the container. Anchor every pattern with
\A...\z to bind true string ends, and keep the secretBase64 pattern
strictly structural (alphabet + padding shape only). Canonicality —
including nonzero pad bits, which a structural regex cannot detect —
is proven by the original decode-and-reencode equality check, now safe
because the pattern already excludes whitespace from the extracted
value. Same \A\z hardening applied to the release-pin filter.

Constraint: systemd PATH provides jq and coreutils only — no node
Rejected: derive canonicality from the structural regex alone | nonzero
  pad bits match the pattern but never round-trip, weakening the old
  validator's exact semantics
Confidence: high
Scope-risk: narrow
Directive: never use $ anchors in jq test() for security-relevant
  patterns — always \A...\z; and never let a structural regex stand in
  for a canonicality proof
Tested: infra suite 29/29 incl. escaped-trailing-newline kid and
  secretBase64, a noncanonical pad-bit variant, and a newline-suffixed release pin, all rejected
  before docker; git diff --check clean
Not-tested: live systemd run on the production box
@thetangstr
thetangstr force-pushed the fix/compose-up-strict-jq branch from b015d89 to 4eb6aa8 Compare September 17, 2026 04:13
@thetangstr
thetangstr merged commit 9f02f34 into main Sep 17, 2026
2 checks passed
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