(command: string)
| 160 | * 'chmod 755 file' → null (number, not a subcommand) |
| 161 | */ |
| 162 | export function getSimpleCommandPrefix(command: string): string | null { |
| 163 | const tokens = command.trim().split(/\s+/).filter(Boolean) |
| 164 | if (tokens.length === 0) return null |
| 165 | |
| 166 | // Skip env var assignments (VAR=value) at the start, but only if they are |
| 167 | // in SAFE_ENV_VARS (or ANT_ONLY_SAFE_ENV_VARS for ant users). If a non-safe |
| 168 | // env var is encountered, return null to fall back to exact match. This |
| 169 | // prevents generating prefix rules like Bash(npm run:*) that can never match |
| 170 | // at allow-rule check time, because stripSafeWrappers only strips safe vars. |
| 171 | let i = 0 |
| 172 | while (i < tokens.length && ENV_VAR_ASSIGN_RE.test(tokens[i]!)) { |
| 173 | const varName = tokens[i]!.split('=')[0]! |
| 174 | const isAntOnlySafe = |
| 175 | isInternalBuild() && ANT_ONLY_SAFE_ENV_VARS.has(varName) |
| 176 | if (!SAFE_ENV_VARS.has(varName) && !isAntOnlySafe) { |
| 177 | return null |
| 178 | } |
| 179 | i++ |
| 180 | } |
| 181 | |
| 182 | const remaining = tokens.slice(i) |
| 183 | if (remaining.length < 2) return null |
| 184 | const subcmd = remaining[1]! |
| 185 | // Second token must look like a subcommand (e.g., "commit", "run", "compose"), |
| 186 | // not a flag (-rf), filename (file.txt), path (/tmp), URL, or number (755). |
| 187 | if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(subcmd)) return null |
| 188 | return remaining.slice(0, 2).join(' ') |
| 189 | } |
| 190 | |
| 191 | // Bare-prefix suggestions like `bash:*` or `sh:*` would allow arbitrary code |
| 192 | // via `-c`. Wrapper suggestions like `env:*` or `sudo:*` would do the same: |
no test coverage detected