Files
spark-control/image/tests/test_shellsafe.py
T

99 lines
2.8 KiB
Python

"""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)