"""Tests for the numeric risk scoring layer (T17).""" from __future__ import annotations import pytest from core.scoring import EMPLOYEE_ACCOUNTS, score_finding # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- 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" # --------------------------------------------------------------------------- # Severity contribution # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- # Confidence contribution # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- # User-input path heuristic # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- # Upload widget escalation # --------------------------------------------------------------------------- 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]) # WARNING severity should not get upload escalation with_widget = score_finding(finding, REPO, all_findings) assert with_widget == no_widget # --------------------------------------------------------------------------- # Employee account bonus # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- # MCP presence escalation # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- # Score clamped to max 10 # --------------------------------------------------------------------------- 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 # --------------------------------------------------------------------------- # Unknown severity/confidence graceful handling # --------------------------------------------------------------------------- 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)