mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
First implementation slice of the approved harnessx-self-improving-rulesets spec (Foundation: governance + scoring planes). Additive and zero-dependency; findings stay byte-identical and the >=90% recall / <10% FP detection gate is unchanged. - PolicyStore (policy/verycode.py): vendors verycode-policies CWE_Score.tsv (167 CWEs, CRLF + trailing-space header parsed defensively); CwePolicy(severity 0-5, static/dynamic); load_policy, resolve_severity (severity is policy-governed, keyed on CWE — one CWE = one severity), assert_rules_resolve (fail-loud if an enabled rule's CWE is unmapped). Remap the C/C++ buffer rules CWE-120 -> CWE-121 (CWE-120 is absent from verycode; 121 = Stack-Based Buffer Overflow, severity 5). - ScoreModel (scoring/project_score.py): SEV_WEIGHT, REACH_MULT, finding_penalty, project_score (0-100 exponential decay; one sev-5 reachable -> ~43), two-condition gate (sev-5+reachable always fails; score<min_score fails only when blocking), build_score_artifact. - Wire into _run_scan: policy_load + fail-loud policy_check stages; score stage writes score.json and merges a score block into the manifest (advisory-first). - ScoreConfig (k/min_score/block_severity_reachable/blocking) in config.py. - pyproject uv_build ships the vendored TSV in the wheel (verified). - Tests: test_policy, test_scoring, test_score_pipeline (artifact + fail-loud). 85 passed; ruff + mypy clean. Spec phases 1.1-2.4 checked off; spec approved. Co-Authored-By: Claude Opus 4.8 <[email protected]>
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from openultrasast.findings import PATTERN_RULES
|
|
from openultrasast.policy import (
|
|
CwePolicy,
|
|
PolicyError,
|
|
assert_rules_resolve,
|
|
load_policy,
|
|
resolve_severity,
|
|
)
|
|
|
|
|
|
def test_load_policy_parses_severity_and_scope() -> None:
|
|
policy = load_policy()
|
|
|
|
assert policy["CWE-121"] == CwePolicy("Buffer Overflow", 5, True, False)
|
|
assert policy["CWE-89"].severity == 4
|
|
assert policy["CWE-78"].static is True
|
|
assert len(policy) > 150 # ~167 CWEs vendored from verycode-policies
|
|
|
|
|
|
def test_resolve_severity_is_policy_governed_and_identical_for_same_cwe() -> None:
|
|
policy = load_policy()
|
|
|
|
# Two different rules that map to the same CWE must get the same severity.
|
|
assert resolve_severity(policy, "CWE-78") == resolve_severity(policy, "CWE-78") == 5
|
|
# Unmapped CWE resolves to 0 (report-only, never scored).
|
|
assert resolve_severity(policy, "CWE-99999") == 0
|
|
|
|
|
|
def test_builtin_rules_all_resolve_in_policy() -> None:
|
|
# The fail-loud invariant must pass for the shipped ruleset (CWE-120 remapped to CWE-121).
|
|
assert_rules_resolve(PATTERN_RULES, load_policy())
|
|
|
|
|
|
def test_assert_rules_resolve_fails_loud_on_unmapped_enabled_rule() -> None:
|
|
policy = load_policy()
|
|
bad_enabled = SimpleNamespace(rule_id="bad", cwe="CWE-99999", status="enabled")
|
|
bad_disabled = SimpleNamespace(rule_id="bad-off", cwe="CWE-99999", status="disabled")
|
|
|
|
with pytest.raises(PolicyError, match="bad"):
|
|
assert_rules_resolve([bad_enabled], policy)
|
|
|
|
# A disabled rule with an unmapped CWE does not abort the scan.
|
|
assert_rules_resolve([bad_disabled], policy)
|