scripts/ had ~80 flat files. Entry points (ci_*.sh, codegen.sh, run_tests.sh, format_native.sh, setup_hooks.sh, upload-symbols.*) and the shared pubspec_version.py stay at the root; checkers, generators, maestro tooling and release tooling move into subdirectories with their tests. Updated every reference: workflow steps, guard-test glob, Docker COPY paths and .dockerignore whitelist, website audit path, dart test imports, and regenerated the five outputs whose headers embed generator paths.
363 lines
13 KiB
Python
Executable File
363 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Enforce an exact, expiring baseline for the website's Bun audit results."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
from datetime import date, timedelta
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from typing import Any
|
|
|
|
|
|
MAX_ACCEPTANCE_DAYS = 90
|
|
MAX_AUDIT_OUTPUT_BYTES = 5 * 1024 * 1024
|
|
MAX_DIAGNOSTICS = 20
|
|
MAX_ID_LENGTH = 128
|
|
MAX_PACKAGE_LENGTH = 214
|
|
MAX_RANGE_LENGTH = 256
|
|
MAX_RATIONALE_LENGTH = 500
|
|
MAX_SCANNER_DIAGNOSTIC_LENGTH = 300
|
|
SEVERITIES = frozenset({"low", "moderate", "high", "critical"})
|
|
ISO_DATE = re.compile(r"\d{4}-\d{2}-\d{2}")
|
|
PACKAGE_NAME = re.compile(
|
|
r"(?:@[a-z0-9][a-z0-9._~-]*/)?[a-z0-9][a-z0-9._~-]*",
|
|
re.IGNORECASE,
|
|
)
|
|
ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*m")
|
|
EXPECTED_BUN_BANNER = re.compile(r"bun audit v1\.3\.14 \([0-9a-f]{8}\)")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Advisory:
|
|
advisory_id: str
|
|
package: str
|
|
severity: str
|
|
vulnerable_range: str
|
|
|
|
@property
|
|
def identity(self) -> tuple[str, str]:
|
|
return (self.advisory_id, self.package)
|
|
|
|
@property
|
|
def label(self) -> str:
|
|
return f"{self.advisory_id} ({self.package})"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Acceptance:
|
|
advisory: Advisory
|
|
expires_on: date
|
|
rationale: str
|
|
|
|
|
|
def _load_json(path: Path) -> Any:
|
|
try:
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
except (OSError, json.JSONDecodeError) as error:
|
|
raise ValueError(f"cannot read valid JSON from {path}: {error}") from error
|
|
|
|
|
|
def _parse_iso_date(value: Any, field: str) -> date:
|
|
if not isinstance(value, str) or ISO_DATE.fullmatch(value) is None:
|
|
raise ValueError(f"{field} must be an ISO date in YYYY-MM-DD form")
|
|
try:
|
|
parsed = date.fromisoformat(value)
|
|
except ValueError as error:
|
|
raise ValueError(f"{field} must be an ISO date in YYYY-MM-DD form") from error
|
|
if parsed.isoformat() != value:
|
|
raise ValueError(f"{field} must be an ISO date in YYYY-MM-DD form")
|
|
return parsed
|
|
|
|
def _bounded_string(
|
|
value: Any,
|
|
*,
|
|
field: str,
|
|
maximum: int,
|
|
pattern: re.Pattern[str] | None = None,
|
|
) -> str:
|
|
if (
|
|
not isinstance(value, str)
|
|
or not value
|
|
or value != value.strip()
|
|
or len(value) > maximum
|
|
or any(ord(character) < 32 for character in value)
|
|
or (pattern is not None and pattern.fullmatch(value) is None)
|
|
):
|
|
raise ValueError(f"{field} is invalid")
|
|
return value
|
|
|
|
|
|
def load_baseline(path: Path) -> tuple[date, dict[tuple[str, str], Acceptance]]:
|
|
payload = _load_json(path)
|
|
if not isinstance(payload, dict) or set(payload) != {
|
|
"schemaVersion",
|
|
"reviewedOn",
|
|
"accepted",
|
|
}:
|
|
raise ValueError("baseline must contain schemaVersion, reviewedOn, and accepted")
|
|
if payload["schemaVersion"] != 1:
|
|
raise ValueError("unsupported baseline schemaVersion")
|
|
reviewed_on = _parse_iso_date(payload["reviewedOn"], "reviewedOn")
|
|
entries = payload["accepted"]
|
|
if not isinstance(entries, list):
|
|
raise ValueError("baseline accepted must be a list")
|
|
|
|
accepted: dict[tuple[str, str], Acceptance] = {}
|
|
required = {
|
|
"id",
|
|
"package",
|
|
"severity",
|
|
"vulnerableRange",
|
|
"expiresOn",
|
|
"rationale",
|
|
}
|
|
for index, entry in enumerate(entries):
|
|
if not isinstance(entry, dict) or set(entry) != required:
|
|
raise ValueError(f"baseline accepted[{index}] has an unknown schema")
|
|
if not isinstance(entry["id"], (str, int)) or isinstance(entry["id"], bool):
|
|
raise ValueError(f"baseline accepted[{index}] has an invalid advisory id")
|
|
advisory_id = _bounded_string(
|
|
str(entry["id"]),
|
|
field=f"baseline accepted[{index}].id",
|
|
maximum=MAX_ID_LENGTH,
|
|
pattern=re.compile(r"[A-Za-z0-9][A-Za-z0-9._:-]*"),
|
|
)
|
|
package = _bounded_string(
|
|
entry["package"],
|
|
field=f"baseline accepted[{index}].package",
|
|
maximum=MAX_PACKAGE_LENGTH,
|
|
pattern=PACKAGE_NAME,
|
|
)
|
|
severity = _bounded_string(
|
|
entry["severity"],
|
|
field=f"baseline accepted[{index}].severity",
|
|
maximum=16,
|
|
)
|
|
if severity not in SEVERITIES:
|
|
raise ValueError(f"baseline accepted[{index}].severity is invalid")
|
|
vulnerable_range = _bounded_string(
|
|
entry["vulnerableRange"],
|
|
field=f"baseline accepted[{index}].vulnerableRange",
|
|
maximum=MAX_RANGE_LENGTH,
|
|
)
|
|
rationale = _bounded_string(
|
|
entry["rationale"],
|
|
field=f"baseline accepted[{index}].rationale",
|
|
maximum=MAX_RATIONALE_LENGTH,
|
|
)
|
|
advisory = Advisory(
|
|
advisory_id=advisory_id,
|
|
package=package,
|
|
severity=severity,
|
|
vulnerable_range=vulnerable_range,
|
|
)
|
|
expires_on = _parse_iso_date(entry["expiresOn"], f"accepted[{index}].expiresOn")
|
|
if expires_on < reviewed_on:
|
|
raise ValueError(f"baseline {advisory.label} expires before its review date")
|
|
if expires_on > reviewed_on + timedelta(days=MAX_ACCEPTANCE_DAYS):
|
|
raise ValueError(
|
|
f"baseline {advisory.label} expires more than {MAX_ACCEPTANCE_DAYS} days after review"
|
|
)
|
|
if advisory.identity in accepted:
|
|
raise ValueError(f"duplicate baseline advisory {advisory.label}")
|
|
accepted[advisory.identity] = Acceptance(
|
|
advisory=advisory,
|
|
expires_on=expires_on,
|
|
rationale=rationale,
|
|
)
|
|
return reviewed_on, accepted
|
|
|
|
|
|
def parse_audit_json(output: str) -> dict[tuple[str, str], Advisory]:
|
|
if len(output.encode("utf-8")) > MAX_AUDIT_OUTPUT_BYTES:
|
|
raise ValueError("bun audit result exceeds the policy size limit")
|
|
try:
|
|
payload = json.loads(output)
|
|
except json.JSONDecodeError as error:
|
|
raise ValueError(f"bun audit returned malformed JSON: {error.msg}") from error
|
|
if not isinstance(payload, dict):
|
|
raise ValueError("bun audit result must be a package object")
|
|
|
|
advisories: dict[tuple[str, str], Advisory] = {}
|
|
for package_value, package_entries in payload.items():
|
|
package = _bounded_string(
|
|
package_value,
|
|
field="bun audit package name",
|
|
maximum=MAX_PACKAGE_LENGTH,
|
|
pattern=PACKAGE_NAME,
|
|
)
|
|
if not isinstance(package_entries, list) or not package_entries:
|
|
raise ValueError(f"bun audit entries for {package} must be a non-empty list")
|
|
for index, entry in enumerate(package_entries):
|
|
if not isinstance(entry, dict):
|
|
raise ValueError(f"bun audit entry {package}[{index}] must be an object")
|
|
required = ("id", "severity", "vulnerable_versions")
|
|
if any(field not in entry for field in required):
|
|
raise ValueError(f"bun audit entry {package}[{index}] has an unknown schema")
|
|
if not isinstance(entry["id"], (str, int)) or isinstance(entry["id"], bool):
|
|
raise ValueError(f"bun audit entry {package}[{index}] has an invalid id")
|
|
advisory_id = _bounded_string(
|
|
str(entry["id"]),
|
|
field=f"bun audit entry {package}[{index}].id",
|
|
maximum=MAX_ID_LENGTH,
|
|
pattern=re.compile(r"[A-Za-z0-9][A-Za-z0-9._:-]*"),
|
|
)
|
|
severity = _bounded_string(
|
|
entry["severity"],
|
|
field=f"bun audit entry {package}[{index}].severity",
|
|
maximum=16,
|
|
)
|
|
if severity not in SEVERITIES:
|
|
raise ValueError(f"bun audit entry {package}[{index}].severity is invalid")
|
|
vulnerable_range = _bounded_string(
|
|
entry["vulnerable_versions"],
|
|
field=f"bun audit entry {package}[{index}].vulnerable_versions",
|
|
maximum=MAX_RANGE_LENGTH,
|
|
)
|
|
advisory = Advisory(
|
|
advisory_id=advisory_id,
|
|
package=package,
|
|
severity=severity,
|
|
vulnerable_range=vulnerable_range,
|
|
)
|
|
if advisory.identity in advisories:
|
|
raise ValueError(f"bun audit returned duplicate advisory {advisory.label}")
|
|
advisories[advisory.identity] = advisory
|
|
return advisories
|
|
|
|
|
|
def _has_scanner_diagnostic(stderr: str) -> bool:
|
|
rendered = ANSI_ESCAPE.sub("", stderr).strip()
|
|
return bool(rendered) and EXPECTED_BUN_BANNER.fullmatch(rendered) is None
|
|
|
|
|
|
def evaluate(
|
|
*,
|
|
exit_code: int,
|
|
output: str,
|
|
accepted: dict[tuple[str, str], Acceptance],
|
|
today: date,
|
|
stderr: str = "",
|
|
) -> list[str]:
|
|
if exit_code not in (0, 1):
|
|
return [f"bun audit execution failed with exit code {exit_code}"]
|
|
if _has_scanner_diagnostic(stderr):
|
|
return ["bun audit reported a scanner or network diagnostic"]
|
|
try:
|
|
current = parse_audit_json(output)
|
|
except (UnicodeError, ValueError) as error:
|
|
return [str(error)]
|
|
if exit_code == 0 and current:
|
|
return ["bun audit exited cleanly but returned advisories"]
|
|
if exit_code == 1 and not current:
|
|
return ["bun audit exited with advisories but returned an empty result"]
|
|
|
|
errors = []
|
|
for identity, advisory in sorted(current.items()):
|
|
acceptance = accepted.get(identity)
|
|
if acceptance is None:
|
|
errors.append(f"unaccepted advisory {advisory.label}")
|
|
continue
|
|
if acceptance.expires_on < today:
|
|
errors.append(
|
|
f"expired acceptance {advisory.label} ({acceptance.expires_on.isoformat()})"
|
|
)
|
|
if acceptance.advisory.severity != advisory.severity:
|
|
errors.append(
|
|
f"severity changed for {advisory.label}: "
|
|
f"{acceptance.advisory.severity} -> {advisory.severity}"
|
|
)
|
|
if acceptance.advisory.vulnerable_range != advisory.vulnerable_range:
|
|
errors.append(
|
|
f"vulnerable range changed for {advisory.label}: "
|
|
f"{acceptance.advisory.vulnerable_range} -> {advisory.vulnerable_range}"
|
|
)
|
|
for identity, acceptance in sorted(accepted.items()):
|
|
if identity not in current:
|
|
errors.append(f"stale baseline advisory {acceptance.advisory.label}")
|
|
if len(errors) > MAX_DIAGNOSTICS:
|
|
omitted = len(errors) - MAX_DIAGNOSTICS
|
|
return errors[:MAX_DIAGNOSTICS] + [
|
|
f"{omitted} additional policy error(s) omitted"
|
|
]
|
|
return errors
|
|
|
|
|
|
def _run_bun_audit(project: Path) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
["bun", "audit", "--json"],
|
|
cwd=project,
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
def _scanner_diagnostic(value: str) -> str:
|
|
compact = " ".join(value.split())
|
|
if len(compact) > MAX_SCANNER_DIAGNOSTIC_LENGTH:
|
|
return f"{compact[:MAX_SCANNER_DIAGNOSTIC_LENGTH]}..."
|
|
return compact
|
|
|
|
|
|
def main(
|
|
argv: list[str] | None = None,
|
|
*,
|
|
run_audit: Callable[[Path], subprocess.CompletedProcess[str]] = _run_bun_audit,
|
|
today: date | None = None,
|
|
) -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--project", type=Path, required=True)
|
|
parser.add_argument("--baseline", type=Path, required=True)
|
|
args = parser.parse_args(argv)
|
|
evaluation_date = date.today() if today is None else today
|
|
|
|
project = args.project.resolve()
|
|
baseline_path = args.baseline
|
|
if not baseline_path.is_absolute():
|
|
baseline_path = project / baseline_path
|
|
if not (project / "bun.lock").is_file():
|
|
print(f"ERROR: missing Bun lockfile in {project}", file=sys.stderr)
|
|
return 1
|
|
try:
|
|
reviewed_on, accepted = load_baseline(baseline_path)
|
|
if reviewed_on > evaluation_date:
|
|
raise ValueError("baseline reviewedOn cannot be in the future")
|
|
except ValueError as error:
|
|
print(f"ERROR: {error}", file=sys.stderr)
|
|
return 1
|
|
|
|
try:
|
|
result = run_audit(project)
|
|
except (OSError, subprocess.SubprocessError) as error:
|
|
print(f"ERROR: cannot execute bun audit: {_scanner_diagnostic(str(error))}", file=sys.stderr)
|
|
return 1
|
|
errors = evaluate(
|
|
exit_code=result.returncode,
|
|
output=result.stdout,
|
|
stderr=result.stderr,
|
|
accepted=accepted,
|
|
today=evaluation_date,
|
|
)
|
|
if errors:
|
|
for error in errors:
|
|
print(f"ERROR: {error}", file=sys.stderr)
|
|
if result.stderr.strip():
|
|
print(
|
|
f"ERROR: bun audit diagnostic: {_scanner_diagnostic(result.stderr)}",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
print(f"Bun advisory policy passed ({len(accepted)} reviewed acceptance(s)).")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|