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>
bun install --production strips execute bits from vendored binaries
(bun bug). The Claude Agent SDK ships rg binaries in:
node_modules/@anthropic-ai/claude-agent-sdk/vendor/ripgrep/
{x64,arm64}-{linux,darwin}/rg
{x64,arm64}-win32/rg.exe
After bun --production, all of these lose +x, causing EACCES when the
SDK tries to spawn ripgrep. The fix is a targeted find(1) that restores
+x on the rg binaries immediately after bun install.
Design notes:
- -type f excludes symlinks (symlink attack safety, no || true needed)
- -name "rg" naturally excludes rg.exe on Windows (find returns nothing,
chmod never called — safe and correct on all platforms)
- .node audio-capture files use dlopen, not exec — no +x needed there
- Fails loudly if the binary path is missing (no || true) so a SDK
packaging change is immediately visible rather than silently broken
Fixes#1140
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add shouldRetry predicate to RetryOptions so callers can abort retries
for errors that will never succeed (e.g. 401 WorkflowValidationSkipError).
Previously, retryWithBackoff retried all errors blindly, wasting ~35s on
deterministic failures like workflow validation 401s.
Fixes#1081
Co-authored-by: Claude <noreply@anthropic.com>
When id-token: write permission is enabled, ACTIONS_ID_TOKEN_REQUEST_URL
and ACTIONS_ID_TOKEN_REQUEST_TOKEN are passed to the Claude session via
the process.env spread in parseSdkOptions(). This allows Claude to mint
new OIDC tokens, which is an unintended capability.
This commit deletes these two variables from the env object before passing
it to the Claude SDK. The OIDC flow in token.ts reads directly from
process.env and runs before parseSdkOptions(), so it is unaffected.
Fixes#1010
The reviewData variable is typed as `{ nodes: GitHubReview[] } | null`,
but the fallback value was `[]` (a plain array). When
`pullRequest.reviews` is null/undefined, `reviewData` becomes `[]`,
causing `reviewData.nodes` to return `undefined` instead of `[]`.
This leads to silent failures in downstream code that iterates over
`reviewData.nodes`, such as `filterReviewsToTriggerTime` and
`filterCommentsByActor`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: document include_comments_by_actor and exclude_comments_by_actor inputs
These inputs were added in #812 but never documented in usage.md or
security.md. This adds them to the inputs table in usage.md and
references comment filtering as a prompt injection mitigation in
security.md.
Fixes#972
* docs: clarify wildcard support is limited to *[bot] pattern
Address review feedback: "Supports wildcards" was misleading since
only the *[bot] pattern is supported, not general glob matching.
Add a check for non-empty github_token output before attempting to
revoke the app token in the cleanup step. When the prepare phase fails
(e.g., unsupported event type with track_progress), no token is
acquired, causing the cleanup curl to send an empty Bearer token
and produce a confusing "Bad credentials" 401 error.
Fixes#858
Co-authored-by: Dave-London <hello@os4us.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Bun's execFileSync without an explicit env option spawns with the process
startup environment, dropping runtime process.env mutations. The credential
helper reads GH_TOKEN which is set at runtime, so git fetch in the
restore-config path failed with empty password.
Fixes#1139🏠 Remote-Dev: homespace
* fix: fall back to repo default_branch instead of hardcoded "main"
When no explicit base_branch input is provided, the action previously
fell back to a hardcoded "main", which fails on repositories whose
default branch is named differently (e.g. "master", "develop").
This reads repository.default_branch from the GitHub event payload
(populated once in parseGitHubContext) and uses it as the fallback in
all three callsites: agent/index.ts, run.ts, and update-comment-link.ts.
Explicit env/input precedence is preserved; "main" remains only as a
last-resort defensive fallback if the payload somehow lacks the field.
* test: drop unused BASE_BRANCH env handling from default_branch test
agent/index.ts no longer reads process.env.BASE_BRANCH directly (it now
goes through context.inputs.baseBranch which is set on the mock context),
so saving/clearing/restoring that env var in the regression test is dead
code.
* Revert "chore: bump Claude Code to 2.1.89 and Agent SDK to 0.2.89"
This reverts commit bee87b3258c251f9279e5371b0cc3660f37f3f77.
* Revert "chore: bump Claude Code to 2.1.88 and Agent SDK to 0.2.88"
This reverts commit 7225f045c6219dd201504adc5534baf31024db31.