AUR-486 -- Go, C#, PowerShell and bash in their idiomatic spelling
Outcome
Command injection is found in the idiomatic spelling of Go, C#, PowerShell and bash, and SQL injection built by shell interpolation is found too. This is the remainder of AUR-481, which delivered Rust and was reduced in scope to what it proved.
Measured finding (2026-08-26)
The same four defects (secret, sql, cmd, xss) were planted in eight languages and the deterministic security pass was run over them. The command-injection cell was missed in all four languages this card covers:
| Language | secret | sql | cmd |
|---|---|---|---|
| Go | sim | sim | NAO |
| C# | sim | sim | NAO |
| PowerShell | sim | sim | NAO |
| Bash | sim | NAO | NAO |
Causes, read from the pattern at the time:
- Go:
exec.Command("sh", "-c", "ping "+host). The branch wasexec[lv]p?e?, which needs anlorvright afterexec(the C spellingsexecl/execv/execve); the dot inexec.Commandalso breaks the sequence. Go is this project's own primary language. - C#:
Process.Start(...)was not in the pattern at all. - PowerShell:
Invoke-Expressionwas not in the pattern at all. - Bash:
eval "ping $1"andsh -c "ping $1"were not in the pattern; and SQL assembled by shell interpolation (query="... WHERE name = '$1'") has no+for the SQL rule to anchor on.
The fix
internal/review/rules/security.yml gains branches, all of them
deterministic (no LLM, no AST):
security/command-injection:- Go:
exec.Command("sh"|"bash"|"cmd"|"powershell", "-c"|"/c"|"-Command", ...). The safe argv formexec.Command("ping", host)names no shell and is excluded structurally. - C#:
Process.Start(...)reaching a shell by quote-then-+concatenation, or a literalcmd/powershellfile name.Process.Start("ping", host)and aProcessStartInfowith separatedArgumentListare excluded. - PowerShell:
Invoke-Expression/iex. - bash:
sh -c/bash -c, andevalanchored at the start of the line (^\s*eval) so the word inside a#comment or a string literal does not fire. security/sql-injection: a SQL verb followed later by a quote immediately before a shell sigil ('$1',"$name",'${ID}'). The quote/$adjacency is what separates shell interpolation from a database placeholder ("SELECT ... = $1"in Rust,?in Python), so AUR-481's parametrized-query AC-002 stays green.
Non-goals
- No existing pattern is loosened. False positives in a merge gate teach the team to disable the gate (AUR-462, AUR-466).
- No semantic analysis or AST: the pass stays a deterministic matcher over added lines.
- No full language parity is promised; what is not covered is declared here.
Acceptance
- AC-001: over a fixture with Go
exec.Command(..., "sh","-c", ...), C#Process.Start, PowerShellInvoke-Expression, basheval/sh -cand SQL-by-shell, the pass reports each with the correct rule citation. - AC-002: the Go argv form, C# separated arguments,
evalinside a comment or a string literal, and parametrized SQL produce no finding. - AC-003 (regression): the Rust (AUR-481), Node (AUR-462), Python
(AUR-435) and C++ (AUR-442/AUR-461) shapes still produce exactly the
findings they produced before. C++ is asserted at the unit boundary: the
system("ping " + host)true positive still fires and its benign neighboursystem("ping example.com")does not. The Rust/Node/Python fixtures are additionally counted at the CLI boundary, scoped to the security section so the separate deterministic-analysis section cannot skew the count.
Selectors: unit tests/unit/AUR-486.go::TestAUR486; integration
tests/integration/AUR-486.go::IntegrationAUR486; acceptance
tests/acceptance/AUR-486.sh; e2e tests/e2e/AUR-486.sh::E2EAUR486.
Mutations
- MUT-001: remove the Go branch (
exec\.Command-> non-matching). The Go command-injection finding must vanish while C#/bash stay; AC-001 RED. - MUT-002: widen the Go branch to a bare
exec\.Command\s*\(. The safe argv form must wrongly match; AC-002 RED.
Runtime note
The sealed profile bootstrap-readonly-v1 has bash and Go but no git, and
this card's paths do not include tests/fixtures. The e2e script and the
integration test therefore write a bare repository in the loose-object
format tests/fixtures/repos/git-demo/build-fixture.sh emits; nothing
shells out to git.
Known limitation, accepted deliberately
The bash eval anchor is the start of the line, so an eval reached
through a command substitution or a wrapper on the same line
(foo; eval "...") is not matched. This is the trade that keeps eval in a
comment or inside a string literal from firing, which is this card's AC-002.