Skip to content

Let git read the operator's own configuration - #13

Merged
flyingrobots merged 1 commit into
mainfrom
fix/pass-user-git-config
Aug 23, 2026
Merged

flyingrobots merged 1 commit into
mainfrom
fix/pass-user-git-config

Conversation

@flyingrobots

Copy link
Copy Markdown
Member

The bug

EnvironmentPolicy filters the environment down to an allowlist before spawning git. That allowlist contained no way for git to find the operator's configuration — no HOME, no GIT_CONFIG_GLOBAL, no XDG_CONFIG_HOME.

Without HOME, git cannot read ~/.gitconfig at all. It does not fail when that happens — it invents an identity from the system account and hostname:

configured in ~/.gitconfig:   James Ross <james@flyingrobots.dev>
what actually got committed:  James Ross <james@Jamess-MacBook-Pro-2.local>

That address exists nowhere and verifies against nothing. On a forge it reports as unverified with reason no_user, so a branch rule requiring signed commits treats every such commit as unsigned. The caller gets no indication their configuration was never consulted.

Found downstream in think, where every memory commit was misattributed this way. The workaround there had been to write user.name/user.email into the target repository — which then rewrote the committer identity of whatever directory it was pointed at, including a developer's own source checkout.

The fix

HOME, XDG_CONFIG_HOME, GIT_CONFIG_GLOBAL and USERPROFILE now pass through — the four paths git uses to locate user configuration, including the Windows home directory.

The boundary is preserved

The policy's stated purpose is blocking variables that override security settings, and that still holds. Letting git find configuration the operator owns is ordinary git behaviour; letting a caller inject configuration is not:

Variable Status
GIT_CONFIG_PARAMETERS still blocked
GIT_EXEC_PATH still blocked
GIT_TEMPLATE_DIR still blocked

A test asserts all three stay blocked even when HOME is present, so the new keys cannot be used as a route around them.

Verification

Run through the repository's own harness (docker-guard refuses to run tests on the host), all three runtimes:

✅ node-test passed     Test Files 26 passed (26)   Tests 203 passed (203)
✅ bun-test passed      203 pass, 0 fail, 364 expect() calls
✅ deno-test passed     26 passed (240 steps), 0 failed

The four new assertions failed before the change (4 failed | 199 passed).

Beyond the unit tests, test/UserGitConfig.test.js drives a real commit-tree against a temporary HOME containing a known ~/.gitconfig and asserts the resulting author is the configured identity rather than an invented one.

End-to-end through a consumer: with this patch applied to think's copy and think's own identity workaround removed, captures commit as James Ross <james@flyingrobots.dev>, matching the global config exactly. Unpatched, the same code produces james@Jamess-MacBook-Pro-2.local.

Behaviour change

Callers relying on git being unable to see user configuration — expecting a fixed synthetic author, or an environment where core.* settings could not apply — will observe different behaviour. GIT_AUTHOR_* / GIT_COMMITTER_* still take precedence and remain the way to pin an identity explicitly.

Worth a maintainer decision on version bump: it is a fix, but it changes what git sees. Minor seems right given the allowlist is internal; major is defensible.

EnvironmentPolicy filtered the environment down to an allowlist before
spawning git, and that allowlist contained no way for git to find the
operator's configuration. Without HOME, git cannot read ~/.gitconfig at all.

It does not fail when that happens. It invents an identity from the system
account and the hostname, so commits are written as addresses like
user@laptop.local that exist nowhere, verify against nothing, and silently
replace the identity the operator actually configured. A caller has no
indication their configuration was never consulted.

HOME, XDG_CONFIG_HOME, GIT_CONFIG_GLOBAL and USERPROFILE now pass through:
the four paths git uses to locate user configuration, including the Windows
home directory.

The policy's real boundary is preserved. Letting git *find* configuration the
operator owns is ordinary git behaviour; letting a caller *inject*
configuration is not. GIT_CONFIG_PARAMETERS, GIT_EXEC_PATH and
GIT_TEMPLATE_DIR stay blocked, and a test asserts they stay blocked even when
HOME is present.

Verified in Docker per the repository's guard: 203/203 across 26 files. The
four new assertions failed before the change. Beyond the unit tests,
test/UserGitConfig.test.js drives a real commit-tree with a configured
~/.gitconfig and asserts the resulting author is the configured identity
rather than an invented one.

Callers relying on git being unable to see user configuration will observe
different behaviour; GIT_AUTHOR_* and GIT_COMMITTER_* still take precedence
and remain the way to pin an identity explicitly.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 953d5c02-dab7-4218-936b-00c106dcc7cf

📥 Commits

Reviewing files that changed from the base of the PR and between b976526 and d7f7253.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/domain/services/EnvironmentPolicy.js
  • test/UserGitConfig.test.js
  • test/domain/services/EnvironmentPolicy.test.js
📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test-multi-runtime
🧰 Additional context used
🪛 ast-grep (0.45.0)
test/UserGitConfig.test.js

[warning] 35-38: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(
path.join(homePath, '.gitconfig'),
[user]\n\tname = ${CONFIGURED_NAME}\n\temail = ${CONFIGURED_EMAIL}\n
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

🔇 Additional comments (4)
src/domain/services/EnvironmentPolicy.js (1)

6-25: LGTM!

Also applies to: 41-48

test/domain/services/EnvironmentPolicy.test.js (1)

57-96: LGTM!

test/UserGitConfig.test.js (1)

1-79: LGTM!

CHANGELOG.md (1)

8-27: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Git operations can now discover user configuration through supported environment settings.
    • Commits use the configured Git identity when available, while explicit author and committer overrides continue to work.
  • Bug Fixes
    • Improved environment handling so legitimate Git configuration discovery works without allowing direct configuration overrides.
  • Documentation
    • Added changelog documentation describing Git configuration and identity behavior.

Walkthrough

EnvironmentPolicy now passes Git configuration discovery variables to spawned Git processes while blocking direct configuration injection variables. Tests cover filtering, platform-specific home resolution, and commit identity from a temporary user configuration.

Changes

Git configuration passthrough

Layer / File(s) Summary
Environment policy and filtering coverage
src/domain/services/EnvironmentPolicy.js, test/domain/services/EnvironmentPolicy.test.js
The allowlist includes HOME, XDG_CONFIG_HOME, GIT_CONFIG_GLOBAL, and USERPROFILE. Tests confirm these variables pass through while direct or unsafe Git variables remain blocked.
User Git configuration integration
test/UserGitConfig.test.js, CHANGELOG.md
An integration test verifies that a real commit uses identity values from temporary user Git configuration. The changelog records the passthrough and explicit author/committer precedence.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant UserGitConfigTest
  participant EnvironmentPolicy
  participant spawnedGit as spawned Git
  participant userGitConfig as user .gitconfig
  UserGitConfigTest->>EnvironmentPolicy: filter configuration discovery variables
  EnvironmentPolicy->>spawnedGit: pass filtered environment
  spawnedGit->>userGitConfig: discover user identity
  spawnedGit-->>UserGitConfigTest: create commit with configured author
Loading

Poem

A rabbit checks the Git config trail,
Through home and XDG without fail.
Unsafe settings stay behind,
While author names are neatly signed.
Commit hops cleanly down the line.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: Git can read the operator's own configuration.
Description check ✅ Passed The description accurately explains the bug, fix, security boundary, tests, and behavior change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@flyingrobots
flyingrobots merged commit 76c7c56 into main Aug 23, 2026
3 checks passed
@flyingrobots
flyingrobots deleted the fix/pass-user-git-config branch August 23, 2026 22:38
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