Skip to content

fix(git-cred): diagnose a missing authtype capability - #8533

Open
SilverLenZ wants to merge 1 commit into
block:mainfrom
SilverLenZ:feature/git-cred-authtype-diagnostic
Open

SilverLenZ wants to merge 1 commit into
block:mainfrom
SilverLenZ:feature/git-cred-authtype-diagnostic

Conversation

@SilverLenZ

@SilverLenZ SilverLenZ commented Oct 22, 2026 •

Copy link
Copy Markdown

Problem

A git client older than 2.46 never sends capability[]=authtype. The helper then exited 0 with no output, git fell through to a username prompt, and under GIT_TERMINAL_PROMPT=0 the whole thing failed as:

fatal: could not read Username for 'https://relay.invalid/git/test/repo': terminal prompts disabled

The failure is silent and indistinguishable from a missing or broken credential helper. The README documents git >= 2.46 as a requirement, but nothing at runtime ever says the client is too old.

Fix

Keep the contract exactly as it was -- stdout stays empty and the exit code stays 0, so git still falls through and the authtype security gate is untouched. The only change is that when the server did issue a Nostr challenge (the case where this helper is the one that should have answered), print one stderr line naming the requirement.

A non-Nostr remote still exits silently, because that is not a buzz remote and has nothing to diagnose.

if req.wwwauth.is_some() {
    eprintln!(
        "git-credential-nostr: the git client did not announce the `authtype` \
         capability, so it cannot use a Nostr credential. This remote needs \
         git 2.46 or newer; upgrade git."
    );

Why the wwwauth check is the right trigger

wwwauth[]=Nostr method="GET" is the authoritative signal that the server issued a Nostr challenge. It is also the one field git does forward to the helper, which is what makes the diagnosis possible at all.

For context on the underlying capability gate: crates/git-credential-nostr gates on capability[]=authtype and exits 0 silently when it is absent. The wwwauth Nostr challenge is sufficient to establish that the server spoke Nostr, so the capability check is the redundant part -- and it is the part that is harmful, because it is the part git does not forward on older clients. This PR does not change that gate; it makes the symptom legible.

Test

old_git_no_authtype_capability is now table-driven over three challenges and asserts the full contract in each case:

server challenge stdout exit stderr
none empty 0 silent
wwwauth[]=Nostr empty 0 the git-version hint
wwwauth[]=Basic empty 0 silent

Verified falsifiable: reverting the diagnostic fails the test at integration.rs:237.

Suite: 8 passed, 0 failed.

The helper itself is sound on a current git

This change is about diagnosability, not a repair. On git 2.55 the helper produces a working NIP-98 credential end to end, with the capability[]=authtype line present in git's request:

$ printf 'capability[]=authtype\ncapability[]=state\nprotocol=http\nhost=...\npath=git/test/repo\nwwwauth[]=Nostr method="GET"\n\n' \
    | git-credential-nostr get
capability[]=authtype
authtype=Nostr
credential=eyJpZC...AifQ==
ephemeral=true
quit=true

Captured from git's own credential request on the same client, the only difference between the two clients is the capability[] line:

git 2.55 sends:                 git 2.43 sends:
  capability[]=authtype          (absent)
  capability[]=state
  protocol=...                   protocol=...
  host=...                       host=...
  path=...                       path=...
  wwwauth[]=Nostr method="GET"   wwwauth[]=Nostr method="GET"

So the capability[] gate is not wrong in intent -- it is simply never reached on clients older than 2.46, where git does not announce the capability at all. This PR keeps the gate and makes the symptom name itself instead of looking like a missing helper.

🤖 Generated with Claude Code

A git client older than 2.46 never sends `capability[]=authtype`, so the
helper exits 0 with no output and git falls through to a username prompt,
which fails under `GIT_TERMINAL_PROMPT=0` as "could not read Username". The
failure is silent and looks exactly like a missing or broken helper: the
README documents git >= 2.46 as a requirement, but nothing at runtime says
the client is too old.

Keep the contract: stdout stays empty and the exit code stays 0, so git
still falls through and the `authtype` security gate is untouched. When the
server did issue a `Nostr` challenge -- the case where this helper is the
one that should have answered -- print one stderr line naming the
requirement. A non-Nostr remote still exits silently, because that is not a
buzz remote and has nothing to diagnose.

Test: `old_git_no_authtype_capability` becomes table-driven over three
challenges (none, Nostr, Basic) and asserts empty stdout/exit 0 in all
three, the hint only for Nostr, and silence for Basic. Verified falsifiable:
reverting the diagnostic fails the test at integration.rs:237.

Co-authored-by: Ma'at <maat@vmi3238719.contaboserver.net>
Signed-off-by: Lumen <82261e322dbb1c513d43f7dfbd700ff266732ff6180de73307fd169600fa3f01@vmi3238719.mellori-dojo.ts.net>
@SilverLenZ
SilverLenZ requested a review from a team as a code owner October 22, 2026 10:17
@github-actions

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 347ce88d5dd8bc631c073f1947fea6b75d5a43c0...7b4efcf4015e0af9cc8b7f49d921f44c73732f26.
A new review must complete for this exact range. When manual authorization
is required, a user with write access must comment exactly
@buzz-security-review 7b4efcf4015e0af9cc8b7f49d921f44c73732f26 to authorize a new review.
Any previous review applies only to its recorded range.

This branch has not been deployed

No deployments
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