fix(wake): #912 exercise the digest/HMAC trust suite in real CI
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
Make the wake digest/HMAC suite RUN and PASS in the real Woodpecker CI
runner (Alpine/musl, root), then hard-require the HMAC legs in CI.
Root cause of the runner-only D1/D4/D5/D6 failures (a masked-local run
passed, so it was runner-specific): a TOOLCHAIN divergence, not locale or
root. digest.sh's _scrub_ctrl used GNU-sed `\xNN` hex-escape byte matching.
The CI runner is node:24-alpine, whose sed is BusyBox — BusyBox sed REJECTS
a `\xNN` character range ("bad regex ... Invalid character range"), aborting
the whole scrub sed and silently VOIDING the scrub. Every scrubbed value
collapsed to empty, cascading into D1 (blank locators), D4 (no scrub/redact,
SHA blanked), D5 (blank agent prefix), D6 (blank [digest] class). Confirmed
by reproducing the exact 9-assertion failure in the ci-base image as root.
Fix (at the correct layer — a wake digest must render identically on any
runner):
- digest.sh _scrub_ctrl: patterns are now LITERAL bytes (printf %b), matching
byte-identically under GNU sed (glibc dev) and BusyBox sed (Alpine CI).
Verified identical output on both. Contract preserved: two-tier trust,
exit-4 hard-locator FAIL-LOUD, secret-scrub, and 40-hex SHA preservation
all unchanged — deterministic, not weakened.
- test-wake-digest-hmac.sh D4: replaced PCRE `grep -qP` (BusyBox grep has no
-P; the `&&` silently skipped the check in CI) with portable literal-byte
`grep -E` ranges (two disjoint bidi/zero-width ranges, excluding legit
U+2014 em-dash).
CI enablement:
- Dockerfile.ci + .woodpecker/ci.yml test step: add openssl (the non-circular
HMAC signer) so H1/H2, beacon B12, install I8 can run. The apk add in the
test step covers PR pipelines before ci-base rebuilds.
- Flip the 3 openssl skip-guards (digest whole-file, beacon B12, install I8)
to HARD-REQUIRE openssl when CI is set (Woodpecker CI=woodpecker) and FAIL
loud if absent; KEEP the skip for openssl-less local dev.
- manifest.txt: wake 0.6.2 -> 0.6.3 (digest.sh scrub portability; precedent).
Red-first verified in the ci-base container (root): D4 catches a broken
redaction, H1 catches a tamper that doesn't break the MAC, B12/I8 catch a
corrupted signer.
Closes #912
Part of #892
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb
This commit is contained in:
@@ -88,16 +88,37 @@ EOF
|
||||
# _scrub_ctrl (stdin) — strip ANSI escape sequences, Unicode bidi controls,
|
||||
# zero-width characters, and C0/C1 control bytes. Byte-exact under LC_ALL=C so a
|
||||
# multibyte control sequence cannot slip through a locale-dependent class.
|
||||
#
|
||||
# PORTABILITY (#912): the byte patterns are LITERAL bytes (materialized via
|
||||
# `printf %b`), NOT GNU-sed `\xNN` hex escapes. `\xNN` is a GNU-sed extension;
|
||||
# BusyBox sed (the Alpine/musl CI runner, running as root) REJECTS a `\xNN`
|
||||
# character range with "bad regex ... Invalid character range", which aborted
|
||||
# the whole sed and silently VOIDED the scrub in CI — the digest suite's D1/D4/
|
||||
# D5/D6 all failed only in the Woodpecker runner because every scrubbed value
|
||||
# collapsed to empty. Literal bytes match identically under GNU sed (glibc dev)
|
||||
# and BusyBox sed (Alpine CI): a wake digest must render byte-for-byte the same
|
||||
# regardless of the runner's sed implementation. LC_ALL=C keeps every match
|
||||
# byte-exact (no locale-dependent multibyte class).
|
||||
_scrub_ctrl() {
|
||||
local ESC p280 p281 aa ae a6 a9 x8b x8f a0 bom alm
|
||||
ESC="$(printf '%b' '\x1b')" # U+001B ESC
|
||||
p280="$(printf '%b' '\xe2\x80')" # UTF-8 lead bytes for U+2000..U+203F
|
||||
p281="$(printf '%b' '\xe2\x81')" # UTF-8 lead bytes for U+2040..U+207F
|
||||
aa="$(printf '%b' '\xaa')"; ae="$(printf '%b' '\xae')" # U+202A..U+202E bidi
|
||||
a6="$(printf '%b' '\xa6')"; a9="$(printf '%b' '\xa9')" # U+2066..U+2069 isolates
|
||||
x8b="$(printf '%b' '\x8b')"; x8f="$(printf '%b' '\x8f')" # U+200B..U+200F zero-width
|
||||
a0="$(printf '%b' '\xa0')" # U+2060 word joiner
|
||||
bom="$(printf '%b' '\xef\xbb\xbf')" # U+FEFF BOM/ZWNBSP
|
||||
alm="$(printf '%b' '\xd8\x9c')" # U+061C arabic letter mark
|
||||
LC_ALL=C sed -E \
|
||||
-e 's/\x1b\[[0-9;?]*[ -/]*[@-~]//g' \
|
||||
-e 's/\x1b[@-Z\\-_]//g' \
|
||||
-e 's/\xe2\x80[\xaa-\xae]//g' \
|
||||
-e 's/\xe2\x81[\xa6-\xa9]//g' \
|
||||
-e 's/\xe2\x80[\x8b-\x8f]//g' \
|
||||
-e 's/\xe2\x81\xa0//g' \
|
||||
-e 's/\xef\xbb\xbf//g' \
|
||||
-e 's/\xd8\x9c//g' |
|
||||
-e 's/'"$ESC"'\[[0-9;?]*[ -/]*[@-~]//g' \
|
||||
-e 's/'"$ESC"'[@-Z\\-_]//g' \
|
||||
-e 's/'"$p280"'['"$aa"'-'"$ae"']//g' \
|
||||
-e 's/'"$p281"'['"$a6"'-'"$a9"']//g' \
|
||||
-e 's/'"$p280"'['"$x8b"'-'"$x8f"']//g' \
|
||||
-e 's/'"$p281$a0"'//g' \
|
||||
-e 's/'"$bom"'//g' \
|
||||
-e 's/'"$alm"'//g' |
|
||||
LC_ALL=C tr -d '\000-\010\013\014\016-\037\177'
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user