Check an agent's shell command against an allowlist
Your task
Your coding agent may run shell commands, but only ones on a short allowlist. A line from the model can carry a second command, so a guard reads the whole line before anything runs. The guard follows the small part of the shell's rules below.
Write allowed(command, allowlist) that returns True when the line may run and False when it may not.
Input and output:
command: the line, up to 100,000 characters of printable ASCII, tabs and newlines.allowlist: up to 50 command names. None is empty, and they are compared exactly, case included.- Return a boolean.
Rules:
- Outside quotes, each of
;,&,|and newline ends one command and starts the next. A command with no words is fine. - Outside quotes, spaces and tabs separate words. Every other character is part of a word, including
>,(,=and$. - The first word of every command must be on the allowlist, read after quotes and escaping backslashes come off:
"rm",r'm'andr\mare allrm. An empty pair of quotes makes an empty word. - Inside single quotes, every character is literal up to the next single quote.
- Inside double quotes, every character is literal up to the next double quote, with these exceptions. A backslash before
$, a backtick,"or\is dropped and makes that character literal. Any other backslash stays as a backslash. - Outside quotes, a backslash is dropped and makes the next character literal.
- Return
Falseif the line has a$(or a backtick that is outside quotes or inside double quotes and is not made literal by a backslash. - Return
Falseif the line has a<(or>(that is outside quotes and is not made literal by a backslash. - Return
Falseif a quote never closes, or if a backslash outside single quotes is the last character or comes right before a newline.
Examples
allowed(command: str, allowlist: list[str]) -> boolExample 1
Input
allowed("git status && ls -la | grep src", ["ls", "cat", "git", "grep"])Output
TrueExample 2
Input
allowed("ls; rm -rf /", ["ls", "cat", "git", "grep"])Output
FalseExample 3
Input
allowed("grep 'a;b|c' notes.txt", ["ls", "cat", "git", "grep"])Output
True