Skip to content

Fix #116: CWD-safe git config calls (git -C /) — git >= 2.55 fatals from an unreadable sudo-inherited CWD - #120

Merged
steffenmaechtel merged 4 commits into
masterfrom
fix/issue-116
Oct 1, 2026
Merged

steffenmaechtel merged 4 commits into
masterfrom
fix/issue-116

Conversation

@steffenmaechtel

Copy link
Copy Markdown
Owner

Fixes #116

Summary

opk update on hosts with git >= 2.55 and a mode-750 developer home
printed fatal: error reading '/home/<dev>/.git' and — worse — silently
failed to ensure the agent's safe.directory '*' (the issue #17 git
access exception).

Root cause

sudo keeps the invoking user's working directory, so

sudo -u opencode -H git config --global …

runs with the developer's home as CWD — unreadable for the agent user.
git >= 2.55 dies during setup (fatal: error reading '<cwd>/.git',
rc 128) before ever touching the global config. The update itself
continued (the &&-guarded success message just disappeared), which is
why the only visible symptom was the fatal line.

Verified live on the reporting host and reproduced in an alpine/git
2.55 container: correct HOME, stock sudoers, no env leaks — the CWD is
the trigger. Homes with 755 and git < 2.55 are immune (why the e2e
suite, 755 dev home + container git 2.43, never caught it).

Fix

git -C / on all four privileged git config calls (update.sh and
install.sh safe.directory blocks, install.sh's two gitconfig backup
listings): chdir to / before anything else — --global ignores the
worktree anyway. Confirmed on the reporting host: the manual call now
returns the safe.directory entries instead of the fatal.

Tests

  • test-git-config.sh pins the -C / shape and asserts no bare
    (CWD-inheriting) sudo … git config --global remains; hardened per
    the wave review (continuation-joining + comment-stripping extraction,
    safe.directory-scoped counts, loud-FAIL on zero) with three negative
    controls: revert fails loud, a wrapped bare call is caught, a
    commented example does not trip.

Review

Wave review snapshot 0.0.39n (docs/design/review/2026-10-01-v0.0.39-n.md):
no new MED/HIGH; findings limited to the wave's own test checks (fixed
in this branch) and two recorded residuals (unconditional "ensured" log
line; backups lack -H under env_keep-HOME sudo configs).

Verification

make test (28 suites) · make lint · make check-version — green.
make e2e 272/0 · make e2e-rootless 47/0 (run at the fix commit;
the follow-ups are test/formatting only, token-identical commands).

…rom an unreadable sudo-inherited CWD

Root cause (reproduced live on the reporting host and in an alpine/git
2.55 container): sudo keeps the invoking user's CWD, so
'sudo -u opencode -H git config --global ...' run from the developer's
home (mode 750, unreadable for the agent user) makes git >= 2.55 die
with "fatal: error reading '<cwd>/.git'" before touching the global
config — the update continues (the &&-guarded success message just
disappears), but safe.directory '*' is never ensured and the fatal
line leaks into the output. Homes with 755 and git < 2.55 never see
it, which is why the e2e (755 dev home, container git 2.43) stayed
green.

Fix: 'git -C /' on every privileged git config call (update.sh
safe.directory block, install.sh safe.directory block + the two
gitconfig backup listings) — chdir to / first; --global ignores the
worktree anyway. Live-verified: rc 0 where the bare call fatals.

Tests (test-git-config.sh): wiring checks pin the -C / shape and assert
no bare sudo git config --global remains.

make test/lint/check-version green; make e2e 272/0, e2e-rootless 47/0.
Keeps the line-length ratchet branch (feature/test-line-length) mergeable
without a baseline regen -- its update.sh/install.sh baselines were
burned to zero and these lines would have counted as new violations.
…ents and fail loud

0.0.39n review findings (test quality): the count check was satisfiable
by the --list backups alone (no safe.directory attribution), both checks
were line-anchored (a wrapped call escaped, a commented example tripped),
and grep -c's rc-1-on-zero aborted the set -e suite silently instead of
reaching the fail branch (dead failure path, dash-verified). Extraction
now joins backslash continuations, drops comments, scopes the count to
the safe.directory get-all/add pair, and || true keeps zero counts a
loud FAIL. Negative controls verified live: revert -> FAIL with counts,
wrapped bare call -> caught, commented example -> no trip.

@steffenmaechtel steffenmaechtel left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Reviewed

@steffenmaechtel
steffenmaechtel merged commit 74b414e into master Oct 1, 2026
6 checks passed
@steffenmaechtel
steffenmaechtel deleted the fix/issue-116 branch October 1, 2026 01:10
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.

Fatal error in update to master version 0.0.40 (pre release)

1 participant