baldart 5.8.0 → 5.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +30 -29
- package/VERSION +1 -1
- package/framework/.claude/agents/CHANGELOG.md +42 -0
- package/framework/.claude/agents/REGISTRY.md +4 -0
- package/framework/.claude/agents/code-simplifier.md +120 -0
- package/framework/.claude/agents/coder.md +3 -1
- package/framework/.claude/agents/security-reviewer.md +53 -1
- package/framework/.claude/skills/new/references/review-cycle.md +5 -6
- package/framework/.claude/skills/new/references/team-mode.md +1 -1
- package/framework/.claude/skills/simplify/CHANGELOG.md +7 -0
- package/framework/.claude/skills/simplify/SKILL.md +28 -8
- package/framework/.claude/skills/simplify/scripts/simplify-scan.mjs +234 -0
- package/framework/.claude/skills/simplify/scripts/simplify-scan.test.mjs +87 -0
- package/framework/.claude/workflows/new-card-review.js +5 -3
- package/framework/agents/index.md +4 -1
- package/framework/agents/security-review-protocol.md +233 -0
- package/framework/agents/security.md +50 -46
- package/framework/agents/simplify-protocol.md +280 -0
- package/package.json +1 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Security Review Protocol — the AppSec verification engine
|
|
2
|
+
|
|
3
|
+
**Purpose**: the security-specific passes that turn a generic code review into
|
|
4
|
+
an adversarial AppSec audit — repository-scoped threat modeling, the modern
|
|
5
|
+
high-miss discovery lens, class-specific proof tuples, structured attack-path →
|
|
6
|
+
mechanical severity, adversarial self-refutation (attacker vs victim), and a
|
|
7
|
+
coverage ledger so *"not observed" never reads as "not reviewed"*. Distilled
|
|
8
|
+
from the Codex `codex-security` workbench + the Claude Code `security-guidance`
|
|
9
|
+
agentic reviewer. This module is the **single SSOT** of that methodology.
|
|
10
|
+
|
|
11
|
+
**Relationship to `review-protocol.md`**: that module owns the GENERIC verifier
|
|
12
|
+
passes (Challenge, Actionability, Simulation, Chain-of-Verification, risk
|
|
13
|
+
scoring). This module **extends**, never duplicates them: `adversarial-refute`
|
|
14
|
+
is the security-specialized layer that runs *inside* the generic Challenge pass;
|
|
15
|
+
`proof-tuples` is the security grounding for the generic CoVe pass. On the
|
|
16
|
+
generic passes, `review-protocol.md` wins; on AppSec specifics, this module
|
|
17
|
+
wins.
|
|
18
|
+
|
|
19
|
+
**Consumer**: `security-reviewer` (body citations). Portable as-is across Claude
|
|
20
|
+
Code and Codex (read at runtime by both).
|
|
21
|
+
|
|
22
|
+
## Contract
|
|
23
|
+
|
|
24
|
+
- **Dispatch**: cite `agents/security-review-protocol.md SECTION=<threat-model|discovery-lens|proof-tuples|attack-path|adversarial-refute|coverage-ledger|boundary-gate>`.
|
|
25
|
+
Grep for `### SECTION: <name>` and Read ONLY that section.
|
|
26
|
+
- **Order of passes** (normative): `boundary-gate` (once, at kickoff) →
|
|
27
|
+
`threat-model` (adopt or generate) → discovery under the `discovery-lens` →
|
|
28
|
+
per candidate: `proof-tuples` (validation) → `attack-path` (structured facts)
|
|
29
|
+
→ `adversarial-refute` → mechanical severity (`review-protocol.md
|
|
30
|
+
SECTION=risk-scoring`) → `coverage-ledger` close. The generic Challenge + CoVe
|
|
31
|
+
passes still run; the security passes feed them.
|
|
32
|
+
- **Degrade-safe**: skipping a pass means less rigor, never a malformed report —
|
|
33
|
+
the report/YAML shape stays defined in the agent body.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### SECTION: boundary-gate
|
|
38
|
+
|
|
39
|
+
Run ONCE before confirming any finding. A vulnerability only exists when an
|
|
40
|
+
attacker crosses a trust boundary of a **shipped/deployed** surface — so
|
|
41
|
+
classify the surface first, or you will flag same-privilege local code as a
|
|
42
|
+
"vuln".
|
|
43
|
+
|
|
44
|
+
1. **Read the repo's own policy FIRST**: `SECURITY.md`, `AGENTS.md` deployment
|
|
45
|
+
notes, package metadata. They define which surfaces are in-scope and what the
|
|
46
|
+
supported threat model is. A finding that contradicts a documented,
|
|
47
|
+
intentional design is a policy question, not a confirmed vuln.
|
|
48
|
+
2. **Classify the surface under review**:
|
|
49
|
+
- `product_surface`: hosted service | library API | CLI | local dev UI |
|
|
50
|
+
MCP/tooling | example/demo | test | docs | generated | vendored.
|
|
51
|
+
- `source_trust` of the input: untrusted (remote/attacker) |
|
|
52
|
+
trusted_operator | trusted_developer_config | local_only |
|
|
53
|
+
intentional_code_execution.
|
|
54
|
+
- `boundary_crossed`: does attacker-controlled input reach the sink across a
|
|
55
|
+
real privilege/trust boundary? true | false | unknown.
|
|
56
|
+
3. **Gate**: no boundary crossed (attacker == victim, same privilege, local dev
|
|
57
|
+
tool with no request handler) → NOT a finding. Record the disposition in the
|
|
58
|
+
coverage ledger, do not emit it as LOW.
|
|
59
|
+
|
|
60
|
+
### SECTION: threat-model
|
|
61
|
+
|
|
62
|
+
Establish (or adopt) a **repository-scoped** threat model — deliberately NOT
|
|
63
|
+
biased by the current diff, so it stays valid for any change in the same repo.
|
|
64
|
+
|
|
65
|
+
- **Adopt** an existing model from the user, `SECURITY.md`, or `AGENTS.md` when
|
|
66
|
+
present. Only **generate** a fallback when none exists.
|
|
67
|
+
- Fallback generation, repository-level (not diff-scoped):
|
|
68
|
+
1. Real-world purpose + primary product/runtime surfaces actually exposed
|
|
69
|
+
(separate them from test/demo/tooling/docs paths).
|
|
70
|
+
2. Trust boundaries + the actors on each side; separate **attacker-controlled**
|
|
71
|
+
vs **operator-controlled** vs **developer-controlled** inputs explicitly.
|
|
72
|
+
3. Sensitive assets: user data, auth artifacts, authorization state,
|
|
73
|
+
secrets/keys, config, models/weights, audit logs, availability-critical
|
|
74
|
+
resources, tenant-isolation state.
|
|
75
|
+
4. Vulnerability CLASSES relevant to this repo's context (not the diff's
|
|
76
|
+
findings) + existing mitigations grounded in real files.
|
|
77
|
+
5. When realistic vs out-of-scope: state attacker (non-)capabilities so
|
|
78
|
+
severity is not inflated.
|
|
79
|
+
- Use it to pick **focus paths** for the review; do not let the diff recenter it
|
|
80
|
+
unless the user asks for a diff-scoped model.
|
|
81
|
+
|
|
82
|
+
### SECTION: discovery-lens
|
|
83
|
+
|
|
84
|
+
Beyond the OWASP core checklist (in the agent body), sweep for the **modern
|
|
85
|
+
high-miss patterns** — the ones a generic checklist walks past. For each changed
|
|
86
|
+
region, ask:
|
|
87
|
+
|
|
88
|
+
- **FAIL-OPEN state drift**: a default that *allows* on the unknown/error path —
|
|
89
|
+
catch-all `default: allow`, `unwrap_or({})`, a removed `finally` that left the
|
|
90
|
+
deny, an early-return before the check. Security must fail *closed*.
|
|
91
|
+
- **ALLOWLIST semantic escape**: a matcher weakened — a `||`/disjunction added, a
|
|
92
|
+
regex left unanchored (`^…$` missing), an allowlist checked by `startsWith`/
|
|
93
|
+
substring, a URL trusted by substring instead of parsed host.
|
|
94
|
+
- **CONTROL regression**: a deny-by-default replaced by a single positive
|
|
95
|
+
condition; an authz/confirm/audit control loosened or removed in the diff.
|
|
96
|
+
- **GATE / ACTION field mismatch**: the gate checks resource A (parent, id X) but
|
|
97
|
+
the action writes/reads resource B (child, id Y) — the check and the effect
|
|
98
|
+
disagree.
|
|
99
|
+
- **UNDER-VALIDATED sink arg**: user data flows into sink arg *X* while a sibling
|
|
100
|
+
path validates arg *Y* — asymmetry between siblings is the tell.
|
|
101
|
+
- **PARSER/VALIDATOR differential**: the validator and the consumer parse the
|
|
102
|
+
input differently (two URL parsers, JSON vs form, unicode/normalization) — the
|
|
103
|
+
gap is the exploit.
|
|
104
|
+
- **SENSITIVE-to-observability**: secrets/tokens/PII reaching logs, traces,
|
|
105
|
+
error responses, analytics, crash dumps.
|
|
106
|
+
- **CI/CD trust**: `pull_request_target` / `workflow_dispatch` / injectable
|
|
107
|
+
`${{ github.event.*.body }}` combined with secrets or repo-write; a workflow
|
|
108
|
+
running untrusted code with elevated tokens.
|
|
109
|
+
- **IaC omitted arg**: a Terraform/CDK/K8s resource missing a security flag
|
|
110
|
+
(public bucket, open SG, no encryption, over-broad IAM `*`).
|
|
111
|
+
- **OVER-BROAD grant**: an admin/`*` role/scope where a narrower one exists;
|
|
112
|
+
new IAM/RLS/role wider than the action needs.
|
|
113
|
+
- **STALE identity mapping**: a window between unregister/rotate and the new
|
|
114
|
+
binding where the old identity still resolves.
|
|
115
|
+
- **SECURITY-REGISTRY fanout**: a new entity type (model, scope, route, event)
|
|
116
|
+
added but missing from the sanitizer/allowlist/authz registries that must
|
|
117
|
+
enumerate it.
|
|
118
|
+
- **RESOURCE-bound placement**: a cap/limit on the wrong accumulator; an
|
|
119
|
+
under/overflow; a size check after the allocation.
|
|
120
|
+
|
|
121
|
+
### SECTION: proof-tuples
|
|
122
|
+
|
|
123
|
+
A security finding is a *hypothesis* until its **class-specific proof tuple** is
|
|
124
|
+
grounded against real code (this is the AppSec form of the CoVe pass). Confirm
|
|
125
|
+
every element by grep/read before emitting; drop the finding if any element is
|
|
126
|
+
missing.
|
|
127
|
+
|
|
128
|
+
- **authz / IDOR**: attacker reachability path + the missing/bypassed guard + the
|
|
129
|
+
protected object or state it exposes.
|
|
130
|
+
- **injection** (SQL/NoSQL/OS/LDAP/template): attacker-controlled bytes + the
|
|
131
|
+
sanitizer/parameterization result (or its absence) + the dangerous sink.
|
|
132
|
+
- **deserialization**: attacker serialized input + the unsafe loader + the
|
|
133
|
+
execution/instantiation effect.
|
|
134
|
+
- **SSRF / open redirect**: attacker-controlled destination + the control bypass
|
|
135
|
+
+ the network reach / side effect.
|
|
136
|
+
- **auth / token**: attacker-supplied value + the validator's exact semantics +
|
|
137
|
+
the mismatch with the trusted value.
|
|
138
|
+
- **XSS**: attacker string + the encoding/escaping context at output + the sink
|
|
139
|
+
(`innerHTML`/`dangerouslySetInnerHTML`/template) with no encode.
|
|
140
|
+
|
|
141
|
+
**Confidence anchored to validation METHOD, never to how scary the bug class
|
|
142
|
+
sounds**:
|
|
143
|
+
- reproduced PoC / failing test that triggers the sink → confidence 90–100.
|
|
144
|
+
- debugger/trace or a proven interface reproduction → 75–90.
|
|
145
|
+
- static source→sink trace fully grounded in read code → 60–80.
|
|
146
|
+
- code-understanding argument with a defensible but not fully-traced path →
|
|
147
|
+
30–55. Below that, it is a hypothesis — do not ship as HIGH.
|
|
148
|
+
|
|
149
|
+
**Instance-preserving**: do NOT collapse independently exploitable sibling
|
|
150
|
+
instances (`execute` / `executemany` / `executescript`; two routes with the same
|
|
151
|
+
flaw) into one finding. Each independently triggerable sink gets its own
|
|
152
|
+
finding/closure row.
|
|
153
|
+
|
|
154
|
+
### SECTION: attack-path
|
|
155
|
+
|
|
156
|
+
BEFORE assigning severity, write the attack path as **structured facts**, then
|
|
157
|
+
let severity fall out of them mechanically (`review-protocol.md
|
|
158
|
+
SECTION=risk-scoring`) — this stops severity from being argued emotionally.
|
|
159
|
+
|
|
160
|
+
- `attacker`: who, at what trust level, from where.
|
|
161
|
+
- `entrypoint`: the exact reachable entry (route/RPC/CLI/webhook/MCP tool).
|
|
162
|
+
- `preconditions`: conditions that must hold for the exploit (feature flag on,
|
|
163
|
+
authenticated user, specific config). A **precondition** narrows likelihood but
|
|
164
|
+
does NOT refute — keep it distinct from counterevidence (which kills the
|
|
165
|
+
finding, see `adversarial-refute`).
|
|
166
|
+
- `dataflow`: source → sink in one line.
|
|
167
|
+
- `outcome`: the concrete impact (data read/written, code executed, tenant
|
|
168
|
+
crossed).
|
|
169
|
+
- `impact` (1–5) and `likelihood` (1–5), each with one line of repo evidence.
|
|
170
|
+
|
|
171
|
+
Severity is then `review-protocol.md SECTION=risk-scoring` applied to
|
|
172
|
+
`impact × likelihood` — do not re-argue it afterward.
|
|
173
|
+
|
|
174
|
+
### SECTION: adversarial-refute
|
|
175
|
+
|
|
176
|
+
The security-specialized layer of the generic Challenge pass. Stance: **a finding
|
|
177
|
+
SURVIVES unless you produce concrete refuting evidence** — but the single most
|
|
178
|
+
common false positive is a **missing privilege boundary**, so test that first.
|
|
179
|
+
|
|
180
|
+
**Privilege-boundary test (BINDING)**: name the attacker (who controls the input)
|
|
181
|
+
and the victim (who is harmed). If attacker == victim operating on their own
|
|
182
|
+
machine / same-privilege process (a CLI arg in a same-trust process, a dev
|
|
183
|
+
script, a local tool with no remote entrypoint) → **REFUTE**. Keep it only when
|
|
184
|
+
the attacker is a lower-trust actor and the impact reaches other users, tenants,
|
|
185
|
+
or infrastructure.
|
|
186
|
+
|
|
187
|
+
**Refute the finding when**:
|
|
188
|
+
- it is **pre-existing** code untouched by the diff (in diff-scoped review, flag
|
|
189
|
+
only newly introduced or newly reachable surface; an off-diff finding must name
|
|
190
|
+
the specific `+`/`-` line that enables the sink).
|
|
191
|
+
- a sanitizer / validator / parameterization / authz check on the real path
|
|
192
|
+
already prevents the exploit.
|
|
193
|
+
- the sink is not actually dangerous here (typed-schema decoder, hardcoded
|
|
194
|
+
`https` URL, ORM/parameterized query, non-HTML context for an "XSS").
|
|
195
|
+
- a frontend-only gate is independently enforced by the backend (or vice-versa).
|
|
196
|
+
- validation is delegated to a caller/framework that provably runs it.
|
|
197
|
+
- it is throwaway/dev-only code (`scripts/`, `dev/`, `__main__`, fixtures, tests)
|
|
198
|
+
with no shipped surface.
|
|
199
|
+
- a dependency bump moved the control into the library (documented).
|
|
200
|
+
|
|
201
|
+
**Precondition vs counterevidence (keep precise)**: "deployment is internal-only"
|
|
202
|
+
is a *precondition* on the finding (still exploitable if it holds) → keep, lower
|
|
203
|
+
likelihood. "the code is test-only" is *counterevidence* → refute. Do not confuse
|
|
204
|
+
the two.
|
|
205
|
+
|
|
206
|
+
**Never-refute guard**: an item the agent's never-demote list marks (auth bypass,
|
|
207
|
+
RCE, secret exposure, cross-tenant breach with a grounded proof tuple) is never a
|
|
208
|
+
false positive — do not refute it however convincing the argument sounds.
|
|
209
|
+
|
|
210
|
+
Record refuted candidates (title + one-line refutation) so the audit trail shows
|
|
211
|
+
they were considered, not missed.
|
|
212
|
+
|
|
213
|
+
### SECTION: coverage-ledger
|
|
214
|
+
|
|
215
|
+
Make the review's *coverage* explicit so a clean verdict is honest and a gap is
|
|
216
|
+
never silent. For each security-relevant surface in scope, record a disposition:
|
|
217
|
+
|
|
218
|
+
- `reported` — a finding was emitted.
|
|
219
|
+
- `no_issue_found` — reviewed, no credible issue survived the passes.
|
|
220
|
+
- `not_applicable` — the class does not apply to this surface (with the reason).
|
|
221
|
+
- `needs_follow_up` — a real concern out of this review's scope/ownership
|
|
222
|
+
(surfaces as residual).
|
|
223
|
+
- `deferred` — could not complete (tool/access/time), with the reason + paths.
|
|
224
|
+
|
|
225
|
+
Rules:
|
|
226
|
+
- A **complete** review has zero `deferred` / `needs_follow_up` rows; otherwise
|
|
227
|
+
it is **partial** — say so. Zero findings on a fully-covered surface is a
|
|
228
|
+
legitimate, reportable outcome.
|
|
229
|
+
- A seeded obligation (a CVE/advisory/user pointer at a specific file/function)
|
|
230
|
+
stays OPEN until its exact row is closed as reported / not_applicable /
|
|
231
|
+
deferred — a neighboring same-family finding does not close it.
|
|
232
|
+
- In the pooled YAML mode, carry this as a compact `coverage:` block in the
|
|
233
|
+
`# Security Review Summary` header, not as fake LOW findings.
|
|
@@ -55,72 +55,76 @@ Document security requirements, threats, and mitigation strategies.
|
|
|
55
55
|
- [Sensitive data masking in logs]
|
|
56
56
|
- [Secrets management]
|
|
57
57
|
|
|
58
|
-
## Common Vulnerabilities (OWASP Top 10)
|
|
58
|
+
## Common Vulnerabilities (OWASP Top 10 — 2021)
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
> Modernized to the OWASP Top 10:2021 categories (the 2017 list — with XXE and
|
|
61
|
+
> "Sensitive Data Exposure" as standalone entries — is superseded). XXE now folds
|
|
62
|
+
> into Security Misconfiguration; insecure deserialization into Software & Data
|
|
63
|
+
> Integrity Failures; XSS into Injection. A 2025 revision is in draft — revisit
|
|
64
|
+
> when it is finalized (expected consolidation of supply-chain risks).
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
- Validate and sanitize inputs
|
|
64
|
-
- Implement least privilege database access
|
|
66
|
+
### A01 Broken Access Control
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
- Enforce authorization on every request, server-side; deny by default
|
|
69
|
+
- Prevent IDOR — never trust a user-supplied record/object identifier
|
|
70
|
+
- Enforce ownership + tenant isolation on every sensitive action
|
|
71
|
+
- Principle of least privilege; no privilege escalation via business logic
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
- Use strong password policies
|
|
70
|
-
- Implement account lockout
|
|
71
|
-
- Protect against brute force
|
|
73
|
+
### A02 Cryptographic Failures
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
- Encrypt sensitive data at rest and in transit (HTTPS/TLS everywhere)
|
|
76
|
+
- Use strong, modern algorithms (no MD5/SHA1 for security, no AES-ECB, no static IV)
|
|
77
|
+
- Proper key management + rotation; never log secrets or PII
|
|
78
|
+
- Strong password hashing (argon2/bcrypt/scrypt) with per-user salt
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
- Use HTTPS everywhere
|
|
77
|
-
- Don't log sensitive information
|
|
78
|
-
- Implement secure key management
|
|
80
|
+
### A03 Injection (incl. XSS)
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
- Parameterized queries / ORM; never build queries by string concatenation
|
|
83
|
+
- Validate + sanitize inputs; allowlists over denylists
|
|
84
|
+
- Least-privilege database access
|
|
85
|
+
- Output encoding at the right context + Content Security Policy for XSS
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
- Use safe XML parsers
|
|
84
|
-
- Validate XML inputs
|
|
87
|
+
### A04 Insecure Design
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
- Threat-model the feature before building; secure-by-default patterns
|
|
90
|
+
- Establish trust boundaries and enforce them; fail closed
|
|
91
|
+
- Rate limits, resource bounds, and abuse cases considered up front
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
- Validate permissions on every request
|
|
90
|
-
- Use principle of least privilege
|
|
93
|
+
### A05 Security Misconfiguration (incl. XXE)
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
- Harden defaults; remove unnecessary features/debug endpoints in prod
|
|
96
|
+
- Set security headers; keep software updated
|
|
97
|
+
- Disable XML external-entity processing; use safe parsers/validators
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
- Keep software updated
|
|
96
|
-
- Remove unnecessary features
|
|
97
|
-
- Implement security headers
|
|
99
|
+
### A06 Vulnerable & Outdated Components
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
- Keep dependencies patched; monitor security advisories (CVE/GHSA)
|
|
102
|
+
- Run dependency + vulnerability scanning in CI (e.g. audit / govulncheck)
|
|
103
|
+
- Remove unused dependencies and features
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
- Use Content Security Policy
|
|
103
|
-
- Validate and sanitize inputs
|
|
104
|
-
- Use framework protections
|
|
105
|
+
### A07 Identification & Authentication Failures
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
- Secure session management (rotation, expiry, secure cookies)
|
|
108
|
+
- Strong password policy, account lockout / brute-force protection, MFA where applicable
|
|
109
|
+
- No session fixation, no token leakage in URLs/logs
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
- Use safe deserialization libraries
|
|
110
|
-
- Implement integrity checks
|
|
111
|
+
### A08 Software & Data Integrity Failures (incl. insecure deserialization)
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
- Never deserialize untrusted data with unsafe loaders; validate + integrity-check
|
|
114
|
+
- Verify integrity of updates/CI artifacts (signatures, SRI, pinned digests)
|
|
115
|
+
- Guard the CI/CD supply chain — no untrusted code with elevated tokens
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
- Monitor security advisories
|
|
116
|
-
- Use dependency scanning tools
|
|
117
|
+
### A09 Security Logging & Monitoring Failures
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
- Log security events; monitor for suspicious activity; alert
|
|
120
|
+
- Protect log integrity; never log secrets/PII
|
|
121
|
+
- Ensure exploitation attempts are detectable and auditable
|
|
119
122
|
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
123
|
+
### A10 Server-Side Request Forgery (SSRF)
|
|
124
|
+
|
|
125
|
+
- Validate + allowlist outbound destinations; parse (don't substring) URLs
|
|
126
|
+
- Block internal metadata endpoints and private ranges
|
|
127
|
+
- Enforce egress controls; treat any user-supplied URL/host as hostile
|
|
124
128
|
|
|
125
129
|
## Rate Limiting
|
|
126
130
|
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Simplify Protocol — the reuse & simplicity verification engine
|
|
2
|
+
|
|
3
|
+
**Purpose**: the simplification-specific methodology that turns a generic code
|
|
4
|
+
pass into a ruthless anti-duplication / reuse-first / right-altitude audit — an
|
|
5
|
+
agentic-defect taxonomy grounded in empirical research, a canonical refactoring
|
|
6
|
+
vocabulary so findings are actionable, the clone-type vocabulary, a deterministic
|
|
7
|
+
clone-floor contract, an internal-documentation awareness contract for reuse
|
|
8
|
+
retrieval, and the judge-bias guards a *simplify* reviewer specifically needs
|
|
9
|
+
(a simplify judge that rewards verbosity is inverted). This module is the
|
|
10
|
+
**single SSOT** of that methodology.
|
|
11
|
+
|
|
12
|
+
**Relationship to `review-protocol.md`**: that module owns the GENERIC verifier
|
|
13
|
+
passes (Challenge, Actionability, Simulation, Chain-of-Verification, risk
|
|
14
|
+
scoring). This module **extends**, never duplicates them — it supplies the
|
|
15
|
+
simplify-specialized taxonomy, vocabulary and evidence rules that FEED those
|
|
16
|
+
generic passes. On the generic passes, `review-protocol.md` wins; on
|
|
17
|
+
simplification specifics, this module wins.
|
|
18
|
+
|
|
19
|
+
**Relationship to the `simplify` SKILL**: the skill owns the OPERATIONAL
|
|
20
|
+
workflow (identify diff → three-lens fan-out → aggregate → fix → verify). This
|
|
21
|
+
module owns the taxonomy/vocabulary/bias/doc-awareness the lenses apply. The
|
|
22
|
+
skill and the `code-simplifier` agent CITE this module; they do not restate it.
|
|
23
|
+
|
|
24
|
+
**Consumers**: the `code-simplifier` agent (body citations), the `simplify`
|
|
25
|
+
skill (Step 2), the `coder` author-time mirror (§ Author-Time Simplicity
|
|
26
|
+
Discipline). Portable as-is across Claude Code and Codex (read at runtime by
|
|
27
|
+
both).
|
|
28
|
+
|
|
29
|
+
**Empirical anchors** (why this exists): LLM-authored code carries a
|
|
30
|
+
duplication/inefficiency tax that generic linters miss — Abbassi et al.,
|
|
31
|
+
*A Taxonomy of Inefficiencies in LLM-Generated Python Code* (arXiv:2503.06327,
|
|
32
|
+
~90% of samples ≥1 inefficiency); Sonar, *The Coding Personalities of Leading
|
|
33
|
+
LLMs* (>90% of introduced issues are code smells). Self-review is unreliable —
|
|
34
|
+
Huang et al., *LLMs Cannot Self-Correct Reasoning Yet* (ICLR 2024,
|
|
35
|
+
arXiv:2310.01798); Xu et al., *Pride and Prejudice: LLM Amplifies Self-Bias in
|
|
36
|
+
Self-Refinement* (ACL 2024, arXiv:2402.11436). Hence: an INDEPENDENT reviewer,
|
|
37
|
+
grounded in EXTERNAL evidence (clone match / zero-caller proof / resolved
|
|
38
|
+
symbol), never the author's own opinion.
|
|
39
|
+
|
|
40
|
+
## Contract
|
|
41
|
+
|
|
42
|
+
- **Dispatch**: cite `agents/simplify-protocol.md SECTION=<taxonomy|canonical-vocab|clone-vocab|rubric|deterministic-tier|doc-awareness|bias-guards>`.
|
|
43
|
+
Grep for `### SECTION: <name>` and Read ONLY that section.
|
|
44
|
+
- **Order** (normative): `deterministic-tier` (anchored clone candidates, once,
|
|
45
|
+
before eyeballing) → retrieval under `doc-awareness` → per candidate the
|
|
46
|
+
`rubric` lens applies its `taxonomy` class + `canonical-vocab` name →
|
|
47
|
+
`clone-vocab` classifies duplicates → generic Challenge + CoVe
|
|
48
|
+
(`review-protocol.md`) → `bias-guards` neutralize the verdict.
|
|
49
|
+
- **Evidence rule (BINDING)**: every finding cites executed/retrieved evidence —
|
|
50
|
+
a clone match (`path:line` × 2), a zero-inbound-caller proof, a resolved-or-not
|
|
51
|
+
symbol, an existing-utility `path:line`. A finding whose only support is the
|
|
52
|
+
reviewer's unaided judgment is a HYPOTHESIS, not a finding — drop it or ground
|
|
53
|
+
it. This is the simplify twin of the anti-assertion-fitting rule: retrieve/run
|
|
54
|
+
first, judge second.
|
|
55
|
+
- **Degrade-safe**: skipping a pass means less rigor, never a malformed report —
|
|
56
|
+
the report/YAML shape stays defined in the agent/skill body.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### SECTION: taxonomy
|
|
61
|
+
|
|
62
|
+
The agentic-defect taxonomy. Each class carries the DETECTABLE SIGNAL a reviewer
|
|
63
|
+
or the deterministic tier keys on — never flag on vibes. Classes A1/A2/A4/A5 are
|
|
64
|
+
the highest-yield agentic-specific findings (generic linters miss them; the
|
|
65
|
+
research shows they are pervasive).
|
|
66
|
+
|
|
67
|
+
- **A1 — Reuse-miss** (does an equivalent already exist in the repo?). Signal:
|
|
68
|
+
a new function/class/hook whose name, signature, or body is ≥Type-3 similar to
|
|
69
|
+
an existing exported symbol elsewhere; a local helper shadowing a public util;
|
|
70
|
+
no import added from the module that already owns the capability. **Highest
|
|
71
|
+
agentic-specific value.** Retrieval-driven (§ doc-awareness).
|
|
72
|
+
- **A2 — Duplication / near-duplicate block**. Signal: identical token/line
|
|
73
|
+
sequence ≥ threshold in 2+ places (in the diff OR against the repo). The
|
|
74
|
+
deterministic tier (§ deterministic-tier) anchors these; classify per
|
|
75
|
+
§ clone-vocab.
|
|
76
|
+
- **A3 — Reinventing stdlib/native/framework capability**. Signal: hand-rolled
|
|
77
|
+
logic that is a one-liner in the language stdlib or an already-imported lib
|
|
78
|
+
(manual dedup vs `Set`, manual deep-clone vs `structuredClone`, manual date
|
|
79
|
+
math vs the date lib, manual debounce/groupBy/chunk). A new third-party
|
|
80
|
+
dependency for a stdlib-covered capability is A3 + an ADR trigger.
|
|
81
|
+
- **A4 — Over-engineering / premature generalization / wrong altitude**. Signal:
|
|
82
|
+
an abstraction (interface/factory/generic/config flag) with exactly ONE
|
|
83
|
+
caller/one implementation; parameters never varied; speculative extension
|
|
84
|
+
points with no consumer; the Rule-of-Three violated (abstraction minted for the
|
|
85
|
+
first or second occurrence). See § canonical-vocab (Speculative Generality,
|
|
86
|
+
AHA, YAGNI).
|
|
87
|
+
- **A5 — Dead / unused code left behind**. Signal: unused export/param/var/import;
|
|
88
|
+
unreachable branch; a function with zero inbound call-graph edges. **Advisory
|
|
89
|
+
by default** — dynamic dispatch / reflection / DI / side-effect imports produce
|
|
90
|
+
false positives; verify before deleting.
|
|
91
|
+
- **A6 — Pattern inconsistency vs the surrounding codebase** (cargo-cult
|
|
92
|
+
boilerplate). Signal: the new code uses a different idiom / error-handling /
|
|
93
|
+
naming / state pattern than the 2–3 nearest sibling files; boilerplate copied
|
|
94
|
+
but partially wired.
|
|
95
|
+
- **A7 — Unresolved/hallucinated API or package**. Signal: an import/call that
|
|
96
|
+
resolves to no real symbol/package (LSP "unresolved"); a method not on the
|
|
97
|
+
type's real surface. Cheap, high-value, security-adjacent (slopsquatting).
|
|
98
|
+
- **A8 — Missed optimization**. Signal: quadratic where linear exists; repeated
|
|
99
|
+
recompute instead of hoist/memo; N+1; independent ops run sequentially; new
|
|
100
|
+
blocking work on a startup/per-request/per-render hot path.
|
|
101
|
+
- **A9 — Complexity spike** (the umbrella smell). Signal: cyclomatic/cognitive
|
|
102
|
+
complexity, function length, nesting depth, or parameter count materially above
|
|
103
|
+
the surrounding code — see § canonical-vocab for the calibrated thresholds.
|
|
104
|
+
|
|
105
|
+
### SECTION: canonical-vocab
|
|
106
|
+
|
|
107
|
+
Map every finding to a NAMED refactoring concept so the fix is actionable and
|
|
108
|
+
the severity is calibrated, not invented. Cite the name in the finding.
|
|
109
|
+
|
|
110
|
+
**Fowler / refactoring.guru code smells** (`refactoring.com/catalog`):
|
|
111
|
+
- *Bloaters* → Long Method, Large Class, Long Parameter List, Data Clumps,
|
|
112
|
+
Primitive Obsession. (A9 / A4)
|
|
113
|
+
- *Dispensables* → **Duplicate Code** (A2), **Dead Code** (A5), **Speculative
|
|
114
|
+
Generality** (A4), Lazy Class, Comments-as-deodorant.
|
|
115
|
+
- *Couplers* → Feature Envy, Message Chains, Middle Man, Inappropriate Intimacy
|
|
116
|
+
(leaky boundaries — A4/A6).
|
|
117
|
+
|
|
118
|
+
**Four Rules of Simple Design** (Beck, `martinfowler.com/bliki/BeckDesignRules.html`),
|
|
119
|
+
priority order — a finding that violates a higher rule outranks a lower one:
|
|
120
|
+
1. Passes the tests. 2. Reveals intention. 3. **No duplication** (the simplify
|
|
121
|
+
core — A1/A2/A3). 4. Fewest elements (A4 — no speculative parts).
|
|
122
|
+
|
|
123
|
+
**Governing principles** (cite when scoping an abstraction finding):
|
|
124
|
+
- **Rule of Three** — do NOT abstract until the third occurrence; abstracting on
|
|
125
|
+
the first/second is A4. The mirror caveat: `code-reviewer` will not *demand*
|
|
126
|
+
abstraction for ≤3 repetitions either — the two agents agree at the boundary.
|
|
127
|
+
- **AHA** (*Avoid Hasty Abstractions*, Kent C. Dodds) / **"duplication is far
|
|
128
|
+
cheaper than the wrong abstraction"** (Sandi Metz) — prefer a little
|
|
129
|
+
duplication over a wrong/leaky abstraction. So an A2 duplicate is only worth
|
|
130
|
+
unifying when the shared shape is genuine, not coincidental.
|
|
131
|
+
- **YAGNI / KISS** — no extension point, config flag, or parameter without a
|
|
132
|
+
present consumer (A4).
|
|
133
|
+
|
|
134
|
+
**Calibrated complexity thresholds** (A9 — cite the number, never invent one;
|
|
135
|
+
these are surrounding-code-relative signals, not absolute mandates):
|
|
136
|
+
- Cyclomatic complexity per function **>10** warn / **>15** act (McCabe 1976;
|
|
137
|
+
NIST SP 500-235).
|
|
138
|
+
- Cognitive complexity per function **>15** (SonarSource S3776 default;
|
|
139
|
+
Campbell white paper — penalizes nesting/flow-breaks over raw branch count).
|
|
140
|
+
- Function length **>~50 LOC**, nesting depth **>4**, parameters **>3–4**
|
|
141
|
+
(ESLint `max-lines-per-function` / `max-depth` / `max-params` defaults).
|
|
142
|
+
Treat as *relative to the file's neighbours*; a spike vs siblings matters more
|
|
143
|
+
than the absolute number.
|
|
144
|
+
|
|
145
|
+
### SECTION: clone-vocab
|
|
146
|
+
|
|
147
|
+
Classify every A2 duplicate. The vocabulary sets the fix and who catches it.
|
|
148
|
+
|
|
149
|
+
- **Type-1** — identical modulo whitespace/comments. Deterministic tier catches
|
|
150
|
+
it. Fix: delete + reuse, or extract.
|
|
151
|
+
- **Type-2** — identical modulo renamed identifiers/literals/types. Deterministic
|
|
152
|
+
tier catches it only with identifier normalization (which the built-in floor
|
|
153
|
+
does NOT do — the reviewer catches these). Fix: extract parametrized shared.
|
|
154
|
+
- **Type-3** — copied-then-modified (added/removed/changed statements — the gap
|
|
155
|
+
case). Partially caught by the floor's line-window overlap; the reviewer
|
|
156
|
+
confirms. Fix: extract with parameters for the delta.
|
|
157
|
+
- **Type-4** — semantically equivalent, syntactically different. Structurally
|
|
158
|
+
invisible to any deterministic tier — **this is the reviewer's job**, backed by
|
|
159
|
+
retrieval (§ doc-awareness) and behavioural reasoning, never a hash.
|
|
160
|
+
|
|
161
|
+
Fix-classification (reused by the skill's aggregation): Exact duplicate → delete
|
|
162
|
+
new, use existing · Near duplicate → extract shared parametrized · Inline
|
|
163
|
+
reimplementation → replace with the existing utility · Dependency-reimplements-
|
|
164
|
+
native → native primitive + remove dep.
|
|
165
|
+
|
|
166
|
+
### SECTION: rubric
|
|
167
|
+
|
|
168
|
+
The lens checklists the operational pass applies. The `simplify` skill fans these
|
|
169
|
+
out as its three parallel lenses; the `code-simplifier` agent runs them as one
|
|
170
|
+
persona covering all lenses. Each lens tags findings with its `taxonomy` class.
|
|
171
|
+
|
|
172
|
+
- **Reuse lens** (A1/A2/A3) — the load-bearing, cross-codebase job the author
|
|
173
|
+
could not see from inside the task. Consume the deterministic tier's anchored
|
|
174
|
+
clone candidates FIRST (§ deterministic-tier), then retrieve reuse candidates
|
|
175
|
+
(§ doc-awareness), then judge Type-2/3/4 the tier missed. Classify per
|
|
176
|
+
§ clone-vocab.
|
|
177
|
+
- **Quality lens** — redundant state (derivable values, effects where a direct
|
|
178
|
+
call works), parameter sprawl, copy-paste-with-variation, leaky abstractions,
|
|
179
|
+
stringly-typed code, unnecessary nesting/wrappers, unnecessary comments
|
|
180
|
+
(WHAT/change-narration — keep only non-obvious WHY).
|
|
181
|
+
- **Design-altitude lens** (A4/A6 — the *design-defect* coverage) — single-caller
|
|
182
|
+
abstraction / Rule-of-Three violation / speculative generality / config flag
|
|
183
|
+
with no consumer (A4); pattern inconsistency vs the 2–3 nearest sibling files
|
|
184
|
+
(A6); leaky boundaries. **Advisory by default** — "wrong altitude" is the most
|
|
185
|
+
judgment-heavy class; a *simplify* pass that fabricates abstraction complaints
|
|
186
|
+
causes the churn it exists to prevent. Fold into the Quality lens (skill) /
|
|
187
|
+
cover as one persona (agent); do NOT spin a fourth parallel spawn.
|
|
188
|
+
- **Efficiency lens** (A8) — unnecessary work, missed concurrency, hot-path
|
|
189
|
+
bloat, recurring no-op updates (add change-detection guards), unnecessary
|
|
190
|
+
existence checks (TOCTOU), memory leaks, overly broad reads.
|
|
191
|
+
|
|
192
|
+
### SECTION: deterministic-tier
|
|
193
|
+
|
|
194
|
+
The anchored clone floor. Grounds the reviewer with an external signal it lacks
|
|
195
|
+
(the research: an LLM eyeballing a diff misses clones exactly as it misses its
|
|
196
|
+
own smells). Run BEFORE eyeballing; feed anchored candidates to the Reuse lens.
|
|
197
|
+
|
|
198
|
+
- **Tool**: `framework/.claude/skills/simplify/scripts/simplify-scan.mjs` — a
|
|
199
|
+
zero-dependency Node scanner (identical on Claude Code and Codex; the twin of
|
|
200
|
+
`ui-design/scripts/craft-check.mjs`). Rolling-hash near-duplicate detection
|
|
201
|
+
over `diff × repo`.
|
|
202
|
+
- **Contract** (what it MUST do to be signal, not noise): whitespace + comment
|
|
203
|
+
normalization; NO identifier normalization by default (that explodes
|
|
204
|
+
boilerplate false positives — Type-2 is the reviewer's job); minimum window
|
|
205
|
+
(~6 lines / ~50 tokens) below which everything matches; an ignore-list that
|
|
206
|
+
reuses the project's `.gitignore` + generated/vendored/lockfile/min/snapshot/
|
|
207
|
+
migration conventions; import-block and trivial-statement suppression. It emits
|
|
208
|
+
anchored candidates (`path:line` × the matching loci + clone type). It NEVER
|
|
209
|
+
edits and NEVER decides — it PROPOSES; the reviewer DISPOSES (verifies each
|
|
210
|
+
candidate, discards false positives, promotes real ones).
|
|
211
|
+
- **No external-tool tier in the floor** (deliberate, since v5.10.0): the floor
|
|
212
|
+
is the always-on, stack-agnostic capability. Richer external detectors (jscpd,
|
|
213
|
+
lizard, ast-grep, knip, vulture) are NOT wired here — they are 3-ecosystem
|
|
214
|
+
binaries whose silent presence would be non-deterministic capability drift, and
|
|
215
|
+
they break Codex portability. Should they ever be added, it is behind a
|
|
216
|
+
DEDICATED flag with full schema-change propagation + a documented Codex
|
|
217
|
+
fallback + a measured redundancy analysis vs this floor — never ambient
|
|
218
|
+
detection and never gated on `has_toolchain` (a proven false coupling: that
|
|
219
|
+
flag installs Biome/Vitest/tsc/Lefthook, none of these).
|
|
220
|
+
|
|
221
|
+
### SECTION: doc-awareness
|
|
222
|
+
|
|
223
|
+
Reuse-miss (A1) is a retrieval problem, and the highest-signal source in a
|
|
224
|
+
BALDART consumer is the internal documentation that catalogs what already exists.
|
|
225
|
+
The reviewer MUST consult it — but under a strict posture, because docs drift.
|
|
226
|
+
|
|
227
|
+
**Retrieve-then-verify (BINDING)**: internal docs are a HIGH-RECALL INDEX for
|
|
228
|
+
FINDING reuse candidates; **the code is the SSOT** and CONFIRMS the match. Never
|
|
229
|
+
decide a reuse-miss on the doc alone (a stale registry entry may point at dead
|
|
230
|
+
code); never feed a textual doc proxy where the ground-truth code is readable
|
|
231
|
+
(the anti-assertion-fitting rule). Consult, in order of signal:
|
|
232
|
+
|
|
233
|
+
1. **Component / utility registry** — `${paths.references_dir}/component-registry.md`
|
|
234
|
+
(the authoritative inventory of primitives, shared components, hooks, utility
|
|
235
|
+
modules). The #1 reuse map; scan it before grepping.
|
|
236
|
+
2. **Design system** — `${paths.design_system}/INDEX.md` + `components/<Name>.md`
|
|
237
|
+
(UI reuse + closed-set members) when `features.has_design_system: true`.
|
|
238
|
+
3. **Code graph** — Graphify `GRAPH_REPORT.md` (communities / god-node clusters)
|
|
239
|
+
+ `graphify query`/`affected` when `features.has_code_graph: true`.
|
|
240
|
+
4. **Symbol layer** — LSP `workspace/symbol` + `find-references` when
|
|
241
|
+
`features.has_lsp_layer: true`.
|
|
242
|
+
5. **API / data-model / schemas** — `${paths.references_dir}/api/`,
|
|
243
|
+
`data-model.md` — to catch a reinvented endpoint/type/validator.
|
|
244
|
+
6. **Wiki syntheses** — `${paths.wiki_dir}/` ("we already have a pattern X").
|
|
245
|
+
7. **ADRs** — `${paths.adrs_dir}/` — a dependency/abstraction with a deliberate
|
|
246
|
+
ADR is NOT a removal candidate; respect the recorded intent.
|
|
247
|
+
|
|
248
|
+
**Do NOT duplicate the retrieval protocols** — cite them: the search hierarchy is
|
|
249
|
+
`agents/code-search-protocol.md`, the graph queries `agents/code-graph-protocol.md`,
|
|
250
|
+
the registry-first cascade `agents/design-system-protocol.md`, path resolution
|
|
251
|
+
`agents/project-context.md`. Universal fallback is grep (silent, never abort).
|
|
252
|
+
|
|
253
|
+
**Doc-drift is a handoff, not a fix** (strict specialization): if the reviewer
|
|
254
|
+
finds the registry stale w.r.t. a symbol (a new util duplicating an existing one
|
|
255
|
+
that is not in the registry, or an entry pointing at dead code), it emits an
|
|
256
|
+
**advisory** finding and hands the doc reconciliation to `doc-reviewer` — it does
|
|
257
|
+
NOT rewrite the registry itself.
|
|
258
|
+
|
|
259
|
+
### SECTION: bias-guards
|
|
260
|
+
|
|
261
|
+
An LLM judge carries biases that are specifically corrosive to a *simplify* pass.
|
|
262
|
+
Neutralize them mechanically.
|
|
263
|
+
|
|
264
|
+
- **No verbosity reward (INVERTED for simplify)** — general LLM-judge verbosity
|
|
265
|
+
bias (MT-Bench; *Justice or Prejudice?* arXiv:2410.02736) rewards longer
|
|
266
|
+
answers. A simplify reviewer wants the SHORTER, fewer-elements solution; longer
|
|
267
|
+
code is a *negative* signal, never a positive. Never prefer a change because it
|
|
268
|
+
adds structure.
|
|
269
|
+
- **Independence (no self-grading)** — the reviewer is INDEPENDENT of the author:
|
|
270
|
+
fresh-context minimum, cross-model ideal. On a same-model run it is
|
|
271
|
+
fresh-context adversarial, NOT cross-model — state that honestly, never claim
|
|
272
|
+
cross-model diversity the run does not have. The author (`coder`) applies an
|
|
273
|
+
author-time subset (its § Author-Time Simplicity Discipline) but never grades
|
|
274
|
+
the final diff for reuse — that is this pass's exclusive, load-bearing job.
|
|
275
|
+
- **Evidence over opinion** — see the Contract's evidence rule; a survivor of the
|
|
276
|
+
generic Actionability pass that needs no change is a cleared concern, not a
|
|
277
|
+
finding.
|
|
278
|
+
- **Severity is absolute, not a quota** — zero findings on a clean diff is a
|
|
279
|
+
legitimate, expected outcome. Do NOT invent problems (the skill says it
|
|
280
|
+
verbatim). A false abstraction complaint causes churn — the anti-goal.
|