# finding

This skill should be used when the user asks about findings, finding structure, finding format, finding best practices, "how should a finding look", "what goes in a finding", "/finding", or wants to understand how security findings are structured and written. Teaches the format, best practices, and conventions for security findings. For specific workflows use /finding-draft, /finding-review, or /finding-dedup.

- **Kind:** skill
- **Source:** https://github.com/JoranHonig/grimoire
- **Page:** https://forefy.com/skills/5870b074-3e95-4274-ba92-19978d892dc2
- **API (JSON + files):** https://forefy.com/api/asr/5870b074-3e95-4274-ba92-19978d892dc2

---

## SKILL.md

---
name: finding
description: >-
  This skill should be used when the user asks about findings, finding structure,
  finding format, finding best practices, "how should a finding look", "what goes in a
  finding", "/finding", or wants to understand how security findings are structured and
  written. Teaches the format, best practices, and conventions for security findings.
  For specific workflows use /finding-draft, /finding-review, or /finding-dedup.
user_invocable: true
---

# Finding

Security findings are the core deliverable of security research — structured markdown files
that prove a vulnerability exists and tell the recipient what to fix.

## Philosophy

A finding is not a code review comment or a chat message. It is a standalone document that
must be understandable by someone who has never seen the codebase. Every finding must be
self-contained, fact-checked, and verifiable.

A finding should never suggest non-trivial code changes. Security researchers are external,
unbiased reviewers. By suggesting complex implementations we become biased. If the fix
requires architectural redesign, say so and move on. The recommendation states *what* to
fix, not *how* to rewrite the code.

> **You are responsible for your findings.** Agents make mistakes. Always perform thorough
> review of references, proof of concepts, and claims before submitting.

## Finding Structure

Every finding is a markdown file with YAML frontmatter:

```yaml
---
title: Theft of deposited funds via reentrancy in Vault.withdraw()
severity: High
type: reentrancy
context:
  - src/Vault.sol:142-158
  - src/interfaces/IVault.sol:23
---
```

| Field      | Required | Description                                                    |
|------------|----------|----------------------------------------------------------------|
| `title`    | yes      | Concise title following the where/how/what rule                |
| `severity` | yes      | Critical, High, Medium, Low, or Informational                  |
| `type`     | yes      | Flaw classification (reentrancy, access-control, dos, etc.)    |
| `context`  | yes      | YAML list of affected files with optional line numbers/ranges  |

### Sections

| Section              | Required | Purpose                                             |
|----------------------|----------|-----------------------------------------------------|
| `## Description`     | yes      | Explains the vulnerability and its impact            |
| `## Details`         | no       | Technical deep dive for complex mechanisms           |
| `## Proof of Concept`| no       | References the PoC artifact via `@path/to/poc`       |
| `## Recommendation`  | yes      | Concise, objective fix direction                     |
| `## References`      | no       | Numbered citations to standards, prior art, docs     |

For complete format specification see `references/finding-format.md`.

## Title Best Practices

The title must convey **where** (component), **how** (mechanism), and **what** (impact):

**Good:** `"Theft of deposited funds via reentrancy in Vault.withdraw() due to state update after external call"`

**Bad:** `"Missing check"`, `"Reentrancy"`, `"Incorrect implementation"`

For detailed guidelines with more examples see `references/finding-best-practices.md`.

## Recommendation Best Practices

- **Objective voice.** State what needs to change, not how you would rewrite the code.
- **One-sentence fixes preferred.** If you can express the fix in one sentence, do so.
- **Never suggest non-trivial code changes.** Acceptable: add a check, use a different
  function, add comments, reorder operations. Not acceptable: full reimplementations.
- **Out of scope escape hatch.** If no simple fix exists: *"The design space for a
  solution to this flaw is out of scope for this report."*

## Filing Conventions

- **Manual audit findings:** `grimoire/findings/<slug>.md`
- **Automated / sigil findings:** `grimoire/sigil-findings/<slug>.md`
- **Filenames:** kebab-case from title, `.md` extension, max 60 characters
- **Collision handling:** append numeric suffix (`-2`, `-3`)

For directory layout details see `references/finding-structure.md`.

## Severity Scale

| Level         | Criteria                                                            |
|---------------|---------------------------------------------------------------------|
| Critical      | Direct path to fund loss, RCE, or full compromise. Minimal preconditions. |
| High          | Significant impact, exploitable with moderate effort or conditions.  |
| Medium        | Real but conditional impact. Requires chaining or elevated privileges.|
| Low           | Minor or theoretical impact. Worth documenting for defense in depth.  |
| Informational | Best-practice deviation with no direct exploitable impact.           |

Severity is always an estimate, not a formal CVSS score. Justify with one sentence.

## Key Principles

- **Self-contained.** A reader must fully understand the issue from the finding alone.
- **Fact-checked.** Never refer to a best practice, standard, or prior finding that does
  not exist. All cited references must be real and verifiable.
- **Benign payloads only.** PoCs use `alert(1)`, `sleep()`, `id` — never destructive.
- **Parameterized targets.** Localhost and variables, never hardcoded production URLs.
- **Use `@path` for PoC references.** Never inline large code blocks. Reference the file.
- **Validate.** Run `scripts/validate-finding.sh` on every new or modified finding.

## Examples

- `examples/reentrancy-finding.md` — complete finding with all sections (smart contract)
- `examples/access-control-finding.md` — minimal valid finding (web app, no Details section)
- `examples/dedup-scenario.md` — duplicate vs similar classification walkthrough

## Workflows

Use the specific workflow skills for finding operations:
- `/finding-draft` — create a new finding from a vulnerability observation
- `/finding-review` — review an existing finding against best practices
- `/finding-dedup` — identify and resolve duplicate or overlapping findings

## examples

```

```

## examples/access-control-finding.md

# Example: Access Control Finding

A minimal valid finding for a missing authentication check on a backend API route.
Demonstrates a finding with no Details section and a PoC placeholder — the simplest
complete finding that satisfies all required fields and sections.

## Finding File

```markdown
---
title: Account takeover enabled by lack of authentication in backend route PUT /user
severity: Critical
type: access-control
context:
  - src/routes/user.js:34-52
  - src/middleware/auth.js
---

## Description

The `PUT /user` endpoint at `src/routes/user.js:34` allows updating any user's profile,
including email and password, without requiring authentication. The route handler directly
processes the request body and updates the user record identified by the `id` parameter.

An unauthenticated attacker can send a PUT request to `/user/:id` with arbitrary profile
data, overwriting the target user's email and password. This enables full account takeover
of any user. No privileges or preconditions are required — the endpoint is publicly
accessible.

The `auth.js` middleware exists and is applied to other routes (`GET /user`, `DELETE /user`)
but is missing from the `PUT` handler's middleware chain.

## Proof of Concept

No PoC yet — run `/write-poc` to generate one.

## Recommendation

Add the authentication middleware to the `PUT /user` route handler's middleware chain,
consistent with the other user endpoints.
```

## Why This Finding Works

- **Title.** Where (PUT /user backend route), how (lack of authentication), what (account
  takeover). All three elements present.
- **No Details section.** The vulnerability mechanism is straightforward — a missing
  middleware application. The Description covers it completely. Adding Details would be
  redundant.
- **Severity: Critical.** No authentication required, any user account can be taken over,
  no preconditions. The Description justifies this without overstating.
- **Self-contained.** Explains what the endpoint does, what the flaw is, why auth.js should
  be there (it's on other routes), and exactly what an attacker can do.
- **PoC placeholder.** Honestly states no PoC exists yet and directs to the appropriate
  skill. This is better than omitting the section or fabricating a PoC.
- **Minimal recommendation.** One sentence. Names what to do (add middleware) and why
  (consistency with other endpoints). Does not suggest how to rewrite the route.
- **No References section.** Not every finding needs references. A missing auth middleware
  is self-evidently a problem — no external citation needed.

## examples/dedup-scenario.md

# Example: Dedup Scenario

An annotated walkthrough of the duplicate detection workflow, showing how to classify
finding pairs and what actions to take for each classification.

## Scenario: Four Findings in an Audit

The `grimoire/findings/` directory contains four findings:

| File | Title | Type | Severity |
|------|-------|------|----------|
| `reentrancy-vault-withdraw.md` | Theft of deposited funds via reentrancy in Vault.withdraw() | reentrancy | High |
| `unsafe-external-call-vault.md` | Unsafe external call in Vault.withdraw() allows reentrancy | reentrancy | High |
| `missing-auth-admin-panel.md` | Unauthorized access to admin panel via missing role check | access-control | Critical |
| `privilege-escalation-admin-routes.md` | Privilege escalation through unprotected admin API routes | access-control | High |

## Classification

### Pair 1: Duplicate

**reentrancy-vault-withdraw.md** vs **unsafe-external-call-vault.md**

| Criterion | Finding A | Finding B |
|-----------|-----------|-----------|
| Root cause | State update after external call in withdraw() | External call before state update in withdraw() |
| Affected component | Vault.sol:142-158 | Vault.sol:142-158 |
| Impact | Theft of all deposited funds | Reentrancy enabling fund drainage |

**Classification: Duplicate.** Same root cause (CEI violation in withdraw), same affected
code, same impact. The two findings describe the identical issue in different words.

**Action:** Keep the more complete finding. In this case, `reentrancy-vault-withdraw.md` has
a detailed exploit walkthrough and PoC reference. Delete `unsafe-external-call-vault.md`.

**Confirmation prompt:** *"Delete `unsafe-external-call-vault.md`? It duplicates
`reentrancy-vault-withdraw.md` (same root cause: CEI violation in Vault.withdraw). [y/n]"*

### Pair 2: Similar

**missing-auth-admin-panel.md** vs **privilege-escalation-admin-routes.md**

| Criterion | Finding C | Finding D |
|-----------|-----------|-----------|
| Root cause | Missing role check on admin panel route | Multiple admin API routes lack authorization |
| Affected component | src/routes/admin.js:12 (panel endpoint) | src/routes/admin.js:12-89 (all admin routes) |
| Impact | Unauthorized admin panel access | Full admin privilege escalation |

**Classification: Similar.** Related root cause (missing authorization on admin routes) but
different scope. Finding C covers one specific endpoint. Finding D covers the entire admin
route file. Deleting either loses information.

**Options:**
1. **Merge** — combine into a single finding covering all admin routes, using Finding D as
   the base (broader scope) and incorporating Finding C's specific detail about the panel
   endpoint.
2. **Cross-reference** — keep both, add a note in each referencing the other.
3. **Leave as-is** — if the user considers them distinct enough to report separately.

**Confirmation prompt:** *"Findings C and D both cover admin authorization gaps. Merge into
`privilege-escalation-admin-routes.md` (broader scope)? [y/n/skip]"*

## After Dedup

Assuming the user confirms both actions:

| File | Title | Type | Severity |
|------|-------|------|----------|
| `reentrancy-vault-withdraw.md` | Theft of deposited funds via reentrancy in Vault.withdraw() | reentrancy | High |
| `privilege-escalation-admin-routes.md` | Privilege escalation through unprotected admin API routes (includes admin panel) | access-control | Critical |

Two findings reduced to two, but cleaner: one duplicate removed, one pair merged with no
information loss.

The merged finding should be reviewed with `/finding-review` to ensure the incorporated
content reads well and the severity is still appropriate (promoted from High to Critical
since the merged scope includes the critical panel access).

## Why This Workflow Works

- **Duplicate vs similar is about information loss.** Can you delete one without losing
  anything? Duplicate. Would you lose scope, detail, or a different perspective? Similar.
- **User confirms every action.** No automated deletion or merging. The skill proposes,
  the researcher decides.
- **The more complete finding survives.** When deleting a duplicate, keep the one with
  better documentation, a PoC reference, or more detailed explanation.
- **Merged findings need review.** Combining content from two findings can introduce
  inconsistencies. Always suggest `/finding-review` after a merge.
- **Grouping by type first.** The reentrancy findings were only compared to each other,
  and the access-control findings were only compared to each other. This prevents false
  matches across unrelated vulnerability classes.

## examples/reentrancy-finding.md

# Example: Reentrancy Finding

A complete finding for a classic reentrancy vulnerability in a Solidity vault contract.
Demonstrates all frontmatter fields, all required sections, and an optional Details section
with a multi-step exploit walkthrough.

## Finding File

```markdown
---
title: Theft of deposited funds via reentrancy in Vault.withdraw() due to state update after external call
severity: High
type: reentrancy
context:
  - src/Vault.sol:142-158
  - src/interfaces/IVault.sol:23
---

## Description

The `Vault` contract allows users to deposit and withdraw ETH. The `withdraw()` function
at `src/Vault.sol:142` sends ETH to the caller via a low-level `call` before updating the
internal `balances` mapping. An attacker can deploy a contract with a `receive()` function
that re-enters `withdraw()` during the ETH transfer, draining the vault of all deposited
funds.

Any user who has deposited at least 1 wei can exploit this. The attack requires no special
privileges and can be executed in a single transaction. The impact is loss of all ETH held
by the Vault contract.

## Details

The exploit proceeds in four steps:

1. The attacker deposits a small amount of ETH into the Vault via `deposit()`.
2. The attacker calls `withdraw()`, which checks `balances[msg.sender] > 0` (line 143) and
   sends ETH via `(bool success, ) = msg.sender.call{value: amount}("")` (line 150).
3. The attacker's `receive()` function re-enters `withdraw()`. Because the balance update
   `balances[msg.sender] = 0` at line 155 has not yet executed, the check at line 143
   passes again.
4. Steps 2-3 repeat until the Vault is drained. The balance is only set to zero after the
   final call returns.

The vulnerable code sequence:

```solidity
function withdraw() external {
    uint256 amount = balances[msg.sender];  // line 143
    require(amount > 0, "No balance");

    (bool success, ) = msg.sender.call{value: amount}("");  // line 150
    require(success, "Transfer failed");

    balances[msg.sender] = 0;  // line 155 — too late
}
```

## Proof of Concept

@grimoire/pocs/reentrancy-vault-poc.t.sol

## Recommendation

Update the contract balance state before performing the external call, following the
checks-effects-interactions pattern. Specifically, set `balances[msg.sender] = 0` before
the `call` on line 150.

## References

[1] SWC-107: Reentrancy — https://swcregistry.io/docs/SWC-107
[2] Checks-Effects-Interactions pattern — Solidity documentation
```

## Why This Finding Works

- **Title.** Contains where (Vault.withdraw), how (reentrancy, state update after external
  call), and what (theft of deposited funds).
- **Severity justification.** High is appropriate: direct fund loss, minimal preconditions
  (any depositor), single transaction.
- **Self-contained Description.** A reader who has never seen the Vault contract understands
  what it does, what the flaw is, what preconditions exist, and what the impact is.
- **Details add value.** The four-step walkthrough and code snippet explain a mechanism that
  is not obvious from the Description alone.
- **Recommendation is minimal.** States what to change (reorder operations) and names the
  pattern (CEI). Does not provide a rewritten contract.
- **References are real.** SWC-107 is a real registry entry. The Solidity documentation
  genuinely covers the CEI pattern.
- **Context field.** Lists the exact file and line range, plus the interface for completeness.

## references

```

```

## references/finding-best-practices.md

# Finding Best Practices

Quality guidelines for drafting and reviewing security findings. Used by both draft mode
(to write well) and review mode (to evaluate quality).

## Title Guidelines

The title must convey **where**, **how**, and **what** (impact):

- **Where** — the affected component, route, function, or contract
- **How** — the mechanism or flaw type
- **What** — the impact or consequence

### Good Titles

| Title | Why it works |
|-------|-------------|
| "Theft of deposited funds via reentrancy in Vault.withdraw() due to state update after external call" | Where (Vault.withdraw), How (reentrancy + state after call), What (theft of funds) |
| "Account takeover enabled by lack of authentication in backend route UPDATE /user" | Where (UPDATE /user), How (no auth), What (account takeover) |
| "Denial of service in token transfer via unbounded loop over holder array" | Where (token transfer), How (unbounded loop), What (DoS) |

### Bad Titles

| Title | Problem |
|-------|---------|
| "Missing authentication" | No where, no what. Which route? What can an attacker do? |
| "Account takeover" | No where, no how. What component? What mechanism? |
| "Incorrect backend implementation" | Vague. Says nothing specific about the flaw. |
| "Reentrancy" | No where, no what. Which function? What is the impact? |
| "Bug in Vault.sol" | No how, no what. What kind of bug? What does it enable? |

### Common Anti-patterns

- **Too short** — missing one or more of where/how/what
- **Too long** — more than ~120 characters; move detail to Description
- **Impact-only** — states what happens but not where or how
- **Location-only** — states where but not what an attacker gains
- **Jargon without context** — assumes the reader knows project-specific terms

## Description Guidelines

The description is the most important section. Self-containment test: *Could someone who
has never opened this repo understand the vulnerability from this section alone?*

### Structure

1. **What is the component?** One sentence establishing context.
2. **What is the flaw?** The mechanism — what goes wrong and why.
3. **What are the preconditions?** Privileges required, timing, configuration needed.
4. **What is the impact?** What an attacker can achieve. Be specific.

### Tips

- Include code snippets when they clarify the mechanism. Keep them short — just the
  relevant lines.
- State severity cues explicitly: "Any unauthenticated user can...", "An attacker with
  admin access could...", "Under specific timing conditions..."
- Do not reference other findings. Each finding stands alone.
- Keep it concise. If you need more than 4 paragraphs, move technical detail to the
  Details section.

## Details Guidelines

### When to Include

- The exploit involves multiple steps (e.g., flash loan + swap + withdrawal)
- The mechanism requires a code walkthrough to understand
- There are edge cases or timing windows that need explanation

### When to Omit

- The Description already covers the mechanism adequately
- The flaw is straightforward (e.g., missing authorization on one route)
- The PoC demonstrates the issue clearly enough on its own

### Tips

- Use numbered steps for multi-step exploits
- Reference specific code lines with `file:line` format
- Do not repeat content from the Description

## Recommendation Guidelines

### Principles

- **Objective voice.** State what needs to change, not how you would rewrite the code.
- **One-sentence fixes preferred.** If you can express the fix in one sentence, do so.
- **Never suggest non-trivial code changes.** Security researchers are external, unbiased
  reviewers. Suggesting complex implementations introduces bias.
- **Acceptable suggestions:** Add a check, use a different function, add comments, reorder
  operations, add rate limiting, validate input.
- **Not acceptable:** Full reimplementations, architectural redesigns, multi-file refactors.

### The "Out of Scope" Escape Hatch

If the vulnerability has no simple fix, state:

> *"The design space for a solution to this flaw is out of scope for this report."*

This is honest and appropriate. Not every finding has a one-line fix.

### Tips

- Reference established patterns when applicable (e.g., "checks-effects-interactions",
  "principle of least privilege")
- If suggesting a function change, name the function but do not write the implementation

## Severity Estimation

### Factors to Consider

| Factor | Higher severity | Lower severity |
|--------|----------------|----------------|
| **Exploitability** | No authentication needed, simple to trigger | Requires admin access, complex setup |
| **Impact scope** | All users, all funds, full compromise | Single user, limited data, partial |
| **Preconditions** | None or minimal | Specific config, timing, privilege |
| **Reversibility** | Irreversible (fund loss, data deletion) | Recoverable (temporary DoS) |
| **Likelihood** | Common scenario, easy to discover | Edge case, requires specific knowledge |

### Guidelines

- **Critical** — direct, unconditional path to maximum impact. No reasonable preconditions.
- **High** — significant impact with moderate preconditions or effort.
- **Medium** — real but conditional impact. Requires chaining, specific config, or elevated
  privileges.
- **Low** — minor or largely theoretical impact. Worth documenting for defense in depth.
- **Informational** — deviation from best practice. No direct exploitation path.

Severity is an estimate, not a formal CVSS score. Justify with one sentence in the finding.
Do not overstate confidence.

## References and Fact-Checking

- Every cited reference must be real and verifiable
- Never fabricate CVEs, SWC entries, blog posts, or documentation links
- When referencing a concept (e.g., reentrancy), provide background for readers who may
  not know the term — do not assume universal knowledge
- Prefer primary sources: official documentation, specification documents, registry entries
- Use the librarian agent for reference discovery — it searches external documentation,
  audit databases, and specifications to find citable sources for claims in findings

## Common Mistakes Checklist

Use this during review to catch frequent issues:

- [ ] Title missing impact (what)
- [ ] Title missing location (where)
- [ ] Description not self-contained — references external context
- [ ] Recommendation suggests non-trivial code changes
- [ ] Severity estimate does not match described impact
- [ ] PoC reference points to nonexistent file
- [ ] References cite fabricated sources
- [ ] Code snippets included without file/line attribution
- [ ] Preconditions not stated (assumes the reader knows requirements)
- [ ] Uses project-specific jargon without explanation

## references/finding-format.md

# Finding File Format

This reference defines the format for finding files stored in `grimoire/findings/` and
`grimoire/sigil-findings/`.

## File Location

- **Manual audit findings:** `grimoire/findings/<slug>.md`
- **Automated / sigil findings:** `grimoire/sigil-findings/<slug>.md`

Filenames are kebab-case, derived from the finding title, with `.md` extension. Maximum 60
characters. Must be unique within the target directory. If a collision occurs, append a
numeric suffix (`-2`, `-3`).

## Frontmatter

Every finding file starts with YAML frontmatter:

```yaml
---
title: Theft of deposited funds via reentrancy in Vault.withdraw()
severity: High
type: reentrancy
context:
  - src/Vault.sol:142-158
  - src/interfaces/IVault.sol:23
---
```

| Field      | Required | Description                                                        |
|------------|----------|--------------------------------------------------------------------|
| `title`    | yes      | Concise vulnerability title following the where/how/what rule.     |
| `severity` | yes      | Severity estimate: Critical, High, Medium, Low, or Informational.  |
| `type`     | yes      | Flaw classification for search and indexing.                       |
| `context`  | yes      | YAML list of affected files, optionally with line numbers/ranges.  |

### Context Field Format

Each entry in the `context` list is a file path relative to the project root. Optionally
append a colon and line number or range:

```yaml
context:
  - src/Vault.sol                   # whole file
  - src/Vault.sol:142               # specific line
  - src/Vault.sol:142-158           # line range
  - src/routes/user.js:45-67
```

### Severity Scale

| Level           | Criteria                                                        |
|-----------------|-----------------------------------------------------------------|
| Critical        | Direct path to fund loss, RCE, or full system compromise. Minimal preconditions. |
| High            | Significant impact, exploitable with moderate effort or conditions. |
| Medium          | Real impact but requires specific conditions, chaining, or elevated privileges. |
| Low             | Minor impact or largely theoretical. Worth documenting.          |
| Informational   | Observation or best-practice deviation with no direct exploitable impact. |

Severity is always an estimate. Agents adjust based on context but should justify the
assessment in the Description section.

### Type Taxonomy

Recommended (non-exhaustive) type values:

| Type                    | Description                                              |
|-------------------------|----------------------------------------------------------|
| `reentrancy`            | State modification after external call                   |
| `access-control`        | Missing or insufficient authorization checks             |
| `dos`                   | Denial of service through resource exhaustion or revert  |
| `integer-overflow`      | Arithmetic overflow or underflow                         |
| `logic-error`           | Incorrect business logic or state machine flaw           |
| `memory-corruption`     | Buffer overflow, use-after-free, or similar              |
| `injection`             | SQL, command, XSS, or other injection vectors            |
| `information-disclosure`| Leaking sensitive data through logs, errors, or responses|
| `race-condition`        | TOCTOU, front-running, or concurrency issues             |
| `cryptographic`         | Weak randomness, broken primitives, or key management    |
| `configuration`         | Insecure defaults, missing hardening, or misconfiguration|
| `supply-chain`          | Dependency vulnerabilities or compromised packages       |

Any non-empty string is accepted. Use the taxonomy above when a standard term fits. For
novel flaw types, use a descriptive kebab-case value.

## Sections

The body follows the closing `---` of the frontmatter:

| Section              | Required | Purpose                                                 |
|----------------------|----------|---------------------------------------------------------|
| `## Description`     | yes      | Explains the vulnerability and its impact.              |
| `## Details`         | no       | Technical deep dive for complex mechanisms.             |
| `## Proof of Concept`| no       | References the PoC artifact file.                       |
| `## Recommendation`  | yes      | Concise, objective fix direction.                       |
| `## References`      | no       | Numbered citations to standards, prior art, docs.       |

### Description

2-4 paragraphs. Must be self-contained — a reader unfamiliar with the codebase should
fully understand the vulnerability from this section alone. Cover:

1. What component is affected
2. What the flaw is (mechanism)
3. What preconditions exist (privileges, timing, configuration)
4. What the impact is (what an attacker can achieve)

Include code snippets where they aid comprehension.

### Details

Optional. Include when:
- The exploit involves multiple steps
- The mechanism is non-obvious
- Code walkthrough is needed to understand the flaw

Omit when the Description already covers the mechanism adequately.

### Proof of Concept

Reference format: `@path/to/poc-file` where the path is relative to the project root.

```markdown
## Proof of Concept

@grimoire/pocs/reentrancy-vault-poc.t.sol
```

If no PoC exists yet, note this explicitly and suggest creating one with [[write-poc]].

### Recommendation

Objective fix direction. Preferred format: one or two sentences stating what to change.

**Acceptable:** "Update the contract balance state before performing the external call
(checks-effects-interactions pattern)."

**Acceptable:** "Add authentication middleware to the `/user` PUT endpoint."

**Acceptable:** "The design space for a solution to this flaw is out of scope for this
report."

**Not acceptable:** Multi-paragraph implementation with code blocks rewriting the contract.

### References

Numbered citations:

```markdown
## References

[1] SWC-107: Reentrancy — https://swcregistry.io/docs/SWC-107
[2] Checks-Effects-Interactions pattern — Solidity docs
```

All cited references must be real and verifiable. Never fabricate citations.

## Example

See `examples/reentrancy-finding.md` for a complete finding and
`examples/access-control-finding.md` for a minimal valid finding.

## references/finding-structure.md

# Finding Directory Structure

Documents the layout and conventions for security findings within a grimoire workspace.

## Directory Layout

```
grimoire/
  findings/                  # Manual audit findings
    <finding-slug>.md
    <finding-slug>.md
  sigil-findings/            # Automated / agent-derived findings
    <finding-slug>.md
  pocs/                      # Proof-of-concept artifacts (referenced by findings)
  cartography/               # Code flow documentation (cross-referenced)
  spells/                    # Detection modules (checks, sigils)
GRIMOIRE.md                  # Project context map
```

## When to Use Which Directory

### `grimoire/findings/`

Findings produced through manual security research:
- Human-directed hypothesis and investigation
- Findings drafted with `/finding` or `/finding-draft`
- Findings from manual triage and PoC development
- This is the default directory for the draft workflow

### `grimoire/sigil-findings/`

Findings produced through automated tooling:
- Output from sigil agents (static analysis, pattern matching)
- Findings generated by automated scanning pipelines
- These findings often need human refinement via `/finding-review`

Both directories use the same file format (see `finding-format.md`).

## File Naming

- **Format:** kebab-case derived from the title, `.md` extension
- **Max length:** 60 characters
- **Uniqueness:** Must be unique within the target directory
- **Collision handling:** Append numeric suffix (`-2`, `-3`) if the slug already exists
- **Examples:**
  - `theft-of-funds-via-reentrancy-vault-withdraw.md`
  - `missing-auth-update-user-route.md`
  - `unbounded-loop-token-transfer.md`

## Indexing

Run the index script to list all findings with metadata:

```bash
bash skills/finding/scripts/index-findings.sh
```

Output is tab-separated: `FILE  TITLE  SEVERITY  TYPE`

Findings are sorted by severity (Critical first). The script scans both `grimoire/findings/`
and `grimoire/sigil-findings/`.

## Relationship to Other Artifacts

### Proof of Concept Files

Findings reference PoC files using the `@path` syntax in the `## Proof of Concept` section.
PoC paths are relative to the project root. The PoC file itself is created by the
[[write-poc]] skill.

### Cartography Flows

Findings may reference cartography flows when the vulnerability spans a documented code
flow. Use `[[cartography/flow-name]]` to cross-reference. The `context` frontmatter field
lists specific files; cartography provides the broader flow context.

### Checks and Sigils

Confirmed findings are candidates for generalization into checks (via [[checks]]) or
detection modules (via [[scribe-distill]]). A finding answers "what went wrong here";
a check answers "where else might this go wrong." The Scribe agent assesses automation
feasibility and creates the appropriate detection module type.

### GRIMOIRE.md

The project context map does not list individual findings, but findings should be consistent
with the architecture, crown jewels, and attack surface documented in GRIMOIRE.md.

## Lifecycle

1. **Draft** — created via `/finding` or manually by the researcher
2. **Review** — hardened via `/finding-review` (best practices, fact-checking, clarity)
3. **Dedup** — cleaned via `/finding-dedup` (duplicates removed, similar findings merged)
4. **Final** — ready for inclusion in the audit report

Findings are never "locked." They can always be re-reviewed or updated as understanding
evolves during the engagement.

## scripts

```

```

## scripts/index-findings.sh

```bash
#!/usr/bin/env bash
# index-findings.sh — Index finding files by reading YAML frontmatter.
# Outputs tab-separated: filepath\ttitle\tseverity\ttype
# Scans both grimoire/findings/ and grimoire/sigil-findings/ by default.
# Usage: index-findings.sh [directory ...]
# If no directories given, scans the default finding directories.

set -euo pipefail

# Severity sort order (lower number = higher priority)
severity_order() {
  case "$(echo "$1" | tr '[:upper:]' '[:lower:]')" in
    critical)      echo 1 ;;
    high)          echo 2 ;;
    medium)        echo 3 ;;
    low)           echo 4 ;;
    informational) echo 5 ;;
    *)             echo 9 ;;
  esac
}

# Collect directories to scan
if [ $# -gt 0 ]; then
  dirs=("$@")
else
  dirs=(grimoire/findings/ grimoire/sigil-findings/)
fi

# Temporary file for sorting
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

for dir in "${dirs[@]}"; do
  # Skip if directory does not exist
  [ -d "$dir" ] || continue

  for file in "$dir"/*.md; do
    # Handle case where glob matches nothing
    [ -e "$file" ] || continue

    # Skip _index.md if present
    basename=$(basename "$file")
    if [ "$basename" = "_index.md" ]; then
      continue
    fi

    title=""
    severity=""
    type=""
    in_frontmatter=0

    while IFS= read -r line; do
      # Detect frontmatter boundaries
      if [ "$line" = "---" ]; then
        if [ "$in_frontmatter" -eq 0 ]; then
          in_frontmatter=1
          continue
        else
          break
        fi
      fi

      if [ "$in_frontmatter" -eq 1 ]; then
        case "$line" in
          title:*)
            title="${line#title:}"
            title="${title# }"
            title="${title#\"}"
            title="${title%\"}"
            title="${title#\'}"
            title="${title%\'}"
            ;;
          severity:*)
            severity="${line#severity:}"
            severity="${severity# }"
            severity="${severity#\"}"
            severity="${severity%\"}"
            ;;
          type:*)
            type="${line#type:}"
            type="${type# }"
            type="${type#\"}"
            type="${type%\"}"
            ;;
        esac
      fi
    done < "$file"

    # Only output if we found the required display fields
    if [ -n "$title" ] && [ -n "$severity" ]; then
      order=$(severity_order "$severity")
      printf '%s\t%s\t%s\t%s\t%s\n' "$order" "$file" "$title" "$severity" "$type" >> "$tmpfile"
    fi
  done
done

# Sort by severity order and output without the sort key
sort -t$'\t' -k1,1n "$tmpfile" | cut -f2-
```

## scripts/validate-finding.sh

```bash
#!/usr/bin/env bash
# validate-finding.sh — Validate a finding file against the schema.
# Checks frontmatter fields, required sections, and PoC references.
# Usage: validate-finding.sh <finding-file>
# Exits 0 if valid, 1 if errors found. Prints results to stderr.

set -euo pipefail

file="${1:-}"
if [ -z "$file" ] || [ ! -f "$file" ]; then
  echo "Usage: validate-finding.sh <finding-file>" >&2
  exit 1
fi

errors=0
warnings=0
passes=0

pass() {
  printf 'PASS\t%s\n' "$1" >&2
  passes=$((passes + 1))
}

fail() {
  printf 'FAIL\t%s\t%s\n' "$1" "$2" >&2
  errors=$((errors + 1))
}

warn() {
  printf 'WARN\t%s\t%s\n' "$1" "$2" >&2
  warnings=$((warnings + 1))
}

# --- Parse frontmatter ---
title=""
severity=""
type=""
context_found=0
in_frontmatter=0
frontmatter_closed=0

# Section tracking
has_description=0
has_recommendation=0
has_poc_section=0
in_poc_section=0
poc_reference=""

while IFS= read -r line; do
  if [ "$line" = "---" ]; then
    if [ "$in_frontmatter" -eq 0 ]; then
      in_frontmatter=1
      continue
    else
      frontmatter_closed=1
      continue
    fi
  fi

  if [ "$in_frontmatter" -eq 1 ] && [ "$frontmatter_closed" -eq 0 ]; then
    case "$line" in
      title:*)
        title="${line#title:}"
        title="${title# }"
        title="${title#\"}"
        title="${title%\"}"
        last_key="title"
        ;;
      severity:*)
        severity="${line#severity:}"
        severity="${severity# }"
        severity="${severity#\"}"
        severity="${severity%\"}"
        last_key="severity"
        ;;
      type:*)
        type="${line#type:}"
        type="${type# }"
        type="${type#\"}"
        type="${type%\"}"
        last_key="type"
        ;;
      context:*)
        context_found=1
        last_key="context"
        ;;
      "  - "*)
        # YAML list item — only counts for context if immediately following context: key
        if [ "${last_key:-}" = "context" ]; then
          context_found=1
        fi
        ;;
    esac
  fi

  if [ "$frontmatter_closed" -eq 1 ]; then
    # Check for required sections
    case "$line" in
      "## Description"*) has_description=1; in_poc_section=0 ;;
      "## Recommendation"*) has_recommendation=1; in_poc_section=0 ;;
      "## Proof of Concept"*|"## Proof of concept"*) has_poc_section=1; in_poc_section=1 ;;
      "## "*) in_poc_section=0 ;;  # any other heading exits PoC section
    esac

    # Check for @reference only while inside the PoC section
    if [ "$in_poc_section" -eq 1 ] && [ -z "$poc_reference" ]; then
      case "$line" in
        @*) poc_reference="${line#@}" ;;
      esac
    fi
  fi
done < "$file"

# --- Validate frontmatter ---
if [ "$in_frontmatter" -eq 0 ]; then
  fail "frontmatter" "No frontmatter found (missing opening ---)"
elif [ "$frontmatter_closed" -eq 0 ]; then
  fail "frontmatter" "Frontmatter not closed (missing closing ---)"
else
  pass "frontmatter-structure"
fi

# Required fields
if [ -n "$title" ]; then
  pass "title"
else
  fail "title" "Missing required field: title"
fi

if [ -n "$severity" ]; then
  # Validate severity value
  case "$(echo "$severity" | tr '[:upper:]' '[:lower:]')" in
    critical|high|medium|low|informational)
      pass "severity"
      ;;
    *)
      fail "severity" "Invalid severity value: '$severity'. Must be Critical, High, Medium, Low, or Informational"
      ;;
  esac
else
  fail "severity" "Missing required field: severity"
fi

if [ -n "$type" ]; then
  pass "type"
else
  fail "type" "Missing required field: type"
fi

if [ "$context_found" -eq 1 ]; then
  pass "context"
else
  fail "context" "Missing required field: context (list of affected files)"
fi

# --- Validate sections ---
if [ "$has_description" -eq 1 ]; then
  pass "description-section"
else
  fail "description-section" "Missing required section: ## Description"
fi

if [ "$has_recommendation" -eq 1 ]; then
  pass "recommendation-section"
else
  fail "recommendation-section" "Missing required section: ## Recommendation"
fi

# PoC section is optional but if present, check for @reference
if [ "$has_poc_section" -eq 1 ]; then
  if [ -n "$poc_reference" ]; then
    # Check if referenced file exists
    if [ -f "$poc_reference" ]; then
      pass "poc-reference"
    else
      warn "poc-reference" "PoC file not found: $poc_reference"
    fi
  else
    warn "poc-reference" "Proof of Concept section exists but contains no @reference"
  fi
fi

# --- Validate filename ---
basename=$(basename "$file" .md)
if echo "$basename" | grep -qE '^[a-z0-9]+(-[a-z0-9]+)*$'; then
  pass "filename-format"
else
  warn "filename-format" "Filename '$basename' is not strict kebab-case (lowercase alphanumeric with hyphens)"
fi

# --- Summary ---
total=$((passes + errors + warnings))
echo "" >&2
if [ "$errors" -gt 0 ]; then
  echo "FAIL: $passes/$total passed, $errors error(s), $warnings warning(s) — $file" >&2
  exit 1
else
  if [ "$warnings" -gt 0 ]; then
    echo "PASS with $warnings warning(s): $passes/$total passed — $file" >&2
  else
    echo "PASS: $passes/$total passed — $file" >&2
  fi
  exit 0
fi
```

