auth setup-git installs broken credential helper — sed eats the appended verb #1

Closed
opened 2026-05-14 16:20:23 +00:00 by stephen · 1 comment
Owner

Summary

fj auth setup-git (v0.1.0, from rasterandstate/tap) installs a git credential helper that fails on every invocation because git appends the verb (get, store, erase) to the end of the command line, and sed treats it as a filename.

Repro

On a fresh machine:

brew tap rasterandstate/tap
brew install fj
fj auth login                       # add rasterhub.com
fj auth setup-git
git ls-remote https://rasterhub.com/<owner>/<repo>.git

Result:

sed: get: No such file or directory
thread 'main' panicked … failed printing to stdout: Broken pipe (os error 32)
fatal: could not read Password for 'https://<user>@rasterhub.com': Device not configured

Root cause

The installed helper is literally:

!fj auth token --host rasterhub.com | sed 's/^/password=/'

Git appends the verb to the helper command, so it actually runs:

fj auth token --host rasterhub.com | sed 's/^/password=/' get

get is now a positional arg to sed, which interprets it as a filename and bails. The broken pipe panic comes from fj flushing stdout after sed has already died.

Fix

Wrap the helper so the verb lands as $1 and gets ignored (or matched against get):

!f() { [ "$1" = get ] && fj auth token --host <host> | sed "s/^/password=/"; }; f

This is what fj auth setup-git should install per host.

Also worth checking

On macOS, git-credential-osxkeychain can intercept the request before any host-specific helper runs if there's a stale credential for the host. fj auth setup-git could clear any existing osxkeychain entry for the host as part of setup:

printf 'protocol=https\nhost=<host>\n\n' | git credential-osxkeychain erase

Environment

  • fj 0.1.0
  • macOS (Darwin 25.3.0)
  • git via Homebrew (/opt/homebrew/opt/git)
  • Forgejo: rasterhub.com (Gitea 1.22.0 / Forgejo 15.0.2)
## Summary `fj auth setup-git` (v0.1.0, from `rasterandstate/tap`) installs a git credential helper that fails on every invocation because git appends the verb (`get`, `store`, `erase`) to the end of the command line, and `sed` treats it as a filename. ## Repro On a fresh machine: ```sh brew tap rasterandstate/tap brew install fj fj auth login # add rasterhub.com fj auth setup-git git ls-remote https://rasterhub.com/<owner>/<repo>.git ``` Result: ``` sed: get: No such file or directory thread 'main' panicked … failed printing to stdout: Broken pipe (os error 32) fatal: could not read Password for 'https://<user>@rasterhub.com': Device not configured ``` ## Root cause The installed helper is literally: ``` !fj auth token --host rasterhub.com | sed 's/^/password=/' ``` Git appends the verb to the helper command, so it actually runs: ``` fj auth token --host rasterhub.com | sed 's/^/password=/' get ``` `get` is now a positional arg to `sed`, which interprets it as a filename and bails. The broken pipe panic comes from `fj` flushing stdout after sed has already died. ## Fix Wrap the helper so the verb lands as `$1` and gets ignored (or matched against `get`): ``` !f() { [ "$1" = get ] && fj auth token --host <host> | sed "s/^/password=/"; }; f ``` This is what `fj auth setup-git` should install per host. ## Also worth checking On macOS, `git-credential-osxkeychain` can intercept the request before any host-specific helper runs if there's a stale credential for the host. `fj auth setup-git` could clear any existing osxkeychain entry for the host as part of setup: ```sh printf 'protocol=https\nhost=<host>\n\n' | git credential-osxkeychain erase ``` ## Environment - fj 0.1.0 - macOS (Darwin 25.3.0) - git via Homebrew (`/opt/homebrew/opt/git`) - Forgejo: rasterhub.com (Gitea 1.22.0 / Forgejo 15.0.2)
Author
Owner

Fixed in b68fb98 on main.

Root cause was as diagnosed: git appends "$@" inside the helper's sh -c command line, sed gobbles the verb as a filename, the credential dance fails. New helper wraps the pipeline in a shell function so the trailing arg is swallowed by f instead of leaking to sed.

Recovery on an already-broken machine:

fj auth setup-git --host <hostname>

This overwrites the broken credential.https://<host>.helper git config value with the new function-wrapped form.

Regression tests added in src/cli/auth.rs::setup_git_tests:

  • credential_helper_wraps_in_shell_function — structural invariants
  • credential_helper_isolates_git_verb — verb-trailing simulation for get/store/erase

Manual verification, replicating git's actual invocation:

# OLD form (broken)
$ sh -c 'echo TOKEN | sed "s/^/password=/" "$@"' sh get
sed: get: No such file or directory

# NEW form (this fix)
$ sh -c 'f() { echo TOKEN | sed "s/^/password=/"; }; f "$@"' sh get
password=TOKEN

Will land in v0.1.1 along with the Homebrew formula bump.

Fixed in [`b68fb98`](https://rasterhub.com/rasterstate/fj/commit/b68fb98) on `main`. Root cause was as diagnosed: git appends `"$@"` inside the helper's `sh -c` command line, sed gobbles the verb as a filename, the credential dance fails. New helper wraps the pipeline in a shell function so the trailing arg is swallowed by `f` instead of leaking to sed. **Recovery on an already-broken machine:** ```sh fj auth setup-git --host <hostname> ``` This overwrites the broken `credential.https://<host>.helper` git config value with the new function-wrapped form. **Regression tests** added in `src/cli/auth.rs::setup_git_tests`: - `credential_helper_wraps_in_shell_function` — structural invariants - `credential_helper_isolates_git_verb` — verb-trailing simulation for get/store/erase **Manual verification**, replicating git's actual invocation: ``` # OLD form (broken) $ sh -c 'echo TOKEN | sed "s/^/password=/" "$@"' sh get sed: get: No such file or directory # NEW form (this fix) $ sh -c 'f() { echo TOKEN | sed "s/^/password=/"; }; f "$@"' sh get password=TOKEN ``` Will land in v0.1.1 along with the Homebrew formula bump.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rasterstate/fj#1
No description provided.