537 Commits

Author SHA1 Message Date
GitHub Actions
398370690e chore: bump Claude Code to 2.1.94 and Agent SDK to 0.2.94 2026-04-07 21:22:37 +00:00
Max Flanagan
6cad158a17
security: reject PATH_TO_CLAUDE_CODE_EXECUTABLE with control characters (#1185)
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>
2026-04-05 17:26:08 -07:00
Max Flanagan
0f1fe5ef85
fix: forward MCP_TIMEOUT, MCP_TOOL_TIMEOUT, MAX_MCP_OUTPUT_TOKENS to action step (#1162)
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>
2026-04-05 10:37:03 -07:00
Ashwin Bhat
6e2bd52842
fix: pin bun runtime config and improve log hygiene (#1174)
* fix: pin bun runtime config and improve log hygiene

* snapshot all SENSITIVE_PATHS to .claude-pr/, not just .claude/
v1.0.89
2026-04-05 07:42:02 -07:00
Ashwin Bhat
3534c326a5
chore: fix prettier formatting in parse-sdk-options.test.ts (#1176) 2026-04-04 23:10:12 -07:00
Ashwin Bhat
6685b26dfb
chore: fix prettier formatting (#1171) 2026-04-04 20:56:18 -07:00
Max Flanagan
5150ea9643
fix: snapshot PR's .claude/ to .claude-pr/ before security restore (#1172)
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>
2026-04-04 20:47:27 -07:00
VoidChecksum
eb8baa46af
fix: strip shell comment lines before parsing claude_args (#1055)
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>
2026-04-04 20:26:13 -07:00
Max Flanagan
f328a5c889
fix: prevent hang in restoreConfigFromBase on repos with .gitmodules (#1166)
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>
2026-04-04 20:21:28 -07:00
Max Flanagan
b15d4751a6
fix: allow # in branch names for PR checkout and base restore (#1167)
`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>
2026-04-04 20:17:46 -07:00
Max Flanagan
d5db8208f9
fix: restore ripgrep execute bits after bun install --production (#1163)
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>
2026-04-04 20:15:31 -07:00
Andrew Grigorev
d8af4e9f01
fix: skip retries for non-retryable errors in retryWithBackoff (#1082)
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>
2026-04-04 20:14:47 -07:00
chyipin
f37c786ad3
Strip OIDC token request env vars from Claude session (#1011)
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
2026-04-04 20:13:05 -07:00
Maxwell Calkin
21b0f0f9aa
fix: use correct fallback type for reviewData in fetcher (#1034)
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>
2026-04-04 20:12:05 -07:00
Mario Yuri Mota Lara
27f549ae64
docs: document include/exclude_comments_by_actor inputs (#1130)
* 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.
2026-04-04 20:10:29 -07:00
David Dworken
263993d836
Use env vars for workflow_run context values in example workflows (#1125)
* Use env vars for workflow_run context values in example workflows

* Add security note to ci-failure-auto-fix example about trust requirements
2026-04-04 20:10:11 -07:00
Dave London
85133eeab2
fix: skip token revocation when no token was acquired (#918)
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>
2026-04-04 20:09:21 -07:00
GitHub Actions
1eddb334cf chore: bump Claude Code to 2.1.92 and Agent SDK to 0.2.92 v1.0.88 2026-04-04 00:45:34 +00:00
GitHub Actions
0432df8bfe chore: bump Claude Code to 2.1.91 and Agent SDK to 0.2.91 v1.0.87 2026-04-03 00:19:01 +00:00
Octavian Guzu
ba026a3e56
Pass env to execFileSync git calls (#1151)
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
v1.0.86
2026-04-02 21:52:02 +01:00
Octavian Guzu
c95e735eb1
Fix subprocess isolation install step never running (#1148)
env context isn't available in composite-action if: conditions.
Move opt-out check into run: body.

🏠 Remote-Dev: homespace
2026-04-02 14:05:08 +01:00
GitHub Actions
58dbe8ed68 chore: bump Claude Code to 2.1.90 and Agent SDK to 0.2.90 v1.0.85 2026-04-01 23:57:02 +00:00
Ashwin Bhat
c281e17d7f
fix: fall back to repo default_branch instead of hardcoded "main" (#1143)
* 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.
2026-04-01 14:48:46 -07:00
Ashwin Bhat
408a40e7c2
Pin Claude Code to 2.1.87 (#1142)
* 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.
v1.0.84
2026-04-01 11:29:30 -07:00
GitHub Actions
bee87b3258 chore: bump Claude Code to 2.1.89 and Agent SDK to 0.2.89 v1.0.83 2026-04-01 01:13:44 +00:00
Octavian Guzu
32156b120b
Add subprocess isolation setup and git credential helper (#1132)
- Add optional bubblewrap setup step for Linux subprocess isolation
  when allowed_non_write_users is configured
- Use git credential helper instead of embedding token in remote URL
- edit-issue-labels.sh: read issue number from workflow event payload
  instead of CLI arg
- Add CLAUDE_CODE_SCRIPT_CAPS env for per-script call limit config
- docs/security.md: note recommended github_token configuration

🏠 Remote-Dev: homespace
2026-03-31 12:36:51 +01:00
GitHub Actions
7225f045c6 chore: bump Claude Code to 2.1.88 and Agent SDK to 0.2.88 2026-03-31 00:35:26 +00:00
GitHub Actions
88c168b39e chore: bump Claude Code to 2.1.87 and Agent SDK to 0.2.87 v1.0.82 2026-03-29 02:29:10 +00:00
GitHub Actions
e7b588b6ea chore: bump Claude Code to 2.1.86 and Agent SDK to 0.2.86 v1.0.81 2026-03-27 21:50:59 +00:00
GitHub Actions
094bd24d57 chore: bump Claude Code to 2.1.85 and Agent SDK to 0.2.85 v1.0.80 2026-03-26 22:51:40 +00:00
GitHub Actions
3ac52d0da9 chore: bump Claude Code to 2.1.84 and Agent SDK to 0.2.84 v1.0.79 2026-03-26 00:37:42 +00:00
GitHub Actions
0ee1beea58 chore: bump Claude Code to 2.1.83 and Agent SDK to 0.2.83 v1.0.78 2026-03-25 06:35:03 +00:00
Octavian Guzu
ff9acae588
Auto-set subprocess env scrub when allowed_non_write_users is configured (#1093)
* Auto-set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB when allowed_non_write_users is configured

Sets the env var automatically whenever allowed_non_write_users is
non-empty, so downstream workflows don't need to add it manually.
Updates the input description and docs/security.md to note the behavior.

🏠 Remote-Dev: homespace

* Fall back to inherited env when allowed_non_write_users is unset

🏠 Remote-Dev: homespace

* Let workflow/job env override the auto-set scrub flag

Env var takes priority so users can opt in/out via CLAUDE_CODE_SUBPROCESS_ENV_SCRUB
at job or workflow level independently of allowed_non_write_users.

🏠 Remote-Dev: homespace
v1.0.77
2026-03-23 12:10:02 +00:00
GitHub Actions
6062f37096 chore: bump Claude Code to 2.1.81 and Agent SDK to 0.2.81 v1.0.76 2026-03-20 22:30:13 +00:00
GitHub Actions
df37d2f076 chore: bump Claude Code to 2.1.79 and Agent SDK to 0.2.79 v1.0.75 2026-03-18 22:39:18 +00:00
David Dworken
1ba15be4f0
Remove redundant git status/diff/log from tag mode allowlist (#1075) v1.0.74 2026-03-18 09:06:33 -07:00
kashyap murali
9ddce40de8
Restore .claude/ and .mcp.json from PR base branch before CLI runs (#1066)
* Restore .claude/ and .mcp.json from PR base branch before CLI runs

The CLI's non-interactive mode trusts cwd: it reads .mcp.json and
.claude/settings{,.local}.json from the working directory and acts on
them before any tool-permission gating — executing hooks, setting env
vars (NODE_OPTIONS, LD_PRELOAD), running apiKeyHelper shell commands,
and auto-approving MCP servers. When this action checks out a PR head,
these files are attacker-controlled.

Rather than enumerate dangerous keys, replace the entire .claude/ tree
and .mcp.json with the versions from the PR base branch (which a
maintainer has reviewed). Paths absent on base are deleted. Uses local
git state, so no TOCTOU against the GitHub API.

* Read PR base ref from payload for config restore in agent mode

Agent mode's branchInfo.baseBranch defaults to "main" (or env/input
override) instead of the PR's actual target branch — it doesn't query
prData.baseRefName like tag mode does. This meant a PR targeting
develop would get .claude/ restored from main.

Fix by reading pull_request.base.ref directly from the webhook payload
for pull_request, pull_request_review, and pull_request_review_comment
events. For issue_comment on a PR (no base.ref in payload), fall back
to the mode-provided value — tag mode's value is correct (from GraphQL);
agent mode on issue_comment is an edge case that at worst restores from
the wrong trusted branch, which is still secure.

The payload value passes through validateBranchName for defense-in-depth
(GitHub enforces valid branch names server-side, but we validate anyway).

* Extend restored paths to .gitmodules, .ripgreprc, .claude.json

.gitmodules defines submodule URLs and paths; path-confusion attacks
against git submodule operations can write into .git/hooks. .ripgreprc
can set --pre (arbitrary command on each file) if RIPGREP_CONFIG_PATH
points at it. .claude.json is cheap defense-in-depth.

Documented why .git/ is excluded (not trackable in commits, and
restoring it would undo the PR checkout), along with .gitconfig
(git never reads it from cwd) and shell rc files (sourced from $HOME,
not cwd — checkout cannot reach $HOME).
2026-03-18 12:00:18 -04:00
GitHub Actions
1b422b3517 chore: bump Claude Code to 2.1.78 and Agent SDK to 0.2.77 v1.0.73 2026-03-17 23:47:59 +00:00
GitHub Actions
4c044bb2f5 chore: bump Claude Code to 2.1.77 and Agent SDK to 0.2.77 2026-03-17 00:33:47 +00:00
GitHub Actions
cd77b50d2b chore: bump Claude Code to 2.1.76 and Agent SDK to 0.2.76 v1.0.72 2026-03-14 01:29:31 +00:00
GitHub Actions
0e80d3c5b8 chore: bump Claude Code to 2.1.75 and Agent SDK to 0.2.75 2026-03-13 17:07:33 +00:00
kashyap murali
f956510b1a
Harden tag mode tool permissions against prompt injection (#1002)
Two defenses for tag mode where an attacker with repo write access could
craft a prompt injection payload in an issue/PR to gain RCE on the
Actions runner:

1. git-push wrapper (H1 #3556799)
   The Bash(git\ push:*) rule permitted arbitrary flags and remotes,
   including combinations that execute shell commands locally. Replaced
   with scripts/git-push.sh which allowlists exactly 'origin <ref>' with
   no flags, validates the ref via check-ref-format. Same pattern as
   scripts/gh.sh.

2. acceptEdits instead of blanket Write/Edit (Asana 1213310082312048)
   Edit/MultiEdit/Write in allowedTools granted write access to the
   whole runner filesystem (~/.bashrc etc). Removed from allowedTools
   and set --permission-mode acceptEdits, which auto-accepts edits
   inside cwd ($GITHUB_WORKSPACE) and denies outside. Headless SDK has
   no prompt handler so 'ask' becomes deny.

Also:
- Noted that create-prompt/index.ts exports ALLOWED_TOOLS env var that
  nothing reads. The live path is modes/tag/index.ts. Mirrored the fix
  in both so the file the H1 report likely points to stays in sync.
- Updated prompt text (3 callsites) to reference the wrapper.
- Updated tests (4 prompt-content asserts, 7 tool-list asserts).
2026-03-12 13:35:17 -07:00
kashyap murali
5d0cc745cd
feat(inline-comment): add confirmed param + probe-pattern safety net (#1048)
* feat(inline-comment): add confirmed param + probe-pattern safety net

Subagents that inherit this tool sometimes probe it with test comments
('Test comment to see if I can create inline comments') after hitting
unrelated errors elsewhere. Recurring issue across customer PRs.

Adds two defenses:
1. confirmed param: set true to post (final review comments should pass
   this). When false, buffers to a JSONL file instead of posting.
2. Probe-pattern safety net: when confirmed is omitted (backward compat
   for existing prompts), the body is checked against obvious probe
   patterns ('test comment', 'can i', 'does this work', etc.). Matching
   calls are buffered instead of posted.

A post-run step in action.yml reports the buffered call count and bodies
as a workflow warning for diagnostics.

Backward compatibility:
- Existing single-agent prompts (no confirmed param) post normally unless
  the body happens to start with a probe phrase (unlikely for real
  review comments)
- The code-review skill is being updated to pass confirmed: true in its
  final posting step
- Subagent probes that would previously post now harmlessly buffer

* refactor: replace probe-regex with Haiku classification in post-step

The regex approach was narrow and could miss creative probe phrasings.
Replaced with a batch Haiku classification that runs after the session
completes.

Flow:
- MCP server: confirmed !== true -> buffer to JSONL (no classification
  in-band, no latency in the tool path)
- Post-step (src/entrypoints/post-buffered-inline-comments.ts): reads
  buffer, sends all bodies to a single Haiku call, posts only those
  classified as real review comments
- confirmed=false entries are never posted regardless of classification

Fail-open: if ANTHROPIC_API_KEY is unavailable (Bedrock/Vertex users)
or the classification call fails, posts all unconfirmed comments. This
matches pre-PR behavior where all calls posted immediately.

The post-step emits :⚠️: for each filtered comment so users can
see what was dropped and why.

* feat: add classify_inline_comments opt-out input

New action input classify_inline_comments (default 'true'). Setting to
'false' restores pre-buffering behavior: all inline comment calls post
immediately regardless of the confirmed param.

Threads through: action input -> CLASSIFY_INLINE_COMMENTS env ->
context.inputs.classifyInlineComments -> MCP server env ->
CLASSIFY_ENABLED module const.

Post-step is also gated on the input so it skips entirely when
classification is disabled.

* docs: document classify_inline_comments input and confirmed param

- usage.md: add classify_inline_comments to inputs table
- solutions.md: mention confirmed=true in the prompt example and explain
  buffering/classification in the tool permissions section
v1.0.71
2026-03-12 00:12:55 -07:00
GitHub Actions
567be3da98 chore: bump Claude Code to 2.1.73 and Agent SDK to 0.2.73 2026-03-11 18:33:26 +00:00
GitHub Actions
eb99fb38f0 chore: bump Claude Code to 2.1.72 and Agent SDK to 0.2.72 2026-03-10 00:49:35 +00:00
dustin
33fbb80626
docs: warn that allowed_bots can expose the action to external triggers (#1039)
allowed_bots does not verify that a matching bot is installed on the
repository or has write access. On a public repo, external GitHub Apps
may be able to trigger workflow events (issues, comments, PR reviews).
If the workflow listens on those events and allowed_bots is '*', an
external App can invoke this action with a prompt it controls.

Default config (allowed_bots: "") is unaffected.

- docs/security.md: add warning and mitigation guidance
- docs/usage.md: add inline warning to the allowed_bots input row
- action.yml: add warning to the allowed_bots input description

🏠 Remote-Dev: homespace
2026-03-09 13:04:11 -07:00
GitHub Actions
3428ca8991 chore: bump Claude Code to 2.1.71 and Agent SDK to 0.2.71 2026-03-07 00:11:30 +00:00
GitHub Actions
26ec041249 chore: bump Claude Code to 2.1.70 and Agent SDK to 0.2.70 v1.0.70 2026-03-06 01:18:43 +00:00
GitHub Actions
1fc90f3ed9 chore: bump Claude Code to 2.1.69 and Agent SDK to 0.2.69 v1.0.69 2026-03-05 00:24:53 +00:00
GitHub Actions
e763fe78de chore: bump Claude Code to 2.1.68 and Agent SDK to 0.2.68 v1.0.68 2026-03-04 10:09:58 +00:00