Skip to content

fix(agents): address hook review feedback - #2208

Merged
shunkakinoki merged 1 commit into
mainfrom
codex/harden-agent-github-hooks
Aug 4, 2026
Merged

fix(agents): address hook review feedback#2208
shunkakinoki merged 1 commit into
mainfrom
codex/harden-agent-github-hooks

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #2207, which repository automation merged before its review feedback could be incorporated.

  • normalize lowercase direct HTTP mutation methods and avoid HTTPie GET false positives
  • preserve feature branches ending in main or master while recognizing remote-qualified protected destinations
  • make shell-alias command splitting portable to macOS
  • structurally validate active hook wiring and make Git test setup independent of global default-branch configuration

Validation

  • shellspec spec/block_git_push_spec.sh spec/block_gh_settings_spec.sh spec/agent_github_hook_wiring_spec.sh (83 examples)
  • shellcheck on all changed shell scripts and specs
  • make shell-test-dev (1,805 ShellSpec examples and 422 Fish tests)
  • make shell-check-dev
  • make nix-format-check
  • git diff --check

Merge handling

This PR is intentionally a draft so repository automation does not merge it before manual review.


Summary by cubic

Hardened GitHub guardrail hooks to avoid false positives and catch more direct mutation attempts. Improves branch protection handling and cross-platform parsing, with stronger wiring and tests.

  • Bug Fixes
    • Prevent HTTPie GET false positives; normalize method and block lowercase http, curl, and xh mutations.
    • Protect remote-qualified destinations across ref formats; allow feature branches ending in main/master; use portable command splitting on macOS.
    • Validate hook wiring by parsing tool configs with jq; tests no longer depend on global default branch and cover new cases.

Written for commit 39d347c. Summary will update on new commits.

Review in cubic

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@shunkakinoki, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 859e0f99-1990-4c18-9387-5b563f59e3e7

📥 Commits

Reviewing files that changed from the base of the PR and between d28a486 and 39d347c.

📒 Files selected for processing (5)
  • config/shared/hooks/block-gh-settings.sh
  • config/shared/hooks/block-git-push.sh
  • spec/agent_github_hook_wiring_spec.sh
  • spec/block_gh_settings_spec.sh
  • spec/block_git_push_spec.sh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@shunkakinoki
shunkakinoki marked this pull request as ready for review August 4, 2026 07:38
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@indent-zero

indent-zero Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Addresses review feedback on the shared agent GitHub hooks to cut false positives (HTTPie GETs with query strings/options, branch names like feature/main) while closing a real bypass (lowercase HTTP verbs) and tightening the wiring test so it verifies each hook is actually registered under the Bash matcher for every agent config.

  • block-gh-settings.sh: tightened HTTPie implicit-body regex so leading -,?,:,/ chars no longer trigger, and normalise http_method to uppercase before matching the mutation set (blocks lowercase http delete … / curl --request delete …).
  • block-git-push.sh: check_destination now only strips refs/remotes/<r>/<b>, refs/heads/<b>, heads/<b>, or <r>/<b> when <r> is a real git remote — so feature/main/release/master are no longer misread as protected. Segment splitter swapped from sed to tr ';&|' '\n' (equivalent).
  • spec/agent_github_hook_wiring_spec.sh: replaces raw grep -Fq on the JSON with a per-config jq extraction of PreToolUse Bash-matcher commands, then exact-line match — catches misplaced hooks that the old check would silently accept.
  • Spec coverage added for HTTPie GET with query string / equals-form option, lowercase HTTPie & curl mutations, and feature branches whose final component is main/master. Test repos now init with -b main for deterministic fixtures.

Issues

2 potential issues found:

  • Latent: For Codex/Claude the wiring spec extracts hooks with select(.matcher == "Bash") (exact string), while Grok uses a regex. A future compound matcher like "Bash|Edit" in Codex/Claude would make the assertion fail even though the guardrail is wired correctly — apply the grok-style test("(^|\\|)Bash($|\\|)") uniformly. → Autofix
  • Latent: HTTPie's URL-query form key==value (e.g. http .../repos/o/r/hooks per_page==10) is still classified as an implicit POST body and blocked, because the regex matches per_page= and then greedily consumes =10. Pre-existing (old regex had the same problem), but worth distinguishing == from = while this area is being tightened. → Autofix

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@shunkakinoki
shunkakinoki merged commit 1ebcc95 into main Aug 4, 2026
37 of 39 checks passed
@shunkakinoki
shunkakinoki deleted the codex/harden-agent-github-hooks branch August 4, 2026 07:38
if [[ -z $http_method ]] &&
printf '%s\n' "$command" | grep -Eiq '(^|[;&|[:space:]])(http|https|xh)([[:space:]]|$)' &&
printf '%s\n' "$command" | grep -Eq '(^|[[:space:]])[^[:space:]=]+(:=|=)[^[:space:]]+'; then
printf '%s\n' "$command" | grep -Eq '(^|[[:space:]])[^-[:space:]=?:/][^[:space:]=?/]*(:=|=)[^[:space:]]+'; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latent: HTTPie query-param syntax still false-blocked.

The new regex correctly excludes URL/option shapes, but HTTPie's URL query-parameter form k==v still matches: the middle class stops before the first =, then (:=|=) consumes the first =, and [^[:space:]]+ greedily eats =value.

Repro on any protected suffix:

printf '{"tool_input":{"command":"http https://api.github.com/repos/o/r/hooks per_page==10"}}' \
  | bash config/shared/hooks/block-gh-settings.sh
# exit 2: A direct POST request to a repository control-plane endpoint was requested.

This is pre-existing behavior (the old regex had it too), so it's not a regression — but since this PR is loosening HTTPie handling anyway, worth teaching the regex that == is a URL query param, not a body. One option is to require the value not start with =, e.g. (:=|=)[^=[:space:]][^[:space:]]*.

local config="$1"
case "$config" in
config/codex/hooks.json | config/claude/settings.json)
jq -r '.hooks.PreToolUse[] | select(.matcher == "Bash") | .hooks[]?.command' "$config"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latent: exact-string matcher check will miss compound matchers.

For Codex/Claude you use select(.matcher == "Bash"), while Grok (line 18) already uses the more permissive test("(^|\\|)Bash($|\\|)"). If either Codex or Claude ever adopts a compound matcher such as "Bash|Edit" (Claude-side documentation supports this shape), the extraction returns nothing and the wiring test would fail even though the hook is correctly registered. Applying the same regex form uniformly across all three branches would be forward-compatible.

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