"""shellsafe validators: the API-boundary whitelist behind the v0.19.0 SSH command-injection hardening. The quoting *sink* is covered in test_launch_command.py; this locks in the *boundary* — that hostile input is rejected early, and that a valid value passes through unchanged so callers can use `validate_x(v)` inline. """ import pytest from app.shellsafe import validate_container, validate_image, validate_repo # Shell metacharacters that must never survive any validator — these are the # actual injection vectors. (Path traversal like "../" is NOT in scope here: # validate_image legitimately permits "/" and "." for real image refs such as # nvcr.io/nim/...; the defense for images is "no shell metacharacters" + the # quote_arg sink, not path-shape. Slash-rejection is tested directly for repo # and container, where "/" is disallowed.) HOSTILE = [ "; rm -rf /", " a b", "$(touch pwned)", "`id`", "x|cat", "x&y", "x>out", "x\nrm", ] # ---- validate_repo: HF 'org/name', exactly one slash ---- @pytest.mark.parametrize("repo", [ "RedHatAI/Qwen3.6-35B-A3B-NVFP4", # the live production model "org/name", "a.b_c-d/x.y_z-1", ]) def test_repo_valid_passes_through_unchanged(repo): assert validate_repo(repo) == repo @pytest.mark.parametrize("repo", [ "", "noslash", "a/b/c", # two slashes "/name", # empty org "org/", # empty name ] + [f"org/name{h}" for h in HOSTILE]) def test_repo_rejects_malformed_and_hostile(repo): with pytest.raises(ValueError): validate_repo(repo) # ---- validate_image: registry/path:tag@digest ---- @pytest.mark.parametrize("image", [ "nvcr.io/nim/nvidia/parakeet-1_1b-ctc-en-us:latest", "ubuntu", "img@sha256:deadbeefcafe", "a.b/c:1.2_3-4", ]) def test_image_valid_passes_through_unchanged(image): assert validate_image(image) == image @pytest.mark.parametrize("image", [ "", "-leading", # must start alphanumeric ".leading", "/leading", ":leading", "a" * 513, # over the 512 cap ] + [f"img{h}" for h in HOSTILE]) def test_image_rejects_malformed_and_hostile(image): with pytest.raises(ValueError): validate_image(image) # ---- validate_container: Docker name rule, no slash ---- @pytest.mark.parametrize("name", [ "parakeet-asr", "a", "vol_1.2-3", ]) def test_container_valid_passes_through_unchanged(name): assert validate_container(name) == name @pytest.mark.parametrize("name", [ "", "_leading", # underscore is not a valid first char "-leading", ".leading", "has/slash", # slash not allowed in a container name "a" * 129, # over the 128 cap ] + [f"name{h}" for h in HOSTILE]) def test_container_rejects_malformed_and_hostile(name): with pytest.raises(ValueError): validate_container(name)