Compare commits

..

1 Commits

Author SHA1 Message Date
Claude
ff0fdc0676 fix(security): Resolve symlinks before checking deny rules (CVE-2025-59829)
This commit fixes a security vulnerability where deny rules could be
bypassed by creating symbolic links to restricted files.

Changes:
- Add symlink resolution in rule_engine.py _extract_field method
- Add symlink resolution in security_reminder_hook.py check_patterns
- Create new symlink_deny_hook.py for blocking symlinks to system paths
- Include Read tool in file event handlers for deny rule checking
- Update hooks.json to apply security hooks to Read tool

The vulnerability allowed attackers to bypass deny rules like
Read(/etc/passwd) by creating a symlink (e.g., ln -s /etc/passwd test.txt)
and then reading the symlink instead of the restricted file directly.

The fix uses os.path.realpath() to resolve all symlinks to their canonical
paths before checking against deny patterns, ensuring that deny rules are
enforced regardless of whether the path is accessed directly or via symlink.
2026-01-08 20:08:08 +00:00
12 changed files with 411 additions and 328 deletions

View File

@@ -17,22 +17,19 @@ jobs:
permissions:
contents: read
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run Claude Code slash command
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: anthropics/claude-code-base-action@beta
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--model claude-sonnet-4-5-20250929"
claude_env: |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Log duplicate comment event to Statsig
if: always()

View File

@@ -11,12 +11,65 @@ jobs:
permissions:
contents: read
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create triage prompt
run: |
mkdir -p /tmp/claude-prompts
cat > /tmp/claude-prompts/triage-prompt.txt << 'EOF'
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
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.
2. Next, use the GitHub tools to get context about the issue:
- You have access to these tools:
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
- Start by using mcp__github__get_issue to get the issue details
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
- Consider platform labels (android, ios) if applicable
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
5. Apply the selected labels:
- Use mcp__github__update_issue to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list above
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using mcp__github__update_issue
- It's okay to not add any labels if none are clearly applicable
EOF
- name: Setup GitHub MCP Server
run: |
mkdir -p /tmp/mcp-config
@@ -42,64 +95,13 @@ jobs:
EOF
- name: Run Claude Code for Issue Triage
timeout-minutes: 5
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: anthropics/claude-code-base-action@beta
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
prompt: |
You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list.
IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels.
Issue Information:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
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.
2. Next, use the GitHub tools to get context about the issue:
- You have access to these tools:
- mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels
- mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments
- mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting)
- mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues
- mcp__github__list_issues: Use this to understand patterns in how other issues are labeled
- Start by using mcp__github__get_issue to get the issue details
3. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
4. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
- Consider platform labels (android, ios) if applicable
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
5. Apply the selected labels:
- Use mcp__github__update_issue to apply your selected labels
- DO NOT post any comments explaining your decision
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
IMPORTANT GUIDELINES:
- Be thorough in your analysis
- Only select labels from the provided list above
- DO NOT post any comments to the issue
- Your ONLY action should be to apply labels using mcp__github__update_issue
- It's okay to not add any labels if none are clearly applicable
prompt_file: /tmp/claude-prompts/triage-prompt.txt
allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
timeout_minutes: "5"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--model claude-sonnet-4-5-20250929
--mcp-config /tmp/mcp-config/mcp-servers.json
--allowedTools "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues"
mcp_config: /tmp/mcp-config/mcp-servers.json
claude_args: "--model claude-sonnet-4-5-20250929"
claude_env: |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -31,7 +31,7 @@ jobs:
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@beta
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--model claude-sonnet-4-5-20250929"

View File

@@ -16,12 +16,74 @@ jobs:
permissions:
contents: read
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create oncall triage prompt
run: |
mkdir -p /tmp/claude-prompts
cat > /tmp/claude-prompts/oncall-triage-prompt.txt << 'EOF'
You're an oncall triage assistant for GitHub issues. Your task is to identify critical issues that require immediate oncall attention.
Important: Don't post any comments or messages to the issues. Your only action should be to apply the "oncall" label to qualifying issues.
Repository: ${{ github.repository }}
Task overview:
1. Fetch all open issues updated in the last 3 days:
- Use mcp__github__list_issues with:
- state="open"
- first=5 (fetch only 5 issues per page)
- orderBy="UPDATED_AT"
- direction="DESC"
- This will give you the most recently updated issues first
- For each page of results, check the updatedAt timestamp of each issue
- Add issues updated within the last 3 days (72 hours) to your TODO list as you go
- Keep paginating using the 'after' parameter until you encounter issues older than 3 days
- Once you hit issues older than 3 days, you can stop fetching (no need to fetch all open issues)
2. Build your TODO list incrementally as you fetch:
- As you fetch each page, immediately add qualifying issues to your TODO list
- One TODO item per issue number (e.g., "Evaluate issue #123")
- This allows you to start processing while still fetching more pages
3. For each issue in your TODO list:
- Use mcp__github__get_issue to read the issue details (title, body, labels)
- Use mcp__github__get_issue_comments to read all comments
- Evaluate whether this issue needs the oncall label:
a) Is it a bug? (has "bug" label or describes bug behavior)
b) Does it have at least 50 engagements? (count comments + reactions)
c) Is it truly blocking? Read and understand the full content to determine:
- Does this prevent core functionality from working?
- Can users work around it?
- Consider severity indicators: "crash", "stuck", "frozen", "hang", "unresponsive", "cannot use", "blocked", "broken"
- Be conservative - only flag issues that truly prevent users from getting work done
4. For issues that meet all criteria and do not already have the "oncall" label:
- Use mcp__github__update_issue to add the "oncall" label
- Do not post any comments
- Do not remove any existing labels
- Do not remove the "oncall" label from issues that already have it
Important guidelines:
- Use the TODO list to track your progress through ALL candidate issues
- Process issues efficiently - don't read every single issue upfront, work through your TODO list systematically
- Be conservative in your assessment - only flag truly critical blocking issues
- Do not post any comments to issues
- Your only action should be to add the "oncall" label using mcp__github__update_issue
- Mark each issue as complete in your TODO list as you process it
7. After processing all issues in your TODO list, provide a summary of your actions:
- Total number of issues processed (candidate issues evaluated)
- Number of issues that received the "oncall" label
- For each issue that got the label: list issue number, title, and brief reason why it qualified
- Close calls: List any issues that almost qualified but didn't quite meet the criteria (e.g., borderline blocking, had workarounds)
- If no issues qualified, state that clearly
- Format the summary clearly for easy reading
EOF
- name: Setup GitHub MCP Server
run: |
mkdir -p /tmp/mcp-config
@@ -47,72 +109,12 @@ jobs:
EOF
- name: Run Claude Code for Oncall Triage
timeout-minutes: 10
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: anthropics/claude-code-base-action@beta
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
prompt: |
You're an oncall triage assistant for GitHub issues. Your task is to identify critical issues that require immediate oncall attention.
Important: Don't post any comments or messages to the issues. Your only action should be to apply the "oncall" label to qualifying issues.
Repository: ${{ github.repository }}
Task overview:
1. Fetch all open issues updated in the last 3 days:
- Use mcp__github__list_issues with:
- state="open"
- first=5 (fetch only 5 issues per page)
- orderBy="UPDATED_AT"
- direction="DESC"
- This will give you the most recently updated issues first
- For each page of results, check the updatedAt timestamp of each issue
- Add issues updated within the last 3 days (72 hours) to your TODO list as you go
- Keep paginating using the 'after' parameter until you encounter issues older than 3 days
- Once you hit issues older than 3 days, you can stop fetching (no need to fetch all open issues)
2. Build your TODO list incrementally as you fetch:
- As you fetch each page, immediately add qualifying issues to your TODO list
- One TODO item per issue number (e.g., "Evaluate issue #123")
- This allows you to start processing while still fetching more pages
3. For each issue in your TODO list:
- Use mcp__github__get_issue to read the issue details (title, body, labels)
- Use mcp__github__get_issue_comments to read all comments
- Evaluate whether this issue needs the oncall label:
a) Is it a bug? (has "bug" label or describes bug behavior)
b) Does it have at least 50 engagements? (count comments + reactions)
c) Is it truly blocking? Read and understand the full content to determine:
- Does this prevent core functionality from working?
- Can users work around it?
- Consider severity indicators: "crash", "stuck", "frozen", "hang", "unresponsive", "cannot use", "blocked", "broken"
- Be conservative - only flag issues that truly prevent users from getting work done
4. For issues that meet all criteria and do not already have the "oncall" label:
- Use mcp__github__update_issue to add the "oncall" label
- Do not post any comments
- Do not remove any existing labels
- Do not remove the "oncall" label from issues that already have it
Important guidelines:
- Use the TODO list to track your progress through ALL candidate issues
- Process issues efficiently - don't read every single issue upfront, work through your TODO list systematically
- Be conservative in your assessment - only flag truly critical blocking issues
- Do not post any comments to issues
- Your only action should be to add the "oncall" label using mcp__github__update_issue
- Mark each issue as complete in your TODO list as you process it
7. After processing all issues in your TODO list, provide a summary of your actions:
- Total number of issues processed (candidate issues evaluated)
- Number of issues that received the "oncall" label
- For each issue that got the label: list issue number, title, and brief reason why it qualified
- Close calls: List any issues that almost qualified but didn't quite meet the criteria (e.g., borderline blocking, had workarounds)
- If no issues qualified, state that clearly
- Format the summary clearly for easy reading
prompt_file: /tmp/claude-prompts/oncall-triage-prompt.txt
allowed_tools: "mcp__github__list_issues,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue"
timeout_minutes: "10"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--mcp-config /tmp/mcp-config/mcp-servers.json
--allowedTools "mcp__github__list_issues,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue"
mcp_config: /tmp/mcp-config/mcp-servers.json
claude_env: |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,150 +1,5 @@
# Changelog
## 2.1.14
- Added history-based autocomplete in bash mode (`!`) - type a partial command and press Tab to complete from your bash command history
- Added search to installed plugins list - type to filter by name or description
- Added support for pinning plugins to specific git commit SHAs, allowing marketplace entries to install exact versions
- Fixed a regression where the context window blocking limit was calculated too aggressively, blocking users at ~65% context usage instead of the intended ~98%
- Fixed memory issues that could cause crashes when running parallel subagents
- Fixed memory leak in long-running sessions where stream resources were not cleaned up after shell commands completed
- Fixed `@` symbol incorrectly triggering file autocomplete suggestions in bash mode
- Fixed `@`-mention menu folder click behavior to navigate into directories instead of selecting them
- Fixed `/feedback` command generating invalid GitHub issue URLs when description is very long
- Fixed `/context` command to show the same token count and percentage as the status line in verbose mode
- Fixed an issue where `/config`, `/context`, `/model`, and `/todos` command overlays could close unexpectedly
- Fixed slash command autocomplete selecting wrong command when typing similar commands (e.g., `/context` vs `/compact`)
- Fixed inconsistent back navigation in plugin marketplace when only one marketplace is configured
- Fixed iTerm2 progress bar not clearing properly on exit, preventing lingering indicators and bell sounds
- Improved backspace to delete pasted text as a single token instead of one character at a time
- [VSCode] Added `/usage` command to display current plan usage
## 2.1.12
- Fixed message rendering bug
## 2.1.11
- Fixed excessive MCP connection requests for HTTP/SSE transports
## 2.1.10
- Added new `Setup` hook event that can be triggered via `--init`, `--init-only`, or `--maintenance` CLI flags for repository setup and maintenance operations
- Added keyboard shortcut 'c' to copy OAuth URL when browser doesn't open automatically during login
- Fixed a crash when running bash commands containing heredocs with JavaScript template literals like `${index + 1}`
- Improved startup to capture keystrokes typed before the REPL is fully ready
- Improved file suggestions to show as removable attachments instead of inserting text when accepted
- [VSCode] Added install count display to plugin listings
- [VSCode] Added trust warning when installing plugins
## 2.1.9
- Added `auto:N` syntax for configuring the MCP tool search auto-enable threshold, where N is the context window percentage (0-100)
- Added `plansDirectory` setting to customize where plan files are stored
- Added external editor support (Ctrl+G) in AskUserQuestion "Other" input field
- Added session URL attribution to commits and PRs created from web sessions
- Added support for `PreToolUse` hooks to return `additionalContext` to the model
- Added `${CLAUDE_SESSION_ID}` string substitution for skills to access the current session ID
- Fixed long sessions with parallel tool calls failing with an API error about orphan tool_result blocks
- Fixed MCP server reconnection hanging when cached connection promise never resolves
- Fixed Ctrl+Z suspend not working in terminals using Kitty keyboard protocol (Ghostty, iTerm2, kitty, WezTerm)
## 2.1.7
- Added `showTurnDuration` setting to hide turn duration messages (e.g., "Cooked for 1m 6s")
- Added ability to provide feedback when accepting permission prompts
- Added inline display of agent's final response in task notifications, making it easier to see results without reading the full transcript file
- Fixed security vulnerability where wildcard permission rules could match compound commands containing shell operators
- Fixed false "file modified" errors on Windows when cloud sync tools, antivirus scanners, or Git touch file timestamps without changing content
- Fixed orphaned tool_result errors when sibling tools fail during streaming execution
- Fixed context window blocking limit being calculated using the full context window instead of the effective context window (which reserves space for max output tokens)
- Fixed spinner briefly flashing when running local slash commands like `/model` or `/theme`
- Fixed terminal title animation jitter by using fixed-width braille characters
- Fixed plugins with git submodules not being fully initialized when installed
- Fixed bash commands failing on Windows when temp directory paths contained characters like `t` or `n` that were misinterpreted as escape sequences
- Improved typing responsiveness by reducing memory allocation overhead in terminal rendering
- Enabled MCP tool search auto mode by default for all users. When MCP tool descriptions exceed 10% of the context window, they are automatically deferred and discovered via the MCPSearch tool instead of being loaded upfront. This reduces context usage for users with many MCP tools configured. Users can disable this by adding `MCPSearch` to `disallowedTools` in their settings.
- Changed OAuth and API Console URLs from console.anthropic.com to platform.claude.com
- [VSCode] Fixed `claudeProcessWrapper` setting passing the wrapper path instead of the Claude binary path
## 2.1.6
- Added search functionality to `/config` command for quickly filtering settings
- Added Updates section to `/doctor` showing auto-update channel and available npm versions (stable/latest)
- Added date range filtering to `/stats` command - press `r` to cycle between Last 7 days, Last 30 days, and All time
- Added automatic discovery of skills from nested `.claude/skills` directories when working with files in subdirectories
- Added `context_window.used_percentage` and `context_window.remaining_percentage` fields to status line input for easier context window display
- Added an error display when the editor fails during Ctrl+G
- Fixed permission bypass via shell line continuation that could allow blocked commands to execute
- Fixed false "File has been unexpectedly modified" errors when file watchers touch files without changing content
- Fixed text styling (bold, colors) getting progressively misaligned in multi-line responses
- Fixed the feedback panel closing unexpectedly when typing 'n' in the description field
- Fixed rate limit warning appearing at low usage after weekly reset (now requires 70% usage)
- Fixed rate limit options menu incorrectly auto-opening when resuming a previous session
- Fixed numpad keys outputting escape sequences instead of characters in Kitty keyboard protocol terminals
- Fixed Option+Return not inserting newlines in Kitty keyboard protocol terminals
- Fixed corrupted config backup files accumulating in the home directory (now only one backup is created per config file)
- Fixed `mcp list` and `mcp get` commands leaving orphaned MCP server processes
- Fixed visual artifacts in ink2 mode when nodes become hidden via `display:none`
- Improved the external CLAUDE.md imports approval dialog to show which files are being imported and from where
- Improved the `/tasks` dialog to go directly to task details when there's only one background task running
- Improved @ autocomplete with icons for different suggestion types and single-line formatting
- Updated "Help improve Claude" setting fetch to refresh OAuth and retry when it fails due to a stale OAuth token
- Changed task notification display to cap at 3 lines with overflow summary when multiple background tasks complete simultaneously
- Changed terminal title to "Claude Code" on startup for better window identification
- Removed ability to @-mention MCP servers to enable/disable - use `/mcp enable <name>` instead
- [VSCode] Fixed usage indicator not updating after manual compact
## 2.1.5
- Added `CLAUDE_CODE_TMPDIR` environment variable to override the temp directory used for internal temp files, useful for environments with custom temp directory requirements
## 2.1.4
- Added `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` environment variable to disable all background task functionality including auto-backgrounding and the Ctrl+B shortcut
- Fixed "Help improve Claude" setting fetch to refresh OAuth and retry when it fails due to a stale OAuth token
## 2.1.3
- Merged slash commands and skills, simplifying the mental model with no change in behavior
- Added release channel (`stable` or `latest`) toggle to `/config`
- Added detection and warnings for unreachable permission rules, with warnings in `/doctor` and after saving rules that include the source of each rule and actionable fix guidance
- Fixed plan files persisting across `/clear` commands, now ensuring a fresh plan file is used after clearing a conversation
- Fixed false skill duplicate detection on filesystems with large inodes (e.g., ExFAT) by using 64-bit precision for inode values
- Fixed mismatch between background task count in status bar and items shown in tasks dialog
- Fixed sub-agents using the wrong model during conversation compaction
- Fixed web search in sub-agents using incorrect model
- Fixed trust dialog acceptance when running from the home directory not enabling trust-requiring features like hooks during the session
- Improved terminal rendering stability by preventing uncontrolled writes from corrupting cursor state
- Improved slash command suggestion readability by truncating long descriptions to 2 lines
- Changed tool hook execution timeout from 60 seconds to 10 minutes
- [VSCode] Added clickable destination selector for permission requests, allowing you to choose where settings are saved (this project, all projects, shared with team, or session only)
## 2.1.2
- Added source path metadata to images dragged onto the terminal, helping Claude understand where images originated
- Added clickable hyperlinks for file paths in tool output in terminals that support OSC 8 (like iTerm)
- Added support for Windows Package Manager (winget) installations with automatic detection and update instructions
- Added Shift+Tab keyboard shortcut in plan mode to quickly select "auto-accept edits" option
- Added `FORCE_AUTOUPDATE_PLUGINS` environment variable to allow plugin autoupdate even when the main auto-updater is disabled
- Added `agent_type` to SessionStart hook input, populated if `--agent` is specified
- Fixed a command injection vulnerability in bash command processing where malformed input could execute arbitrary commands
- Fixed a memory leak where tree-sitter parse trees were not being freed, causing WASM memory to grow unbounded over long sessions
- Fixed binary files (images, PDFs, etc.) being accidentally included in memory when using `@include` directives in CLAUDE.md files
- Fixed updates incorrectly claiming another installation is in progress
- Fixed crash when socket files exist in watched directories (defense-in-depth for EOPNOTSUPP errors)
- Fixed remote session URL and teleport being broken when using `/tasks` command
- Fixed MCP tool names being exposed in analytics events by sanitizing user-specific server configurations
- Improved Option-as-Meta hint on macOS to show terminal-specific instructions for native CSIu terminals like iTerm2, Kitty, and WezTerm
- Improved error message when pasting images over SSH to suggest using `scp` instead of the unhelpful clipboard shortcut hint
- Improved permission explainer to not flag routine dev workflows (git fetch/rebase, npm install, tests, PRs) as medium risk
- Changed large bash command outputs to be saved to disk instead of truncated, allowing Claude to read the full content
- Changed large tool outputs to be persisted to disk instead of truncated, providing full output access via file references
- Changed `/plugins` installed tab to unify plugins and MCPs with scope-based grouping
- Deprecated Windows managed settings path `C:\ProgramData\ClaudeCode\managed-settings.json` - administrators should migrate to `C:\Program Files\ClaudeCode\managed-settings.json`
- [SDK] Changed minimum zod peer dependency to ^4.0.0
- [VSCode] Fixed usage display not updating after manual compact
## 2.1.0
- Added automatic skill hot-reload - skills created or modified in `~/.claude/skills` or `.claude/skills` are now immediately available without restarting the session

View File

@@ -11,37 +11,30 @@ Claude Code is an agentic coding tool that lives in your terminal, understands y
<img src="./demo.gif" />
## Get started
> [!NOTE]
> Installation via npm is deprecated. Use one of the recommended methods below.
For more installation options, uninstall steps, and troubleshooting, see the [setup documentation](https://code.claude.com/docs/en/setup).
1. Install Claude Code:
**MacOS/Linux (Recommended):**
```bash
curl -fsSL https://claude.ai/install.sh | bash
```
**MacOS/Linux:**
```bash
curl -fsSL https://claude.ai/install.sh | bash
```
**Homebrew (MacOS/Linux):**
```bash
brew install --cask claude-code
```
**Homebrew (MacOS):**
```bash
brew install --cask claude-code
```
**Windows (Recommended):**
```powershell
irm https://claude.ai/install.ps1 | iex
```
**Windows:**
```powershell
irm https://claude.ai/install.ps1 | iex
```
**WinGet (Windows):**
```powershell
winget install Anthropic.ClaudeCode
```
**NPM:**
```bash
npm install -g @anthropic-ai/claude-code
```
**NPM (Deprecated):**
```bash
npm install -g @anthropic-ai/claude-code
```
NOTE: If installing with NPM, you also need to install [Node.js 18+](https://nodejs.org/en/download/)
2. Navigate to your project directory and run `claude`.

View File

@@ -5,10 +5,6 @@ description: Code review a pull request
Provide a code review for the given pull request.
**Agent assumptions (applies to all agents and subagents):**
- All tools are functional and will work without error. Do not test tools or make exploratory calls. Make sure this is clear to every subagent that is launched.
- Only call a tool if it is required to complete the task. Every tool call should have a clear purpose.
To do this, follow these steps precisely:
1. Launch a haiku agent to check if any of the following are true:
@@ -38,15 +34,15 @@ Note: Still review Claude generated PR's.
Agent 4: Opus bug agent (parallel subagent with agent 3)
Look for problems that exist in the introduced code. This could be security issues, incorrect logic, etc. Only look for issues that fall within the changed code.
**CRITICAL: We only want HIGH SIGNAL issues.** Flag issues where:
- The code will fail to compile or parse (syntax errors, type errors, missing imports, unresolved references)
- The code will definitely produce wrong results regardless of inputs (clear logic errors)
**CRITICAL: We only want HIGH SIGNAL issues.** This means:
- Objective bugs that will cause incorrect behavior at runtime
- Clear, unambiguous CLAUDE.md violations where you can quote the exact rule being broken
Do NOT flag:
- Code style or quality concerns
- Potential issues that depend on specific inputs or state
- Subjective suggestions or improvements
We do NOT want:
- Subjective concerns or "suggestions"
- Style preferences not explicitly required by CLAUDE.md
- Potential issues that "might" be problems
- Anything requiring interpretation or judgment calls
If you are not certain an issue is real, do not flag it. False positives erode trust and waste reviewer time.
@@ -61,13 +57,23 @@ Note: Still review Claude generated PR's.
If NO issues were found, post a summary comment using `gh pr comment` (if `--comment` argument is provided):
"No issues found. Checked for bugs and CLAUDE.md compliance."
8. Create a list of all comments that you plan on leaving. This is only for you to make sure you are comfortable with the comments. Do not post this list anywhere.
8. Post inline comments for each issue using `mcp__github_inline_comment__create_inline_comment`:
- `path`: the file path
- `line` (and `startLine` for ranges): select the buggy lines so the user sees them
- `body`: Brief description of the issue (no "Bug:" prefix). For small fixes (up to 5 lines changed), include a committable suggestion:
```suggestion
corrected code here
```
9. Post inline comments for each issue using `mcp__github_inline_comment__create_inline_comment`. For each comment:
- Provide a brief description of the issue
- For small, self-contained fixes, include a committable suggestion block
- For larger fixes (6+ lines, structural changes, or changes spanning multiple locations), describe the issue and suggested fix without a suggestion block
- Never post a committable suggestion UNLESS committing the suggestion fixes the issue entirely. If follow up steps are required, do not leave a committable suggestion.
**Suggestions must be COMPLETE.** If a fix requires additional changes elsewhere (e.g., renaming a variable requires updating all usages), do NOT use a suggestion block. The author should be able to click "Commit suggestion" and have a working fix - no followup work required.
For larger fixes (6+ lines, structural changes, or changes spanning multiple locations), do NOT use suggestion blocks. Instead:
1. Describe what the issue is
2. Explain the suggested fix at a high level
3. Include a copyable prompt for Claude Code that the user can use to fix the issue, formatted as:
```
Fix [file:line]: [brief description of issue and suggested fix]
```
**IMPORTANT: Only post ONE comment per unique issue. Do not post duplicate comments.**

View File

@@ -141,6 +141,39 @@ class RuleEngine:
patterns = matcher.split('|')
return tool_name in patterns
def _resolve_symlink_path(self, file_path: str) -> str:
"""Resolve symlinks in file path to get canonical path.
Security fix for CVE-2025-59829: Deny rules could be bypassed by creating
a symlink to a restricted file. This method resolves the symlink to its
target path so that deny rules are checked against the actual file.
Args:
file_path: The file path that may contain symlinks
Returns:
The canonical path with symlinks resolved, or original path if
resolution fails (e.g., file doesn't exist yet)
"""
import os
if not file_path:
return file_path
try:
# Expand user home directory first
expanded_path = os.path.expanduser(file_path)
# Use realpath to resolve all symlinks and get canonical path
# This handles nested symlinks and relative path components
resolved = os.path.realpath(expanded_path)
return resolved
except (OSError, ValueError):
# If resolution fails (e.g., permission denied, invalid path),
# return the original path to avoid blocking legitimate operations
return file_path
def _check_condition(self, condition: Condition, tool_name: str,
tool_input: Dict[str, Any], input_data: Dict[str, Any] = None) -> bool:
"""Check if a single condition matches.
@@ -196,6 +229,10 @@ class RuleEngine:
if field in tool_input:
value = tool_input[field]
if isinstance(value, str):
# Security fix: resolve symlinks for file_path fields to prevent bypass
# CVE-2025-59829: Deny rules could be bypassed via symlinks
if field == 'file_path':
value = self._resolve_symlink_path(value)
return value
return str(value)
@@ -241,11 +278,18 @@ class RuleEngine:
elif field == 'old_text' or field == 'old_string':
return tool_input.get('old_string', '')
elif field == 'file_path':
return tool_input.get('file_path', '')
# Security fix: resolve symlinks to prevent deny rule bypass
return self._resolve_symlink_path(tool_input.get('file_path', ''))
elif tool_name == 'Read':
# Security fix for CVE-2025-59829: Read tool symlink bypass
if field == 'file_path':
return self._resolve_symlink_path(tool_input.get('file_path', ''))
elif tool_name == 'MultiEdit':
if field == 'file_path':
return tool_input.get('file_path', '')
# Security fix: resolve symlinks to prevent deny rule bypass
return self._resolve_symlink_path(tool_input.get('file_path', ''))
elif field in ['new_text', 'content']:
# Concatenate all edits
edits = tool_input.get('edits', [])

View File

@@ -45,7 +45,9 @@ def main():
event = None
if tool_name == 'Bash':
event = 'bash'
elif tool_name in ['Edit', 'Write', 'MultiEdit']:
elif tool_name in ['Edit', 'Write', 'MultiEdit', 'Read']:
# Include Read tool in file events to check symlink bypass
# Security fix for CVE-2025-59829
event = 'file'
# Load rules

View File

@@ -1,7 +1,16 @@
{
"description": "Security reminder hook that warns about potential security issues when editing files",
"description": "Security hooks for file access validation and security pattern warnings",
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/symlink_deny_hook.py"
}
],
"matcher": "Edit|Write|MultiEdit|Read"
},
{
"hooks": [
{
@@ -9,7 +18,7 @@
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/security_reminder_hook.py"
}
],
"matcher": "Edit|Write|MultiEdit"
"matcher": "Edit|Write|MultiEdit|Read"
}
]
}

View File

@@ -180,10 +180,46 @@ def save_state(session_id, shown_warnings):
pass # Fail silently if we can't save state
def resolve_symlink_path(file_path):
"""Resolve symlinks in file path to get canonical path.
Security fix for CVE-2025-59829: Deny rules could be bypassed by creating
a symlink to a restricted file. This method resolves the symlink to its
target path so that security patterns are checked against the actual file.
Args:
file_path: The file path that may contain symlinks
Returns:
The canonical path with symlinks resolved, or original path if
resolution fails (e.g., file doesn't exist yet)
"""
if not file_path:
return file_path
try:
# Expand user home directory first
expanded_path = os.path.expanduser(file_path)
# Use realpath to resolve all symlinks and get canonical path
# This handles nested symlinks and relative path components
resolved = os.path.realpath(expanded_path)
return resolved
except (OSError, ValueError):
# If resolution fails (e.g., permission denied, invalid path),
# return the original path to avoid blocking legitimate operations
return file_path
def check_patterns(file_path, content):
"""Check if file path or content matches any security patterns."""
# Security fix: resolve symlinks before checking patterns
# CVE-2025-59829: Security patterns could be bypassed via symlinks
resolved_path = resolve_symlink_path(file_path)
# Normalize path by removing leading slashes
normalized_path = file_path.lstrip("/")
normalized_path = resolved_path.lstrip("/")
for pattern in SECURITY_PATTERNS:
# Check path-based patterns
@@ -241,7 +277,7 @@ def main():
tool_input = input_data.get("tool_input", {})
# Check if this is a relevant tool
if tool_name not in ["Edit", "Write", "MultiEdit"]:
if tool_name not in ["Edit", "Write", "MultiEdit", "Read"]:
sys.exit(0) # Allow non-file tools to proceed
# Extract file path from tool_input

View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""
Symlink Deny Hook for Claude Code
Security fix for CVE-2025-59829: Deny rules could be bypassed via symlinks.
This hook resolves symlinks before checking file paths against deny patterns,
preventing attackers from using symlinks to access restricted files.
"""
import json
import os
import re
import sys
from fnmatch import fnmatch
# System directories that should be blocked by default
# These match common deny rule patterns
BLOCKED_PATHS = [
"/etc/**",
"/etc/passwd",
"/etc/shadow",
"/etc/sudoers",
"/etc/ssh/**",
"/etc/ssl/**",
"/root/**",
"/var/log/**",
"/proc/**",
"/sys/**",
"/boot/**",
]
def resolve_symlink_path(file_path: str) -> str:
"""Resolve symlinks in file path to get canonical path.
Args:
file_path: The file path that may contain symlinks
Returns:
The canonical path with symlinks resolved, or original path if
resolution fails (e.g., file doesn't exist)
"""
if not file_path:
return file_path
try:
# Expand user home directory first
expanded_path = os.path.expanduser(file_path)
# Use realpath to resolve all symlinks and get canonical path
resolved = os.path.realpath(expanded_path)
return resolved
except (OSError, ValueError):
return file_path
def is_path_blocked(resolved_path: str, original_path: str) -> tuple:
"""Check if the resolved path matches any blocked patterns.
Only blocks if:
1. The path was a symlink (resolved != original)
2. The resolved path matches a blocked pattern
Args:
resolved_path: The canonical path after symlink resolution
original_path: The original path before resolution
Returns:
Tuple of (is_blocked: bool, reason: str)
"""
# Only apply symlink protection if path was actually a symlink
original_real = os.path.realpath(os.path.expanduser(original_path))
if original_real == resolved_path:
# Check if original was a symlink
expanded_original = os.path.expanduser(original_path)
if not os.path.islink(expanded_original):
# Not a symlink, allow normal deny rule checking to handle this
return False, ""
# Check if resolved path matches any blocked patterns
for pattern in BLOCKED_PATHS:
if pattern.endswith("/**"):
# Directory wildcard pattern
base_dir = pattern[:-3]
if resolved_path.startswith(base_dir + "/") or resolved_path == base_dir:
return True, f"Symlink bypass blocked: '{original_path}' resolves to '{resolved_path}' which matches blocked pattern '{pattern}'"
elif fnmatch(resolved_path, pattern):
return True, f"Symlink bypass blocked: '{original_path}' resolves to '{resolved_path}' which matches blocked pattern '{pattern}'"
return False, ""
def main():
"""Main hook function."""
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(0) # Allow on parse error
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Only check file-related tools
if tool_name not in ["Read", "Edit", "Write", "MultiEdit"]:
sys.exit(0)
# Extract file path
file_path = tool_input.get("file_path", "")
if not file_path:
sys.exit(0)
# Resolve symlinks
resolved_path = resolve_symlink_path(file_path)
# Check if blocked
is_blocked, reason = is_path_blocked(resolved_path, file_path)
if is_blocked:
# Output denial response
response = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny"
},
"systemMessage": f"Security: {reason}"
}
print(json.dumps(response))
sys.exit(0)
# Allow the operation
sys.exit(0)
if __name__ == "__main__":
main()