Skip to content

[FEATURE]: Extract inner commands from xargs/parallel/find -exec for permission matching #9516

Closed as not planned

Description

@rcdailey

Problem

When a command is wrapped in xargs, find -exec, parallel, watch, or similar command wrappers, the permission system only sees the wrapper command, not the inner command being executed.

For example, with this permission config:

"bash": {
  "*": "allow",
  "gh api* -X DELETE*": "ask"
}

This command bypasses the permission check entirely:

gh api repos/owner/repo/code-scanning/analyses --paginate -q '.[] | .id' | xargs -I{} gh api -X DELETE repos/owner/repo/code-scanning/analyses/{}

The bash tool parses this as two commands via tree-sitter:

  1. gh api repos/... --paginate -q '...' - matches *: allow
  2. xargs -I{} gh api -X DELETE ... - starts with xargs, not gh api, so matches *: allow

The dangerous gh api -X DELETE is never checked because it appears as arguments to xargs, not as a standalone command.

Proposed Solution

The bash tool could recognize common command wrappers and extract their inner commands for permission checking:

  • xargs [-I...] <command> - extract command after flags
  • find ... -exec <command> \; or -exec <command> + - extract command between -exec and terminator
  • parallel <command> - extract command
  • watch [-n...] <command> - extract command after flags
  • env [VAR=val...] <command> - extract command after env vars

When a wrapper is detected, both the wrapper invocation AND the inner command should be checked against permission rules.

Workaround

Users can currently work around this by adding a leading * to patterns:

"*gh api* -X DELETE*": "ask"

This matches the pattern anywhere in the command string, but it's non-obvious and easy to miss.

Activity

  1. github-actions commented on Jan 19, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  2. HeySora commented on Feb 6, 2026

    @HeySora

    I will also add that awk can do system calls too, through something like awk '{system("<command>")}'

  3. aspiers commented on Feb 16, 2026

    @aspiers
    Contributor

    And perl / python / ruby / node ... in fact pretty much any language interpreter will allow that! If your default policy is allow then all bets are off and you will need some other form of protection e.g. kernel-level sandboxing.

  4. Chetic commented on Mar 9, 2026

    @Chetic

    This also affects simple execution wrappers like timeout, time, nice, nohup, ionice, and stdbuf. These don't change what runs, only how — but the permission system treats them as the command:

    # "git log*": "allow" configured, but this still prompts:
    timeout 5 git log --oneline
    
    # Same for:
    nice -n 10 grep -r "TODO" src/
    time git status

    These wrappers have simpler argument patterns than xargs/find -exec, so they'd be a good starting point for implementation.

  5. added a commit that references this issue on Mar 9, 2026
    0783a1b
  6. Chetic commented on Mar 9, 2026

    @Chetic

    I've opened a PR implementing this: #16724

    It adds a BashWrapper module that extracts inner commands from wrapper commands (timeout, sudo, xargs, nohup, nice, env, find -exec, etc.) so both the wrapper and inner command get checked against permission rules. This prevents bypasses like sudo rm -rf / evading "rm *": "deny".

  7. github-actions commented on May 9, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions