import random import string import pytest from src.contracts.parser import parse_output, parse_statement_lines GOOD_OUTPUT = """ CWE-120 12-14, 27 line 12: buf = getenv("PATH") [source: env input] line 27: memcpy(dst, buf, n) [sink: unchecked copy] The buffer is filled from an unchecked environment variable and then copied without a bounds check, allowing overflow. """ def test_well_formed_parses_ok(): out = parse_output(GOOD_OUTPUT) assert out.format_ok is True assert out.label == "VULNERABLE" assert out.cwe == "CWE-120" assert out.statement_lines == {12, 13, 14, 27} assert len(out.trace) == 2 assert out.trace[0].line == 12 assert "source" in out.trace[0].tag assert out.trace[1].line == 27 assert out.explanation def test_safe_label_with_none_cwe(): text = """NONE No vulnerability found in this function.""" out = parse_output(text) assert out.format_ok is True assert out.label == "SAFE" assert out.cwe == "NONE" assert out.statement_lines == set() @pytest.mark.parametrize( "bad", [ "", "not even xml", "", "", "not-a-cwe", "CWE-120", # no explanation "", # no wrapping analysis tag "NONEx", "" * 50, None, 12345, ], ) def test_malformed_never_crashes(bad): out = parse_output(bad) assert out.format_ok is False def test_bad_statement_range_does_not_crash_but_is_logged(): text = ( "CWE-120" "99999999999999999999999999-1" "x" ) out = parse_output(text) assert out.format_ok is True # structurally valid; the bad range is just logged assert out.statement_lines == set() assert out.parse_errors def test_fuzz_never_crashes(): rng = random.Random(42) alphabet = string.printable tags = ["", "", "", "", "", "", "", "", "", "", "", "", "", "VULNERABLE", "SAFE", "CWE-120", "NONE", "line 12: foo()"] for _ in range(150): n_pieces = rng.randint(0, 15) pieces = [] for _ in range(n_pieces): if rng.random() < 0.5: pieces.append(rng.choice(tags)) else: pieces.append("".join(rng.choice(alphabet) for _ in range(rng.randint(0, 20)))) garbage = "".join(pieces) out = parse_output(garbage) # must not raise assert isinstance(out.format_ok, bool) def test_parse_statement_lines_ranges(): lines, errors = parse_statement_lines("12-14, 27") assert lines == {12, 13, 14, 27} assert errors == [] def test_parse_statement_lines_rejects_inverted_range(): lines, errors = parse_statement_lines("20-10") assert lines == set() assert errors