openhacker 0.1.0 → 0.1.2

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 (35) hide show
  1. package/README.md +2 -3
  2. package/bin/openhacker +1 -1
  3. package/package.json +3 -3
  4. package/src/index.d.ts +1 -0
  5. package/src/index.js +305 -1
  6. package/templates/agent/.env.example +0 -7
  7. package/templates/agent/README.md +1 -2
  8. package/templates/agent/agent/agent.ts +1 -5
  9. package/templates/agent/agent/channels/eve.ts +7 -0
  10. package/templates/agent/agent/instructions.md +7 -45
  11. package/templates/agent/app/globals.css +65 -197
  12. package/templates/agent/app/layout.tsx +2 -22
  13. package/templates/agent/app/page.tsx +80 -102
  14. package/templates/agent/package.json +2 -3
  15. package/src/cli.js +0 -153
  16. package/src/index.ts +0 -1
  17. package/templates/agent/agent/lib/auth.ts +0 -23
  18. package/templates/agent/agent/lib/github.ts +0 -74
  19. package/templates/agent/agent/lib/osv.ts +0 -152
  20. package/templates/agent/agent/lib/scan.ts +0 -153
  21. package/templates/agent/agent/lib/store.ts +0 -151
  22. package/templates/agent/agent/lib/types.ts +0 -63
  23. package/templates/agent/agent/schedules/daily_audit.ts +0 -20
  24. package/templates/agent/agent/tools/check_advisories.ts +0 -27
  25. package/templates/agent/agent/tools/list_targets.ts +0 -21
  26. package/templates/agent/agent/tools/read_repo_file.ts +0 -31
  27. package/templates/agent/agent/tools/report_finding.ts +0 -59
  28. package/templates/agent/agent/tools/run_dependency_scan.ts +0 -16
  29. package/templates/agent/app/_components/ui.tsx +0 -29
  30. package/templates/agent/app/actions.ts +0 -120
  31. package/templates/agent/app/api/scan/route.ts +0 -34
  32. package/templates/agent/app/login/page.tsx +0 -40
  33. package/templates/agent/app/settings/page.tsx +0 -92
  34. package/templates/agent/app/targets/[id]/page.tsx +0 -127
  35. package/templates/agent/proxy.ts +0 -21
@@ -1,92 +0,0 @@
1
- import { getStore } from "@/agent/lib/store";
2
- import { saveSettings } from "../actions";
3
-
4
- export const dynamic = "force-dynamic";
5
-
6
- const MODELS = [
7
- "anthropic/claude-sonnet-4.6",
8
- "anthropic/claude-opus-4.8",
9
- "openai/gpt-5.5",
10
- "google/gemini-2.5-pro",
11
- ];
12
-
13
- export default async function SettingsPage() {
14
- const settings = await getStore().getSettings();
15
-
16
- return (
17
- <main className="container">
18
- <h1>Settings</h1>
19
- <p className="sub">Model, integrations, and remediation policy for this instance.</p>
20
-
21
- <form action={saveSettings}>
22
- <div className="panel">
23
- <h2 style={{ marginTop: 0 }}>Model</h2>
24
- <label htmlFor="model">Gateway model (routed via Vercel AI Gateway)</label>
25
- <select id="model" name="model" defaultValue={settings.model}>
26
- {MODELS.map((m) => (
27
- <option key={m} value={m}>
28
- {m}
29
- </option>
30
- ))}
31
- </select>
32
- <p className="mono-sm" style={{ marginTop: 8 }}>
33
- Note: the eve agent reads its model from the <code>OPENHACKER_MODEL</code> env var at
34
- boot. Set that in Vercel to change the deep-analysis model (a redeploy applies it).
35
- </p>
36
- </div>
37
-
38
- <div className="panel">
39
- <h2 style={{ marginTop: 0 }}>Remediation</h2>
40
- <div className="check">
41
- <input
42
- id="autoRemediate"
43
- name="autoRemediate"
44
- type="checkbox"
45
- defaultChecked={settings.autoRemediate}
46
- />
47
- <label htmlFor="autoRemediate">
48
- Open remediation PRs automatically for proven findings (never auto-merges)
49
- </label>
50
- </div>
51
- </div>
52
-
53
- <div className="panel">
54
- <h2 style={{ marginTop: 0 }}>Integrations</h2>
55
- <div className="check" style={{ marginBottom: 12 }}>
56
- <input
57
- id="githubConnected"
58
- name="githubConnected"
59
- type="checkbox"
60
- defaultChecked={settings.integrations.github.connected}
61
- />
62
- <label htmlFor="githubConnected">GitHub connected (for remediation PRs)</label>
63
- </div>
64
- <div className="check" style={{ marginBottom: 12 }}>
65
- <input
66
- id="hackeroneConnected"
67
- name="hackeroneConnected"
68
- type="checkbox"
69
- defaultChecked={settings.integrations.hackerone.connected}
70
- />
71
- <label htmlFor="hackeroneConnected">
72
- HackerOne connected (validate inbound reports)
73
- </label>
74
- </div>
75
- <label htmlFor="hackeroneHandle">HackerOne program handle</label>
76
- <input
77
- id="hackeroneHandle"
78
- name="hackeroneHandle"
79
- type="text"
80
- placeholder="acme"
81
- defaultValue={settings.integrations.hackerone.handle ?? ""}
82
- />
83
- <p className="mono-sm" style={{ marginTop: 8 }}>
84
- Integration wiring is stubbed in this build; toggles persist the intended config.
85
- </p>
86
- </div>
87
-
88
- <button type="submit">Save settings</button>
89
- </form>
90
- </main>
91
- );
92
- }
@@ -1,127 +0,0 @@
1
- import Link from "next/link";
2
- import { notFound } from "next/navigation";
3
- import { getStore } from "@/agent/lib/store";
4
- import type { Severity } from "@/agent/lib/types";
5
- import { SeverityBadge } from "../../_components/ui";
6
- import { deleteTarget, scanTarget, setFindingStatus } from "../../actions";
7
-
8
- export const dynamic = "force-dynamic";
9
-
10
- const SEV_RANK: Record<Severity, number> = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
11
- const STATUSES = ["open", "triaged", "fixed", "ignored", "false_positive"] as const;
12
-
13
- export default async function TargetPage({ params }: { params: Promise<{ id: string }> }) {
14
- const { id } = await params;
15
- const store = getStore();
16
- const target = await store.getTarget(id);
17
- if (!target) notFound();
18
-
19
- const findings = (await store.listFindings(id)).sort(
20
- (a, b) => SEV_RANK[a.severity] - SEV_RANK[b.severity] || a.title.localeCompare(b.title),
21
- );
22
-
23
- return (
24
- <main className="container">
25
- <p className="mono-sm">
26
- <Link href="/">← Targets</Link>
27
- </p>
28
- <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
29
- <div className="grow" style={{ flex: 1 }}>
30
- <h1>{target.name}</h1>
31
- <p className="sub">
32
- {target.repo}@{target.branch}
33
- {target.autoRemediate ? " · auto-remediate on" : ""}
34
- {target.lastScanAt ? ` · last scan ${new Date(target.lastScanAt).toLocaleString()}` : ""}
35
- </p>
36
- </div>
37
- <div className="actions">
38
- <form action={scanTarget} className="inline">
39
- <input type="hidden" name="id" value={target.id} />
40
- <button type="submit">Scan now</button>
41
- </form>
42
- <form action={deleteTarget} className="inline">
43
- <input type="hidden" name="id" value={target.id} />
44
- <button type="submit" className="btn-danger">
45
- Delete
46
- </button>
47
- </form>
48
- </div>
49
- </div>
50
-
51
- {target.lastScanStatus === "error" ? (
52
- <div className="banner">Last scan failed: {target.lastScanError}</div>
53
- ) : null}
54
-
55
- <h2>Findings ({findings.length})</h2>
56
- {findings.length === 0 ? (
57
- <div className="empty">
58
- No findings recorded yet. Run a scan to check this repository&apos;s dependencies.
59
- </div>
60
- ) : (
61
- <table>
62
- <thead>
63
- <tr>
64
- <th>Severity</th>
65
- <th>Finding</th>
66
- <th>Advisory</th>
67
- <th>Remediation</th>
68
- <th>Status</th>
69
- </tr>
70
- </thead>
71
- <tbody>
72
- {findings.map((f) => (
73
- <tr key={f.id}>
74
- <td>
75
- <SeverityBadge severity={f.severity} />
76
- </td>
77
- <td>
78
- <div>{f.title}</div>
79
- {f.proof.evidence ? <div className="mono-sm">{f.proof.evidence}</div> : null}
80
- <div className="mono-sm">
81
- {f.category}
82
- {f.location ? ` · ${f.location.file}` : ""} · proof: {f.proof.status}
83
- </div>
84
- </td>
85
- <td className="mono-sm">
86
- {(f.advisoryIds ?? []).map((a, i) => (
87
- <div key={a}>
88
- {f.references?.[i] ? (
89
- <a href={f.references[i]} target="_blank" rel="noreferrer">
90
- {a}
91
- </a>
92
- ) : (
93
- a
94
- )}
95
- </div>
96
- ))}
97
- </td>
98
- <td className="mono-sm">
99
- {f.remediation?.summary}
100
- {f.remediation?.fixedVersion ? (
101
- <div>→ {f.remediation.fixedVersion}</div>
102
- ) : null}
103
- </td>
104
- <td>
105
- <form action={setFindingStatus} className="actions">
106
- <input type="hidden" name="targetId" value={target.id} />
107
- <input type="hidden" name="findingId" value={f.id} />
108
- <select name="status" defaultValue={f.status}>
109
- {STATUSES.map((s) => (
110
- <option key={s} value={s}>
111
- {s.replace("_", " ")}
112
- </option>
113
- ))}
114
- </select>
115
- <button type="submit" className="btn-ghost">
116
- Set
117
- </button>
118
- </form>
119
- </td>
120
- </tr>
121
- ))}
122
- </tbody>
123
- </table>
124
- )}
125
- </main>
126
- );
127
- }
@@ -1,21 +0,0 @@
1
- import { NextResponse, type NextRequest } from "next/server";
2
- import { SESSION_COOKIE, authEnabled, expectedToken } from "./agent/lib/auth";
3
-
4
- export async function proxy(req: NextRequest) {
5
- if (!authEnabled()) return NextResponse.next();
6
-
7
- const token = req.cookies.get(SESSION_COOKIE)?.value;
8
- const expected = await expectedToken();
9
- if (token && token === expected) return NextResponse.next();
10
-
11
- const url = req.nextUrl.clone();
12
- url.pathname = "/login";
13
- url.searchParams.set("next", req.nextUrl.pathname);
14
- return NextResponse.redirect(url);
15
- }
16
-
17
- // Protect the dashboard. Excludes Next internals, the login page, the eve agent
18
- // routes (guarded by eve's own auth), and the API (guarded by a bearer token).
19
- export const config = {
20
- matcher: ["/((?!_next/static|_next/image|favicon.ico|login|eve|api).*)"],
21
- };