Files
Marius CiepluchandClaude Opus 4.8 ae365f6cb7 feat(governance): Phase 1 — CWE policy store + project score (zero-dep)
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]>
2026-06-22 12:31:35 +02:00

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)