Permission rules don't evaluate pipe segments independently — piped commands bypass allow rules

Resolved 💬 2 comments Opened Jun 22, 2026 by nemoDreamer Closed Jun 26, 2026

Summary

When a Bash command contains pipes (|), semicolons (;), &&, or ||, the full command string is matched as a unit against permission rules. This means a command like grep pattern file | head -20 does not match Bash(grep *) in the allow list, even though the command starts with grep.

Expected behaviour

Each segment of a piped/compound command should be evaluated independently against the allow/ask/deny rules. For example:

  • grep pattern file | head -20
  • Segment 1: grep pattern file → matches Bash(grep *) → ✅ allow
  • Segment 2: head -20 → matches Bash(head *) → ✅ allow
  • Result: auto-approved, no prompt
  • grep pattern file | rm -rf /
  • Segment 1: grep pattern file → matches Bash(grep *) → ✅ allow
  • Segment 2: rm -rf / → no allow rule → ⚠️ prompt
  • Result: user is prompted

Current behaviour

The entire command string is matched as a unit. grep pattern file | head -20 does not match Bash(grep *) because * does not match the | character, so the user is prompted even though both segments are individually covered by allow rules.

Why the obvious workaround is a security hole

Adding Bash(grep * | *) to the allow list would fix the prompt for grep ... | head ..., but it would also silently allow grep foo | rm -rf / — the second segment is completely unchecked. So per-segment evaluation is the correct fix, not broader wildcard patterns.

Steps to reproduce

  1. Add Bash(grep *) and Bash(head *) to the allow list in ~/.claude/settings.json
  2. Have Claude run a command like grep -r "pattern" . | head -20
  3. Observe: permission prompt appears despite both commands being individually covered

Environment

  • Claude Code version: 2.1.175
  • Running via: VSCode extension
  • OS: macOS

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗