fix(0.4.0): G9 injection — Codex round-1 blockers#33
Merged
Conversation
added 6 commits
April 19, 2026 00:07
Address four post-merge Codex findings on PR #25 (G9): - denyOnSuspicious silently loosened 'block' action for non-bst consumers - 7 ASCII pattern library bypassed by NBSP/zero-width/Unicode space - decodeBase64Strings exported but never wired into middleware path - regex-timeout records lacked stable verdict field Audit entry: .rea/audit.jsonl hash 44281f39 Refs: #25 Signed-off-by: Jake Strawn <bandy.strawn@clarityhouse.press>
…odex round-1) Codex adversarial review of the G9 blocker-patch (commit c1c79b6) flagged two follow-ups: - [P1] act as a / act as an patterns were too broad. At read tier a single literal match escalates to likely_injection (always deny), so benign prose such as "this proxy can act as a bridge" or "the service can act as an intermediary" would be falsely denied. Dropped those phrases and also pretend to be. The remaining additions (pretend you are, roleplay as) retain second-person / explicit-roleplay framing which is rare in ordinary documentation. Added two regression guards in injection.test.ts asserting the dropped phrases do NOT match in benign contexts. - [P3] InjectionMetadataSchema was advertised as "exported for external consumer validation" but the module is not in the published package's exports map (only ., ./policy, ./middleware, ./audit are public). Downgraded the docstring to flag it as internal-only today, with a G9.2 follow-up to promote it to a public entrypoint. Updated the changeset body to match. Quality gates: pnpm lint / type-check / test / build all green. Injection suite now 53 tests (2 new regression guards). Signed-off-by: Jake Strawn <bandy.strawn@clarityhouse.press>
…t in block mode
Replace hand-enumerated ZERO_WIDTH_RE with \p{Default_Ignorable_Code_Point}/gu
(Unicode property class, Node 22+) to cover all invisible bypass vectors including
soft hyphen, BIDI isolation controls, variation selector-16, and combining grapheme
joiner that the old regex missed.
In createInjectionMiddleware, the scanTimedOut branch now fails closed when
action === 'block': a timeout under block policy denies the request rather than
allowing it through with verdict:'error'.
Replace flaky best-effort timeout tests (wrapped in 'if (meta !== undefined)')
with deterministic tests that mock wrapRegex via vi.mock to always fire onTimeout,
unconditionally asserting warn-mode fail-open and block-mode fail-closed behavior.
Signed-off-by: Jake Strawn <bandy.strawn@clarityhouse.press>
…0.3.x suspicious default Signed-off-by: Jake Strawn <bandy.strawn@clarityhouse.press>
…ault-behavior doc Signed-off-by: Jake Strawn <bandy.strawn@clarityhouse.press>
Signed-off-by: Jake Strawn <bandy.strawn@clarityhouse.press>
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.
Summary
Addresses four post-merge Codex adversarial-review blockers on PR #25 (G9 three-tier injection classifier). Patch-level bump targeted for the 0.4.0 line so rea can ship with the injection middleware behaving as advertised.
Closes the round-1 + round-2 Codex concerns on that classifier in a single follow-up PR:
denyOnSuspiciousdefaulted tofalseonaction: 'block' + flag unset, silently loosening 0.2.xinjection_detection: blockbehavior for non-bst consumers upgrading to 0.3.0. Middleware now defaults totruein that case, restoring 0.2.x parity. The zod schema no longer applies a default forsuspicious_blocks_writes, so absence is distinguishable from an explicitfalse. Consumers who want the looser warn-only posture must opt in viainjection.suspicious_blocks_writes: false.bst-internal*profiles continue to pintrue.pretend you are,roleplay as). Broader candidates (act as a/act as an/pretend to be) were considered but dropped — at read tier a single literal match escalates tolikely_injection, which would deny benign prose like "this proxy can act as a bridge." Pattern-set extensibility via policy is filed as G9.1 follow-up.decodeBase64Stringswas exported and tested but never wired into the middleware execution path — 28 lines of dead code advertised as a second-opinion base64 probe. It is now invoked after the primary scan; any phrase detected in a decoded whole-string payload is merged intobase64DecodedMatchesand triggers classification rule chore(ci)(deps): bump actions/setup-node from 4.0.3 to 6.3.0 #2 (likely_injection).injection.regex_timeoutbut noverdictfield underinjection. A newverdict: 'error'value is emitted when a timeout produces no actionable signal, giving downstream audit consumers a stable record shape. A newInjectionMetadataSchemazod schema is added to the injection middleware module for internal test coverage; promoting it to a public package entrypoint is tracked as G9.2 follow-up (the module is not reachable through the currentexportsmap).Behavior change
Non-bst consumers with
injection_detection: blockwill now block onsuspiciousclassifications by default, restoring 0.2.x parity. This is a narrow tightening for consumers who upgraded to 0.3.0 without adding the newinjection:block. To restore the 0.3.0 looser behavior, opt out explicitly:likely_injectioncontinues to deny unconditionally in all configurations.Follow-ups filed
InjectionMetadataSchemato a public entrypoint for downstream audit-consumer validation (Helix, etc.)Test plan
pnpm lint— clean (includinglint:regexsafe-regex check)pnpm type-check— strict, 0 errorspnpm test— 440 passed / 1 skipped (53 injection-specific tests, 2 new regression guards against the dropped broad patterns)pnpm build— cleantsc -p tsconfig.build.json/codex-reviewround-1 blockers addressed in follow-up commit (narrowed persona-swap patterns, schema scope clarified).changeset/fix-0.4.0-g9-injection.md(patch bump)src/gateway/middleware/)Files touched
src/gateway/middleware/injection.ts—normalizeForMatch,decodeBase64Stringswiring,verdict: 'error',InjectionMetadataSchema, narrowed phrase library,denyOnSuspicioustri-state defaultsrc/gateway/middleware/injection.test.ts— 4 newdescribeblocks covering all 4 findings + 2 regression guardssrc/policy/loader.ts— drop default onsuspicious_blocks_writes(make presence observable)src/policy/loader.test.ts— updated expectations for absence-preservationsrc/policy/types.ts—suspicious_blocks_writes?: boolean(optional)src/gateway/server.ts— conditionally omitsuspiciousBlocksWriteswhen unset (preserves tri-state to middleware underexactOptionalPropertyTypes).changeset/fix-0.4.0-g9-injection.md— patch changeset