claude-code - 💡(How to fix) Fix Deny rules with root-level /** patterns silently match nothing [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#52123Fetched 2026-04-23 07:36:02
View on GitHub
Comments
2
Participants
2
Timeline
7
Reactions
1
Timeline (top)
labeled ×4commented ×2subscribed ×1

Root Cause

In matchingRuleForInput (src/utils/permissions/filesystem.ts:980-984), patterns ending with /** have that suffix stripped before being passed to the ignore library:

if (adjustedPattern.endsWith('/**')) {
    adjustedPattern = adjustedPattern.slice(0, -3)
}

This optimization works for patterns like foo/**foo (the ignore library treats a directory name as matching the directory and its contents). But for root-level patterns:

  • ~/**patternWithRoot produces relativePattern = "/**", root = homedir
  • /** stripped of /**"" (empty string)
  • The ignore library with an empty pattern matches nothing

Same issue for // prefix: //** → root /, relativePattern /** → stripped to "".

Fix Action

Fix / Workaround

Workaround: use Read(//home/username/**) with the full absolute path (double-slash prefix). This produces a non-empty relative pattern after stripping.

Code Example

if (adjustedPattern.endsWith('/**')) {
    adjustedPattern = adjustedPattern.slice(0, -3)
}
RAW_BUFFERClick to expand / collapse

Bug

Permission deny rules where the relative pattern is only /** (after root resolution) silently fail to match any path. This affects patterns like:

  • Read(~/**)
  • Read(//**)
  • Edit(~/**)

These rules appear to deny all reads/edits under ~/ or /, but actually deny nothing.

Root cause

In matchingRuleForInput (src/utils/permissions/filesystem.ts:980-984), patterns ending with /** have that suffix stripped before being passed to the ignore library:

if (adjustedPattern.endsWith('/**')) {
    adjustedPattern = adjustedPattern.slice(0, -3)
}

This optimization works for patterns like foo/**foo (the ignore library treats a directory name as matching the directory and its contents). But for root-level patterns:

  • ~/**patternWithRoot produces relativePattern = "/**", root = homedir
  • /** stripped of /**"" (empty string)
  • The ignore library with an empty pattern matches nothing

Same issue for // prefix: //** → root /, relativePattern /** → stripped to "".

Impact

Users who set "deny": ["Read(~/**)"] expecting to block all reads under their home directory get no protection. The rule is silently accepted and silently ignored. This is a security footgun — users configure deny rules expecting enforcement, get none.

Reproduction

  1. Add "deny": ["Read(~/**)"] to ~/.claude/settings.json
  2. Use the Read tool to read any file under ~/
  3. Expected: hard deny
  4. Actual: read succeeds without prompting

Workaround: use Read(//home/username/**) with the full absolute path (double-slash prefix). This produces a non-empty relative pattern after stripping.

Suggested fix

Handle the degenerate case where stripping /** produces an empty string. For example, use ** as the pattern instead of "", or skip the stripping when the pattern is exactly /**.

extent analysis

TL;DR

Handle the degenerate case where stripping /** produces an empty string by using ** as the pattern instead of "" or skipping the stripping when the pattern is exactly /**.

Guidance

  • Identify patterns that end with /** and adjust the stripping logic to handle the root-level cases correctly.
  • Consider using ** as the pattern instead of "" when stripping /** results in an empty string.
  • Review the ignore library's behavior with empty patterns to ensure it aligns with the expected security rules.
  • Test the fix with the provided reproduction steps to verify the correct behavior.

Example

if (adjustedPattern.endsWith('/**')) {
    adjustedPattern = adjustedPattern.slice(0, -3) || '**'; // use '**' if stripping results in an empty string
}

Notes

The suggested fix assumes that using ** as the pattern instead of "" will correctly match all files and directories. However, this may have unintended consequences and should be thoroughly tested.

Recommendation

Apply the suggested fix to handle the degenerate case where stripping /** produces an empty string, as it directly addresses the root cause of the issue and provides a clear solution.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix Deny rules with root-level /** patterns silently match nothing [2 comments, 2 participants]