Answer architecture round 2: 1 🔴, 3 🟡; read the report and edit the plan in two functions, propose only while no pull request is open, and test filing into the repository's own todo.md

This commit is contained in:
2026-10-14 13:03:56 +02:00
committed by Lillem4n
parent 273dd45128
commit 2d86f4f0c0
9 changed files with 209 additions and 141 deletions
+85 -56
View File
@@ -9,13 +9,28 @@ import ast
import json
import re
import sys
from dataclasses import dataclass, field
ROW = re.compile(r"^\| (\d+) \| ([^|]+?) \| [^|]*\| \*\*", re.M)
WEIGHT = re.compile(r"^\| ([\d.]+) \| ([\d.]+) \|$", re.M)
NEXT_ID = re.compile(r"`Next ID = (\d+)`")
SCORES = {"R": 1, "S": 1, "C": 1, "A": 8, "G": 8}
GOAL = "1.1"
@dataclass
class Module:
found_in: str
fixed: list[str] = field(default_factory=list)
osvs: dict[str, dict] = field(default_factory=dict)
def name(self, path: str) -> str:
return "Go" if path == "stdlib" else f"`{path}`"
def version(self, path: str, version: str) -> str:
return version.removeprefix("v") if path == "stdlib" else version
def messages(report: str):
decoder, at = json.JSONDecoder(), 0
while at < len(report):
@@ -26,17 +41,25 @@ def messages(report: str):
yield message
def vulnerable_modules(report: str) -> dict[str, Module]:
"""Each module govulncheck found a vulnerability in, by module path, stdlib being Go."""
osvs, modules = {}, {}
for message in messages(report):
if "osv" in message:
osvs[message["osv"]["id"]] = message["osv"]
elif "finding" in message:
finding = message["finding"]
frame = finding["trace"][0]
module = modules.setdefault(frame["module"], Module(frame["version"]))
module.osvs[finding["osv"]] = osvs[finding["osv"]]
module.fixed.append(finding.get("fixed_version", ""))
return modules
def version_key(version: str):
return [int(part) for part in re.findall(r"\d+", version)]
def score(todo: str) -> str:
formula = re.search(r"`Score = (.+?)`", todo).group(1)
weights = dict(WEIGHT.findall(todo))
values = dict(SCORES, W=float(weights[GOAL]))
return f"{evaluate(ast.parse(formula, mode='eval').body, values):.1f}"
def evaluate(node, values):
match node:
case ast.Constant(value=value):
@@ -45,75 +68,81 @@ def evaluate(node, values):
return values[name]
case ast.UnaryOp(op=ast.USub(), operand=operand):
return -evaluate(operand, values)
case ast.BinOp(left=left, op=op, right=right):
a, b = evaluate(left, values), evaluate(right, values)
return {ast.Add: a + b, ast.Sub: a - b, ast.Mult: a * b, ast.Div: a / b if b else 0}[type(op)]
raise SystemExit(f"todo.md's formula holds {ast.dump(node)}, which this script cannot evaluate")
case ast.BinOp(left=left, op=ast.Add(), right=right):
return evaluate(left, values) + evaluate(right, values)
case ast.BinOp(left=left, op=ast.Sub(), right=right):
return evaluate(left, values) - evaluate(right, values)
case ast.BinOp(left=left, op=ast.Mult(), right=right):
return evaluate(left, values) * evaluate(right, values)
case ast.BinOp(left=left, op=ast.Div(), right=right):
return evaluate(left, values) / evaluate(right, values)
raise ValueError(f"todo.md's formula holds {ast.dump(node)}, which this script cannot evaluate")
def item(module: str, found_in: str, fixed: str, osvs: list[dict]) -> tuple[str, str]:
if module == "stdlib":
tagline = f"Raise Go to {fixed.lstrip('v')}, so the release binaries carry none of its known vulnerabilities."
where = f"Go {found_in.lstrip('v')}"
else:
tagline = f"Raise `{module}` to {fixed}, so the module requires none of its known vulnerabilities."
where = f"`{module}` {found_in}"
def score(todo: str) -> str:
formula = re.search(r"`Score = (.+?)`", todo).group(1)
values = dict(SCORES, W=float(dict(WEIGHT.findall(todo))[GOAL]))
return f"{evaluate(ast.parse(formula, mode='eval').body, values):.1f}"
def tagline(path: str, module: Module) -> str:
carried = f"{module.name(path)} {module.version(path, module.found_in)}"
if "" in module.fixed:
return f"Ship no release while fejkdata carries {carried}, some of whose known vulnerabilities no version fixes yet."
fixed = module.version(path, max(module.fixed, key=version_key))
if path == "stdlib":
return f"Raise Go to {fixed}, so the release binaries carry none of {carried}'s known vulnerabilities."
return f"Raise {module.name(path)} to {fixed}, so fejkdata requires no version of it with a known vulnerability."
def detail(path: str, module: Module) -> str:
lines = []
for osv in sorted(osvs, key=lambda o: o["id"]):
aliases = f" ({', '.join(osv['aliases'])})" if osv.get("aliases") else ""
lines.append(f"- {osv['id']}{aliases}: {osv.get('summary', '')}")
return tagline, f"govulncheck found these in {where}:\n\n" + "\n".join(lines) + "\n"
for osv in sorted(module.osvs.values(), key=lambda o: o["id"]):
aliases = f" ({', '.join(osv['aliases'])})" if osv["aliases"] else ""
lines.append(f"- {osv['id']}{aliases}: {osv['summary']}")
carried = f"{module.name(path)} {module.version(path, module.found_in)}"
return f"govulncheck found these in {carried}:\n\n" + "\n".join(lines) + "\n"
def file_items(report: str, todo: str) -> tuple[str, int]:
"""Return todo.md with an item for each module whose vulnerabilities it does not yet name, and how many are known."""
osvs, modules = {}, {}
for message in messages(report):
if "osv" in message:
osvs[message["osv"]["id"]] = message["osv"]
elif "finding" in message:
finding = message["finding"]
module, found_in = finding["trace"][0]["module"], finding["trace"][0].get("version", "")
entry = modules.setdefault(module, {"found_in": found_in, "fixed": "", "ids": set()})
entry["ids"].add(finding["osv"])
if version_key(finding.get("fixed_version", "")) > version_key(entry["fixed"]):
entry["fixed"] = finding["fixed_version"]
known = sum(len(entry["ids"]) for entry in modules.values())
for module in sorted(modules, key=lambda m: (m != "stdlib", m)):
entry = modules[module]
if all(osv in todo for osv in entry["ids"]):
def file_items(modules: dict[str, Module], todo: str) -> tuple[str, list[int]]:
"""Return todo.md with an item for each module whose vulnerabilities it does not yet name, and the items' IDs."""
filed = []
for path in sorted(modules, key=lambda m: (m != "stdlib", m)):
module = modules[path]
if all(osv in todo for osv in module.osvs):
continue
first = ROW.search(todo)
if first is None:
raise SystemExit("todo.md has no item to take the next release from")
next_id = int(re.search(r"`Next ID = (\d+)`", todo).group(1))
tagline, detail = item(module, entry["found_in"], entry["fixed"], [osvs.get(i, {"id": i}) for i in entry["ids"]])
raise ValueError("todo.md has no item to take the next release from")
next_id = int(NEXT_ID.search(todo).group(1))
line = tagline(path, module)
scores = " | ".join(str(SCORES[k]) for k in "RSCAG")
row = f"| {next_id} | {first.group(2)} | security | **{tagline}** | {scores} | {GOAL} | {score(todo)} |\n"
row = f"| {next_id} | {first.group(2)} | security | **{line}** | {scores} | {GOAL} | {score(todo)} |\n"
todo = todo[: first.start()] + row + todo[first.start() :]
todo = todo.replace(f"`Next ID = {next_id}`", f"`Next ID = {next_id + 1}`", 1)
todo = todo.replace("## Details\n\n", f"## Details\n\n### {next_id}. {tagline}\n\n{detail}\n", 1)
return todo, known
todo = todo.replace("## Details\n\n", f"## Details\n\n### {next_id}. {line}\n\n{detail(path, module)}\n", 1)
filed.append(next_id)
return todo, filed
def main(argv: list[str]) -> int:
report_path, todo_path = argv
with open(report_path, encoding="utf-8") as f:
report = f.read()
with open(todo_path, encoding="utf-8") as f:
todo = f.read()
filed, known = file_items(report, todo)
if not known:
modules = vulnerable_modules(f.read())
if not modules:
print("No known vulnerabilities.")
return 0
if filed == todo:
print(f"{known} known vulnerabilities, already planned in todo.md.")
count = sum(len(m.osvs) for m in modules.values())
known = f"{count} known {'vulnerability' if count == 1 else 'vulnerabilities'}"
with open(todo_path, encoding="utf-8") as f:
todo, filed = file_items(modules, f.read())
if not filed:
print(f"{known}, already planned in todo.md.")
return 1
with open(todo_path, "w", encoding="utf-8") as f:
f.write(filed)
next_id = re.compile(r"`Next ID = (\d+)`")
ids = range(int(next_id.search(todo).group(1)), int(next_id.search(filed).group(1)))
print(f"{known} known vulnerabilities; todo.md item {', '.join(map(str, ids))} plans the fix.")
f.write(todo)
items = ", ".join(map(str, filed))
print(f"{known}; todo.md {'item ' + items + ' plans' if len(filed) == 1 else 'items ' + items + ' plan'} the fix.")
return 1