perf(kvb-check-security): cache findings and bound scan workers

security.py caches findings for 24 h, keyed by content, mode, the medusa binary, PATH and MEDUSA_ variables, in an owner-only directory, and runs medusa with min(files, 4) workers. Committed below the bar on the operator's word 2026-11-12 (one gate after the merge): test_security 14 green, the before-and-after measurement never ran; the GOMAXPROCS cap stays unstaged.

🎯 Fix: 70 %
💥 Break: 10 %
💸 Cost: 100 %
⏱️ Time: 100 %
🛡️ Security: 95 %
🏆 Score: 60
🚦 Gate: ⛔ fix 70 % < 90, break 10 % > 5; security 95 % < 100: ask the operator before the commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-12 16:18:08 +00:00
1 parent fd8fc08603
commit 6540e4e4f1
3 files changed
+156 -9

No files matched your search

+5 -2
View File
@@ -31,12 +31,15 @@ python3 ${CLAUDE_SKILL_DIR}/scripts/security.py --repo <root> --base <ref>
It copies the files changed since `<ref>`, untracked ones included and
symlinks left out, to a temporary tree, scans it once with
`medusa scan <tree> --format json -o <dir> --no-cache` and prints one line:
`medusa scan <tree> --format json -o <dir> --no-cache --workers <w>` (w at
most `WORKERS` of the script) and prints one line:
`🛡️ security <n> % (medusa: <k> findings in <m> files: <severity> <path>:<line>, ...)`.
`<n>` is 100 minus 50 per critical, 25 per high, 10 per medium and 2 per low
finding, at least 0. It shows locations and counts only, never a matched
value. Pass `<n>` as `--security` and the critical count as `--critical`
(`--json` holds it) to `score.py`.
(`--json` holds it) to `score.py`. A rescan of byte-identical files with the
same medusa reuses the findings in `<council root>/security-cache/` for
`CACHE_SECONDS` instead of running medusa again.
- Exit 0: 100 %. Exit 3: below 100 without a critical finding; the score
gate asks the operator. Exit 1: a critical finding; the score gate forbids
+97 -6
View File
@@ -15,11 +15,22 @@ Args:
Scan:
The regular files `git diff <ref>` names plus untracked ones, symlinks left
out, copied to a temporary tree and scanned once with
`medusa scan <tree> --format json -o <dir> --no-cache`, HOME pointing into
the temporary directory because medusa creates ~/.medusa on start, read from
`medusa scan <tree> --format json -o <dir> --no-cache --workers <w>`, w the
files scanned but at most WORKERS, HOME pointing into the temporary
directory because medusa creates ~/.medusa on start, read from
`<dir>/medusa-scan-*.json`: a `findings` list whose entries carry
`severity` (critical, high, medium, low or info), `rule_id`, `file` and `line`.
Cache:
The findings of a scan, keyed by the sha256 of this script, the resolved
medusa binary with its mtime and size, PATH, every MEDUSA_* variable and
the path, mode and content sha256 of every scanned file, in
<council root>/security-cache/<key>.json for CACHE_SECONDS. A later scan
with the same key reads them instead of running medusa; any edit of a
scanned file, of medusa or of this script misses. The folder is used only
when it is a directory of the current user that neither group nor others
may write, an entry only when it is a regular file of the current user.
Accepted:
One JSON row per reviewed false positive (id, rule, path, sha1 of the stripped
source line, or of the whole file for a finding without a line, context: the
@@ -58,9 +69,11 @@ import json
import os
import re
import shutil
import stat
import subprocess
import sys
import tempfile
import time
from collections import Counter
from pathlib import Path
@@ -68,6 +81,14 @@ WEIGHTS = {"critical": 50, "high": 25, "medium": 10, "low": 2, "info": 0}
SAFE = 100
SHOWN = 5
REACH = 2
WORKERS = 4
CACHE = "security-cache"
CACHE_SECONDS = 24 * 3600
NOTHING = type(None)
SHAPE = {
"severity": (str,), "rule": (str, NOTHING), "path": (str,), "line": (int, NOTHING),
"sha1": (str, NOTHING), "context": (str, NOTHING),
}
SHA1 = re.compile(r"[0-9a-f]{40}")
WATCH = Path(__file__).resolve().parents[2] / "kvb-org-organize" / "scripts" / "watch.py"
HAND = (
@@ -250,7 +271,69 @@ def findings(reports: Path, tree: Path) -> list[dict]:
return sorted(found, key=lambda f: (rank.index(f["severity"]), f["path"], f["line"] or 0))
def scan(medusa: str, root: Path, base: str) -> tuple[int, list[dict]]:
def keyed(medusa: str, tree: Path) -> str:
binary = Path(medusa).resolve()
info = binary.stat()
key = hashlib.sha256(Path(__file__).read_bytes())
key.update(f"\0{binary}\0{info.st_mtime_ns}\0{info.st_size}\0{os.environ.get('PATH', '')}\0".encode())
key.update("".join(f"{k}={v}\0" for k, v in sorted(os.environ.items()) if k.startswith("MEDUSA_")).encode())
for path in sorted(p for p in tree.rglob("*") if p.is_file()):
key.update(f"{path.relative_to(tree).as_posix()}\0{path.stat().st_mode & 0o777:o}\0".encode())
key.update(hashlib.sha256(path.read_bytes()).digest())
return key.hexdigest()
def shelf(root: Path) -> Path | None:
folder = root / CACHE
try:
folder.mkdir(mode=0o700, parents=True, exist_ok=True)
info = folder.lstat()
except OSError:
return None
if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o022:
return None
return folder
def recalled(folder: Path, key: str) -> list[dict] | None:
entry = folder / f"{key}.json"
try:
info = entry.lstat()
if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or time.time() - info.st_mtime > CACHE_SECONDS:
return None
found = json.loads(entry.read_text(encoding="utf-8"))
except (OSError, ValueError):
return None
if not isinstance(found, list) or not all(
isinstance(f, dict) and f.keys() == SHAPE.keys() and all(isinstance(f[k], t) for k, t in SHAPE.items())
and f["severity"] in WEIGHTS
for f in found
):
return None
return found
def remember(folder: Path, key: str, found: list[dict]) -> None:
now = time.time()
for old in folder.glob("*.json"):
try:
if now - old.lstat().st_mtime > CACHE_SECONDS:
old.unlink()
except OSError:
continue
try:
descriptor, name = tempfile.mkstemp(dir=folder, prefix=".", suffix=".tmp")
except OSError:
return
try:
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
json.dump(found, handle)
os.replace(name, folder / f"{key}.json")
except OSError:
Path(name).unlink(missing_ok=True)
def scan(medusa: str, root: Path, base: str, cache: Path | None) -> tuple[int, list[dict]]:
with tempfile.TemporaryDirectory(prefix="kvb-security-") as scratch:
work = Path(scratch)
tree, reports, home = work / "tree", work / "reports", work / "home"
@@ -259,15 +342,23 @@ def scan(medusa: str, root: Path, base: str) -> tuple[int, list[dict]]:
scanned = copy(root, changed(root, base), tree)
if not scanned:
return 0, []
key = keyed(medusa, tree)
hit = recalled(cache, key) if cache else None
if hit is not None:
return scanned, hit
result = subprocess.run(
[medusa, "scan", str(tree), "--format", "json", "-o", str(reports), "--no-cache"],
[medusa, "scan", str(tree), "--format", "json", "-o", str(reports), "--no-cache",
"--workers", str(min(scanned, WORKERS))],
cwd=work, stdin=subprocess.DEVNULL, capture_output=True, check=False,
env={**os.environ, "HOME": str(home)},
)
try:
return scanned, findings(reports, tree)
found = findings(reports, tree)
except Unreadable as error:
raise Unreadable(f"{error} (medusa exit {result.returncode})") from None
if cache:
remember(cache, key, found)
return scanned, found
def plural(n: int, noun: str) -> str:
@@ -296,7 +387,7 @@ def main() -> int:
problem = None if medusa else "medusa missing"
if medusa:
try:
scanned, every = scan(medusa, root, args.base)
scanned, every = scan(medusa, root, args.base, shelf(council.ROOT))
except Unreadable as error:
problem = f"medusa report unreadable: {error}"
if problem:
+54 -1
View File
@@ -95,7 +95,9 @@ class TestSecurity(unittest.TestCase):
)
self.assertEqual(result.returncode, 3, result.stderr)
argv = json.loads((self.bin / "argv.json").read_text(encoding="utf-8"))
self.assertEqual([argv[0], *argv[2:4], argv[4], argv[6]], ["scan", "--format", "json", "-o", "--no-cache"])
self.assertEqual(
[argv[0], *argv[2:4], argv[4], *argv[6:]], ["scan", "--format", "json", "-o", "--no-cache", "--workers", "2"]
)
result = self.scan("--json", env=self.env)
self.assertNotIn(PLACEHOLDER, result.stdout + result.stderr)
found = json.loads(result.stdout)
@@ -258,6 +260,57 @@ class TestSecurity(unittest.TestCase):
self.assertEqual(result.returncode, 4)
self.assertRegex(result.stdout, r"^🛡️ security – \(medusa report unreadable: .*holds no findings list")
def test_a_rescan_of_unchanged_files_reads_the_cache_and_any_edit_runs_medusa_again(self):
self.write("app.py", "print(1)\nx = 1 # MARK-HIGH\n")
argv = self.bin / "argv.json"
first = self.scan("--json", env=self.env)
self.assertTrue(argv.exists())
argv.unlink()
again = self.scan("--json", env=self.env)
self.assertEqual((again.stdout, again.returncode), (first.stdout, first.returncode))
self.assertFalse(argv.exists())
self.assertEqual(len(list((self.base / "council" / "security-cache").glob("*.json"))), 1)
self.write("app.py", "print(1)\nx = 2 # MARK-HIGH\n")
edited = self.scan("--json", env=self.env)
self.assertTrue(argv.exists())
self.assertNotEqual(json.loads(edited.stdout)["findings"][0]["sha1"], json.loads(first.stdout)["findings"][0]["sha1"])
argv.unlink()
(self.repo / "app.py").chmod(0o755)
self.scan(env=self.env)
self.assertTrue(argv.exists())
argv.unlink()
medusa = self.bin / "medusa"
os.utime(medusa, ns=(medusa.stat().st_atime_ns, medusa.stat().st_mtime_ns + 1))
self.scan(env=self.env)
self.assertTrue(argv.exists())
argv.unlink()
self.scan(env={**self.env, "MEDUSA_TRACE_RULES": "1"})
self.assertTrue(argv.exists())
def test_an_expired_tampered_or_shared_cache_runs_medusa(self):
self.write("app.py", "print(2)\n")
argv = self.bin / "argv.json"
folder = self.base / "council" / "security-cache"
self.scan(env=self.env)
[entry] = folder.glob("*.json")
os.utime(entry, (0, 0))
argv.unlink()
self.scan(env=self.env)
self.assertTrue(argv.exists())
self.assertEqual(list(folder.glob("*.json")), [entry])
self.assertGreater(entry.stat().st_mtime, 0)
entry.write_text('[{"severity": "none"}]', encoding="utf-8")
argv.unlink()
result = self.scan(env=self.env)
self.assertTrue(argv.exists())
self.assertEqual(result.stdout, "🛡️ security 100 % (medusa: 0 findings in 1 file)\n")
folder.chmod(0o777)
entry.unlink()
argv.unlink()
self.scan(env=self.env)
self.assertTrue(argv.exists())
self.assertEqual(list(folder.glob("*.json")), [])
def test_usage_errors_exit_2(self):
result = subprocess.run(
[sys.executable, str(SECURITY), "--repo", str(self.repo), "--base", "nope"],