restoreConfigFromBase replaces .claude/, CLAUDE.md and the other sensitive
paths with the PR base branch's versions, then deliberately unstages them so
the revert does not reach a commit. checkAndCommitOrDeleteBranch then ran a
bare `git add -A`, which staged them again and pushed a silent revert of the
PR author's own config onto their branch, under a commit message that says
only "Auto-commit: Save uncommitted changes from Claude".
restoreConfigFromBase now returns the paths it restored. run.ts threads them
through updateCommentLink into checkAndCommitOrDeleteBranch, which excludes
them via pathspec from both the staging and the git status check.
The exclusion is driven by what was actually restored rather than applied
unconditionally. This path also runs for issues, where no restore happens and
Claude may legitimately have been asked to edit CLAUDE.md or
.claude/settings.json; excluding those there would silently drop the work —
trading one silent-data-loss bug for another. Reverting the fix, dropping the
status scoping, and switching to an unconditional exclusion each fail the new
tests.
The status check is scoped the same way as the staging: when the reverted
config is the only dirty entry there is no real work, so the branch is now
correctly treated as empty and deleted instead of receiving a pure revert.
Reachable on a closed or merged PR where Claude left uncommitted changes with
use_commit_signing false — the only combination where a restore has run and
claudeBranch is set.
Fixes#1669
The REST client honors GITHUB_API_URL, but the GraphQL client derived its
base URL from GITHUB_API_URL as well and ignored the standard
GITHUB_GRAPHQL_URL variable that GitHub Actions provides. On standard GitHub
Enterprise Server this still worked because @octokit/graphql rewrites a
".../api/v3" REST base to ".../api/graphql", but any deployment whose GraphQL
endpoint is not derivable from the REST base (custom proxy, separate host)
sent GraphQL requests to the wrong URL.
Honor GITHUB_GRAPHQL_URL independently and fall back to GITHUB_API_URL when it
is unset, so behavior is unchanged for github.com and standard GHES. A single
trailing "/graphql" is stripped because @octokit/graphql appends its own.
Add wire-level regression tests that run the real client factory in a fresh
process and assert the final request URLs and Authorization headers;
constructor-option assertions are insufficient because @octokit/graphql
rewrites the path after the client is constructed.
configureGitAuth() removed the actions/checkout auth header with
`git config --unset-all http.<server>/.extraheader`, which only edits the
repo-local config. Since actions/checkout v6.0.0 (backported to v5.0.1 and
v4.3.1) the header is written to a separate file under RUNNER_TEMP and
pulled in via include.path, so --unset-all on the local config is a no-op:
the code logged "No existing authentication headers to remove" while the
checkout credential (usually the workflow GITHUB_TOKEN) stayed usable by
git for the rest of the job.
Also clear the header from every included file, so it is neutralized under
both the pre-v6 (local) and v6+ (include) layouts. Includes that do not
define the header are left untouched.
Fixes#1510
`exclude_comments_by_actor` and `include_comments_by_actor` never matched
any bot. Both the documented `*[bot]` wildcard and exact entries such as
`dependabot[bot]` silently did nothing.
GitHub's GraphQL API returns the bare login for App actors ("dependabot"),
while REST and the GitHub UI append a suffix ("dependabot[bot]"). Filter
patterns are written in the suffixed form, so matching a GraphQL login
against them could never succeed and `actor.endsWith("[bot]")` was dead
code.
Request `__typename` on the Actor-typed author selections and normalize
App actors to their suffixed name via `resolveActorName()` before matching.
Normalizing at the filter boundary fixes the wildcard and exact-match cases
together, and leaves the author names shown in the prompt unchanged.
The existing test mocked `login: "scanner[bot]"`, a payload GraphQL never
produces, which is why the gap was invisible. It now mocks the real shape
(`__typename: "Bot", login: "scanner"`) and fails without this fix.
The commit author selection is left alone: it is a GitCommit, not an Actor.
Fixes#1514
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Ensure all public issue, PR, and inline comments apply redactSecrets() in addition to sanitizeContent() before submitting payloads to the GitHub API. This aligns public comment output with error log and step-summary redaction policies, preventing potential leakage of Anthropic API keys, AWS credentials, Slack tokens, JWTs, and GitHub tokens.
shell-quote 1.8.3 (root and base-action dependency) is affected by
CVE-2026-9277, a CRITICAL severity vulnerability. 1.8.4 contains the fix.
Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
* ci: skip Claude-backed test jobs on fork PRs
Jobs that run the action against the Claude API authenticate via workload
identity federation, which fork PRs cannot mint an OIDC token for, so they
always failed on external contributions. Gate each such job on the PR head
repo matching the base repo; push and workflow_dispatch runs are unaffected.
No-Verification-Needed: CI workflow config only, exercised by Actions on the PR
* test: pin the bare remote's initial branch in fetch-depth test
The shallow-clone case created its bare remote with a plain git init, so
HEAD pointed at whatever init.defaultBranch resolves to (master on CI)
while the test only pushed main. git clone --depth=1 implies
--single-branch, and with a dangling remote HEAD it produces an empty,
non-shallow clone, so the is-shallow assertion failed on runners whose
default branch is not main.
No-Verification-Needed: test-only change
* feat(context): include diffHunk in PR review comment context
Review comments arrived with only path and line, so the code they were
written against was missing from the prompt. Fetch diffHunk in the PR
GraphQL query and render it under the comment as a diff block.
The hunk is PR-authored content, so it goes through sanitizeContent like
the comment body. Comments without a hunk are unchanged.
Fixes#855
* test(formatter): cover outdated review comments with an empty diff hunk
GitHub returns diffHunk: "" (not null) for comments whose line no longer
exists in the diff, so the render guard has to reject empty strings too.
Found running the real query against anthropics/claude-code-action#1025.
restoreConfigFromBase and setupBranch pass --depth to every git fetch. On a
checkout made with fetch-depth: 0 that does not just cap the download: it
truncates the history already present and marks the repository shallow, which
drops the merge base with the base branch. `git log origin/<base>..HEAD` then
silently includes commits that are already merged, and
`git diff origin/<base>...HEAD` fails with "no merge base" — the two commands
the prompt tells Claude to run to scope its work to the PR.
Gate the flag on `git rev-parse --is-shallow-repository`, so a checkout that is
already shallow (the fetch-depth: 1 default) keeps the same depth behaviour and
the fetch savings it was added for, while a full checkout stays full.
Fixes#1642
Mirror the path validation already performed by the commit_files tool.
delete_files previously only normalized absolute paths against CWD and
passed relative paths through unchecked; it now runs each path through
validatePathWithinRepo, rejecting "../" traversal and symlinked escapes
for consistency and defense-in-depth.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
formatResultContent recognized structured tool output shaped like
`[{ type: "text", text: "..." }]` but read only `parsedContent[0].text`.
When a tool result split its output across several text blocks, the step
summary showed the first and silently dropped the rest, so extra findings,
file paths and follow-up instructions vanished from the rendered
Claude Code Report while remaining in the execution transcript.
Collect the text from every block instead of just the first. Blocks of other
types, such as images, are skipped rather than stringified into the summary.
Fixes#1572
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
A branch_name_template that places {{description}} (or another variable)
next to a slash crashes the run when the variable resolves to an empty
string. An issue/PR title with no ASCII-alphanumeric content — emoji-only,
CJK-only, or punctuation-only — makes extractDescription() return "", so a
template like "{{prefix}}{{description}}/{{entityNumber}}" produces
"claude//123". validateBranchName rejects consecutive (and leading/trailing)
slashes, and the thrown error propagates uncaught out of setupBranch,
aborting the entire run.
Normalize the templated result before the empty-result check: collapse runs
of slashes and drop any leading/trailing slash. Single-slash and dash
separators are untouched, so existing template behavior is unchanged; a
template that collapses to empty still falls back to the default format.
This is distinct from the {{label}} sanitization tracked in #1491 (and its
open PRs), which deliberately leave {{description}} alone — so this path
remained broken. Fixes the whole empty-segment class regardless of variable.
Adds regression tests for emoji-only and CJK-only titles, a trailing empty
segment, and a direct validateBranchName assertion proving the run no longer
aborts.
The non-signing path validated newBranch before checkout, but the
use_commit_signing path passed it straight to the file ops server, so an
invalid branch_name_template surfaced only as a 422 "Reference name is
not valid" on the first commit.
Validate once after the name is resolved so both paths fail early with
the same message.
Fixes#1573
The upstream oven-sh/setup-bun action saves with a deterministic key
(Bun version) that isn't ref-aware. On every second-and-subsequent run
against the same PR ref, the GitHub cache API rejects the duplicate
key+ref with a 409 (HTML body), and @actions/cache treats the unparsable
response as transient and burns ~20-30s on 5 retries before warning.
The 35 MB Bun binary downloads in 2-3s, so disabling the cache is a net
wallclock win and removes the noisy warning that fires on every PR push
after the first.
Closes#1252
Co-authored-by: Mukunda Rao Katta <mukunda.vjcs6@gmail.com>
The link pointed at `#for-aws-bedrock:`, which does not exist on the
github-actions docs page, so it landed readers at the top of a long page
instead of the Bedrock section.
The current heading id is `#using-with-amazon-bedrock-and-google-cloud`.
label_trigger used a case-sensitive exact comparison, so a workflow
configured with label_trigger: "claude-task" did not fire when an issue
received a label named "Claude-Task" (the same label name with different
casing).
GitHub label names are unique without regard to case, so comparing without
case is unambiguous. It also matches the trigger_phrase check in the same
function, which is already case-insensitive.
Compare labelName and labelTrigger with toLowerCase(), and add a test
covering a mixed-case label.
Fixes#1571
Adds "labeled" to the valid pull_request actions for track_progress,
mirroring the existing support for issue events. Previously, adding
a label to a PR (e.g. to trigger a label-driven Claude review) would
fail validation even though the same pattern works for issues.
Fixes#1585
GitHub's GraphQL API returns files: null (with no errors entry, and
changedFiles misreported as 0) when a PR's diff is too large to compute.
The unguarded pullRequest.files.nodes dereference in the fetcher crashed
the action with 'TypeError: null is not an object', and the formatter had
the same latent crash on prData.files.nodes.length.
Widen the GitHubPullRequest type to files | null so the compiler enforces
guards, degrade gracefully in the fetcher with a warning, and render the
file count as unavailable (not '0 files') in the formatter.
Fixes#1587
* Run checkout auth cleanup when API commit signing is enabled
* Derive git-config test expectations from GITHUB_SERVER_URL
No-Verification-Needed: test-only change
* Scope config snapshot to files inside the working tree
* Record excluded snapshot entries as placeholders instead of links
* Limit linked snapshot content to unmodified tracked files and tracked directories
File targets reached through a link are included only when their content is
unchanged from HEAD, and directory targets only when they contain tracked
files; anything else is recorded as a single placeholder. Adds tests for a
sensitive path that links to a tracked directory, links to untracked
directories, and links to tracked files modified after checkout.
* Redact common credential patterns from published run output
* Handle color codes and escape sequences ahead of redacted values
Vendor-prefixed formats no longer require a leading word boundary, so a
value that follows an ANSI SGR terminator or a serialized JSON escape is
still matched. AWS key ids keep a boundary but also accept those cases.
sanitizeContent goes back to GitHub-only redaction for inbound content,
and the failure annotation is redacted like the tracking comment.
* Coerce non-string text content before redacting tool results
No-Verification-Needed: one-line coercion in a formatting helper plus regression test
* Invoke the formatter directly from the format hook
The PostToolUse format hook now runs prettier directly with a pinned
version and --no-config instead of going through the package.json
"format" script, so the hook resolves the same way regardless of the
scripts and formatter config in the checked-out tree. Output matches
the previous "bun run format" (both .prettierrc files are empty).
Also documents which paths the action restores from the PR base branch
and recommends keeping base-branch hooks self-contained.
No-Verification-Needed: config, comment, and doc-only change
* Qualify the self-contained hook guidance for Bun-only runners
Note in docs/security.md and the restore-config JSDoc that bunx runs the
tool under node when node is on PATH, but on a Bun-only runner Bun runs
the script itself and reads bunfig.toml (preload etc.) from the
checkout, so that file and .npmrc are runtime config from the PR head.
No-Verification-Needed: comment- and doc-only change
* Exclude .claude-pr from prettier
No-Verification-Needed: prettierignore-only change
The write-permission gate previously only ran for issue/PR entity
events. Apply it to workflow_run events as well, checking both the
workflow actor and the actor recorded on the upstream run when they
differ. allowed_non_write_users and the github_token override behave
the same as for entity events. Document the behavior for workflow_run
pipelines.