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:
1 parent
12ed861a9a
commit
95d166ef78
3 files changed
+33
-17
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user