"""Platform safety rules: subdomain validation, proxy targets, port ranges.""" from __future__ import annotations import pytest from app.domain.platform.rules import ( RuleViolation, assert_not_protected_port, assert_port_allocatable, build_host, is_port_allocatable, validate_managed_host, validate_proxy_target, validate_subdomain_label, ) @pytest.mark.parametrize( "label", ["my-app", "app1", "a" * 40, "abc"] ) def test_valid_labels(label: str): assert validate_subdomain_label(label) == label @pytest.mark.parametrize( "label", [ "ab", # too short "a" * 41, # too long "-abc", # leading hyphen "abc-", # trailing hyphen "My-App", # uppercase is normalised, but this one has no other issue "app_name", # underscore not allowed "app.name", # dot not allowed "app name", # whitespace "app{}", # brace injection attempt "app\nother", # newline injection attempt "", # empty ], ) def test_invalid_labels(label: str): if label == "My-App": # Documented behaviour: input is lower-cased, so this is accepted as "my-app". assert validate_subdomain_label(label) == "my-app" return with pytest.raises(RuleViolation): validate_subdomain_label(label) def test_build_host_appends_root_domain(): assert build_host("shop", "msa.artacloud.ir") == "shop.msa.artacloud.ir" def test_managed_host_rejects_foreign_domain(): with pytest.raises(RuleViolation): validate_managed_host("evil.example.com", "msa.artacloud.ir") def test_managed_host_rejects_nested_label(): with pytest.raises(RuleViolation): validate_managed_host("a.b.msa.artacloud.ir", "msa.artacloud.ir") def test_managed_host_rejects_injection_attempt(): with pytest.raises(RuleViolation): validate_managed_host("app { reverse_proxy evil:80 }.msa.artacloud.ir", "msa.artacloud.ir") def test_managed_host_accepts_valid_host(): assert ( validate_managed_host("my-app.msa.artacloud.ir", "msa.artacloud.ir") == "my-app.msa.artacloud.ir" ) @pytest.mark.parametrize( "target", ["localhost:8080", "127.0.0.1:8080", "http://localhost:8080", "http://127.0.0.1:9000"], ) def test_valid_proxy_targets(target: str): host, port = validate_proxy_target(target) assert host in {"localhost", "127.0.0.1"} assert 1 <= port <= 65535 @pytest.mark.parametrize( "target", [ "example.com:80", "169.254.169.254:80", # cloud metadata endpoint "10.0.0.5:8080", # internal network "localhost", # no port "localhost:notaport", "0.0.0.0:8080", ], ) def test_ssrf_proxy_targets_rejected(target: str): with pytest.raises(RuleViolation): validate_proxy_target(target) @pytest.mark.parametrize("port", [22, 53, 80, 443, 3000, 5432, 6379, 8008, 2019, 7999, 9000]) def test_reserved_and_out_of_range_ports_rejected(port: int): assert not is_port_allocatable(port) with pytest.raises(RuleViolation): assert_port_allocatable(port) @pytest.mark.parametrize("port", [8080, 8500, 8999]) def test_allocatable_ports(port: int): assert is_port_allocatable(port) assert assert_port_allocatable(port) == port @pytest.mark.parametrize("port", [3000, 35555, 35556, 35557, 5432]) def test_protected_ports_cannot_be_proxied(port: int): with pytest.raises(RuleViolation): assert_not_protected_port(port)