hex-audit 0.4.3 → 0.4.5

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.
Files changed (136) hide show
  1. package/DEMO.md +3 -2
  2. package/README.md +45 -41
  3. package/dashboard/.next/BUILD_ID +1 -1
  4. package/dashboard/.next/app-build-manifest.json +24 -17
  5. package/dashboard/.next/app-path-routes-manifest.json +1 -1
  6. package/dashboard/.next/build-manifest.json +4 -4
  7. package/dashboard/.next/next-minimal-server.js.nft.json +1 -1
  8. package/dashboard/.next/next-server.js.nft.json +1 -1
  9. package/dashboard/.next/prerender-manifest.js +1 -1
  10. package/dashboard/.next/prerender-manifest.json +1 -1
  11. package/dashboard/.next/required-server-files.json +1 -1
  12. package/dashboard/.next/routes-manifest.json +1 -1
  13. package/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/dashboard/.next/server/app/_not-found.html +1 -1
  15. package/dashboard/.next/server/app/_not-found.rsc +4 -4
  16. package/dashboard/.next/server/app/access/page.js +1 -1
  17. package/dashboard/.next/server/app/access/page_client-reference-manifest.js +1 -1
  18. package/dashboard/.next/server/app/api/flows/route.js +1 -1
  19. package/dashboard/.next/server/app/api/github-status/route.js +1 -1
  20. package/dashboard/.next/server/app/api/issues/[id]/move/route.js +1 -1
  21. package/dashboard/.next/server/app/api/issues/[id]/route.js +1 -1
  22. package/dashboard/.next/server/app/api/mermaid/route.js +1 -1
  23. package/dashboard/.next/server/app/api/progress/route.js +1 -1
  24. package/dashboard/.next/server/app/api/watch/route.js +2 -2
  25. package/dashboard/.next/server/app/calls/page.js +1 -1
  26. package/dashboard/.next/server/app/calls/page_client-reference-manifest.js +1 -1
  27. package/dashboard/.next/server/app/conformance/page.js +1 -1
  28. package/dashboard/.next/server/app/conformance/page_client-reference-manifest.js +1 -1
  29. package/dashboard/.next/server/app/diagram/page_client-reference-manifest.js +1 -1
  30. package/dashboard/.next/server/app/diagram.html +1 -1
  31. package/dashboard/.next/server/app/diagram.rsc +4 -4
  32. package/dashboard/.next/server/app/flows/page_client-reference-manifest.js +1 -1
  33. package/dashboard/.next/server/app/flows.html +1 -1
  34. package/dashboard/.next/server/app/flows.rsc +4 -4
  35. package/dashboard/.next/server/app/functions/page.js +1 -1
  36. package/dashboard/.next/server/app/functions/page_client-reference-manifest.js +1 -1
  37. package/dashboard/.next/server/app/issues/page.js +12 -1
  38. package/dashboard/.next/server/app/issues/page_client-reference-manifest.js +1 -1
  39. package/dashboard/.next/server/app/overleaf/page.js +1 -0
  40. package/dashboard/.next/server/app/overleaf/page.js.nft.json +1 -0
  41. package/dashboard/.next/server/app/overleaf/page_client-reference-manifest.js +1 -0
  42. package/dashboard/.next/server/app/page.js +2 -2
  43. package/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  44. package/dashboard/.next/server/app/progress/page.js +1 -1
  45. package/dashboard/.next/server/app/progress/page_client-reference-manifest.js +1 -1
  46. package/dashboard/.next/server/app/stats/page.js +1 -1
  47. package/dashboard/.next/server/app/stats/page_client-reference-manifest.js +1 -1
  48. package/dashboard/.next/server/app-paths-manifest.json +12 -11
  49. package/dashboard/.next/server/chunks/431.js +1 -1
  50. package/dashboard/.next/server/chunks/557.js +1 -1
  51. package/dashboard/.next/server/functions-config-manifest.json +1 -1
  52. package/dashboard/.next/server/middleware-build-manifest.js +1 -1
  53. package/dashboard/.next/server/pages/404.html +1 -1
  54. package/dashboard/.next/server/pages/500.html +1 -1
  55. package/dashboard/.next/server/server-reference-manifest.js +1 -1
  56. package/dashboard/.next/server/server-reference-manifest.json +1 -1
  57. package/dashboard/.next/static/chunks/app/issues/page-30f7bcde0f4f6ddb.js +1 -0
  58. package/dashboard/.next/static/chunks/app/{layout-e0f50820b06a7621.js → layout-cde7d75ea6df4f81.js} +1 -1
  59. package/dashboard/.next/static/chunks/app/overleaf/page-bcea12a80c97b92f.js +1 -0
  60. package/dashboard/.next/static/chunks/{main-70c6d841ad572657.js → main-8f1e837090aef5cf.js} +1 -1
  61. package/dashboard/.next/static/css/36c6bb859d4545fd.css +3 -0
  62. package/dashboard/app/access/page.tsx +7 -0
  63. package/dashboard/app/api/flows/route.ts +4 -0
  64. package/dashboard/app/api/issues/[id]/move/route.ts +29 -58
  65. package/dashboard/app/api/issues/[id]/route.ts +11 -102
  66. package/dashboard/app/calls/page.tsx +7 -0
  67. package/dashboard/app/conformance/page.tsx +7 -0
  68. package/dashboard/app/functions/page.tsx +7 -0
  69. package/dashboard/app/issues/IssuesBoardClient.tsx +109 -35
  70. package/dashboard/app/issues/page.tsx +43 -2
  71. package/dashboard/app/overleaf/OverleafClient.tsx +97 -0
  72. package/dashboard/app/overleaf/page.tsx +32 -0
  73. package/dashboard/app/page.tsx +7 -0
  74. package/dashboard/app/progress/page.tsx +7 -0
  75. package/dashboard/app/stats/page.tsx +7 -0
  76. package/dashboard/components/DashboardLayout.tsx +8 -2
  77. package/dashboard/lib/data.ts +4 -1
  78. package/dashboard/lib/finding-markdown.ts +59 -0
  79. package/dashboard/next.config.mjs +4 -0
  80. package/dist/analysis/external-calls.d.ts +8 -1
  81. package/dist/analysis/external-calls.d.ts.map +1 -1
  82. package/dist/analysis/external-calls.js +57 -13
  83. package/dist/analysis/external-calls.js.map +1 -1
  84. package/dist/commands/calls.d.ts.map +1 -1
  85. package/dist/commands/calls.js +15 -6
  86. package/dist/commands/calls.js.map +1 -1
  87. package/dist/commands/dashboard.js +6 -6
  88. package/dist/commands/dashboard.js.map +1 -1
  89. package/dist/commands/init.d.ts.map +1 -1
  90. package/dist/commands/init.js +17 -19
  91. package/dist/commands/init.js.map +1 -1
  92. package/dist/commands/issue.d.ts +3 -0
  93. package/dist/commands/issue.d.ts.map +1 -0
  94. package/dist/commands/issue.js +115 -0
  95. package/dist/commands/issue.js.map +1 -0
  96. package/dist/core/issues.d.ts +79 -0
  97. package/dist/core/issues.d.ts.map +1 -0
  98. package/dist/core/issues.js +253 -0
  99. package/dist/core/issues.js.map +1 -0
  100. package/dist/index.js +3 -1
  101. package/dist/index.js.map +1 -1
  102. package/dist/skills/init-audit.md +3 -1
  103. package/dist/skills/sync-issues.md +17 -34
  104. package/dist/skills/validate-issue.md +20 -10
  105. package/dist/skills/write-finding.md +1 -1
  106. package/package.json +1 -1
  107. package/dashboard/.next/server/app/access.html +0 -1
  108. package/dashboard/.next/server/app/access.meta +0 -5
  109. package/dashboard/.next/server/app/access.rsc +0 -8
  110. package/dashboard/.next/server/app/api/flows.body +0 -1
  111. package/dashboard/.next/server/app/api/flows.meta +0 -1
  112. package/dashboard/.next/server/app/calls.html +0 -1
  113. package/dashboard/.next/server/app/calls.meta +0 -5
  114. package/dashboard/.next/server/app/calls.rsc +0 -8
  115. package/dashboard/.next/server/app/conformance.html +0 -1
  116. package/dashboard/.next/server/app/conformance.meta +0 -5
  117. package/dashboard/.next/server/app/conformance.rsc +0 -8
  118. package/dashboard/.next/server/app/functions.html +0 -1
  119. package/dashboard/.next/server/app/functions.meta +0 -5
  120. package/dashboard/.next/server/app/functions.rsc +0 -8
  121. package/dashboard/.next/server/app/index.html +0 -1
  122. package/dashboard/.next/server/app/index.meta +0 -5
  123. package/dashboard/.next/server/app/index.rsc +0 -9
  124. package/dashboard/.next/server/app/issues.html +0 -1
  125. package/dashboard/.next/server/app/issues.meta +0 -5
  126. package/dashboard/.next/server/app/issues.rsc +0 -8
  127. package/dashboard/.next/server/app/progress.html +0 -1
  128. package/dashboard/.next/server/app/progress.meta +0 -5
  129. package/dashboard/.next/server/app/progress.rsc +0 -8
  130. package/dashboard/.next/server/app/stats.html +0 -1
  131. package/dashboard/.next/server/app/stats.meta +0 -5
  132. package/dashboard/.next/server/app/stats.rsc +0 -8
  133. package/dashboard/.next/static/chunks/app/issues/page-dc110d4cc0a5c398.js +0 -1
  134. package/dashboard/.next/static/css/265d7e2538aaffc0.css +0 -3
  135. /package/dashboard/.next/static/{f3-iD1w0WCbTDKsUc5-Id → 4HWoeGmBSZi6iGcKL3VWA}/_buildManifest.js +0 -0
  136. /package/dashboard/.next/static/{f3-iD1w0WCbTDKsUc5-Id → 4HWoeGmBSZi6iGcKL3VWA}/_ssgManifest.js +0 -0
@@ -2,6 +2,12 @@ import { readJsonFile, readNestedJsonFile, listSubdirs } from '@/lib/data';
2
2
  import { NotYetGenerated } from '@/components/NotYetGenerated';
3
3
  import { IssuesBoardClient, type BoardIssue } from './IssuesBoardClient';
4
4
 
5
+ // Read .hex/ at request time, not at build time. Without this, next build
6
+ // prerenders this page with whatever .hex/ data was available in the build
7
+ // environment and ships static HTML — the dashboard then renders that
8
+ // stale (or empty) HTML for every audit project, ignoring the runtime cwd.
9
+ export const dynamic = 'force-dynamic';
10
+
5
11
  interface FindingsFile {
6
12
  findings: Array<{
7
13
  id: string;
@@ -13,11 +19,17 @@ interface FindingsFile {
13
19
  update_from_client?: string;
14
20
  root_cause?: { locations?: Array<{ file: string; snippet?: string }> };
15
21
  poc?: { status?: string; file?: string | null; validation_memo?: string | null };
16
- github?: { issue_number?: number; issue_url?: string; state?: string };
22
+ github?: { issue_number?: number; issue_url?: string; state?: string; sync_status?: string };
17
23
  category?: string;
18
24
  }>;
19
25
  }
20
26
 
27
+ interface ConfigFile {
28
+ settings?: {
29
+ report?: { repository_url?: string; initial_commit?: string };
30
+ };
31
+ }
32
+
21
33
  interface TrackingFile {
22
34
  findings: Array<{
23
35
  id: string;
@@ -67,6 +79,26 @@ interface SpecConformanceFile {
67
79
  }>;
68
80
  }
69
81
 
82
+ /**
83
+ * Derive the GitHub sync chip state. Only verified findings are push
84
+ * candidates, so anything else returns 'none' (no chip).
85
+ */
86
+ function computeSyncState(
87
+ status: string | undefined,
88
+ github: { issue_number?: number; state?: string; sync_status?: string } | undefined,
89
+ ): BoardIssue['sync_state'] {
90
+ if (status !== 'verified') return 'none';
91
+ if (!github?.issue_number) return 'unsynced';
92
+ if (
93
+ github.sync_status === 'conflict' ||
94
+ github.sync_status === 'local_ahead' ||
95
+ github.sync_status === 'remote_ahead'
96
+ ) {
97
+ return 'conflict';
98
+ }
99
+ return github.state === 'closed' ? 'synced_closed' : 'synced_open';
100
+ }
101
+
70
102
  function statusToColumn(status: string | undefined): BoardIssue['column'] {
71
103
  switch (status) {
72
104
  case 'verified':
@@ -172,7 +204,9 @@ function buildBoardIssues(): BoardIssue[] {
172
204
  code_locations: locations,
173
205
  github_synced: Boolean(finding?.github?.issue_number),
174
206
  github_issue_url: finding?.github?.issue_url,
207
+ github_issue_number: finding?.github?.issue_number,
175
208
  github_state: finding?.github?.state,
209
+ sync_state: computeSyncState(t.status, finding?.github),
176
210
  duplicate_of: t.duplicate_of ?? dup?.matches ?? null,
177
211
  match_signals: matchSignals,
178
212
  reasoning: dup?.reasoning,
@@ -203,7 +237,9 @@ function buildBoardIssues(): BoardIssue[] {
203
237
  code_locations: f.root_cause?.locations ?? [],
204
238
  github_synced: Boolean(f.github?.issue_number),
205
239
  github_issue_url: f.github?.issue_url,
240
+ github_issue_number: f.github?.issue_number,
206
241
  github_state: f.github?.state,
242
+ sync_state: computeSyncState('verified', f.github),
207
243
  duplicate_of: null,
208
244
  match_signals: undefined,
209
245
  reasoning: undefined,
@@ -220,6 +256,11 @@ function buildBoardIssues(): BoardIssue[] {
220
256
 
221
257
  export default function IssuesPage() {
222
258
  const issues = buildBoardIssues();
259
+ const config = readJsonFile<ConfigFile>('config.json');
260
+ const formatCtx = {
261
+ repositoryUrl: config?.settings?.report?.repository_url,
262
+ commit: config?.settings?.report?.initial_commit,
263
+ };
223
264
 
224
265
  if (issues.length === 0) {
225
266
  return (
@@ -235,5 +276,5 @@ export default function IssuesPage() {
235
276
  );
236
277
  }
237
278
 
238
- return <IssuesBoardClient issues={issues} />;
279
+ return <IssuesBoardClient issues={issues} formatCtx={formatCtx} />;
239
280
  }
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+
5
+ export interface OverleafSections {
6
+ executive_summary: string | null;
7
+ audited_files: string | null;
8
+ summary_of_findings: string | null;
9
+ findings: string | null;
10
+ }
11
+
12
+ type SectionKey = keyof OverleafSections;
13
+
14
+ const TABS: Array<{ key: SectionKey; label: string; file: string }> = [
15
+ { key: 'executive_summary', label: 'Executive Summary', file: 'executive_summary.txt' },
16
+ { key: 'audited_files', label: 'Audited Files', file: 'audited_files.txt' },
17
+ { key: 'summary_of_findings', label: 'Summary of Issues', file: 'summary_of_findings.txt' },
18
+ { key: 'findings', label: 'Issues', file: 'findings.txt' },
19
+ ];
20
+
21
+ export function OverleafClient({ sections }: { sections: OverleafSections }) {
22
+ const [active, setActive] = useState<SectionKey>('executive_summary');
23
+ const [copied, setCopied] = useState(false);
24
+
25
+ const content = sections[active];
26
+
27
+ const copy = async () => {
28
+ if (!content) return;
29
+ try {
30
+ await navigator.clipboard.writeText(content);
31
+ setCopied(true);
32
+ setTimeout(() => setCopied(false), 1500);
33
+ } catch {
34
+ /* clipboard blocked (insecure context) — no-op */
35
+ }
36
+ };
37
+
38
+ return (
39
+ <div>
40
+ <header className="mb-6">
41
+ <h1 className="text-title font-semibold text-text-primary">Overleaf</h1>
42
+ <p className="mt-1 text-body text-text-secondary">
43
+ LaTeX report sections from <span className="font-mono text-caption">.hex/overleaf/</span>.
44
+ Copy each into the matching slot of the Nethermind Overleaf template.
45
+ </p>
46
+ </header>
47
+
48
+ {/* Segmented control */}
49
+ <div className="mb-4 flex flex-wrap gap-1.5">
50
+ {TABS.map((tab) => {
51
+ const isActive = tab.key === active;
52
+ const present = Boolean(sections[tab.key]);
53
+ return (
54
+ <button
55
+ key={tab.key}
56
+ type="button"
57
+ onClick={() => setActive(tab.key)}
58
+ className={`rounded-md px-3 py-1.5 text-caption font-medium ${
59
+ isActive
60
+ ? 'bg-accent text-surface-0'
61
+ : 'bg-surface-2 text-text-secondary hover:text-text-primary'
62
+ } ${present ? '' : 'opacity-50'}`}
63
+ >
64
+ {tab.label}
65
+ </button>
66
+ );
67
+ })}
68
+ </div>
69
+
70
+ <div className="rounded-lg border border-border-subtle bg-surface-1">
71
+ <div className="flex items-center justify-between border-b border-border-subtle px-4 py-2.5">
72
+ <span className="font-mono text-caption text-text-tertiary">
73
+ {TABS.find((t) => t.key === active)?.file}
74
+ </span>
75
+ <button
76
+ type="button"
77
+ onClick={copy}
78
+ disabled={!content}
79
+ className="rounded-md border border-border-default px-2.5 py-1 text-caption text-text-secondary hover:bg-surface-2 hover:text-text-primary disabled:opacity-50"
80
+ >
81
+ {copied ? 'Copied!' : 'Copy'}
82
+ </button>
83
+ </div>
84
+ {content ? (
85
+ <pre className="max-h-[70vh] overflow-auto p-4 font-mono text-caption leading-relaxed text-text-secondary whitespace-pre-wrap break-words">
86
+ {content}
87
+ </pre>
88
+ ) : (
89
+ <p className="p-4 text-body text-text-tertiary">
90
+ This section has not been generated yet. Run{' '}
91
+ <span className="font-mono text-caption">/generate-overleaf</span>.
92
+ </p>
93
+ )}
94
+ </div>
95
+ </div>
96
+ );
97
+ }
@@ -0,0 +1,32 @@
1
+ import { readMarkdownFile } from '@/lib/data';
2
+ import { NotYetGenerated } from '@/components/NotYetGenerated';
3
+ import { OverleafClient, type OverleafSections } from './OverleafClient';
4
+
5
+ // Read .hex/overleaf/ at request time, not at build time.
6
+ export const dynamic = 'force-dynamic';
7
+
8
+ export default function OverleafPage() {
9
+ const sections: OverleafSections = {
10
+ executive_summary: readMarkdownFile('overleaf/executive_summary.txt'),
11
+ audited_files: readMarkdownFile('overleaf/audited_files.txt'),
12
+ summary_of_findings: readMarkdownFile('overleaf/summary_of_findings.txt'),
13
+ findings: readMarkdownFile('overleaf/findings.txt'),
14
+ };
15
+
16
+ const anyPresent = Object.values(sections).some(Boolean);
17
+ if (!anyPresent) {
18
+ return (
19
+ <div>
20
+ <header className="mb-6">
21
+ <h1 className="text-title font-semibold text-text-primary">Overleaf</h1>
22
+ <p className="mt-1 text-body text-text-secondary">
23
+ LaTeX report sections, ready to paste into the Nethermind Overleaf template.
24
+ </p>
25
+ </header>
26
+ <NotYetGenerated command="/generate-overleaf" />
27
+ </div>
28
+ );
29
+ }
30
+
31
+ return <OverleafClient sections={sections} />;
32
+ }
@@ -3,6 +3,13 @@ import { readJsonFile, readMarkdownFile } from '@/lib/data';
3
3
  import { NotYetGenerated } from '@/components/NotYetGenerated';
4
4
  import { MarkdownRenderer } from '@/components/MarkdownRenderer';
5
5
 
6
+ // Read .hex/ at request time, not at build time. Without this, next build
7
+ // prerenders this page with whatever .hex/ data was available in the build
8
+ // environment and ships static HTML — the dashboard then renders that
9
+ // stale (or empty) HTML for every audit project, ignoring the runtime cwd.
10
+ export const dynamic = 'force-dynamic';
11
+
12
+
6
13
  interface ProjectConfig {
7
14
  project: {
8
15
  name: string;
@@ -2,6 +2,13 @@ import { readJsonFile, fileExists } from '@/lib/data';
2
2
  import { NotYetGenerated } from '@/components/NotYetGenerated';
3
3
  import { ProgressClient, type ProgressClientProps } from './ProgressClient';
4
4
 
5
+ // Read .hex/ at request time, not at build time. Without this, next build
6
+ // prerenders this page with whatever .hex/ data was available in the build
7
+ // environment and ships static HTML — the dashboard then renders that
8
+ // stale (or empty) HTML for every audit project, ignoring the runtime cwd.
9
+ export const dynamic = 'force-dynamic';
10
+
11
+
5
12
  // ─── Types ──────────────────────────────────────────────────────────
6
13
 
7
14
  interface PerContractStats {
@@ -2,6 +2,13 @@ import { readJsonFile } from '@/lib/data';
2
2
  import { NotYetGenerated } from '@/components/NotYetGenerated';
3
3
  import { StatsClient, type StatsClientProps } from './StatsClient';
4
4
 
5
+ // Read .hex/ at request time, not at build time. Without this, next build
6
+ // prerenders this page with whatever .hex/ data was available in the build
7
+ // environment and ships static HTML — the dashboard then renders that
8
+ // stale (or empty) HTML for every audit project, ignoring the runtime cwd.
9
+ export const dynamic = 'force-dynamic';
10
+
11
+
5
12
  // ─── Types ──────────────────────────────────────────────────────────
6
13
 
7
14
  interface Stats {
@@ -326,7 +326,13 @@ const NAV_GROUPS = [
326
326
  { href: '/flows', label: 'Flows', icon: IconFlows },
327
327
  ],
328
328
  },
329
- { label: 'Issues', items: [{ href: '/issues', label: 'Board', icon: IconFindings }] },
329
+ {
330
+ label: 'Issues',
331
+ items: [
332
+ { href: '/issues', label: 'Board', icon: IconFindings },
333
+ { href: '/overleaf', label: 'Overleaf', icon: IconReport },
334
+ ],
335
+ },
330
336
  ];
331
337
 
332
338
  export { NAV_GROUPS };
@@ -395,7 +401,7 @@ export function DashboardLayout({ children }: { children: React.ReactNode }) {
395
401
  <div className="px-5 pb-4 pt-6">
396
402
  <div className="flex items-baseline gap-2">
397
403
  <span className="text-title font-semibold text-text-primary">Hex</span>
398
- <span className="text-caption text-text-tertiary">v0.4.3</span>
404
+ <span className="text-caption text-text-tertiary">v0.4.5</span>
399
405
  </div>
400
406
  </div>
401
407
 
@@ -2,7 +2,10 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
4
  function getProjectDir(): string {
5
- return process.env.HEX_PROJECT_DIR ?? process.env.SOLAUDIT_PROJECT_DIR ?? process.cwd();
5
+ // The `hex dashboard` CLI spawns Next.js with cwd = the user's audit project,
6
+ // so process.cwd() is authoritative. (The HEX_PROJECT_DIR env var was a
7
+ // vestige from the old monorepo layout and has been removed.)
8
+ return process.cwd();
6
9
  }
7
10
 
8
11
  function getOutputDir(): string {
@@ -0,0 +1,59 @@
1
+ import type { BoardIssue } from '@/app/issues/IssuesBoardClient';
2
+
3
+ /**
4
+ * Single source of truth for the finding markdown format used by the board's
5
+ * "Copy as HackMD" button (and matching the GitHub issue body the /sync-issues
6
+ * skill renders). Keep this aligned with src/skills/sync-issues.md.
7
+ */
8
+ export interface FormatContext {
9
+ /** config.json settings.report.repository_url, if set */
10
+ repositoryUrl?: string;
11
+ /** config.json settings.report.initial_commit, if set */
12
+ commit?: string;
13
+ }
14
+
15
+ function fileLink(file: string, ctx: FormatContext): string {
16
+ if (ctx.repositoryUrl && ctx.commit) {
17
+ const base = ctx.repositoryUrl.replace(/\/+$/, '');
18
+ return `[\`${file}\`](${base}/blob/${ctx.commit}/${file})`;
19
+ }
20
+ // Empty link target — explicit, matches the GitHub issue template.
21
+ return `[\`${file}\`]()`;
22
+ }
23
+
24
+ /** The five-field body shared by HackMD copy and the GitHub issue body. */
25
+ function bodyFields(issue: BoardIssue, ctx: FormatContext): string {
26
+ const seen = new Set<string>();
27
+ const files = issue.code_locations
28
+ .map((l) => l.file)
29
+ .filter((f) => f && !seen.has(f) && seen.add(f))
30
+ .map((f) => fileLink(f, ctx))
31
+ .join(', ');
32
+ const status = issue.resolution || 'Unresolved';
33
+ const update = issue.update_from_client ?? '';
34
+ return [
35
+ `**File(s)**: ${files}`,
36
+ ``,
37
+ `**Description**: ${issue.description}`,
38
+ ``,
39
+ `**Recommendation(s)**: ${issue.recommendation}`,
40
+ ``,
41
+ `**Status**: ${status}`,
42
+ ``,
43
+ `**Update from the client**: ${update}`,
44
+ ].join('\n');
45
+ }
46
+
47
+ /** HackMD-style: leading `## [Severity] Title` heading, then the body. */
48
+ export function findingToHackmd(issue: BoardIssue, ctx: FormatContext = {}): string {
49
+ return `## [${issue.severity}] ${issue.title}\n\n${bodyFields(issue, ctx)}\n`;
50
+ }
51
+
52
+ /**
53
+ * GitHub issue body: no heading (the GH issue title carries `[Severity] Title`),
54
+ * no trailing attribution. Returns the body WITHOUT the round-trip footer; the
55
+ * sync caller appends `<!-- hex-finding-id: <id> -->` separately.
56
+ */
57
+ export function findingToGithubBody(issue: BoardIssue, ctx: FormatContext = {}): string {
58
+ return bodyFields(issue, ctx);
59
+ }
@@ -3,6 +3,10 @@ const nextConfig = {
3
3
  reactStrictMode: true,
4
4
  experimental: {
5
5
  serverComponentsExternalPackages: ['chokidar'],
6
+ // Allow importing the canonical issue-mutation module from ../src/core/
7
+ // (shared with the `hex issue` CLI). The module depends only on node
8
+ // builtins, so nothing heavy leaks into the bundle.
9
+ externalDir: true,
6
10
  },
7
11
  };
8
12
 
@@ -1,8 +1,15 @@
1
1
  import type { SlitherDetectorResult } from '../parsers/slither.js';
2
2
  import type { ASTExternalCall } from '../parsers/solidity-parser.js';
3
3
  import type { ExternalCall } from '../types/index.js';
4
+ /**
5
+ * Whether Slither enrichment ran, and what it found:
6
+ * - 'with-findings': Slither ran and reported detector results (most authoritative)
7
+ * - 'clean': Slither ran but reported nothing for this codebase
8
+ * - 'unavailable': Slither did not run (binary missing, parse error, etc.)
9
+ */
10
+ export type SlitherState = 'with-findings' | 'clean' | 'unavailable';
4
11
  /**
5
12
  * Build external calls from AST-extracted calls, optionally enriched with Slither data.
6
13
  */
7
- export declare function buildExternalCalls(astCalls: ASTExternalCall[], stateVarsImmutability: Map<string, boolean>, scopeContracts: Set<string>, accessControlledSetters: Map<string, string>, slitherDetectors?: SlitherDetectorResult[]): ExternalCall[];
14
+ export declare function buildExternalCalls(astCalls: ASTExternalCall[], stateVarsImmutability: Map<string, boolean>, scopeContracts: Set<string>, accessControlledSetters: Map<string, string>, slitherDetectors?: SlitherDetectorResult[], slitherState?: SlitherState): ExternalCall[];
8
15
  //# sourceMappingURL=external-calls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"external-calls.d.ts","sourceRoot":"","sources":["../../src/analysis/external-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAatD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EAAE,EAC3B,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3C,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,EAC3B,uBAAuB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5C,gBAAgB,CAAC,EAAE,qBAAqB,EAAE,GACzC,YAAY,EAAE,CAkEhB"}
1
+ {"version":3,"file":"external-calls.d.ts","sourceRoot":"","sources":["../../src/analysis/external-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAiCtD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,OAAO,GAAG,aAAa,CAAC;AAErE;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EAAE,EAC3B,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3C,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,EAC3B,uBAAuB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5C,gBAAgB,CAAC,EAAE,qBAAqB,EAAE,EAC1C,YAAY,GAAE,YAIG,GAChB,YAAY,EAAE,CA8FhB"}
@@ -1,9 +1,29 @@
1
1
  // Call type classification heuristics
2
2
  const CALL_TYPE_PATTERNS = {
3
- token_transfer: ['transfer', 'transferFrom', 'safeTransfer', 'safeTransferFrom', 'approve', 'send'],
4
- oracle_read: ['getPrice', 'latestRoundData', 'latestAnswer', 'getRoundData', 'consult', 'observe'],
3
+ token_transfer: [
4
+ 'transfer',
5
+ 'transferFrom',
6
+ 'safeTransfer',
7
+ 'safeTransferFrom',
8
+ 'approve',
9
+ 'send',
10
+ ],
11
+ oracle_read: [
12
+ 'getPrice',
13
+ 'latestRoundData',
14
+ 'latestAnswer',
15
+ 'getRoundData',
16
+ 'consult',
17
+ 'observe',
18
+ ],
5
19
  flash_loan: ['flashLoan', 'flash', 'flashBorrow'],
6
- swap: ['swap', 'swapExactTokensForTokens', 'swapTokensForExactTokens', 'exactInputSingle', 'exactInput'],
20
+ swap: [
21
+ 'swap',
22
+ 'swapExactTokensForTokens',
23
+ 'swapTokensForExactTokens',
24
+ 'exactInputSingle',
25
+ 'exactInput',
26
+ ],
7
27
  liquidity: ['addLiquidity', 'removeLiquidity', 'mint', 'burn'],
8
28
  delegate_call: ['delegatecall'],
9
29
  low_level: ['call', 'staticcall'],
@@ -11,7 +31,11 @@ const CALL_TYPE_PATTERNS = {
11
31
  /**
12
32
  * Build external calls from AST-extracted calls, optionally enriched with Slither data.
13
33
  */
14
- export function buildExternalCalls(astCalls, stateVarsImmutability, scopeContracts, accessControlledSetters, slitherDetectors) {
34
+ export function buildExternalCalls(astCalls, stateVarsImmutability, scopeContracts, accessControlledSetters, slitherDetectors, slitherState = slitherDetectors != null
35
+ ? slitherDetectors.length > 0
36
+ ? 'with-findings'
37
+ : 'clean'
38
+ : 'unavailable') {
15
39
  // Build Slither enrichment maps if available
16
40
  const slitherUnchecked = new Set();
17
41
  const slitherReentrancy = new Set();
@@ -41,9 +65,29 @@ export function buildExternalCalls(astCalls, stateVarsImmutability, scopeContrac
41
65
  const trustLevel = classifyTrustLevel(ac.target, stateVarsImmutability, scopeContracts, accessControlledSetters);
42
66
  // Slither enrichment
43
67
  const lineKey = `${ac.file}:${ac.lineStart}`;
44
- const hasSlither = slitherDetectors != null;
68
+ const ran = slitherState !== 'unavailable';
45
69
  const isUnchecked = slitherUnchecked.has(lineKey);
46
70
  const isReentrancy = slitherReentrancy.has(lineKey);
71
+ // Confidence: a detector that actually fired on this call is the strongest
72
+ // signal; a clean Slither run that simply didn't flag it is medium; no
73
+ // Slither run at all is a low-confidence heuristic default.
74
+ const returnCheckedConfidence = isUnchecked
75
+ ? 'high'
76
+ : slitherState === 'with-findings'
77
+ ? 'high'
78
+ : slitherState === 'clean'
79
+ ? 'medium'
80
+ : 'low';
81
+ const reentrancyConfidence = isReentrancy
82
+ ? 'high'
83
+ : slitherState === 'with-findings'
84
+ ? 'high'
85
+ : slitherState === 'clean'
86
+ ? 'medium'
87
+ : 'low';
88
+ const ranReasoning = 'Slither ran without flagging this call';
89
+ const unavailableReturnReasoning = 'Slither unavailable — assuming checked (heuristic default)';
90
+ const unavailableReentrancyReasoning = 'Slither unavailable — reentrancy guard status unknown (heuristic default)';
47
91
  calls.push({
48
92
  contract: ac.contract,
49
93
  function: ac.function,
@@ -56,16 +100,16 @@ export function buildExternalCalls(astCalls, stateVarsImmutability, scopeContrac
56
100
  target: ac.target,
57
101
  method: ac.method,
58
102
  return_checked: {
59
- value: hasSlither ? !isUnchecked : true,
60
- confidence: hasSlither ? 'high' : 'low',
61
- derived_from: hasSlither ? 'slither' : 'heuristic',
62
- ...(hasSlither ? {} : { reasoning: 'No Slither data — assuming checked' }),
103
+ value: ran ? !isUnchecked : true,
104
+ confidence: returnCheckedConfidence,
105
+ derived_from: isUnchecked ? 'slither' : ran ? 'slither' : 'heuristic',
106
+ ...(isUnchecked ? {} : { reasoning: ran ? ranReasoning : unavailableReturnReasoning }),
63
107
  },
64
108
  inside_reentrancy_guard: {
65
- value: hasSlither ? !isReentrancy : false,
66
- confidence: hasSlither ? (isReentrancy ? 'high' : 'medium') : 'low',
67
- derived_from: hasSlither ? 'slither' : 'heuristic',
68
- ...(hasSlither ? {} : { reasoning: 'No Slither data — reentrancy guard status unknown' }),
109
+ value: ran ? !isReentrancy : false,
110
+ confidence: reentrancyConfidence,
111
+ derived_from: isReentrancy ? 'slither' : ran ? 'slither' : 'heuristic',
112
+ ...(isReentrancy ? {} : { reasoning: ran ? ranReasoning : unavailableReentrancyReasoning }),
69
113
  },
70
114
  call_type: callType,
71
115
  trust_level: trustLevel,
@@ -1 +1 @@
1
- {"version":3,"file":"external-calls.js","sourceRoot":"","sources":["../../src/analysis/external-calls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"external-calls.js","sourceRoot":"","sources":["../../src/analysis/external-calls.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../src/commands/calls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,eAAO,MAAM,YAAY,SA+FrB,CAAC"}
1
+ {"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../src/commands/calls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,eAAO,MAAM,YAAY,SA+GrB,CAAC"}
@@ -30,19 +30,28 @@ export const callsCommand = new Command('calls')
30
30
  allAstCalls.push(...calls);
31
31
  }
32
32
  logger.info(`AST extraction found ${allAstCalls.length} external calls`);
33
- // Tier 2: Try Slither for enrichment (unchecked returns, reentrancy)
34
- let slitherDetectors = undefined;
33
+ // Tier 2: Try Slither for enrichment (unchecked returns, reentrancy).
34
+ // Track three states explicitly so the analyzer can distinguish "Slither
35
+ // ran clean" (medium confidence) from "Slither never ran" (low confidence).
36
+ let slitherDetectors = [];
37
+ let slitherState = 'unavailable';
35
38
  try {
36
39
  spin.text = 'Running Slither detectors for enrichment...';
37
40
  const result = await runSlither(config.project.project_dir, [
38
41
  '.',
39
42
  '--detect',
40
43
  'unchecked-transfer,unchecked-lowlevel,reentrancy-eth,reentrancy-no-eth,reentrancy-benign,reentrancy-events,calls-loop',
41
- '--json', '-',
44
+ '--json',
45
+ '-',
42
46
  ]);
43
- if (result.exitCode === 0 && result.stdout) {
47
+ // Slither exits non-zero when it finds issues, so gate on parseable JSON
48
+ // output rather than exit code.
49
+ if (result.stdout) {
44
50
  slitherDetectors = parseDetectors(JSON.parse(result.stdout));
45
- logger.info('Slither enrichment available — enhanced return check and reentrancy data');
51
+ slitherState = slitherDetectors.length > 0 ? 'with-findings' : 'clean';
52
+ logger.info(slitherState === 'clean'
53
+ ? 'Slither ran clean (no detector findings) — Tier 2 fields use medium-confidence defaults'
54
+ : 'Slither enrichment available — enhanced return check and reentrancy data');
46
55
  }
47
56
  }
48
57
  catch {
@@ -76,7 +85,7 @@ export const callsCommand = new Command('calls')
76
85
  }
77
86
  }
78
87
  }
79
- const calls = buildExternalCalls(allAstCalls, immutabilityMap, scopeContracts, accessControlledSetters, slitherDetectors);
88
+ const calls = buildExternalCalls(allAstCalls, immutabilityMap, scopeContracts, accessControlledSetters, slitherDetectors, slitherState);
80
89
  const externalCalls = { calls };
81
90
  const outPath = writeJsonOutput(outputDir, 'external-calls.json', externalCalls);
82
91
  spin.succeed('External call analysis complete');
@@ -1 +1 @@
1
- {"version":3,"file":"calls.js","sourceRoot":"","sources":["../../src/commands/calls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"calls.js","sourceRoot":"","sources":["../../src/commands/calls.ts"],"names":[],"mappings":"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"}
@@ -34,15 +34,15 @@ export const dashboardCommand = new Command('dashboard')
34
34
  }
35
35
  // Use the next binary that ships with this package.
36
36
  const nextBin = resolveNextBin(dashboardDir);
37
+ // Set cwd to the audit project so the dashboard's data loaders find .hex/
38
+ // via process.cwd(). This replaces the old HEX_PROJECT_DIR env-var bridge,
39
+ // which was a vestige from the monorepo days when the dashboard was started
40
+ // from a different working directory.
37
41
  const child = spawn(nextBin, [mode, dashboardDir, '--port', port], {
38
42
  stdio: 'inherit',
39
43
  shell: true,
40
- env: {
41
- ...process.env,
42
- HEX_PROJECT_DIR: projectDir,
43
- SOLAUDIT_PROJECT_DIR: projectDir,
44
- PORT: port,
45
- },
44
+ cwd: projectDir,
45
+ env: { ...process.env, PORT: port },
46
46
  });
47
47
  if (opts.open !== false) {
48
48
  setTimeout(() => {