Severity (impact x likelihood, fidelity modifier for controls that lie, headline-vs-constraint, build-mode double grade, the floor), disclosure (publish/embargoed/restricted, and the build-mode deferral re-taken and narrowed with RISK-F-0001 in hand), escalation (the five INTENT triggers settled plus an ordering-hazard trigger the RISK-F-0002 case forced; proposed, awaiting the custodian), review (intervals, what a review is, what missing one produces, the production re-score). Then applied: RISK-F-0001 critical/embargoed/escalated, RISK-F-0002 medium with a high constraint on RISK-F-0001's remediation, filed as a peer and escalated only on the ordering, RISK-F-0003 high/embargoed/no escalation. No unset field remains. REGISTER.md is generated; make check reports overdue, stalled, ungraded and unanswered escalations without changing anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
42 lines
1.3 KiB
Python
42 lines
1.3 KiB
Python
"""Shared front-matter reading for the register tools.
|
|
|
|
The finding files are the source of truth. Nothing here writes to them.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pathlib
|
|
import yaml
|
|
|
|
REPO = pathlib.Path(__file__).resolve().parent.parent
|
|
FINDINGS = REPO / "findings"
|
|
NOTES = REPO / "notes"
|
|
|
|
SEVERITIES = ["critical", "high", "medium", "low"]
|
|
REVIEW_INTERVAL_DAYS = {"critical": 7, "high": 30, "medium": 90, "low": 180}
|
|
STALE_MULTIPLIER = 2
|
|
GRADED_FIELDS = ["severity", "disclosure", "escalation", "review_by", "last_reviewed"]
|
|
|
|
|
|
def load(path: pathlib.Path) -> dict:
|
|
text = path.read_text(encoding="utf-8")
|
|
if not text.startswith("---\n"):
|
|
raise ValueError(f"{path.name}: no front-matter")
|
|
_, fm, _body = text.split("---\n", 2)
|
|
data = yaml.safe_load(fm) or {}
|
|
data["_path"] = path
|
|
return data
|
|
|
|
|
|
def findings() -> list[dict]:
|
|
items = [load(p) for p in sorted(FINDINGS.glob("RISK-F-*.md"))]
|
|
return sorted(items, key=lambda f: f["id"], reverse=True)
|
|
|
|
|
|
def notes() -> list[dict]:
|
|
if not NOTES.exists():
|
|
return []
|
|
return sorted((load(p) for p in NOTES.glob("RISK-N-*.md")), key=lambda n: n["id"], reverse=True)
|
|
|
|
|
|
def sev_rank(sev: str) -> int:
|
|
return SEVERITIES.index(sev) if sev in SEVERITIES else len(SEVERITIES)
|