* test(116): reproduce base64-scan illegal byte sequence on non-UTF8 fixtures
Adds regression fixtures and failing tests for #116. Empirically verified
on macOS 26.5 (BSD tr) that `tr -cd '[:print:]'` under LC_CTYPE=en_US.UTF-8
exits non-zero with "Illegal byte sequence" when its input contains bytes
that are not valid UTF-8 start sequences (e.g. lone continuation bytes 0x80–0x9F).
The base64-scan.sh root cause is a known bash pitfall: the assignment
`local printable_count=$(... | tr -cd '[:print:]' | ...)`
uses `local` on the same line, which always returns exit 0, masking the tr
failure. Result: tr errors surface only on stderr; the scan exits 0 with
incomplete coverage (false-clean signal).
Two new tests FAIL on origin/main:
- "scans non-UTF8 file containing a b64 blob without emitting Illegal byte sequence"
- "dir scan with non-UTF8 files under non-C locale completes cleanly within 30s"
Fixtures in tests/fixtures/base64-locale/:
utf8-with-injection.md — UTF-8 + base64-encoded injection (positive control)
non-utf8-with-b64blob.bin — raw 0x80-0x9F bytes + b64 blob that decodes to
binary (this is the reproducer that triggers tr error)
mixed-encoding.txt — valid UTF-8 + lone continuation bytes
clean-text.md — negative control (must not be flagged)
Test helpers use spawnSync (not execFileSync) so stderr is captured even on
exit 0 — execFileSync only surfaces stderr via the thrown error on non-zero exit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(116): locale-safe base64-scan with portable timeout and partial-scan signaling
Root cause: BSD tr(1) on macOS rejects input bytes that are not valid UTF-8
start sequences with "Illegal byte sequence" when LC_CTYPE is set to any
UTF-8 locale (e.g. en_US.UTF-8). Empirically verified on macOS 26.5 using
`man tr` (ENVIRONMENT section) and direct testing:
printf '\x80\x81hello' | LC_ALL=en_US.UTF-8 tr -cd '[:print:]'
→ tr: Illegal byte sequence (exit 1)
The error is silently masked because base64-scan.sh uses `local` on the same
line as the tr assignment. Bash's `local` built-in always returns 0 regardless
of the subshell's exit code — so the tr failure never propagates under
`set -euo pipefail`. Result: the script exits 0 with truncated printable_count,
causing binary-decoded blobs to be skipped (false-clean, security gap).
Fix: `export LC_ALL=C` at script level (line 33).
- LC_ALL=C forces the POSIX C locale throughout: tr treats every byte 0x00–0xFF
as a valid character, never rejects high bytes.
- Safe for all script operations: all injection patterns are ASCII, grep POSIX
classes ([:space:], [:print:]) behave correctly in C locale, base64 -d is
locale-independent.
- Script-level export is appropriate because all operations in this script are
byte-level; no multi-byte character handling is needed.
Additional hardening:
- MAX_LINE_BYTES=1048576 guard in extract_and_check_blobs: lines longer than
1 MiB are skipped with an explicit "partial scan" warning to stderr. This
bounds grep -oE cost on pathological inputs (minified JS, single-line binary
blobs) and satisfies the "partial-scan failure signaling" requirement.
- Portable run_with_timeout + is_timeout_exit: probes for GNU timeout,
gtimeout (homebrew), and falls back to perl alarm(N)+exec. Defined for
future use guarding external sub-commands. Verified: no timeout binary on
this macOS host, perl alarm fallback works correctly (exit 142 on SIGALRM).
Test-rigor fixes applied per test-rigor skill review:
- Fixture validity check: assert `isInvalidUtf8` (round-trip length difference)
rather than checking for a specific byte range — the property that matters is
"file is not valid UTF-8", not "file has bytes in 0x80–0x9F".
- FAIL assertions: assert `FAIL: ${INJECTION_FIXTURE}` (specific filepath) not
`result.stdout.includes('FAIL')` — rules out false-positives on other fixtures.
- Test name: renamed "mixed-encoding file does not cause scan to abort or hang"
to accurately describe what is tested (no extractable blobs → exits clean).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(116): fix shellcheck warnings in base64-scan.sh
Address SC2034 (unused variables) and SC2329 (functions never invoked)
warnings flagged by shellcheck after the locale-hardening changes.
SC2034 fixes (pre-existing):
- Remove unused SCRIPT_DIR variable (set but never referenced)
- Remove unused printable_ratio local (declared but no assignment or use)
SC2329 fixes (new functions from this PR):
- Add shellcheck disable=SC2329 annotations on run_with_timeout,
_init_timeout_cmd, and is_timeout_exit — these are intentionally
defined as infrastructure helpers, not called from the main loop.
The line-length guard (MAX_LINE_BYTES) is the primary runtime
protection; the timeout helpers are available for future use.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#116): exclude scanner fixtures from base64-scan diff mode
Add tests/fixtures/* to should_skip_file() so deliberate prompt-injection
samples in scanner fixture directories are never flagged in CI diff-mode.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>