test: cover shellsafe validators (repo/image/container injection boundary)
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user