(
command: string,
options?: { allowFileWrites?: boolean },
)
| 245 | * @returns true if the command is allowed (matches allowlist and passes denylist check), false otherwise |
| 246 | */ |
| 247 | export function sedCommandIsAllowedByAllowlist( |
| 248 | command: string, |
| 249 | options?: { allowFileWrites?: boolean }, |
| 250 | ): boolean { |
| 251 | const allowFileWrites = options?.allowFileWrites ?? false |
| 252 | |
| 253 | // Extract sed expressions (content inside quotes where actual sed commands live) |
| 254 | let expressions: string[] |
| 255 | try { |
| 256 | expressions = extractSedExpressions(command) |
| 257 | } catch (_error) { |
| 258 | // If parsing failed, treat as not allowed |
| 259 | return false |
| 260 | } |
| 261 | |
| 262 | // Check if sed command has file arguments |
| 263 | const hasFileArguments = hasFileArgs(command) |
| 264 | |
| 265 | // Check if command matches allowlist patterns |
| 266 | let isPattern1 = false |
| 267 | let isPattern2 = false |
| 268 | |
| 269 | if (allowFileWrites) { |
| 270 | // When allowing file writes, only check substitution commands (Pattern 2 variant) |
| 271 | // Pattern 1 (line printing) doesn't need file writes |
| 272 | isPattern2 = isSubstitutionCommand(command, expressions, hasFileArguments, { |
| 273 | allowFileWrites: true, |
| 274 | }) |
| 275 | } else { |
| 276 | // Standard read-only mode: check both patterns |
| 277 | isPattern1 = isLinePrintingCommand(command, expressions) |
| 278 | isPattern2 = isSubstitutionCommand(command, expressions, hasFileArguments) |
| 279 | } |
| 280 | |
| 281 | if (!isPattern1 && !isPattern2) { |
| 282 | return false |
| 283 | } |
| 284 | |
| 285 | // Pattern 2 does not allow semicolons (command separators) |
| 286 | // Pattern 1 allows semicolons for separating print commands |
| 287 | for (const expr of expressions) { |
| 288 | if (isPattern2 && expr.includes(';')) { |
| 289 | return false |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | // Defense-in-depth: Even if allowlist matches, check denylist |
| 294 | for (const expr of expressions) { |
| 295 | if (containsDangerousOperations(expr)) { |
| 296 | return false |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | return true |
| 301 | } |
| 302 | |
| 303 | /** |
| 304 | * Check if a sed command has file arguments (not just stdin) |
no test coverage detected