@bookedsolid/rea 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,8 @@ You may read additional files in the repo if needed for context, but do so read-
33
33
  2. **Validate Codex availability** — if `/codex` is not installed, report and stop. Do not silently fall back to another reviewer.
34
34
  3. **Prepare the Codex invocation** — construct the adversarial-review prompt with the diff, commit log, and any relevant context files.
35
35
  4. **Invoke `/codex:adversarial-review`** — this call flows through the REA middleware chain (audit → kill-switch → tier → policy → redact → injection → execute → result-size-cap).
36
+
37
+ **Model pinning (0.16.1+):** when the codex plugin's adversarial-review supports model overrides, request `gpt-5.4` with `model_reasoning_effort: high` to match the push-gate's iron-gate defaults. Pre-0.16.1, in-session adversarial reviews ran on whatever the plugin defaulted to (likely `codex-auto-review` at medium reasoning) — meaningfully WEAKER than the push-gate's `gpt-5.4` + `high`. This caused a "in-session review passes, push-gate review fails" pattern reported by helix across 014 / 015 / 016. If the plugin call accepts model parameters, pass them. If it does not, fall back to invoking `codex exec review --base <ref> --json --ephemeral -c model="gpt-5.4" -c model_reasoning_effort="high"` directly via `Bash` — same shape the push-gate uses (see `src/hooks/push-gate/codex-runner.ts::runCodexReview`). The cost of the stronger model is small relative to the cost of shipping a release with a P1 bypass that gets caught at consumer push time.
36
38
  5. **Parse the Codex output** — extract structured findings.
37
39
  6. **Classify findings** by category: security, correctness, edge cases, test gaps, API design, performance.
38
40
  7. **Assign verdict**: `pass` (no material findings), `concerns` (findings worth addressing but not blocking), `blocking` (findings that must be fixed before merge).
@@ -71,9 +71,17 @@ _rea_split_segments() {
71
71
  # under any encoding we care about (any agent that intentionally
72
72
  # included this string would already be obviously trying to confuse
73
73
  # the splitter — and even then, the worst case is fail-closed).
74
+ # 0.16.1 helix-016 P1 fix: also split on single `&` (background-process
75
+ # operator). Pre-fix the splitter only broke on `&&|||;|`; a command like
76
+ # `sleep 1 & git push --force` was treated as ONE segment whose first
77
+ # token is `sleep`, and `any_segment_starts_with($CMD, 'git push')`
78
+ # missed the force-push entirely. Add `&` to the separator set, but
79
+ # AFTER `&&` is already swapped out so we don't break it apart.
74
80
  printf '%s\n' "$cmd" \
75
81
  | sed -E 's/>\|/__REA_GTPIPE_a8f2c1__/g' \
76
- | sed -E 's/(\|\||&&|;|\|)/\n/g' \
82
+ | sed -E 's/&&/__REA_LOGAND_a8f2c1__/g' \
83
+ | sed -E 's/(\|\||;|\||&)/\n/g' \
84
+ | sed -E 's/__REA_LOGAND_a8f2c1__/\n/g' \
77
85
  | sed -E 's/__REA_GTPIPE_a8f2c1__/>|/g'
78
86
  }
79
87
 
@@ -151,6 +159,30 @@ any_segment_matches() {
151
159
  return 1
152
160
  }
153
161
 
162
+ # Return 0 if any segment of $1 (RAW — no prefix-stripping) matches the
163
+ # extended regex $2. Use this for checks where the prefix itself IS the
164
+ # signal — e.g. H10's `HUSKY=0 git commit` detection (the prefix-stripper
165
+ # would strip the `HUSKY=0` before any_segment_matches sees it). Also
166
+ # right for H15 (`REA_BYPASS=...`) and H16 (alias/function defs).
167
+ #
168
+ # 0.16.1 helix-016 sibling fix: H10 baseline corpus regressed from
169
+ # 0.15.0 because it migrated to `any_segment_matches` which strips
170
+ # env-var prefixes. The check needs the raw segment to fire.
171
+ any_segment_raw_matches() {
172
+ local cmd="$1"
173
+ local pattern="$2"
174
+ local segment
175
+ while IFS= read -r segment; do
176
+ # Trim leading whitespace for clean anchor matching, but otherwise
177
+ # leave the segment intact (env-var assignments preserved).
178
+ segment="${segment#"${segment%%[![:space:]]*}"}"
179
+ if printf '%s' "$segment" | grep -qiE "$pattern"; then
180
+ return 0
181
+ fi
182
+ done < <(_rea_split_segments "$cmd")
183
+ return 1
184
+ }
185
+
154
186
  # Return 0 if any segment of $1 (after prefix-stripping) STARTS WITH
155
187
  # the extended regex $2. Case-insensitive. Returns 1 if no segment
156
188
  # starts with the pattern.
@@ -216,7 +216,7 @@ if any_segment_starts_with "$CMD" 'git[[:space:]]+commit.*--no-verify'; then
216
216
  fi
217
217
 
218
218
  # H10: HUSKY=0 bypass — suppresses all git hooks without --no-verify
219
- if any_segment_matches "$CMD" '(^|[[:space:];]|&&|\|\|)HUSKY=0[[:space:]]+git[[:space:]]+(commit|push|tag)'; then
219
+ if any_segment_raw_matches "$CMD" '^HUSKY=0[[:space:]]+git[[:space:]]+(commit|push|tag)'; then
220
220
  add_high \
221
221
  "HUSKY=0 — bypasses all husky git hooks" \
222
222
  "Setting HUSKY=0 disables pre-commit, commit-msg, and pre-push safety gates without --no-verify." \
@@ -243,8 +243,15 @@ if any_segment_starts_with "$CMD" "rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*f[[:space:]
243
243
  "Alt: Move to a temp location first, or use 'rm -ri' for interactive deletion."
244
244
  fi
245
245
 
246
- # H12: curl/wget piped directly to shell (supply chain attack vector)
247
- if any_segment_matches "$CMD" '(curl|wget)[^|]*\|[[:space:]]*(bash|sh|zsh|fish)'; then
246
+ # H12: curl/wget piped directly to shell (supply chain attack vector).
247
+ # 0.16.1 helix-016 P1 fix: this check requires BOTH the curl/wget call
248
+ # AND the `| sh` to appear in the same shell pipeline. The 0.16.0
249
+ # refactor moved this into `any_segment_matches`, but the segmenter
250
+ # splits on `|` first — so `curl https://x | sh` decomposed into two
251
+ # segments (`curl https://x`, `sh`) and the regex (which requires both
252
+ # in one segment) never matched. Pipe-RCE is fundamentally a
253
+ # multi-segment property and must be checked against the raw command.
254
+ if printf '%s' "$CMD" | grep -qiE '(curl|wget)[^|]*\|[[:space:]]*(sudo[[:space:]]+)?(bash|sh|zsh|fish)'; then
248
255
  add_high \
249
256
  "curl/wget piped to shell — remote code execution" \
250
257
  "Executing remote scripts without inspection is a major supply chain risk." \
@@ -268,7 +275,7 @@ if any_segment_starts_with "$CMD" 'git[[:space:]]+-c[[:space:]]+core\.hookspath'
268
275
  fi
269
276
 
270
277
  # H15: REA_BYPASS env var — attempted escape hatch
271
- if any_segment_matches "$CMD" '(^|[[:space:];]|&&|\|\|)REA_BYPASS[[:space:]]*='; then
278
+ if any_segment_raw_matches "$CMD" '^REA_BYPASS[[:space:]]*='; then
272
279
  add_high \
273
280
  "REA_BYPASS env var — unauthorized bypass attempt" \
274
281
  "Setting REA_BYPASS is not a supported escape mechanism and indicates a bypass attempt." \
@@ -276,7 +283,7 @@ if any_segment_matches "$CMD" '(^|[[:space:];]|&&|\|\|)REA_BYPASS[[:space:]]*=';
276
283
  fi
277
284
 
278
285
  # H16: alias/function definitions containing bypass strings
279
- if any_segment_matches "$CMD" '(alias|function)[[:space:]]+[a-zA-Z_]+.*(--(no-verify|force)|HUSKY=0|core\.hookspath)'; then
286
+ if any_segment_raw_matches "$CMD" '^(alias|function)[[:space:]]+[a-zA-Z_]+.*(--(no-verify|force)|HUSKY=0|core\.hookspath)'; then
280
287
  add_high \
281
288
  "Alias/function definition with bypass — circumventing safety gates" \
282
289
  "Defining aliases or functions that embed bypass flags defeats safety hooks." \
@@ -73,15 +73,35 @@ extract_packages() {
73
73
  # Anchor to start: only match when the install command is the FIRST
74
74
  # thing on the segment, optionally preceded by `sudo` / `exec` /
75
75
  # `time` / etc.
76
- if printf '%s' "$segment" | grep -qiE '^(sudo[[:space:]]+|exec[[:space:]]+|time[[:space:]]+)*(npm[[:space:]]+(install|i|add)|pnpm[[:space:]]+(add|install|i)|yarn[[:space:]]+add)[[:space:]]+'; then
76
+ #
77
+ # 0.16.1 helix-016 P2 fix: also strip leading KEY=VALUE env-var
78
+ # assignments. Pre-fix the prefix allow-list only permitted
79
+ # sudo/exec/time, so `CI=1 pnpm add foo` and
80
+ # `NODE_ENV=development npm install bar` bypassed the audit
81
+ # entirely. POSIX shell allows any number of leading KEY=VALUE
82
+ # assignments before the command word; we strip them the same
83
+ # way the shell does.
84
+ local stripped_segment
85
+ stripped_segment=$(printf '%s' "$segment" | sed -E 's/^([[:space:]]*[A-Za-z_][A-Za-z0-9_]*=[^[:space:]]+[[:space:]]+)+//')
86
+
87
+ if printf '%s' "$stripped_segment" | grep -qiE '^(sudo[[:space:]]+|exec[[:space:]]+|time[[:space:]]+)*(npm[[:space:]]+(install|i|add)|pnpm[[:space:]]+(add|install|i)|yarn[[:space:]]+add)[[:space:]]+'; then
77
88
  # Strip the leading prefix wrappers + install command, leaving args.
78
89
  local after_cmd
79
- after_cmd=$(printf '%s' "$segment" | sed -E 's/^(sudo[[:space:]]+|exec[[:space:]]+|time[[:space:]]+)*(npm[[:space:]]+(install|i|add)|pnpm[[:space:]]+(add|install|i)|yarn[[:space:]]+add)[[:space:]]+//')
90
+ after_cmd=$(printf '%s' "$stripped_segment" | sed -E 's/^(sudo[[:space:]]+|exec[[:space:]]+|time[[:space:]]+)*(npm[[:space:]]+(install|i|add)|pnpm[[:space:]]+(add|install|i)|yarn[[:space:]]+add)[[:space:]]+//')
80
91
 
81
92
  for token in $after_cmd; do
82
93
  if [[ "$token" == -* ]]; then continue; fi
83
94
  if [[ "$token" == ./* || "$token" == /* || "$token" == ../* ]]; then continue; fi
84
95
  if [[ -z "$token" ]]; then continue; fi
96
+ # 0.16.1: tighten token classification (helix-016 sibling concern).
97
+ # A "package name" is something that doesn't contain shell
98
+ # metacharacters — `2>&1`, `$VAR`, etc. are never valid npm
99
+ # package names. Skip any token containing `=`, `>`, `<`, `&`,
100
+ # `|`, `;`, `$`, backtick, or quotes.
101
+ if [[ "$token" == *=* || "$token" == *">"* || "$token" == *"<"* ||
102
+ "$token" == *"&"* || "$token" == *"|"* || "$token" == *";"* ||
103
+ "$token" == *'$'* || "$token" == *'`'* ||
104
+ "$token" == *'"'* || "$token" == *"'"* ]]; then continue; fi
85
105
  # `npm view` can't validate `@workspace:*` / `link:` / `file:`
86
106
  # prefixes (workspace protocols). Skip them — they're never npm
87
107
  # registry packages.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/rea",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "Agentic governance layer for Claude Code — policy enforcement, hook-based safety gates, audit logging, and Codex-integrated adversarial review for AI-assisted projects",
5
5
  "license": "MIT",
6
6
  "author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",