diff --git a/security.py b/security.py index 0a22785..44507a4 100644 --- a/security.py +++ b/security.py @@ -273,15 +273,17 @@ def validate_pkill_command( if not args: return False, "pkill requires a process name" - # The target is typically the last non-flag argument - target = args[-1] + # Validate every non-flag argument (pkill accepts multiple patterns on BSD) + # This defensively ensures no disallowed process can be targeted + targets = [] + for arg in args: + # For -f flag (full command line match), take the first word as process name + # e.g., "pkill -f 'node server.js'" -> target is "node server.js", process is "node" + t = arg.split()[0] if " " in arg else arg + targets.append(t) - # For -f flag (full command line match), extract the first word as process name - # e.g., "pkill -f 'node server.js'" -> target is "node server.js", process is "node" - if " " in target: - target = target.split()[0] - - if target in allowed_process_names: + disallowed = [t for t in targets if t not in allowed_process_names] + if not disallowed: return True, "" return False, f"pkill only allowed for processes: {sorted(allowed_process_names)}" diff --git a/test_security.py b/test_security.py index 4bb6d24..5068e1e 100644 --- a/test_security.py +++ b/test_security.py @@ -876,6 +876,34 @@ pkill_processes: print(" FAIL: Process name with space should be rejected") failed += 1 + # Test 12: Multiple patterns - all must be allowed (BSD behavior) + # On BSD, "pkill node sshd" would kill both, so we must validate all patterns + allowed, reason = validate_pkill_command("pkill node npm") + if allowed: + print(" PASS: Multiple allowed patterns accepted") + passed += 1 + else: + print(f" FAIL: Multiple allowed patterns should be accepted: {reason}") + failed += 1 + + # Test 13: Multiple patterns - block if any is disallowed + allowed, reason = validate_pkill_command("pkill node sshd") + if not allowed: + print(" PASS: Multiple patterns blocked when one is disallowed") + passed += 1 + else: + print(" FAIL: Should block when any pattern is disallowed") + failed += 1 + + # Test 14: Multiple patterns - only first allowed, second disallowed + allowed, reason = validate_pkill_command("pkill npm python") + if not allowed: + print(" PASS: Multiple patterns blocked (first allowed, second not)") + passed += 1 + else: + print(" FAIL: Should block when second pattern is disallowed") + failed += 1 + return passed, failed