GitHub @-mention autocomplete inserts @Claude (capitalized) when users
pick the bot from the dropdown, but the trigger regex had no 'i' flag,
so the action would log 'No trigger was met for @claude' and exit. The
workflow's outer 'if: contains(...)' gate is case-insensitive, so the
job runs and looks like it silently ignored the user.
The regex was case-sensitive since the initial commit with no test
asserting either behavior; the existing tests focus on word-boundary
precision (email@claude.com etc.), not case.
* docs: add pull_request_target/workflow_run guidance and base-action trust model
Adds a security.md section on safe checkout patterns under
pull_request_target/workflow_run, and a trust-model section to the
base-action README clarifying that callers are responsible for the
working directory and prompt being trusted.
🏠 Remote-Dev: homespace
* docs: refine PRT/workflow_run guidance — root checkout + workflow_run ref
Second example now checks out the base ref at the workspace root before
the head-ref subdirectory checkout (this action expects a git repo at
the root). Adds the workflow_run ref form, drops the PRT-specific
gh-pr-diff hint from the first example, and generalises the closing
line to cover both event types.
🏠 Remote-Dev: homespace
* docs: use actions/checkout@v6 in examples (consistency)
🏠 Remote-Dev: homespace
Claude Code's EnterWorktree tool converts "/" to "+" when generating
branch names from worktree names (e.g. EnterWorktree("feat/foo") creates
branch "worktree-feat+foo"). The strict whitelist in validateBranchName
rejected these names, causing claude-code-action to fail on any PR opened
from an EnterWorktree-generated branch.
Since all git calls use execFileSync (not shell interpolation), "+" carries
no command injection risk — the same rationale used for allowing "#".
Git itself permits "+" in branch names per git-check-ref-format.
Fixes: https://github.com/anthropics/claude-code-action/issues/1244
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Agent SDK 0.2.113 dropped vendor/ripgrep and now ships native binaries
via per-platform optionalDependencies. Two breakages:
- action.yml chmod'd vendor/ripgrep which no longer exists, failing the
Install Dependencies step with find exit 1.
- The SDK auto-resolves its bundled binary by trying the -musl platform
package before the glibc one. bun install does not respect the
package.json libc field and installs both on glibc Linux, so the SDK
picks the musl binary and spawn fails with ENOENT.
Remove the obsolete ripgrep chmod. Make installClaudeCode() return the
install.sh binary path and pass it explicitly as
pathToClaudeCodeExecutable so the SDK skips auto-resolution entirely.
When a PR originates from a fork, `git fetch origin <branch>` fails
because the branch only exists on the fork's remote.
Fix: detect cross-repository PRs via the `isCrossRepository` GraphQL
field and fetch using `pull/<number>/head:<branch>` refspec instead,
which is the standard GitHub mechanism for accessing fork PR branches.
Changes:
- Add `isCrossRepository` and `headRepository` to PR GraphQL query
- Add corresponding fields to GitHubPullRequest type
- Branch checkout uses pull ref for fork PRs
- Update test fixtures with new fields
Co-authored-by: User <user@example.com>
Ensures later steps resolve standard tools like git and tar from /usr/bin
regardless of what setup actions added earlier in the job. Also strengthens
the PAT guidance in security.md.
🏠 Remote-Dev: homespace
Copies the bun binary into $GITHUB_ACTION_PATH/bin before the claude step
runs and uses that copy in the two post-steps that invoke bun. Falls back
to PATH-resolved bun when allowed_non_write_users is empty.
🏠 Remote-Dev: homespace
dirname() preserves embedded newlines, so a value like
`/usr/bin/claude\n/attacker/path` writes two lines to GITHUB_PATH,
injecting an attacker-controlled directory into PATH for all subsequent
workflow steps.
Validate the input immediately after reading it and throw if it
contains any control characters (0x00-0x1f, 0x7f). This is fail-closed
rather than silent stripping — a path with control characters is always
misconfigured or malicious.
Fixes#1160
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
These three env vars are read directly from process.env by the Claude CLI
subprocess to configure MCP server behavior. Users setting them in their
workflow had no reliable way to make them reach the CLI:
- Job-level env: shadowed by the step's explicit env: block
- Step-level env: on the calling workflow step is not inherited by
composite action steps
- GITHUB_ENV from a prior step: same shadowing problem
- settings input: writes to ~/.claude/settings.json, not process.env
The fix is to add explicit ${{ env.VAR }} passthrough lines for all three
vars, matching the existing pattern already used for OTEL_*, AWS_*, and
Vertex configuration (lines 271-317). No TypeScript changes are needed;
the forwarding chain in parse-sdk-options.ts is already correct.
Fixes#1152
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
When a PR modifies files under .claude/, the security restore in
restoreConfigFromBase() overwrites them with the base branch version —
correct for execution safety, but it means review agents never see what
the PR actually changes.
Before deleting the PR-controlled .claude/ tree, copy it to .claude-pr/.
Review agents can read .claude-pr/ to inspect the PR's hooks, MCP
configs, settings, and CLAUDE.md without those files ever being executed.
The snapshot is taken before the security delete so it captures the full
PR-authored version.
Fixes#1134.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
shell-quote treats # as a shell comment character, swallowing all
subsequent content including flags on new lines. Strip comment lines
(lines starting with #) before passing input to shell-quote.
Fixes#802
Co-authored-by: VoidChecksum <Admin@CyberNord>
When a PR head contains `.gitmodules`, git's default
`fetch.recurseSubmodules=on-demand` config causes `git fetch` to attempt
submodule object fetches. In CI (no credentials), this blocks indefinitely
waiting for auth — producing ~4-hour hangs reported in #1088.
Two changes, both defence-in-depth:
1. Delete SENSITIVE_PATHS *before* fetching. The attacker-controlled
`.gitmodules` is absent during the network operation, so git never
sees a submodule config to follow regardless of git settings.
2. Pass `--no-recurse-submodules` to the fetch. Suppresses submodule
fetching explicitly, independent of any git config on the runner.
The original order (fetch-then-delete) was a brief window where
`.gitmodules` from the PR head could influence the fetch. Reordering
also tightens the security property: if `git checkout` below fails, the
attacker-controlled file is already gone rather than present during fetch.
Fixes#1088.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
`validateBranchName` used a strict whitelist that excluded `#`,
causing the action to fail on PRs from branches like `put-back-arm64-#2`
with "Invalid branch name" — even though the branch already exists in
git and `#` is permitted by git-check-ref-format.
The validation was designed to prevent command injection. However, every
git call in the action uses `execFileSync`, which bypasses the shell
entirely and passes arguments directly to the kernel's execve. There is
no shell to interpret `#` as a metacharacter, so the strict whitelist was
over-blocking valid names with no security benefit.
Add `#` to the whitelist pattern, and update the JSDoc and error message
to reflect the allowed character set.
Fixes#1137.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>