@harbinger-ai/harbinger 0.1.1 → 0.1.3
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/lib/chat/actions.js +416 -0
- package/lib/chat/components/agents-page.js +545 -0
- package/lib/chat/components/agents-page.jsx +571 -0
- package/lib/chat/components/app-sidebar.js +33 -1
- package/lib/chat/components/app-sidebar.jsx +37 -1
- package/lib/chat/components/findings-page.js +164 -103
- package/lib/chat/components/findings-page.jsx +156 -101
- package/lib/chat/components/icons.js +62 -0
- package/lib/chat/components/icons.jsx +62 -0
- package/lib/chat/components/index.js +3 -0
- package/lib/chat/components/mcp-page.js +383 -55
- package/lib/chat/components/mcp-page.jsx +404 -101
- package/lib/chat/components/mission-control.js +490 -0
- package/lib/chat/components/mission-control.jsx +618 -0
- package/lib/chat/components/registry-page.js +267 -133
- package/lib/chat/components/registry-page.jsx +299 -138
- package/lib/chat/components/settings-layout.js +3 -2
- package/lib/chat/components/settings-layout.jsx +2 -1
- package/lib/chat/components/settings-providers-page.js +337 -0
- package/lib/chat/components/settings-providers-page.jsx +410 -0
- package/lib/chat/components/settings-secrets-page.js +91 -66
- package/lib/chat/components/settings-secrets-page.jsx +83 -72
- package/lib/chat/components/targets-page.js +269 -200
- package/lib/chat/components/targets-page.jsx +181 -111
- package/lib/mcp/actions.js +120 -0
- package/lib/mcp/registry.js +164 -0
- package/package.json +1 -1
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
4
5
|
import { CrosshairIcon, PlusIcon, TrashIcon, ChevronDownIcon, GlobeIcon, SpinnerIcon, DownloadIcon, CheckIcon } from "./icons.js";
|
|
5
6
|
import { getPrograms, createProgram, deleteProgram, getTargets, createTarget, deleteTarget, updateTarget, syncTargetsFromPlatform, syncAllTargets, getSyncStatus } from "../../bounty/actions.js";
|
|
6
7
|
const PLATFORMS = [
|
|
7
|
-
{ id: "hackerone", label: "HackerOne",
|
|
8
|
-
{ id: "bugcrowd", label: "Bugcrowd",
|
|
9
|
-
{ id: "intigriti", label: "Intigriti",
|
|
10
|
-
{ id: "yeswehack", label: "YesWeHack",
|
|
11
|
-
{ id: "federacy", label: "Federacy",
|
|
12
|
-
{ id: "custom", label: "Custom",
|
|
8
|
+
{ id: "hackerone", label: "HackerOne", bg: "bg-purple-500/10", text: "text-purple-500", border: "border-purple-500/20" },
|
|
9
|
+
{ id: "bugcrowd", label: "Bugcrowd", bg: "bg-orange-500/10", text: "text-orange-500", border: "border-orange-500/20" },
|
|
10
|
+
{ id: "intigriti", label: "Intigriti", bg: "bg-blue-500/10", text: "text-blue-500", border: "border-blue-500/20" },
|
|
11
|
+
{ id: "yeswehack", label: "YesWeHack", bg: "bg-teal-500/10", text: "text-teal-500", border: "border-teal-500/20" },
|
|
12
|
+
{ id: "federacy", label: "Federacy", bg: "bg-pink-500/10", text: "text-pink-500", border: "border-pink-500/20" },
|
|
13
|
+
{ id: "custom", label: "Custom", bg: "bg-white/5", text: "text-muted-foreground", border: "border-white/10" }
|
|
13
14
|
];
|
|
14
15
|
const TARGET_TYPES = ["domain", "wildcard", "ip", "cidr", "url", "api", "mobile"];
|
|
15
16
|
const STATUS_COLORS = {
|
|
16
|
-
in_scope: "bg-green-500/10 text-green-500",
|
|
17
|
-
out_of_scope: "bg-red-500/10 text-red-500",
|
|
18
|
-
testing: "bg-yellow-500/10 text-yellow-500",
|
|
19
|
-
completed: "bg-blue-500/10 text-blue-500"
|
|
17
|
+
in_scope: { bg: "bg-green-500/10", text: "text-green-500", border: "border-green-500/20", dot: "bg-green-500" },
|
|
18
|
+
out_of_scope: { bg: "bg-red-500/10", text: "text-red-500", border: "border-red-500/20", dot: "bg-red-500" },
|
|
19
|
+
testing: { bg: "bg-yellow-500/10", text: "text-yellow-500", border: "border-yellow-500/20", dot: "bg-yellow-500 animate-pulse" },
|
|
20
|
+
completed: { bg: "bg-blue-500/10", text: "text-blue-500", border: "border-blue-500/20", dot: "bg-blue-500" }
|
|
20
21
|
};
|
|
21
22
|
function timeAgo(ts) {
|
|
22
23
|
if (!ts) return "never";
|
|
@@ -27,6 +28,9 @@ function timeAgo(ts) {
|
|
|
27
28
|
if (hrs < 24) return `${hrs}h ago`;
|
|
28
29
|
return `${Math.floor(hrs / 24)}d ago`;
|
|
29
30
|
}
|
|
31
|
+
function getPlatStyle(platformId) {
|
|
32
|
+
return PLATFORMS.find((p) => p.id === platformId) || PLATFORMS[5];
|
|
33
|
+
}
|
|
30
34
|
function SyncPanel({ onSync, syncStatus }) {
|
|
31
35
|
const [syncing, setSyncing] = useState(null);
|
|
32
36
|
const [results, setResults] = useState(null);
|
|
@@ -49,183 +53,229 @@ function SyncPanel({ onSync, syncStatus }) {
|
|
|
49
53
|
setSyncing(null);
|
|
50
54
|
}
|
|
51
55
|
const syncablePlatforms = PLATFORMS.filter((p) => p.id !== "custom");
|
|
52
|
-
return /* @__PURE__ */ jsxs(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
return /* @__PURE__ */ jsxs(
|
|
57
|
+
motion.div,
|
|
58
|
+
{
|
|
59
|
+
initial: { opacity: 0, y: 8 },
|
|
60
|
+
animate: { opacity: 1, y: 0 },
|
|
61
|
+
transition: { duration: 0.3 },
|
|
62
|
+
className: "rounded-lg border border-white/[0.06] bg-[--card] p-4 mb-6",
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
65
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
66
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-0.5", children: [
|
|
67
|
+
/* @__PURE__ */ jsx(DownloadIcon, { size: 14 }),
|
|
68
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-mono font-medium text-[--cyan]", children: "Sync from Bounty Platforms" })
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground font-mono", children: "Import programs and targets from bounty-targets-data" })
|
|
71
|
+
] }),
|
|
72
|
+
syncStatus?.lastSyncedAt && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
|
|
73
|
+
"Last sync: ",
|
|
74
|
+
timeAgo(syncStatus.lastSyncedAt)
|
|
75
|
+
] })
|
|
76
|
+
] }),
|
|
77
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 mb-3", children: [
|
|
78
|
+
syncablePlatforms.map((p) => {
|
|
79
|
+
const count = syncStatus?.platformCounts?.[p.id] || 0;
|
|
80
|
+
return /* @__PURE__ */ jsxs(
|
|
81
|
+
"button",
|
|
82
|
+
{
|
|
83
|
+
onClick: () => handleSync(p.id),
|
|
84
|
+
disabled: syncing !== null,
|
|
85
|
+
className: `inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-mono font-medium border transition-colors hover:bg-white/[0.04] disabled:opacity-50 ${p.border}`,
|
|
86
|
+
children: [
|
|
87
|
+
syncing === p.id ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(DownloadIcon, { size: 12 }),
|
|
88
|
+
p.label,
|
|
89
|
+
count > 0 && /* @__PURE__ */ jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[9px] font-mono ${p.bg} ${p.text}`, children: count })
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
p.id
|
|
93
|
+
);
|
|
94
|
+
}),
|
|
95
|
+
/* @__PURE__ */ jsxs(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
onClick: () => handleSync("all"),
|
|
99
|
+
disabled: syncing !== null,
|
|
100
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-mono font-medium bg-[--cyan] text-[--primary-foreground] hover:opacity-90 disabled:opacity-50 transition-opacity",
|
|
101
|
+
children: [
|
|
102
|
+
syncing === "all" ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(DownloadIcon, { size: 12 }),
|
|
103
|
+
"Sync All"
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-3", children: [
|
|
109
|
+
/* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground font-mono uppercase tracking-wider", children: "Max programs:" }),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
"input",
|
|
112
|
+
{
|
|
113
|
+
type: "number",
|
|
114
|
+
value: maxPrograms,
|
|
115
|
+
onChange: (e) => setMaxPrograms(e.target.value),
|
|
116
|
+
className: "w-20 text-xs border border-white/[0.06] rounded-md px-2 py-1 bg-black/20 font-mono focus:outline-none focus:border-[--cyan]/40 transition-colors",
|
|
117
|
+
min: "0",
|
|
118
|
+
placeholder: "0 = all"
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground font-mono", children: "(0 = unlimited)" })
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: results && /* @__PURE__ */ jsx(
|
|
124
|
+
motion.div,
|
|
68
125
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
]
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
" updated"
|
|
119
|
-
] }),
|
|
120
|
-
/* @__PURE__ */ jsxs("span", { className: "text-green-500", children: [
|
|
121
|
-
"+",
|
|
122
|
-
stats.targetsAdded,
|
|
123
|
-
" targets"
|
|
124
|
-
] }),
|
|
125
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
126
|
-
stats.targetsSkipped,
|
|
127
|
-
" skipped"
|
|
128
|
-
] }),
|
|
129
|
-
stats.errors?.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-destructive", children: [
|
|
130
|
-
stats.errors.length,
|
|
131
|
-
" errors"
|
|
132
|
-
] })
|
|
133
|
-
] }, platform)) }) }),
|
|
134
|
-
syncStatus && syncStatus.totalSyncedPrograms > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mt-2 pt-2 border-t", children: [
|
|
135
|
-
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
136
|
-
syncStatus.totalSyncedPrograms,
|
|
137
|
-
" synced programs:"
|
|
138
|
-
] }),
|
|
139
|
-
Object.entries(syncStatus.platformCounts || {}).map(([p, count]) => {
|
|
140
|
-
const plat = PLATFORMS.find((x) => x.id === p);
|
|
141
|
-
return /* @__PURE__ */ jsxs("span", { className: `inline-flex rounded-full px-2 py-0.5 text-[9px] font-medium ${plat?.color || ""}`, children: [
|
|
142
|
-
plat?.label || p,
|
|
143
|
-
" (",
|
|
144
|
-
count,
|
|
145
|
-
")"
|
|
146
|
-
] }, p);
|
|
147
|
-
})
|
|
148
|
-
] })
|
|
149
|
-
] });
|
|
126
|
+
initial: { opacity: 0, height: 0 },
|
|
127
|
+
animate: { opacity: 1, height: "auto" },
|
|
128
|
+
exit: { opacity: 0, height: 0 },
|
|
129
|
+
className: "overflow-hidden",
|
|
130
|
+
children: /* @__PURE__ */ jsx("div", { className: "rounded-md bg-black/30 border border-white/[0.04] p-3 mt-2", children: results.error ? /* @__PURE__ */ jsx("p", { className: "text-xs font-mono text-[--destructive]", children: results.error }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: Object.entries(results).map(([platform, stats]) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs font-mono", children: [
|
|
131
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium w-24 text-foreground/80", children: platform }),
|
|
132
|
+
/* @__PURE__ */ jsxs("span", { className: "text-green-500", children: [
|
|
133
|
+
"+",
|
|
134
|
+
stats.programsAdded,
|
|
135
|
+
" programs"
|
|
136
|
+
] }),
|
|
137
|
+
/* @__PURE__ */ jsxs("span", { className: "text-blue-500", children: [
|
|
138
|
+
stats.programsUpdated,
|
|
139
|
+
" updated"
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsxs("span", { className: "text-green-500", children: [
|
|
142
|
+
"+",
|
|
143
|
+
stats.targetsAdded,
|
|
144
|
+
" targets"
|
|
145
|
+
] }),
|
|
146
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
147
|
+
stats.targetsSkipped,
|
|
148
|
+
" skipped"
|
|
149
|
+
] }),
|
|
150
|
+
stats.errors?.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[--destructive]", children: [
|
|
151
|
+
stats.errors.length,
|
|
152
|
+
" errors"
|
|
153
|
+
] })
|
|
154
|
+
] }, platform)) }) })
|
|
155
|
+
}
|
|
156
|
+
) }),
|
|
157
|
+
syncStatus && syncStatus.totalSyncedPrograms > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mt-3 pt-3 border-t border-white/[0.06]", children: [
|
|
158
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
|
|
159
|
+
syncStatus.totalSyncedPrograms,
|
|
160
|
+
" synced:"
|
|
161
|
+
] }),
|
|
162
|
+
Object.entries(syncStatus.platformCounts || {}).map(([p, count]) => {
|
|
163
|
+
const plat = getPlatStyle(p);
|
|
164
|
+
return /* @__PURE__ */ jsxs("span", { className: `inline-flex rounded-full px-2 py-0.5 text-[9px] font-mono font-medium border ${plat.bg} ${plat.text} ${plat.border}`, children: [
|
|
165
|
+
plat.label,
|
|
166
|
+
" (",
|
|
167
|
+
count,
|
|
168
|
+
")"
|
|
169
|
+
] }, p);
|
|
170
|
+
})
|
|
171
|
+
] })
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
);
|
|
150
175
|
}
|
|
151
|
-
function ProgramCard({ program, onSelect, selected, onDelete }) {
|
|
152
|
-
const
|
|
176
|
+
function ProgramCard({ program, onSelect, selected, onDelete, index }) {
|
|
177
|
+
const plat = getPlatStyle(program.platform);
|
|
153
178
|
return /* @__PURE__ */ jsxs(
|
|
154
|
-
|
|
179
|
+
motion.button,
|
|
155
180
|
{
|
|
181
|
+
initial: { opacity: 0, x: -8 },
|
|
182
|
+
animate: { opacity: 1, x: 0 },
|
|
183
|
+
transition: { duration: 0.25, delay: index * 0.02 },
|
|
156
184
|
onClick: () => onSelect(program.id),
|
|
157
|
-
className: `flex items-center gap-3 w-full text-left p-3 rounded-lg border transition-
|
|
185
|
+
className: `flex items-center gap-3 w-full text-left p-3 rounded-lg border transition-all ${selected ? "border-[--cyan]/40 bg-[--cyan]/5 glow-cyan" : "border-white/[0.06] bg-[--card] hover:border-[--cyan]/20"}`,
|
|
158
186
|
children: [
|
|
159
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
188
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
189
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
190
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
191
|
+
] }),
|
|
160
192
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
161
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: program.name }),
|
|
193
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium truncate", children: program.name }),
|
|
162
194
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5 flex-wrap", children: [
|
|
163
|
-
/* @__PURE__ */ jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-[10px] font-medium ${
|
|
164
|
-
program.maxBounty > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-
|
|
195
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-[10px] font-mono font-medium border ${plat.bg} ${plat.text} ${plat.border}`, children: plat.label }),
|
|
196
|
+
program.maxBounty > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-mono text-green-500", children: [
|
|
165
197
|
"Up to $",
|
|
166
198
|
program.maxBounty.toLocaleString()
|
|
167
199
|
] }),
|
|
168
|
-
program.syncHandle && /* @__PURE__ */ jsx("span", { className: "inline-flex rounded-full bg-cyan
|
|
200
|
+
program.syncHandle && /* @__PURE__ */ jsx("span", { className: "inline-flex rounded-full bg-[--cyan]/10 border border-[--cyan]/20 px-1.5 py-0.5 text-[9px] font-mono text-[--cyan]", children: "synced" })
|
|
169
201
|
] })
|
|
170
202
|
] }),
|
|
171
203
|
!program.syncHandle && /* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
172
204
|
e.stopPropagation();
|
|
173
205
|
onDelete(program.id);
|
|
174
|
-
}, className: "shrink-0 p-1 text-muted-foreground hover:text-destructive rounded", children: /* @__PURE__ */ jsx(TrashIcon, { size: 12 }) })
|
|
206
|
+
}, className: "shrink-0 p-1 text-muted-foreground hover:text-[--destructive] rounded transition-colors", children: /* @__PURE__ */ jsx(TrashIcon, { size: 12 }) })
|
|
175
207
|
]
|
|
176
208
|
}
|
|
177
209
|
);
|
|
178
210
|
}
|
|
179
|
-
function TargetRow({ target, onDelete, onStatusChange }) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
function TargetRow({ target, onDelete, onStatusChange, index }) {
|
|
212
|
+
const st = STATUS_COLORS[target.status] || STATUS_COLORS.in_scope;
|
|
213
|
+
return /* @__PURE__ */ jsxs(
|
|
214
|
+
motion.div,
|
|
215
|
+
{
|
|
216
|
+
initial: { opacity: 0, y: 6 },
|
|
217
|
+
animate: { opacity: 1, y: 0 },
|
|
218
|
+
transition: { duration: 0.2, delay: index * 0.02 },
|
|
219
|
+
className: "flex items-center gap-3 p-3 rounded-lg border border-white/[0.06] bg-[--card] hover:border-[--cyan]/20 transition-colors",
|
|
220
|
+
children: [
|
|
221
|
+
/* @__PURE__ */ jsx("div", { className: `shrink-0 w-2 h-2 rounded-full ${st.dot}` }),
|
|
222
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
223
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium truncate", children: target.value }),
|
|
224
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5 flex-wrap", children: [
|
|
225
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex rounded-full bg-white/5 border border-white/10 px-2 py-0.5 text-[10px] font-mono font-medium text-muted-foreground", children: target.type }),
|
|
226
|
+
target.syncSource && /* @__PURE__ */ jsx("span", { className: "inline-flex rounded-full bg-[--cyan]/10 border border-[--cyan]/20 px-1.5 py-0.5 text-[9px] font-mono text-[--cyan]", children: target.syncSource }),
|
|
227
|
+
target.technologies && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground truncate", children: JSON.parse(target.technologies).join(", ") })
|
|
228
|
+
] })
|
|
229
|
+
] }),
|
|
230
|
+
/* @__PURE__ */ jsxs(
|
|
231
|
+
"select",
|
|
232
|
+
{
|
|
233
|
+
value: target.status,
|
|
234
|
+
onChange: (e) => onStatusChange(target.id, e.target.value),
|
|
235
|
+
className: `text-[10px] font-mono font-medium rounded-full px-2 py-0.5 border cursor-pointer bg-transparent ${st.bg} ${st.text} ${st.border} focus:outline-none`,
|
|
236
|
+
children: [
|
|
237
|
+
/* @__PURE__ */ jsx("option", { value: "in_scope", children: "in scope" }),
|
|
238
|
+
/* @__PURE__ */ jsx("option", { value: "testing", children: "testing" }),
|
|
239
|
+
/* @__PURE__ */ jsx("option", { value: "completed", children: "completed" }),
|
|
240
|
+
/* @__PURE__ */ jsx("option", { value: "out_of_scope", children: "out of scope" })
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
/* @__PURE__ */ jsx("button", { onClick: () => onDelete(target.id), className: "shrink-0 p-1 text-muted-foreground hover:text-[--destructive] rounded transition-colors", children: /* @__PURE__ */ jsx(TrashIcon, { size: 12 }) })
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
);
|
|
206
248
|
}
|
|
207
249
|
function AddForm({ fields, onSubmit }) {
|
|
208
250
|
const [values, setValues] = useState({});
|
|
209
251
|
const [open, setOpen] = useState(false);
|
|
210
|
-
if (!open) return /* @__PURE__ */ jsxs("button", { onClick: () => setOpen(true), className: "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium border border-dashed hover:bg-
|
|
252
|
+
if (!open) return /* @__PURE__ */ jsxs("button", { onClick: () => setOpen(true), className: "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-mono font-medium border border-dashed border-white/[0.12] hover:bg-white/[0.04] transition-colors text-muted-foreground hover:text-foreground", children: [
|
|
211
253
|
/* @__PURE__ */ jsx(PlusIcon, { size: 12 }),
|
|
212
254
|
" Add"
|
|
213
255
|
] });
|
|
214
|
-
return /* @__PURE__ */ jsxs(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
256
|
+
return /* @__PURE__ */ jsxs(
|
|
257
|
+
motion.div,
|
|
258
|
+
{
|
|
259
|
+
initial: { opacity: 0, scale: 0.98 },
|
|
260
|
+
animate: { opacity: 1, scale: 1 },
|
|
261
|
+
className: "flex flex-wrap items-end gap-2 p-3 rounded-lg border border-white/[0.06] bg-[--card]",
|
|
262
|
+
children: [
|
|
263
|
+
fields.map((f) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
264
|
+
/* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground font-mono uppercase tracking-wider", children: f.label }),
|
|
265
|
+
f.type === "select" ? /* @__PURE__ */ jsx("select", { value: values[f.name] || "", onChange: (e) => setValues({ ...values, [f.name]: e.target.value }), className: "text-xs border border-white/[0.06] rounded-md px-2 py-1.5 bg-black/20 font-mono focus:outline-none focus:border-[--cyan]/40 transition-colors", children: f.options.map((o) => /* @__PURE__ */ jsx("option", { value: o, children: o }, o)) }) : /* @__PURE__ */ jsx("input", { type: f.type || "text", placeholder: f.placeholder, value: values[f.name] || "", onChange: (e) => setValues({ ...values, [f.name]: e.target.value }), className: "text-xs border border-white/[0.06] rounded-md px-2 py-1.5 bg-black/20 font-mono min-w-[120px] placeholder:text-muted-foreground/50 focus:outline-none focus:border-[--cyan]/40 transition-colors" })
|
|
266
|
+
] }, f.name)),
|
|
267
|
+
/* @__PURE__ */ jsx("button", { onClick: () => {
|
|
268
|
+
onSubmit(values);
|
|
269
|
+
setValues({});
|
|
270
|
+
setOpen(false);
|
|
271
|
+
}, className: "inline-flex items-center gap-1 rounded-md px-3 py-1.5 text-xs font-mono font-medium bg-[--cyan] text-[--primary-foreground] hover:opacity-90 transition-opacity", children: "Save" }),
|
|
272
|
+
/* @__PURE__ */ jsx("button", { onClick: () => {
|
|
273
|
+
setValues({});
|
|
274
|
+
setOpen(false);
|
|
275
|
+
}, className: "text-xs font-mono text-muted-foreground hover:text-foreground px-2 py-1.5 transition-colors", children: "Cancel" })
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
);
|
|
229
279
|
}
|
|
230
280
|
function TargetsPage() {
|
|
231
281
|
const [programs_, setPrograms] = useState([]);
|
|
@@ -289,39 +339,58 @@ function TargetsPage() {
|
|
|
289
339
|
loadTargets();
|
|
290
340
|
}
|
|
291
341
|
const filteredPrograms = platformFilter === "all" ? programs_ : programs_.filter((p) => p.platform === platformFilter);
|
|
292
|
-
|
|
342
|
+
const inScopeCount = targets_.filter((t) => t.status === "in_scope").length;
|
|
343
|
+
if (loading) return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: [...Array(3)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-16 animate-pulse rounded-lg bg-white/[0.04] border border-white/[0.06]" }, i)) });
|
|
293
344
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
294
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
295
|
-
/* @__PURE__ */
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
", ",
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
"
|
|
345
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
|
|
346
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
347
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-mono font-semibold text-[--cyan] text-glow-cyan", children: "Targets" }),
|
|
348
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-1 font-mono", children: "Bug bounty program and target management" })
|
|
349
|
+
] }),
|
|
350
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
351
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[10px] font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20", children: [
|
|
352
|
+
programs_.length,
|
|
353
|
+
" programs"
|
|
354
|
+
] }),
|
|
355
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[10px] font-mono font-medium bg-green-500/10 text-green-500 border border-green-500/20", children: [
|
|
356
|
+
inScopeCount,
|
|
357
|
+
" in scope"
|
|
358
|
+
] })
|
|
305
359
|
] })
|
|
306
360
|
] }),
|
|
307
361
|
/* @__PURE__ */ jsx(SyncPanel, { onSync: handleSync, syncStatus }),
|
|
308
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1 mb-4 overflow-x-auto pb-1", children: [
|
|
309
|
-
/* @__PURE__ */ jsxs(
|
|
310
|
-
"
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
362
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 mb-4 overflow-x-auto pb-1 scrollbar-thin", children: [
|
|
363
|
+
/* @__PURE__ */ jsxs(
|
|
364
|
+
"button",
|
|
365
|
+
{
|
|
366
|
+
onClick: () => setPlatformFilter("all"),
|
|
367
|
+
className: `shrink-0 px-3 py-1 rounded-full text-[10px] font-mono font-medium border transition-colors ${platformFilter === "all" ? "bg-[--cyan]/10 text-[--cyan] border-[--cyan]/20" : "border-white/[0.06] text-muted-foreground hover:text-foreground hover:border-white/[0.12]"}`,
|
|
368
|
+
children: [
|
|
369
|
+
"All (",
|
|
370
|
+
programs_.length,
|
|
371
|
+
")"
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
),
|
|
375
|
+
PLATFORMS.filter((p) => programs_.some((prog) => prog.platform === p.id)).map((p) => /* @__PURE__ */ jsxs(
|
|
376
|
+
"button",
|
|
377
|
+
{
|
|
378
|
+
onClick: () => setPlatformFilter(p.id),
|
|
379
|
+
className: `shrink-0 px-3 py-1 rounded-full text-[10px] font-mono font-medium border transition-colors ${platformFilter === p.id ? `${p.bg} ${p.text} ${p.border}` : "border-white/[0.06] text-muted-foreground hover:text-foreground hover:border-white/[0.12]"}`,
|
|
380
|
+
children: [
|
|
381
|
+
p.label,
|
|
382
|
+
" (",
|
|
383
|
+
programs_.filter((prog) => prog.platform === p.id).length,
|
|
384
|
+
")"
|
|
385
|
+
]
|
|
386
|
+
},
|
|
387
|
+
p.id
|
|
388
|
+
))
|
|
320
389
|
] }),
|
|
321
390
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6", children: [
|
|
322
391
|
/* @__PURE__ */ jsxs("div", { className: "lg:col-span-1", children: [
|
|
323
392
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
324
|
-
/* @__PURE__ */ jsx("h2", { className: "text-
|
|
393
|
+
/* @__PURE__ */ jsx("h2", { className: "text-[10px] font-mono font-medium uppercase tracking-wider text-muted-foreground", children: "Programs" }),
|
|
325
394
|
/* @__PURE__ */ jsx(
|
|
326
395
|
AddForm,
|
|
327
396
|
{
|
|
@@ -335,15 +404,15 @@ function TargetsPage() {
|
|
|
335
404
|
}
|
|
336
405
|
)
|
|
337
406
|
] }),
|
|
338
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 max-h-[600px] overflow-y-auto", children: filteredPrograms.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8 text-center", children: [
|
|
339
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-
|
|
340
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No programs yet" }),
|
|
341
|
-
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground mt-1", children: "Sync from platforms or add manually" })
|
|
342
|
-
] }) : filteredPrograms.map((p) => /* @__PURE__ */ jsx(ProgramCard, { program: p, selected: selectedProgram === p.id, onSelect: setSelectedProgram, onDelete: handleDeleteProgram }, p.id)) })
|
|
407
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 max-h-[600px] overflow-y-auto scrollbar-thin", children: filteredPrograms.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8 text-center", children: [
|
|
408
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-white/[0.04] border border-white/[0.06] p-3 mb-3", children: /* @__PURE__ */ jsx(GlobeIcon, { size: 20 }) }),
|
|
409
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono text-muted-foreground", children: "No programs yet" }),
|
|
410
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-mono text-muted-foreground mt-1", children: "Sync from platforms or add manually" })
|
|
411
|
+
] }) : filteredPrograms.map((p, i) => /* @__PURE__ */ jsx(ProgramCard, { program: p, selected: selectedProgram === p.id, onSelect: setSelectedProgram, onDelete: handleDeleteProgram, index: i }, p.id)) })
|
|
343
412
|
] }),
|
|
344
413
|
/* @__PURE__ */ jsxs("div", { className: "lg:col-span-2", children: [
|
|
345
414
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
346
|
-
/* @__PURE__ */ jsxs("h2", { className: "text-
|
|
415
|
+
/* @__PURE__ */ jsxs("h2", { className: "text-[10px] font-mono font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
347
416
|
"Targets ",
|
|
348
417
|
selectedProgram && `\u2014 ${programs_.find((p) => p.id === selectedProgram)?.name || ""}`
|
|
349
418
|
] }),
|
|
@@ -358,15 +427,15 @@ function TargetsPage() {
|
|
|
358
427
|
}
|
|
359
428
|
)
|
|
360
429
|
] }),
|
|
361
|
-
!selectedProgram ? /* @__PURE__ */ jsxs(
|
|
362
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-
|
|
363
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "Select a program" }),
|
|
364
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
365
|
-
] }) : targets_.length === 0 ? /* @__PURE__ */ jsxs(
|
|
366
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-
|
|
367
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "No targets yet" }),
|
|
368
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
369
|
-
] }) : /* @__PURE__ */ jsx(
|
|
430
|
+
/* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: !selectedProgram ? /* @__PURE__ */ jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "flex flex-col items-center py-12 text-center", children: [
|
|
431
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-white/[0.04] border border-white/[0.06] p-4 mb-4", children: /* @__PURE__ */ jsx(CrosshairIcon, { size: 24 }) }),
|
|
432
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium mb-1", children: "Select a program" }),
|
|
433
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-mono text-muted-foreground", children: "Choose a program to manage its targets" })
|
|
434
|
+
] }, "empty") : targets_.length === 0 ? /* @__PURE__ */ jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "flex flex-col items-center py-12 text-center", children: [
|
|
435
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-white/[0.04] border border-white/[0.06] p-4 mb-4", children: /* @__PURE__ */ jsx(CrosshairIcon, { size: 24 }) }),
|
|
436
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium mb-1", children: "No targets yet" }),
|
|
437
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-mono text-muted-foreground", children: "Add domains, IPs, and URLs to start hunting" })
|
|
438
|
+
] }, "no-targets") : /* @__PURE__ */ jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "flex flex-col gap-2 max-h-[600px] overflow-y-auto scrollbar-thin", children: targets_.map((t, i) => /* @__PURE__ */ jsx(TargetRow, { target: t, onDelete: handleDeleteTarget, onStatusChange: handleStatusChange, index: i }, t.id)) }, "targets") })
|
|
370
439
|
] })
|
|
371
440
|
] })
|
|
372
441
|
] });
|