Files

151 lines
6.5 KiB
Python
Raw Permalink Normal View History

"""authorization-obligations task 3.2: reports and ranking name the obligation (offline)."""
from __future__ import annotations
import json
from pathlib import Path
from openultrasast.findings import StaticFinding
from openultrasast.reports import write_markdown_report, write_sarif_report
def _obligation() -> StaticFinding:
return StaticFinding(
finding_id="obligation:protected_read:app.py:18:identity_constraint",
path="app.py",
title="Obligation not discharged: protected_read on book without identity_constraint",
severity="high",
confidence="low",
evidence_level="suspicion",
rationale=(
"protected_read on book in leaky is reached without a dominating identity_constraint (a identity_constraint is present "
"but bound from request_input). Why the obligation exists: app.py::constrained discharges identity_constraint. "
"Label: consistency_violation. Known fix learned from the corpus: corpus:abc."
),
line=18,
function_name="leaky",
reachability_status="unknown",
reachability_evidence=[],
reachability_conditions=[],
tags=[
"obligation:protected_read",
"discharger:identity_constraint",
"obligation_evidence:consistency_violation",
"resource:book",
"mechanism:corpus:abc",
],
ranking_priority=0.0,
)
def _proven() -> StaticFinding:
return StaticFinding(
**{
**_obligation().__dict__,
"finding_id": "python-os-command:app.py:3",
"evidence_level": "crash_reproduced",
"tags": ["injection"],
"line": 3,
"ranking_priority": 1.0,
}
)
OBLIGATIONS = {
"obligation:protected_read:app.py:18:identity_constraint": {
"obligation": "protected_read",
"resource": "book",
"missing": "identity_constraint",
"provenance": "request_input",
"label": "consistency_violation",
"evidence": ["app.py::constrained"],
"known_fix": "corpus:abc",
"intent": None,
}
}
MECHANISMS = {
"corpus:abc": {
"summary": "missing_auth_guard: protected_read without identity_constraint; fix binds authenticated_context",
"guard": "identity_constraint",
"pairs": ["vampi"],
"cwe": "CWE-639",
}
}
def test_markdown_names_obligation_missing_discharger_evidence_and_known_fix(tmp_path: Path) -> None:
path = tmp_path / "report.md"
write_markdown_report([_obligation()], path, mechanisms=MECHANISMS, obligations=OBLIGATIONS)
text = path.read_text()
assert "- Obligation: `protected_read` on `book`" in text
assert "- Missing discharger: `identity_constraint` (`request_input`)" in text
assert "- Evidence: `consistency_violation`" in text and "app.py::constrained" in text
assert "- Known fix: `identity_constraint`" in text and "vampi" in text
assert "- Evidence level: `suspicion`" in text
assert "## Obligations" in text
summary = {
"sibling_sets": 2,
"under_populated": 1,
"sets": [{"module": "api/books", "resource": "book", "handlers": 3}],
"policy_version": "abc123",
}
write_markdown_report([_obligation()], path, mechanisms=MECHANISMS, obligations=OBLIGATIONS, obligations_summary=summary)
text = path.read_text()
assert "Sibling sets evaluated: 2 (under-populated: 1)" in text and "- `api/books` / `book`: 3 handlers" in text
assert "Policy version: `abc123`" in text
def test_sarif_carries_obligation_properties(tmp_path: Path) -> None:
path = tmp_path / "report.sarif"
write_sarif_report([_obligation()], [], path, mechanisms=MECHANISMS, obligations=OBLIGATIONS)
(result,) = json.loads(path.read_text())["runs"][0]["results"]
props = result["properties"]
assert props["obligation_kind"] == "protected_read" and props["obligation_missing"] == "identity_constraint"
assert props["obligation_label"] == "consistency_violation" and props["obligation_evidence"] == ["app.py::constrained"]
assert props["obligation_known_fix"] == "corpus:abc" and props["evidence_level"] == "suspicion"
def test_obligation_ranking_weighs_sensitivity_and_label_but_stays_below_proven_findings() -> None:
from openultrasast.rank import rank_obligations
declared = StaticFinding(
**{
**_obligation().__dict__,
"finding_id": "obligation:protected_read:app.py:20:identity_constraint",
"tags": ["obligation:protected_read", "discharger:identity_constraint", "obligation_evidence:declared_policy_violation"],
"line": 20,
}
)
local = StaticFinding(
**{
**_obligation().__dict__,
"finding_id": "obligation:protected_read:app.py:22:identity_constraint",
"tags": ["obligation:protected_read", "discharger:identity_constraint", "obligation_evidence:function_local"],
"severity": "medium",
"line": 22,
}
)
ranked = rank_obligations([_proven(), _obligation(), declared, local])
by_id = {f.finding_id: f.ranking_priority for f in ranked}
assert by_id[declared.finding_id] > by_id[_obligation().finding_id] > by_id[local.finding_id] > 0
assert max(by_id[f] for f in by_id if f.startswith("obligation:")) < by_id["python-os-command:app.py:3"]
assert by_id["python-os-command:app.py:3"] == 1.0 # non-obligation findings untouched
def test_obligation_cap_binds_on_verdict_ids_because_the_pipeline_never_sets_proof_levels_on_static_findings() -> None:
"""Round-3 finding: proof rungs live on verdicts, so the cap is keyed on the proven finding ids the sandbox returned."""
from openultrasast.rank import rank_obligations
proven_by_sandbox = StaticFinding(**{**_proven().__dict__, "evidence_level": "static_corroboration", "ranking_priority": 0.35})
ranked = rank_obligations([proven_by_sandbox, _obligation()], proven_ids={proven_by_sandbox.finding_id})
by_id = {f.finding_id: f.ranking_priority for f in ranked}
assert 0 < by_id[_obligation().finding_id] < 0.35 and by_id[proven_by_sandbox.finding_id] == 0.35
unproven = rank_obligations([proven_by_sandbox, _obligation()])
assert {f.finding_id: f.ranking_priority for f in unproven}[_obligation().finding_id] > 0.35 # no verdict, no cap
def test_reports_without_obligations_are_unchanged(tmp_path: Path) -> None:
path = tmp_path / "report.md"
write_markdown_report([_proven()], path)
assert "Obligation:" not in path.read_text() and "## Obligations" not in path.read_text()