Skip to content

chore: Cherry-picked changes from upstream - #42

Merged
Raj-StepSecurity merged 3 commits into
mainfrom
auto-cherry-pick
Aug 3, 2026
Merged

chore: Cherry-picked changes from upstream#42
Raj-StepSecurity merged 3 commits into
mainfrom
auto-cherry-pick

Conversation

@Raj-StepSecurity

Copy link
Copy Markdown
Collaborator

No description provided.

@Raj-StepSecurity

Copy link
Copy Markdown
Collaborator Author

🚀 PR Updated!

The PR has been updated with the latest cherry-picked commits.

@step-security/maintained-actions-dev Please review and approve the changes.

📦 Target Release Version: v1.0.183
📋 Previous Release Version: v1.0.167

@github-actions

Copy link
Copy Markdown

🔍 Cherry-Pick Verification Report

📦 Upstream Changes: v1.0.167...v1.0.183

📋 File-by-File Analysis:

base-action/action.yml

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+1 -1)

base-action/bun.lock

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+10 -10) | Missing 1 deletions

base-action/package.json

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+1 -1) | Missing 1 deletions

base-action/src/index.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ❌ No
  • Status: 🔴 Missing - ❌ No PR patch available (+2 -1)

base-action/src/parse-sdk-options.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+4 -1)

base-action/src/run-claude-sdk.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+14 -4)

base-action/src/workload-identity.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+79 -3)

base-action/test/parse-sdk-options.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+37 -6)

base-action/test/run-claude-sdk.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+65 -0)

base-action/test/workload-identity.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+132 -1) | Missing 24 additions | Missing 1 deletions

bun.lock

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+10 -10) | Missing 1 additions | Missing 1 deletions

docs/configuration.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ❌ No
  • Status: 🔴 Missing - ❌ No PR patch available (+11 -11)

docs/custom-automations.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ❌ No
  • Status: 🔴 Missing - ❌ No PR patch available (+2 -2)

docs/faq.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ❌ No
  • Status: 🔴 Missing - ❌ No PR patch available (+2 -2)

docs/migration-guide.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ❌ No
  • Status: 🔴 Missing - ❌ No PR patch available (+24 -23)

docs/usage.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ❌ No
  • Status: 🔴 Missing - ❌ No PR patch available (+2 -2)

src/entrypoints/format-turns.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+2 -1)

src/entrypoints/run.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+3 -2) | Missing 2 additions | Missing 1 deletions

src/github/data/fetcher.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+6 -6)

src/github/data/formatter.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+4 -4)

src/github/operations/branch.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+6 -3) | Missing 1 additions | Missing 1 deletions

src/github/types.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+8 -4)

src/github/utils/sanitizer.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+13 -1)

src/utils/branch-template.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+15 -1)

test/branch-template.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+48 -0)

test/comments-common.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+74 -0)

test/create-prompt.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+82 -0)

test/data-fetcher.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+38 -0)

test/data-formatter.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+56 -0)

test/format-turns.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+30 -0)

test/sanitizer.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+26 -0)

test/validate-branch-name.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+10 -0)

📊 Summary:

  • Total files changed upstream: 32
  • Files present in PR: 26/32
  • Files with matching changes: 20/32

Overall Status: 🔴 INCOMPLETE - Missing files or changes

@github-actions

Copy link
Copy Markdown

🔍 Cherry-Pick Verification Report

📦 Upstream Changes: v1.0.167...v1.0.183

📋 File-by-File Analysis:

base-action/action.yml

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+1 -1)

base-action/bun.lock

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+10 -10) | Missing 1 deletions

base-action/package.json

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+1 -1) | Missing 1 deletions

base-action/src/index.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+2 -1)

base-action/src/parse-sdk-options.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+4 -1)

base-action/src/run-claude-sdk.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+14 -4)

base-action/src/workload-identity.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+79 -3)

base-action/test/parse-sdk-options.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+37 -6)

base-action/test/run-claude-sdk.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+65 -0)

base-action/test/workload-identity.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+132 -1) | Missing 24 additions | Missing 1 deletions

bun.lock

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ❌ No
  • Status: 🟡 Partial - ❌ Cherry-pick incomplete (+10 -10) | Missing 1 deletions

docs/configuration.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+11 -11)

docs/custom-automations.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+2 -2)

docs/faq.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+2 -2)

docs/migration-guide.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+24 -23)

docs/usage.md

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+2 -2)

src/entrypoints/format-turns.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+2 -1)

src/entrypoints/run.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+3 -2)

src/github/data/fetcher.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+6 -6)

src/github/data/formatter.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+4 -4)

src/github/operations/branch.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+6 -3)

src/github/types.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+8 -4)

src/github/utils/sanitizer.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+13 -1)

src/utils/branch-template.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+15 -1)

test/branch-template.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+48 -0)

test/comments-common.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+74 -0)

test/create-prompt.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+82 -0)

test/data-fetcher.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+38 -0)

test/data-formatter.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+56 -0)

test/format-turns.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+30 -0)

test/sanitizer.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+26 -0)

test/validate-branch-name.test.ts

  • Upstream has changes: ✅ Yes
  • File exists in PR: ✅ Yes
  • Changes match: ✅ Yes
  • Status: 🟢 Perfect - ✅ All changes applied correctly (+10 -0)

📊 Summary:

  • Total files changed upstream: 32
  • Files present in PR: 32/32
  • Files with matching changes: 28/32

⚠️ Overall Status: 🟡 PARTIAL - All files present but some changes missing

// RUNNER_TEMP is per-job, not per-step: remove the identity token, the
// profile, and the cached exchanged credential so they don't outlive
// this step.
rmSync(tokenDir, { recursive: true, force: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stop() can now throw, and it's the first unguarded statement in run.ts's cleanup finally (src/entrypoints/run.ts:371).

force: true only swallows ENOENT — EPERM/EACCES/EBUSY/ENOTEMPTY still throw, and maxRetries defaults to 0. ENOTEMPTY is plausible here specifically because ANTHROPIC_CONFIG_DIR is a subdirectory of tokenDir, so a claude subprocess that outlives the parent can repopulate it mid-walk.

If it throws, the rest of the finally never runs: the tracking comment stays on "Claude is working…", no step summary, and core.setOutput("github_token", …) (run.ts:412) never fires — so the always() "Revoke app token" step, which is gated on steps.run.outputs.github_token != '' (action.yml:483), is skipped and the GitHub App installation token stays valid for up to an hour.

Suggested change
rmSync(tokenDir, { recursive: true, force: true });
try {
rmSync(tokenDir, {
recursive: true,
force: true,
maxRetries: 3,
retryDelay: 100,
});
} catch (error) {
core.warning(
`Failed to remove the workload identity token dir: ${error instanceof Error ? error.message : String(error)}`,
);
}

tokenFile,
stop: () => clearInterval(refreshInterval),
stop: () => {
clearInterval(refreshInterval);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An in-flight refresh can recreate the directory right after rmSync deletes it, defeating the cleanup this PR adds.

clearInterval stops future ticks but can't cancel a writeIdentityToken() already awaiting fetchIdentityToken, which goes through retryWithBackoff (3 attempts, 5s → 10s) and can stay pending 15+ seconds. When it finally resolves it runs mkdirSync(tokenDir, …) + writeFileSync(tokenFile, …) (lines 149-150) and writes a fresh OIDC JWT to disk after cleanup. Since RUNNER_TEMP is per-job, it then survives for the rest of the job — exactly what the comment below says the rmSync prevents.

Suggest a stopped flag checked after the await:

let stopped = false;

const writeIdentityToken = async () => {
  const identityToken = await fetchIdentityToken(audience);
  if (stopped) return; // cleanup already ran; don't resurrect the token file
  core.setSecret(identityToken);
  mkdirSync(tokenDir, { recursive: true, mode: 0o700 });
  writeFileSync(tokenFile, identityToken, { mode: 0o600 });
};

…and set stopped = true before clearInterval here.

*/
function writeFederationProfile(baseDir: string): string {
// Every input that changes which credential the exchange mints must be in
// here; service_account_id and scope are sent in the exchange request body.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fingerprint omits ANTHROPIC_OIDC_AUDIENCE, which contradicts this comment's own invariant. It's a first-class input (action.yml:375) and is used to mint the JWT at line 139.

Two invocations in one job with identical rule/org/service-account/workspace/scope but different audiences land in the same config-<fp> dir. Per the doc comment above, the SDK's cache reuses on expires_at alone — so the second invocation silently reuses the first one's token instead of exchanging with the new audience assertion, bypassing any audience constraint the federation rule enforces.

Suggested change
// here; service_account_id and scope are sent in the exchange request body.
// Every input that changes which credential the exchange mints must be in
// here; service_account_id and scope are sent in the exchange request body,
// and the audience is bound into the identity token assertion.

…plus process.env.ANTHROPIC_OIDC_AUDIENCE?.trim() ?? "" in the hashed array.

Comment thread base-action/src/index.ts
} finally {
// Stop refreshing the workload identity token file so the process can exit
// Stop refreshing the workload identity token file (so the process can
// exit) and delete the token material so it doesn't outlive this step

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment now documents an invariant the code doesn't hold: process.exit(1) at line 126 doesn't unwind the stack, so on every failure path of the standalone base-action this finally never runs and the token dir is left behind.

That was tolerable before (the leftover was a single-use OIDC JWT that fails with jti_reused), but this PR makes the same directory hold the SDK's cached exchanged Anthropic credential — a reusable bearer token valid until expires_at. RUNNER_TEMP is per-job, so any later step in the same job can read it.

core.setFailed already forces a non-zero exit, so dropping the explicit process.exit(1) is the smallest fix. (An always() cleanup step in action.yml — the convention this repo already uses for token revocation and SSH signing — would also cover SIGTERM/cancellation, which no finally catches in either entrypoint.)

}

if (!isSuccess) {
if (resultMessage.subtype === "success" && resultMessage.is_error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new diagnostic is unreachable when --json-schema is in play, and the user gets a misleading error instead.

Before this change subtype === "success" implied isSuccess, so the hasJsonSchema && !isSuccess && subtype === "success" combination couldn't happen. It can now — and the hasJsonSchema block at lines 218-237 throws first with --json-schema was provided but Claude did not return structured_output. Result subtype: success, which reads as "the model just didn't emit structured output" and hides the real cause.

Moving the if (!isSuccess) { … } block above the if (hasJsonSchema) block fixes it.

// Reference-style images: ![alt][ref] -> ![][ref] (keep the label, drop the
// alt text, which is otherwise a hidden-instruction channel just like the
// inline form above).
content = content.replace(/!\[[^\]]*\](\[[^\]]*\])/g, "![]$1");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks collapsed reference images, which is the one case where the alt text is the lookup label.

![][]![][]: [^\]]* matches alt text, then the capture group matches the empty []. The label is now empty, so it no longer resolves to its [alt text]: url definition and GitHub renders the literal text instead of the image. The comment's stated intent ("keep the label, drop the alt text") is violated here. Guarding on a non-empty label fixes it:

Suggested change
content = content.replace(/!\[[^\]]*\](\[[^\]]*\])/g, "![]$1");
content = content.replace(/!\[[^\]]*\](\[[^\]]+\])/g, "![]$1");

Two related gaps, if you want to close the channel more completely (happy to defer these to a follow-up):

  • The shortcut form ![payload] + a separate [payload]: url definition still renders as an image with alt="payload" and isn't matched at all.
  • Link reference definitions render as nothing, so [x]: https://ex.com "instructions here" is 100% invisible to a reviewer yet reaches the prompt verbatim — stripMarkdownLinkTitles only handles inline ](url "title"). Keeping [ref] labels intact (correctly) makes this channel more relevant than before.

Comment on lines +204 to +205
const modelFromClaudeArgs = extraArgs["model"] || undefined;
delete extraArgs["model"];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This inverts CLI precedence: an explicit --model in claude_args now silently loses to an ambient env var.

--model is deleted from extraArgs (so it never reaches the CLI) and is only a fallback at line 310: model: options.model || modelFromClaudeArgs. But options.model is process.env.ANTHROPIC_MODEL (base-action/src/index.ts:106, src/entrypoints/run.ts:338) — an ambient env var, not an explicit input. A CLI flag beating an env var is the normal expectation; this does the opposite, with nothing logged.

Scenario: a workflow sets env: ANTHROPIC_MODEL: <opus> at the job level and one step passes claude_args: --model <haiku> for a cheap task. Previously the flag was passed through to the CLI; now it's dropped and opus runs, at ~10× the cost, with no indication in the logs.

Suggest modelFromClaudeArgs || options.model, or at minimum a core.warning() when both are set.

## Automated Documentation Updates

Automatically update documentation when specific files change (see [`examples/claude-pr-path-specific.yml`](../examples/claude-pr-path-specific.yml)):
Automatically update documentation when specific files change (see [`examples/pr-review-filtered-paths.yml`](../examples/pr-review-filtered-paths.yml)):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file exists, but it doesn't match this section. examples/pr-review-filtered-paths.yml is name: Claude Review - Path Specific — a path-filtered code review workflow that posts inline comments and never touches docs, whereas this section is "Automated Documentation Updates" with a docs-update prompt.

The parallel change at line 50 (author filtering → pr-review-filtered-authors.yml) is a correct match; only this one is mismatched. Either retitle the section or drop the link.

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Reviewed across code quality, security, performance, test coverage, and docs accuracy. The individual fixes here are well-motivated and the comments explaining why (the jti_reused rationale, the RUNNER_TEMP-is-per-job note, the ghost-account explanation) are genuinely good — unusually so for a cherry-pick. 8 inline comments; the workload-identity cleanup ones are the two I would not merge without.

Things I verified as clean, since they were the obvious places to look:

  • The author: GitHubAuthor | null widening is complete. Every dereference in src/ is guarded (formatter.ts:24,36,74,88, fetcher.ts:222), and the as GitHubPullRequest / as GitHubIssue casts are within the union, so nothing is hidden from the compiler. The "ghost" default is safe — that login is held by GitHub and unregistrable, and no authorization decision keys off a comment author.
  • Allowing a leading _ in validateBranchName does not weaken the injection posture. Option injection needs a leading - (still rejected), _ is inert in git's refspec grammar, it was already allowed in every non-leading position, and every consumer is execFileSync or Bun $. Doc comment and error message were both updated consistently.
  • --append-system-prompt really is supported — there is no flag allowlist in the claude_args path, so it lands in extraArgs and is forwarded verbatim. Both new example files exist. Version bump to 2.1.220 is complete (no 2.1.203 left).
  • sanitizeLabel is not meaningful duplication of extractDescription — they deliberately differ (replace-with-hyphen vs. drop).

Smaller items, take or leave:

  1. Leftover --system-prompt guidance. The PR's premise is that "extra instructions" must use --append-system-prompt, but three places still recommend --system-prompt for exactly that: examples/claude.yml:50 (verbatim the custom_instructions pattern, and docs/setup.md tells users to copy this file), docs/experimental.md:61, and docs/custom-automations.md:118. In tag mode this is not cosmetic — the built-in prompt that tag mode's MCP/commit workflow depends on gets discarded.

  2. test/data-fetcher.test.ts uses require() inside test bodies (~9 occurrences, lines 1414-1536) while the module is already statically imported at line 2. That yields any, so the { author: null } fixtures bypass TypeScript entirely — reverting the generic constraint at fetcher.ts:207-208 would leave both bun test and bun run typecheck green. Switching to the top-level import and typing the fixtures as GitHubComment[] would make these tests actually pin the type change. (test/data-formatter.test.ts does this correctly.)

  3. test/comments-common.test.ts:19-24 is tautological. GITHUB_SERVER_URL is a module-level const resolving to https://github.com in both local and CI environments, so expect(createJobRunLink(...)).toContain(GITHUB_SERVER_URL) passes even against a hardcoded implementation — it asserts nothing about GHES. Needs the env var set before first import to be real. (Also worth noting this file covers comments/common.ts, which the PR does not change — net-new coverage rather than verification of this PR.)

  4. No positive control for the changed isSuccess condition. Both tests in base-action/test/run-claude-sdk.test.ts assert failure, and no test anywhere asserts conclusion === "success" for runClaudeWithSdk. An over-broad edit making isSuccess always false would pass the suite. Also, mock.module("@anthropic-ai/claude-agent-sdk", ...) is never restored, so it leaks to every later test file in the run — latent today, but a trap.

  5. Pre-existing, so out of scope, but flagging while nearby: stripMarkdownLinkTitles (sanitizer.ts:23-24) has genuine catastrophic backtracking — [^)]+ and \s+ overlap on whitespace, so [a]( followed by ~65k spaces (a single issue body fits) is ~2.1e9 backtrack steps per pattern, ~5-30s of pegged CPU. Every non-trigger comment in a thread is attacker-controlled and all 100+ get sanitized, so this is reachable by anyone who can comment. Fix is one character class: [^)\s]+ (also more correct — a markdown link destination cannot contain an unescaped space).

🤖 Generated with Claude Code

@Raj-StepSecurity
Raj-StepSecurity merged commit 4396cc4 into main Aug 3, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants