Compare commits

..

5 Commits

Author SHA1 Message Date
Ashwin Bhat
a017b830c0 chore: comment out release-base-action job in release workflow (#833)
Temporarily disable the release-base-action job that syncs releases
to the claude-code-base-action repository. The job checkout step
and subsequent tag/release creation steps are now commented out.


Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 2
Claude-Permission-Prompts: 2
Claude-Escapes: 0

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-16 14:16:27 -08:00
GitHub Actions
75f52e56b2 chore: bump Claude Code to 2.1.9 and Agent SDK to 0.2.9 2026-01-16 02:18:38 +00:00
Ashwin Bhat
1bbc9e7ff7 fix: add checkHumanActor to agent mode (#826)
Fixes issue #641 where users were getting banned due to rapid successive
Claude runs triggered by the synchronize event.

Changes:
- Add checkHumanActor call to agent mode's prepare() method to reject
  bot-triggered workflows unless explicitly allowed via allowed_bots
- Update checkHumanActor to accept GitHubContext (union type) instead
  of just ParsedGitHubContext
- Add tests for bot rejection/allowance in agent mode

Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 1
Claude-Permission-Prompts: 3
Claude-Escapes: 0
2026-01-15 10:28:46 -08:00
Ashwin Bhat
625ea1519c docs: clarify that Claude does not auto-create PRs by default (#824)
Add a new section to security.md explaining that in the default
configuration, Claude commits to a branch and provides a link for
the user to create the PR themselves, ensuring human oversight.


Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 2
Claude-Permission-Prompts: 2
Claude-Escapes: 0

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 15:22:40 -08:00
GitHub Actions
a9171f0ced chore: bump Claude Code to 2.1.7 and Agent SDK to 0.2.7 2026-01-14 00:03:29 +00:00
24 changed files with 206 additions and 149 deletions

View File

@@ -17,6 +17,7 @@ TASK OVERVIEW:
1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else. 1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else.
2. Next, use gh commands to get context about the issue: 2. Next, use gh commands to get context about the issue:
- Use `gh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details - Use `gh issue view ${{ github.event.issue.number }}` to retrieve the current issue's details
- Use `gh search issues` to find similar issues that might provide context for proper categorization - Use `gh search issues` to find similar issues that might provide context for proper categorization
- You have access to these Bash commands: - You have access to these Bash commands:
@@ -26,6 +27,7 @@ TASK OVERVIEW:
- Bash(gh search:\*) - to search for similar issues - Bash(gh search:\*) - to search for similar issues
3. Analyze the issue content, considering: 3. Analyze the issue content, considering:
- The issue title and description - The issue title and description
- The type of issue (bug report, feature request, question, etc.) - The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned - Technical areas mentioned
@@ -34,6 +36,7 @@ TASK OVERVIEW:
- Components affected - Components affected
4. Select appropriate labels from the available labels list provided above: 4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature - Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive - Be specific but comprehensive
- IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from gh label list - IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from gh label list

View File

@@ -24,5 +24,4 @@ jobs:
prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER${{ github.event.issue.number }}" prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER${{ github.event.issue.number }}"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues
bypass_write_permission_check_acknowledgment: true # Required when using wildcard
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -109,48 +109,48 @@ jobs:
echo "Updated $major_version tag to point to $next_version" echo "Updated $major_version tag to point to $next_version"
release-base-action: # release-base-action:
needs: create-release # needs: create-release
if: ${{ !inputs.dry_run }} # if: ${{ !inputs.dry_run }}
runs-on: ubuntu-latest # runs-on: ubuntu-latest
environment: production # environment: production
steps: # steps:
- name: Checkout base-action repo # - name: Checkout base-action repo
uses: actions/checkout@v5 # uses: actions/checkout@v5
with: # with:
repository: anthropics/claude-code-base-action # repository: anthropics/claude-code-base-action
token: ${{ secrets.CLAUDE_CODE_BASE_ACTION_PAT }} # token: ${{ secrets.CLAUDE_CODE_BASE_ACTION_PAT }}
fetch-depth: 0 # fetch-depth: 0
#
# - name: Create and push tag # - name: Create and push tag
# run: | # run: |
# next_version="${{ needs.create-release.outputs.next_version }}" # next_version="${{ needs.create-release.outputs.next_version }}"
#
# git config user.name "github-actions[bot]" # git config user.name "github-actions[bot]"
# git config user.email "github-actions[bot]@users.noreply.github.com" # git config user.email "github-actions[bot]@users.noreply.github.com"
#
# # Create the version tag # # Create the version tag
# git tag -a "$next_version" -m "Release $next_version - synced from claude-code-action" # git tag -a "$next_version" -m "Release $next_version - synced from claude-code-action"
# git push origin "$next_version" # git push origin "$next_version"
#
# # Update the beta tag # # Update the beta tag
# git tag -fa beta -m "Update beta tag to ${next_version}" # git tag -fa beta -m "Update beta tag to ${next_version}"
# git push origin beta --force # git push origin beta --force
#
# - name: Create GitHub release # - name: Create GitHub release
# env: # env:
# GH_TOKEN: ${{ secrets.CLAUDE_CODE_BASE_ACTION_PAT }} # GH_TOKEN: ${{ secrets.CLAUDE_CODE_BASE_ACTION_PAT }}
# run: | # run: |
# next_version="${{ needs.create-release.outputs.next_version }}" # next_version="${{ needs.create-release.outputs.next_version }}"
#
# # Create the release # # Create the release
# gh release create "$next_version" \ # gh release create "$next_version" \
# --repo anthropics/claude-code-base-action \ # --repo anthropics/claude-code-base-action \
# --title "$next_version" \ # --title "$next_version" \
# --notes "Release $next_version - synced from anthropics/claude-code-action" \ # --notes "Release $next_version - synced from anthropics/claude-code-action" \
# --latest=false # --latest=false
#
# # Update beta release to be latest # # Update beta release to be latest
# gh release edit beta \ # gh release edit beta \
# --repo anthropics/claude-code-base-action \ # --repo anthropics/claude-code-base-action \
# --latest # --latest

View File

@@ -35,10 +35,6 @@ inputs:
description: "Comma-separated list of usernames to allow without write permissions, or '*' to allow all users. Only works when github_token input is provided. WARNING: Use with extreme caution - this bypasses security checks and should only be used for workflows with very limited permissions (e.g., issue labeling)." description: "Comma-separated list of usernames to allow without write permissions, or '*' to allow all users. Only works when github_token input is provided. WARNING: Use with extreme caution - this bypasses security checks and should only be used for workflows with very limited permissions (e.g., issue labeling)."
required: false required: false
default: "" default: ""
bypass_write_permission_check_acknowledgment:
description: "REQUIRED when using allowed_non_write_users='*'. Set to 'true' to explicitly acknowledge the security implications of bypassing write permission checks for all users. This flag serves as a safeguard against accidental security misconfigurations."
required: false
default: "false"
# Claude Code configuration # Claude Code configuration
prompt: prompt:
@@ -190,7 +186,6 @@ runs:
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }} OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
ALLOWED_BOTS: ${{ inputs.allowed_bots }} ALLOWED_BOTS: ${{ inputs.allowed_bots }}
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }} ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
BYPASS_WRITE_PERMISSION_CHECK_ACKNOWLEDGMENT: ${{ inputs.bypass_write_permission_check_acknowledgment }}
GITHUB_RUN_ID: ${{ github.run_id }} GITHUB_RUN_ID: ${{ github.run_id }}
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }} USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }} DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
@@ -218,7 +213,7 @@ runs:
# Install Claude Code if no custom executable is provided # Install Claude Code if no custom executable is provided
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
CLAUDE_CODE_VERSION="2.1.6" CLAUDE_CODE_VERSION="2.1.9"
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..." echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
for attempt in 1 2 3; do for attempt in 1 2 3; do
echo "Installation attempt $attempt..." echo "Installation attempt $attempt..."

View File

@@ -57,6 +57,7 @@ Thank you for your interest in contributing to Claude Code Base Action! This doc
``` ```
This script: This script:
- Installs `act` if not present (requires Homebrew on macOS) - Installs `act` if not present (requires Homebrew on macOS)
- Runs the GitHub Action workflow locally using Docker - Runs the GitHub Action workflow locally using Docker
- Requires your `ANTHROPIC_API_KEY` to be set - Requires your `ANTHROPIC_API_KEY` to be set

View File

@@ -85,26 +85,26 @@ Add the following to your workflow file:
## Inputs ## Inputs
| Input | Description | Required | Default | | Input | Description | Required | Default |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------- | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------- |
| `prompt` | The prompt to send to Claude Code | No\* | '' | | `prompt` | The prompt to send to Claude Code | No\* | '' |
| `prompt_file` | Path to a file containing the prompt to send to Claude Code | No\* | '' | | `prompt_file` | Path to a file containing the prompt to send to Claude Code | No\* | '' |
| `allowed_tools` | Comma-separated list of allowed tools for Claude Code to use | No | '' | | `allowed_tools` | Comma-separated list of allowed tools for Claude Code to use | No | '' |
| `disallowed_tools` | Comma-separated list of disallowed tools that Claude Code cannot use | No | '' | | `disallowed_tools` | Comma-separated list of disallowed tools that Claude Code cannot use | No | '' |
| `max_turns` | Maximum number of conversation turns (default: no limit) | No | '' | | `max_turns` | Maximum number of conversation turns (default: no limit) | No | '' |
| `mcp_config` | Path to the MCP configuration JSON file, or MCP configuration JSON string | No | '' | | `mcp_config` | Path to the MCP configuration JSON file, or MCP configuration JSON string | No | '' |
| `settings` | Path to Claude Code settings JSON file, or settings JSON string | No | '' | | `settings` | Path to Claude Code settings JSON file, or settings JSON string | No | '' |
| `system_prompt` | Override system prompt | No | '' | | `system_prompt` | Override system prompt | No | '' |
| `append_system_prompt` | Append to system prompt | No | '' | | `append_system_prompt` | Append to system prompt | No | '' |
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML multiline format) | No | '' | | `claude_env` | Custom environment variables to pass to Claude Code execution (YAML multiline format) | No | '' |
| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | 'claude-4-0-sonnet-20250219' | | `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | 'claude-4-0-sonnet-20250219' |
| `anthropic_model` | DEPRECATED: Use 'model' instead | No | 'claude-4-0-sonnet-20250219' | | `anthropic_model` | DEPRECATED: Use 'model' instead | No | 'claude-4-0-sonnet-20250219' |
| `fallback_model` | Enable automatic fallback to specified model when default model is overloaded | No | '' | | `fallback_model` | Enable automatic fallback to specified model when default model is overloaded | No | '' |
| `anthropic_api_key` | Anthropic API key (required for direct Anthropic API) | No | '' | | `anthropic_api_key` | Anthropic API key (required for direct Anthropic API) | No | '' |
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No | '' | | `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No | '' |
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | 'false' | | `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | 'false' |
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | 'false' | | `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | 'false' |
| `use_node_cache` | Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files) | No | 'false' | | `use_node_cache` | Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files) | No | 'false' |
| `show_full_output` | Show full JSON output (⚠️ May expose secrets - see [security docs](../docs/security.md#-full-output-security-warning)) | No | 'false'\*\* | | `show_full_output` | Show full JSON output (⚠️ May expose secrets - see [security docs](../docs/security.md#-full-output-security-warning)) | No | 'false'\*\* |
\*Either `prompt` or `prompt_file` must be provided, but not both. \*Either `prompt` or `prompt_file` must be provided, but not both.
@@ -490,6 +490,7 @@ This example shows how to use OIDC authentication with GCP Vertex AI:
To securely use your Anthropic API key: To securely use your Anthropic API key:
1. Add your API key as a repository secret: 1. Add your API key as a repository secret:
- Go to your repository's Settings - Go to your repository's Settings
- Navigate to "Secrets and variables" → "Actions" - Navigate to "Secrets and variables" → "Actions"
- Click "New repository secret" - Click "New repository secret"

View File

@@ -124,7 +124,7 @@ runs:
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }} PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
run: | run: |
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
CLAUDE_CODE_VERSION="2.1.6" CLAUDE_CODE_VERSION="2.1.9"
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..." echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
for attempt in 1 2 3; do for attempt in 1 2 3; do
echo "Installation attempt $attempt..." echo "Installation attempt $attempt..."

View File

@@ -6,7 +6,7 @@
"name": "@anthropic-ai/claude-code-base-action", "name": "@anthropic-ai/claude-code-base-action",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@anthropic-ai/claude-agent-sdk": "^0.2.6", "@anthropic-ai/claude-agent-sdk": "^0.2.9",
"shell-quote": "^1.8.3", "shell-quote": "^1.8.3",
}, },
"devDependencies": { "devDependencies": {
@@ -27,7 +27,7 @@
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="], "@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.6", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-lwswHo6z/Kh9djafk2ajPju62+VqHwJ23gueG1alfaLNK4GRYHgCROfiX6/wlxAd8sRvgTo6ry1hNzkyz7bOpw=="], "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.9", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-b4JD6ZKCZeVDqpWBnb+zJISWi3HzlweNlV7Oy/uo5G2XAfUV2M5AJ/tomKZCvZsvmr1fYbmmfyde3GL2h0pksA=="],
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],

View File

@@ -11,7 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@anthropic-ai/claude-agent-sdk": "^0.2.6", "@anthropic-ai/claude-agent-sdk": "^0.2.9",
"shell-quote": "^1.8.3" "shell-quote": "^1.8.3"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -7,7 +7,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/github": "^6.0.1", "@actions/github": "^6.0.1",
"@anthropic-ai/claude-agent-sdk": "^0.2.6", "@anthropic-ai/claude-agent-sdk": "^0.2.9",
"@modelcontextprotocol/sdk": "^1.11.0", "@modelcontextprotocol/sdk": "^1.11.0",
"@octokit/graphql": "^8.2.2", "@octokit/graphql": "^8.2.2",
"@octokit/rest": "^21.1.1", "@octokit/rest": "^21.1.1",
@@ -37,7 +37,7 @@
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="], "@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.6", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-lwswHo6z/Kh9djafk2ajPju62+VqHwJ23gueG1alfaLNK4GRYHgCROfiX6/wlxAd8sRvgTo6ry1hNzkyz7bOpw=="], "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.9", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-b4JD6ZKCZeVDqpWBnb+zJISWi3HzlweNlV7Oy/uo5G2XAfUV2M5AJ/tomKZCvZsvmr1fYbmmfyde3GL2h0pksA=="],
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],

View File

@@ -116,6 +116,7 @@ The `additional_permissions` input allows Claude to access GitHub Actions workfl
To allow Claude to view workflow run results, job logs, and CI status: To allow Claude to view workflow run results, job logs, and CI status:
1. **Grant the necessary permission to your GitHub token**: 1. **Grant the necessary permission to your GitHub token**:
- When using the default `GITHUB_TOKEN`, add the `actions: read` permission to your workflow: - When using the default `GITHUB_TOKEN`, add the `actions: read` permission to your workflow:
```yaml ```yaml

View File

@@ -228,10 +228,12 @@ jobs:
The action now automatically detects the appropriate mode: The action now automatically detects the appropriate mode:
1. **If `prompt` is provided** → Runs in **automation mode** 1. **If `prompt` is provided** → Runs in **automation mode**
- Executes immediately without waiting for @claude mentions - Executes immediately without waiting for @claude mentions
- Perfect for scheduled tasks, PR automation, etc. - Perfect for scheduled tasks, PR automation, etc.
2. **If no `prompt` but @claude is mentioned** → Runs in **interactive mode** 2. **If no `prompt` but @claude is mentioned** → Runs in **interactive mode**
- Waits for and responds to @claude mentions - Waits for and responds to @claude mentions
- Creates tracking comments with progress - Creates tracking comments with progress

View File

@@ -7,13 +7,22 @@
- **⚠️ Non-Write User Access (RISKY)**: The `allowed_non_write_users` parameter allows bypassing the write permission requirement. **This is a significant security risk and should only be used for workflows with extremely limited permissions** (e.g., issue labeling workflows that only have `issues: write` permission). This feature: - **⚠️ Non-Write User Access (RISKY)**: The `allowed_non_write_users` parameter allows bypassing the write permission requirement. **This is a significant security risk and should only be used for workflows with extremely limited permissions** (e.g., issue labeling workflows that only have `issues: write` permission). This feature:
- Only works when `github_token` is provided as input (not with GitHub App authentication) - Only works when `github_token` is provided as input (not with GitHub App authentication)
- Accepts either a comma-separated list of specific usernames or `*` to allow all users - Accepts either a comma-separated list of specific usernames or `*` to allow all users
- **When using the wildcard (`*`)**, you MUST also set `bypass_write_permission_check_acknowledgment: true` to explicitly acknowledge the security implications. Without this flag, the action will fail as a safeguard against accidental security misconfigurations
- **Should be used with extreme caution** as it bypasses the primary security mechanism of this action - **Should be used with extreme caution** as it bypasses the primary security mechanism of this action
- Is designed for automation workflows where user permissions are already restricted by the workflow's permission scope - Is designed for automation workflows where user permissions are already restricted by the workflow's permission scope
- **Token Permissions**: The GitHub app receives only a short-lived token scoped specifically to the repository it's operating in - **Token Permissions**: The GitHub app receives only a short-lived token scoped specifically to the repository it's operating in
- **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered - **No Cross-Repository Access**: Each action invocation is limited to the repository where it was triggered
- **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions - **Limited Scope**: The token cannot access other repositories or perform actions beyond the configured permissions
## Pull Request Creation
In its default configuration, **Claude does not create pull requests automatically** when responding to `@claude` mentions. Instead:
- Claude commits code changes to a new branch
- Claude provides a **link to the GitHub PR creation page** in its response
- **The user must click the link and create the PR themselves**, ensuring human oversight before any code is proposed for merging
This design ensures that users retain full control over what pull requests are created and can review the changes before initiating the PR workflow.
## ⚠️ Prompt Injection Risks ## ⚠️ Prompt Injection Risks
**Beware of potential hidden markdown when tagging Claude on untrusted content.** External contributors may include hidden instructions through HTML comments, invisible characters, hidden attributes, or other techniques. The action sanitizes content by stripping HTML comments, invisible characters, markdown image alt text, hidden HTML attributes, and HTML entities, but new bypass techniques may emerge. We recommend reviewing the raw content of all input coming from external contributors before allowing Claude to process it. **Beware of potential hidden markdown when tagging Claude on untrusted content.** External contributors may include hidden instructions through HTML comments, invisible characters, hidden attributes, or other techniques. The action sanitizes content by stripping HTML comments, invisible characters, markdown image alt text, hidden HTML attributes, and HTML entities, but new bypass techniques may emerge. We recommend reviewing the raw content of all input coming from external contributors before allowing Claude to process it.
@@ -76,12 +85,14 @@ Commits will show as verified and attributed to the GitHub account that owns the
``` ```
2. Add the **public key** to your GitHub account: 2. Add the **public key** to your GitHub account:
- Go to GitHub → Settings → SSH and GPG keys - Go to GitHub → Settings → SSH and GPG keys
- Click "New SSH key" - Click "New SSH key"
- Select **Key type: Signing Key** (important) - Select **Key type: Signing Key** (important)
- Paste the contents of `~/.ssh/signing_key.pub` - Paste the contents of `~/.ssh/signing_key.pub`
3. Add the **private key** to your repository secrets: 3. Add the **private key** to your repository secrets:
- Go to your repo → Settings → Secrets and variables → Actions - Go to your repo → Settings → Secrets and variables → Actions
- Create a new secret named `SSH_SIGNING_KEY` - Create a new secret named `SSH_SIGNING_KEY`
- Paste the contents of `~/.ssh/signing_key` - Paste the contents of `~/.ssh/signing_key`

View File

@@ -31,23 +31,27 @@ The fastest way to create a custom GitHub App is using our pre-configured manife
**🚀 [Download the Quick Setup Tool](./create-app.html)** (Right-click → "Save Link As" or "Download Linked File") **🚀 [Download the Quick Setup Tool](./create-app.html)** (Right-click → "Save Link As" or "Download Linked File")
After downloading, open `create-app.html` in your web browser: After downloading, open `create-app.html` in your web browser:
- **For Personal Accounts:** Click the "Create App for Personal Account" button - **For Personal Accounts:** Click the "Create App for Personal Account" button
- **For Organizations:** Enter your organization name and click "Create App for Organization" - **For Organizations:** Enter your organization name and click "Create App for Organization"
The tool will automatically configure all required permissions and submit the manifest. The tool will automatically configure all required permissions and submit the manifest.
Alternatively, you can use the manifest file directly: Alternatively, you can use the manifest file directly:
- Use the [`github-app-manifest.json`](../github-app-manifest.json) file from this repository - Use the [`github-app-manifest.json`](../github-app-manifest.json) file from this repository
- Visit https://github.com/settings/apps/new (for personal) or your organization's app settings - Visit https://github.com/settings/apps/new (for personal) or your organization's app settings
- Look for the "Create from manifest" option and paste the JSON content - Look for the "Create from manifest" option and paste the JSON content
2. **Complete the creation flow:** 2. **Complete the creation flow:**
- GitHub will show you a preview of the app configuration - GitHub will show you a preview of the app configuration
- Confirm the app name (you can customize it) - Confirm the app name (you can customize it)
- Click "Create GitHub App" - Click "Create GitHub App"
- The app will be created with all required permissions automatically configured - The app will be created with all required permissions automatically configured
3. **Generate and download a private key:** 3. **Generate and download a private key:**
- After creating the app, you'll be redirected to the app settings - After creating the app, you'll be redirected to the app settings
- Scroll down to "Private keys" - Scroll down to "Private keys"
- Click "Generate a private key" - Click "Generate a private key"
@@ -60,6 +64,7 @@ The fastest way to create a custom GitHub App is using our pre-configured manife
If you prefer to configure the app manually or need custom permissions: If you prefer to configure the app manually or need custom permissions:
1. **Create a new GitHub App:** 1. **Create a new GitHub App:**
- Go to https://github.com/settings/apps (for personal apps) or your organization's settings - Go to https://github.com/settings/apps (for personal apps) or your organization's settings
- Click "New GitHub App" - Click "New GitHub App"
- Configure the app with these minimum permissions: - Configure the app with these minimum permissions:
@@ -72,16 +77,19 @@ If you prefer to configure the app manually or need custom permissions:
- Create the app - Create the app
2. **Generate and download a private key:** 2. **Generate and download a private key:**
- After creating the app, scroll down to "Private keys" - After creating the app, scroll down to "Private keys"
- Click "Generate a private key" - Click "Generate a private key"
- Download the `.pem` file (keep this secure!) - Download the `.pem` file (keep this secure!)
3. **Install the app on your repository:** 3. **Install the app on your repository:**
- Go to the app's settings page - Go to the app's settings page
- Click "Install App" - Click "Install App"
- Select the repositories where you want to use Claude - Select the repositories where you want to use Claude
4. **Add the app credentials to your repository secrets:** 4. **Add the app credentials to your repository secrets:**
- Go to your repository's Settings → Secrets and variables → Actions - Go to your repository's Settings → Secrets and variables → Actions
- Add these secrets: - Add these secrets:
- `APP_ID`: Your GitHub App's ID (found in the app settings) - `APP_ID`: Your GitHub App's ID (found in the app settings)
@@ -130,6 +138,7 @@ For more information on creating GitHub Apps, see the [GitHub documentation](htt
To securely use your Anthropic API key: To securely use your Anthropic API key:
1. Add your API key as a repository secret: 1. Add your API key as a repository secret:
- Go to your repository's Settings - Go to your repository's Settings
- Navigate to "Secrets and variables" → "Actions" - Navigate to "Secrets and variables" → "Actions"
- Click "New repository secret" - Click "New repository secret"

View File

@@ -52,36 +52,35 @@ jobs:
## Inputs ## Inputs
| Input | Description | Required | Default | | Input | Description | Required | Default |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------- | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------- |
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | | `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | | `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - |
| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | | `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - |
| `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` | | `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` |
| `include_fix_links` | Include 'Fix this' links in PR code review feedback that open Claude Code with context to fix the identified issue | No | `true` | | `include_fix_links` | Include 'Fix this' links in PR code review feedback that open Claude Code with context to fix the identified issue | No | `true` |
| `claude_args` | Additional [arguments to pass directly to Claude CLI](https://docs.claude.com/en/docs/claude-code/cli-reference#cli-flags) (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | | `claude_args` | Additional [arguments to pass directly to Claude CLI](https://docs.claude.com/en/docs/claude-code/cli-reference#cli-flags) (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" |
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | | `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | | `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` |
| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | | `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | | `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` |
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | | `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` |
| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | | `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - |
| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | | `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - |
| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | | `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` |
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | | `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | | `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | | `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
| `use_commit_signing` | Enable commit signing using GitHub's API. Simple but cannot perform complex git operations like rebasing. See [Security](./security.md#commit-signing) | No | `false` | | `use_commit_signing` | Enable commit signing using GitHub's API. Simple but cannot perform complex git operations like rebasing. See [Security](./security.md#commit-signing) | No | `false` |
| `ssh_signing_key` | SSH private key for signing commits. Enables signed commits with full git CLI support (rebasing, etc.). See [Security](./security.md#commit-signing) | No | "" | | `ssh_signing_key` | SSH private key for signing commits. Enables signed commits with full git CLI support (rebasing, etc.). See [Security](./security.md#commit-signing) | No | "" |
| `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID). Required with `ssh_signing_key` for verified commits | No | `41898282` | | `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID). Required with `ssh_signing_key` for verified commits | No | `41898282` |
| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name). Required with `ssh_signing_key` for verified commits | No | `claude[bot]` | | `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name). Required with `ssh_signing_key` for verified commits | No | `claude[bot]` |
| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | | `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" |
| `allowed_non_write_users` | **⚠️ RISKY**: Comma-separated list of usernames to allow without write permissions, or '\*' for all users. Only works with `github_token` input. See [Security](./security.md) | No | "" | | `allowed_non_write_users` | **⚠️ RISKY**: Comma-separated list of usernames to allow without write permissions, or '\*' for all users. Only works with `github_token` input. See [Security](./security.md) | No | "" |
| `bypass_write_permission_check_acknowledgment` | **REQUIRED** when using `allowed_non_write_users='*'`. Set to `true` to explicitly acknowledge security implications. Prevents accidental security misconfigurations | No | `false` | | `path_to_claude_code_executable` | Optional path to a custom Claude Code executable. Skips automatic installation. Useful for Nix, custom containers, or specialized environments | No | "" |
| `path_to_claude_code_executable` | Optional path to a custom Claude Code executable. Skips automatic installation. Useful for Nix, custom containers, or specialized environments | No | "" | | `path_to_bun_executable` | Optional path to a custom Bun executable. Skips automatic Bun installation. Useful for Nix, custom containers, or specialized environments | No | "" |
| `path_to_bun_executable` | Optional path to a custom Bun executable. Skips automatic Bun installation. Useful for Nix, custom containers, or specialized environments | No | "" | | `plugin_marketplaces` | Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., see example in workflow above). Marketplaces are added before plugin installation | No | "" |
| `plugin_marketplaces` | Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., see example in workflow above). Marketplaces are added before plugin installation | No | "" | | `plugins` | Newline-separated list of Claude Code plugin names to install (e.g., see example in workflow above). Plugins are installed before Claude Code execution | No | "" |
| `plugins` | Newline-separated list of Claude Code plugin names to install (e.g., see example in workflow above). Plugins are installed before Claude Code execution | No | "" |
### Deprecated Inputs ### Deprecated Inputs

View File

@@ -26,5 +26,4 @@ jobs:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues
bypass_write_permission_check_acknowledgment: true # Required when using wildcard
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -12,7 +12,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/github": "^6.0.1", "@actions/github": "^6.0.1",
"@anthropic-ai/claude-agent-sdk": "^0.2.6", "@anthropic-ai/claude-agent-sdk": "^0.2.9",
"@modelcontextprotocol/sdk": "^1.11.0", "@modelcontextprotocol/sdk": "^1.11.0",
"@octokit/graphql": "^8.2.2", "@octokit/graphql": "^8.2.2",
"@octokit/rest": "^21.1.1", "@octokit/rest": "^21.1.1",

View File

@@ -37,7 +37,6 @@ async function run() {
context, context,
context.inputs.allowedNonWriteUsers, context.inputs.allowedNonWriteUsers,
githubTokenProvided, githubTokenProvided,
context.inputs.bypassWritePermissionCheckAcknowledgment,
); );
if (!hasWritePermissions) { if (!hasWritePermissions) {
throw new Error( throw new Error(

View File

@@ -96,7 +96,6 @@ type BaseContext = {
botName: string; botName: string;
allowedBots: string; allowedBots: string;
allowedNonWriteUsers: string; allowedNonWriteUsers: string;
bypassWritePermissionCheckAcknowledgment: boolean;
trackProgress: boolean; trackProgress: boolean;
includeFixLinks: boolean; includeFixLinks: boolean;
}; };
@@ -155,8 +154,6 @@ export function parseGitHubContext(): GitHubContext {
botName: process.env.BOT_NAME ?? CLAUDE_BOT_LOGIN, botName: process.env.BOT_NAME ?? CLAUDE_BOT_LOGIN,
allowedBots: process.env.ALLOWED_BOTS ?? "", allowedBots: process.env.ALLOWED_BOTS ?? "",
allowedNonWriteUsers: process.env.ALLOWED_NON_WRITE_USERS ?? "", allowedNonWriteUsers: process.env.ALLOWED_NON_WRITE_USERS ?? "",
bypassWritePermissionCheckAcknowledgment:
process.env.BYPASS_WRITE_PERMISSION_CHECK_ACKNOWLEDGMENT === "true",
trackProgress: process.env.TRACK_PROGRESS === "true", trackProgress: process.env.TRACK_PROGRESS === "true",
includeFixLinks: process.env.INCLUDE_FIX_LINKS === "true", includeFixLinks: process.env.INCLUDE_FIX_LINKS === "true",
}, },

View File

@@ -6,11 +6,11 @@
*/ */
import type { Octokit } from "@octokit/rest"; import type { Octokit } from "@octokit/rest";
import type { ParsedGitHubContext } from "../context"; import type { GitHubContext } from "../context";
export async function checkHumanActor( export async function checkHumanActor(
octokit: Octokit, octokit: Octokit,
githubContext: ParsedGitHubContext, githubContext: GitHubContext,
) { ) {
// Fetch user information from GitHub API // Fetch user information from GitHub API
const { data: userData } = await octokit.users.getByUsername({ const { data: userData } = await octokit.users.getByUsername({

View File

@@ -8,7 +8,6 @@ import type { Octokit } from "@octokit/rest";
* @param context - The GitHub context * @param context - The GitHub context
* @param allowedNonWriteUsers - Comma-separated list of users allowed without write permissions, or '*' for all * @param allowedNonWriteUsers - Comma-separated list of users allowed without write permissions, or '*' for all
* @param githubTokenProvided - Whether github_token was provided as input (not from app) * @param githubTokenProvided - Whether github_token was provided as input (not from app)
* @param bypassAcknowledgment - Explicit acknowledgment required when using wildcard (*)
* @returns true if the actor has write permissions, false otherwise * @returns true if the actor has write permissions, false otherwise
*/ */
export async function checkWritePermissions( export async function checkWritePermissions(
@@ -16,7 +15,6 @@ export async function checkWritePermissions(
context: ParsedGitHubContext, context: ParsedGitHubContext,
allowedNonWriteUsers?: string, allowedNonWriteUsers?: string,
githubTokenProvided?: boolean, githubTokenProvided?: boolean,
bypassAcknowledgment?: boolean,
): Promise<boolean> { ): Promise<boolean> {
const { repository, actor } = context; const { repository, actor } = context;
@@ -27,17 +25,6 @@ export async function checkWritePermissions(
if (allowedNonWriteUsers && githubTokenProvided) { if (allowedNonWriteUsers && githubTokenProvided) {
const allowedUsers = allowedNonWriteUsers.trim(); const allowedUsers = allowedNonWriteUsers.trim();
if (allowedUsers === "*") { if (allowedUsers === "*") {
if (!bypassAcknowledgment) {
core.error(
`❌ SECURITY ERROR: Attempting to bypass write permission checks for all users with allowed_non_write_users='*' without explicit acknowledgment. ` +
`This is a critical security misconfiguration. To proceed, you must set bypass_write_permission_check_acknowledgment='true' ` +
`to explicitly acknowledge the security implications.`,
);
throw new Error(
"Cannot bypass write permission checks with wildcard (*) without explicit acknowledgment. " +
"Set bypass_write_permission_check_acknowledgment='true' to acknowledge security implications.",
);
}
core.warning( core.warning(
`⚠️ SECURITY WARNING: Bypassing write permission check for ${actor} due to allowed_non_write_users='*'. This should only be used for workflows with very limited permissions.`, `⚠️ SECURITY WARNING: Bypassing write permission check for ${actor} due to allowed_non_write_users='*'. This should only be used for workflows with very limited permissions.`,
); );

View File

@@ -8,6 +8,7 @@ import {
configureGitAuth, configureGitAuth,
setupSshSigning, setupSshSigning,
} from "../../github/operations/git-config"; } from "../../github/operations/git-config";
import { checkHumanActor } from "../../github/validation/actor";
import type { GitHubContext } from "../../github/context"; import type { GitHubContext } from "../../github/context";
import { isEntityContext } from "../../github/context"; import { isEntityContext } from "../../github/context";
@@ -80,7 +81,14 @@ export const agentMode: Mode = {
return false; return false;
}, },
async prepare({ context, githubToken }: ModeOptions): Promise<ModeResult> { async prepare({
context,
octokit,
githubToken,
}: ModeOptions): Promise<ModeResult> {
// Check if actor is human (prevents bot-triggered loops)
await checkHumanActor(octokit.rest, context);
// Configure git authentication for agent mode (same as tag mode) // Configure git authentication for agent mode (same as tag mode)
// SSH signing takes precedence if provided // SSH signing takes precedence if provided
const useSshSigning = !!context.inputs.sshSigningKey; const useSshSigning = !!context.inputs.sshSigningKey;

View File

@@ -145,12 +145,12 @@ describe("Agent Mode", () => {
users: { users: {
getAuthenticated: mock(() => getAuthenticated: mock(() =>
Promise.resolve({ Promise.resolve({
data: { login: "test-user", id: 12345 }, data: { login: "test-user", id: 12345, type: "User" },
}), }),
), ),
getByUsername: mock(() => getByUsername: mock(() =>
Promise.resolve({ Promise.resolve({
data: { login: "test-user", id: 12345 }, data: { login: "test-user", id: 12345, type: "User" },
}), }),
), ),
}, },
@@ -187,6 +187,65 @@ describe("Agent Mode", () => {
process.env.GITHUB_REF_NAME = originalRefName; process.env.GITHUB_REF_NAME = originalRefName;
}); });
test("prepare method rejects bot actors without allowed_bots", async () => {
const contextWithPrompts = createMockAutomationContext({
eventName: "workflow_dispatch",
});
contextWithPrompts.actor = "claude[bot]";
contextWithPrompts.inputs.allowedBots = "";
const mockOctokit = {
rest: {
users: {
getByUsername: mock(() =>
Promise.resolve({
data: { login: "claude[bot]", id: 12345, type: "Bot" },
}),
),
},
},
} as any;
await expect(
agentMode.prepare({
context: contextWithPrompts,
octokit: mockOctokit,
githubToken: "test-token",
}),
).rejects.toThrow(
"Workflow initiated by non-human actor: claude (type: Bot)",
);
});
test("prepare method allows bot actors when in allowed_bots list", async () => {
const contextWithPrompts = createMockAutomationContext({
eventName: "workflow_dispatch",
});
contextWithPrompts.actor = "dependabot[bot]";
contextWithPrompts.inputs.allowedBots = "dependabot";
const mockOctokit = {
rest: {
users: {
getByUsername: mock(() =>
Promise.resolve({
data: { login: "dependabot[bot]", id: 12345, type: "Bot" },
}),
),
},
},
} as any;
// Should not throw - bot is in allowed list
await expect(
agentMode.prepare({
context: contextWithPrompts,
octokit: mockOctokit,
githubToken: "test-token",
}),
).resolves.toBeDefined();
});
test("prepare method creates prompt file with correct content", async () => { test("prepare method creates prompt file with correct content", async () => {
const contextWithPrompts = createMockAutomationContext({ const contextWithPrompts = createMockAutomationContext({
eventName: "workflow_dispatch", eventName: "workflow_dispatch",
@@ -199,12 +258,12 @@ describe("Agent Mode", () => {
users: { users: {
getAuthenticated: mock(() => getAuthenticated: mock(() =>
Promise.resolve({ Promise.resolve({
data: { login: "test-user", id: 12345 }, data: { login: "test-user", id: 12345, type: "User" },
}), }),
), ),
getByUsername: mock(() => getByUsername: mock(() =>
Promise.resolve({ Promise.resolve({
data: { login: "test-user", id: 12345 }, data: { login: "test-user", id: 12345, type: "User" },
}), }),
), ),
}, },

View File

@@ -73,7 +73,6 @@ describe("checkWritePermissions", () => {
botName: CLAUDE_BOT_LOGIN, botName: CLAUDE_BOT_LOGIN,
allowedBots: "", allowedBots: "",
allowedNonWriteUsers: "", allowedNonWriteUsers: "",
bypassWritePermissionCheckAcknowledgment: false,
trackProgress: false, trackProgress: false,
includeFixLinks: true, includeFixLinks: true,
}, },
@@ -198,7 +197,7 @@ describe("checkWritePermissions", () => {
); );
}); });
test("should bypass permission check for all users with wildcard when acknowledgment provided", async () => { test("should bypass permission check for all users with wildcard", async () => {
const mockOctokit = createMockOctokit("read"); const mockOctokit = createMockOctokit("read");
const context = createContext(); const context = createContext();
@@ -207,7 +206,6 @@ describe("checkWritePermissions", () => {
context, context,
"*", "*",
true, true,
true, // acknowledgment provided
); );
expect(result).toBe(true); expect(result).toBe(true);
@@ -216,17 +214,6 @@ describe("checkWritePermissions", () => {
); );
}); });
test("should FAIL to bypass permission check with wildcard when acknowledgment NOT provided", async () => {
const mockOctokit = createMockOctokit("read");
const context = createContext();
await expect(
checkWritePermissions(mockOctokit, context, "*", true, false),
).rejects.toThrow(
"Cannot bypass write permission checks with wildcard (*) without explicit acknowledgment",
);
});
test("should NOT bypass permission check when user not in allowed list", async () => { test("should NOT bypass permission check when user not in allowed list", async () => {
const mockOctokit = createMockOctokit("read"); const mockOctokit = createMockOctokit("read");
const context = createContext(); const context = createContext();