Skip to content

fix: escape credential.helper path so git-credential-nostr survives spaces - #3435

Open
1nderboi wants to merge 1 commit into
block:mainfrom
1nderboi:fix/3298-credential-helper-path-spaces
Open

fix: escape credential.helper path so git-credential-nostr survives spaces#3435
1nderboi wants to merge 1 commit into
block:mainfrom
1nderboi:fix/3298-credential-helper-path-spaces

Conversation

@1nderboi

Copy link
Copy Markdown

Summary

  • git-credential-nostr failed to run whenever its resolved absolute path contained a space (e.g. /Users/x/My Apps/git-credential-nostr) — git concatenates the raw credential.helper config value with the operation (get/store/erase) and runs the result through sh -c, so an unescaped path gets word-split by that shell.
  • A naive fix of wrapping the whole value in shell quotes doesn't work: git detects an absolute-path helper by checking whether the unescaped string starts with a path separator (or drive letter on Windows) — a leading quote defeats that check and git falls back to treating the value as a bare helper name (git credential-'...'), which is worse.
  • Fix: backslash-escape each shell-special character in the path individually, leaving the leading separator untouched, so it still passes git's absolute-path check and survives the sh -c word-split. Added one shared helper (git_credential_helper_config_value in managed_agents/discovery.rs) and used it at both call sites that build this config value:
    • commands/project_git_exec.rs::credential_helper_config_value
    • managed_agents/runtime.rs's GIT_CONFIG_VALUE_0 write

Verified the underlying escaping approach against a real git credential fill invocation (plain path, path with a space, path with an apostrophe) before writing the Rust, since the shared build machine hit disk-space limits mid-build (see test plan).

Fixes #3298

Test plan

  • Added unit tests for plain / space / apostrophe paths in commands/project_git_exec.rs (credential_helper_config_value_uses_forward_slashes, credential_helper_config_value_escapes_spaces, credential_helper_config_value_escapes_apostrophes)
  • rustfmt --check clean on all three changed files
  • Manually reproduced the original bug and verified the fix end-to-end against real git credential fill (outside the Rust build, due to shared-machine disk pressure — see below)
  • cargo test -p desktop --lib — could not run to completion locally; the shared build machine repeatedly hit No space left on device while three agents ran concurrent full-workspace builds. Please confirm green in CI.

…paces

Git builds the credential helper invocation by concatenating the raw
credential.helper config value with the operation (get/store/erase) and
running the result through `sh -c`. An unescaped absolute path containing
a space gets word-split by that shell, so
git-credential-nostr never runs when installed under a path like
"/Users/x/My Apps/git-credential-nostr".

Naively wrapping the whole value in shell quotes doesn't work either:
git detects an absolute-path helper by checking whether the *unescaped*
string starts with a path separator (or drive letter on Windows), so a
leading quote character defeats that check and git falls back to
treating the value as a bare helper name.

Fix: backslash-escape each shell-special character individually, leaving
the leading separator intact. Added a shared
git_credential_helper_config_value helper in managed_agents/discovery.rs
and used it from both call sites that build this config value
(project_git_exec.rs's credential_helper_config_value and runtime.rs's
GIT_CONFIG_VALUE_0 write), plus unit tests for plain/space/apostrophe
paths.

Fixes block#3298

Signed-off-by: 1nderboi <201919958+1nderboi@users.noreply.github.com>
@1nderboi
1nderboi requested a review from a team as a code owner July 28, 2026 23:54
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.

[Bug] git-credential-nostr absolute path with spaces breaks credential.helper (Projects + agents)

1 participant