tracegrpo / tests /test_contract_parser.py
Dhruv1000's picture
Add files using upload-large-folder tool
5bfc214 verified
Raw History Blame Contribute Delete
3.57 kB
import random
import string
import pytest
from src.contracts.parser import parse_output, parse_statement_lines
GOOD_OUTPUT = """
<analysis>
<label>VULNERABLE</label>
<cwe>CWE-120</cwe>
<statements>12-14, 27</statements>
<trace>
<step>line 12: buf = getenv("PATH") [source: env input]</step>
<step>line 27: memcpy(dst, buf, n) [sink: unchecked copy]</step>
</trace>
<explanation>The buffer is filled from an unchecked environment variable and then
copied without a bounds check, allowing overflow.</explanation>
</analysis>
"""
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 = """<analysis><label>SAFE</label><cwe>NONE</cwe><statements></statements>
<trace></trace><explanation>No vulnerability found in this function.</explanation></analysis>"""
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",
"<analysis></analysis>",
"<analysis><label>MAYBE</label></analysis>",
"<analysis><label>VULNERABLE</label><cwe>not-a-cwe</cwe></analysis>",
"<analysis><label>VULNERABLE</label><cwe>CWE-120</cwe></analysis>", # no explanation
"<label>VULNERABLE</label>", # no wrapping analysis tag
"<analysis><label></label><cwe>NONE</cwe><explanation>x</explanation></analysis>",
"<analysis>" * 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 = (
"<analysis><label>VULNERABLE</label><cwe>CWE-120</cwe>"
"<statements>99999999999999999999999999-1</statements>"
"<explanation>x</explanation></analysis>"
)
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 = ["<analysis>", "</analysis>", "<label>", "</label>", "<cwe>", "</cwe>",
"<statements>", "</statements>", "<trace>", "</trace>", "<step>",
"</step>", "<explanation>", "</explanation>", "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