mishkan-harness 0.1.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/LICENSE +21 -0
- package/README.md +205 -0
- package/bin/mishkan.js +221 -0
- package/docs/design/MISHKAN_agent_aliases.md +140 -0
- package/docs/design/MISHKAN_decisions.md +172 -0
- package/docs/design/MISHKAN_harness_design.md +820 -0
- package/docs/design/MISHKAN_ontology.md +87 -0
- package/docs/design/MISHKAN_token_optimisation.md +181 -0
- package/docs/engineer/README.md +37 -0
- package/docs/engineer/profile.example.md +79 -0
- package/docs/usage/01-installation.md +178 -0
- package/docs/usage/02-project-init.md +151 -0
- package/docs/usage/03-orchestration.md +218 -0
- package/docs/usage/04-memory-layer.md +201 -0
- package/docs/usage/05-selective-ingest.md +177 -0
- package/docs/usage/06-llm-providers.md +195 -0
- package/docs/usage/07-troubleshooting.md +316 -0
- package/docs/usage/08-glossary.md +154 -0
- package/docs/usage/09-workflows.md +123 -0
- package/docs/usage/README.md +77 -0
- package/package.json +43 -0
- package/payload/install/settings.hooks.json +47 -0
- package/payload/mishkan/AGENT_SPEC.md +154 -0
- package/payload/mishkan/agents/ahikam.md +58 -0
- package/payload/mishkan/agents/aholiab.md +68 -0
- package/payload/mishkan/agents/asaph.md +73 -0
- package/payload/mishkan/agents/baruch.md +88 -0
- package/payload/mishkan/agents/benaiah.md +76 -0
- package/payload/mishkan/agents/bezalel.md +83 -0
- package/payload/mishkan/agents/caleb.md +74 -0
- package/payload/mishkan/agents/deborah.md +63 -0
- package/payload/mishkan/agents/elasah.md +58 -0
- package/payload/mishkan/agents/eliashib.md +68 -0
- package/payload/mishkan/agents/ezra.md +69 -0
- package/payload/mishkan/agents/hanun.md +64 -0
- package/payload/mishkan/agents/hiram.md +68 -0
- package/payload/mishkan/agents/hizkiah.md +76 -0
- package/payload/mishkan/agents/huldah.md +59 -0
- package/payload/mishkan/agents/huram.md +66 -0
- package/payload/mishkan/agents/hushai.md +59 -0
- package/payload/mishkan/agents/igal.md +58 -0
- package/payload/mishkan/agents/ira.md +86 -0
- package/payload/mishkan/agents/jahaziel.md +71 -0
- package/payload/mishkan/agents/jakin.md +66 -0
- package/payload/mishkan/agents/jehonathan.md +62 -0
- package/payload/mishkan/agents/jehoshaphat.md +68 -0
- package/payload/mishkan/agents/joab.md +71 -0
- package/payload/mishkan/agents/joah.md +62 -0
- package/payload/mishkan/agents/maaseiah.md +61 -0
- package/payload/mishkan/agents/meremoth.md +65 -0
- package/payload/mishkan/agents/meshullam.md +67 -0
- package/payload/mishkan/agents/nathan.md +70 -0
- package/payload/mishkan/agents/nehemiah.md +93 -0
- package/payload/mishkan/agents/obed.md +60 -0
- package/payload/mishkan/agents/oholiab.md +67 -0
- package/payload/mishkan/agents/palal.md +63 -0
- package/payload/mishkan/agents/phinehas.md +73 -0
- package/payload/mishkan/agents/rehum.md +60 -0
- package/payload/mishkan/agents/salma.md +69 -0
- package/payload/mishkan/agents/seraiah.md +73 -0
- package/payload/mishkan/agents/shallum.md +66 -0
- package/payload/mishkan/agents/shaphan.md +64 -0
- package/payload/mishkan/agents/shemaiah.md +67 -0
- package/payload/mishkan/agents/shevna.md +58 -0
- package/payload/mishkan/agents/uriah.md +70 -0
- package/payload/mishkan/agents/zaccur.md +58 -0
- package/payload/mishkan/agents/zadok.md +67 -0
- package/payload/mishkan/agents/zerubbabel.md +69 -0
- package/payload/mishkan/cognee/.env.curated.example +61 -0
- package/payload/mishkan/cognee/.env.example +165 -0
- package/payload/mishkan/cognee/Dockerfile +50 -0
- package/payload/mishkan/cognee/README.md +129 -0
- package/payload/mishkan/cognee/docker-compose.curated-ui.yml +61 -0
- package/payload/mishkan/cognee/docker-compose.curated.yml +85 -0
- package/payload/mishkan/cognee/docker-compose.hardening.yml +16 -0
- package/payload/mishkan/cognee/docker-compose.selfhosted.yml +114 -0
- package/payload/mishkan/cognee/docker-compose.ui.yml +70 -0
- package/payload/mishkan/cognee/docker-compose.yml +71 -0
- package/payload/mishkan/cognee/ingest-curated.py +92 -0
- package/payload/mishkan/commands/dep-audit.md +24 -0
- package/payload/mishkan/commands/mishkan-init.md +25 -0
- package/payload/mishkan/commands/mishkan-resume.md +21 -0
- package/payload/mishkan/commands/promote.md +19 -0
- package/payload/mishkan/commands/sefer-pull.md +19 -0
- package/payload/mishkan/commands/sprint-close.md +21 -0
- package/payload/mishkan/config/curated-library.yaml +113 -0
- package/payload/mishkan/config/improvement-queries.md +29 -0
- package/payload/mishkan/config/model-routing.yaml +87 -0
- package/payload/mishkan/config/projects.yaml +38 -0
- package/payload/mishkan/evals/baruch/README.md +93 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/bad-outcome-enum.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/bad-sprint-pattern.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/bad-trigger-enum.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/malformed-json.json +7 -0
- package/payload/mishkan/evals/baruch/fixtures/invalid/missing-required-field.json +14 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/blocked-vendor.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/curated-shortcircuit.json +15 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/partial-no-write.json +14 -0
- package/payload/mishkan/evals/baruch/fixtures/valid/resolved-cross-harness.json +15 -0
- package/payload/mishkan/evals/baruch/golden_case/expected.yaml +35 -0
- package/payload/mishkan/evals/baruch/golden_case/input.yaml +47 -0
- package/payload/mishkan/evals/baruch/golden_case/produced.json +15 -0
- package/payload/mishkan/evals/baruch/run.sh +129 -0
- package/payload/mishkan/hooks/model-route.py +96 -0
- package/payload/mishkan/hooks/post-tool-observe.sh +45 -0
- package/payload/mishkan/hooks/pre-tool-security.sh +150 -0
- package/payload/mishkan/hooks/session-start.sh +20 -0
- package/payload/mishkan/hooks/stop-reporter.sh +29 -0
- package/payload/mishkan/ontology.md +87 -0
- package/payload/mishkan/rules/backend/yasad.md +23 -0
- package/payload/mishkan/rules/common/dependencies.md +53 -0
- package/payload/mishkan/rules/common/quality.md +16 -0
- package/payload/mishkan/rules/common/security.md +20 -0
- package/payload/mishkan/rules/documentation/sefer.md +19 -0
- package/payload/mishkan/rules/frontend/panim.md +21 -0
- package/payload/mishkan/rules/infrastructure/migdal.md +22 -0
- package/payload/mishkan/scripts/dependency-audit.sh +171 -0
- package/payload/mishkan/scripts/ensure-curated-box.sh +66 -0
- package/payload/mishkan/scripts/mishkan-ingest.sh +92 -0
- package/payload/mishkan/scripts/observability-aggregate.sh +57 -0
- package/payload/mishkan/scripts/seed-curated-library.sh +62 -0
- package/payload/mishkan/scripts/sync-profile.sh +65 -0
- package/payload/mishkan/scripts/validate-research-log.sh +108 -0
- package/payload/mishkan/skills/asaph-a11y-seo-craft/SKILL.md +289 -0
- package/payload/mishkan/skills/baruch-research-reporting-craft/SKILL.md +460 -0
- package/payload/mishkan/skills/benaiah-devsecops-craft/SKILL.md +329 -0
- package/payload/mishkan/skills/bezalel-cto-craft/SKILL.md +391 -0
- package/payload/mishkan/skills/caleb-web-research-craft/SKILL.md +306 -0
- package/payload/mishkan/skills/cognee-promote/SKILL.md +40 -0
- package/payload/mishkan/skills/cognee-quickstart/SKILL.md +66 -0
- package/payload/mishkan/skills/context-compress/SKILL.md +36 -0
- package/payload/mishkan/skills/deborah-ux-craft/SKILL.md +295 -0
- package/payload/mishkan/skills/dependency-audit/SKILL.md +59 -0
- package/payload/mishkan/skills/dependency-vetting/SKILL.md +59 -0
- package/payload/mishkan/skills/documentation-craft/SKILL.md +468 -0
- package/payload/mishkan/skills/ezra-research-formulation-craft/SKILL.md +319 -0
- package/payload/mishkan/skills/hanun-observability-craft/SKILL.md +312 -0
- package/payload/mishkan/skills/hiram-ui-craft/SKILL.md +334 -0
- package/payload/mishkan/skills/hizkiah-implementation-craft/SKILL.md +701 -0
- package/payload/mishkan/skills/hushai-security-advisor-craft/SKILL.md +282 -0
- package/payload/mishkan/skills/ira-code-security-craft/SKILL.md +553 -0
- package/payload/mishkan/skills/jakin-intent-clarification-craft/SKILL.md +299 -0
- package/payload/mishkan/skills/jehonathan-publication-craft/SKILL.md +262 -0
- package/payload/mishkan/skills/joab-app-security-craft/SKILL.md +266 -0
- package/payload/mishkan/skills/meremoth-devops-craft/SKILL.md +298 -0
- package/payload/mishkan/skills/meshullam-infra-design-craft/SKILL.md +302 -0
- package/payload/mishkan/skills/mishkan-ingest/SKILL.md +65 -0
- package/payload/mishkan/skills/mishkan-init/SKILL.md +65 -0
- package/payload/mishkan/skills/nathan-architecture-craft/SKILL.md +547 -0
- package/payload/mishkan/skills/nehemiah-pm-craft/SKILL.md +484 -0
- package/payload/mishkan/skills/obed-asset-pipeline-craft/SKILL.md +286 -0
- package/payload/mishkan/skills/oholiab-design-system-craft/SKILL.md +334 -0
- package/payload/mishkan/skills/palal-systems-craft/SKILL.md +281 -0
- package/payload/mishkan/skills/qa-evaluation-craft/SKILL.md +406 -0
- package/payload/mishkan/skills/rehum-sre-advisor-craft/SKILL.md +228 -0
- package/payload/mishkan/skills/reporter-discipline-craft/SKILL.md +351 -0
- package/payload/mishkan/skills/research-pipeline/SKILL.md +55 -0
- package/payload/mishkan/skills/salma-frontend-implementation-craft/SKILL.md +369 -0
- package/payload/mishkan/skills/sefer-pull/SKILL.md +37 -0
- package/payload/mishkan/skills/shallum-database-craft/SKILL.md +347 -0
- package/payload/mishkan/skills/shaphan-summarisation-craft/SKILL.md +271 -0
- package/payload/mishkan/skills/shemaiah-evaluation-craft/SKILL.md +342 -0
- package/payload/mishkan/skills/sprint-report/SKILL.md +28 -0
- package/payload/mishkan/skills/team-lead-craft/SKILL.md +457 -0
- package/payload/mishkan/skills/zadok-contract-craft/SKILL.md +520 -0
- package/payload/mishkan/templates/case-node.schema.json +22 -0
- package/payload/mishkan/templates/mcp.json +22 -0
- package/payload/mishkan/templates/observability-log.schema.json +24 -0
- package/payload/mishkan/templates/project-CLAUDE.md +47 -0
- package/payload/mishkan/templates/research-log.schema.json +40 -0
- package/payload/mishkan/templates/settings.json +12 -0
- package/payload/mishkan/templates/settings.local.json +6 -0
- package/payload/mishkan/templates/sprint-state.schema.json +47 -0
- package/payload/mishkan/templates/team-report.schema.json +50 -0
- package/payload/mishkan/templates/user-CLAUDE.md +62 -0
- package/payload/mishkan/workflows/README.md +88 -0
- package/payload/mishkan/workflows/mishkan-architecture-panel.js +156 -0
- package/payload/mishkan/workflows/mishkan-codebase-audit.js +188 -0
- package/payload/mishkan/workflows/mishkan-deep-research.js +251 -0
- package/payload/mishkan/workflows/mishkan-init.js +156 -0
- package/payload/mishkan/workflows/mishkan-migration-wave.js +180 -0
- package/payload/mishkan/workflows/mishkan-release-readiness.js +163 -0
- package/payload/mishkan/workflows/mishkan-sprint-close.js +112 -0
- package/payload/user/CLAUDE.md +62 -0
- package/payload/user/rules/engineer-standards.md +66 -0
- package/payload/user/rules/y4nn-standards.md +167 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ira-code-security-craft
|
|
3
|
+
description: How Ira reviews code for security at the moment of write — the pre-block rubric, the false-positive guard list that prevents noise, severity calibration anchored to OWASP/CWE, and the durable remediation patterns. Invoke when a write is being reviewed for security implications, when a finding is about to be raised, or when blocking a write is on the table.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ira — Code Security Craft
|
|
7
|
+
|
|
8
|
+
> Not a checklist. How the watchful priest reasons at the moment a write
|
|
9
|
+
> arrives — what he flags, what he refuses to flag, and the rule that no
|
|
10
|
+
> finding ships without an anchor.
|
|
11
|
+
|
|
12
|
+
Invoked when code-level security review is in scope: a Write/Edit
|
|
13
|
+
intercepted by the PreToolUse hook, a SAST run, a dependency surfacing,
|
|
14
|
+
or a hand-raised review request.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. The rule above all other rules
|
|
19
|
+
|
|
20
|
+
**Every finding has an anchor. Anchorless findings are not raised.**
|
|
21
|
+
|
|
22
|
+
An anchor is one of:
|
|
23
|
+
|
|
24
|
+
- A specific **OWASP** entry (`OWASP-A03:2021 — Injection`).
|
|
25
|
+
- A specific **CWE** id (`CWE-89`, `CWE-79`).
|
|
26
|
+
- A specific scanner finding (`semgrep <rule-id>`, `gitleaks <rule>`,
|
|
27
|
+
`bandit B608`).
|
|
28
|
+
- A specific MISHKAN rule (`rules/common/dependencies.md` §3).
|
|
29
|
+
- A specific cited research-pipeline source (from a previous Baruch
|
|
30
|
+
log entry).
|
|
31
|
+
|
|
32
|
+
If you cannot name the anchor, you cannot name the severity, you cannot
|
|
33
|
+
write the remediation, and the finding is not real *yet*. Either find
|
|
34
|
+
the anchor or do not flag.
|
|
35
|
+
|
|
36
|
+
The reason the rule exists: ungrounded findings are noise, noise erodes
|
|
37
|
+
trust in the reviewer, eroded trust means the next *real* finding gets
|
|
38
|
+
ignored. The first defence is not flagging things you cannot defend.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 2. The pre-block rubric — every question must answer yes
|
|
43
|
+
|
|
44
|
+
Before blocking a write, Ira answers all five questions, in writing,
|
|
45
|
+
inside the `/plan` surface. Any "no" — do not block; raise as a
|
|
46
|
+
non-blocking advisory or drop.
|
|
47
|
+
|
|
48
|
+
1. **Can I cite the exact line(s) the finding affects?** File:line, or
|
|
49
|
+
the diff hunk. "Somewhere in this file" is not a finding.
|
|
50
|
+
2. **Can I describe the concrete failure mode?** The attacker action,
|
|
51
|
+
the input, the impact. "Could be exploited" is not a description.
|
|
52
|
+
3. **Is the severity defensible?** Anchor + impact, not vibes. §5.
|
|
53
|
+
4. **Is there a durable remediation I can propose right now?** Not
|
|
54
|
+
"consider adding validation" — the *fix*. §7.
|
|
55
|
+
5. **Is the rule layer the right place to block, or is this a one-off
|
|
56
|
+
coding decision the engineer should know about but not be blocked
|
|
57
|
+
by?** §3.
|
|
58
|
+
|
|
59
|
+
If yes to all five: block, plan the explanation, name the rule, propose
|
|
60
|
+
the fix.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 3. Block vs. advise — the calibration
|
|
65
|
+
|
|
66
|
+
Not every security observation justifies a block. Blocking too often is
|
|
67
|
+
how the security layer becomes "the thing engineers learn to suppress."
|
|
68
|
+
|
|
69
|
+
| Situation | Action |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Hardcoded credential / secret | **Block** |
|
|
72
|
+
| SQL injection via string concatenation | **Block** |
|
|
73
|
+
| Unsafe dynamic execution (`eval`, `exec`, `os.system` with user input) | **Block** |
|
|
74
|
+
| Missing input validation on a public endpoint | **Block** |
|
|
75
|
+
| Missing output encoding (XSS path) | **Block** |
|
|
76
|
+
| Dependency with critical CVE pinned with no exception note | **Block** |
|
|
77
|
+
| Missing rate limit on an unauthenticated endpoint | **Block** if endpoint is sensitive; advise otherwise |
|
|
78
|
+
| Logging that may leak PII | **Advise** (with severity), block only if the leak is direct |
|
|
79
|
+
| Cryptographic primitive that is dated but not broken | **Advise** with deprecation path |
|
|
80
|
+
| Missing defence-in-depth control (e.g. CSP header) | **Advise**, raise to Hushai for prioritisation |
|
|
81
|
+
| Code-quality issue dressed as security | **Drop** (route to Uriah / QA) |
|
|
82
|
+
|
|
83
|
+
The line is: **does this code, if shipped today, create a directly-
|
|
84
|
+
exploitable bug under realistic conditions?** Yes → block. Maybe →
|
|
85
|
+
advise. No → drop.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 4. The false-positive guard list — what Ira does NOT flag
|
|
90
|
+
|
|
91
|
+
These are the patterns LLM-driven security review tends to flag as
|
|
92
|
+
findings. They are not findings. Stop before raising them.
|
|
93
|
+
|
|
94
|
+
1. **Missing error handling on caller-managed errors.** If the caller's
|
|
95
|
+
contract is "raises on failure" and the caller handles it, missing
|
|
96
|
+
a try/except in the callee is correct, not a defect.
|
|
97
|
+
2. **Missing JSDoc / docstring on self-describing functions.** Not a
|
|
98
|
+
security concern.
|
|
99
|
+
3. **"Magic numbers" that are protocol constants.** HTTP status codes,
|
|
100
|
+
port numbers (`443`, `80`), well-known sizes (`64` for SHA-256
|
|
101
|
+
output bytes), ULID length (`26`). Not findings.
|
|
102
|
+
4. **`TODO` comments.** Project hygiene, not security.
|
|
103
|
+
5. **String concatenation outside of SQL / shell / HTML / JS contexts.**
|
|
104
|
+
Building a log message with `+` is not injection. Injection requires
|
|
105
|
+
a *target language*; concatenating into a Python format string does
|
|
106
|
+
not create one.
|
|
107
|
+
6. **`int` overflow in Python.** Python integers are arbitrary precision.
|
|
108
|
+
This is not C.
|
|
109
|
+
7. **Use of `pickle` against data the program itself wrote** in the
|
|
110
|
+
same trust boundary. Pickle is dangerous against untrusted input,
|
|
111
|
+
not against round-tripping your own state.
|
|
112
|
+
8. **Logging of `request_id`, `user_id`, or other non-PII identifiers.**
|
|
113
|
+
Operational requirement, not a leak.
|
|
114
|
+
9. **Wide exception catches in top-level handlers** — `except Exception`
|
|
115
|
+
in the main loop is correct; `except Exception` swallowed silently
|
|
116
|
+
inside a function is a defect, but a *correctness* defect, not a
|
|
117
|
+
security one (route to Uriah).
|
|
118
|
+
10. **Use of `random` for non-cryptographic purposes** — IDs, jitter,
|
|
119
|
+
sampling. `secrets` is for cryptographic randomness; `random` is
|
|
120
|
+
fine for everything else.
|
|
121
|
+
|
|
122
|
+
Raising one of these wastes the engineer's attention and burns the
|
|
123
|
+
reviewer's credibility on the *next* finding.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 5. Severity calibration — anchored, not invented
|
|
128
|
+
|
|
129
|
+
Severity is a numeric, defensible claim. The shape:
|
|
130
|
+
|
|
131
|
+
| Severity | Definition | Anchor pattern |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| **critical** | Direct, remote, unauthenticated exploit producing data loss / RCE / auth bypass | OWASP A01–A03 in the wild; CVE with public exploit; live secret leak |
|
|
134
|
+
| **high** | Direct exploit producing data loss / privilege escalation; requires authentication or specific conditions | OWASP A04–A07; CVE without public PoC but with high CVSS; auth-bypass-with-precondition |
|
|
135
|
+
| **medium** | Defence-in-depth gap; misconfiguration; weak primitive not in the exploit path today | Missing security header on sensitive page; dated TLS suite enabled; verbose error in prod |
|
|
136
|
+
| **low** | Hardening recommendation; not exploitable in itself | Verbose logging on success path; cookie missing `SameSite=Lax` on non-session cookie |
|
|
137
|
+
|
|
138
|
+
Three rules:
|
|
139
|
+
|
|
140
|
+
- **Anchor → severity, never the other way.** "It feels high so I'll
|
|
141
|
+
call it OWASP A03" is the inversion that produces noise.
|
|
142
|
+
- **Authenticated-only does not mean low.** Auth-bypass-once equals
|
|
143
|
+
remote-once. Calibrate by what the bypass *yields*.
|
|
144
|
+
- **A finding with no exploit path is at most medium.** "Could be
|
|
145
|
+
combined with something else for impact" is medium until the
|
|
146
|
+
combination is shown.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 6. The OWASP / CWE mapping table Ira uses on autopilot
|
|
151
|
+
|
|
152
|
+
| Pattern in code | OWASP 2021 | CWE | Default severity |
|
|
153
|
+
|---|---|---|---|
|
|
154
|
+
| String-concat SQL | A03 Injection | CWE-89 | critical |
|
|
155
|
+
| `eval` / `exec` of input | A03 Injection | CWE-95 | critical |
|
|
156
|
+
| `os.system` / `subprocess(shell=True)` w/ input | A03 Injection | CWE-78 | critical |
|
|
157
|
+
| Missing input validation on public endpoint | A03 / A04 | CWE-20 | high |
|
|
158
|
+
| Reflected unsanitised input in HTML | A03 | CWE-79 | high |
|
|
159
|
+
| Hardcoded secret in source | A07 ID & Auth Failures | CWE-798 | critical |
|
|
160
|
+
| Missing auth check on protected endpoint | A01 Broken Access Control | CWE-862 | critical |
|
|
161
|
+
| Missing authz check (auth ok, ownership not checked) | A01 | CWE-639 | critical (IDOR) |
|
|
162
|
+
| Weak crypto primitive (MD5/SHA1 for hashing secrets) | A02 Crypto Failures | CWE-327 | high |
|
|
163
|
+
| Predictable / non-crypto RNG for tokens | A02 | CWE-338 | high |
|
|
164
|
+
| Verbose error messages in prod | A05 Misconfig | CWE-209 | medium |
|
|
165
|
+
| Outdated dependency w/ known CVE | A06 Vuln & Outdated | (per CVE) | per CVE |
|
|
166
|
+
| Insecure deserialization of untrusted input | A08 Software & Data Integrity | CWE-502 | high |
|
|
167
|
+
| Missing logging on auth events | A09 Logging Failures | CWE-778 | medium |
|
|
168
|
+
| SSRF — outbound HTTP from server to user-controlled URL | A10 SSRF | CWE-918 | high |
|
|
169
|
+
|
|
170
|
+
Memorising the table is not the goal. Memorising the *shape* of the
|
|
171
|
+
table is — anchor first, severity derived.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 7. Durable remediations — the patterns Ira proposes by reflex
|
|
176
|
+
|
|
177
|
+
Every block must propose the fix. The fix must be durable, not a
|
|
178
|
+
workaround.
|
|
179
|
+
|
|
180
|
+
### 7.1 SQL injection → parameterised queries (every stack)
|
|
181
|
+
|
|
182
|
+
**Python (asyncpg):**
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
# WRONG
|
|
186
|
+
await pool.fetch(f"SELECT * FROM users WHERE email = '{email}'")
|
|
187
|
+
# RIGHT
|
|
188
|
+
await pool.fetch("SELECT * FROM users WHERE email = $1", email)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**TypeScript (Knex):**
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// WRONG
|
|
195
|
+
await knex.raw(`SELECT * FROM users WHERE email = '${email}'`);
|
|
196
|
+
// RIGHT
|
|
197
|
+
await knex.raw("SELECT * FROM users WHERE email = ?", [email]);
|
|
198
|
+
// or the query builder, which parameterises automatically:
|
|
199
|
+
await knex("users").where({ email }).first();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**TypeScript (Prisma):**
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// WRONG — $queryRawUnsafe takes a string and runs it as-is
|
|
206
|
+
await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`);
|
|
207
|
+
// RIGHT — tagged template literal is parameterised at the driver
|
|
208
|
+
await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`;
|
|
209
|
+
// or the type-safe API:
|
|
210
|
+
await prisma.user.findUnique({ where: { email } });
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**PHP (Laravel):**
|
|
214
|
+
|
|
215
|
+
```php
|
|
216
|
+
// WRONG
|
|
217
|
+
DB::select("SELECT * FROM users WHERE email = '$email'");
|
|
218
|
+
// RIGHT — DB facade with bindings
|
|
219
|
+
DB::select("SELECT * FROM users WHERE email = ?", [$email]);
|
|
220
|
+
// or query builder / Eloquent (parameterised by default):
|
|
221
|
+
DB::table('users')->where('email', $email)->first();
|
|
222
|
+
User::query()->where('email', $email)->first();
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Not "validate the input first" — that is a workaround that fails on the
|
|
226
|
+
next code path. Parameterise everywhere, in every stack.
|
|
227
|
+
|
|
228
|
+
### 7.2 Hardcoded secret → env + secret manager + .gitignore + revoke
|
|
229
|
+
|
|
230
|
+
Four steps, in order. Anything short is a workaround:
|
|
231
|
+
|
|
232
|
+
1. Read the secret from env / SOPS / secret manager.
|
|
233
|
+
2. Add the env var to `.env.example` *without* the real value.
|
|
234
|
+
3. Confirm `.env` is in `.gitignore`.
|
|
235
|
+
4. **Revoke the leaked secret.** It is in git history; rotation is
|
|
236
|
+
mandatory, not optional. Hand the revoke command to Y4NN.
|
|
237
|
+
|
|
238
|
+
(Examples in §9 use the placeholder string `<PLACEHOLDER_FAKE_KEY>` for
|
|
239
|
+
the leaked-secret string. Real keys never appear in this skill.)
|
|
240
|
+
|
|
241
|
+
### 7.3 XSS → output encoding at the template layer, not before
|
|
242
|
+
|
|
243
|
+
```jsx
|
|
244
|
+
// WRONG — manual encoding before storage
|
|
245
|
+
storeUserBio(escapeHtml(input))
|
|
246
|
+
|
|
247
|
+
// RIGHT — store raw, encode on render
|
|
248
|
+
storeUserBio(input)
|
|
249
|
+
<div>{userBio}</div> // React encodes on render
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The framework's render-time encoding is the durable answer. Pre-encoding
|
|
253
|
+
on store creates double-encoding bugs and breaks search.
|
|
254
|
+
|
|
255
|
+
### 7.4 Missing input validation → Pydantic at the boundary
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
# WRONG — manual checks scattered through service code
|
|
259
|
+
if len(email) > 254: raise BadRequest(...)
|
|
260
|
+
if "@" not in email: raise BadRequest(...)
|
|
261
|
+
|
|
262
|
+
# RIGHT — validate at the contract boundary
|
|
263
|
+
class CreateUser(BaseModel):
|
|
264
|
+
model_config = ConfigDict(extra="forbid")
|
|
265
|
+
email: EmailStr = Field(max_length=254)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The Pydantic model is the validation layer. Inputs that survive the
|
|
269
|
+
model are valid by construction; the service does not re-check.
|
|
270
|
+
|
|
271
|
+
### 7.5 Auth/authz — middleware + ownership check at service
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
# Auth (middleware) — is the caller authenticated?
|
|
275
|
+
# Authz (service) — does this caller own this resource?
|
|
276
|
+
async def get_invoice(id: str, principal: Principal) -> Invoice:
|
|
277
|
+
inv = await self._repo.get(id)
|
|
278
|
+
if inv is None:
|
|
279
|
+
raise NotFoundError(...)
|
|
280
|
+
if inv.customer_id != principal.customer_id:
|
|
281
|
+
raise NotFoundError(...) # 404 not 403 — do not leak existence
|
|
282
|
+
return inv
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The 404-on-authz-fail pattern is deliberate: a 403 confirms the
|
|
286
|
+
resource exists.
|
|
287
|
+
|
|
288
|
+
### 7.6 SSRF → allowlist of resolved IPs / domains, not deny-list
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
# WRONG — try to block private IPs by string match
|
|
292
|
+
if url.startswith("http://10.") or "localhost" in url: ...
|
|
293
|
+
|
|
294
|
+
# RIGHT — resolve, then check against a positive allowlist
|
|
295
|
+
host = urlparse(url).hostname
|
|
296
|
+
ip = await resolver.resolve(host)
|
|
297
|
+
if not in_allowlist(ip):
|
|
298
|
+
raise ForbiddenURL(...)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Deny-lists are bypassable by encoding tricks, DNS rebinding, IPv6
|
|
302
|
+
aliases. Allowlists fail closed.
|
|
303
|
+
|
|
304
|
+
### 7.7 Weak crypto → name the right primitive
|
|
305
|
+
|
|
306
|
+
| For | Use |
|
|
307
|
+
|---|---|
|
|
308
|
+
| Password hashing | `argon2id` (preferred) / `bcrypt` |
|
|
309
|
+
| Symmetric encryption | `AES-256-GCM` / `XChaCha20-Poly1305` |
|
|
310
|
+
| Asymmetric signing | `Ed25519` |
|
|
311
|
+
| MAC | `HMAC-SHA-256` |
|
|
312
|
+
| Token generation | `secrets.token_urlsafe(32)` |
|
|
313
|
+
| File hashing (non-secret) | `SHA-256` |
|
|
314
|
+
|
|
315
|
+
Not "use a stronger algorithm." Name the primitive.
|
|
316
|
+
|
|
317
|
+
### 7.8 Insecure deserialization → JSON + Pydantic, not pickle
|
|
318
|
+
|
|
319
|
+
If the data crosses a trust boundary, it must arrive as JSON and be
|
|
320
|
+
validated by a Pydantic model. Pickle is an internal-trust-boundary
|
|
321
|
+
tool only.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 8. The dependency layer — where Ira gates before Benaiah vets
|
|
326
|
+
|
|
327
|
+
Code-level dependency hygiene Ira enforces (Benaiah owns the deeper
|
|
328
|
+
supply-chain analysis):
|
|
329
|
+
|
|
330
|
+
- **Every dependency pinned.** No `^`, no `~`, no `*`. Hash-pinned in
|
|
331
|
+
the lockfile.
|
|
332
|
+
- **Lockfile present and committed.** `pnpm-lock.yaml`, `poetry.lock`,
|
|
333
|
+
`uv.lock` — committed. Generated reproducibly.
|
|
334
|
+
- **New dependency = block until Benaiah vets via `dependency-vetting`
|
|
335
|
+
skill.** Cannot land without the vet log.
|
|
336
|
+
- **Critical CVE in a dependency = block until pinned-around or
|
|
337
|
+
exception noted.** If the vulnerable version is pinned, an inline
|
|
338
|
+
comment with the CVE id explains the pin choice.
|
|
339
|
+
- **CI gate runs OSV-Scanner and `trivy fs` on every PR.** Findings
|
|
340
|
+
block the merge.
|
|
341
|
+
|
|
342
|
+
The standards rule named: `payload/mishkan/rules/common/dependencies.md`
|
|
343
|
+
(if present in the project). When a new dep arrives, Ira routes to
|
|
344
|
+
Benaiah; until Benaiah signs off, the dep does not land.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 9. Worked example A — "Hardcoded API key in a config file"
|
|
349
|
+
|
|
350
|
+
The write that arrived (key shown as a placeholder; never put a real
|
|
351
|
+
key in code):
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
# config.py
|
|
355
|
+
GEMINI_API_KEY = "<PLACEHOLDER_FAKE_KEY>"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Ira's path:
|
|
359
|
+
|
|
360
|
+
**§2 rubric:**
|
|
361
|
+
|
|
362
|
+
1. Line cited: `config.py:1`.
|
|
363
|
+
2. Failure mode: secret is in git history forever; any read of the
|
|
364
|
+
public repo (intentional or via a fork / leak) exposes the live key
|
|
365
|
+
to the world; the key authenticates billing to the project's
|
|
366
|
+
account.
|
|
367
|
+
3. Severity: **critical**. Anchor: OWASP A07, CWE-798.
|
|
368
|
+
4. Durable remediation: §7.2 — move to env, document in `.env.example`,
|
|
369
|
+
confirm `.gitignore`, **revoke the key.**
|
|
370
|
+
5. Right place to block: yes — this is a write that ships the secret;
|
|
371
|
+
blocking before commit is the layer.
|
|
372
|
+
|
|
373
|
+
**Action:** block, fix the file:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
import os
|
|
377
|
+
GEMINI_API_KEY = os.environ["GEMINI_API_KEY"]
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Update `.env.example`:
|
|
381
|
+
|
|
382
|
+
```
|
|
383
|
+
GEMINI_API_KEY=
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Hand to Y4NN** (asymmetric delegation):
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
# Revoke the leaked key — required, the key is in git history.
|
|
390
|
+
# 1. Open the provider console → API keys → revoke the leaked key.
|
|
391
|
+
# 2. Issue a new key, place it in .env (not source).
|
|
392
|
+
# 3. Audit provider billing for unauthorised use in the leak window.
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The remediation is not done until the key is revoked. The code edit is
|
|
396
|
+
necessary; the rotation is the actual fix.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## 10. Worked example B — "Raw f-string SQL with user-controlled input"
|
|
401
|
+
|
|
402
|
+
The write:
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
async def find_by_email(self, email: str):
|
|
406
|
+
return await self._pool.fetch(
|
|
407
|
+
f"SELECT * FROM users WHERE email = '{email}'"
|
|
408
|
+
)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Ira's path:
|
|
412
|
+
|
|
413
|
+
**§2 rubric:**
|
|
414
|
+
|
|
415
|
+
1. Line: `users.py:12`.
|
|
416
|
+
2. Failure mode: `email = "x' OR '1'='1"` returns the whole table.
|
|
417
|
+
Worse: `email = "x'; UPDATE users SET admin = true WHERE email = 'x"`
|
|
418
|
+
on a connection that supports multi-statements is privilege
|
|
419
|
+
escalation. The injection is direct and remote.
|
|
420
|
+
3. Severity: **critical**. Anchor: OWASP A03, CWE-89.
|
|
421
|
+
4. Remediation: §7.1 — parameterise.
|
|
422
|
+
5. Block: yes.
|
|
423
|
+
|
|
424
|
+
**Action:** block, fix the file:
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
async def find_by_email(self, email: str):
|
|
428
|
+
return await self._pool.fetch(
|
|
429
|
+
"SELECT * FROM users WHERE email = $1", email
|
|
430
|
+
)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**What Ira does NOT also do:** rewrite the surrounding code,
|
|
434
|
+
add input validation as a workaround instead of parameterising,
|
|
435
|
+
add a `try/except` to "handle the case," refactor the repository layer.
|
|
436
|
+
Scope is the security finding. The fix is the fix.
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## 11. Worked example C — the false-positive guard in action
|
|
441
|
+
|
|
442
|
+
The write that arrived:
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
log.info(f"completed order {order_id} for user {user_id}")
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
LLM-review instinct: "This logs a user_id; could be PII."
|
|
449
|
+
|
|
450
|
+
Ira's path:
|
|
451
|
+
|
|
452
|
+
**§4 guard list:** entry 8 — operational identifiers (`user_id`,
|
|
453
|
+
`request_id`) are not PII. They are how support correlates and how
|
|
454
|
+
incidents are traced. Logging them is required by the observability
|
|
455
|
+
rule, not a leak.
|
|
456
|
+
|
|
457
|
+
**Action:** do not flag. Move on.
|
|
458
|
+
|
|
459
|
+
The discipline of *not* flagging this is what keeps the next real
|
|
460
|
+
finding credible.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## 12. The SAST configuration Ira reaches for
|
|
465
|
+
|
|
466
|
+
Three layers, in order:
|
|
467
|
+
|
|
468
|
+
| Layer | Tool | What it catches |
|
|
469
|
+
|---|---|---|
|
|
470
|
+
| **Pre-commit** | `gitleaks` | secrets in working tree |
|
|
471
|
+
| **Pre-merge CI** | `semgrep` (with the curated rule pack) | high-confidence patterns: hardcoded secrets, SQL string-concat, eval-on-input, unsafe deserialise |
|
|
472
|
+
| **Pre-merge CI** | `bandit` (Python) / `eslint-plugin-security` (JS) | language-specific lint of known anti-patterns |
|
|
473
|
+
| **Per-merge** | `osv-scanner` + `trivy fs` | dependency CVEs and container CVEs |
|
|
474
|
+
|
|
475
|
+
Three rules:
|
|
476
|
+
|
|
477
|
+
- **Rules over scanners.** A small, curated rule set with low false-
|
|
478
|
+
positives beats a large default ruleset that engineers learn to
|
|
479
|
+
ignore.
|
|
480
|
+
- **CI gates merges, hooks gate writes.** The PreToolUse hook stops
|
|
481
|
+
obvious things before a write lands; the CI catches what survives.
|
|
482
|
+
- **No silenced scanners.** A `nosec` / `# noqa` annotation requires an
|
|
483
|
+
inline comment naming the reason and an anchor (CVE, rule id) — same
|
|
484
|
+
discipline as a finding.
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## 13. The interface with Benaiah, Hushai, Joab, Phinehas
|
|
489
|
+
|
|
490
|
+
- **Ira → Benaiah.** Anything supply-chain or infrastructure-level
|
|
491
|
+
(new dependency, container image, deploy config) routes to Benaiah.
|
|
492
|
+
Ira flags; Benaiah decides.
|
|
493
|
+
- **Ira → Hushai.** When a control prioritisation question surfaces
|
|
494
|
+
("we have 14 medium findings, where do we invest?"), route to Hushai
|
|
495
|
+
for strategic counsel. Ira does not prioritise at the program level.
|
|
496
|
+
- **Ira → Joab.** Application-surface findings (auth flow shape,
|
|
497
|
+
session, CSRF, mobile/desktop client) are Joab's. Ira flags the
|
|
498
|
+
code; Joab owns the surface analysis.
|
|
499
|
+
- **Phinehas → Ira.** Cross-cutting constraints flow down from
|
|
500
|
+
Phinehas. Ira enforces them at the code layer.
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
## 14. The recurring traps Ira rejects on sight
|
|
505
|
+
|
|
506
|
+
1. **"Add validation to be safe."** Not a finding. A finding names the
|
|
507
|
+
exploit path. Defensive validation as a general hedge is noise.
|
|
508
|
+
|
|
509
|
+
2. **"This `try/except: pass` is suppressing security."** Maybe, but
|
|
510
|
+
the anchor is correctness (CWE-755 improper error handling), not
|
|
511
|
+
security. Route to Uriah.
|
|
512
|
+
|
|
513
|
+
3. **"This dependency has a CVE."** A finding *names the CVE*, the
|
|
514
|
+
affected version range, the version currently used, and whether
|
|
515
|
+
the project is in the exploit path. "Has a CVE" without that is
|
|
516
|
+
noise. Use OSV-Scanner output verbatim.
|
|
517
|
+
|
|
518
|
+
4. **"This crypto is weak."** §7.7. Name the primitive, the
|
|
519
|
+
replacement, the migration path. "Use stronger crypto" is not a
|
|
520
|
+
remediation.
|
|
521
|
+
|
|
522
|
+
5. **"This could be exploited under certain conditions."** What
|
|
523
|
+
conditions, explicitly? If you cannot state them, you do not have
|
|
524
|
+
a finding yet — you have a hypothesis. Park it; come back when you
|
|
525
|
+
can.
|
|
526
|
+
|
|
527
|
+
6. **Suppressing a finding without an anchor.** A `# semgrep: ignore`
|
|
528
|
+
without a reason note is a defect. Suppression has the same
|
|
529
|
+
anchoring discipline as a finding.
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## 15. Style — Ira's working voice
|
|
534
|
+
|
|
535
|
+
- **Direct, brief, anchored.** "OWASP A03, CWE-89, line 12. Fix:
|
|
536
|
+
parameterise." Not five paragraphs of context.
|
|
537
|
+
- **No "could," "might," "may."** A finding states what the code does
|
|
538
|
+
and what the exploit yields. Conditional language is suppression in
|
|
539
|
+
disguise.
|
|
540
|
+
- **Cite the rule, every time.** The engineer reading the finding
|
|
541
|
+
should be able to look up the anchor and verify the call.
|
|
542
|
+
- **Watchful without paranoia.** The role title is the discipline. A
|
|
543
|
+
watcher who flags everything is the same as a watcher who flags
|
|
544
|
+
nothing — the signal is gone either way.
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
*Cross-references: `~/.claude/rules/y4nn-standards.md`
|
|
549
|
+
(verify-before-fix §2, durable rule §3, no-fabrication §6,
|
|
550
|
+
asymmetric-delegation §5 — Ira never executes a rotation, hands the
|
|
551
|
+
command to Y4NN), `payload/mishkan/agents/ira.md` (the agent that
|
|
552
|
+
invokes this skill), `payload/mishkan/hooks/pre-tool-security.sh`
|
|
553
|
+
(the runtime side; Ira is its live intelligence).*
|