anthropics / anthropics/claude-code-action

Checkout credential cleanup is still a no-op: checkout persists via includeIf.gitdir, not include.path (follow-up to #1510)

Offen
#1,721 3 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area:permissions bug p2
Vorherrschende Sprache
TypeScript
Sterne
8.9k
Forks
2.1k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

## Summary

The fix for #1510 walks `include.path` entries of the local git config, but `actions/checkout`
does not use `include.path` in the normal (non-global) code path — it writes `includeIf.gitdir:`
entries. So `replaceCheckoutCredentials` still removes nothing, and the checkout credential
(the workflow `GITHUB_TOKEN`) stays active for every git operation in the job, overriding the
app token the action puts into the remote URL.

Observed with `anthropics/claude-code-action@v1.0.199` and `actions/checkout@v7.0.1`.

## Evidence

Job log of a real tag-mode run (`@claude` on an issue comment, `use_commit_signing: false`):

```
Configuring git authentication for non-signing mode
Configuring git user...
Removing existing git authentication headers...
No existing authentication headers to remove <-- cleanup found nothing
Updating remote URL with authentication...
✓ Updated remote URL with authentication token
```

The agent then committed and pushed successfully — but under `GITHUB_TOKEN`, not under the app
token, which we could confirm from the outside: the push produced **no** `pull_request:
synchronize` event, so neither CI nor our review workflow ran on the commit the agent added to
its own PR. GitHub suppresses events for actions performed with `GITHUB_TOKEN`, and that is only
consistent with the header winning over the credentials embedded in the remote URL.

For contrast, the same repository on `actions/checkout@v5.1.0` (which still wrote the header into
`.git/config`) logged `✓ Removed existing authentication headers`, and pushes from the agent did
raise `synchronize` — CI and review ran on bot-pushed commits. The regression appeared for us
exactly when Dependabot bumped checkout from v5.1.0 to v7.0.1.

## Why the current cleanup misses it

`replaceCheckoutCredentials` (`src/github/operations/git-config.ts`, v1.0.199) unsets
`http./.extraheader` from the local config and then from every file listed in
`git config --local --get-all include.path`.

`actions/checkout` v7.0.1 (`src/git-auth-helper.ts`, `configureToken`) only writes `include.path`
in the `globalConfig` branch. In the branch that runs for a normal checkout it writes, into the
repository-local config:

```ts
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`
await this.git.config(hostIncludeKey, credentialsConfigPath)

const hostWorktreeIncludeKey = `includeIf.gitdir:${gitDir}/worktrees/*.path`
await this.git.config(hostWorktreeIncludeKey, credentialsConfigPath)
```

plus equivalent container-path variants. None of these are `include.path`, so the loop iterates
over an empty list and the header survives.

## Impact

Two shapes, depending on what the workflow grants `GITHUB_TOKEN`:

1. **`contents: write` granted** — the push succeeds under `GITHUB_TOKEN` and silently raises no
events. Downstream workflows never run on commits the agent adds to an existing PR, while the
PR still shows the checks from the previous SHA, so it looks verified. This is the failure we
hit; it is silent, which makes it worse than a hard error.
2. **`contents: write` not granted** — the push fails with `403 Write access to repository not
granted`, which appears to be what #907 describes.

## Suggested fix

Collect every include target rather than just `include.path`, e.g.

```
git config --local --get-regexp '^include(If\..+)?\.path$'
```

and unset `http./.extraheader` from each referenced file (the same
`git config --file --unset-all` call already used). Optionally also drop the
`includeIf.gitdir:*` keys themselves once their credential file no longer holds the header.

A regression test would need to cover the non-global branch specifically — the current one only
exercises `include.path`, which checkout uses solely for global config.

## Workaround for others hitting this

Give `actions/checkout` a token that is not `GITHUB_TOKEN` (e.g. from
`actions/create-github-app-token`), so whichever credential wins is still one whose actions raise
events. `persist-credentials: false` also avoids the conflict, but it has separate reported
issues (#1236, #1711).

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.