abhigyanpatwari/GitNexus · critical · ValueError

unsafe git object id: {orig_sha!r}

Error message

unsafe git object id: {orig_sha!r}

What it means

Thrown by implementation_diff_digest before orig_sha is interpolated into a shell command string. Because the SHA is substituted into a `/bin/sh -c` pipeline (`git diff <sha> -- ... | sha256sum`), it must be a bare hex object id; anything else is a command-injection vector. The regex [0-9a-fA-F]{40,64} accepts both SHA-1 (40) and SHA-256 (64) git object ids and rejects everything else.

Source

Thrown at eval/workflow_bench/runner_artifacts.py:383

    if not result.ok:
        raise ManagedProcessError(command, result)
    return result.stdout_tail


def _prepare_untracked_for_diff(sandbox: SandboxSession) -> None:
    _sandbox_git(sandbox, ["add", "--intent-to-add", "-A"])


def implementation_diff_digest(
    sandbox: SandboxSession,
    orig_sha: str,
    *,
    prepare_untracked: bool = True,
) -> str:
    """Digest non-plan final work entirely inside the containment boundary."""

    if not re.fullmatch(r"[0-9a-fA-F]{40,64}", orig_sha):
        raise ValueError(f"unsafe git object id: {orig_sha!r}")
    if prepare_untracked:
        _prepare_untracked_for_diff(sandbox)
    command = (
        "/usr/bin/git -c core.fsmonitor=false diff --no-ext-diff --no-textconv --binary "
        f"{orig_sha} -- . ':(exclude)docs/plans' ':(exclude).claude/skills' "
        "| /usr/bin/sha256sum"
    )
    result = sandbox.run(
        ["/bin/sh", "-c", command],
        timeout=60,
        env=build_sandbox_environment(),
    )
    if not result.ok:
        raise ManagedProcessError(command, result)
    digest = result.stdout_tail.strip().split()[0] if result.stdout_tail.strip() else ""
    if not re.fullmatch(r"[0-9a-f]{64}", digest):
        raise RuntimeError("sandboxed git diff did not produce a SHA-256 digest")
    return digest

View on GitHub (pinned to d540b00184)

Solutions

  1. Resolve to a full object id before calling: `git rev-parse <ref>^{commit}` and pass the 40/64-char hex output.
  2. Never pass abbreviated SHAs, branch names, or HEAD to implementation_diff_digest.
  3. If you compute orig_sha from agent output, validate it with the same regex before passing it in.
  4. Treat any non-hex value as a programming error in the caller, not a runtime user input.

Example fix

// before — passing a short sha
implementation_diff_digest(sandbox, orig_sha='abc1234')

// after — resolve to a full object id first
full = run_checked(['git','-C',str(worktree),'rev-parse','HEAD^{commit}']).strip()
implementation_diff_digest(sandbox, orig_sha=full)
Defensive patterns

Strategy: validation

Validate before calling

import re

def is_safe_object_id(sha: str) -> bool:
    return isinstance(sha, str) and bool(re.fullmatch(r'[0-9a-fA-F]{40,64}', sha))

Type guard

import re

def is_full_sha(s: str) -> bool:
    return isinstance(s, str) and bool(re.fullmatch(r'[0-9a-fA-F]{40,64}', s))

Try / catch

try:
    implementation_diff_digest(sandbox, orig_sha)
except ValueError as e:
    if 'unsafe git object id' in str(e):
        # resolve the ref to a full object id and retry
        raise
    raise

Prevention

When it happens

Trigger: re.fullmatch(r'[0-9a-fA-F]{40,64}', orig_sha) fails — orig_sha is None, a shortened SHA, a ref name, contains uppercase G/z, or includes shell metacharacters.

Common situations: Caller passed a short SHA (git's abbreviated 7-12 chars) instead of the full id; passed a branch/tag ref; passed HEAD or a relative ref; the SHA came from an untrusted source and contains a newline/semicolon.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/4ad4323259eeeefa. Report an issue: GitHub.