feat(kvb-check-security): accept takes every rule of a line; acceptance only after kvb-think-criticize

MEDUSA often fires several rules on one line (14 INF-SCAN rules on one tempfile line), so security.py accept now takes --rule repeatedly and appends one row per rule, same id formula, in one locked write; a single --rule works as before. The skill now allows an acceptance only once kvb-think-criticize judged the finding a false positive or by design, per the operator's order of 2026-11-05.

🎯 Fix: 96 %
💥 Break: 2 %
💸 Cost: 100 %
⏱️ Time: 100 %
🛡️ Security: 100 %
🏆 Score: 94
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-06 13:26:59 +00:00
1 parent 12ed861a9a
commit 95d166ef78
3 files changed
+33 -17

No files matched your search

+6 -6
View File
@@ -47,12 +47,12 @@ value. Pass `<n>` as `--security` and the critical count as `--critical`
`--security '~<n>'` (quoted: zsh expands a bare `~100` as a user's home) and the critical findings you found as `--critical`.
- Exit 2: usage error.
- **Accepted false positives**: `security.py accept --repo <root> --rule
<id> --path <path> --sha1 <sha1> --reason "<one line>"`, rule, path and
sha1 from `--json`, appends a row outside the repository: to the council
root, which the `kvb-org-organize` watch hook keeps in
`~/.local/state/agents-council/`. A match leaves the score and shows as
`<a> accepted`; editing the line brings it back. Accept only after reading
the rule's pattern, and name each acceptance in the commit message.
<id> [--rule <id> ...] --path <path> --sha1 <sha1> --reason "<one line>"`
(from `--json`) appends a row per rule to the council root, which the
`kvb-org-organize` watch hook mirrors to `~/.local/state/agents-council/`.
A match leaves the score as `<a> accepted` until the line changes. Accept
only after `kvb-think-criticize` judged it a false positive or by design;
name each acceptance in the commit message.
## Rules
+15 -11
View File
@@ -6,9 +6,9 @@ Args:
--base: git ref the change starts from.
--json: print the figures as JSON instead of the line.
accept --repo <root> --rule <rule_id> --path <repo path> --sha1 <40 hex> --reason <one line>:
record a reviewed false positive instead; --json of a scan prints rule, path
and sha1 of every finding.
accept --repo <root> --rule <rule_id> [--rule ...] --path <repo path> --sha1 <40 hex> --reason <one line>:
record reviewed false positives of one line instead, a row per rule; --json
of a scan prints rule, path and sha1 of every finding.
Scan:
The regular files `git diff <ref>` names plus untracked ones, symlinks left
@@ -151,25 +151,29 @@ def accepted(listings: tuple[Path, ...]) -> set[tuple[str, str, str]]:
def accept(argv: list[str]) -> int:
parser = argparse.ArgumentParser(prog="security.py accept", description="Record a reviewed false positive.")
for flag in ("--repo", "--rule", "--path", "--sha1", "--reason"):
for flag in ("--repo", "--path", "--sha1", "--reason"):
parser.add_argument(flag, required=True)
parser.add_argument("--rule", action="append", required=True)
args = parser.parse_args(argv)
if not SHA1.fullmatch(args.sha1):
parser.error("--sha1 must be 40 lowercase hex digits")
if not args.reason.strip() or "\n" in args.reason:
parser.error("--reason must be one non-empty line")
root = git(Path(args.repo), "rev-parse", "--show-toplevel").strip()
key = f"{args.rule}\t{args.path}\t{args.sha1}"
row = {
"id": hashlib.sha1(key.encode()).hexdigest(), "rule": args.rule, "path": args.path, "sha1": args.sha1,
"reason": args.reason, "repo": root, "date": datetime.datetime.now(datetime.UTC).isoformat(timespec="seconds"),
}
date = datetime.datetime.now(datetime.UTC).isoformat(timespec="seconds")
rows = [
{
"id": hashlib.sha1(f"{rule}\t{args.path}\t{args.sha1}".encode()).hexdigest(), "rule": rule,
"path": args.path, "sha1": args.sha1, "reason": args.reason, "repo": root, "date": date,
}
for rule in args.rule
]
target = watch().ACCEPTED
target.parent.mkdir(parents=True, exist_ok=True)
with target.open("a", encoding="utf-8") as listing:
fcntl.flock(listing, fcntl.LOCK_EX)
listing.write(json.dumps(row) + "\n")
print(f"accepted {args.rule} {args.path} ({args.sha1[:12]}) in {target}")
listing.write("".join(json.dumps(row) + "\n" for row in rows))
print(f"accepted {', '.join(args.rule)} {args.path} ({args.sha1[:12]}) in {target}")
return 0
+12
View File
@@ -163,6 +163,18 @@ class TestSecurity(unittest.TestCase):
self.assertEqual(result.returncode, 2)
self.assertIn("--sha1 must be 40 lowercase hex digits", result.stderr)
def test_one_accept_takes_every_rule_of_a_line(self):
line = "z = both() # MARK-HIGH MARK-LOW"
self.write("app.py", f"{line}\n")
sha1 = hashlib.sha1(line.encode()).hexdigest()
result = self.accept(
"--rule", "FAKE-HIGH", "--rule", "FAKE-LOW", "--path", "app.py", "--sha1", sha1, "--reason", "fixture"
)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("accepted FAKE-HIGH, FAKE-LOW app.py", result.stdout)
result = self.scan(env=self.env)
self.assertEqual(result.stdout, "🛡️ security 100 % (medusa: 0 findings, 2 accepted in 1 file)\n")
def test_critical_exits_1_and_floors_at_0(self):
self.write("app.py", "a # MARK-CRITICAL\nb # MARK-CRITICAL\nc # MARK-CRITICAL\n")
result = self.scan(env=self.env)