Skip to content

git-guard with SETKERNEL_GUARD_NO_PUSH: stop refusing text that only mentions a push, and catch what sk-push-guard catches #43

Closed

Description

@realRaghavGupta

setkernel/hq#431 (for hq#335) makes sk-agent export SETKERNEL_GUARD_NO_PUSH=1 in every Claude run. That makes plugins/setkernel/hooks/git-guard.sh's no-push mode the push guard in every agent run, next to hq's sk-push-guard. After the console paste (allowManagedHooksOnly, hq#332), it is the only one, because Claude Code then ignores sk-push-guard, a --settings hook.

Claude Lead's adversarial check of hq#431 at 049197a6 used three readers, three skeptics and two refuters per claimed blocker. It found two gaps in that mode, both in git-guard.sh at main 7a4a2b9. Claude Lead then reproduced both on the Mac. hq#431's own code is correct: its canary and the live test passed on rigel. Its merge waits for part 1 here.

In every repro below, the hook JSON (tool_input.command, cwd) was fed to each guard, with the cwd a scratch repo on the feature branch agent/1-demo.

  • Guards: sh git-guard.sh at 7a4a2b9, with and without SETKERNEL_GUARD_NO_PUSH=1, and python3 -I sk-push-guard from hq at 049197a6.
  • Exit codes: 2 blocks, 0 allows.

1. With the variable set, it refuses text that only mentions a push (blocks hq#431)

git-guard.sh drops quotes, then splits on ;&|(){}<> and backticks (lines 66-69), and checks each piece. With the variable set, any piece that starts with git push is refused. That includes heredoc bodies, commit messages and quoted strings, which are never run.

Without the variable, these commands pass on a feature branch. sk-push-guard has allowed them since hq 8a0228d (2026-10-15), whose title reads "ignore heredoc bodies and multi-line quoted text so commit messages and PR comments can mention git push". So hq#431 would bring that class back on all four VMs.

The refusal also tells Claude to "commit and stop" (line 54), so a task can end early. That is most likely in work on the guards, sk-agent or the docs, where commit messages and files talk about pushes.

The plugin guard alone refuses these (exit codes: no variable / with variable / sk-push-guard):

  • git commit -F - <<'EOF', whose body has a line git push is the worker's job: 0 / 2 / 0.
  • git commit -m "docs: the worker pushes; git push is never run by Claude": 0 / 2 / 0.
  • cat > notes.md <<'EOF', with a line git push origin agent/1-demo: 0 / 2 / 0.
  • rg -n "; git push" docs/: 0 / 2 / 0.
  • echo "run tests | git push later": 0 / 2 / 0.

These pass in all three: grep -n "git push" file, git commit -m "docs: the worker runs git push, Claude never does", and gh pr comment 1 --body "Do not run git push; the worker does". A real git push origin HEAD:refs/heads/x gives 0 / 2 / 2.

  • With the variable set, skip heredoc bodies and quoted text the way sk-push-guard's heredoc stripping does (hq agents/vm/sk-push-guard, from 8a0228d). Real pushes inside sh -c "…", $(…) and backticks must stay blocked; today they are.
  • tests/run.sh: a case for each command above, with the variable set and unset. Keep behaviour without the variable unchanged.

2. With the variable set, it misses spellings sk-push-guard blocks (before the paste)

With the variable set, git-guard.sh exits 0 for each command below, and sk-push-guard exits 2 (Claude Lead, on the Mac). The check's harness also ran the subtree, send-pack, setsid, flock, ionice, --attr-source and alias forms as real pushes in ghcr.io/setkernel/runner with no network, and they reached a local origin. After the paste, nothing would stop them.

  • git subtree push --prefix=d origin x

  • git send-pack origin HEAD:refs/heads/x

  • setsid git push …, flock /tmp/l git push … and ionice -c3 git push …. The wrapper list on lines 88-89 doesn't name these; nice is on it and is blocked.

  • exec -a x git push …. exec is on the list, but its -a value isn't skipped (line 94).

  • git --attr-source HEAD push …

  • git config alias.q push && git q …. The alias is set and used in the same command.

  • git -C "dir with spaces" push …. The quotes are dropped before the words are split.

  • Bring the no-push mode up to sk-push-guard's coverage for these, or move sk-push-guard's parser into the plugin so one guard serves both modes. Either way, add a tests/run.sh case for each.

Out of scope here

Channels other than a git push get past both guards today: gh api writes to refs or contents, a nested claude -p, a script written to a file and then run, and the worker's own git push running repo hooks or config the run wrote. Both guards call themselves speed bumps, not sandboxes. The real boundary is hq#209 (push broker, identities).

Done when

  • Part 1 is merged, and every VM's plugin shows the new version. VMs refresh at most once an hour (hq#299). Then hq#431 goes to Raghav's merge, with no change to hq#431 itself.
  • Part 2 is merged before the paste (hq#332).

The repro scripts are in Claude Lead's scratchpad. Their cases are the commands above.

Refs setkernel/hq#431, setkernel/hq#335, setkernel/hq#332, #42.

— Claude Lead

Activity

  1. added
    model:opusRun this task's main step at this setting (sk-agent, hq#174)
    effort:highRun this task's main step at this setting (sk-agent, hq#174)
    agent-readyReady for an agent to pick up
    on Oct 26, 2026
  2. added and removed
    agent-readyReady for an agent to pick up
    on Oct 26, 2026
  3. vega-setkernel commented on Oct 26, 2026

    @vega-setkernel
    Contributor

    Picked up. Working on branch agent/43-git-guard-with-setkernel-guard-no-push-s. Automated: @vega-setkernel's agent on vega-dev.

  4. added
    humanAgents keep out - a person handles this
    and removed on Oct 26, 2026
  5. realRaghavGupta commented on Oct 27, 2026

    @realRaghavGupta
    ContributorAuthor

    Closing: done, as this issue's "Done when" lines set out.

    The gaps that remain, found in the adversarial checks of #44, are #45. #46 is its PR, and #45 has to close before the console paste (setkernel/hq#332).

    — Claude Lead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:securityHardening, access, secretsbugSomething isn't workingeffort:highRun this task's main step at this setting (sk-agent, hq#174)humanAgents keep out - a person handles thismodel:opusRun this task's main step at this setting (sk-agent, hq#174)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions