Validate pkill commands - only allow killing dev-related processes. Uses shlex to parse the command, avoiding regex bypass vulnerabilities. Args: command_string: The pkill command to validate extra_processes: Optional set of additional process names to allow
(
command_string: str,
extra_processes: Optional[set[str]] = None
)
| 315 | |
| 316 | |
| 317 | def validate_pkill_command( |
| 318 | command_string: str, |
| 319 | extra_processes: Optional[set[str]] = None |
| 320 | ) -> tuple[bool, str]: |
| 321 | """ |
| 322 | Validate pkill commands - only allow killing dev-related processes. |
| 323 | |
| 324 | Uses shlex to parse the command, avoiding regex bypass vulnerabilities. |
| 325 | |
| 326 | Args: |
| 327 | command_string: The pkill command to validate |
| 328 | extra_processes: Optional set of additional process names to allow |
| 329 | (from org/project config pkill_processes) |
| 330 | |
| 331 | Returns: |
| 332 | Tuple of (is_allowed, reason_if_blocked) |
| 333 | """ |
| 334 | # Merge default processes with any extra configured processes |
| 335 | allowed_process_names = DEFAULT_PKILL_PROCESSES.copy() |
| 336 | if extra_processes: |
| 337 | allowed_process_names |= extra_processes |
| 338 | |
| 339 | try: |
| 340 | tokens = shlex.split(command_string) |
| 341 | except ValueError: |
| 342 | return False, "Could not parse pkill command" |
| 343 | |
| 344 | if not tokens: |
| 345 | return False, "Empty pkill command" |
| 346 | |
| 347 | # Separate flags from arguments |
| 348 | args = [] |
| 349 | for token in tokens[1:]: |
| 350 | if not token.startswith("-"): |
| 351 | args.append(token) |
| 352 | |
| 353 | if not args: |
| 354 | return False, "pkill requires a process name" |
| 355 | |
| 356 | # Validate every non-flag argument (pkill accepts multiple patterns on BSD) |
| 357 | # This defensively ensures no disallowed process can be targeted |
| 358 | targets = [] |
| 359 | for arg in args: |
| 360 | # For -f flag (full command line match), take the first word as process name |
| 361 | # e.g., "pkill -f 'node server.js'" -> target is "node server.js", process is "node" |
| 362 | t = arg.split()[0] if " " in arg else arg |
| 363 | targets.append(t) |
| 364 | |
| 365 | disallowed = [t for t in targets if t not in allowed_process_names] |
| 366 | if not disallowed: |
| 367 | return True, "" |
| 368 | return False, f"pkill only allowed for processes: {sorted(allowed_process_names)}" |
| 369 | |
| 370 | |
| 371 | def validate_chmod_command(command_string: str) -> tuple[bool, str]: |
no outgoing calls