Skip to content

Approval prompt shows phantom "N directories" scopes for commands with ? in echo text or bare numeric args #1795

Description

@Aaronontheweb

Approval prompt shows phantom "N directories" scopes for commands with ? in echo text or bare numeric args

Found in: 0.25.3+ (introduced by #1768, "Check every parsed shell path approval scope")
Expected: The approval header names the real directory scopes of the command, or falls back to cwd.
Actual: The header says "Approve in 2 directories?" where neither directory exists on disk — they are artifacts of argument classification, not real paths.

Repro

Run shell_execute with this command (working directory /home/user/repos/demo):

echo "=== stats JSON ==="; curl -s -m 15 "https://example.com/stats" | grep -oE "SearXNG|error|Errors" | head; echo "---try /stats?format=json---"; curl -s -m 15 "https://example.com/stats?format=json" | head -c 2000

The approval prompt renders:

Approve in 2 directories?
• echo
• curl
• grep
• head

ExtractCandidates produces 6 candidates with 2 distinct directories — neither exists:

verb=echo dir=[/home/user/repos/demo/---try]
verb=head dir=[/home/user/repos/demo/2000]

Root cause

Both phantom scopes come from ShellApprovalMatcher.ResolveClauseDirectories / IsAuthorizationPathArg in src/Netclaw.Security/IToolApprovalMatcher.cs:

  1. echo "---try /stats?format=json---" — the ? inside the quoted echo text makes the parser classify the token as a Glob path. ResolveGlobCoveringDirectory derives a covering directory from the static prefix before the glob char: /home/user/repos/demo/---try. The ? is URL query syntax, not a glob.
  2. head -c 2000 — the bare value token 2000 contains no slash, so IsAuthorizationPathArg accepts it as a path (!arg.Raw.Contains('/') branch) and it resolves relative to cwd to /home/user/repos/demo/2000.

Minimal one-command repros, both verified against ShellApprovalMatcher:

  • echo "---try /stats?format=json---" → 1 candidate, dir /home/user/repos/demo/---try
  • head -c 2000 → 1 candidate, dir /home/user/repos/demo/2000

echo "a?b" (no slash) and echo "---try /stats---" (no glob char) do not trigger it — both glob classification and a slash-free token are required.

Impact

This fails safe: the phantom scopes force an approval prompt instead of auto-approving, so there is no security hole. The problem is operator trust and signal — the prompt claims specific directory scopes that do not exist, and "Always here" grants persist noise scopes. It also inflates the Approve in N directories header (multi-distinct-dir path added in #962) on routine commands, which is why it surfaced in 0.25.3 after #1768 started classifying every parsed path arg.

Not related to the trailing-slash glob scoping in #1785, though both touch the same matcher.

Suggested fix

Tighten scope extraction so URL-query ? and bare value tokens do not become authorization scopes, while keeping #1768's intent of checking every real path arg:

  • In IsAuthorizationPathArg, require a slash or a known path shape (., .., ~, @file) before treating a slash-free token as a path; bare numerics and ---try-style tokens should not qualify.
  • In ResolveGlobCoveringDirectory, do not classify a token as a glob scope when the ? occurs after a / in a URL-shaped token (query string) — or require the glob prefix to resolve to an existing directory before emitting a scope.
  • Add regression cases to ShellApprovalMatcherTests for both minimal repros above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingshellIssues related to the shell tool, since it has the largest security perimeter.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions