Skip to content

Verb chain extraction includes bare integer positional arguments, creating overly-specific approval patterns #1331

@petabridge-netclaw

Description

@petabridge-netclaw

Problem

Verb chain extraction greedily includes pure integer tokens in the approval pattern because the spec only stops extraction at flags, paths, and URLs. This creates overly-specific approval entries that do not generalize.

Example

freshdesk ticket get 123

Extracted verb chain: freshdesk ticket get 123

When the user approves this command, the persisted pattern is the full 4-token chain. On the next invocation with a different ticket ID:

freshdesk ticket get 456

The verb chain is freshdesk ticket get 456 — which does not match freshdesk ticket get 123. Every unique ticket ID triggers a new approval prompt.

Root Cause

From the open spec:

The verb chain SHALL consist of non-flag tokens from the start of the command until the first flag (-), path, or URL argument. Extraction is greedy: bare-word operands that are neither flags, paths, nor URLs (subcommands, remote names, branch names, refs) SHALL remain in the verb chain.

Pure integers are not flags, not paths, and not URLs — so they pass through as bare-word operands and get baked into the verb chain. A number is never a CLI subcommand.

Impact

  • User-facing: Every unique integer argument (ticket IDs, port numbers, record IDs) produces a distinct approval pattern. Users must approve cmd arg 1, then cmd arg 2, then cmd arg 3, etc.
  • Approval store bloat: Each unique integer creates a new ApprovalEntry in tool-approvals.json.

Other Affected Cases

Command Current Verb Expected Verb
freshdesk ticket get 123 freshdesk ticket get 123 freshdesk ticket get
nc host 8080 nc host 8080 nc host
timeout 30 curl http://… timeout 30 curl timeout
ssh user@host 22 ssh user@host 22 ssh user@host

Proposed Fix

Add integer detection as a verb-chain termination condition in TryGreedyExtract (ShellApprovalSemantics.cs). When the greedy walk encounters a token that is purely digits (with optional leading - for negative numbers, distinguished from flags by not starting with --), stop extraction.

The change should live in ExtractVerbChain — either:

  1. In TryGreedyExtract: stop the greedy walk when int.TryParse(token) succeeds, or
  2. In the post-extraction pass: trim trailing numeric tokens from the result

Option 1 is preferred because it matches the existing pattern (stop at flags, stop at paths, stop at URLs → stop at numbers).

Files Involved

  • src/Netclaw.Security/ShellApprovalSemantics.csTryGreedyExtract / ExtractVerbChain
  • src/Netclaw.Security/ShellTokenizer.csApplyVerbShortCircuit (may need coordination)
  • src/Netclaw.Security.Tests/ShellTokenizerTests.csExtractVerbChain_extracts_expected_chain
  • openspec/specs/tool-approval-gates/spec.md — spec update for termination conditions

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtoolsIssues related to agent tools: file_read, web_search, shell_execute, image processing, etc.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions