Skip to content

fix: make the pre-push key scan usable from worktrees and on macOS - #2

Merged
tnunamak merged 2 commits into
mainfrom
maciej/worktree-and-macos-prepush-fix
Aug 18, 2026
Merged

fix: make the pre-push key scan usable from worktrees and on macOS#2
tnunamak merged 2 commits into
mainfrom
maciej/worktree-and-macos-prepush-fix

Conversation

@maciejwitowski

Copy link
Copy Markdown
Contributor

The optional EVM key scan could not run at all in two common setups. Both fail closed, so nothing was exposed — but a hook that cannot be installed, or cannot pass, is a hook developers route around with --no-verify. That is the outcome this policy exists to prevent.

1. Git worktrees

Git exports GIT_DIR into hook processes. In a linked worktree that value is an absolute path, so git -C "$shared_dir" ... still resolves against the pushing repository: the hook read the repo's HEAD, origin and status instead of the pinned policy checkout's, and refused with Vana scanner checkout is at <repo HEAD>, expected <policy SHA>. In a normal checkout GIT_DIR is the relative .git, which happens to resolve correctly under -C — which is why this only bites worktrees.

Adds a shared_git helper that scrubs the inherited repository environment and routes every command that must target the policy checkout through it, in both hooks/pre-push and scripts/install-pre-push.sh. Commands that scan the pushing repository keep the inherited environment, which is what they want.

The scrub list is taken from git rev-parse --local-env-vars rather than hardcoded, plus the GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM/GIT_CONFIG_NOSYSTEM overrides that are not in that list, with a hardcoded fallback for a git too old to answer.

Why the config vars matter. git -c remote.origin.url=... push exports GIT_CONFIG_PARAMETERS into hooks. Scrubbing only the directory variables fixes the worktree bug but makes the origin integrity check satisfiable from the caller's environment rather than the checkout's real config. That is not reachable on main today — the GIT_DIR bug masks it by failing at the SHA check first — so it is a hole this fix would have opened had it stopped at the directory variables. Verified both ways, see below.

2. macOS checksum verification

macOS ships its own /sbin/sha256sum (sha256sum (Darwin) 1.0). It rejects the GNU long options used in install-gitleaks.sh, so command -v sha256sum succeeded, the GNU branch was taken, and the download check failed for every download regardless of authenticity — the shasum fallback was never reachable on the one platform that needed it. install-pre-push.sh prepare therefore died on usage: sha256sum [-bctwz] and the hook could never be installed.

Prefers shasum -a 256, which accepts --check/--status on both platforms, keeping sha256sum for minimal images that ship coreutils but no perl.

Note verify-gitleaks.sh is deliberately untouched: its plain sha256sum "$file" produces byte-identical <hash> <path> output on Darwin and GNU, so there is no bug there.

Verification

On macOS with a stock PATH (no Homebrew coreutils), against a scratch policy cache:

  • install-pre-push.sh prepare completes; previously it died on the sha256sum usage error
  • the hook runs from a linked worktree with GIT_DIR set and exits 0 on a clean range; previously it refused before scanning anything
  • detection is unchanged — the same hook still exits 1 and reports Potential EVM private key detected on a range containing one
  • shasum -a 256 --check --status was checked to exit 0 on a matching digest and 1 on a mismatch, so verification is still real
  • spoof test: with the policy cache's origin set to https://github.com/attacker/evil.git and GIT_CONFIG_PARAMETERS/GIT_CONFIG_COUNT set to claim vana-com/.github, this branch refuses at the origin check. A directory-variables-only scrub accepted it and proceeded.

Downstream

vana-sdk pins this repo by SHA (CENTRAL_POLICY_SHA in .github/scripts/install-evm-key-scan-hook.sh) and has the same GIT_DIR bug in its own bootstrap — fixed in vana-com/vana-sdk#189. That PR needs its pinned SHA bumped to this merge commit before the fix takes effect end to end.

🤖 Generated with Claude Code

The optional EVM key scan could not run at all in two common setups. Both
fail closed, so nothing was exposed — but a hook that cannot be installed or
cannot pass is a hook developers route around with --no-verify, which is the
outcome this is meant to prevent.

Git worktrees
-------------
Git exports GIT_DIR into hook processes. In a linked worktree that value is an
absolute path, so `git -C "$shared_dir" ...` still resolves against the pushing
repository: the hook read the *repo's* HEAD, origin and status instead of the
pinned policy checkout's, and refused with "Vana scanner checkout is at
<repo HEAD>, expected <policy SHA>". In a normal checkout GIT_DIR is the
relative ".git", which happens to resolve correctly under -C, which is why this
only bites worktrees.

Adds a `shared_git` helper that scrubs the inherited repository environment
(GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY,
GIT_ALTERNATE_OBJECT_DIRECTORIES, GIT_COMMON_DIR) and routes every command that
must target the policy checkout through it, in both hooks/pre-push and
scripts/install-pre-push.sh. Commands that scan the pushing repository keep the
inherited environment, which is what they want.

macOS checksum verification
---------------------------
macOS ships its own /sbin/sha256sum ("sha256sum (Darwin) 1.0"). It rejects the
GNU long options used in install-gitleaks.sh, so `command -v sha256sum`
succeeded, the GNU branch was taken, and the download check failed for every
download regardless of authenticity — the `shasum` fallback was never reachable
on the one platform that needed it. Prefer `shasum -a 256`, which accepts
--check/--status on both platforms, and keep sha256sum for minimal images that
ship coreutils but no perl.

Verified on macOS with a stock PATH (no Homebrew coreutils):
  - install-pre-push.sh prepare now completes; previously it died on
    `usage: sha256sum [-bctwz]`
  - the hook runs from a linked worktree with GIT_DIR set and exits 0 on a
    clean range; previously it refused before scanning
  - detection is unchanged: the same hook still exits 1 and reports
    "Potential EVM private key detected" on a range containing one
  - `shasum -a 256 --check --status` was checked to exit 0 on a matching
    digest and 1 on a mismatch, so the verification is still real

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tnunamak
tnunamak merged commit 9990452 into main Aug 18, 2026
1 check passed
@tnunamak
tnunamak deleted the maciej/worktree-and-macos-prepush-fix branch August 18, 2026 17:27
tnunamak pushed a commit to vana-com/vana-sdk that referenced this pull request Aug 18, 2026
…ixed policy

Addresses both review findings on this PR.

The hardcoded scrub list omitted GIT_CONFIG_GLOBAL, so the origin check was
satisfiable from the caller's environment: a policy cache whose real origin is
attacker-controlled passes validation when the caller supplies a global config
naming vana-com/.github. Verified against this branch on git 2.53.0 — the check
returned the spoofed URL, and returns the real (attacker) URL once the variable
is scrubbed. The origin check runs before the SHA check, so the spoof is
reachable rather than masked by a stale-cache refusal.

Deriving the list from `git rev-parse --local-env-vars` also covers the
repository-local variables the hardcoded set missed (GIT_SHALLOW_FILE,
GIT_GRAFT_FILE, GIT_REPLACE_REF_BASE, GIT_IMPLICIT_WORK_TREE) plus
GIT_CONFIG_PARAMETERS/GIT_CONFIG_COUNT. The GIT_CONFIG_* file overrides are not
in that list, so they are added explicitly; a hardcoded fallback covers older git.

Bumps CENTRAL_POLICY_SHA to vana-com/.github@9990452 (the merge of
vana-com/.github#2), which is what makes this end-to-end: until the pinned
policy contained the same fix, prepare and run failed one layer later.

Verified against the real merged policy from a linked worktree with GIT_DIR set:
prepare succeeds, a clean range exits 0, a range containing a key in a
secret-shaped declaration is still detected and exits 1, and a cache with a
spoofed origin is refused.

Assisted-by: AI
tnunamak added a commit to vana-com/vana-smart-contracts that referenced this pull request Aug 18, 2026
…igin check (#71)

* ci(hooks): resolve the policy cache from a worktree and harden the origin check

Applies the fix already merged in vana-com/.github#2 and vana-com/vana-sdk#189.

Git exports an absolute GIT_DIR into hook processes from a linked worktree, so
a bare `git -C "$policy_dir"` read the pushing repo's remote/HEAD/status and
refused a valid policy cache. Routes every policy-targeting command through a
`policy_git` helper that scrubs the inherited repository environment, derived
from `git rev-parse --local-env-vars` plus the GIT_CONFIG_* file overrides —
without GIT_CONFIG_GLOBAL the origin check is satisfiable from the caller's
environment against an attacker-controlled cache.

Bumps CENTRAL_POLICY_SHA to vana-com/.github@9990452 so the fix is end-to-end.

Assisted-by: AI

* ci: pin the key-scan workflow to the fixed policy

The reusable workflow file is byte-identical between 5f1b4b1 and 9990452, so
CI behavior is unchanged — but the workflow calls install-gitleaks.sh, which
9990452 fixes for macOS (Darwin ships its own /sbin/sha256sum that rejects the
GNU long options, so the download checksum failed for every download). Keeps
the CI pin and the bootstrap pin on the same reviewed policy commit.

Assisted-by: AI

---------

Co-authored-by: tnunamak <everyone@appears.blue>
tnunamak added a commit to vana-com/vana that referenced this pull request Aug 18, 2026
…igin check (#24)

The EVM key-scan bootstrap cannot pass from a git worktree, and its
policy-cache origin check is satisfiable from the caller's environment.
Both are fixed in the central policy (vana-com/.github#2) and in
vana-sdk (vana-com/vana-sdk#189, merged); this applies the same fix
here.

## Worktree bug

Git exports `GIT_DIR` into hook processes. In a linked worktree that
value is an **absolute** path, so `git -C "$policy_dir" ...` still
resolves against the pushing repository and reports ITS remote, HEAD and
status instead of the policy cache's. Validation then rejects a
perfectly good cache:

```
Refusing unexpected policy-cache origin: ~/.local/share/vana-secret-scan/policy/<sha>
```

In a normal checkout `GIT_DIR` is the relative `.git`, which happens to
resolve correctly under `-C` — which is why this only bites worktrees. A
hook that cannot pass is a hook developers route around with
`--no-verify`, which is the outcome this policy exists to prevent.

## Origin check hardening

The scrub list is derived from `git rev-parse --local-env-vars` rather
than hardcoded. Beyond the directory variables that fix the worktree
bug, that covers the repository-local variables (`GIT_SHALLOW_FILE`,
`GIT_GRAFT_FILE`, `GIT_REPLACE_REF_BASE`, `GIT_IMPLICIT_WORK_TREE`) and
`GIT_CONFIG_PARAMETERS`/`GIT_CONFIG_COUNT`, which `git -c foo=bar push`
exports into hooks.

`GIT_CONFIG_GLOBAL` is **not** in that list, so it is added explicitly.
Without it, a caller can point `remote.origin.url` at `vana-com/.github`
from its own environment and satisfy the origin check against a cache
whose real origin is attacker-controlled. Verified on git 2.53.0: with
the directory-only scrub the check returned the spoofed URL; with
`GIT_CONFIG_GLOBAL` scrubbed it returns the real (attacker) URL and
refuses.

The `git rev-parse --show-toplevel` that discovers the pushing repo
deliberately keeps the inherited environment — that is exactly what it
wants.

## Policy SHA bump

`CENTRAL_POLICY_SHA` moves to `9990452` (the merge of
vana-com/.github#2). This is what makes the fix end-to-end: until the
pinned policy contained the same fix, `install`/`status` would fail one
layer later in the central scripts.

## Verification

Against the real merged policy, from a linked worktree with `GIT_DIR`
set the way git sets it for hooks:

- **before**: `Refusing unexpected policy-cache origin`, nothing scanned
- **after**: validation passes and the policy cache is fetched at the
pinned SHA
- **spoof test**: with the cache's origin set to
`https://github.com/attacker/evil.git` and `GIT_CONFIG_GLOBAL` claiming
`vana-com/.github`, this branch refuses at the origin check
- **detection unchanged**: verified in vana-sdk against the same policy
— a range containing a key in a secret-shaped declaration still exits 1
with `Potential EVM private key detected`

This file is byte-identical across the five remaining repos, so the same
patch applies to each.

Assisted-by: AI

---------

Co-authored-by: tnunamak <everyone@appears.blue>
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.

2 participants