| """Tests for the numeric risk scoring layer (T17)."""
|
| from __future__ import annotations
|
|
|
| import pytest
|
| from core.scoring import EMPLOYEE_ACCOUNTS, score_finding
|
|
|
|
|
|
|
|
|
|
|
|
|
| def _finding(severity="ERROR", confidence="confirmed", message="", file="app.py", rule="test-rule"):
|
| return {
|
| "tool": "test",
|
| "rule": rule,
|
| "severity": severity,
|
| "confidence": confidence,
|
| "file": file,
|
| "line": 1,
|
| "message": message,
|
| "owasp": [],
|
| "remediation": "",
|
| }
|
|
|
|
|
| REPO = "https://huggingface.co/testuser/myspace"
|
| EMPLOYEE_REPO = "https://huggingface.co/clem/myspace"
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_score_error_higher_than_warning():
|
| err = score_finding(_finding(severity="ERROR", confidence="possible"), REPO, [])
|
| warn = score_finding(_finding(severity="WARNING", confidence="possible"), REPO, [])
|
| assert err > warn
|
|
|
|
|
| def test_score_warning_higher_than_info():
|
| warn = score_finding(_finding(severity="WARNING", confidence="possible"), REPO, [])
|
| info = score_finding(_finding(severity="INFO", confidence="possible"), REPO, [])
|
| assert warn > info
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_score_confirmed_higher_than_likely():
|
| conf = score_finding(_finding(severity="ERROR", confidence="confirmed"), REPO, [])
|
| likely = score_finding(_finding(severity="ERROR", confidence="likely"), REPO, [])
|
| assert conf > likely
|
|
|
|
|
| def test_score_likely_higher_than_possible():
|
| likely = score_finding(_finding(severity="ERROR", confidence="likely"), REPO, [])
|
| possible = score_finding(_finding(severity="ERROR", confidence="possible"), REPO, [])
|
| assert likely > possible
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_user_input_message_adds_points():
|
| with_ui = score_finding(
|
| _finding(severity="WARNING", confidence="possible", message="request.files upload handler"),
|
| REPO, []
|
| )
|
| without_ui = score_finding(
|
| _finding(severity="WARNING", confidence="possible", message="hardcoded constant"),
|
| REPO, []
|
| )
|
| assert with_ui > without_ui
|
|
|
|
|
| def test_upload_pattern_in_file_adds_points():
|
| with_upload = score_finding(
|
| _finding(severity="ERROR", confidence="possible", file="upload_handler.py"),
|
| REPO, []
|
| )
|
| without_upload = score_finding(
|
| _finding(severity="ERROR", confidence="possible", file="models.py"),
|
| REPO, []
|
| )
|
| assert with_upload > without_upload
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_upload_widget_escalation_for_error():
|
| finding = _finding(severity="ERROR", confidence="confirmed")
|
| all_findings = [
|
| finding,
|
| _finding(message="gr.File upload widget detected", severity="INFO"),
|
| ]
|
| with_widget = score_finding(finding, REPO, all_findings)
|
| without_widget = score_finding(finding, REPO, [finding])
|
| assert with_widget > without_widget
|
|
|
|
|
| def test_upload_widget_no_escalation_for_warning():
|
| finding = _finding(severity="WARNING", confidence="confirmed")
|
| all_findings = [
|
| finding,
|
| _finding(message="gr.File upload widget", severity="INFO"),
|
| ]
|
| no_widget = score_finding(finding, REPO, [finding])
|
|
|
| with_widget = score_finding(finding, REPO, all_findings)
|
| assert with_widget == no_widget
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_employee_owner_adds_point():
|
| base = score_finding(_finding(severity="ERROR", confidence="confirmed"), REPO, [])
|
| emp = score_finding(_finding(severity="ERROR", confidence="confirmed"), EMPLOYEE_REPO, [])
|
| assert emp == base + 1
|
|
|
|
|
| def test_non_employee_no_bonus():
|
| base = score_finding(_finding(severity="ERROR", confidence="confirmed"), REPO, [])
|
| other_repo = "https://huggingface.co/nobody/myspace"
|
| other = score_finding(_finding(severity="ERROR", confidence="confirmed"), other_repo, [])
|
| assert base == other
|
|
|
|
|
| def test_employee_accounts_contains_known_members():
|
| assert "clem" in EMPLOYEE_ACCOUNTS
|
| assert "thomwolf" in EMPLOYEE_ACCOUNTS
|
| assert "julien-c" in EMPLOYEE_ACCOUNTS
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_mcp_presence_adds_point():
|
| finding = _finding(severity="WARNING", confidence="likely")
|
| no_mcp = score_finding(finding, REPO, [finding])
|
| mcp_finding = _finding(rule="mcp-tool-poisoning", severity="ERROR")
|
| with_mcp = score_finding(finding, REPO, [finding, mcp_finding])
|
| assert with_mcp == no_mcp + 1
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_score_clamped_at_10():
|
| """Even with all bonuses stacking, score never exceeds 10."""
|
| finding = _finding(
|
| severity="ERROR",
|
| confidence="confirmed",
|
| message="request.form upload user_input query_params",
|
| file="upload_handler.py",
|
| )
|
| all_findings = [
|
| finding,
|
| _finding(message="gr.File widget", severity="INFO"),
|
| _finding(rule="mcp-poisoning", severity="ERROR"),
|
| ]
|
| score = score_finding(finding, EMPLOYEE_REPO, all_findings)
|
| assert score <= 10
|
|
|
|
|
| def test_score_always_non_negative():
|
| finding = _finding(severity="INFO", confidence="possible")
|
| score = score_finding(finding, REPO, [])
|
| assert score >= 0
|
|
|
|
|
|
|
|
|
|
|
|
|
| def test_unknown_severity_does_not_crash():
|
| finding = _finding(severity="UNKNOWN_SEVERITY", confidence="possible")
|
| score = score_finding(finding, REPO, [])
|
| assert isinstance(score, int)
|
|
|
|
|
| def test_unknown_confidence_does_not_crash():
|
| finding = _finding(severity="ERROR", confidence="UNKNOWN")
|
| score = score_finding(finding, REPO, [])
|
| assert isinstance(score, int)
|
|
|