autoscan / tests /test_scoring.py
Chris4K's picture
Upload 384 files
a2a5bfd verified
Raw
History Blame Contribute Delete
7.18 kB
"""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)