hex-audit 0.5.4 → 0.5.6
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/DEMO.md +5 -5
- package/README.md +31 -10
- package/dashboard/.next/BUILD_ID +1 -1
- package/dashboard/.next/app-build-manifest.json +22 -22
- package/dashboard/.next/app-path-routes-manifest.json +1 -1
- package/dashboard/.next/build-manifest.json +4 -4
- package/dashboard/.next/next-server.js.nft.json +1 -1
- package/dashboard/.next/prerender-manifest.js +1 -1
- package/dashboard/.next/prerender-manifest.json +1 -1
- package/dashboard/.next/required-server-files.json +1 -1
- package/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/_not-found.html +1 -1
- package/dashboard/.next/server/app/_not-found.rsc +1 -1
- package/dashboard/.next/server/app/access/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/api/notes/done/route.js +1 -0
- package/dashboard/.next/server/app/api/notes/done/route.js.nft.json +1 -0
- package/dashboard/.next/server/app/api/notes/lead/route.js +1 -0
- package/dashboard/.next/server/app/api/notes/lead/route.js.nft.json +1 -0
- package/dashboard/.next/server/app/api/notes/question/route.js +1 -0
- package/dashboard/.next/server/app/api/notes/question/route.js.nft.json +1 -0
- package/dashboard/.next/server/app/api/notes/record/route.js +3 -3
- package/dashboard/.next/server/app/api/notes/route.js +2 -2
- package/dashboard/.next/server/app/calls/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/conformance/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/diagram/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/diagram.html +1 -1
- package/dashboard/.next/server/app/diagram.rsc +1 -1
- package/dashboard/.next/server/app/flows/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/flows.html +1 -1
- package/dashboard/.next/server/app/flows.rsc +1 -1
- package/dashboard/.next/server/app/functions/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/issues/page.js +5 -5
- package/dashboard/.next/server/app/issues/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/notes/page.js +3 -3
- package/dashboard/.next/server/app/notes/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/overleaf/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/page.js +2 -2
- package/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/progress/page.js +1 -1
- package/dashboard/.next/server/app/progress/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app/stats/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/server/app-paths-manifest.json +11 -8
- package/dashboard/.next/server/chunks/42.js +1 -1
- package/dashboard/.next/server/chunks/530.js +1 -1
- package/dashboard/.next/server/chunks/622.js +3 -0
- package/dashboard/.next/server/functions-config-manifest.json +1 -1
- package/dashboard/.next/server/middleware-build-manifest.js +1 -1
- package/dashboard/.next/server/pages/404.html +1 -1
- package/dashboard/.next/server/pages/500.html +1 -1
- package/dashboard/.next/server/server-reference-manifest.js +1 -1
- package/dashboard/.next/server/server-reference-manifest.json +1 -1
- package/dashboard/.next/static/chunks/app/issues/page-d1607f57783782ae.js +1 -0
- package/dashboard/.next/static/chunks/app/notes/page-17af94c67ba79c00.js +1 -0
- package/dashboard/.next/static/chunks/app/page-24bac51dfe007c66.js +1 -0
- package/dashboard/.next/static/chunks/app/progress/page-427fb18da3afb061.js +1 -0
- package/dashboard/.next/static/chunks/{main-a6cc6e599cefa893.js → main-9a811c95265dbd11.js} +1 -1
- package/dashboard/.next/static/css/4a38d8f6f6b7a68f.css +3 -0
- package/dashboard/app/api/notes/done/route.ts +26 -0
- package/dashboard/app/api/notes/lead/route.ts +40 -0
- package/dashboard/app/api/notes/question/route.ts +30 -0
- package/dashboard/app/api/notes/record/route.ts +10 -2
- package/dashboard/app/issues/IssuesBoardClient.tsx +18 -2
- package/dashboard/app/notes/NotesClient.tsx +494 -77
- package/dashboard/app/notes/page.tsx +5 -2
- package/dashboard/app/progress/ProgressClient.tsx +29 -8
- package/dashboard/app/progress/page.tsx +2 -0
- package/dashboard/components/MarkdownRenderer.tsx +31 -0
- package/dashboard/next.config.mjs +11 -0
- package/dashboard/whisper-server.py +85 -0
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +23 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/init.js +2 -2
- package/dist/commands/issue.js +2 -2
- package/dist/commands/issue.js.map +1 -1
- package/dist/commands/note.d.ts.map +1 -1
- package/dist/commands/note.js +55 -2
- package/dist/commands/note.js.map +1 -1
- package/dist/core/issues.d.ts +1 -1
- package/dist/core/issues.d.ts.map +1 -1
- package/dist/core/issues.js +2 -1
- package/dist/core/issues.js.map +1 -1
- package/dist/core/notes.d.ts +68 -0
- package/dist/core/notes.d.ts.map +1 -1
- package/dist/core/notes.js +167 -0
- package/dist/core/notes.js.map +1 -1
- package/dist/core/review.d.ts +14 -0
- package/dist/core/review.d.ts.map +1 -0
- package/dist/core/review.js +80 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/whisper.d.ts +8 -0
- package/dist/core/whisper.d.ts.map +1 -1
- package/dist/core/whisper.js +55 -0
- package/dist/core/whisper.js.map +1 -1
- package/dist/skills/diane.md +49 -67
- package/dist/skills/ingest-aa-report.md +6 -6
- package/dist/skills/init-audit.md +1 -1
- package/dist/skills/validate-all-findings.md +23 -9
- package/dist/skills/{validate-issue.md → validate-finding.md} +71 -25
- package/dist/skills/write-finding.md +3 -3
- package/package.json +3 -2
- package/dashboard/.next/static/chunks/app/issues/page-abb062e4cf118340.js +0 -1
- package/dashboard/.next/static/chunks/app/notes/page-bc458d3e1639c526.js +0 -1
- package/dashboard/.next/static/chunks/app/page-1226422e170d8895.js +0 -1
- package/dashboard/.next/static/chunks/app/progress/page-f07ac39d1d27b480.js +0 -1
- package/dashboard/.next/static/css/23b3f575e87bea75.css +0 -3
- /package/dashboard/.next/static/{JIjye0HUTWALF3gbtaZF8 → hV9GBuhq9vm6hXiHOWEfE}/_buildManifest.js +0 -0
- /package/dashboard/.next/static/{JIjye0HUTWALF3gbtaZF8 → hV9GBuhq9vm6hXiHOWEfE}/_ssgManifest.js +0 -0
package/dist/skills/diane.md
CHANGED
|
@@ -45,87 +45,69 @@ When a unit is ambiguous, treat it as a note (the safe default — nothing is lo
|
|
|
45
45
|
|
|
46
46
|
This is what makes you a pair auditor, not a stenographer. For **every checkable claim** in the narration — how a function behaves, a value, an invariant, "this is safe because…" — open the relevant source (Read / Grep over the project scope) and check it. Classify each:
|
|
47
47
|
|
|
48
|
-
- **Confirmed** → record it as fact in the
|
|
49
|
-
- **Wrong** → record the **correct** behavior
|
|
50
|
-
- **Incomplete / missed** → add
|
|
51
|
-
- **Interesting angle** you spot independently →
|
|
48
|
+
- **Confirmed** → record it as fact in `description` (the function's `notes`, or purpose/storage/roles); don't clutter.
|
|
49
|
+
- **Wrong** → record the **correct** behavior, and add a `⚠` string to that function's `warnings` naming the contradiction + `file:line` (e.g. `⚠ you said rounds down; rounds UP (Vault.sol:88)`).
|
|
50
|
+
- **Incomplete / missed** → add a `leads` entry (e.g. an unchecked return, a missing bound or zero-check, an edge case the auditor walked past), noting they didn't mention it.
|
|
51
|
+
- **Interesting angle** you spot independently → a `leads` entry, phrased as a hypothesis to investigate.
|
|
52
52
|
|
|
53
53
|
Push back only with concrete, code-grounded evidence — never contrarian for its own sake. If a claim isn't checkable from the code, note it as an assumption rather than asserting it true or false.
|
|
54
54
|
|
|
55
55
|
## Step 4 — Route the triggers
|
|
56
56
|
|
|
57
|
-
All three edit the same
|
|
57
|
+
All three edit the same structured record; Step 5 covers the read-merge-write mechanics.
|
|
58
58
|
|
|
59
|
-
**create a finding** → invoke the existing `/write-finding` skill, prefilled from the narration (the affected contract/function and the auditor's stated impact). Let that skill own duplicate-checking and the board write. After it returns,
|
|
59
|
+
**create a finding** → invoke the existing `/write-finding` skill, prefilled from the narration (the affected contract/function and the auditor's stated impact). Let that skill own duplicate-checking and the board write. After it returns, set the matching lead's `status` to `"logged"` and its `ref` to the new `H-NNN` — it leaves the open-leads list but stays in the record (audit trail). If no lead matched, no-op.
|
|
60
60
|
|
|
61
|
-
**answer a question** → answer it yourself using codebase access (Read / Grep over the project scope), or search online for things you're unsure about. Be concrete, concise, and cite `file:line`.
|
|
61
|
+
**answer a question** → answer it yourself using codebase access (Read / Grep over the project scope), or search online for things you're unsure about. Be concrete, concise, and cite `file:line`. Put it in `questions[]`: fill the `answer` of the matching open question if one exists, else add `{ q, answer }`.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
- <question>? **Answer**: <concise answer, with file:line>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
If it answers a question already open in that list, fill that bullet in place — don't add a duplicate. Never write a separate `**Q:**` / `**A:**` block.
|
|
68
|
-
|
|
69
|
-
**search online** → use WebSearch, read the most relevant result, and fold it into wherever it belongs — an answer in **Questions**, a point in **Description**, or a **Leads** bullet — with the source link. No standalone block.
|
|
63
|
+
**search online** → use WebSearch, read the most relevant result, and fold it where it belongs — a question's `answer`, a `description` field, or a `leads` entry — with the source link.
|
|
70
64
|
|
|
71
65
|
## Step 5 — Update the contract profile
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
The
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
## Leads
|
|
107
|
-
- <lead> — why it might be an issue
|
|
108
|
-
- setFee has no upper bound (Vault.sol:120) — you didn't mention it
|
|
109
|
-
- ~~<promoted lead>~~ → logged H-007
|
|
110
|
-
````
|
|
67
|
+
A contract's notes are a **structured record** you grow over time — not per-session blocks. Each run: read the current record, merge this session **and your Step 3 verification** into it, write it back. Clean up filler/repetition; keep the reasoning, not the disfluency.
|
|
68
|
+
|
|
69
|
+
The record (JSON):
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"contract": "Vault.sol",
|
|
74
|
+
"marked_done": false,
|
|
75
|
+
"description": {
|
|
76
|
+
"purpose": ["ERC-4626-ish vault"],
|
|
77
|
+
"inheritance": ["..."],
|
|
78
|
+
"storage": ["`totalAssets` (uint256) — managed balance"],
|
|
79
|
+
"roles": ["`onlyOwner` — gates setFee / setOwner"],
|
|
80
|
+
"functions": [
|
|
81
|
+
{
|
|
82
|
+
"sig": "deposit(assets, receiver) — external",
|
|
83
|
+
"purpose": "pull assets, mint shares",
|
|
84
|
+
"access": "anyone",
|
|
85
|
+
"effects": "transferFrom → _mint → totalAssets +=",
|
|
86
|
+
"notes": ["pulls before state update"],
|
|
87
|
+
"warnings": ["⚠ you said rounds down; rounds UP (Vault.sol:88)"]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"questions": [
|
|
92
|
+
{ "id": "Q1", "q": "is uint8 enough for decimals?", "answer": "yes, 0–255 (MockToken.sol:9)" }
|
|
93
|
+
],
|
|
94
|
+
"leads": [
|
|
95
|
+
{ "id": "L1", "text": "setFee has no upper bound (Vault.sol:120) — you didn't mention it", "status": "open" }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
111
99
|
|
|
112
|
-
- **
|
|
113
|
-
- **
|
|
114
|
-
- **
|
|
100
|
+
- **description** = how the contract works (verified truth), top-to-bottom: `purpose`, `inheritance`, `storage`, `roles`, then `functions` in **source order** (each with Purpose / Access / Effects + `notes`). Step-3 corrections go in that function's `warnings`, with `file:line`.
|
|
101
|
+
- **questions** = your Q&A; an entry with no `answer` is still open.
|
|
102
|
+
- **leads** = things worth investigating (the Step-3 misses/angles). Only `status:"open"` leads show in the dashboard; `logged` (→ finding) and `dismissed` are kept but hidden.
|
|
115
103
|
|
|
116
|
-
Merge mechanics:
|
|
104
|
+
Merge mechanics — read the record, edit JSON, write it back:
|
|
117
105
|
|
|
118
|
-
1.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
2. Edit `/tmp/diane_<ts>.md` **in place**: scaffold the skeleton if it was empty; otherwise refine existing entries and add new ones where they belong (fill a Storage row, flesh out a function's Effects, answer an open question, add a lead). Do **not** append a dated block, and do **not** duplicate something already captured — sharpen it instead.
|
|
123
|
-
3. Write the whole doc back:
|
|
124
|
-
```bash
|
|
125
|
-
npx hex note write "<contract>" --body-file /tmp/diane_<ts>.md
|
|
126
|
-
```
|
|
106
|
+
1. `npx hex note show "<contract>" > /tmp/diane_<ts>.json` — prints an empty skeleton on first run.
|
|
107
|
+
2. Edit `/tmp/diane_<ts>.json`: add or refine entries where they belong (fill a storage row, flesh out a function, set a question's `answer`, add a lead, attach a `warning`). **Preserve existing `id`s, don't duplicate** — sharpen what's there. Keep arrays concise.
|
|
108
|
+
3. `npx hex note set "<contract>" --json-file /tmp/diane_<ts>.json` (this also recomputes the `/progress` review state).
|
|
127
109
|
|
|
128
|
-
`<contract>` is the session's `contract` value verbatim (e.g. `Vault.sol`). Cross-cutting remarks go to `general
|
|
110
|
+
`<contract>` is the session's `contract` value verbatim (e.g. `Vault.sol`). Cross-cutting remarks still go to the free-form `general` markdown note: `npx hex note append general --body-file …` (`general` isn't structured). If the auditor says they're done with a contract, run `npx hex note done "<contract>"`.
|
|
129
111
|
|
|
130
112
|
## Step 6 — Close the loop & deliver the verdict
|
|
131
113
|
|
|
@@ -143,4 +125,4 @@ Then deliver a candid **pair-audit verdict** — lead with the substance (correc
|
|
|
143
125
|
|
|
144
126
|
- **Never hand-edit** `notes.json` or the `.md` files directly — always go through `npx hex note …`, exactly as the other skills route through `npx hex issue …`.
|
|
145
127
|
- One session per invocation. If the auditor wants the next one, they run `/diane` again (or process them in a loop if they ask).
|
|
146
|
-
- Do not create PoCs from narration — that stays an explicit, auditor-driven `/validate-
|
|
128
|
+
- Do not create PoCs from narration — that stays an explicit, auditor-driven `/validate-finding` step.
|
|
@@ -43,15 +43,15 @@ Never invoke `aa scan` to start a new scan. If the auditor has no scan ID, instr
|
|
|
43
43
|
|
|
44
44
|
## Step 3 — Fetch findings
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Export the full findings for the linked scan:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
aa
|
|
49
|
+
aa findings --all
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
`aa findings --all` writes the full findings (with details) to `auditagent/findings-<scan_id>.md`. If the scan isn't linked yet, run `aa link <scan_id>` first, then re-run `aa findings --all`. (`aa findings` without `--all` only prints summaries; **do not** use `aa scan --report` — that subcommand does not exist.)
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Copy the exported `auditagent/findings-<scan_id>.md` to `<output_dir>/ai-results/auditagent/raw-output.md` and parse it from there.
|
|
55
55
|
|
|
56
56
|
## Step 4 — Normalize into findings.json
|
|
57
57
|
|
|
@@ -142,7 +142,7 @@ Then record the match in `<output_dir>/comparison.json` under `duplicates`. The
|
|
|
142
142
|
|
|
143
143
|
If `comparison.json` doesn't exist, create it with `{ "duplicates": [], "novel": [], "rejected": [] }`.
|
|
144
144
|
|
|
145
|
-
For entries that don't match anything, leave them as `status: "unverified"` (the auditor will run `/validate-
|
|
145
|
+
For entries that don't match anything, leave them as `status: "unverified"` (the auditor will run `/validate-finding AA-<NNN>` on them).
|
|
146
146
|
|
|
147
147
|
## Step 7 — Report
|
|
148
148
|
|
|
@@ -154,7 +154,7 @@ Ingested N AuditAgent findings (scan <scan_id>).
|
|
|
154
154
|
- K flagged as Duplicate of existing entries
|
|
155
155
|
- 0 errors
|
|
156
156
|
|
|
157
|
-
Run /validate-
|
|
157
|
+
Run /validate-finding <id> on any new finding to confirm and promote it to Verified.
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
## Outputs (recap)
|
|
@@ -247,7 +247,7 @@ npx hex issue new --source conformance --source-ref "<spec-item-id, e.g. SC-001>
|
|
|
247
247
|
--severity "<spec-item.severity_hint, default Info>"
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
`--source-ref` is the spec item's own id (e.g. `SC-001`) — Hex keeps that as the back-reference; the board card itself gets the uniform `H-NNN`. These appear on the Issues board as Potential cards immediately; the auditor validates them later with `/validate-
|
|
250
|
+
`--source-ref` is the spec item's own id (e.g. `SC-001`) — Hex keeps that as the back-reference; the board card itself gets the uniform `H-NNN`. These appear on the Issues board as Potential cards immediately; the auditor validates them later with `/validate-finding <id>` (or `/validate-all-findings`).
|
|
251
251
|
|
|
252
252
|
**Idempotency:** before creating, check whether a tracking entry already has this `source_ref` (re-running `/init-audit` shouldn't double-create). Skip items whose `source_ref` is already present.
|
|
253
253
|
|
|
@@ -6,7 +6,9 @@ description: "Walk every Potential issue on the board and validate them one by o
|
|
|
6
6
|
|
|
7
7
|
**Recommended model:** Opus
|
|
8
8
|
|
|
9
|
-
Runs `/validate-
|
|
9
|
+
Runs `/validate-finding` over every issue currently in the **Potential** column, one at a time, so the auditor can clear the backlog in a single pass without naming each id.
|
|
10
|
+
|
|
11
|
+
Each issue gets `/validate-finding`'s full two-axis verdict — correctness **and** the Step-2.5 security-relevance gate. **`auditagent` / `github` findings skew recall-over-precision, so the realism gate is mandatory for them**; `manual` / `conformance` items already passed an auditor's eye and can lean lighter.
|
|
10
12
|
|
|
11
13
|
## Step 1 — Build the work list
|
|
12
14
|
|
|
@@ -14,6 +16,16 @@ Read `<output_dir>/tracking.json`. The Potential column is every entry whose `st
|
|
|
14
16
|
|
|
15
17
|
If `N` is 0, report "No Potential issues to validate." and stop.
|
|
16
18
|
|
|
19
|
+
## Step 1.5 — Cheap realism pre-pass
|
|
20
|
+
|
|
21
|
+
Before deep-diving, do a fast first pass over all `N` and emit a one-line realism read per issue — the Step-2.5 reality-gate take only, no full code trace yet:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
<id> [<sev>] <title> — likely: security | by-design | self-inflicted | incorrect | unsure
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Print it as a table. This front-loads "which of these even deserve a deep trace" so effort concentrates on the genuine security candidates instead of spreading evenly. It's a triage hint, not a verdict — every issue still gets its real verdict in Step 2.
|
|
28
|
+
|
|
17
29
|
## Step 2 — Loop
|
|
18
30
|
|
|
19
31
|
For each issue `i` of `N` (1-based), print a header:
|
|
@@ -22,15 +34,16 @@ For each issue `i` of `N` (1-based), print a header:
|
|
|
22
34
|
── Validating i/N: <id> [<severity>] <title> ──
|
|
23
35
|
```
|
|
24
36
|
|
|
25
|
-
Then run the full **`/validate-
|
|
37
|
+
Then run the full **`/validate-finding`** flow for that `<id>` exactly as that skill defines it:
|
|
26
38
|
1. Assemble context (the tracking entry + its source record + the affected source).
|
|
27
39
|
2. Trace the claim in the code.
|
|
28
|
-
3. Reach a verdict:
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
40
|
+
3. Reach a verdict (correctness + the Step-2.5 security-relevance gate):
|
|
41
|
+
- **Verified** (true + security-relevant) → re-rate severity, ask PoC-vs-memo, write the memo, `hex issue patch <id> ...` + `hex issue move <id> --to verified`.
|
|
42
|
+
- **Rejected — by-design** (true, not security-relevant) → memo with `Disposition: by-design`, `hex issue patch <id> --resolution Acknowledged` + `hex issue move <id> --to rejected`.
|
|
43
|
+
- **Rejected — incorrect** (false) → `hex issue move <id> --to rejected` (no resolution), memo records why.
|
|
31
44
|
- **Uncertain** → leave in place, `hex issue patch <id> --notes "<open questions>"`.
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
Batch by tier — don't rubber-stamp. **Security-relevant candidates get the full per-issue prompt flow** (PoC-vs-memo and severity), one at a time — the auditor stays in control of every real finding. **Info / by-design / self-inflicted items may be batched**: collect them and confirm their dispositions (and any PoC/severity calls) in a single consolidated **AskUserQuestion** instead of N separate modals. That keeps a large sweep controllable without diluting scrutiny on the findings that matter.
|
|
34
47
|
|
|
35
48
|
## Step 3 — Resume safety
|
|
36
49
|
|
|
@@ -42,9 +55,10 @@ After the loop, print a tally:
|
|
|
42
55
|
|
|
43
56
|
```
|
|
44
57
|
Validated N Potential issues:
|
|
45
|
-
→ Verified:
|
|
46
|
-
→
|
|
47
|
-
→
|
|
58
|
+
→ Verified: V
|
|
59
|
+
→ Rejected (incorrect): X
|
|
60
|
+
→ Rejected (valid, not security): B
|
|
61
|
+
→ Left for review (uncertain): U
|
|
48
62
|
```
|
|
49
63
|
|
|
50
64
|
Remind the auditor of the next steps: `/sync-issues` to push the Verified findings to GitHub, then `/generate-overleaf` for the report.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Validate any potential
|
|
2
|
+
description: "Validate any potential finding on the board (manual / auditagent / conformance / github): check both correctness and security relevance, then route to Verified, Rejected, or leave Uncertain"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Skill: Validate
|
|
5
|
+
# Skill: Validate Finding
|
|
6
6
|
|
|
7
7
|
**Recommended model:** Opus
|
|
8
8
|
|
|
@@ -15,17 +15,17 @@ Takes a single potential issue from the dashboard's Issues board and runs an ind
|
|
|
15
15
|
- **Conformance** (materialized by `/init-audit`) — `source: "conformance"`, `status: "pending_validation"`.
|
|
16
16
|
- **GitHub** (teammate findings via `/sync-issues`) — `source: "github"`, `status: "pending_validation"`.
|
|
17
17
|
|
|
18
|
-
The skill writes a validation memo (always), optionally generates a PoC, and
|
|
18
|
+
The skill judges **two** things, not one: first **correctness** (is the claim true?), then for true claims **security relevance** (is it actually a vuln worth reporting, or true-but-by-design / a missing feature / self-inflicted?). It writes a validation memo (always), optionally generates a PoC, and moves the card. Outcomes: **Verified** (true + security-relevant), **Rejected** (either factually incorrect, *or* true-but-not-a-security-issue), or **Uncertain**. Both Rejected kinds land in the board's **Rejected** column, distinguished by disposition.
|
|
19
19
|
|
|
20
20
|
## Invocation
|
|
21
21
|
|
|
22
22
|
The auditor invokes by id or by free-text reference:
|
|
23
23
|
|
|
24
24
|
```
|
|
25
|
-
/validate-
|
|
26
|
-
/validate-
|
|
27
|
-
/validate-
|
|
28
|
-
/validate-
|
|
25
|
+
/validate-finding F003
|
|
26
|
+
/validate-finding SC-007
|
|
27
|
+
/validate-finding AA-N002
|
|
28
|
+
/validate-finding for the rounding issue in Vault.sol
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Resolve to a single tracking entry by:
|
|
@@ -65,12 +65,35 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
|
|
|
65
65
|
- Check existing protections (modifiers, reentrancy guards, input validation, economic constraints).
|
|
66
66
|
- For conformance items: re-read the cited spec section, compare to the implementation.
|
|
67
67
|
|
|
68
|
+
### 2.5 Reality gate — is it security-relevant?
|
|
69
|
+
|
|
70
|
+
Correctness is not enough. AI scanners (and sometimes teammates) surface claims that are **factually true but not security issues** — by-design behavior, missing features, defense-in-depth nice-to-haves, or risks only the deployer can inflict on themselves. This is the precision pass. **Run it for every finding; treat it as mandatory for `source: auditagent | github`** (those skew recall-over-precision). `manual` / `conformance` items already passed an auditor's eye, so they can lean lighter.
|
|
71
|
+
|
|
72
|
+
For a claim you found **true**, run these five checks — each one pushes toward *not-security-relevant*:
|
|
73
|
+
|
|
74
|
+
- **Trigger model.** Can only the deployer/owner harm their own deployment? Then it's self-inflicted, not an attack.
|
|
75
|
+
- **Out-of-scope dependency.** Does the impact require an out-of-scope component (e.g. the integrating contract) to be buggy or malicious — and if so, does the finding grant a capability that component didn't already have? No capability gain → defense-in-depth noise.
|
|
76
|
+
- **Trivial workaround.** Is there an integrator-controlled or already-documented mitigation (a constructor param, a config, standard usage)?
|
|
77
|
+
- **Class.** Defect, deliberate design decision, or missing feature? Only defects are findings.
|
|
78
|
+
- **Net test.** Would a senior auditor put this in a client report as a security issue, as an informational note, or not at all?
|
|
79
|
+
|
|
80
|
+
Carry the answers into the verdict and the memo (Triggerability / Depends-on-out-of-scope / Disposition).
|
|
81
|
+
|
|
68
82
|
### 3. Verdict
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
Combine the two axes — correctness (Step 2) and security relevance (Step 2.5):
|
|
85
|
+
|
|
86
|
+
| Correctness | Security-relevant? | → Outcome |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| True | Yes | **Verified** |
|
|
89
|
+
| True | No | **Rejected** (by-design / informational) |
|
|
90
|
+
| False | — | **Rejected** (incorrect) |
|
|
91
|
+
| Uncertain | — | **Uncertain** |
|
|
92
|
+
|
|
93
|
+
**Verified — true and security-relevant:**
|
|
71
94
|
|
|
72
95
|
1. Use **AskUserQuestion** to ask:
|
|
73
|
-
> The
|
|
96
|
+
> The finding appears **VALID and security-relevant**. How would you like to proceed?
|
|
74
97
|
>
|
|
75
98
|
> 1. Generate a full PoC test, then promote to Verified.
|
|
76
99
|
> 2. Accept with rational verification only — validation memo, no PoC.
|
|
@@ -81,7 +104,9 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
|
|
|
81
104
|
|
|
82
105
|
3. **If memo-only chosen:** no PoC file.
|
|
83
106
|
|
|
84
|
-
4. **
|
|
107
|
+
4. **Calibrate severity — don't inherit it.** The source's severity (especially AuditAgent's) is a *hypothesis*, not authoritative. Re-derive it from realistic impact × likelihood; many technically-true findings collapse to Info. A finding whose worst case is self-inflicted or chain-conditional must say so and re-rate accordingly.
|
|
108
|
+
|
|
109
|
+
5. **Persist via the CLI — do not hand-edit `findings.json` or `tracking.json`.** The `hex issue` command is the single source of truth for board mutations; it materializes a `findings.json` entry from the source record (conformance / auditagent / github) when one does not exist yet, and routes fields correctly. Write the validation memo and the recommendation to temp files, then:
|
|
85
110
|
|
|
86
111
|
```bash
|
|
87
112
|
# Land the editable fields (materializes a finding for conformance/auditagent/github ids):
|
|
@@ -99,21 +124,34 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
|
|
|
99
124
|
|
|
100
125
|
Hex does not write `@audit-issue` comments into the client's source files — the board and finding record are the only place the issue lives.
|
|
101
126
|
|
|
102
|
-
|
|
127
|
+
6. Use **AskUserQuestion** to ask:
|
|
103
128
|
> Issue verified as **\<id\>** with severity **\<current severity\>**.
|
|
104
129
|
>
|
|
105
130
|
> Would you like to change the severity? Reply with the new severity (Critical / High / Medium / Low / Info), or **no** to keep it.
|
|
106
131
|
|
|
107
|
-
|
|
132
|
+
7. If the user provides a new severity, run `npx hex issue patch <id> --severity <NewSeverity>`.
|
|
108
133
|
|
|
109
|
-
**
|
|
134
|
+
**Rejected — true but not a security issue (by-design / informational):**
|
|
110
135
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
The claim is correct, but Step 2.5 says it isn't a vuln worth reporting (self-inflicted, design decision, missing feature, defense-in-depth noise). This is **not** the same as "incorrect" — give it its own disposition so it's distinguishable from a false positive and AuditAgent gets honest feedback.
|
|
137
|
+
|
|
138
|
+
1. Write the memo with `Disposition: by-design` (or `informational`) and the Step-2.5 reasoning.
|
|
139
|
+
2. Mark the disposition and move it:
|
|
140
|
+
```bash
|
|
141
|
+
npx hex issue patch <id> --resolution Acknowledged
|
|
142
|
+
npx hex issue move <id> --to rejected
|
|
143
|
+
```
|
|
144
|
+
`Acknowledged` on a Rejected card means "AA was right, but it's by-design / not a security issue" — the board shows a **By-design** chip. (A Rejected card with *no* resolution = factually incorrect; see below.)
|
|
145
|
+
3. Do NOT write a finding body or carry it into the report.
|
|
146
|
+
|
|
147
|
+
**Rejected — incorrect (false positive):**
|
|
148
|
+
|
|
149
|
+
The claim is factually wrong. Explain why:
|
|
150
|
+
- Is the attack path blocked? By what specifically (modifier, require, state check)?
|
|
151
|
+
- Are the preconditions impossible? Why?
|
|
152
|
+
- Is the impact assessment wrong? What actually happens?
|
|
153
|
+
|
|
154
|
+
Move it to the Rejected column with **no** resolution (the board shows an **Invalid** chip): `npx hex issue move <id> --to rejected`. Do NOT write a finding body — the memo records the reasoning.
|
|
117
155
|
|
|
118
156
|
**Uncertain:**
|
|
119
157
|
|
|
@@ -126,11 +164,15 @@ For conformance items, the "claim" is `spec_text` + the finding paragraph — i.
|
|
|
126
164
|
Write `<output_dir>/validations/<id>_memo.md` (always — for valid, invalid, and uncertain verdicts):
|
|
127
165
|
|
|
128
166
|
```markdown
|
|
129
|
-
#
|
|
167
|
+
# Finding Validation: <ID> — <Title>
|
|
130
168
|
|
|
131
169
|
**Source:** <manual | auditagent | conformance | github>
|
|
132
170
|
**Original record:** <pointer to the specific record being validated — e.g., findings.json#F003, ai-results/auditagent/findings.json#AA-014>
|
|
133
|
-
**
|
|
171
|
+
**Correctness:** True / False / Uncertain
|
|
172
|
+
**Security relevance:** security-relevant / not-security-relevant / n/a
|
|
173
|
+
**Triggerability:** external | self-inflicted
|
|
174
|
+
**Depends on out-of-scope component:** no | yes (capability gain: yes/no)
|
|
175
|
+
**Disposition:** security-finding | by-design | informational | false
|
|
134
176
|
**Date:** <YYYY-MM-DD>
|
|
135
177
|
|
|
136
178
|
## Claim being validated
|
|
@@ -142,13 +184,17 @@ Write `<output_dir>/validations/<id>_memo.md` (always — for valid, invalid, an
|
|
|
142
184
|
## Protections evaluated
|
|
143
185
|
[Access control, reentrancy guards, input validation, economic constraints, etc.]
|
|
144
186
|
|
|
187
|
+
## Reality gate
|
|
188
|
+
[The five Step-2.5 checks for true claims: trigger model, out-of-scope dependency, trivial workaround, class, net test. Skip with "n/a (claim is false)" for incorrect findings.]
|
|
189
|
+
|
|
145
190
|
## Verdict reasoning
|
|
146
|
-
[Why
|
|
191
|
+
[Why Verified / Rejected (by-design or incorrect) / Uncertain — tie it to the two axes above]
|
|
147
192
|
|
|
148
193
|
## Action taken
|
|
149
|
-
[If
|
|
150
|
-
[If
|
|
151
|
-
[If
|
|
194
|
+
[If Verified + PoC: PoC at <path>, test passing, finding updated/created; severity re-rated to <X>]
|
|
195
|
+
[If Verified + memo-only: finding updated/created, no PoC; severity <X>]
|
|
196
|
+
[If Rejected by-design: moved to Rejected with resolution Acknowledged, no finding written]
|
|
197
|
+
[If Rejected incorrect: moved to Rejected, no resolution, no finding written]
|
|
152
198
|
[If uncertain: open questions listed; tracking status unchanged]
|
|
153
199
|
```
|
|
154
200
|
|
|
@@ -6,7 +6,7 @@ description: "Write a manual issue as a Potential card on the board (pending val
|
|
|
6
6
|
|
|
7
7
|
**Recommended model:** Sonnet
|
|
8
8
|
|
|
9
|
-
This skill records a manual issue that the auditor has spotted during review. The issue lands on the dashboard's Issues board as a **Potential** card with `status: "pending_validation"` and `source: "manual"`. The auditor promotes it to Verified later (via drag-drop on the board, or by running `/validate-
|
|
9
|
+
This skill records a manual issue that the auditor has spotted during review. The issue lands on the dashboard's Issues board as a **Potential** card with `status: "pending_validation"` and `source: "manual"`. The auditor promotes it to Verified later (via drag-drop on the board, or by running `/validate-finding <id>` which produces a validation memo and optional PoC).
|
|
10
10
|
|
|
11
11
|
## Context Assembly
|
|
12
12
|
|
|
@@ -140,8 +140,8 @@ There is **no source-file annotation** — Hex does not write `@audit-issue` com
|
|
|
140
140
|
|
|
141
141
|
The finding appears on the dashboard's `/issues` board as a Potential card (`H-NNN`). The auditor can:
|
|
142
142
|
|
|
143
|
-
- Run `/validate-
|
|
143
|
+
- Run `/validate-finding <id>` to write a validation memo and optionally generate a PoC; on success the card moves to Verified.
|
|
144
144
|
- Drag the card from Potential → Verified on the board if no PoC is needed (e.g., trivial Info-level issues).
|
|
145
145
|
- Run `/sync-issues` (once findings are Verified and `settings.github.repo` is set) to push them to GitHub.
|
|
146
146
|
|
|
147
|
-
Do NOT run `/validate-
|
|
147
|
+
Do NOT run `/validate-finding` or `/sync-issues` automatically — those are auditor-driven.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hex-audit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI toolkit for Solidity smart contract auditors",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"build": "tsc && npm run copy-skills && npm run build:dashboard",
|
|
13
13
|
"build:cli": "tsc && npm run copy-skills",
|
|
14
14
|
"build:dashboard": "next build dashboard",
|
|
15
|
-
"copy-skills": "node -e \"const fs=require('fs');const p=require('path');
|
|
15
|
+
"copy-skills": "node -e \"const fs=require('fs');const p=require('path');const d=p.join('dist','skills');fs.rmSync(d,{recursive:true,force:true});fs.cpSync(p.join('src','skills'),d,{recursive:true})\"",
|
|
16
16
|
"clean:next-cache": "node -e \"const fs=require('fs');for (const d of ['dashboard/.next/cache','dashboard/.next/trace','dashboard/.next/types']) fs.rmSync(d,{recursive:true,force:true})\"",
|
|
17
17
|
"prepack": "npm run build && npm run clean:next-cache",
|
|
18
18
|
"dev:cli": "tsc --watch",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"dashboard/public",
|
|
32
32
|
"dashboard/next.config.mjs",
|
|
33
33
|
"dashboard/next-env.d.ts",
|
|
34
|
+
"dashboard/whisper-server.py",
|
|
34
35
|
"dashboard/postcss.config.cjs",
|
|
35
36
|
"dashboard/tailwind.config.ts",
|
|
36
37
|
"dashboard/tsconfig.json",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4521],{4260:function(e,t,n){Promise.resolve().then(n.bind(n,7063))},7063:function(e,t,n){"use strict";n.d(t,{IssuesBoardClient:function(){return m}});var r=n(7821),a=n(8078),o=n(3924);let s={Critical:"bg-[var(--critical)]/18 text-[var(--critical)]",High:"bg-[var(--high)]/18 text-[var(--high)]",Medium:"bg-[var(--medium)]/18 text-[var(--medium)]",Low:"bg-[var(--low)]/18 text-[var(--low)]",Info:"bg-[var(--info)]/18 text-[var(--info)]"};function i(e){var t;let{severity:n}=e,a=null!==(t=s[n])&&void 0!==t?t:s.Info;return(0,r.jsx)("span",{className:"inline-flex items-center rounded-md px-2 py-0.5 text-caption font-medium ".concat(a),children:n})}var c=n(9487);function l(e,t){var n;let r=new Set,a=e.code_locations.map(e=>e.file).filter(e=>e&&!r.has(e)&&r.add(e)).map(e=>(function(e,t){if(t.repositoryUrl&&t.commit){let n=t.repositoryUrl.replace(/\/+$/,"");return"[`".concat(e,"`](").concat(n,"/blob/").concat(t.commit,"/").concat(e,")")}return"[`".concat(e,"`]()")})(e,t)).join(", "),o=e.resolution||"Unresolved",s=null!==(n=e.update_from_client)&&void 0!==n?n:"";return["**File(s)**: ".concat(a),"","**Description**: ".concat(function(e){if(!e)return e;let t=!1,n="";return e.split("\n").map(e=>{let r=e.match(/^\s*(```+|~~~+)/);if(r)return t?r[1][0]===n&&(t=!1,n=""):(t=!0,n=r[1][0]),e;if(t)return e;let a=e.match(/^\s*#{1,6}\s+(.*\S)\s*$/);return a?"**".concat(a[1],"**"):/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(e)?"":e}).join("\n")}(e.description)),"","**Recommendation(s)**: ".concat(e.recommendation),"","**Status**: ".concat(o),"","**Update from the client**: ".concat(s)].join("\n")}let d=[{id:"potential",label:"Potential",description:"Awaiting validation"},{id:"verified",label:"Verified",description:"Confirmed, ready to push"},{id:"synced",label:"Synced to GitHub",description:"On GitHub — edit there, not here"},{id:"invalid",label:"Invalid",description:"Rejected after review"},{id:"duplicate",label:"Duplicate",description:"Covered by another entry"}],u={Critical:0,High:1,Medium:2,Low:3,Info:4},x=["Critical","High","Medium","Low","Info"],p=["Fixed","Mitigated","Acknowledged","Not Fixed","Unresolved"];function m(e){var t;let{issues:n,formatCtx:s={}}=e,[i,c]=(0,a.useState)(n),[l,x]=(0,a.useState)(null),[p,m]=(0,a.useState)("all");(0,a.useEffect)(()=>{c(n)},[n]);let h=(0,o.Dy)((0,o.VT)(o.we,{activationConstraint:{distance:4}})),y=(0,a.useMemo)(()=>"all"===p?i:i.filter(e=>e.source===p),[i,p]),v=(0,a.useMemo)(()=>{let e={potential:[],verified:[],synced:[],invalid:[],duplicate:[]};for(let t of y)e[t.column].push(t);for(let t of Object.values(e))t.sort((e,t)=>{var n,r;return(null!==(n=u[e.severity])&&void 0!==n?n:5)-(null!==(r=u[t.severity])&&void 0!==r?r:5)||e.id.localeCompare(t.id)});return e},[y]),j=(0,a.useMemo)(()=>{let e={potential:0,verified:0,synced:0,invalid:0,duplicate:0};for(let t of i)e[t.column]++;return e},[i]),N=(0,a.useCallback)(async(e,t)=>{c(n=>n.map(n=>n.id===e?{...n,column:t,status:function(e){switch(e){case"potential":return"pending_validation";case"verified":return"verified";case"synced":return"synced";case"invalid":return"rejected";case"duplicate":return"duplicate"}}(t),duplicate_of:"duplicate"===t?n.duplicate_of:null}:n));try{let n=await fetch("/api/issues/".concat(encodeURIComponent(e),"/move"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({column:t})});if(!n.ok){var r;let e=await n.json().catch(()=>({}));throw Error(null!==(r=e.error)&&void 0!==r?r:"move failed")}}catch(t){console.error(t),c(t=>t.map(t=>{var r;return t.id===e&&null!==(r=n.find(t=>t.id===e))&&void 0!==r?r:t}))}},[n]),w=(0,a.useCallback)(async(e,t)=>{c(n=>n.map(n=>n.id===e?{...n,...t}:n));try{let r=await fetch("/api/issues/".concat(encodeURIComponent(e)),{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){var n;let e=await r.json().catch(()=>({}));throw Error(null!==(n=e.error)&&void 0!==n?n:"update failed")}}catch(e){console.error(e)}},[]),_=null!==(t=i.find(e=>e.id===l))&&void 0!==t?t:null;return(0,r.jsxs)("div",{children:[(0,r.jsxs)("header",{className:"mb-6 flex flex-wrap items-end justify-between gap-3",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("h1",{className:"text-title font-semibold text-text-primary",children:"Issues"}),(0,r.jsxs)("p",{className:"mt-1 text-body text-text-secondary",children:[i.length," total — ",j.potential," potential, ",j.verified," verified,"," ",j.synced," synced, ",j.invalid," invalid, ",j.duplicate," duplicate. Drag a card between columns; click to edit. Synced cards are read-only (edit on GitHub)."]})]}),(0,r.jsx)(b,{value:p,onChange:m})]}),(0,r.jsx)(o.LB,{sensors:h,onDragEnd:e=>{let{active:t,over:n}=e;if(!n)return;let r=n.id,a=t.id,o=i.find(e=>e.id===a);o&&o.column!==r&&"synced"!==r&&"synced"!==o.column&&N(a,r)},children:(0,r.jsx)("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-5",children:d.map(e=>(0,r.jsx)(f,{column:e,issues:v[e.id],onOpen:x},e.id))})}),_&&(0,r.jsx)(g,{issue:_,formatCtx:s,onClose:()=>x(null),onSave:e=>w(_.id,e)})]})}function b(e){let{value:t,onChange:n}=e;return(0,r.jsx)("div",{className:"flex flex-wrap gap-1.5",children:[{id:"all",label:"All sources"},{id:"manual",label:"Manual"},{id:"auditagent",label:"AuditAgent"},{id:"conformance",label:"Conformance"},{id:"github",label:"GitHub"}].map(e=>{let a=t===e.id;return(0,r.jsx)("button",{type:"button",onClick:()=>n(e.id),className:"rounded-md px-2.5 py-1 text-caption font-medium ".concat(a?"bg-accent text-surface-0":"bg-surface-2 text-text-secondary hover:text-text-primary"),children:e.label},e.id)})})}function f(e){let{column:t,issues:n,onOpen:a}=e,{isOver:s,setNodeRef:i}=(0,o.Zj)({id:t.id,disabled:"synced"===t.id});return(0,r.jsxs)("section",{ref:i,"aria-label":t.label,className:"flex flex-col gap-2 rounded-lg border bg-surface-1 p-3 transition-colors ".concat(s?"border-accent bg-surface-2":"border-border-subtle"),children:[(0,r.jsxs)("header",{className:"flex items-baseline justify-between",children:[(0,r.jsx)("h2",{className:"text-heading font-medium text-text-primary",children:t.label}),(0,r.jsx)("span",{className:"text-caption text-text-tertiary",children:n.length})]}),(0,r.jsx)("p",{className:"text-caption text-text-tertiary",children:t.description}),(0,r.jsxs)("div",{className:"mt-1 flex flex-col gap-2",children:[n.map(e=>(0,r.jsx)(h,{issue:e,onOpen:a},e.id)),0===n.length&&(0,r.jsx)("div",{className:"rounded-md border border-dashed border-border-subtle py-6 text-center text-caption text-text-tertiary",children:"Empty"})]})]})}function h(e){let{issue:t,onOpen:n}=e,a="synced"===t.column,{attributes:s,listeners:c,setNodeRef:l,isDragging:d,transform:u}=(0,o.O1)({id:t.id,disabled:a}),x=u?{transform:"translate3d(".concat(u.x,"px, ").concat(u.y,"px, 0)"),zIndex:30}:{};return(0,r.jsxs)("article",{ref:l,style:x,...a?{}:s,...a?{}:c,onClick:e=>{u||(e.stopPropagation(),n(t.id))},className:"group rounded-md border bg-surface-0 p-3 text-left ".concat(a?"cursor-pointer":"cursor-grab active:cursor-grabbing"," ").concat(d?"border-accent shadow-lg":"border-border-subtle hover:border-border-default"),children:[(0,r.jsxs)("div",{className:"flex items-start justify-between gap-2",children:[(0,r.jsx)("span",{className:"font-mono text-caption text-text-tertiary",children:t.id}),(0,r.jsx)(i,{severity:t.severity})]}),(0,r.jsx)("h3",{title:t.title,className:"mt-1 text-body font-medium text-text-primary line-clamp-3",children:t.title}),(0,r.jsxs)("div",{className:"mt-2 flex flex-wrap items-center gap-1.5 text-caption",children:[(0,r.jsx)(y,{source:t.source}),(0,r.jsx)(v,{issue:t}),t.duplicate_of&&(0,r.jsxs)("span",{className:"rounded-md bg-surface-2 px-1.5 py-0.5 text-text-secondary",children:["dup of ",t.duplicate_of]}),"passing"===t.poc_status&&(0,r.jsx)("span",{className:"rounded-md bg-[var(--low)]/15 px-1.5 py-0.5 text-[var(--low)]",children:"PoC ✓"}),"failing"===t.poc_status&&(0,r.jsx)("span",{className:"rounded-md bg-[var(--critical)]/15 px-1.5 py-0.5 text-[var(--critical)]",children:"PoC ✕"}),t.resolution&&(0,r.jsx)("span",{className:"rounded-md bg-surface-2 px-1.5 py-0.5 text-text-secondary",children:t.resolution})]}),t.match_signals&&(0,r.jsx)("div",{className:"mt-2 text-caption text-text-tertiary",children:(0,r.jsx)("span",{className:"font-mono",children:[t.match_signals.contract?"contract":null,t.match_signals.function?"function":null,"different"!==t.match_signals.root_cause?"rc:".concat(t.match_signals.root_cause):null,"different"!==t.match_signals.attack_vector?"av:".concat(t.match_signals.attack_vector):null].filter(Boolean).join(" \xb7 ")})})]})}function y(e){var t;let{source:n}=e;return(0,r.jsx)("span",{className:"rounded-md bg-surface-2 px-1.5 py-0.5 text-text-tertiary",children:null!==(t=({manual:"Manual",auditagent:"AuditAgent",conformance:"Conformance",github:"GitHub"})[n])&&void 0!==t?t:n})}function v(e){var t,n;let{issue:a}=e,o=null!==(t=a.sync_state)&&void 0!==t?t:"none";if("none"===o)return null;if("unsynced"===o)return(0,r.jsx)("span",{title:"Run /sync-issues to push verified findings to GitHub.",className:"rounded-md border border-dashed border-border-default px-1.5 py-0.5 text-text-tertiary",children:"GH unsynced"});if("conflict"===o)return(0,r.jsx)("span",{title:"Local and remote diverged. Re-run /sync-issues to reconcile.",className:"rounded-md bg-[var(--critical)]/15 px-1.5 py-0.5 text-[var(--critical)]",children:"GH conflict"});let s="synced_closed"===o,i="GH #".concat(null!==(n=a.github_issue_number)&&void 0!==n?n:"?"," ").concat(s?"closed":"open"),c=s?"bg-surface-2 text-text-tertiary":"bg-[var(--low)]/15 text-[var(--low)]";return a.github_issue_url?(0,r.jsx)("a",{href:a.github_issue_url,target:"_blank",rel:"noopener noreferrer",onClick:e=>e.stopPropagation(),className:"rounded-md px-1.5 py-0.5 hover:underline ".concat(c),children:i}):(0,r.jsx)("span",{className:"rounded-md px-1.5 py-0.5 ".concat(c),children:i})}function g(e){var t,n,o;let{issue:s,formatCtx:i,onClose:d,onSave:u}=e,[m,b]=(0,a.useState)(s.title),[f,h]=(0,a.useState)(s.severity),[g,N]=(0,a.useState)(s.description),[w,_]=(0,a.useState)(s.recommendation),[C,k]=(0,a.useState)(null!==(t=s.resolution)&&void 0!==t?t:""),[S,H]=(0,a.useState)(null!==(n=s.update_from_client)&&void 0!==n?n:""),[M,G]=(0,a.useState)(!1),[E,I]=(0,a.useState)(!1),[R,O]=(0,a.useState)(!1),P=(0,a.useRef)(null),T="synced"===s.status;(0,a.useEffect)(()=>{var e;null===(e=P.current)||void 0===e||e.focus()},[]);let D=async()=>{let e=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"## [".concat(e.severity,"] ").concat(e.title,"\n\n").concat(l(e,t),"\n\n---\n")}({...s,title:m,severity:f,description:g,recommendation:w,resolution:C||void 0,update_from_client:S||void 0},i);try{await navigator.clipboard.writeText(e),I(!0),setTimeout(()=>I(!1),1500)}catch(e){}},U=async()=>{G(!0),await u({title:m,severity:f,description:g,recommendation:w,resolution:C||void 0,update_from_client:S||void 0}),G(!1),O(!1)};return(0,r.jsx)("div",{ref:P,tabIndex:-1,className:"fixed inset-0 z-50 flex items-center justify-center p-4 outline-none",style:{background:"var(--overlay-bg)"},onClick:d,onKeyDown:e=>{"Escape"===e.key&&d()},children:(0,r.jsxs)("div",{role:"dialog","aria-modal":"true","aria-label":"Issue ".concat(s.id),className:"relative flex h-[85vh] w-full max-w-4xl flex-col overflow-hidden border border-border-emphasis bg-surface-1 shadow-2xl",style:{borderRadius:"var(--radius-md)",boxShadow:"var(--shadow-accent)"},onClick:e=>e.stopPropagation(),children:[(0,r.jsxs)("div",{className:"flex items-center justify-between border-b border-border-default bg-surface-2 px-4 py-3",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2 text-caption",children:[(0,r.jsx)("span",{className:"font-mono text-body text-text-tertiary",children:s.id}),(0,r.jsx)(y,{source:s.source}),(0,r.jsx)(v,{issue:s})]}),(0,r.jsx)("button",{type:"button",onClick:d,"aria-label":"Close",className:"rounded p-1 text-text-tertiary hover:bg-surface-3 hover:text-text-primary",children:(0,r.jsx)("svg",{className:"h-5 w-5","aria-hidden":"true",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,r.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"})})})]}),(0,r.jsxs)("div",{className:"flex-1 overflow-y-auto p-4",children:[T&&(0,r.jsxs)("div",{className:"mb-4 rounded-md border border-border-default bg-surface-2 px-3 py-2 text-caption text-text-secondary",children:["This issue is synced to GitHub and is read-only here. Edit it on GitHub, then run"," ",(0,r.jsx)("span",{className:"font-mono",children:"/sync-issues"})," to pull the changes back."]}),R?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(j,{label:"Title",children:(0,r.jsx)("input",{type:"text",value:m,onChange:e=>b(e.target.value),disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 text-body text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})}),(0,r.jsxs)("div",{className:"mt-3 grid grid-cols-2 gap-3",children:[(0,r.jsx)(j,{label:"Severity",children:(0,r.jsx)("select",{value:f,onChange:e=>h(e.target.value),disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 text-body text-text-primary focus:border-accent focus:outline-none disabled:opacity-60",children:x.map(e=>(0,r.jsx)("option",{value:e,children:e},e))})}),(0,r.jsx)(j,{label:"Resolution (verified only)",children:(0,r.jsxs)("select",{value:C,onChange:e=>k(e.target.value),disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 text-body text-text-primary focus:border-accent focus:outline-none disabled:opacity-60",children:[(0,r.jsx)("option",{value:"",children:"— none —"}),p.map(e=>(0,r.jsx)("option",{value:e,children:e},e))]})})]}),(0,r.jsx)(j,{label:"Description",className:"mt-3",children:(0,r.jsx)("textarea",{value:g,onChange:e=>N(e.target.value),rows:8,disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})}),(0,r.jsx)(j,{label:"Recommendation",className:"mt-3",children:(0,r.jsx)("textarea",{value:w,onChange:e=>_(e.target.value),rows:4,disabled:T,className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})}),(0,r.jsx)(j,{label:"Update from the client",className:"mt-3",children:(0,r.jsx)("textarea",{value:S,onChange:e=>H(e.target.value),rows:4,disabled:T,placeholder:"How the client responded — used by /generate-overleaf.",className:"w-full rounded-md border border-border-default bg-surface-0 px-3 py-2 font-mono text-caption text-text-primary focus:border-accent focus:outline-none disabled:opacity-60"})})]}):(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(c.MarkdownRenderer,{content:"**Title**: ".concat(s.title,"\n\n**Severity**: ").concat(s.severity,"\n\n").concat(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return l(e,t)}(s,i))})}),s.code_locations.length>0&&(0,r.jsxs)("div",{className:"mt-4",children:[(0,r.jsx)("div",{className:"mb-1 text-caption font-medium text-text-tertiary",children:"Code locations"}),(0,r.jsx)("ul",{className:"space-y-1 font-mono text-caption text-text-secondary",children:s.code_locations.map((e,t)=>(0,r.jsx)("li",{children:e.file},"".concat(e.file,"-").concat(t)))})]}),s.match_signals&&(0,r.jsxs)("div",{className:"mt-4",children:[(0,r.jsx)("div",{className:"mb-1 text-caption font-medium text-text-tertiary",children:"Duplicate signals"}),(0,r.jsx)("p",{className:"text-caption text-text-secondary",children:null!==(o=s.reasoning)&&void 0!==o?o:"Matched against another entry."}),(0,r.jsxs)("div",{className:"mt-1 flex flex-wrap gap-1.5 font-mono text-caption text-text-tertiary",children:[(0,r.jsxs)("span",{children:["contract: ",s.match_signals.contract?"✓":"—"]}),(0,r.jsxs)("span",{children:["function: ",s.match_signals.function?"✓":"—"]}),(0,r.jsxs)("span",{children:["root_cause: ",s.match_signals.root_cause]}),(0,r.jsxs)("span",{children:["attack_vector: ",s.match_signals.attack_vector]})]})]})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between gap-2 border-t border-border-default bg-surface-2 px-4 py-3",children:[(0,r.jsx)("button",{type:"button",onClick:D,className:"rounded-md border border-border-default px-3 py-1.5 text-body text-text-secondary hover:bg-surface-3 hover:text-text-primary",children:E?"Copied!":"Copy as HackMD"}),(0,r.jsx)("div",{className:"flex items-center gap-2",children:R?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("button",{type:"button",onClick:()=>O(!1),className:"rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary",children:"Cancel"}),(0,r.jsx)("button",{type:"button",onClick:U,disabled:M,className:"rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:opacity-50",children:M?"Saving…":"Save"})]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("button",{type:"button",onClick:d,className:"rounded-md px-3 py-1.5 text-body text-text-secondary hover:text-text-primary",children:"Close"}),(0,r.jsx)("button",{type:"button",onClick:()=>O(!0),disabled:T,title:T?"Synced to GitHub — edit it there, then run /sync-issues to pull the changes back.":void 0,className:"rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 hover:bg-accent-secondary disabled:cursor-not-allowed disabled:opacity-40",children:"Edit"})]})})]})]})})}function j(e){let{label:t,className:n,children:a}=e;return(0,r.jsxs)("label",{className:"block ".concat(null!=n?n:""),children:[(0,r.jsx)("span",{className:"mb-1 block text-caption font-medium text-text-tertiary",children:t}),a]})}},9487:function(e,t,n){"use strict";n.d(t,{MarkdownRenderer:function(){return s}});var r=n(7821),a=n(8525),o=n(5592);function s(e){let{content:t,className:n}=e;return(0,r.jsx)("div",{className:"prose max-w-none\n prose-headings:text-text-primary\n prose-p:text-text-secondary\n prose-strong:text-text-primary\n prose-code:text-accent\n prose-code:before:content-none\n prose-code:after:content-none\n prose-pre:bg-surface-0\n prose-pre:border\n prose-pre:border-border-default\n prose-a:text-accent\n prose-a:no-underline\n hover:prose-a:underline\n prose-th:text-text-primary\n prose-td:text-text-secondary\n prose-hr:border-border-default\n prose-blockquote:border-border-emphasis\n prose-blockquote:text-text-secondary\n prose-li:text-text-secondary\n ".concat(null!=n?n:""),children:(0,r.jsx)(a.UG,{remarkPlugins:[o.Z],children:t})})}}},function(e){e.O(0,[7397,3924,7115,3205,1744],function(){return e(e.s=4260)}),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9151],{3152:function(e,t,r){Promise.resolve().then(r.bind(r,2024))},2024:function(e,t,r){"use strict";r.d(t,{NotesClient:function(){return d}});var n=r(7821),a=r(8078),o=r(168),s=r(9487);let c="__other__";function d(e){var t,r;let{general:d,contractNotes:i,contractOptions:l,activeContract:u,unprocessedCount:p}=e,x=(0,o.useRouter)(),b=(0,a.useMemo)(()=>[{key:"general",label:"General",body:d},...i.map(e=>({key:e.name,label:e.name,body:e.body}))],[d,i]),m=u&&b.some(e=>e.key===u)?u:"general",[f,y]=(0,a.useState)(m),h=null!==(t=b.find(e=>e.key===f))&&void 0!==t?t:b[0],[g,v]=(0,a.useState)(!1),[j,N]=(0,a.useState)(""),[k,w]=(0,a.useState)(!1),C=async()=>{w(!0);try{var e;let t=await fetch("/api/notes",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({target:h.key,body:j})});if(!t.ok)throw Error(null!==(e=(await t.json().catch(()=>({}))).error)&&void 0!==e?e:"save failed");v(!1),x.refresh()}catch(e){U(e instanceof Error?e.message:"Save failed")}finally{w(!1)}},[S,R]=(0,a.useState)("idle"),[_,E]=(0,a.useState)(null!==(r=null!=u?u:l[0])&&void 0!==r?r:c),[M,P]=(0,a.useState)(""),[T,O]=(0,a.useState)(0),[G,U]=(0,a.useState)(null),q=(0,a.useRef)(null),z=(0,a.useRef)([]),D=(0,a.useRef)(null),I=(0,a.useRef)(null),A=(_===c?M:_).trim(),B=(0,a.useCallback)(async()=>{R("transcribing"),U("Transcribing locally…");try{let t=new Blob(z.current,{type:"audio/webm"}),r=new FormData;r.append("audio",t,"narration.webm"),r.append("contract",A||"general");let n=await fetch("/api/notes/record",{method:"POST",body:r}),a=await n.json().catch(()=>({}));if(422===n.status&&a.whisperMissing)U("Audio saved (".concat(a.audioSaved,") but no local Whisper engine is installed. ").concat(a.error));else if(n.ok)U("Session saved for ".concat(a.contract,". Run /diane in Claude Code to process it.")),x.refresh();else{var e;U(null!==(e=a.error)&&void 0!==e?e:"Recording failed")}}catch(e){U(e instanceof Error?e.message:"Recording failed")}finally{R("idle")}},[A,x]),F=(0,a.useCallback)(async()=>{U(null);try{let e=await navigator.mediaDevices.getUserMedia({audio:!0});D.current=e,z.current=[];let t=new MediaRecorder(e);t.ondataavailable=e=>{e.data.size>0&&z.current.push(e.data)},t.onstop=()=>{var e;null===(e=D.current)||void 0===e||e.getTracks().forEach(e=>e.stop()),I.current&&clearInterval(I.current),B()},q.current=t,t.start(),O(0),I.current=setInterval(()=>O(e=>e+1),1e3),R("recording")}catch(e){U("Could not access the microphone. Grant permission and try again.")}},[B]),J=(0,a.useCallback)(()=>{var e;null===(e=q.current)||void 0===e||e.stop()},[]),W="idle"===S&&A.length>0;return(0,n.jsxs)("div",{className:"space-y-sp-5",children:[(0,n.jsxs)("div",{className:"rounded-lg border border-border-subtle bg-surface-1 p-4",children:[(0,n.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,n.jsx)("span",{className:"text-heading font-medium text-text-primary",children:"Record narration"}),(0,n.jsxs)("select",{value:_,onChange:e=>E(e.target.value),disabled:"idle"!==S,className:"rounded-md border border-border-default bg-surface-2 px-3 py-1.5 text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50",children:[l.map(e=>(0,n.jsx)("option",{value:e,children:e},e)),(0,n.jsx)("option",{value:c,children:"Other…"})]}),_===c&&(0,n.jsx)("input",{type:"text",placeholder:"Contract name",value:M,onChange:e=>P(e.target.value),disabled:"idle"!==S,className:"rounded-md border border-border-default bg-surface-2 px-3 py-1.5 text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-50"}),"recording"===S?(0,n.jsxs)("button",{type:"button",onClick:J,className:"flex items-center gap-2 rounded-md bg-severity-critical px-3 py-1.5 text-body font-medium text-surface-0",children:[(0,n.jsx)("span",{className:"h-2.5 w-2.5 rounded-sm bg-surface-0"}),"Stop \xb7 ","".concat(Math.floor(T/60),":").concat(String(T%60).padStart(2,"0"))]}):(0,n.jsxs)("button",{type:"button",onClick:F,disabled:!W,className:"flex items-center gap-2 rounded-md bg-accent px-3 py-1.5 text-body font-medium text-surface-0 transition-opacity duration-200 hover:opacity-90 disabled:opacity-40",children:[(0,n.jsx)("span",{className:"h-2.5 w-2.5 rounded-full bg-surface-0"}),"transcribing"===S?"Transcribing…":"Record"]})]}),G&&(0,n.jsx)("p",{className:"mt-3 text-caption text-text-secondary",children:G}),p>0&&(0,n.jsxs)("p",{className:"mt-2 text-caption text-accent",children:[p," narration session",p>1?"s":""," waiting — run"," ",(0,n.jsx)("code",{className:"font-mono",children:"/diane"})," in Claude Code to process."]})]}),(0,n.jsxs)("div",{className:"flex gap-sp-5",children:[(0,n.jsx)("nav",{className:"w-44 shrink-0 space-y-0.5",children:b.map(e=>(0,n.jsx)("button",{type:"button",onClick:()=>{y(e.key),v(!1)},className:"block w-full truncate rounded-md px-3 py-1.5 text-left text-body ".concat(e.key===f?"bg-accent-subtle font-medium text-accent":"text-text-secondary hover:bg-surface-2 hover:text-text-primary"),children:e.label},e.key))}),(0,n.jsxs)("div",{className:"min-w-0 flex-1 rounded-lg border border-border-subtle bg-surface-1 p-5",children:[(0,n.jsxs)("div",{className:"mb-3 flex items-center justify-between",children:[(0,n.jsx)("span",{className:"text-heading font-medium text-text-primary",children:h.label}),g?(0,n.jsxs)("div",{className:"flex gap-2",children:[(0,n.jsx)("button",{type:"button",onClick:()=>v(!1),className:"rounded-md px-3 py-1 text-caption text-text-secondary hover:text-text-primary",children:"Cancel"}),(0,n.jsx)("button",{type:"button",onClick:C,disabled:k,className:"rounded-md bg-accent px-3 py-1 text-caption font-medium text-surface-0 disabled:opacity-50",children:k?"Saving…":"Save"})]}):(0,n.jsx)("button",{type:"button",onClick:()=>{N(h.body),v(!0)},className:"rounded-md border border-border-default px-3 py-1 text-caption text-text-secondary hover:bg-surface-2 hover:text-text-primary",children:"Edit"})]}),g?(0,n.jsx)("textarea",{value:j,onChange:e=>N(e.target.value),className:"h-[60vh] w-full resize-y rounded-md border border-border-default bg-surface-0 p-3 font-mono text-body text-text-primary focus:outline-none focus:ring-2 focus:ring-accent"}):h.body.trim()?(0,n.jsx)(s.MarkdownRenderer,{content:h.body}):(0,n.jsxs)("p",{className:"text-body text-text-tertiary",children:["No notes yet for ",h.label,". Record a narration above and run"," ",(0,n.jsx)("code",{className:"font-mono",children:"/diane"}),", or click Edit to write notes by hand."]})]})]})]})}},9487:function(e,t,r){"use strict";r.d(t,{MarkdownRenderer:function(){return s}});var n=r(7821),a=r(8525),o=r(5592);function s(e){let{content:t,className:r}=e;return(0,n.jsx)("div",{className:"prose max-w-none\n prose-headings:text-text-primary\n prose-p:text-text-secondary\n prose-strong:text-text-primary\n prose-code:text-accent\n prose-code:before:content-none\n prose-code:after:content-none\n prose-pre:bg-surface-0\n prose-pre:border\n prose-pre:border-border-default\n prose-a:text-accent\n prose-a:no-underline\n hover:prose-a:underline\n prose-th:text-text-primary\n prose-td:text-text-secondary\n prose-hr:border-border-default\n prose-blockquote:border-border-emphasis\n prose-blockquote:text-text-secondary\n prose-li:text-text-secondary\n ".concat(null!=r?r:""),children:(0,n.jsx)(a.UG,{remarkPlugins:[o.Z],children:t})})}},168:function(e,t,r){"use strict";var n=r(7462);r.o(n,"usePathname")&&r.d(t,{usePathname:function(){return n.usePathname}}),r.o(n,"useRouter")&&r.d(t,{useRouter:function(){return n.useRouter}})}},function(e){e.O(0,[7397,7115,3205,1744],function(){return e(e.s=3152)}),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1931],{9778:function(e,r,n){Promise.resolve().then(n.bind(n,9487)),Promise.resolve().then(n.t.bind(n,7498,23))},9487:function(e,r,n){"use strict";n.d(r,{MarkdownRenderer:function(){return c}});var t=n(7821),o=n(8525),s=n(5592);function c(e){let{content:r,className:n}=e;return(0,t.jsx)("div",{className:"prose max-w-none\n prose-headings:text-text-primary\n prose-p:text-text-secondary\n prose-strong:text-text-primary\n prose-code:text-accent\n prose-code:before:content-none\n prose-code:after:content-none\n prose-pre:bg-surface-0\n prose-pre:border\n prose-pre:border-border-default\n prose-a:text-accent\n prose-a:no-underline\n hover:prose-a:underline\n prose-th:text-text-primary\n prose-td:text-text-secondary\n prose-hr:border-border-default\n prose-blockquote:border-border-emphasis\n prose-blockquote:text-text-secondary\n prose-li:text-text-secondary\n ".concat(null!=n?n:""),children:(0,t.jsx)(o.UG,{remarkPlugins:[s.Z],children:r})})}}},function(e){e.O(0,[7397,7498,7115,3205,1744],function(){return e(e.s=9778)}),_N_E=e.O()}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7092],{5534:function(e,t,s){Promise.resolve().then(s.bind(s,7701))},7701:function(e,t,s){"use strict";s.d(t,{ProgressClient:function(){return x}});var a=s(7821),r=s(8078);function i(e){return"".concat(e.file,"::").concat(e.contract)}function l(e){return e>=67?"text-[var(--success)]":e>=34?"text-[var(--medium)]":"text-[var(--critical)]"}let c={contract:0,abstract:1,library:2,interface:3},n={contract:"bg-[var(--low)]/15 text-[var(--low)]",interface:"bg-[var(--info)]/15 text-[var(--info)]",library:"bg-[var(--info)]/15 text-[var(--info)]",abstract:"bg-[var(--high)]/15 text-[var(--high)]"},d=["Critical","High","Medium","Low","Info","Gas"],o={Critical:"bg-[var(--critical)]/15 text-[var(--critical)]",High:"bg-[var(--high)]/15 text-[var(--high)]",Medium:"bg-[var(--medium)]/15 text-[var(--medium)]",Low:"bg-[var(--low)]/15 text-[var(--low)]",Info:"bg-[var(--info)]/15 text-[var(--info)]",Gas:"bg-surface-3 text-text-secondary"};function x(e){let{contracts:t,totalsNsloc:s,reviewedContracts:x,auditSteps:p,findingsTotal:m,findingsBySeverity:u,findingsTracked:h}=e,[v,f]=(0,r.useState)(x),[b,g]=(0,r.useState)("all"),j=(0,r.useMemo)(()=>t.filter(e=>v[i(e)]).reduce((e,t)=>e+t.nsloc,0),[t,v]),y=(0,r.useMemo)(()=>t.filter(e=>v[i(e)]).length,[t,v]),N=s>0?j/s*100:0,w=p.filter(e=>e.completed).length/p.length*100,C=m>0?h/m*100:100,k=.7*N+.2*w+.1*C,M=(0,r.useMemo)(()=>{let e=[...t].sort((e,t)=>{var s,a;let r=null!==(s=c[e.type])&&void 0!==s?s:99,i=null!==(a=c[t.type])&&void 0!==a?a:99;return r!==i?r-i:t.nsloc-e.nsloc});return"all"===b?e:e.filter(e=>{let t=!!v[i(e)];return"checked"===b?t:!t})},[t,b,v]),O=(0,r.useMemo)(()=>Math.max(...t.map(e=>e.nsloc),1),[t]);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"mb-sp-5 rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"mb-2 flex items-baseline justify-between gap-sp-2",children:[(0,a.jsxs)("span",{className:"text-display font-semibold ".concat(l(k)),children:[k.toFixed(0),"%"]}),(0,a.jsxs)("span",{className:"text-caption text-text-tertiary inline-flex items-center gap-1.5",title:"Overall progress is a weighted average: 70% nSLOC reviewed (contract review), 20% audit-step completion, 10% findings triage.","aria-label":"Overall progress formula: 70% contract review by nSLOC, 20% step completion, 10% findings triage",children:["Contract Review ",N.toFixed(0),"% \xb7 Steps ",w.toFixed(0),"% \xb7 Triage ",C.toFixed(0),"%",(0,a.jsx)("svg",{className:"h-3.5 w-3.5 text-text-tertiary","aria-hidden":"true",viewBox:"0 0 16 16",fill:"currentColor",children:(0,a.jsx)("path",{d:"M8 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zM7.25 7.25a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-1.5 0v-3.5zM8 4.5a.875.875 0 1 1 0 1.75A.875.875 0 0 1 8 4.5z"})})]})]}),(0,a.jsx)("div",{className:"h-3 w-full overflow-hidden rounded-full bg-surface-3",children:(0,a.jsx)("div",{className:"h-full rounded-full transition-all duration-300 ".concat(k>=67?"bg-[var(--success)]":k>=34?"bg-[var(--medium)]":"bg-[var(--critical)]"),style:{width:"".concat(Math.min(k,100),"%")},role:"progressbar","aria-valuenow":Math.round(k),"aria-valuemin":0,"aria-valuemax":100,"aria-label":"Overall audit progress"})}),(0,a.jsx)("p",{className:"mt-2 text-caption text-text-tertiary",children:"Weighted: 70% nSLOC reviewed \xb7 20% audit steps \xb7 10% findings triage. Hover the breakdown for details."})]}),(0,a.jsxs)("div",{className:"mb-sp-5 grid grid-cols-2 gap-sp-3 lg:grid-cols-4",children:[(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"text-display ".concat(l(k)),children:[k.toFixed(0),"%"]}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"Overall Progress"})]}),(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"text-display text-text-primary",children:[y," / ",t.length]}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"Contracts Reviewed"})]}),(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsxs)("div",{className:"text-display text-text-primary",children:[j.toLocaleString()," / ",s.toLocaleString()]}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"nSLOC Reviewed"})]}),(0,a.jsxs)("div",{className:"rounded-md border border-border-default bg-surface-2 p-sp-4",children:[(0,a.jsx)("div",{className:"text-display text-text-primary",children:m}),(0,a.jsx)("div",{className:"mt-1 text-caption text-text-secondary",children:"Findings"}),Object.keys(u).length>0&&(0,a.jsx)("div",{className:"mt-2 flex flex-wrap gap-1.5",children:d.filter(e=>u[e]).map(e=>{var t;return(0,a.jsxs)("span",{className:"inline-flex items-center rounded-md px-2 py-0.5 text-caption font-medium ".concat(null!==(t=o[e])&&void 0!==t?t:"bg-surface-3 text-text-secondary"),children:[u[e]," ",e]},e)})})]})]}),(0,a.jsxs)("div",{className:"mb-sp-5",children:[(0,a.jsx)("h3",{className:"mb-sp-2 text-heading font-medium text-text-primary",children:"Audit Steps"}),(0,a.jsx)("div",{className:"flex flex-wrap gap-2",children:p.map(e=>(0,a.jsxs)("span",{className:"inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-caption font-medium ".concat(e.completed?"bg-[var(--success)]/15 text-[var(--success)]":"bg-surface-3 text-text-tertiary"),children:[e.completed?(0,a.jsx)("svg",{className:"h-3.5 w-3.5",viewBox:"0 0 20 20",fill:"currentColor",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z",clipRule:"evenodd"})}):(0,a.jsx)("svg",{className:"h-3.5 w-3.5",viewBox:"0 0 20 20",fill:"currentColor",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm.75-11.25a.75.75 0 0 0-1.5 0v2.5h-2.5a.75.75 0 0 0 0 1.5h2.5v2.5a.75.75 0 0 0 1.5 0v-2.5h2.5a.75.75 0 0 0 0-1.5h-2.5v-2.5Z",clipRule:"evenodd"})}),e.label]},e.label))})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"mb-sp-2 flex items-center justify-between",children:[(0,a.jsx)("h3",{className:"text-heading font-medium text-text-primary",children:"Contract Checklist"}),(0,a.jsx)("div",{className:"inline-flex rounded-md bg-surface-3 p-0.5",children:["all","unchecked","checked"].map(e=>(0,a.jsx)("button",{type:"button",onClick:()=>g(e),className:"rounded-sm px-2.5 py-1 text-caption font-medium ".concat(b===e?"bg-surface-2 text-text-primary shadow-sm":"text-text-secondary hover:text-text-primary"),children:"all"===e?"All (".concat(t.length,")"):"checked"===e?"Reviewed (".concat(y,")"):"Remaining (".concat(t.length-y,")")},e))})]}),(0,a.jsx)("div",{className:"overflow-x-auto rounded-md border border-border-default",children:(0,a.jsxs)("table",{className:"w-full text-left text-body",children:[(0,a.jsx)("thead",{className:"border-b border-border-default bg-surface-2 text-caption font-medium uppercase tracking-wider text-text-tertiary",children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{className:"w-10 px-sp-3 py-sp-2"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2",children:"Contract"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2",children:"File"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2",children:"Type"}),(0,a.jsx)("th",{className:"px-sp-4 py-sp-2 text-right",children:"nSLOC"}),(0,a.jsx)("th",{className:"w-32 px-sp-4 py-sp-2",children:"Weight"})]})}),(0,a.jsx)("tbody",{className:"divide-y divide-border-subtle",children:M.map(e=>{var t;let s=i(e),r=!!v[s],l=e.nsloc/O*100;return(0,a.jsxs)("tr",{className:"h-9 transition-colors ".concat(r?"bg-[var(--success)]/5":"bg-surface-1"," hover:bg-surface-3"),children:[(0,a.jsx)("td",{className:"px-sp-3 py-sp-2",children:(0,a.jsx)("button",{type:"button",onClick:()=>(function(e){let t=i(e),s=!v[t];f(e=>({...e,[t]:s})),fetch("/api/progress",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reviewed_contracts:{[t]:s}})}).catch(()=>{})})(e),className:"flex h-5 w-5 items-center justify-center rounded border transition-colors ".concat(r?"border-[var(--accent)] bg-[var(--accent)] text-surface-0":"border-border-emphasis bg-surface-1 hover:border-[var(--accent)]"),children:r&&(0,a.jsx)("svg",{className:"h-3 w-3",viewBox:"0 0 12 12",fill:"none",stroke:"currentColor",strokeWidth:2,children:(0,a.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.5 6l2.5 2.5 4.5-5"})})})}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2 font-medium text-text-primary",children:e.contract}),(0,a.jsx)("td",{className:"max-w-[200px] truncate px-sp-4 py-sp-2 font-mono text-caption text-text-tertiary",children:e.file}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2",children:(0,a.jsx)("span",{className:"inline-flex items-center rounded-sm px-2 py-0.5 text-caption font-medium ".concat(null!==(t=n[e.type])&&void 0!==t?t:"bg-surface-3 text-text-secondary"),children:e.type})}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2 text-right font-mono tabular-nums text-text-secondary",children:e.nsloc}),(0,a.jsx)("td",{className:"px-sp-4 py-sp-2",children:(0,a.jsx)("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-surface-3",children:(0,a.jsx)("div",{className:"h-full rounded-full bg-[var(--accent)]",style:{width:"".concat(l,"%")}})})})]},s)})})]})})]})]})}}},function(e){e.O(0,[7115,3205,1744],function(){return e(e.s=5534)}),_N_E=e.O()}]);
|