Documentation
¶
Overview ¶
Package shellparse reads a shell command line roughly as a shell would: quotes (including $'...' escapes and $"..."), a leading backslash, unquoted $IFS as a word break, operators, subshells, $( ) and backtick substitutions, and the strings run by sh -c, eval and ssh. It does not expand other variables, globs or braces. It has no dependencies. The blocking bash check (tools/builtin) and the advisory destructive-bash rule and watchdog (rules) both read commands with it, and share its rm policy (DestructiveRm), so the rule sees whatever the check refuses.
Index ¶
- Constants
- Variables
- func Command(words []string) (name string, args []string)
- func CommandName(w string) string
- func IsRedirect(w string) bool
- func RedirectTakesNext(w string) bool
- func RmFlags(args []string) (recursive, force bool, targets []string)
- func RmRefused(words []string) bool
- func Segments(s string) (segs [][]string, nested []string)
- func Steps() int64
- type Result
Constants ¶
const MaxDepth = 4
MaxDepth bounds recursion into nested shells (sh -c, eval, ssh, substitutions).
Variables ¶
var ShellValueOptions = map[string]bool{"-o": true, "+o": true, "-O": true, "+O": true, "--rcfile": true, "--init-file": true}
ShellValueOptions are a shell's options that take the next word as their value.
var Shells = map[string]bool{"sh": true, "bash": true, "zsh": true, "dash": true, "ksh": true, "fish": true}
Shells are the commands whose -c argument is itself a command line.
var Wrappers = map[string]bool{"sudo": true, "command": true, "exec": true, "nohup": true, "env": true, "time": true, "nice": true, "doas": true, "builtin": true}
Wrappers run their arguments as a command.
Functions ¶
func Command ¶
Command skips assignments (FOO=1, also after env) and wrappers (sudo, env, nohup, ... and their flags) to the command word, and returns its name (CommandName) and arguments. name is "" when there is no command word.
func CommandName ¶
CommandName is a command word as the shell resolves it: a leading backslash removed and the path dropped (/bin/rm -> rm). Quotes are already gone (Segments removes them).
func RedirectTakesNext ¶
RedirectTakesNext: a bare operator ("> file") names its target in the next word; "2>&1" and ">file" do not.
func RmFlags ¶
RmFlags reads rm's arguments: recursive (-r, -R, --recursive or a GNU prefix of it such as --rec), force (-f, --force or a prefix such as --for) and the paths it removes. Redirections are not paths.
func RmRefused ¶
RmRefused reports whether a simple command (a word list) holds an rm the bash tool refuses: any word that resolves to rm, with the words after it as its arguments (rm -r rm / removes /). It reads every rm's arguments in one backward pass, so a line of many rm words stays linear.
Types ¶
type Result ¶
type Result int
Result is what Walk found.
func DestructiveRm ¶
DestructiveRm reports whether any command in the line, nested ones included, is a recursive rm of a system or home path (Found), or whether the line nests command strings too deep to check (TooDeep). Every word that resolves to rm starts a check of the words after it, so wrappers (xargs, timeout 5, nice -n 5) do not hide it. The bash tool refuses what it reports; the advisory destructive-bash rule and the watchdog (rules.Destructive) see at least the same.
Out of scope (documented, not checked): relative targets that climb out with .., paths that come from a variable, a substitution or a glob or brace expansion (X=/; rm -rf $X), find -delete, deletes from other programs (python shutil.rmtree), chmod/chown -R, commands fed to a shell on stdin, and paths that reach rm through stdin (find / | xargs rm -rf) or a script.
func Walk ¶
Walk calls fn with every simple command in cmd (a word list, quotes removed), including the commands nested in $( ), backticks, the command string of sh/bash/... -c (wherever -c sits among the shell's options), script -c, env -S, eval arguments and an ssh remote command, each also as its own command line. It stops at the first command fn returns true for.