fix(client): make our credential helper the only one the repo consults
Live QA against the beta forge failed its first push with 'Failed to authenticate user' while the config held a valid token. Cause: credential.helper is a list accumulated across system/global/repo config, and this machine has osxkeychain (Xcode gitconfig) plus store (~/.gitconfig). A stale entry for the forge host answered before our helper. The same list is a token leak in the other direction: git calls approve on every helper after a successful auth, so 'store' writes the forge token into ~/.git-credentials in plaintext -- undoing the 0600 config and the no-token-in-URL rule. Confirmed accidentally during QA when a verification clone with a URL-embedded token re-created exactly that entry. Fix: set an empty credential.helper first (git reads that as 'forget the inherited list'), then add ours -- in install_credential_helper and in the git clone inside get. 2 regression tests, one of which drives 'git credential fill' against a poisoned outer helper. 143 tests.
This commit is contained in:
@@ -116,6 +116,31 @@ restore point into a *new* directory and refuses a non-empty destination.
|
||||
Someone restoring a backup is already having a bad day; overwriting the files
|
||||
they still have would make the recovery tool the second disaster.
|
||||
|
||||
## The credential helper must be the ONLY helper (found by live QA)
|
||||
|
||||
`credential.helper` is a list that accumulates across system, global and repo
|
||||
config, and git asks every helper in it. A stock mac already has two —
|
||||
`osxkeychain` from Xcode's gitconfig, and `store` from many people's
|
||||
`~/.gitconfig` — and they lose in both directions:
|
||||
|
||||
* **reading:** a stale entry for the forge host answers before our helper, so
|
||||
pushes fail `remote: Failed to authenticate user` long after the token was
|
||||
rotated, and nothing in this tool's config explains why. This is exactly
|
||||
how the first live-QA run failed;
|
||||
* **writing:** git calls `approve` on every helper after a successful auth,
|
||||
so `store` copies the forge token into `~/.git-credentials` **in
|
||||
plaintext**. Keeping the token in a 0600 file and out of remote URLs buys
|
||||
nothing if git then hands it to a plaintext store.
|
||||
|
||||
So `install_credential_helper` (and the `git clone` in `get`) sets an **empty**
|
||||
`credential.helper` first, which resets the inherited list, then adds ours.
|
||||
Exactly one helper serves this repo.
|
||||
|
||||
Corollary worth remembering: a token embedded in a remote URL gets saved by
|
||||
`store` on first use. During QA a verification clone with a URL-embedded
|
||||
token re-created the very entry that had just been cleaned out. That is the
|
||||
whole reason this client passes tokens through a helper and never a URL.
|
||||
|
||||
## Device identity
|
||||
|
||||
`link` mints a uuid on first run and persists it in `~/.granthi-sync/config.json`
|
||||
@@ -356,7 +381,7 @@ deleted it again, `DELETE …/tokens/{id}` returning 204 under basic auth):
|
||||
|
||||
## Tests
|
||||
|
||||
* `python3 -m unittest discover -s tests` — 141 tests. The v1.2 additions
|
||||
* `python3 -m unittest discover -s tests` — 143 tests. The v1.2 additions
|
||||
cover: a snapshot capturing uncommitted work while HEAD, the index and the
|
||||
working tree stay byte-identical; snapshots landing outside `refs/heads`;
|
||||
an unchanged tree not being re-pushed; a diverged folder still being backed
|
||||
@@ -368,7 +393,9 @@ deleted it again, `DELETE …/tokens/{id}` returning 204 under basic auth):
|
||||
disk; mode detection; `list` filtering; `get --all` skipping what is
|
||||
already present, defaulting to snapshot mode, and shouting about
|
||||
truncation; `restore` writing a new folder, refusing a non-empty
|
||||
destination, and leaving the working tree alone.
|
||||
destination, and leaving the working tree alone; and the credential helper
|
||||
being the only one the repo consults, proven by driving
|
||||
`git credential fill` against a deliberately poisoned outer helper.
|
||||
* Earlier suite: autocommit/ff/diverged
|
||||
logic against real temp git repos (including "diverged never touches the
|
||||
remote"), config 0600 handling (including umask-proof creation and a
|
||||
|
||||
Reference in New Issue
Block a user