SECURITY FIX: Addresses authorization_bypass vulnerability (LOW severity)
The allowed_non_write_users='*' configuration previously bypassed write
permission checks for all users with only a warning. This created a
security misconfiguration risk.
Changes:
- Added new input 'bypass_write_permission_check_acknowledgment' required
when using wildcard (*)
- Modified checkWritePermissions() to throw error if wildcard used without
explicit acknowledgment flag
- Updated all documentation (security.md, usage.md) with new requirement
- Updated example workflows to include acknowledgment flag
- Added tests for new validation behavior
This prevents accidental security misconfigurations while maintaining the
feature for intentional use cases like issue triage workflows.
Affected file: src/github/validation/permissions.ts:27
Category: authorization_bypass
Severity: LOW
Previously, the SDK path would result in the CLI setting the entrypoint
to 'sdk-ts' internally, while the non-SDK (CLI) path would correctly
set it to 'claude-code-github-action' based on the CLAUDE_CODE_ACTION
env var.
This change explicitly sets CLAUDE_CODE_ENTRYPOINT in both:
1. The action.yml env block (for consistency)
2. The SDK options env (to override the CLI's internal default)
The CLI respects pre-set entrypoint values, so this ensures consistent
user agent reporting for both execution paths.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: prevent orphaned installer processes from blocking retries
When the `timeout` command expires during Claude Code installation, it only
kills the direct child bash process, not the grandchild installer processes.
These orphaned processes continue holding a lock file, causing retry attempts
to fail with "another process is currently installing Claude".
Add `--foreground` flag to run the command in a foreground process group so
all child processes are killed on timeout. Add `--kill-after=10` to send
SIGKILL if SIGTERM doesn't terminate processes within 10 seconds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: apply same timeout fix to root action.yml
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: support local plugin marketplace paths
Enable installing plugins from local directories in addition to remote
Git URLs. This allows users to use local plugin marketplaces within their
repository without requiring them to be hosted in a separate Git repo.
Example usage:
plugin_marketplaces: "./my-local-marketplace"
plugins: "my-plugin@my-local-marketplace"
Supported path formats:
- Relative paths: ./plugins, ../shared-plugins
- Absolute Unix paths: /home/user/plugins
- Absolute Windows paths: C:\Users\user\plugins
Fixes#664
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* support hidden folders
* Revert "support hidden folders"
This reverts commit a55626c9f1.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: send user request as separate content block for slash command support
When in tag mode with the SDK path, extracts the user's request from the
trigger comment (text after @claude) and sends it as a separate content
block. This enables the CLI to process slash commands like "/review-pr".
- Add extract-user-request utility to parse trigger comments
- Write user request to separate file during prompt generation
- Send multi-block SDKUserMessage when user request file exists
- Add tests for the extraction utility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: address PR feedback
- Fix potential ReDoS vulnerability by using string operations instead of regex
- Remove unused extractUserRequestFromEvent function and tests
- Extract USER_REQUEST_FILENAME to shared constants
- Conditionally log user request based on showFullOutput setting
- Add JSDoc documentation to extractUserRequestFromContext
---------
Co-authored-by: Claude <noreply@anthropic.com>
When users specify --setting-sources in claude_args (e.g., '--setting-sources user'),
the action now respects that value instead of overriding it with all three sources.
This fixes an issue where users who wanted to avoid in-repo configs would still
have them loaded because the settingSources was hardcoded to ['user', 'project', 'local'].
Fixes#749
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
* fix: merge multiple --mcp-config flags instead of overwriting
When users provide their own --mcp-config in claude_args, the action's
built-in MCP servers (github_comment, github_ci, etc.) were being lost
because multiple --mcp-config flags were overwriting each other.
This fix:
- Adds mcp-config to ACCUMULATING_FLAGS to collect all values
- Changes delimiter to null character to avoid conflicts with JSON
- Adds mergeMcpConfigs() to combine mcpServers objects from multiple configs
- Merges inline JSON configs while preserving file path configs
Fixes#745🤖 Generated with [Claude Code](https://claude.ai/code)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
* fix: support hyphenated --allowed-tools flag and multiple values
The --allowed-tools flag was not being parsed correctly when:
1. Using the hyphenated form (--allowed-tools) instead of camelCase (--allowedTools)
2. Passing multiple space-separated values after a single flag
(e.g., --allowed-tools "Tool1" "Tool2" "Tool3")
This fix:
- Adds hyphenated variants (allowed-tools, disallowed-tools) to ACCUMULATING_FLAGS
- Updates parsing to consume all consecutive non-flag values for accumulating flags
- Merges values from both camelCase and hyphenated variants
Fixes#746🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ashwin Bhat <ashwin-ant@users.noreply.github.com>
Add a new `session_id` output that exposes the Claude Code session ID,
allowing other workflows or Claude Code instances to resume the
conversation using `--resume <session_id>`.
Changes:
- Add parseAndSetSessionId() function to extract session_id from
the system.init message in execution output
- Add session_id output to both action.yml and base-action/action.yml
- Add comprehensive tests for the new functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Change USE_AGENT_SDK to default to true instead of false. The Agent SDK
path is now used by default; set USE_AGENT_SDK=false to use the CLI path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Replace direct template expansion of user inputs in shell scripts with
environment variables to prevent potential command injection attacks.
Changes:
- sync-base-action.yml: Use $GITHUB_EVENT_NAME and $GITHUB_ACTOR instead of template expansion
- action.yml: Pass path_to_bun_executable and path_to_claude_code_executable through env vars
- base-action/action.yml: Same env var changes for path inputs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Two fixes for the Agent SDK path (USE_AGENT_SDK=true):
1. Add settingSources to load filesystem settings
- Without this, CLI-installed plugins aren't available to the SDK
- Also needed to load CLAUDE.md files from the project
2. Default systemPrompt to claude_code preset
- Without an explicit systemPrompt, the SDK would use no system prompt
- Now defaults to { type: "preset", preset: "claude_code" } to match CLI behavior
Also adds logging of SDK options (excluding env) for debugging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* fix: merge allowedTools from claudeArgs when using Agent SDK
When USE_AGENT_SDK=true, the allowedTools from claudeArgs (which contains
tag mode's required tools like mcp__github_comment__update_claude_comment)
were being lost because parseClaudeArgsToExtraArgs converts args to a
Record<string, string>, and the SDK was using sdkOptions.allowedTools
(from direct options) instead of merging with extraArgs.allowedTools.
This fix:
- Extracts allowedTools/disallowedTools from extraArgs after parsing
- Merges them with any direct options.allowedTools/disallowedTools
- Removes them from extraArgs to prevent duplicate CLI flags
- Passes the merged list as sdkOptions.allowedTools
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: accumulate multiple --allowedTools flags in claudeArgs
When tag mode adds its --allowedTools (with MCP tools) and the user also
provides --allowedTools in their claude_args, the parseClaudeArgsToExtraArgs
function was only keeping the last value. This caused tag mode's required
tools like mcp__github_comment__update_claude_comment to be lost.
Now allowedTools and disallowedTools flags accumulate their values when
they appear multiple times in claudeArgs, so both tag mode's tools and
user's tools are preserved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: add Agent SDK support with USE_AGENT_SDK feature flag
Add a feature-flagged code path that uses the Agent SDK instead of
spawning the CLI as a subprocess. When USE_AGENT_SDK=true is set,
the new SDK path is used; otherwise, existing CLI behavior is unchanged.
Changes:
- Add parse-sdk-options.ts for parsing ClaudeOptions into SDK format
- Add run-claude-sdk.ts for SDK execution with query() function
- Update run-claude.ts with feature flag check at entry point
- Update update-comment-link.ts to handle both cost_usd and total_cost_usd
- Add @anthropic-ai/claude-agent-sdk dependency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: simplify SDK types by using @anthropic-ai/claude-agent-sdk types directly
- Remove duplicate SdkRunOptions and McpStdioServerConfig types
- Use SDK's Options and McpStdioServerConfig types directly
- Return { sdkOptions, showFullOutput, hasJsonSchema } from parseSdkOptions
- Remove unnecessary convertMcpServers function
- Net reduction of ~70 lines
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: use extraArgs for claudeArgs pass-through to CLI
Simplify option parsing by converting claudeArgs to extraArgs record
and letting the SDK/CLI handle --mcp-config, --json-schema, etc.
- Remove extractJsonSchema and parseMcpConfigs functions
- Add parseClaudeArgsToExtraArgs for simple flag parsing
- CLI handles complex args like --mcp-config directly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* ci
* refactor: remove hardcoded permission bypass flags
The SDK path should match CLI path behavior - permissions are handled
by the CLI itself, not hardcoded in the action.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: add logging for SDK vs CLI path selection
---------
Co-authored-by: Claude <noreply@anthropic.com>
The GNU `timeout` command is not available on macOS. Check if it exists
and use it when available, otherwise run without timeout.
Also extracts the version into a CLAUDE_CODE_VERSION variable for
easier maintenance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
* feat: add Azure AI Foundry provider support
Add support for Azure AI Foundry as a fourth cloud provider option alongside Anthropic API, AWS Bedrock, and Google Vertex AI.
Changes:
- Add use_foundry input to enable Azure AI Foundry authentication
- Add Azure environment variables (ANTHROPIC_FOUNDRY_RESOURCE, ANTHROPIC_FOUNDRY_API_KEY, ANTHROPIC_FOUNDRY_BASE_URL)
- Support automatic base URL construction from resource name
- Add validation logic with mutual exclusivity checks for all providers
- Add comprehensive test coverage (7 Azure-specific tests, 3 mutual exclusivity tests)
- Add complete Azure AI Foundry documentation with OIDC and API key authentication
- Update README to reference Azure AI Foundry support
Features:
- Primary authentication via Microsoft Entra ID (OIDC) using azure/login action
- Optional API key authentication fallback
- Custom model deployment name support via ANTHROPIC_DEFAULT_*_MODEL variables
- Clear validation error messages for missing configuration
All tests pass (25 validation tests total).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: rename Azure AI Foundry to Microsoft Foundry and remove API key support
- Rename all references from "Azure AI Foundry" to "Microsoft Foundry"
- Remove ANTHROPIC_FOUNDRY_API_KEY support (OIDC only)
- Update documentation to reflect OIDC-only authentication
- Update tests to remove API key test case
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: simplify Microsoft Foundry setup and remove URL auto-construction
- Link to official docs instead of duplicating setup instructions
- Remove automatic base URL construction from resource name
- Pass ANTHROPIC_FOUNDRY_BASE_URL as-is
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: add structured output support
Add support for Agent SDK structured outputs.
New input: json_schema
Output: structured_output (JSON string)
Access: fromJSON(steps.id.outputs.structured_output).field
Docs: https://docs.claude.com/en/docs/agent-sdk/structured-outputs
* rm unused
* refactor: simplify structured outputs to use claude_args
Remove json_schema input in favor of passing --json-schema flag directly
in claude_args. This simplifies the interface by treating structured outputs
like other CLI flags (--model, --max-turns, etc.) instead of as a special
input that gets injected.
Users now specify: claude_args: '--json-schema {...}'
Instead of separate: json_schema: {...}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: remove unused json-schema util and revert version
- Remove src/utils/json-schema.ts (no longer used after refactor)
- Revert Claude Code version from 2.0.45 back to 2.0.42
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>