@bbradar/mcp 0.1.12 → 0.1.16
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/README.md +7 -0
- package/dist/server.js +1293 -403
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -20,6 +20,7 @@ const MAX_FIND_TARGET_SAMPLE_PROGRAMS = 10;
|
|
|
20
20
|
const MAX_FIND_TARGET_SAMPLES = 20;
|
|
21
21
|
const MAX_FIND_UPSTREAM_REQUEST_BUDGET = 50;
|
|
22
22
|
const DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET = 20;
|
|
23
|
+
const DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET = 4;
|
|
23
24
|
const MAX_ACCEPTED_NUMERIC_LIMIT = 1_000;
|
|
24
25
|
const PROGRAM_COLLECTION_CONCURRENCY = 4;
|
|
25
26
|
const TARGET_SAMPLE_CONCURRENCY = 3;
|
|
@@ -30,10 +31,12 @@ const MAX_FILTER_TARGET_SAMPLE_PROGRAMS = 25;
|
|
|
30
31
|
const MAX_LOCAL_EXPORT_RESOURCES = 25;
|
|
31
32
|
const EXPORT_PREVIEW_LIMIT = 25;
|
|
32
33
|
const STALE_PROGRAM_ID_RESOLVE_PAGES = 1;
|
|
33
|
-
const STALE_PROGRAM_ID_RESOLVE_BUDGET =
|
|
34
|
+
const STALE_PROGRAM_ID_RESOLVE_BUDGET = 8;
|
|
34
35
|
const PROGRAM_FALLBACK_CACHE_MAX_ENTRIES = 200;
|
|
35
36
|
const MAX_RESOLVE_SEARCH_QUERIES = 4;
|
|
37
|
+
const MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS = 8;
|
|
36
38
|
const SDK_VERSION = "1.29.0";
|
|
39
|
+
const PROGRAM_ID_PLATFORM_GUESSES = ["HackerOne", "Bugcrowd", "Intigriti", "HackenProof", "YesWeHack", "Cantina", "Code4rena", "Immunefi"];
|
|
37
40
|
const WEB3_TAGS = ["web3", "crypto", "blockchain", "smart-contract", "smart contract", "defi", "nft", "ethereum", "solana", "solidity"];
|
|
38
41
|
const WEB3_CONTEST_SIGNALS = ["contest", "audit", "competitive", "code4rena", "sherlock", "cantina", "codehawks", "hats", "spearbit", "warden"];
|
|
39
42
|
const VDP_SIGNALS = ["vdp", "disclosure", "responsible disclosure", "vulnerability disclosure", "no bounty", "non-bounty", "no reward"];
|
|
@@ -44,6 +47,15 @@ const PROGRAM_RESOLUTION_STOP_WORDS = new Set([
|
|
|
44
47
|
"targets",
|
|
45
48
|
"scope",
|
|
46
49
|
"scopes",
|
|
50
|
+
"network",
|
|
51
|
+
"networks",
|
|
52
|
+
"protocol",
|
|
53
|
+
"protocols",
|
|
54
|
+
"bug",
|
|
55
|
+
"bugs",
|
|
56
|
+
"bounty",
|
|
57
|
+
"bounties",
|
|
58
|
+
"security",
|
|
47
59
|
"new",
|
|
48
60
|
"latest",
|
|
49
61
|
"recent",
|
|
@@ -70,13 +82,51 @@ const PROGRAM_RESOLUTION_STOP_WORDS = new Set([
|
|
|
70
82
|
"have",
|
|
71
83
|
"any"
|
|
72
84
|
]);
|
|
85
|
+
const TARGET_QUERY_STOP_WORDS = new Set([
|
|
86
|
+
"program",
|
|
87
|
+
"programs",
|
|
88
|
+
"target",
|
|
89
|
+
"targets",
|
|
90
|
+
"scope",
|
|
91
|
+
"scopes",
|
|
92
|
+
"asset",
|
|
93
|
+
"assets",
|
|
94
|
+
"endpoint",
|
|
95
|
+
"endpoints",
|
|
96
|
+
"url",
|
|
97
|
+
"urls",
|
|
98
|
+
"http",
|
|
99
|
+
"https",
|
|
100
|
+
"www",
|
|
101
|
+
"the",
|
|
102
|
+
"a",
|
|
103
|
+
"an",
|
|
104
|
+
"of",
|
|
105
|
+
"on",
|
|
106
|
+
"in",
|
|
107
|
+
"with",
|
|
108
|
+
"to",
|
|
109
|
+
"for",
|
|
110
|
+
"find",
|
|
111
|
+
"show",
|
|
112
|
+
"lookup",
|
|
113
|
+
"look",
|
|
114
|
+
"up",
|
|
115
|
+
"com",
|
|
116
|
+
"org",
|
|
117
|
+
"net",
|
|
118
|
+
"io"
|
|
119
|
+
]);
|
|
73
120
|
const targetListModeSchema = z.enum(["identifiers", "compact", "full"]);
|
|
74
121
|
const changeListModeSchema = z.enum(["compact", "full"]);
|
|
75
122
|
const programListModeSchema = z.enum(["compact", "full"]);
|
|
76
123
|
const rewardThresholdModeSchema = z.enum(["max_at_least", "min_at_least"]);
|
|
77
124
|
const vdpModeSchema = z.enum(["include", "exclude", "only_likely", "only_known_no_bounty"]);
|
|
78
|
-
const targetMatchModeSchema = z
|
|
125
|
+
const targetMatchModeSchema = z
|
|
126
|
+
.enum(["smart", "contains", "exact", "suffix", "wildcard"])
|
|
127
|
+
.describe("How to match target_query. smart is forgiving for URLs, wildcards, target names, and partial words.");
|
|
79
128
|
const programNameActionSchema = z.enum(["new_targets", "targets", "scope_delta", "brief", "target_breakdown"]);
|
|
129
|
+
const guideTopicSchema = z.enum(["start", "program", "discovery", "freshness", "targets", "compare", "export", "filters"]).default("start");
|
|
80
130
|
const readOnlyAnnotations = {
|
|
81
131
|
readOnlyHint: true,
|
|
82
132
|
destructiveHint: false,
|
|
@@ -84,17 +134,36 @@ const readOnlyAnnotations = {
|
|
|
84
134
|
openWorldHint: true
|
|
85
135
|
};
|
|
86
136
|
const TOOL_METRICS = new Map();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
137
|
+
function filterValueSchema() {
|
|
138
|
+
return z
|
|
139
|
+
.string()
|
|
140
|
+
.trim()
|
|
141
|
+
.min(1)
|
|
142
|
+
.max(128)
|
|
143
|
+
.refine((value) => !/[,\u0000-\u001F\u007F]/.test(value), {
|
|
144
|
+
message: "Filter values cannot contain commas or control characters."
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function stringListSchema(description = "Optional filter values. Empty or omitted means all values; do not infer a platform filter.") {
|
|
148
|
+
return z
|
|
149
|
+
.preprocess(normalizeStringListInput, z.array(filterValueSchema()).max(50).default([]))
|
|
150
|
+
.describe(description);
|
|
151
|
+
}
|
|
152
|
+
function platformListSchema() {
|
|
153
|
+
return stringListSchema("Optional platform filters such as HackerOne, Bugcrowd, Intigriti, or HackenProof. Empty or omitted means all platforms.");
|
|
154
|
+
}
|
|
155
|
+
function tagListSchema() {
|
|
156
|
+
return stringListSchema("Optional combined scope/language/target-type tags. Empty or omitted means all tags.");
|
|
157
|
+
}
|
|
158
|
+
function scopeTagListSchema() {
|
|
159
|
+
return stringListSchema("Optional scope tags such as domain, api, mobile, smart-contract, or wildcard. Empty or omitted means all scope tags.");
|
|
160
|
+
}
|
|
161
|
+
function targetTypeListSchema() {
|
|
162
|
+
return stringListSchema("Optional target-type tags such as domain, api, mobile, smart-contract, url, or wildcard. Empty or omitted means all target types.");
|
|
163
|
+
}
|
|
164
|
+
function languageTagListSchema() {
|
|
165
|
+
return stringListSchema("Optional language or technology tags such as javascript, solidity, web3, php, or python. Empty or omitted means all languages.");
|
|
166
|
+
}
|
|
98
167
|
const acceptedTargetLimitSchema = z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM);
|
|
99
168
|
const searchTextSchema = z
|
|
100
169
|
.string()
|
|
@@ -104,6 +173,15 @@ const searchTextSchema = z
|
|
|
104
173
|
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
105
174
|
message: "Search text cannot contain control characters."
|
|
106
175
|
});
|
|
176
|
+
const targetQuerySchema = z
|
|
177
|
+
.string()
|
|
178
|
+
.trim()
|
|
179
|
+
.min(2)
|
|
180
|
+
.max(512)
|
|
181
|
+
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
182
|
+
message: "Target query cannot contain control characters."
|
|
183
|
+
})
|
|
184
|
+
.describe("Target identifier, domain, URL, wildcard, app/package/repo name, or partial target text. URLs and wildcards are normalized automatically.");
|
|
107
185
|
const isoDateTimeSchema = z.string().datetime({ offset: true });
|
|
108
186
|
const pageSchema = z.number().int().min(1).default(1);
|
|
109
187
|
const programsPageSizeSchema = z.number().int().min(1).max(MAX_PROGRAMS_PER_SEARCH).default(20);
|
|
@@ -133,69 +211,97 @@ const programIdSchema = z
|
|
|
133
211
|
message: "program_id cannot contain control characters."
|
|
134
212
|
})
|
|
135
213
|
.describe("platform:handle id.");
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
214
|
+
const programIdentifierSchema = z
|
|
215
|
+
.string()
|
|
216
|
+
.trim()
|
|
217
|
+
.min(2)
|
|
218
|
+
.max(256)
|
|
219
|
+
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
220
|
+
message: "Program identifiers cannot contain control characters."
|
|
221
|
+
})
|
|
222
|
+
.describe("BBRadar platform:handle program_id or a human program name/handle. Names are resolved automatically.");
|
|
223
|
+
function jsonRecordSchema() {
|
|
224
|
+
return z.record(z.unknown());
|
|
225
|
+
}
|
|
226
|
+
function rateLimitOutputSchema() {
|
|
227
|
+
return z.object({
|
|
228
|
+
remaining: z.number().int().min(0),
|
|
229
|
+
reset_at: z.string()
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function toolTimingOutputSchema() {
|
|
233
|
+
return z.object({
|
|
234
|
+
tool_name: z.string(),
|
|
235
|
+
duration_ms: z.number().int().min(0)
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function cacheOutputSchema() {
|
|
239
|
+
return z.object({
|
|
240
|
+
hit: z.boolean(),
|
|
241
|
+
coalesced_live_request: z.boolean().optional(),
|
|
242
|
+
expires_at: z.string().optional()
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function errorOutputSchema() {
|
|
246
|
+
return z.object({
|
|
247
|
+
message: z.string(),
|
|
248
|
+
status: z.number().int().optional(),
|
|
249
|
+
detail: z.unknown().optional(),
|
|
250
|
+
errors: z.unknown().optional()
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function programOutputSchema() {
|
|
254
|
+
return jsonRecordSchema();
|
|
255
|
+
}
|
|
256
|
+
function apiEnvelopeOutputShape() {
|
|
257
|
+
return {
|
|
258
|
+
request_id: z.string(),
|
|
259
|
+
upstream_request_id: z.string().optional(),
|
|
260
|
+
fetched_at: z.string().optional(),
|
|
261
|
+
cache: cacheOutputSchema().optional(),
|
|
262
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
263
|
+
mcp_rate_limit: rateLimitOutputSchema().optional(),
|
|
264
|
+
mcp_timing: toolTimingOutputSchema().optional(),
|
|
265
|
+
warnings: z.array(z.string()).optional(),
|
|
266
|
+
program_id_resolution: jsonRecordSchema().optional(),
|
|
267
|
+
error: errorOutputSchema().optional()
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function programListOutputShape() {
|
|
271
|
+
return {
|
|
272
|
+
...apiEnvelopeOutputShape(),
|
|
273
|
+
programs: z.array(programOutputSchema()).optional(),
|
|
274
|
+
meta: jsonRecordSchema().optional()
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function findProgramsOutputShape() {
|
|
278
|
+
return {
|
|
279
|
+
...programListOutputShape(),
|
|
280
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
281
|
+
filters: jsonRecordSchema().optional(),
|
|
282
|
+
warnings: z.array(z.string()).optional(),
|
|
283
|
+
errors: z.array(jsonRecordSchema()).optional(),
|
|
284
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
285
|
+
ranking_scope: jsonRecordSchema().optional()
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function decisionOutputShape() {
|
|
289
|
+
return {
|
|
290
|
+
...findProgramsOutputShape(),
|
|
291
|
+
partial_success: z.boolean().optional(),
|
|
292
|
+
program_ids_requested: z.array(z.string()).optional(),
|
|
293
|
+
failed_program_ids: z.array(z.string()).optional(),
|
|
294
|
+
compared_programs: z.array(programOutputSchema()).optional(),
|
|
295
|
+
activity: jsonRecordSchema().optional(),
|
|
296
|
+
changes: z.array(jsonRecordSchema()).optional()
|
|
297
|
+
};
|
|
298
|
+
}
|
|
193
299
|
const programCandidatePresetInputSchema = {
|
|
194
|
-
platforms:
|
|
195
|
-
tags:
|
|
196
|
-
scope_tags:
|
|
197
|
-
target_types:
|
|
198
|
-
language_tags:
|
|
300
|
+
platforms: platformListSchema(),
|
|
301
|
+
tags: tagListSchema(),
|
|
302
|
+
scope_tags: scopeTagListSchema(),
|
|
303
|
+
target_types: targetTypeListSchema(),
|
|
304
|
+
language_tags: languageTagListSchema(),
|
|
199
305
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default(["elite", "hot", "strong", "potential"]),
|
|
200
306
|
web3: z.boolean().default(false),
|
|
201
307
|
wildcard: z.boolean().default(false),
|
|
@@ -225,7 +331,9 @@ export function createBbradarServer(client, config) {
|
|
|
225
331
|
}, {
|
|
226
332
|
instructions: [
|
|
227
333
|
"Use BBRadar only for passive bug bounty intelligence; never scan or contact targets.",
|
|
228
|
-
"
|
|
334
|
+
"When unsure which BBRadar tool to use, call get_bbradar_guide first. It is local-only and cheap.",
|
|
335
|
+
"Simple default path: find_program_candidates for discovery, run_program_name_action for one named program, get_program_eligible_targets_by_name for target lists by program name.",
|
|
336
|
+
"Use advanced tools only after the user's goal is clear or the guide points to them.",
|
|
229
337
|
"Partial program name + action: run_program_name_action. Partial program name only: resolve_program.",
|
|
230
338
|
"Default to bounty-eligible in-scope data. Include out-of-scope, ineligible, VDP, disclosure-only, or no-bounty data only when the user explicitly asks.",
|
|
231
339
|
"Recently updated programs across platforms: find_recently_updated_programs. Do not infer a platform such as Immunefi unless the user explicitly names it.",
|
|
@@ -241,14 +349,14 @@ export function createBbradarServer(client, config) {
|
|
|
241
349
|
title: "Search BBRadar Programs",
|
|
242
350
|
description: `List active programs. page_size max ${MAX_PROGRAMS_PER_SEARCH}.`,
|
|
243
351
|
inputSchema: {
|
|
244
|
-
platforms:
|
|
245
|
-
tags:
|
|
352
|
+
platforms: platformListSchema(),
|
|
353
|
+
tags: tagListSchema(),
|
|
246
354
|
updated_since: isoDateTimeSchema.optional().describe("ISO datetime."),
|
|
247
355
|
vdp_mode: vdpModeSchema.default("exclude").describe("VDP/no-bounty filter mode."),
|
|
248
356
|
page: pageSchema,
|
|
249
357
|
page_size: programsPageSizeSchema
|
|
250
358
|
},
|
|
251
|
-
outputSchema: programListOutputShape,
|
|
359
|
+
outputSchema: programListOutputShape(),
|
|
252
360
|
annotations: readOnlyAnnotations
|
|
253
361
|
}, (args) => runTool("search_programs", config, rateLimiter, async () => {
|
|
254
362
|
const { vdp_mode, ...filters } = args;
|
|
@@ -265,14 +373,14 @@ export function createBbradarServer(client, config) {
|
|
|
265
373
|
}));
|
|
266
374
|
server.registerTool("get_program", {
|
|
267
375
|
title: "Get BBRadar Program",
|
|
268
|
-
description: "Get one program by
|
|
376
|
+
description: "Get one program by platform:handle program_id, handle, or human program name. Names are resolved automatically when unambiguous.",
|
|
269
377
|
inputSchema: {
|
|
270
|
-
program_id:
|
|
378
|
+
program_id: programIdentifierSchema
|
|
271
379
|
},
|
|
272
380
|
outputSchema: {
|
|
273
|
-
...apiEnvelopeOutputShape,
|
|
274
|
-
program: programOutputSchema.optional(),
|
|
275
|
-
meta: jsonRecordSchema.optional()
|
|
381
|
+
...apiEnvelopeOutputShape(),
|
|
382
|
+
program: programOutputSchema().optional(),
|
|
383
|
+
meta: jsonRecordSchema().optional()
|
|
276
384
|
},
|
|
277
385
|
annotations: readOnlyAnnotations
|
|
278
386
|
}, (args) => runTool("get_program", config, rateLimiter, async () => {
|
|
@@ -304,37 +412,37 @@ export function createBbradarServer(client, config) {
|
|
|
304
412
|
description: "Resolve partial name/handle text to likely program_ids. Use this when the user gives a program name fragment like Nado.",
|
|
305
413
|
inputSchema: {
|
|
306
414
|
query: searchTextSchema,
|
|
307
|
-
platforms:
|
|
308
|
-
tags:
|
|
415
|
+
platforms: platformListSchema(),
|
|
416
|
+
tags: tagListSchema(),
|
|
309
417
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
310
418
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
311
419
|
max_results: z.number().int().min(1).max(25).default(10),
|
|
312
420
|
output_mode: programListModeSchema.default("compact"),
|
|
313
|
-
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(
|
|
421
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET)
|
|
314
422
|
},
|
|
315
423
|
outputSchema: {
|
|
316
|
-
...programListOutputShape,
|
|
317
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
318
|
-
query: jsonRecordSchema.optional(),
|
|
319
|
-
matches: z.array(jsonRecordSchema).optional(),
|
|
424
|
+
...programListOutputShape(),
|
|
425
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
426
|
+
query: jsonRecordSchema().optional(),
|
|
427
|
+
matches: z.array(jsonRecordSchema()).optional(),
|
|
320
428
|
warnings: z.array(z.string()).optional(),
|
|
321
|
-
upstream_budget: jsonRecordSchema.optional(),
|
|
322
|
-
ranking_scope: jsonRecordSchema.optional()
|
|
429
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
430
|
+
ranking_scope: jsonRecordSchema().optional()
|
|
323
431
|
},
|
|
324
432
|
annotations: readOnlyAnnotations
|
|
325
433
|
}, (args) => runTool("resolve_program", config, rateLimiter, async () => resolveProgram(client, config, args)));
|
|
326
434
|
server.registerTool("run_program_name_action", {
|
|
327
435
|
title: "Run Program Name Action",
|
|
328
|
-
description: "
|
|
436
|
+
description: "Start here for one named program. Resolves a partial program name, then runs targets, new_targets, scope_delta, brief, or target_breakdown.",
|
|
329
437
|
inputSchema: {
|
|
330
438
|
query: searchTextSchema,
|
|
331
439
|
action: programNameActionSchema.default("new_targets"),
|
|
332
|
-
platforms:
|
|
333
|
-
tags:
|
|
440
|
+
platforms: platformListSchema(),
|
|
441
|
+
tags: tagListSchema(),
|
|
334
442
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
335
443
|
since: isoDateTimeSchema.optional(),
|
|
336
|
-
target_type: filterValueSchema.optional(),
|
|
337
|
-
language_tags:
|
|
444
|
+
target_type: filterValueSchema().optional(),
|
|
445
|
+
language_tags: languageTagListSchema(),
|
|
338
446
|
include_out_of_scope: z.boolean().default(false),
|
|
339
447
|
include_ineligible: z.boolean().default(false),
|
|
340
448
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
@@ -343,17 +451,17 @@ export function createBbradarServer(client, config) {
|
|
|
343
451
|
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
344
452
|
max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
345
453
|
output_mode: programListModeSchema.default("compact"),
|
|
346
|
-
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(
|
|
454
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET)
|
|
347
455
|
},
|
|
348
456
|
outputSchema: {
|
|
349
|
-
...apiEnvelopeOutputShape,
|
|
350
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
351
|
-
resolved_program: jsonRecordSchema.optional(),
|
|
457
|
+
...apiEnvelopeOutputShape(),
|
|
458
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
459
|
+
resolved_program: jsonRecordSchema().optional(),
|
|
352
460
|
action: z.string().optional(),
|
|
353
|
-
result: jsonRecordSchema.optional(),
|
|
461
|
+
result: jsonRecordSchema().optional(),
|
|
354
462
|
warnings: z.array(z.string()).optional(),
|
|
355
|
-
upstream_budget: jsonRecordSchema.optional(),
|
|
356
|
-
ranking_scope: jsonRecordSchema.optional()
|
|
463
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
464
|
+
ranking_scope: jsonRecordSchema().optional()
|
|
357
465
|
},
|
|
358
466
|
annotations: readOnlyAnnotations
|
|
359
467
|
}, (args) => runTool("run_program_name_action", config, rateLimiter, async () => runProgramNameAction(client, config, args)));
|
|
@@ -362,11 +470,11 @@ export function createBbradarServer(client, config) {
|
|
|
362
470
|
description: "Fast one-call path for user asks like 'find in-scope eligible targets for Aztec': resolve the program name, fetch targets, and return filtered in-scope bounty-eligible targets. Prefer this over separate resolve_program + get_program_targets calls when the user asks for a target list by name.",
|
|
363
471
|
inputSchema: {
|
|
364
472
|
query: searchTextSchema,
|
|
365
|
-
platforms:
|
|
366
|
-
tags:
|
|
473
|
+
platforms: platformListSchema(),
|
|
474
|
+
tags: tagListSchema(),
|
|
367
475
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
368
476
|
max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
369
|
-
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(
|
|
477
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET),
|
|
370
478
|
include_out_of_scope: z.boolean().default(false),
|
|
371
479
|
include_ineligible: z.boolean().default(false),
|
|
372
480
|
strict_scope_filter: z.boolean().default(true),
|
|
@@ -376,23 +484,23 @@ export function createBbradarServer(client, config) {
|
|
|
376
484
|
output_mode: programListModeSchema.default("compact")
|
|
377
485
|
},
|
|
378
486
|
outputSchema: {
|
|
379
|
-
...apiEnvelopeOutputShape,
|
|
380
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
381
|
-
resolved_program: jsonRecordSchema.optional(),
|
|
487
|
+
...apiEnvelopeOutputShape(),
|
|
488
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
489
|
+
resolved_program: jsonRecordSchema().optional(),
|
|
382
490
|
program_id: z.string().optional(),
|
|
383
491
|
targets: z.array(z.unknown()).optional(),
|
|
384
492
|
warnings: z.array(z.string()).optional(),
|
|
385
|
-
upstream_budget: jsonRecordSchema.optional(),
|
|
386
|
-
ranking_scope: jsonRecordSchema.optional(),
|
|
387
|
-
meta: jsonRecordSchema.optional()
|
|
493
|
+
upstream_budget: jsonRecordSchema().optional(),
|
|
494
|
+
ranking_scope: jsonRecordSchema().optional(),
|
|
495
|
+
meta: jsonRecordSchema().optional()
|
|
388
496
|
},
|
|
389
497
|
annotations: readOnlyAnnotations
|
|
390
498
|
}, (args) => runTool("get_program_eligible_targets_by_name", config, rateLimiter, async () => getProgramEligibleTargetsByName(client, config, args)));
|
|
391
499
|
server.registerTool("get_program_targets", {
|
|
392
500
|
title: "Get Program Targets",
|
|
393
|
-
description: `Get active targets for one
|
|
501
|
+
description: `Get active targets for one program_id, handle, or human program name. This is a final source for eligible target lists; do not call summary/breakdown unless grouped counts are needed. For a program name query, get_program_eligible_targets_by_name is the simplest path. limit max ${MAX_TARGETS_PER_PROGRAM}.`,
|
|
394
502
|
inputSchema: {
|
|
395
|
-
program_id:
|
|
503
|
+
program_id: programIdentifierSchema,
|
|
396
504
|
include_out_of_scope: z.boolean().default(false),
|
|
397
505
|
include_ineligible: z.boolean().default(false),
|
|
398
506
|
strict_scope_filter: z.boolean().default(true),
|
|
@@ -401,10 +509,10 @@ export function createBbradarServer(client, config) {
|
|
|
401
509
|
output_mode: targetListModeSchema.default("compact")
|
|
402
510
|
},
|
|
403
511
|
outputSchema: {
|
|
404
|
-
...apiEnvelopeOutputShape,
|
|
512
|
+
...apiEnvelopeOutputShape(),
|
|
405
513
|
program_id: z.string().optional(),
|
|
406
514
|
targets: z.array(z.unknown()).optional(),
|
|
407
|
-
meta: jsonRecordSchema.optional()
|
|
515
|
+
meta: jsonRecordSchema().optional()
|
|
408
516
|
},
|
|
409
517
|
annotations: readOnlyAnnotations
|
|
410
518
|
}, (args) => runTool("get_program_targets", config, rateLimiter, async () => {
|
|
@@ -419,17 +527,17 @@ export function createBbradarServer(client, config) {
|
|
|
419
527
|
include_ineligible: z.boolean().default(false),
|
|
420
528
|
include_out_of_scope: z.boolean().default(false),
|
|
421
529
|
search: searchTextSchema.optional(),
|
|
422
|
-
platforms:
|
|
423
|
-
tags:
|
|
530
|
+
platforms: platformListSchema(),
|
|
531
|
+
tags: tagListSchema(),
|
|
424
532
|
page: pageSchema,
|
|
425
533
|
page_size: recentChangesPageSizeSchema,
|
|
426
534
|
output_mode: changeListModeSchema.default("compact")
|
|
427
535
|
},
|
|
428
536
|
outputSchema: {
|
|
429
|
-
...apiEnvelopeOutputShape,
|
|
430
|
-
changes: z.array(jsonRecordSchema).optional(),
|
|
431
|
-
meta: jsonRecordSchema.optional(),
|
|
432
|
-
facets: jsonRecordSchema.optional()
|
|
537
|
+
...apiEnvelopeOutputShape(),
|
|
538
|
+
changes: z.array(jsonRecordSchema()).optional(),
|
|
539
|
+
meta: jsonRecordSchema().optional(),
|
|
540
|
+
facets: jsonRecordSchema().optional()
|
|
433
541
|
},
|
|
434
542
|
annotations: readOnlyAnnotations
|
|
435
543
|
}, (args) => runTool("get_recent_changes", config, rateLimiter, async () => {
|
|
@@ -453,7 +561,7 @@ export function createBbradarServer(client, config) {
|
|
|
453
561
|
title: "Get Latest Added Targets",
|
|
454
562
|
description: "Latest added targets by type, optionally with that program's target list.",
|
|
455
563
|
inputSchema: {
|
|
456
|
-
target_type: filterValueSchema.default("domain"),
|
|
564
|
+
target_type: filterValueSchema().default("domain"),
|
|
457
565
|
include_out_of_scope: z.boolean().default(false).describe("Include out-of-scope."),
|
|
458
566
|
include_ineligible: z.boolean().default(false).describe("Include ineligible."),
|
|
459
567
|
recent_changes_page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
@@ -464,14 +572,14 @@ export function createBbradarServer(client, config) {
|
|
|
464
572
|
full_list_include_ineligible: z.boolean().default(false)
|
|
465
573
|
},
|
|
466
574
|
outputSchema: {
|
|
467
|
-
...apiEnvelopeOutputShape,
|
|
468
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
469
|
-
query: jsonRecordSchema.optional(),
|
|
575
|
+
...apiEnvelopeOutputShape(),
|
|
576
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
577
|
+
query: jsonRecordSchema().optional(),
|
|
470
578
|
latest_added_at: z.string().optional(),
|
|
471
|
-
program: programOutputSchema.optional(),
|
|
579
|
+
program: programOutputSchema().optional(),
|
|
472
580
|
new_targets: z.array(z.unknown()).optional(),
|
|
473
581
|
full_targets: z.array(z.unknown()).optional(),
|
|
474
|
-
meta: jsonRecordSchema.optional()
|
|
582
|
+
meta: jsonRecordSchema().optional()
|
|
475
583
|
},
|
|
476
584
|
annotations: readOnlyAnnotations
|
|
477
585
|
}, (args) => runTool("get_latest_added_targets", config, rateLimiter, async () => getLatestAddedTargets(client, config, args)));
|
|
@@ -479,18 +587,18 @@ export function createBbradarServer(client, config) {
|
|
|
479
587
|
title: "Get Program Scope Summary",
|
|
480
588
|
description: "Grouped compact scope for one program.",
|
|
481
589
|
inputSchema: {
|
|
482
|
-
program_id:
|
|
590
|
+
program_id: programIdentifierSchema,
|
|
483
591
|
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
484
592
|
group_limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(MAX_TARGETS_PER_PROGRAM),
|
|
485
593
|
include_out_of_scope: z.boolean().default(false),
|
|
486
594
|
include_ineligible: z.boolean().default(false)
|
|
487
595
|
},
|
|
488
596
|
outputSchema: {
|
|
489
|
-
...apiEnvelopeOutputShape,
|
|
490
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
491
|
-
program: programOutputSchema.optional(),
|
|
492
|
-
scope: jsonRecordSchema.optional(),
|
|
493
|
-
meta: jsonRecordSchema.optional()
|
|
597
|
+
...apiEnvelopeOutputShape(),
|
|
598
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
599
|
+
program: programOutputSchema().optional(),
|
|
600
|
+
scope: jsonRecordSchema().optional(),
|
|
601
|
+
meta: jsonRecordSchema().optional()
|
|
494
602
|
},
|
|
495
603
|
annotations: readOnlyAnnotations
|
|
496
604
|
}, (args) => runTool("get_program_scope_summary", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramScopeSummary(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramScopeSummaryFromIndexFallback(client, indexFallback, { ...args, program_id: indexFallback.resolvedProgramId }))));
|
|
@@ -498,18 +606,18 @@ export function createBbradarServer(client, config) {
|
|
|
498
606
|
title: "Get Program Target Breakdown",
|
|
499
607
|
description: "Target type, scope tag, language tag, and bucket counts for one program.",
|
|
500
608
|
inputSchema: {
|
|
501
|
-
program_id:
|
|
609
|
+
program_id: programIdentifierSchema,
|
|
502
610
|
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
503
611
|
group_limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(100),
|
|
504
612
|
include_out_of_scope: z.boolean().default(false),
|
|
505
613
|
include_ineligible: z.boolean().default(false)
|
|
506
614
|
},
|
|
507
615
|
outputSchema: {
|
|
508
|
-
...apiEnvelopeOutputShape,
|
|
509
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
510
|
-
program: programOutputSchema.optional(),
|
|
511
|
-
breakdown: jsonRecordSchema.optional(),
|
|
512
|
-
meta: jsonRecordSchema.optional()
|
|
616
|
+
...apiEnvelopeOutputShape(),
|
|
617
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
618
|
+
program: programOutputSchema().optional(),
|
|
619
|
+
breakdown: jsonRecordSchema().optional(),
|
|
620
|
+
meta: jsonRecordSchema().optional()
|
|
513
621
|
},
|
|
514
622
|
annotations: readOnlyAnnotations
|
|
515
623
|
}, (args) => runTool("get_program_target_breakdown", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramTargetBreakdown(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramTargetBreakdownFromIndexFallback(client, indexFallback, { ...args, program_id: indexFallback.resolvedProgramId }))));
|
|
@@ -517,11 +625,11 @@ export function createBbradarServer(client, config) {
|
|
|
517
625
|
title: "Get Program Scope Delta",
|
|
518
626
|
description: "Compact recent scope changes for one program.",
|
|
519
627
|
inputSchema: {
|
|
520
|
-
program_id:
|
|
628
|
+
program_id: programIdentifierSchema,
|
|
521
629
|
since: isoDateTimeSchema.optional(),
|
|
522
630
|
change_type: z.enum(["added", "updated", "removed"]).optional(),
|
|
523
|
-
target_type: filterValueSchema.optional(),
|
|
524
|
-
language_tags:
|
|
631
|
+
target_type: filterValueSchema().optional(),
|
|
632
|
+
language_tags: languageTagListSchema(),
|
|
525
633
|
include_removed: z.boolean().default(true),
|
|
526
634
|
include_out_of_scope: z.boolean().default(false),
|
|
527
635
|
include_ineligible: z.boolean().default(false),
|
|
@@ -530,12 +638,12 @@ export function createBbradarServer(client, config) {
|
|
|
530
638
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
531
639
|
},
|
|
532
640
|
outputSchema: {
|
|
533
|
-
...apiEnvelopeOutputShape,
|
|
534
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
535
|
-
program: programOutputSchema.optional(),
|
|
536
|
-
delta: jsonRecordSchema.optional(),
|
|
537
|
-
changes: z.array(jsonRecordSchema).optional(),
|
|
538
|
-
meta: jsonRecordSchema.optional()
|
|
641
|
+
...apiEnvelopeOutputShape(),
|
|
642
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
643
|
+
program: programOutputSchema().optional(),
|
|
644
|
+
delta: jsonRecordSchema().optional(),
|
|
645
|
+
changes: z.array(jsonRecordSchema()).optional(),
|
|
646
|
+
meta: jsonRecordSchema().optional()
|
|
539
647
|
},
|
|
540
648
|
annotations: readOnlyAnnotations
|
|
541
649
|
}, (args) => runTool("get_program_scope_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramScopeDelta(client, config, { ...args, program_id: programId }), (indexFallback) => getProgramScopeDeltaFromIndexedProgram(client, config, { ...args, program_id: indexFallback.resolvedProgramId }, indexFallback.program))));
|
|
@@ -544,24 +652,24 @@ export function createBbradarServer(client, config) {
|
|
|
544
652
|
description: "Recent target changes grouped by program.",
|
|
545
653
|
inputSchema: {
|
|
546
654
|
change_type: z.enum(["added", "updated", "removed"]).optional(),
|
|
547
|
-
target_type: filterValueSchema.optional(),
|
|
655
|
+
target_type: filterValueSchema().optional(),
|
|
548
656
|
include_removed: z.boolean().default(true),
|
|
549
657
|
include_out_of_scope: z.boolean().default(false),
|
|
550
658
|
include_ineligible: z.boolean().default(false),
|
|
551
659
|
search: searchTextSchema.optional(),
|
|
552
|
-
platforms:
|
|
553
|
-
tags:
|
|
554
|
-
language_tags:
|
|
660
|
+
platforms: platformListSchema(),
|
|
661
|
+
tags: tagListSchema(),
|
|
662
|
+
language_tags: languageTagListSchema(),
|
|
555
663
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
556
664
|
max_programs: z.number().int().min(1).max(50).default(10),
|
|
557
665
|
sample_size: z.number().int().min(1).max(20).default(5),
|
|
558
666
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
559
667
|
},
|
|
560
668
|
outputSchema: {
|
|
561
|
-
...apiEnvelopeOutputShape,
|
|
562
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
563
|
-
activity: z.array(jsonRecordSchema).optional(),
|
|
564
|
-
meta: jsonRecordSchema.optional()
|
|
669
|
+
...apiEnvelopeOutputShape(),
|
|
670
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
671
|
+
activity: z.array(jsonRecordSchema()).optional(),
|
|
672
|
+
meta: jsonRecordSchema().optional()
|
|
565
673
|
},
|
|
566
674
|
annotations: readOnlyAnnotations
|
|
567
675
|
}, (args) => runTool("get_recent_target_activity", config, rateLimiter, async () => getRecentTargetActivity(client, config, args)));
|
|
@@ -569,24 +677,24 @@ export function createBbradarServer(client, config) {
|
|
|
569
677
|
title: "Check Program New Targets",
|
|
570
678
|
description: "Yes/no new in-scope targets for one program.",
|
|
571
679
|
inputSchema: {
|
|
572
|
-
program_id:
|
|
680
|
+
program_id: programIdentifierSchema,
|
|
573
681
|
since: isoDateTimeSchema.optional().describe("ISO datetime."),
|
|
574
|
-
target_type: filterValueSchema.optional(),
|
|
682
|
+
target_type: filterValueSchema().optional(),
|
|
575
683
|
include_ineligible: z.boolean().default(false).describe("Include ineligible."),
|
|
576
684
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
577
685
|
max_targets: acceptedTargetLimitSchema.default(25),
|
|
578
686
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
579
687
|
},
|
|
580
688
|
outputSchema: {
|
|
581
|
-
...apiEnvelopeOutputShape,
|
|
582
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
689
|
+
...apiEnvelopeOutputShape(),
|
|
690
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
583
691
|
program_id: z.string().optional(),
|
|
584
|
-
program: programOutputSchema.optional(),
|
|
692
|
+
program: programOutputSchema().optional(),
|
|
585
693
|
has_new_targets: z.boolean().optional(),
|
|
586
694
|
new_target_count: z.number().int().min(0).optional(),
|
|
587
695
|
latest_added_at: z.string().optional(),
|
|
588
696
|
new_targets: z.array(z.unknown()).optional(),
|
|
589
|
-
meta: jsonRecordSchema.optional()
|
|
697
|
+
meta: jsonRecordSchema().optional()
|
|
590
698
|
},
|
|
591
699
|
annotations: readOnlyAnnotations
|
|
592
700
|
}, (args) => runTool("check_program_new_targets", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => checkProgramNewTargets(client, config, { ...args, program_id: programId }), (indexFallback) => checkProgramNewTargetsFromIndexedProgram(client, config, { ...args, program_id: indexFallback.resolvedProgramId }, indexFallback.program))));
|
|
@@ -594,20 +702,20 @@ export function createBbradarServer(client, config) {
|
|
|
594
702
|
title: "Check Watchlist New Targets",
|
|
595
703
|
description: "New in-scope targets across many known program_ids.",
|
|
596
704
|
inputSchema: {
|
|
597
|
-
program_ids: z.array(
|
|
705
|
+
program_ids: z.array(programIdentifierSchema).min(1).max(50),
|
|
598
706
|
since: isoDateTimeSchema.optional(),
|
|
599
|
-
target_type: filterValueSchema.optional(),
|
|
707
|
+
target_type: filterValueSchema().optional(),
|
|
600
708
|
include_ineligible: z.boolean().default(false),
|
|
601
709
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
602
710
|
max_targets_per_program: z.number().int().min(1).max(50).default(10),
|
|
603
711
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
604
712
|
},
|
|
605
713
|
outputSchema: {
|
|
606
|
-
...apiEnvelopeOutputShape,
|
|
607
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
608
|
-
programs: z.array(jsonRecordSchema).optional(),
|
|
714
|
+
...apiEnvelopeOutputShape(),
|
|
715
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
716
|
+
programs: z.array(jsonRecordSchema()).optional(),
|
|
609
717
|
any_new_targets: z.boolean().optional(),
|
|
610
|
-
meta: jsonRecordSchema.optional()
|
|
718
|
+
meta: jsonRecordSchema().optional()
|
|
611
719
|
},
|
|
612
720
|
annotations: readOnlyAnnotations
|
|
613
721
|
}, (args) => runTool("check_watchlist_new_targets", config, rateLimiter, async () => checkWatchlistNewTargets(client, config, args)));
|
|
@@ -616,15 +724,15 @@ export function createBbradarServer(client, config) {
|
|
|
616
724
|
description: `List programs in one opportunity tier. page_size max ${MAX_PROGRAMS_PER_SEARCH}.`,
|
|
617
725
|
inputSchema: {
|
|
618
726
|
level: opportunityLevelSchema.default("elite"),
|
|
619
|
-
platforms:
|
|
620
|
-
tags:
|
|
727
|
+
platforms: platformListSchema(),
|
|
728
|
+
tags: tagListSchema(),
|
|
621
729
|
updated_since: isoDateTimeSchema.optional(),
|
|
622
730
|
vdp_mode: vdpModeSchema.default("exclude").describe("VDP/no-bounty filter mode."),
|
|
623
731
|
page: pageSchema,
|
|
624
732
|
page_size: programsPageSizeSchema
|
|
625
733
|
},
|
|
626
734
|
outputSchema: {
|
|
627
|
-
...programListOutputShape,
|
|
735
|
+
...programListOutputShape(),
|
|
628
736
|
level: opportunityLevelSchema.optional()
|
|
629
737
|
},
|
|
630
738
|
annotations: readOnlyAnnotations
|
|
@@ -646,11 +754,11 @@ export function createBbradarServer(client, config) {
|
|
|
646
754
|
title: "Find BBRadar Programs",
|
|
647
755
|
description: "Ranked program discovery with filters and compact output.",
|
|
648
756
|
inputSchema: {
|
|
649
|
-
platforms:
|
|
650
|
-
tags:
|
|
651
|
-
scope_tags:
|
|
652
|
-
target_types:
|
|
653
|
-
language_tags:
|
|
757
|
+
platforms: platformListSchema(),
|
|
758
|
+
tags: tagListSchema(),
|
|
759
|
+
scope_tags: scopeTagListSchema(),
|
|
760
|
+
target_types: targetTypeListSchema(),
|
|
761
|
+
language_tags: languageTagListSchema(),
|
|
654
762
|
web3: z.boolean().default(false).describe("Require Web3 signals."),
|
|
655
763
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
656
764
|
updated_since: isoDateTimeSchema.optional(),
|
|
@@ -684,7 +792,7 @@ export function createBbradarServer(client, config) {
|
|
|
684
792
|
output_mode: programListModeSchema.default("compact"),
|
|
685
793
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
686
794
|
},
|
|
687
|
-
outputSchema: findProgramsOutputShape,
|
|
795
|
+
outputSchema: findProgramsOutputShape(),
|
|
688
796
|
annotations: readOnlyAnnotations
|
|
689
797
|
}, (args) => runTool("find_programs", config, rateLimiter, async () => findPrograms(client, config, {
|
|
690
798
|
...args,
|
|
@@ -694,8 +802,8 @@ export function createBbradarServer(client, config) {
|
|
|
694
802
|
title: "Find Wildcard Programs",
|
|
695
803
|
description: "Wildcard program shortlist.",
|
|
696
804
|
inputSchema: {
|
|
697
|
-
platforms:
|
|
698
|
-
tags:
|
|
805
|
+
platforms: platformListSchema(),
|
|
806
|
+
tags: tagListSchema(),
|
|
699
807
|
updated_since: isoDateTimeSchema.optional(),
|
|
700
808
|
max_public_report_count: z.number().int().min(0).default(25),
|
|
701
809
|
require_known_report_count: z.boolean().default(true),
|
|
@@ -710,7 +818,7 @@ export function createBbradarServer(client, config) {
|
|
|
710
818
|
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
711
819
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
712
820
|
},
|
|
713
|
-
outputSchema: findProgramsOutputShape,
|
|
821
|
+
outputSchema: findProgramsOutputShape(),
|
|
714
822
|
annotations: readOnlyAnnotations
|
|
715
823
|
}, (args) => runTool("find_wildcard_programs", config, rateLimiter, async () => findPrograms(client, config, {
|
|
716
824
|
platforms: args.platforms,
|
|
@@ -754,8 +862,8 @@ export function createBbradarServer(client, config) {
|
|
|
754
862
|
title: "Find Web3 Programs",
|
|
755
863
|
description: "Web3/blockchain/smart-contract program shortlist.",
|
|
756
864
|
inputSchema: {
|
|
757
|
-
platforms:
|
|
758
|
-
tags:
|
|
865
|
+
platforms: platformListSchema(),
|
|
866
|
+
tags: tagListSchema(),
|
|
759
867
|
updated_since: isoDateTimeSchema.optional(),
|
|
760
868
|
max_public_report_count: z.number().int().min(0).optional(),
|
|
761
869
|
require_known_report_count: z.boolean().default(false),
|
|
@@ -771,7 +879,7 @@ export function createBbradarServer(client, config) {
|
|
|
771
879
|
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
772
880
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
773
881
|
},
|
|
774
|
-
outputSchema: findProgramsOutputShape,
|
|
882
|
+
outputSchema: findProgramsOutputShape(),
|
|
775
883
|
annotations: readOnlyAnnotations
|
|
776
884
|
}, (args) => runTool("find_web3_programs", config, rateLimiter, async () => findPrograms(client, config, {
|
|
777
885
|
platforms: args.platforms,
|
|
@@ -815,10 +923,10 @@ export function createBbradarServer(client, config) {
|
|
|
815
923
|
title: "Find Reward Programs",
|
|
816
924
|
description: "Programs matching reward thresholds.",
|
|
817
925
|
inputSchema: {
|
|
818
|
-
platforms:
|
|
819
|
-
tags:
|
|
820
|
-
target_types:
|
|
821
|
-
language_tags:
|
|
926
|
+
platforms: platformListSchema(),
|
|
927
|
+
tags: tagListSchema(),
|
|
928
|
+
target_types: targetTypeListSchema(),
|
|
929
|
+
language_tags: languageTagListSchema(),
|
|
822
930
|
min_reward: z.number().int().min(0).default(5_000),
|
|
823
931
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
824
932
|
web3: z.boolean().default(false),
|
|
@@ -837,15 +945,15 @@ export function createBbradarServer(client, config) {
|
|
|
837
945
|
output_mode: programListModeSchema.default("compact"),
|
|
838
946
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
839
947
|
},
|
|
840
|
-
outputSchema: findProgramsOutputShape,
|
|
948
|
+
outputSchema: findProgramsOutputShape(),
|
|
841
949
|
annotations: readOnlyAnnotations
|
|
842
950
|
}, (args) => runTool("find_reward_programs", config, rateLimiter, async () => findRewardPrograms(client, config, args)));
|
|
843
951
|
server.registerTool("find_web3_contests", {
|
|
844
952
|
title: "Find Web3 Contests",
|
|
845
953
|
description: "Web3 contest or competitive audit shortlist.",
|
|
846
954
|
inputSchema: {
|
|
847
|
-
platforms:
|
|
848
|
-
tags:
|
|
955
|
+
platforms: platformListSchema(),
|
|
956
|
+
tags: tagListSchema(),
|
|
849
957
|
min_reward: z.number().int().min(0).optional(),
|
|
850
958
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
851
959
|
require_contest_signal: z.boolean().default(true),
|
|
@@ -860,17 +968,17 @@ export function createBbradarServer(client, config) {
|
|
|
860
968
|
output_mode: programListModeSchema.default("compact"),
|
|
861
969
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
862
970
|
},
|
|
863
|
-
outputSchema: findProgramsOutputShape,
|
|
971
|
+
outputSchema: findProgramsOutputShape(),
|
|
864
972
|
annotations: readOnlyAnnotations
|
|
865
973
|
}, (args) => runTool("find_web3_contests", config, rateLimiter, async () => findWeb3Contests(client, config, args)));
|
|
866
974
|
server.registerTool("find_language_programs", {
|
|
867
975
|
title: "Find Language Programs",
|
|
868
976
|
description: "Language-specific program shortlist.",
|
|
869
977
|
inputSchema: {
|
|
870
|
-
platforms:
|
|
871
|
-
tags:
|
|
872
|
-
language_tags: z.array(filterValueSchema).min(1).max(50),
|
|
873
|
-
target_types:
|
|
978
|
+
platforms: platformListSchema(),
|
|
979
|
+
tags: tagListSchema(),
|
|
980
|
+
language_tags: z.array(filterValueSchema()).min(1).max(50),
|
|
981
|
+
target_types: targetTypeListSchema(),
|
|
874
982
|
min_reward: z.number().int().min(0).optional(),
|
|
875
983
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
876
984
|
require_bounty: z.boolean().default(false),
|
|
@@ -887,18 +995,18 @@ export function createBbradarServer(client, config) {
|
|
|
887
995
|
output_mode: programListModeSchema.default("compact"),
|
|
888
996
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
889
997
|
},
|
|
890
|
-
outputSchema: findProgramsOutputShape,
|
|
998
|
+
outputSchema: findProgramsOutputShape(),
|
|
891
999
|
annotations: readOnlyAnnotations
|
|
892
1000
|
}, (args) => runTool("find_language_programs", config, rateLimiter, async () => findLanguagePrograms(client, config, args)));
|
|
893
1001
|
server.registerTool("find_target_type_programs", {
|
|
894
1002
|
title: "Find Target Type Programs",
|
|
895
1003
|
description: "Target-type program shortlist.",
|
|
896
1004
|
inputSchema: {
|
|
897
|
-
platforms:
|
|
898
|
-
tags:
|
|
899
|
-
target_types: z.array(filterValueSchema).min(1).max(50),
|
|
900
|
-
scope_tags:
|
|
901
|
-
language_tags:
|
|
1005
|
+
platforms: platformListSchema(),
|
|
1006
|
+
tags: tagListSchema(),
|
|
1007
|
+
target_types: z.array(filterValueSchema()).min(1).max(50),
|
|
1008
|
+
scope_tags: scopeTagListSchema(),
|
|
1009
|
+
language_tags: languageTagListSchema(),
|
|
902
1010
|
min_reward: z.number().int().min(0).optional(),
|
|
903
1011
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
904
1012
|
require_bounty: z.boolean().default(false),
|
|
@@ -913,17 +1021,17 @@ export function createBbradarServer(client, config) {
|
|
|
913
1021
|
output_mode: programListModeSchema.default("compact"),
|
|
914
1022
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
915
1023
|
},
|
|
916
|
-
outputSchema: findProgramsOutputShape,
|
|
1024
|
+
outputSchema: findProgramsOutputShape(),
|
|
917
1025
|
annotations: readOnlyAnnotations
|
|
918
1026
|
}, (args) => runTool("find_target_type_programs", config, rateLimiter, async () => findTargetTypePrograms(client, config, args)));
|
|
919
1027
|
server.registerTool("find_vdp_programs", {
|
|
920
1028
|
title: "Find VDP Programs",
|
|
921
1029
|
description: "VDP/disclosure-only/no-bounty program shortlist.",
|
|
922
1030
|
inputSchema: {
|
|
923
|
-
platforms:
|
|
924
|
-
tags:
|
|
925
|
-
target_types:
|
|
926
|
-
language_tags:
|
|
1031
|
+
platforms: platformListSchema(),
|
|
1032
|
+
tags: tagListSchema(),
|
|
1033
|
+
target_types: targetTypeListSchema(),
|
|
1034
|
+
language_tags: languageTagListSchema(),
|
|
927
1035
|
vdp_mode: z.enum(["only_likely", "only_known_no_bounty"]).default("only_likely"),
|
|
928
1036
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
929
1037
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
@@ -933,17 +1041,17 @@ export function createBbradarServer(client, config) {
|
|
|
933
1041
|
output_mode: programListModeSchema.default("compact"),
|
|
934
1042
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
935
1043
|
},
|
|
936
|
-
outputSchema: findProgramsOutputShape,
|
|
1044
|
+
outputSchema: findProgramsOutputShape(),
|
|
937
1045
|
annotations: readOnlyAnnotations
|
|
938
1046
|
}, (args) => runTool("find_vdp_programs", config, rateLimiter, async () => findVdpPrograms(client, config, args)));
|
|
939
1047
|
server.registerTool("find_paid_programs", {
|
|
940
1048
|
title: "Find Paid Programs",
|
|
941
1049
|
description: "Paid bounty program shortlist.",
|
|
942
1050
|
inputSchema: {
|
|
943
|
-
platforms:
|
|
944
|
-
tags:
|
|
945
|
-
target_types:
|
|
946
|
-
language_tags:
|
|
1051
|
+
platforms: platformListSchema(),
|
|
1052
|
+
tags: tagListSchema(),
|
|
1053
|
+
target_types: targetTypeListSchema(),
|
|
1054
|
+
language_tags: languageTagListSchema(),
|
|
947
1055
|
min_reward: z.number().int().min(0).optional(),
|
|
948
1056
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
949
1057
|
min_eligible_targets: z.number().int().min(0).default(1),
|
|
@@ -956,18 +1064,18 @@ export function createBbradarServer(client, config) {
|
|
|
956
1064
|
output_mode: programListModeSchema.default("compact"),
|
|
957
1065
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
958
1066
|
},
|
|
959
|
-
outputSchema: findProgramsOutputShape,
|
|
1067
|
+
outputSchema: findProgramsOutputShape(),
|
|
960
1068
|
annotations: readOnlyAnnotations
|
|
961
1069
|
}, (args) => runTool("find_paid_programs", config, rateLimiter, async () => findPaidPrograms(client, config, args)));
|
|
962
1070
|
server.registerTool("find_stack_matches", {
|
|
963
1071
|
title: "Find Stack Matches",
|
|
964
1072
|
description: "Combined stack/language/type/reward shortlist.",
|
|
965
1073
|
inputSchema: {
|
|
966
|
-
platforms:
|
|
967
|
-
tags:
|
|
968
|
-
language_tags:
|
|
969
|
-
target_types:
|
|
970
|
-
scope_tags:
|
|
1074
|
+
platforms: platformListSchema(),
|
|
1075
|
+
tags: tagListSchema(),
|
|
1076
|
+
language_tags: languageTagListSchema(),
|
|
1077
|
+
target_types: targetTypeListSchema(),
|
|
1078
|
+
scope_tags: scopeTagListSchema(),
|
|
971
1079
|
min_reward: z.number().int().min(0).optional(),
|
|
972
1080
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
973
1081
|
web3: z.boolean().default(false),
|
|
@@ -986,17 +1094,17 @@ export function createBbradarServer(client, config) {
|
|
|
986
1094
|
output_mode: programListModeSchema.default("compact"),
|
|
987
1095
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
988
1096
|
},
|
|
989
|
-
outputSchema: findProgramsOutputShape,
|
|
1097
|
+
outputSchema: findProgramsOutputShape(),
|
|
990
1098
|
annotations: readOnlyAnnotations
|
|
991
1099
|
}, (args) => runTool("find_stack_matches", config, rateLimiter, async () => findStackMatches(client, config, args)));
|
|
992
1100
|
server.registerTool("find_low_noise_programs", {
|
|
993
1101
|
title: "Find Low Noise Programs",
|
|
994
1102
|
description: "Low-public-report program shortlist.",
|
|
995
1103
|
inputSchema: {
|
|
996
|
-
platforms:
|
|
997
|
-
tags:
|
|
998
|
-
target_types:
|
|
999
|
-
language_tags:
|
|
1104
|
+
platforms: platformListSchema(),
|
|
1105
|
+
tags: tagListSchema(),
|
|
1106
|
+
target_types: targetTypeListSchema(),
|
|
1107
|
+
language_tags: languageTagListSchema(),
|
|
1000
1108
|
min_reward: z.number().int().min(0).optional(),
|
|
1001
1109
|
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
1002
1110
|
max_public_report_count: z.number().int().min(0).default(5),
|
|
@@ -1012,43 +1120,43 @@ export function createBbradarServer(client, config) {
|
|
|
1012
1120
|
output_mode: programListModeSchema.default("compact"),
|
|
1013
1121
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1014
1122
|
},
|
|
1015
|
-
outputSchema: findProgramsOutputShape,
|
|
1123
|
+
outputSchema: findProgramsOutputShape(),
|
|
1016
1124
|
annotations: readOnlyAnnotations
|
|
1017
1125
|
}, (args) => runTool("find_low_noise_programs", config, rateLimiter, async () => findLowNoisePrograms(client, config, args)));
|
|
1018
1126
|
server.registerTool("find_recent_by_type", {
|
|
1019
1127
|
title: "Find Recent By Type",
|
|
1020
1128
|
description: "Recent changes by target type grouped by program.",
|
|
1021
1129
|
inputSchema: {
|
|
1022
|
-
target_type: filterValueSchema,
|
|
1130
|
+
target_type: filterValueSchema(),
|
|
1023
1131
|
change_type: z.enum(["added", "updated", "removed"]).default("added"),
|
|
1024
1132
|
include_removed: z.boolean().default(false),
|
|
1025
1133
|
include_out_of_scope: z.boolean().default(false),
|
|
1026
1134
|
include_ineligible: z.boolean().default(false),
|
|
1027
1135
|
search: searchTextSchema.optional(),
|
|
1028
|
-
platforms:
|
|
1029
|
-
tags:
|
|
1030
|
-
language_tags:
|
|
1136
|
+
platforms: platformListSchema(),
|
|
1137
|
+
tags: tagListSchema(),
|
|
1138
|
+
language_tags: languageTagListSchema(),
|
|
1031
1139
|
page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
1032
1140
|
max_programs: z.number().int().min(1).max(50).default(10),
|
|
1033
1141
|
sample_size: z.number().int().min(1).max(20).default(5),
|
|
1034
1142
|
target_list_mode: targetListModeSchema.default("identifiers")
|
|
1035
1143
|
},
|
|
1036
1144
|
outputSchema: {
|
|
1037
|
-
...apiEnvelopeOutputShape,
|
|
1038
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
1039
|
-
activity: z.array(jsonRecordSchema).optional(),
|
|
1040
|
-
meta: jsonRecordSchema.optional()
|
|
1145
|
+
...apiEnvelopeOutputShape(),
|
|
1146
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
1147
|
+
activity: z.array(jsonRecordSchema()).optional(),
|
|
1148
|
+
meta: jsonRecordSchema().optional()
|
|
1041
1149
|
},
|
|
1042
1150
|
annotations: readOnlyAnnotations
|
|
1043
1151
|
}, (args) => runTool("find_recent_by_type", config, rateLimiter, async () => findRecentByType(client, config, args)));
|
|
1044
1152
|
server.registerTool("find_programs_by_target", {
|
|
1045
1153
|
title: "Find Programs By Target",
|
|
1046
|
-
description: "Reverse lookup target/domain ownership.",
|
|
1154
|
+
description: "Reverse lookup target/domain ownership. Accepts domains, URLs, wildcards, target names, and partial target text.",
|
|
1047
1155
|
inputSchema: {
|
|
1048
|
-
target_query:
|
|
1049
|
-
match_mode: targetMatchModeSchema.default("
|
|
1050
|
-
platforms:
|
|
1051
|
-
tags:
|
|
1156
|
+
target_query: targetQuerySchema,
|
|
1157
|
+
match_mode: targetMatchModeSchema.default("smart"),
|
|
1158
|
+
platforms: platformListSchema(),
|
|
1159
|
+
tags: tagListSchema(),
|
|
1052
1160
|
use_api_search: z.boolean().default(true),
|
|
1053
1161
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(2),
|
|
1054
1162
|
max_results: z.number().int().min(1).max(25).default(10),
|
|
@@ -1059,33 +1167,33 @@ export function createBbradarServer(client, config) {
|
|
|
1059
1167
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
1060
1168
|
},
|
|
1061
1169
|
outputSchema: {
|
|
1062
|
-
...findProgramsOutputShape,
|
|
1063
|
-
matches: z.array(jsonRecordSchema).optional()
|
|
1170
|
+
...findProgramsOutputShape(),
|
|
1171
|
+
matches: z.array(jsonRecordSchema()).optional()
|
|
1064
1172
|
},
|
|
1065
1173
|
annotations: readOnlyAnnotations
|
|
1066
1174
|
}, (args) => runTool("find_programs_by_target", config, rateLimiter, async () => findProgramsByTarget(client, config, args)));
|
|
1067
1175
|
server.registerTool("find_program_candidates", {
|
|
1068
1176
|
title: "Find BBRadar Program Candidates",
|
|
1069
|
-
description: "Default ranked
|
|
1177
|
+
description: "Start here for broad program discovery. Default ranked hunt-candidate preset with safe filters and compact output.",
|
|
1070
1178
|
inputSchema: programCandidatePresetInputSchema,
|
|
1071
|
-
outputSchema: findProgramsOutputShape,
|
|
1179
|
+
outputSchema: findProgramsOutputShape(),
|
|
1072
1180
|
annotations: readOnlyAnnotations
|
|
1073
1181
|
}, (args) => runTool("find_program_candidates", config, rateLimiter, async () => findProgramCandidates(client, config, args)));
|
|
1074
1182
|
server.registerTool("find_hunt_candidates", {
|
|
1075
1183
|
title: "Find BBRadar Hunt Candidates",
|
|
1076
|
-
description: "Legacy alias for find_program_candidates.",
|
|
1184
|
+
description: "Legacy alias for find_program_candidates. Prefer find_program_candidates for new calls.",
|
|
1077
1185
|
inputSchema: programCandidatePresetInputSchema,
|
|
1078
|
-
outputSchema: findProgramsOutputShape,
|
|
1186
|
+
outputSchema: findProgramsOutputShape(),
|
|
1079
1187
|
annotations: readOnlyAnnotations
|
|
1080
1188
|
}, (args) => runTool("find_hunt_candidates", config, rateLimiter, async () => findProgramCandidates(client, config, args)));
|
|
1081
1189
|
server.registerTool("find_fresh_hunt_candidates", {
|
|
1082
1190
|
title: "Find Fresh Hunt Candidates",
|
|
1083
1191
|
description: "Freshness-first hunt shortlist.",
|
|
1084
1192
|
inputSchema: {
|
|
1085
|
-
platforms:
|
|
1086
|
-
tags:
|
|
1087
|
-
target_types: z.array(filterValueSchema).max(50).default(["domain"]),
|
|
1088
|
-
language_tags:
|
|
1193
|
+
platforms: platformListSchema(),
|
|
1194
|
+
tags: tagListSchema(),
|
|
1195
|
+
target_types: z.array(filterValueSchema()).max(50).default(["domain"]),
|
|
1196
|
+
language_tags: languageTagListSchema(),
|
|
1089
1197
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default(["elite", "hot", "strong", "potential"]),
|
|
1090
1198
|
min_added_7d: z.number().int().min(0).default(1),
|
|
1091
1199
|
max_public_report_count: z.number().int().min(0).default(25),
|
|
@@ -1107,7 +1215,7 @@ export function createBbradarServer(client, config) {
|
|
|
1107
1215
|
output_mode: programListModeSchema.default("compact"),
|
|
1108
1216
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
1109
1217
|
},
|
|
1110
|
-
outputSchema: findProgramsOutputShape,
|
|
1218
|
+
outputSchema: findProgramsOutputShape(),
|
|
1111
1219
|
annotations: readOnlyAnnotations
|
|
1112
1220
|
}, (args) => runTool("find_fresh_hunt_candidates", config, rateLimiter, async () => findPrograms(client, config, {
|
|
1113
1221
|
platforms: args.platforms,
|
|
@@ -1151,10 +1259,10 @@ export function createBbradarServer(client, config) {
|
|
|
1151
1259
|
title: "Find Recently Updated Programs",
|
|
1152
1260
|
description: "Recently updated program shortlist across all platforms by default. Platform filters are applied only when explicitly provided.",
|
|
1153
1261
|
inputSchema: {
|
|
1154
|
-
platforms:
|
|
1155
|
-
tags:
|
|
1156
|
-
target_types:
|
|
1157
|
-
language_tags:
|
|
1262
|
+
platforms: platformListSchema(),
|
|
1263
|
+
tags: tagListSchema(),
|
|
1264
|
+
target_types: targetTypeListSchema(),
|
|
1265
|
+
language_tags: languageTagListSchema(),
|
|
1158
1266
|
web3: z.boolean().default(false),
|
|
1159
1267
|
updated_since: isoDateTimeSchema.optional(),
|
|
1160
1268
|
require_bounty: z.boolean().default(false),
|
|
@@ -1169,35 +1277,50 @@ export function createBbradarServer(client, config) {
|
|
|
1169
1277
|
output_mode: programListModeSchema.default("compact"),
|
|
1170
1278
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1171
1279
|
},
|
|
1172
|
-
outputSchema: findProgramsOutputShape,
|
|
1280
|
+
outputSchema: findProgramsOutputShape(),
|
|
1173
1281
|
annotations: readOnlyAnnotations
|
|
1174
1282
|
}, (args) => runTool("find_recently_updated_programs", config, rateLimiter, async () => findRecentlyUpdatedPrograms(client, config, args)));
|
|
1175
1283
|
server.registerTool("list_filters", {
|
|
1176
1284
|
title: "List BBRadar Filters",
|
|
1177
1285
|
description: "Discover observed filter values.",
|
|
1178
1286
|
inputSchema: {
|
|
1179
|
-
platforms:
|
|
1180
|
-
tags:
|
|
1287
|
+
platforms: platformListSchema(),
|
|
1288
|
+
tags: tagListSchema(),
|
|
1181
1289
|
max_pages: z.number().int().min(1).max(MAX_FILTER_DISCOVERY_PAGES).default(DEFAULT_FILTER_DISCOVERY_PAGES),
|
|
1182
1290
|
include_target_facets: z.boolean().default(true),
|
|
1183
1291
|
target_sample_programs: z.number().int().min(0).max(MAX_FILTER_TARGET_SAMPLE_PROGRAMS).default(DEFAULT_FILTER_TARGET_SAMPLE_PROGRAMS),
|
|
1184
1292
|
upstream_request_budget: z.number().int().min(1).max(MAX_FIND_UPSTREAM_REQUEST_BUDGET).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1185
1293
|
},
|
|
1186
1294
|
outputSchema: {
|
|
1187
|
-
...apiEnvelopeOutputShape,
|
|
1188
|
-
filters: jsonRecordSchema.optional(),
|
|
1189
|
-
observed_from: jsonRecordSchema.optional(),
|
|
1190
|
-
upstream_budget: jsonRecordSchema.optional()
|
|
1295
|
+
...apiEnvelopeOutputShape(),
|
|
1296
|
+
filters: jsonRecordSchema().optional(),
|
|
1297
|
+
observed_from: jsonRecordSchema().optional(),
|
|
1298
|
+
upstream_budget: jsonRecordSchema().optional()
|
|
1191
1299
|
},
|
|
1192
1300
|
annotations: readOnlyAnnotations
|
|
1193
1301
|
}, (args) => runTool("list_filters", config, rateLimiter, async () => listFilters(client, args)));
|
|
1302
|
+
server.registerTool("get_bbradar_guide", {
|
|
1303
|
+
title: "Get BBRadar Tool Guide",
|
|
1304
|
+
description: "Local quick-start guide for choosing BBRadar tools. Call this when the user's request is ambiguous or when a smaller model may need a decision tree.",
|
|
1305
|
+
inputSchema: {
|
|
1306
|
+
topic: guideTopicSchema.describe("Guide focus area. Use start for the compact default decision tree.")
|
|
1307
|
+
},
|
|
1308
|
+
outputSchema: {
|
|
1309
|
+
...apiEnvelopeOutputShape(),
|
|
1310
|
+
guide: jsonRecordSchema().optional()
|
|
1311
|
+
},
|
|
1312
|
+
annotations: readOnlyAnnotations
|
|
1313
|
+
}, (args) => runTool("get_bbradar_guide", config, rateLimiter, async () => ({
|
|
1314
|
+
request_id: randomUUID(),
|
|
1315
|
+
guide: bbradarGuide(args.topic)
|
|
1316
|
+
})));
|
|
1194
1317
|
server.registerTool("get_mcp_status", {
|
|
1195
1318
|
title: "Get BBRadar MCP Status",
|
|
1196
1319
|
description: "Local MCP status and timing metrics.",
|
|
1197
1320
|
inputSchema: {},
|
|
1198
1321
|
outputSchema: {
|
|
1199
|
-
...apiEnvelopeOutputShape,
|
|
1200
|
-
status: jsonRecordSchema.optional()
|
|
1322
|
+
...apiEnvelopeOutputShape(),
|
|
1323
|
+
status: jsonRecordSchema().optional()
|
|
1201
1324
|
},
|
|
1202
1325
|
annotations: readOnlyAnnotations
|
|
1203
1326
|
}, () => runTool("get_mcp_status", config, rateLimiter, async () => ({
|
|
@@ -1226,41 +1349,41 @@ export function createBbradarServer(client, config) {
|
|
|
1226
1349
|
title: "Compare BBRadar Programs",
|
|
1227
1350
|
description: "Compare programs.",
|
|
1228
1351
|
inputSchema: {
|
|
1229
|
-
program_ids: z.array(
|
|
1352
|
+
program_ids: z.array(programIdentifierSchema).min(2).max(10),
|
|
1230
1353
|
include_target_samples: z.boolean().default(false),
|
|
1231
1354
|
target_sample_size: z.number().int().min(1).max(MAX_FIND_TARGET_SAMPLES).default(5),
|
|
1232
1355
|
only_in_scope_targets: z.boolean().default(true),
|
|
1233
1356
|
only_bounty_eligible_targets: z.boolean().default(true),
|
|
1234
1357
|
output_mode: programListModeSchema.default("compact")
|
|
1235
1358
|
},
|
|
1236
|
-
outputSchema: decisionOutputShape,
|
|
1359
|
+
outputSchema: decisionOutputShape(),
|
|
1237
1360
|
annotations: readOnlyAnnotations
|
|
1238
1361
|
}, (args) => runTool("compare_programs", config, rateLimiter, async () => comparePrograms(client, config, args)));
|
|
1239
1362
|
server.registerTool("compare_programs_compact", {
|
|
1240
1363
|
title: "Compare Programs Compact",
|
|
1241
1364
|
description: "Compact program comparison.",
|
|
1242
1365
|
inputSchema: {
|
|
1243
|
-
program_ids: z.array(
|
|
1366
|
+
program_ids: z.array(programIdentifierSchema).min(2).max(10),
|
|
1244
1367
|
include_target_samples: z.boolean().default(false),
|
|
1245
1368
|
target_sample_size: z.number().int().min(1).max(5).default(3),
|
|
1246
1369
|
only_in_scope_targets: z.boolean().default(true),
|
|
1247
1370
|
only_bounty_eligible_targets: z.boolean().default(true)
|
|
1248
1371
|
},
|
|
1249
|
-
outputSchema: decisionOutputShape,
|
|
1372
|
+
outputSchema: decisionOutputShape(),
|
|
1250
1373
|
annotations: readOnlyAnnotations
|
|
1251
1374
|
}, (args) => runTool("compare_programs_compact", config, rateLimiter, async () => comparePrograms(client, config, { ...args, output_mode: "compact" })));
|
|
1252
1375
|
server.registerTool("summarize_program_activity", {
|
|
1253
1376
|
title: "Summarize Program Activity",
|
|
1254
1377
|
description: "Summarize one program's activity.",
|
|
1255
1378
|
inputSchema: {
|
|
1256
|
-
program_id:
|
|
1379
|
+
program_id: programIdentifierSchema,
|
|
1257
1380
|
recent_changes_limit: z.number().int().min(0).max(MAX_RECENT_CHANGES).default(25)
|
|
1258
1381
|
},
|
|
1259
1382
|
outputSchema: {
|
|
1260
|
-
...apiEnvelopeOutputShape,
|
|
1261
|
-
program: programOutputSchema.optional(),
|
|
1262
|
-
activity: jsonRecordSchema.optional(),
|
|
1263
|
-
changes: z.array(jsonRecordSchema).optional()
|
|
1383
|
+
...apiEnvelopeOutputShape(),
|
|
1384
|
+
program: programOutputSchema().optional(),
|
|
1385
|
+
activity: jsonRecordSchema().optional(),
|
|
1386
|
+
changes: z.array(jsonRecordSchema()).optional()
|
|
1264
1387
|
},
|
|
1265
1388
|
annotations: readOnlyAnnotations
|
|
1266
1389
|
}, (args) => runTool("summarize_program_activity", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => summarizeProgramActivity(client, config, programId, args.recent_changes_limit))));
|
|
@@ -1268,7 +1391,7 @@ export function createBbradarServer(client, config) {
|
|
|
1268
1391
|
title: "Get Program Brief",
|
|
1269
1392
|
description: "Compact worth-hunting brief for one program.",
|
|
1270
1393
|
inputSchema: {
|
|
1271
|
-
program_id:
|
|
1394
|
+
program_id: programIdentifierSchema,
|
|
1272
1395
|
target_sample_size: z.number().int().min(1).max(50).default(10),
|
|
1273
1396
|
target_list_mode: targetListModeSchema.default("compact"),
|
|
1274
1397
|
include_recent_changes: z.boolean().default(true),
|
|
@@ -1277,13 +1400,13 @@ export function createBbradarServer(client, config) {
|
|
|
1277
1400
|
include_ineligible: z.boolean().default(false)
|
|
1278
1401
|
},
|
|
1279
1402
|
outputSchema: {
|
|
1280
|
-
...apiEnvelopeOutputShape,
|
|
1281
|
-
source_requests: z.array(jsonRecordSchema).optional(),
|
|
1282
|
-
program: programOutputSchema.optional(),
|
|
1283
|
-
brief: jsonRecordSchema.optional(),
|
|
1284
|
-
target_samples: jsonRecordSchema.optional(),
|
|
1285
|
-
recent_changes: z.array(jsonRecordSchema).optional(),
|
|
1286
|
-
meta: jsonRecordSchema.optional()
|
|
1403
|
+
...apiEnvelopeOutputShape(),
|
|
1404
|
+
source_requests: z.array(jsonRecordSchema()).optional(),
|
|
1405
|
+
program: programOutputSchema().optional(),
|
|
1406
|
+
brief: jsonRecordSchema().optional(),
|
|
1407
|
+
target_samples: jsonRecordSchema().optional(),
|
|
1408
|
+
recent_changes: z.array(jsonRecordSchema()).optional(),
|
|
1409
|
+
meta: jsonRecordSchema().optional()
|
|
1287
1410
|
},
|
|
1288
1411
|
annotations: readOnlyAnnotations
|
|
1289
1412
|
}, (args) => runTool("get_program_brief", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramBrief(client, config, { ...args, program_id: programId }))));
|
|
@@ -1291,8 +1414,8 @@ export function createBbradarServer(client, config) {
|
|
|
1291
1414
|
title: "Find Recently Added Wildcards",
|
|
1292
1415
|
description: "Recently added wildcard programs.",
|
|
1293
1416
|
inputSchema: {
|
|
1294
|
-
platforms:
|
|
1295
|
-
tags:
|
|
1417
|
+
platforms: platformListSchema(),
|
|
1418
|
+
tags: tagListSchema(),
|
|
1296
1419
|
min_added_7d: z.number().int().min(1).default(1),
|
|
1297
1420
|
max_public_report_count: z.number().int().min(0).optional(),
|
|
1298
1421
|
require_bounty: z.boolean().default(true),
|
|
@@ -1300,7 +1423,7 @@ export function createBbradarServer(client, config) {
|
|
|
1300
1423
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1301
1424
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1302
1425
|
},
|
|
1303
|
-
outputSchema: findProgramsOutputShape,
|
|
1426
|
+
outputSchema: findProgramsOutputShape(),
|
|
1304
1427
|
annotations: readOnlyAnnotations
|
|
1305
1428
|
}, (args) => runTool("find_recently_added_wildcards", config, rateLimiter, async () => findPrograms(client, config, {
|
|
1306
1429
|
platforms: args.platforms,
|
|
@@ -1343,10 +1466,10 @@ export function createBbradarServer(client, config) {
|
|
|
1343
1466
|
title: "Find Low Competition High Reward",
|
|
1344
1467
|
description: "Legacy low-report high-reward shortlist.",
|
|
1345
1468
|
inputSchema: {
|
|
1346
|
-
platforms:
|
|
1347
|
-
tags:
|
|
1348
|
-
target_types:
|
|
1349
|
-
language_tags:
|
|
1469
|
+
platforms: platformListSchema(),
|
|
1470
|
+
tags: tagListSchema(),
|
|
1471
|
+
target_types: targetTypeListSchema(),
|
|
1472
|
+
language_tags: languageTagListSchema(),
|
|
1350
1473
|
max_public_report_count: z.number().int().min(0).default(10),
|
|
1351
1474
|
min_bounty_max: z.number().int().min(0).default(5_000),
|
|
1352
1475
|
min_eligible_targets: z.number().int().min(0).default(1),
|
|
@@ -1359,7 +1482,7 @@ export function createBbradarServer(client, config) {
|
|
|
1359
1482
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1360
1483
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1361
1484
|
},
|
|
1362
|
-
outputSchema: findProgramsOutputShape,
|
|
1485
|
+
outputSchema: findProgramsOutputShape(),
|
|
1363
1486
|
annotations: readOnlyAnnotations
|
|
1364
1487
|
}, (args) => runTool("find_low_competition_high_reward", config, rateLimiter, async () => findPrograms(client, config, {
|
|
1365
1488
|
platforms: args.platforms,
|
|
@@ -1402,17 +1525,17 @@ export function createBbradarServer(client, config) {
|
|
|
1402
1525
|
title: "Get Program Delta",
|
|
1403
1526
|
description: "Recent target changes for one program.",
|
|
1404
1527
|
inputSchema: {
|
|
1405
|
-
program_id:
|
|
1528
|
+
program_id: programIdentifierSchema,
|
|
1406
1529
|
include_removed: z.boolean().default(true),
|
|
1407
1530
|
include_ineligible: z.boolean().default(false),
|
|
1408
1531
|
include_out_of_scope: z.boolean().default(false),
|
|
1409
1532
|
page_size: recentChangesPageSizeSchema.default(50)
|
|
1410
1533
|
},
|
|
1411
1534
|
outputSchema: {
|
|
1412
|
-
...apiEnvelopeOutputShape,
|
|
1413
|
-
program: programOutputSchema.optional(),
|
|
1414
|
-
changes: z.array(jsonRecordSchema).optional(),
|
|
1415
|
-
meta: jsonRecordSchema.optional()
|
|
1535
|
+
...apiEnvelopeOutputShape(),
|
|
1536
|
+
program: programOutputSchema().optional(),
|
|
1537
|
+
changes: z.array(jsonRecordSchema()).optional(),
|
|
1538
|
+
meta: jsonRecordSchema().optional()
|
|
1416
1539
|
},
|
|
1417
1540
|
annotations: readOnlyAnnotations
|
|
1418
1541
|
}, (args) => runTool("get_program_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramDelta(client, config, { ...args, program_id: programId }))));
|
|
@@ -1420,9 +1543,9 @@ export function createBbradarServer(client, config) {
|
|
|
1420
1543
|
title: "Export BBRadar Targets",
|
|
1421
1544
|
description: `Read-only target export. limit max ${MAX_TARGET_EXPORT}.`,
|
|
1422
1545
|
inputSchema: {
|
|
1423
|
-
program_ids: z.array(
|
|
1424
|
-
platforms:
|
|
1425
|
-
tags:
|
|
1546
|
+
program_ids: z.array(programIdentifierSchema).max(50).default([]),
|
|
1547
|
+
platforms: platformListSchema(),
|
|
1548
|
+
tags: tagListSchema(),
|
|
1426
1549
|
opportunity_levels: z.array(z.enum(["elite", "hot", "strong", "potential"])).max(4).default([]),
|
|
1427
1550
|
include_out_of_scope: z.boolean().default(false),
|
|
1428
1551
|
include_ineligible: z.boolean().default(false),
|
|
@@ -1430,19 +1553,26 @@ export function createBbradarServer(client, config) {
|
|
|
1430
1553
|
limit: z.number().int().min(1).max(MAX_TARGET_EXPORT).default(MAX_TARGET_EXPORT)
|
|
1431
1554
|
},
|
|
1432
1555
|
outputSchema: {
|
|
1433
|
-
...apiEnvelopeOutputShape,
|
|
1556
|
+
...apiEnvelopeOutputShape(),
|
|
1434
1557
|
export: z.unknown().optional(),
|
|
1435
|
-
meta: jsonRecordSchema.optional()
|
|
1558
|
+
meta: jsonRecordSchema().optional()
|
|
1436
1559
|
},
|
|
1437
1560
|
annotations: readOnlyAnnotations
|
|
1438
1561
|
}, (args) => runTool("export_targets", config, rateLimiter, async () => {
|
|
1562
|
+
const resolvedProgramIds = await resolveProgramIdentifiers(client, config, args.program_ids);
|
|
1563
|
+
const exportArgs = {
|
|
1564
|
+
...args,
|
|
1565
|
+
program_ids: resolvedProgramIds.entries.map((entry) => entry.resolvedProgramId)
|
|
1566
|
+
};
|
|
1439
1567
|
try {
|
|
1440
|
-
const api = await client.exportTargets(compactRecord(
|
|
1568
|
+
const api = await client.exportTargets(compactRecord(exportArgs));
|
|
1441
1569
|
const exportId = randomUUID();
|
|
1442
1570
|
const sanitizedExport = sanitizeTargetsForExport(api.data, args.limit);
|
|
1443
1571
|
const resourceUri = exportResourceUri(exportId);
|
|
1444
1572
|
rememberExport(exportStore, exportId, sanitizedExport);
|
|
1445
1573
|
return withApiMetadata(api, {
|
|
1574
|
+
source_requests: resolvedProgramIds.sourceRequests.length > 0 ? resolvedProgramIds.sourceRequests : undefined,
|
|
1575
|
+
warnings: resolvedProgramIds.warnings.length > 0 ? resolvedProgramIds.warnings : undefined,
|
|
1446
1576
|
export: {
|
|
1447
1577
|
export_id: exportId,
|
|
1448
1578
|
resource_uri: resourceUri,
|
|
@@ -1456,13 +1586,168 @@ export function createBbradarServer(client, config) {
|
|
|
1456
1586
|
if (!(error instanceof BBRadarApiError) || (error.status !== 404 && error.status !== 405)) {
|
|
1457
1587
|
throw error;
|
|
1458
1588
|
}
|
|
1459
|
-
|
|
1589
|
+
const fallbackPayload = await exportTargetsFromProgramTargetsFallback(client, config, exportStore, exportArgs, error);
|
|
1590
|
+
const fallbackSourceRequests = readArray(fallbackPayload.source_requests).filter((request) => readObject(request) !== undefined);
|
|
1591
|
+
const fallbackWarnings = readArray(fallbackPayload.warnings).filter((warning) => typeof warning === "string");
|
|
1592
|
+
return stripUndefined({
|
|
1593
|
+
...fallbackPayload,
|
|
1594
|
+
source_requests: [...resolvedProgramIds.sourceRequests, ...fallbackSourceRequests],
|
|
1595
|
+
warnings: uniqueStrings([...resolvedProgramIds.warnings, ...fallbackWarnings])
|
|
1596
|
+
});
|
|
1460
1597
|
}
|
|
1461
1598
|
}));
|
|
1462
1599
|
registerPrompts(server);
|
|
1463
1600
|
registerResources(server, client, config, exportStore);
|
|
1464
1601
|
return server;
|
|
1465
1602
|
}
|
|
1603
|
+
function bbradarGuide(topic) {
|
|
1604
|
+
const primaryEntryPoints = [
|
|
1605
|
+
{
|
|
1606
|
+
tool: "find_program_candidates",
|
|
1607
|
+
use_when: "The user wants good programs to hunt, broad discovery, or does not name one exact program.",
|
|
1608
|
+
minimum_arguments: {},
|
|
1609
|
+
useful_arguments: ["platforms", "target_types", "language_tags", "min_bounty_max", "max_public_report_count", "fresh_launch_days", "has_api_scope"]
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
tool: "run_program_name_action",
|
|
1613
|
+
use_when: "The user names one program and asks for a brief, targets, new targets, scope delta, or target breakdown.",
|
|
1614
|
+
minimum_arguments: { query: "program name or handle", action: "brief" },
|
|
1615
|
+
action_values: ["brief", "targets", "new_targets", "scope_delta", "target_breakdown"]
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
tool: "get_program_eligible_targets_by_name",
|
|
1619
|
+
use_when: "The user names one program and asks for in-scope bounty-eligible targets.",
|
|
1620
|
+
minimum_arguments: { query: "program name or handle" },
|
|
1621
|
+
useful_arguments: ["target_list_mode", "limit", "strict_scope_filter"]
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
tool: "get_bbradar_guide",
|
|
1625
|
+
use_when: "The tool choice is unclear.",
|
|
1626
|
+
minimum_arguments: { topic: "start" }
|
|
1627
|
+
}
|
|
1628
|
+
];
|
|
1629
|
+
const decisionTree = [
|
|
1630
|
+
{
|
|
1631
|
+
user_goal: "find programs to hunt",
|
|
1632
|
+
first_tool: "find_program_candidates",
|
|
1633
|
+
then: "Use get_program_brief on the best program_id if the user wants details."
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
user_goal: "named program summary",
|
|
1637
|
+
first_tool: "run_program_name_action",
|
|
1638
|
+
arguments: { query: "program name", action: "brief" }
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
user_goal: "named program targets",
|
|
1642
|
+
first_tool: "get_program_eligible_targets_by_name",
|
|
1643
|
+
arguments: { query: "program name", target_list_mode: "identifiers" }
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
user_goal: "find which program owns a target",
|
|
1647
|
+
first_tool: "find_programs_by_target",
|
|
1648
|
+
arguments: { target_query: "domain, URL, wildcard, package/repo name, or short target phrase", match_mode: "smart" }
|
|
1649
|
+
},
|
|
1650
|
+
{
|
|
1651
|
+
user_goal: "fresh or recently changed scope",
|
|
1652
|
+
first_tool: "find_recently_updated_programs",
|
|
1653
|
+
then: "Use get_latest_added_targets for global added targets, or check_program_new_targets when the user names one program."
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
user_goal: "compare a short list",
|
|
1657
|
+
first_tool: "compare_programs_compact",
|
|
1658
|
+
arguments: { program_ids: ["Platform:handle", "Platform:handle"] }
|
|
1659
|
+
},
|
|
1660
|
+
{
|
|
1661
|
+
user_goal: "export targets",
|
|
1662
|
+
first_tool: "export_targets",
|
|
1663
|
+
then: "Use the returned bbradar://exports resource_uri for the full local export payload."
|
|
1664
|
+
},
|
|
1665
|
+
{
|
|
1666
|
+
user_goal: "unknown filter values",
|
|
1667
|
+
first_tool: "list_filters",
|
|
1668
|
+
then: "Reuse observed platforms, scope tags, target types, and language tags in discovery tools."
|
|
1669
|
+
}
|
|
1670
|
+
];
|
|
1671
|
+
const advancedTools = {
|
|
1672
|
+
discovery: ["find_stack_matches", "find_language_programs", "find_target_type_programs", "find_reward_programs", "find_paid_programs", "find_web3_contests", "find_low_noise_programs"],
|
|
1673
|
+
freshness: ["find_recently_updated_programs", "find_fresh_hunt_candidates", "get_latest_added_targets", "get_recent_target_activity", "get_program_scope_delta", "check_watchlist_new_targets"],
|
|
1674
|
+
details: ["get_program_brief", "get_program_scope_summary", "get_program_target_breakdown", "find_programs_by_target"],
|
|
1675
|
+
exports: ["export_targets"]
|
|
1676
|
+
};
|
|
1677
|
+
const filters = {
|
|
1678
|
+
platforms: "Only set when the user explicitly names a platform. Do not infer Immunefi, HackerOne, etc.",
|
|
1679
|
+
tags: "Broad combined tag filter across scope, language, and target-type signals.",
|
|
1680
|
+
scope_tags: "Scope category signals such as domain, api, mobile, smart-contract, wildcard.",
|
|
1681
|
+
target_types: "Target-kind signals such as domain, api, url, mobile, smart-contract.",
|
|
1682
|
+
language_tags: "Technology/language signals such as javascript, solidity, web3, php, python.",
|
|
1683
|
+
vdp_mode: "Default exclude. Include VDP/no-bounty data only when asked."
|
|
1684
|
+
};
|
|
1685
|
+
const examples = [
|
|
1686
|
+
{
|
|
1687
|
+
user_says: "best low-noise API programs",
|
|
1688
|
+
call: { tool: "find_program_candidates", arguments: { has_api_scope: true, max_public_report_count: 5, target_types: ["api"] } }
|
|
1689
|
+
},
|
|
1690
|
+
{
|
|
1691
|
+
user_says: "targets for Nado",
|
|
1692
|
+
call: { tool: "get_program_eligible_targets_by_name", arguments: { query: "Nado", target_list_mode: "identifiers" } }
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
user_says: "what changed on Aztec",
|
|
1696
|
+
call: { tool: "run_program_name_action", arguments: { query: "Aztec", action: "scope_delta" } }
|
|
1697
|
+
},
|
|
1698
|
+
{
|
|
1699
|
+
user_says: "fresh solidity programs",
|
|
1700
|
+
call: { tool: "find_fresh_hunt_candidates", arguments: { language_tags: ["solidity"], target_types: ["smart-contract"] } }
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
user_says: "who owns https://api.example.com/v1/users",
|
|
1704
|
+
call: { tool: "find_programs_by_target", arguments: { target_query: "https://api.example.com/v1/users" } }
|
|
1705
|
+
}
|
|
1706
|
+
];
|
|
1707
|
+
return {
|
|
1708
|
+
topic,
|
|
1709
|
+
rule_zero: "BBRadar is passive intelligence only. Never scan or contact targets.",
|
|
1710
|
+
defaults: {
|
|
1711
|
+
output_mode: "compact",
|
|
1712
|
+
target_list_mode: "identifiers",
|
|
1713
|
+
target_match_mode: "smart",
|
|
1714
|
+
include_out_of_scope: false,
|
|
1715
|
+
include_ineligible: false,
|
|
1716
|
+
require_bounty: true
|
|
1717
|
+
},
|
|
1718
|
+
primary_entry_points: topic === "start" ? primaryEntryPoints : primaryEntryPoints.filter((entry) => entry.tool !== "get_bbradar_guide"),
|
|
1719
|
+
decision_tree: decisionTree.filter((entry) => topic === "start" || guideEntryMatchesTopic(topic, entry)),
|
|
1720
|
+
filters,
|
|
1721
|
+
advanced_tools: advancedTools,
|
|
1722
|
+
examples
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
function guideEntryMatchesTopic(topic, entry) {
|
|
1726
|
+
const goal = typeof entry.user_goal === "string" ? entry.user_goal : "";
|
|
1727
|
+
const firstTool = typeof entry.first_tool === "string" ? entry.first_tool : "";
|
|
1728
|
+
if (topic === "program") {
|
|
1729
|
+
return goal.includes("named program") || firstTool === "get_program_brief";
|
|
1730
|
+
}
|
|
1731
|
+
if (topic === "discovery") {
|
|
1732
|
+
return goal.includes("find programs");
|
|
1733
|
+
}
|
|
1734
|
+
if (topic === "freshness") {
|
|
1735
|
+
return goal.includes("fresh") || goal.includes("changed");
|
|
1736
|
+
}
|
|
1737
|
+
if (topic === "targets") {
|
|
1738
|
+
return goal.includes("target") || firstTool === "find_programs_by_target";
|
|
1739
|
+
}
|
|
1740
|
+
if (topic === "compare") {
|
|
1741
|
+
return goal.includes("compare");
|
|
1742
|
+
}
|
|
1743
|
+
if (topic === "export") {
|
|
1744
|
+
return goal.includes("export");
|
|
1745
|
+
}
|
|
1746
|
+
if (topic === "filters") {
|
|
1747
|
+
return goal.includes("filter");
|
|
1748
|
+
}
|
|
1749
|
+
return true;
|
|
1750
|
+
}
|
|
1466
1751
|
function registerResources(server, client, config, exportStore) {
|
|
1467
1752
|
server.registerResource("target-export", new ResourceTemplate("bbradar://exports{?export_id}", { list: undefined }), {
|
|
1468
1753
|
title: "BBRadar Target Export",
|
|
@@ -1568,6 +1853,20 @@ async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
|
1568
1853
|
fallback: cachedFallback
|
|
1569
1854
|
};
|
|
1570
1855
|
}
|
|
1856
|
+
if (!isStrictProgramId(requestedProgramId)) {
|
|
1857
|
+
const fallback = await resolveProgramNameIdentifier(client, config, requestedProgramId, "compact");
|
|
1858
|
+
if (fallback) {
|
|
1859
|
+
rememberProgramFallback(config, fallback);
|
|
1860
|
+
return {
|
|
1861
|
+
api: await client.getProgram(fallback.resolvedProgramId),
|
|
1862
|
+
programId: fallback.resolvedProgramId,
|
|
1863
|
+
sourceRequests: fallback.sourceRequests,
|
|
1864
|
+
warnings: fallback.warnings,
|
|
1865
|
+
fallback
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
throw unresolvedProgramNameError(requestedProgramId);
|
|
1869
|
+
}
|
|
1571
1870
|
try {
|
|
1572
1871
|
return {
|
|
1573
1872
|
api: await client.getProgram(requestedProgramId),
|
|
@@ -1619,6 +1918,39 @@ async function runProgramIdTool(client, config, requestedProgramId, callback, in
|
|
|
1619
1918
|
return withProgramIndexFallbackMetadata(payload, indexFallback);
|
|
1620
1919
|
}
|
|
1621
1920
|
}
|
|
1921
|
+
if (!isStrictProgramId(requestedProgramId)) {
|
|
1922
|
+
const fallback = await resolveProgramNameIdentifier(client, config, requestedProgramId, indexFallbackCallback ? "full" : "compact");
|
|
1923
|
+
if (fallback) {
|
|
1924
|
+
try {
|
|
1925
|
+
rememberProgramFallback(config, fallback);
|
|
1926
|
+
const payload = await callback(fallback.resolvedProgramId);
|
|
1927
|
+
return withProgramIdFallbackMetadata(payload, fallback);
|
|
1928
|
+
}
|
|
1929
|
+
catch (fallbackError) {
|
|
1930
|
+
if (!isProgramNotFoundError(fallbackError) || !indexFallbackCallback) {
|
|
1931
|
+
throw fallbackError;
|
|
1932
|
+
}
|
|
1933
|
+
const indexFallback = createProgramIndexFallbackFromStaleResolution(fallback, requestedProgramId, fallbackError) ??
|
|
1934
|
+
(await resolveIndexedProgramId(client, config, fallback.resolvedProgramId, fallbackError));
|
|
1935
|
+
if (!indexFallback) {
|
|
1936
|
+
throw fallbackError;
|
|
1937
|
+
}
|
|
1938
|
+
const combinedFallback = indexFallback.requestedProgramId === requestedProgramId
|
|
1939
|
+
? indexFallback
|
|
1940
|
+
: {
|
|
1941
|
+
...indexFallback,
|
|
1942
|
+
requestedProgramId,
|
|
1943
|
+
reason: fallback.reason,
|
|
1944
|
+
sourceRequests: [...fallback.sourceRequests, ...indexFallback.sourceRequests],
|
|
1945
|
+
warnings: uniqueStrings([...fallback.warnings, ...indexFallback.warnings])
|
|
1946
|
+
};
|
|
1947
|
+
rememberProgramFallback(config, combinedFallback);
|
|
1948
|
+
const payload = await indexFallbackCallback(combinedFallback);
|
|
1949
|
+
return withProgramIndexFallbackMetadata(payload, combinedFallback);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
throw unresolvedProgramNameError(requestedProgramId);
|
|
1953
|
+
}
|
|
1622
1954
|
try {
|
|
1623
1955
|
return await callback(requestedProgramId);
|
|
1624
1956
|
}
|
|
@@ -1776,6 +2108,7 @@ async function resolveIndexedProgramId(client, config, requestedProgramId, stale
|
|
|
1776
2108
|
requestedProgramId,
|
|
1777
2109
|
resolvedProgramId: match.resolvedProgramId,
|
|
1778
2110
|
query,
|
|
2111
|
+
reason: "stale_program_id",
|
|
1779
2112
|
staleRequestId: staleApiError?.requestId,
|
|
1780
2113
|
staleUpstreamRequestId: staleApiError?.upstreamRequestId,
|
|
1781
2114
|
sourceRequests,
|
|
@@ -1784,6 +2117,37 @@ async function resolveIndexedProgramId(client, config, requestedProgramId, stale
|
|
|
1784
2117
|
program: match.program
|
|
1785
2118
|
};
|
|
1786
2119
|
}
|
|
2120
|
+
async function resolveProgramNameIdentifier(client, config, requestedProgramName, outputMode) {
|
|
2121
|
+
const query = programSearchText(requestedProgramName);
|
|
2122
|
+
if (query.length < 2) {
|
|
2123
|
+
return undefined;
|
|
2124
|
+
}
|
|
2125
|
+
const resolution = await resolveProgram(client, config, {
|
|
2126
|
+
query,
|
|
2127
|
+
platforms: [],
|
|
2128
|
+
tags: [],
|
|
2129
|
+
opportunity_levels: [],
|
|
2130
|
+
max_pages: STALE_PROGRAM_ID_RESOLVE_PAGES,
|
|
2131
|
+
max_results: 5,
|
|
2132
|
+
output_mode: outputMode,
|
|
2133
|
+
upstream_request_budget: STALE_PROGRAM_ID_RESOLVE_BUDGET
|
|
2134
|
+
});
|
|
2135
|
+
const match = selectProgramIdFallbackMatch(requestedProgramName, resolution);
|
|
2136
|
+
if (!match) {
|
|
2137
|
+
return undefined;
|
|
2138
|
+
}
|
|
2139
|
+
const sourceRequests = readArray(resolution.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2140
|
+
const warnings = readArray(resolution.warnings).filter((warning) => typeof warning === "string");
|
|
2141
|
+
return {
|
|
2142
|
+
requestedProgramId: requestedProgramName,
|
|
2143
|
+
resolvedProgramId: match.resolvedProgramId,
|
|
2144
|
+
query,
|
|
2145
|
+
reason: "program_name",
|
|
2146
|
+
sourceRequests,
|
|
2147
|
+
warnings: uniqueStrings(warnings),
|
|
2148
|
+
resolution
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
1787
2151
|
async function resolveStaleProgramId(client, config, requestedProgramId, staleError) {
|
|
1788
2152
|
const query = programSearchText(requestedProgramId);
|
|
1789
2153
|
if (query.length < 2) {
|
|
@@ -1811,6 +2175,7 @@ async function resolveStaleProgramId(client, config, requestedProgramId, staleEr
|
|
|
1811
2175
|
requestedProgramId,
|
|
1812
2176
|
resolvedProgramId: match.resolvedProgramId,
|
|
1813
2177
|
query,
|
|
2178
|
+
reason: "stale_program_id",
|
|
1814
2179
|
staleRequestId: staleApiError?.requestId,
|
|
1815
2180
|
staleUpstreamRequestId: staleApiError?.upstreamRequestId,
|
|
1816
2181
|
sourceRequests,
|
|
@@ -1818,6 +2183,32 @@ async function resolveStaleProgramId(client, config, requestedProgramId, staleEr
|
|
|
1818
2183
|
resolution
|
|
1819
2184
|
};
|
|
1820
2185
|
}
|
|
2186
|
+
async function resolveProgramIdentifiers(client, config, identifiers) {
|
|
2187
|
+
const entries = [];
|
|
2188
|
+
const sourceRequests = [];
|
|
2189
|
+
const warnings = [];
|
|
2190
|
+
for (const identifier of identifiers) {
|
|
2191
|
+
if (isStrictProgramId(identifier)) {
|
|
2192
|
+
entries.push({ requestedProgramId: identifier, resolvedProgramId: identifier, resolved: false });
|
|
2193
|
+
continue;
|
|
2194
|
+
}
|
|
2195
|
+
const fallback = await resolveProgramNameIdentifier(client, config, identifier, "compact");
|
|
2196
|
+
if (!fallback) {
|
|
2197
|
+
entries.push({ requestedProgramId: identifier, resolvedProgramId: identifier, resolved: false });
|
|
2198
|
+
warnings.push(`Could not resolve program name ${identifier}; used it as provided.`);
|
|
2199
|
+
continue;
|
|
2200
|
+
}
|
|
2201
|
+
entries.push({ requestedProgramId: identifier, resolvedProgramId: fallback.resolvedProgramId, resolved: true });
|
|
2202
|
+
sourceRequests.push(...fallback.sourceRequests);
|
|
2203
|
+
warnings.push(...fallback.warnings);
|
|
2204
|
+
rememberProgramFallback(config, fallback);
|
|
2205
|
+
}
|
|
2206
|
+
return {
|
|
2207
|
+
entries,
|
|
2208
|
+
sourceRequests,
|
|
2209
|
+
warnings: uniqueStrings(warnings)
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
1821
2212
|
function selectProgramIdFallbackMatch(requestedProgramId, resolution) {
|
|
1822
2213
|
const requestedHandle = normalizeTag(programHandleText(requestedProgramId));
|
|
1823
2214
|
const requestedSearchText = normalizeTag(programSearchText(requestedProgramId));
|
|
@@ -1829,6 +2220,7 @@ function selectProgramIdFallbackMatch(requestedProgramId, resolution) {
|
|
|
1829
2220
|
const program = readObject(entry.program);
|
|
1830
2221
|
const resolvedProgramId = stringField(entry, "program_id") ?? stringField(program, "id");
|
|
1831
2222
|
const candidateHandle = normalizeTag(stringField(program, "handle") ?? "");
|
|
2223
|
+
const candidateName = normalizeTag(stringField(program, "name") ?? "");
|
|
1832
2224
|
const candidateSearchText = resolvedProgramId ? normalizeTag(programSearchText(resolvedProgramId)) : "";
|
|
1833
2225
|
const candidatePlatform = normalizeTag(stringField(program, "platform") ?? (resolvedProgramId ? programPlatformText(resolvedProgramId) : ""));
|
|
1834
2226
|
const score = readNumber(entry.score) ?? 0;
|
|
@@ -1842,16 +2234,24 @@ function selectProgramIdFallbackMatch(requestedProgramId, resolution) {
|
|
|
1842
2234
|
candidateHandle.startsWith(`${requestedHandle}-`) ||
|
|
1843
2235
|
candidateSearchText.startsWith(`${requestedSearchText}_`) ||
|
|
1844
2236
|
candidateSearchText.startsWith(`${requestedSearchText}-`));
|
|
2237
|
+
const looseNameMatch = requestedPlatform.length === 0 &&
|
|
2238
|
+
score >= 85 &&
|
|
2239
|
+
(candidateHandle === requestedHandle ||
|
|
2240
|
+
candidateSearchText === requestedSearchText ||
|
|
2241
|
+
candidateName === requestedSearchText ||
|
|
2242
|
+
candidateName.includes(requestedSearchText) ||
|
|
2243
|
+
(candidateHandle.length > 0 && requestedSearchText.includes(candidateHandle)));
|
|
1845
2244
|
return {
|
|
1846
2245
|
resolvedProgramId,
|
|
1847
2246
|
score,
|
|
1848
2247
|
exactHandleMatch,
|
|
1849
|
-
partialHandleMatch
|
|
2248
|
+
partialHandleMatch,
|
|
2249
|
+
looseNameMatch
|
|
1850
2250
|
};
|
|
1851
2251
|
})
|
|
1852
2252
|
.filter((entry) => entry.resolvedProgramId !== undefined &&
|
|
1853
2253
|
normalizeTag(entry.resolvedProgramId) !== normalizeTag(requestedProgramId) &&
|
|
1854
|
-
(entry.exactHandleMatch || entry.partialHandleMatch) &&
|
|
2254
|
+
(entry.exactHandleMatch || entry.partialHandleMatch || entry.looseNameMatch) &&
|
|
1855
2255
|
entry.score >= 80)
|
|
1856
2256
|
.sort((left, right) => right.score - left.score || left.resolvedProgramId.localeCompare(right.resolvedProgramId));
|
|
1857
2257
|
if (candidates.length === 0) {
|
|
@@ -1876,6 +2276,7 @@ function selectProgramIndexFallbackMatch(requestedProgramId, resolution) {
|
|
|
1876
2276
|
const program = readObject(entry.program);
|
|
1877
2277
|
const resolvedProgramId = stringField(entry, "program_id") ?? stringField(program, "id");
|
|
1878
2278
|
const candidateHandle = normalizeTag(stringField(program, "handle") ?? (resolvedProgramId ? programHandleText(resolvedProgramId) : ""));
|
|
2279
|
+
const candidateName = normalizeTag(stringField(program, "name") ?? "");
|
|
1879
2280
|
const candidatePlatform = normalizeTag(stringField(program, "platform") ?? (resolvedProgramId ? programPlatformText(resolvedProgramId) : ""));
|
|
1880
2281
|
const score = readNumber(entry.score) ?? 0;
|
|
1881
2282
|
const idMatch = resolvedProgramId !== undefined && normalizeTag(resolvedProgramId) === requestedId;
|
|
@@ -1886,11 +2287,17 @@ function selectProgramIndexFallbackMatch(requestedProgramId, resolution) {
|
|
|
1886
2287
|
requestedPlatform.length > 0 &&
|
|
1887
2288
|
candidatePlatform === requestedPlatform &&
|
|
1888
2289
|
(candidateHandle.startsWith(`${requestedHandle}_`) || candidateHandle.startsWith(`${requestedHandle}-`));
|
|
2290
|
+
const looseNameMatch = requestedPlatform.length === 0 &&
|
|
2291
|
+
score >= 85 &&
|
|
2292
|
+
(candidateHandle === requestedHandle ||
|
|
2293
|
+
candidateName === requestedHandle ||
|
|
2294
|
+
candidateName.includes(requestedHandle) ||
|
|
2295
|
+
(candidateHandle.length > 0 && requestedHandle.includes(candidateHandle)));
|
|
1889
2296
|
return {
|
|
1890
2297
|
resolvedProgramId,
|
|
1891
2298
|
program,
|
|
1892
2299
|
score,
|
|
1893
|
-
exactMatch: idMatch || platformHandleMatch || partialPlatformHandleMatch
|
|
2300
|
+
exactMatch: idMatch || platformHandleMatch || partialPlatformHandleMatch || looseNameMatch
|
|
1894
2301
|
};
|
|
1895
2302
|
})
|
|
1896
2303
|
.filter((entry) => entry.resolvedProgramId !== undefined && entry.program !== undefined && entry.exactMatch && entry.score >= 80)
|
|
@@ -1914,8 +2321,8 @@ function withProgramIdFallbackMetadata(payload, fallback) {
|
|
|
1914
2321
|
requested_program_id: fallback.requestedProgramId,
|
|
1915
2322
|
resolved_program_id: fallback.resolvedProgramId,
|
|
1916
2323
|
query: fallback.query,
|
|
1917
|
-
reason: "stale_program_id",
|
|
1918
|
-
stale_status: 404,
|
|
2324
|
+
reason: fallback.reason ?? "stale_program_id",
|
|
2325
|
+
stale_status: fallback.staleRequestId || fallback.staleUpstreamRequestId ? 404 : undefined,
|
|
1919
2326
|
stale_request_id: fallback.staleRequestId,
|
|
1920
2327
|
stale_upstream_request_id: fallback.staleUpstreamRequestId,
|
|
1921
2328
|
resolution_request_id: stringField(fallback.resolution, "request_id")
|
|
@@ -1951,6 +2358,7 @@ async function resolveProgram(client, config, input) {
|
|
|
1951
2358
|
const maxPages = clampLimit("max_pages", input.max_pages, MAX_FIND_PROGRAM_PAGES, warnings);
|
|
1952
2359
|
const searchQueries = programResolutionSearchQueries(input.query);
|
|
1953
2360
|
const collected = await collectProgramsForResolution(client, {
|
|
2361
|
+
query: input.query,
|
|
1954
2362
|
searchQueries,
|
|
1955
2363
|
isHighConfidenceMatch: (programs) => hasHighConfidenceProgramResolutionMatch(programs, config.webBaseUrl, input.query),
|
|
1956
2364
|
scorePrograms: (programs) => bestProgramResolutionScore(programs, config.webBaseUrl, input.query),
|
|
@@ -1961,7 +2369,9 @@ async function resolveProgram(client, config, input) {
|
|
|
1961
2369
|
budget,
|
|
1962
2370
|
warnings
|
|
1963
2371
|
});
|
|
1964
|
-
const
|
|
2372
|
+
const sourceRequests = [...collected.sourceRequests];
|
|
2373
|
+
let targetLookup;
|
|
2374
|
+
let matches = collected.programs
|
|
1965
2375
|
.map((program) => {
|
|
1966
2376
|
const candidate = toProgramCandidate(program, config.webBaseUrl);
|
|
1967
2377
|
const match = programResolutionMatch(candidate, input.query);
|
|
@@ -1970,14 +2380,58 @@ async function resolveProgram(client, config, input) {
|
|
|
1970
2380
|
.filter((entry) => entry.match.score > 0)
|
|
1971
2381
|
.sort((left, right) => right.match.score - left.match.score || compareProgramCandidates(left.candidate, right.candidate, "best_hunt_value"))
|
|
1972
2382
|
.slice(0, input.max_results);
|
|
2383
|
+
if (matches.length === 0 && queryLooksLikeTargetIdentifier(input.query)) {
|
|
2384
|
+
targetLookup = await findProgramsByTarget(client, config, {
|
|
2385
|
+
target_query: input.query,
|
|
2386
|
+
match_mode: "smart",
|
|
2387
|
+
platforms: input.platforms,
|
|
2388
|
+
tags: input.tags,
|
|
2389
|
+
use_api_search: true,
|
|
2390
|
+
max_pages: 1,
|
|
2391
|
+
max_results: input.max_results,
|
|
2392
|
+
include_out_of_scope: false,
|
|
2393
|
+
include_ineligible: false,
|
|
2394
|
+
max_targets_per_program: 3,
|
|
2395
|
+
target_list_mode: "identifiers",
|
|
2396
|
+
upstream_request_budget: Math.max(1, budget.remaining)
|
|
2397
|
+
});
|
|
2398
|
+
sourceRequests.push(...readArray(targetLookup.source_requests).filter((request) => readObject(request) !== undefined));
|
|
2399
|
+
for (const warning of readArray(targetLookup.warnings)) {
|
|
2400
|
+
if (typeof warning === "string") {
|
|
2401
|
+
addUniqueWarning(warnings, warning);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
const targetBudget = readObject(targetLookup.upstream_budget);
|
|
2405
|
+
const targetBudgetUsed = numberField(targetBudget, "used") ?? 0;
|
|
2406
|
+
budget.remaining = Math.max(0, budget.remaining - targetBudgetUsed);
|
|
2407
|
+
matches = readArray(targetLookup.matches)
|
|
2408
|
+
.map((entry) => {
|
|
2409
|
+
const matchEntry = readObject(entry);
|
|
2410
|
+
const program = readObject(matchEntry?.program);
|
|
2411
|
+
if (!program) {
|
|
2412
|
+
return undefined;
|
|
2413
|
+
}
|
|
2414
|
+
return {
|
|
2415
|
+
candidate: toProgramCandidate(program, config.webBaseUrl),
|
|
2416
|
+
match: {
|
|
2417
|
+
score: 75,
|
|
2418
|
+
fields: ["targets"],
|
|
2419
|
+
reasons: ["target search matched query"]
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
})
|
|
2423
|
+
.filter((entry) => entry !== undefined)
|
|
2424
|
+
.slice(0, input.max_results);
|
|
2425
|
+
}
|
|
1973
2426
|
const programs = matches.map((entry) => formatProgram(entry.candidate.program, input.output_mode));
|
|
1974
2427
|
const payload = stripUndefined({
|
|
1975
2428
|
request_id: randomUUID(),
|
|
1976
|
-
source_requests:
|
|
2429
|
+
source_requests: sourceRequests,
|
|
1977
2430
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
1978
2431
|
query: {
|
|
1979
2432
|
text: input.query,
|
|
1980
2433
|
search_queries: searchQueries,
|
|
2434
|
+
target_search_queries: readObject(targetLookup?.filters)?.target_search_queries,
|
|
1981
2435
|
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
1982
2436
|
tags: input.tags.length > 0 ? input.tags : undefined
|
|
1983
2437
|
},
|
|
@@ -2002,7 +2456,8 @@ async function resolveProgram(client, config, input) {
|
|
|
2002
2456
|
meta: {
|
|
2003
2457
|
returned: programs.length,
|
|
2004
2458
|
programs_scanned: collected.programsScanned,
|
|
2005
|
-
upstream_requests_scanned: collected.requestsScanned
|
|
2459
|
+
upstream_requests_scanned: collected.requestsScanned,
|
|
2460
|
+
target_lookup_returned: numberField(readObject(targetLookup?.meta), "returned")
|
|
2006
2461
|
}
|
|
2007
2462
|
});
|
|
2008
2463
|
rememberResolveProgram(config, input, payload);
|
|
@@ -2695,16 +3150,43 @@ function findRecentByType(client, config, input) {
|
|
|
2695
3150
|
}
|
|
2696
3151
|
async function findProgramsByTarget(client, config, input) {
|
|
2697
3152
|
const warnings = [];
|
|
3153
|
+
const budget = {
|
|
3154
|
+
initial: input.upstream_request_budget,
|
|
3155
|
+
remaining: input.upstream_request_budget
|
|
3156
|
+
};
|
|
3157
|
+
const sourceRequests = [];
|
|
3158
|
+
const matches = [];
|
|
3159
|
+
const directHandles = await collectDirectHandlePrograms(client, {
|
|
3160
|
+
query: input.target_query,
|
|
3161
|
+
platforms: input.platforms,
|
|
3162
|
+
budget,
|
|
3163
|
+
warnings,
|
|
3164
|
+
source: "target_query_program_id_guess",
|
|
3165
|
+
maxProgramIds: 1,
|
|
3166
|
+
stopOnFirstMatch: true
|
|
3167
|
+
});
|
|
3168
|
+
sourceRequests.push(...directHandles.sourceRequests);
|
|
3169
|
+
await appendProgramTargetMatches(client, input, budget, warnings, sourceRequests, matches, directHandles.programs.map((program) => toProgramCandidate(program, config.webBaseUrl)));
|
|
3170
|
+
if (matches.length > 0 && input.match_mode === "smart") {
|
|
3171
|
+
return programsByTargetResult(input, warnings, sourceRequests, matches, budget, {
|
|
3172
|
+
source: "direct_program_handle_targets",
|
|
3173
|
+
rankingScope: {
|
|
3174
|
+
mode: "direct_program_handle",
|
|
3175
|
+
possibly_incomplete: false
|
|
3176
|
+
},
|
|
3177
|
+
meta: {
|
|
3178
|
+
returned: matches.length,
|
|
3179
|
+
programs_scanned: directHandles.programsScanned,
|
|
3180
|
+
target_lists_checked: sourceRequests.filter((request) => request.source === "program_targets").length
|
|
3181
|
+
}
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
2698
3184
|
if (input.use_api_search) {
|
|
2699
3185
|
const directResult = await searchProgramsByTargetApi(client, config, input, warnings);
|
|
2700
3186
|
if (directResult) {
|
|
2701
3187
|
return directResult;
|
|
2702
3188
|
}
|
|
2703
3189
|
}
|
|
2704
|
-
const budget = {
|
|
2705
|
-
initial: input.upstream_request_budget,
|
|
2706
|
-
remaining: input.upstream_request_budget
|
|
2707
|
-
};
|
|
2708
3190
|
const maxPages = clampLimit("max_pages", input.max_pages, MAX_FIND_PROGRAM_PAGES, warnings);
|
|
2709
3191
|
const collected = await collectPrograms(client, {
|
|
2710
3192
|
platforms: input.platforms,
|
|
@@ -2715,11 +3197,27 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
2715
3197
|
budget,
|
|
2716
3198
|
warnings
|
|
2717
3199
|
});
|
|
2718
|
-
|
|
2719
|
-
const matches = [];
|
|
3200
|
+
sourceRequests.push(...collected.sourceRequests);
|
|
2720
3201
|
const candidates = collected.programs
|
|
2721
3202
|
.map((program) => toProgramCandidate(program, config.webBaseUrl))
|
|
2722
3203
|
.sort((left, right) => compareProgramCandidates(left, right, "best_hunt_value"));
|
|
3204
|
+
await appendProgramTargetMatches(client, input, budget, warnings, sourceRequests, matches, candidates);
|
|
3205
|
+
return programsByTargetResult(input, warnings, sourceRequests, matches, budget, {
|
|
3206
|
+
source: "sampled_program_targets",
|
|
3207
|
+
rankingScope: {
|
|
3208
|
+
mode: "sampled",
|
|
3209
|
+
max_pages_scanned_per_source: maxPages,
|
|
3210
|
+
upstream_total_pages: collected.totalPagesBySource,
|
|
3211
|
+
possibly_incomplete: isRankingPossiblyIncomplete(collected.totalPagesBySource, maxPages)
|
|
3212
|
+
},
|
|
3213
|
+
meta: {
|
|
3214
|
+
returned: matches.length,
|
|
3215
|
+
programs_scanned: directHandles.programsScanned + collected.programsScanned,
|
|
3216
|
+
target_lists_checked: sourceRequests.filter((request) => request.source === "program_targets").length
|
|
3217
|
+
}
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
async function appendProgramTargetMatches(client, input, budget, warnings, sourceRequests, matches, candidates) {
|
|
2723
3221
|
for (const candidate of candidates) {
|
|
2724
3222
|
if (matches.length >= input.max_results) {
|
|
2725
3223
|
break;
|
|
@@ -2729,7 +3227,9 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
2729
3227
|
continue;
|
|
2730
3228
|
}
|
|
2731
3229
|
if (!tryConsumeBudget(budget)) {
|
|
2732
|
-
|
|
3230
|
+
if (matches.length === 0) {
|
|
3231
|
+
addUniqueWarning(warnings, "Upstream request budget was exhausted before all candidate target lists could be checked.");
|
|
3232
|
+
}
|
|
2733
3233
|
break;
|
|
2734
3234
|
}
|
|
2735
3235
|
const targetsApi = await client.getProgramTargets(programId);
|
|
@@ -2761,6 +3261,8 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
2761
3261
|
total_targets_checked: targets.length
|
|
2762
3262
|
}));
|
|
2763
3263
|
}
|
|
3264
|
+
}
|
|
3265
|
+
function programsByTargetResult(input, warnings, sourceRequests, matches, budget, options) {
|
|
2764
3266
|
return stripUndefined({
|
|
2765
3267
|
request_id: randomUUID(),
|
|
2766
3268
|
source_requests: sourceRequests,
|
|
@@ -2770,7 +3272,7 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
2770
3272
|
match_mode: input.match_mode,
|
|
2771
3273
|
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
2772
3274
|
tags: input.tags.length > 0 ? input.tags : undefined,
|
|
2773
|
-
source:
|
|
3275
|
+
source: options.source,
|
|
2774
3276
|
include_out_of_scope: input.include_out_of_scope,
|
|
2775
3277
|
include_ineligible: input.include_ineligible,
|
|
2776
3278
|
target_list_mode: input.target_list_mode
|
|
@@ -2781,63 +3283,79 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
2781
3283
|
used: budget.initial - budget.remaining,
|
|
2782
3284
|
remaining: budget.remaining
|
|
2783
3285
|
},
|
|
2784
|
-
ranking_scope:
|
|
2785
|
-
|
|
2786
|
-
max_pages_scanned_per_source: maxPages,
|
|
2787
|
-
upstream_total_pages: collected.totalPagesBySource,
|
|
2788
|
-
possibly_incomplete: isRankingPossiblyIncomplete(collected.totalPagesBySource, maxPages)
|
|
2789
|
-
},
|
|
2790
|
-
meta: {
|
|
2791
|
-
returned: matches.length,
|
|
2792
|
-
programs_scanned: collected.programsScanned,
|
|
2793
|
-
target_lists_checked: sourceRequests.filter((request) => request.source === "program_targets").length
|
|
2794
|
-
}
|
|
3286
|
+
ranking_scope: options.rankingScope,
|
|
3287
|
+
meta: options.meta
|
|
2795
3288
|
});
|
|
2796
3289
|
}
|
|
2797
3290
|
async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
2798
|
-
|
|
2799
|
-
try {
|
|
2800
|
-
api = await client.searchTargets({
|
|
2801
|
-
q: input.target_query,
|
|
2802
|
-
match_mode: input.match_mode,
|
|
2803
|
-
platforms: input.platforms,
|
|
2804
|
-
tags: input.tags,
|
|
2805
|
-
include_out_of_scope: input.include_out_of_scope,
|
|
2806
|
-
include_ineligible: input.include_ineligible,
|
|
2807
|
-
page: 1,
|
|
2808
|
-
page_size: Math.max(input.max_results * input.max_targets_per_program, input.max_results)
|
|
2809
|
-
});
|
|
2810
|
-
}
|
|
2811
|
-
catch (error) {
|
|
2812
|
-
if (error instanceof BBRadarApiError && (error.status === 404 || error.status === 405)) {
|
|
2813
|
-
warnings.push("Direct target search API is unavailable; fell back to sampled program target lists.");
|
|
2814
|
-
return undefined;
|
|
2815
|
-
}
|
|
2816
|
-
throw error;
|
|
2817
|
-
}
|
|
2818
|
-
const data = readObject(api.data);
|
|
2819
|
-
const rawMatches = readTargetSearchRows(data);
|
|
3291
|
+
const searchQueries = targetApiSearchQueries(input.target_query);
|
|
2820
3292
|
const grouped = new Map();
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
3293
|
+
const sourceRequests = [];
|
|
3294
|
+
let rawMatchesScanned = 0;
|
|
3295
|
+
let lastMeta;
|
|
3296
|
+
for (const searchQuery of searchQueries) {
|
|
3297
|
+
let api;
|
|
3298
|
+
try {
|
|
3299
|
+
api = await client.searchTargets({
|
|
3300
|
+
q: searchQuery,
|
|
3301
|
+
match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
3302
|
+
platforms: input.platforms,
|
|
3303
|
+
tags: input.tags,
|
|
3304
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
3305
|
+
include_ineligible: input.include_ineligible,
|
|
3306
|
+
page: 1,
|
|
3307
|
+
page_size: Math.max(input.max_results * input.max_targets_per_program, input.max_results)
|
|
3308
|
+
});
|
|
2827
3309
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3310
|
+
catch (error) {
|
|
3311
|
+
if (error instanceof BBRadarApiError && (error.status === 404 || error.status === 405)) {
|
|
3312
|
+
warnings.push("Direct target search API is unavailable; fell back to sampled program target lists.");
|
|
3313
|
+
return undefined;
|
|
3314
|
+
}
|
|
3315
|
+
throw error;
|
|
2834
3316
|
}
|
|
2835
|
-
|
|
2836
|
-
|
|
3317
|
+
const data = readObject(api.data);
|
|
3318
|
+
const rawMatches = readTargetSearchRows(data);
|
|
3319
|
+
rawMatchesScanned += rawMatches.length;
|
|
3320
|
+
lastMeta = readObject(sanitizeJson(data?.meta)) ?? lastMeta;
|
|
3321
|
+
sourceRequests.push({
|
|
3322
|
+
source: "target_search",
|
|
3323
|
+
query: searchQuery,
|
|
3324
|
+
api_match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
3325
|
+
request_id: api.requestId,
|
|
3326
|
+
upstream_request_id: api.upstreamRequestId,
|
|
3327
|
+
...apiSourceMetadata(api)
|
|
3328
|
+
});
|
|
3329
|
+
for (const row of rawMatches) {
|
|
3330
|
+
const rowProgram = sanitizeProgram(readObject(row.program) ?? row, config.webBaseUrl);
|
|
3331
|
+
const rowProgramId = stringField(rowProgram, "id") ?? stringField(row, "program_id");
|
|
3332
|
+
const targets = targetSearchRowTargets(row);
|
|
3333
|
+
for (const targetSource of targets) {
|
|
3334
|
+
const target = sanitizeTarget(targetSource);
|
|
3335
|
+
const nestedProgram = sanitizeProgram(readObject(targetSource.program), config.webBaseUrl);
|
|
3336
|
+
const program = stringField(nestedProgram, "id") ? nestedProgram : rowProgram;
|
|
3337
|
+
const programId = stringField(program, "id") ?? rowProgramId ?? stringField(targetSource, "program_id");
|
|
3338
|
+
if (!programId || !targetIdentifier(target)) {
|
|
3339
|
+
continue;
|
|
3340
|
+
}
|
|
3341
|
+
if (!targetHasAllowedScope(target, {
|
|
3342
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
3343
|
+
include_ineligible: input.include_ineligible,
|
|
3344
|
+
strict_scope_filter: false
|
|
3345
|
+
})) {
|
|
3346
|
+
continue;
|
|
3347
|
+
}
|
|
3348
|
+
if (!targetMatchesQuery(target, input.target_query, input.match_mode)) {
|
|
3349
|
+
continue;
|
|
3350
|
+
}
|
|
3351
|
+
const existing = grouped.get(programId) ?? { program: addProgramResourceLinks({ ...program, id: programId }), targets: [] };
|
|
3352
|
+
existing.targets.push(target);
|
|
3353
|
+
grouped.set(programId, existing);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
if (grouped.size >= input.max_results) {
|
|
3357
|
+
break;
|
|
2837
3358
|
}
|
|
2838
|
-
const existing = grouped.get(programId) ?? { program: { ...program, id: programId }, targets: [] };
|
|
2839
|
-
existing.targets.push(target);
|
|
2840
|
-
grouped.set(programId, existing);
|
|
2841
3359
|
}
|
|
2842
3360
|
const matches = [...grouped.values()].slice(0, input.max_results).map((entry) => stripUndefined({
|
|
2843
3361
|
program: formatProgram(entry.program, "compact"),
|
|
@@ -2845,20 +3363,17 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
2845
3363
|
matching_targets: formatTargetList(entry.targets.slice(0, input.max_targets_per_program), input.target_list_mode),
|
|
2846
3364
|
matching_targets_has_more: entry.targets.length > input.max_targets_per_program
|
|
2847
3365
|
}));
|
|
2848
|
-
|
|
3366
|
+
if (matches.length === 0 && input.match_mode === "smart") {
|
|
3367
|
+
warnings.push("Direct target search returned no smart matches; fell back to sampled program target lists.");
|
|
3368
|
+
return undefined;
|
|
3369
|
+
}
|
|
2849
3370
|
return stripUndefined({
|
|
2850
3371
|
request_id: randomUUID(),
|
|
2851
|
-
source_requests:
|
|
2852
|
-
{
|
|
2853
|
-
source: "target_search",
|
|
2854
|
-
request_id: api.requestId,
|
|
2855
|
-
upstream_request_id: api.upstreamRequestId,
|
|
2856
|
-
...apiSourceMetadata(api)
|
|
2857
|
-
}
|
|
2858
|
-
],
|
|
3372
|
+
source_requests: sourceRequests,
|
|
2859
3373
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
2860
3374
|
filters: {
|
|
2861
3375
|
target_query: input.target_query,
|
|
3376
|
+
target_search_queries: searchQueries.length > 1 ? searchQueries : undefined,
|
|
2862
3377
|
match_mode: input.match_mode,
|
|
2863
3378
|
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
2864
3379
|
tags: input.tags.length > 0 ? input.tags : undefined,
|
|
@@ -2870,19 +3385,52 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
2870
3385
|
matches,
|
|
2871
3386
|
programs: matches.map((match) => readObject(match.program)).filter((program) => program !== undefined),
|
|
2872
3387
|
meta: stripUndefined({
|
|
2873
|
-
...(
|
|
3388
|
+
...(lastMeta ?? {}),
|
|
2874
3389
|
returned: matches.length,
|
|
2875
|
-
target_search_rows_scanned:
|
|
3390
|
+
target_search_rows_scanned: rawMatchesScanned
|
|
2876
3391
|
})
|
|
2877
3392
|
});
|
|
2878
3393
|
}
|
|
2879
3394
|
function readTargetSearchRows(data) {
|
|
2880
|
-
return [data?.results, data?.matches, data?.targets]
|
|
3395
|
+
return [data?.results, data?.matches, data?.programs, data?.targets]
|
|
2881
3396
|
.map(readArray)
|
|
2882
3397
|
.find((rows) => rows.length > 0)
|
|
2883
3398
|
?.map((row) => readObject(row))
|
|
2884
3399
|
.filter((row) => row !== undefined) ?? [];
|
|
2885
3400
|
}
|
|
3401
|
+
function targetSearchRowTargets(row) {
|
|
3402
|
+
const directTarget = readObject(row.target);
|
|
3403
|
+
const arrays = [
|
|
3404
|
+
row.targets,
|
|
3405
|
+
row.matched_targets,
|
|
3406
|
+
row.matching_targets,
|
|
3407
|
+
row.target_matches,
|
|
3408
|
+
row.scope
|
|
3409
|
+
]
|
|
3410
|
+
.flatMap(readArray)
|
|
3411
|
+
.map(targetSearchEntryToTarget)
|
|
3412
|
+
.filter((target) => target !== undefined);
|
|
3413
|
+
if (directTarget) {
|
|
3414
|
+
return [directTarget, ...arrays];
|
|
3415
|
+
}
|
|
3416
|
+
if (arrays.length > 0) {
|
|
3417
|
+
return arrays;
|
|
3418
|
+
}
|
|
3419
|
+
if (targetIdentifier(row)) {
|
|
3420
|
+
return [row];
|
|
3421
|
+
}
|
|
3422
|
+
return [];
|
|
3423
|
+
}
|
|
3424
|
+
function targetSearchEntryToTarget(entry) {
|
|
3425
|
+
if (typeof entry === "string") {
|
|
3426
|
+
return { identifier: entry };
|
|
3427
|
+
}
|
|
3428
|
+
const object = readObject(entry);
|
|
3429
|
+
if (!object) {
|
|
3430
|
+
return undefined;
|
|
3431
|
+
}
|
|
3432
|
+
return readObject(object.target) ?? object;
|
|
3433
|
+
}
|
|
2886
3434
|
function rewardThresholdFilters(minReward, mode) {
|
|
2887
3435
|
return {
|
|
2888
3436
|
min_bounty_min: minReward !== undefined && mode === "min_at_least" ? minReward : undefined,
|
|
@@ -4064,7 +4612,8 @@ async function checkProgramNewTargetsFromIndexedProgram(client, config, input, i
|
|
|
4064
4612
|
});
|
|
4065
4613
|
}
|
|
4066
4614
|
async function checkWatchlistNewTargets(client, config, input) {
|
|
4067
|
-
const
|
|
4615
|
+
const resolvedProgramIds = await resolveProgramIdentifiers(client, config, input.program_ids);
|
|
4616
|
+
const watchlist = new Set(resolvedProgramIds.entries.map((entry) => entry.resolvedProgramId));
|
|
4068
4617
|
const api = await client.getRecentChanges({
|
|
4069
4618
|
change_type: "added",
|
|
4070
4619
|
include_removed: false,
|
|
@@ -4079,8 +4628,8 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4079
4628
|
const sinceTimestamp = input.since ? Date.parse(input.since) : undefined;
|
|
4080
4629
|
const changes = readArray(data?.results).map((change) => sanitizeChange(change, config.webBaseUrl));
|
|
4081
4630
|
const grouped = new Map();
|
|
4082
|
-
for (const
|
|
4083
|
-
grouped.set(
|
|
4631
|
+
for (const entry of resolvedProgramIds.entries) {
|
|
4632
|
+
grouped.set(entry.resolvedProgramId, {
|
|
4084
4633
|
program: undefined,
|
|
4085
4634
|
changes: [],
|
|
4086
4635
|
targets: [],
|
|
@@ -4118,12 +4667,13 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4118
4667
|
existing.latestAddedAt = stringField(change, "changed_at");
|
|
4119
4668
|
}
|
|
4120
4669
|
}
|
|
4121
|
-
const programs =
|
|
4122
|
-
const entry = grouped.get(
|
|
4670
|
+
const programs = resolvedProgramIds.entries.map((programEntry) => {
|
|
4671
|
+
const entry = grouped.get(programEntry.resolvedProgramId);
|
|
4123
4672
|
const targets = entry?.targets ?? [];
|
|
4124
4673
|
const limitedTargets = targets.slice(0, input.max_targets_per_program);
|
|
4125
4674
|
return stripUndefined({
|
|
4126
|
-
program_id:
|
|
4675
|
+
program_id: programEntry.resolvedProgramId,
|
|
4676
|
+
requested_program_id: programEntry.resolved ? programEntry.requestedProgramId : undefined,
|
|
4127
4677
|
program: entry?.program ? formatProgram(entry.program, "compact") : undefined,
|
|
4128
4678
|
has_new_targets: targets.length > 0,
|
|
4129
4679
|
new_target_count: targets.length,
|
|
@@ -4135,6 +4685,7 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4135
4685
|
return stripUndefined({
|
|
4136
4686
|
request_id: randomUUID(),
|
|
4137
4687
|
source_requests: [
|
|
4688
|
+
...resolvedProgramIds.sourceRequests,
|
|
4138
4689
|
{
|
|
4139
4690
|
source: "recent_changes",
|
|
4140
4691
|
request_id: api.requestId,
|
|
@@ -4144,6 +4695,7 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
4144
4695
|
],
|
|
4145
4696
|
any_new_targets: programs.some((program) => program.has_new_targets === true),
|
|
4146
4697
|
programs,
|
|
4698
|
+
warnings: resolvedProgramIds.warnings.length > 0 ? resolvedProgramIds.warnings : undefined,
|
|
4147
4699
|
meta: stripUndefined({
|
|
4148
4700
|
recent_changes_scanned: changes.length,
|
|
4149
4701
|
watchlist_size: input.program_ids.length,
|
|
@@ -4274,7 +4826,7 @@ function addProgramResourceLinks(program) {
|
|
|
4274
4826
|
};
|
|
4275
4827
|
}
|
|
4276
4828
|
function parseProgramIdFromResourceUri(uri) {
|
|
4277
|
-
return
|
|
4829
|
+
return programIdentifierSchema.parse(uri.searchParams.get("program_id") ?? "");
|
|
4278
4830
|
}
|
|
4279
4831
|
function programResourceUri(programId) {
|
|
4280
4832
|
return `bbradar://program?program_id=${encodeURIComponent(programId)}`;
|
|
@@ -4858,7 +5410,7 @@ function programFetchError(programId, error) {
|
|
|
4858
5410
|
message: error.message,
|
|
4859
5411
|
detail: sanitizeJson(error.detail),
|
|
4860
5412
|
errors: sanitizeJson(error.errors),
|
|
4861
|
-
suggested_fix: "
|
|
5413
|
+
suggested_fix: "Use a BBRadar platform:handle program_id or call resolve_program with the human program name and retry with one returned program_id."
|
|
4862
5414
|
});
|
|
4863
5415
|
}
|
|
4864
5416
|
return {
|
|
@@ -4884,6 +5436,12 @@ function apiOperationError(operation, error) {
|
|
|
4884
5436
|
message: error instanceof Error ? error.message : String(error)
|
|
4885
5437
|
};
|
|
4886
5438
|
}
|
|
5439
|
+
function unresolvedProgramNameError(programName) {
|
|
5440
|
+
return new Error(`Could not resolve program name "${programName}" to one unambiguous BBRadar program_id. Call resolve_program with the name, then retry with a returned platform:handle program_id.`);
|
|
5441
|
+
}
|
|
5442
|
+
function isStrictProgramId(value) {
|
|
5443
|
+
return programIdSchema.safeParse(value).success;
|
|
5444
|
+
}
|
|
4887
5445
|
function programSearchText(programId) {
|
|
4888
5446
|
const colonIndex = programId.indexOf(":");
|
|
4889
5447
|
const handle = colonIndex >= 0 ? programId.slice(colonIndex + 1) : programId;
|
|
@@ -5040,9 +5598,27 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5040
5598
|
const collections = [];
|
|
5041
5599
|
const queryCollections = [];
|
|
5042
5600
|
const queries = options.searchQueries.length > 0 ? options.searchQueries : [undefined];
|
|
5601
|
+
const primaryDirectProgramIds = new Set(directProgramIdCandidates(options.query, options.platforms).slice(0, 1));
|
|
5602
|
+
if (primaryDirectProgramIds.size > 0 && options.budget.remaining > 0) {
|
|
5603
|
+
const primaryDirectHandles = await collectDirectHandlePrograms(client, {
|
|
5604
|
+
query: options.query,
|
|
5605
|
+
platforms: options.platforms,
|
|
5606
|
+
budget: options.budget,
|
|
5607
|
+
warnings: options.warnings,
|
|
5608
|
+
source: "program_id_handle_guess",
|
|
5609
|
+
maxProgramIds: 1,
|
|
5610
|
+
stopOnFirstMatch: true
|
|
5611
|
+
});
|
|
5612
|
+
if (primaryDirectHandles.programs.length > 0 || primaryDirectHandles.requestsScanned > 0) {
|
|
5613
|
+
collections.push(primaryDirectHandles);
|
|
5614
|
+
}
|
|
5615
|
+
if (primaryDirectHandles.programs.length > 0 && options.isHighConfidenceMatch?.(mergeProgramCollections(collections).programs)) {
|
|
5616
|
+
return mergeProgramCollections(collections);
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5043
5619
|
for (const search of queries) {
|
|
5044
5620
|
if (options.budget.remaining <= 0) {
|
|
5045
|
-
addUniqueWarning(options.warnings, "
|
|
5621
|
+
addUniqueWarning(options.warnings, "Program name search stopped at the request budget; pass a higher upstream_request_budget for a deeper resolution attempt.");
|
|
5046
5622
|
break;
|
|
5047
5623
|
}
|
|
5048
5624
|
const collection = await collectPrograms(client, {
|
|
@@ -5061,6 +5637,23 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5061
5637
|
return mergeProgramCollections(collections);
|
|
5062
5638
|
}
|
|
5063
5639
|
}
|
|
5640
|
+
if (options.budget.remaining > 0) {
|
|
5641
|
+
const directHandles = await collectDirectHandlePrograms(client, {
|
|
5642
|
+
query: options.query,
|
|
5643
|
+
platforms: options.platforms,
|
|
5644
|
+
budget: options.budget,
|
|
5645
|
+
warnings: options.warnings,
|
|
5646
|
+
source: "program_id_handle_guess",
|
|
5647
|
+
skipProgramIds: primaryDirectProgramIds,
|
|
5648
|
+
stopOnFirstMatch: true
|
|
5649
|
+
});
|
|
5650
|
+
if (directHandles.programs.length > 0 || directHandles.requestsScanned > 0) {
|
|
5651
|
+
collections.push(directHandles);
|
|
5652
|
+
}
|
|
5653
|
+
if (directHandles.programs.length > 0 && options.isHighConfidenceMatch?.(mergeProgramCollections(collections).programs)) {
|
|
5654
|
+
return mergeProgramCollections(collections);
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5064
5657
|
if (options.max_pages > 1) {
|
|
5065
5658
|
const search = bestResolutionDeepSearch(queryCollections, options.scorePrograms) ?? queries[0];
|
|
5066
5659
|
if (options.budget.remaining <= 0) {
|
|
@@ -5121,6 +5714,152 @@ function mergeProgramCollections(collections) {
|
|
|
5121
5714
|
totalPagesBySource
|
|
5122
5715
|
};
|
|
5123
5716
|
}
|
|
5717
|
+
async function collectDirectHandlePrograms(client, options) {
|
|
5718
|
+
const programIds = directProgramIdCandidates(options.query, options.platforms)
|
|
5719
|
+
.filter((programId) => !options.skipProgramIds?.has(programId))
|
|
5720
|
+
.slice(0, options.maxProgramIds ?? MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS);
|
|
5721
|
+
const programs = [];
|
|
5722
|
+
const sourceRequests = [];
|
|
5723
|
+
let requestsScanned = 0;
|
|
5724
|
+
for (const programId of programIds) {
|
|
5725
|
+
if (!tryConsumeBudget(options.budget)) {
|
|
5726
|
+
break;
|
|
5727
|
+
}
|
|
5728
|
+
requestsScanned += 1;
|
|
5729
|
+
try {
|
|
5730
|
+
const api = await client.getProgram(programId);
|
|
5731
|
+
programs.push(api.data);
|
|
5732
|
+
sourceRequests.push({
|
|
5733
|
+
source: options.source,
|
|
5734
|
+
program_id: programId,
|
|
5735
|
+
request_id: api.requestId,
|
|
5736
|
+
upstream_request_id: api.upstreamRequestId,
|
|
5737
|
+
...apiSourceMetadata(api)
|
|
5738
|
+
});
|
|
5739
|
+
if (options.stopOnFirstMatch) {
|
|
5740
|
+
break;
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
catch (error) {
|
|
5744
|
+
if (error instanceof BBRadarApiError && error.status === 404) {
|
|
5745
|
+
sourceRequests.push({
|
|
5746
|
+
source: options.source,
|
|
5747
|
+
endpoint: "program_detail",
|
|
5748
|
+
program_id: programId,
|
|
5749
|
+
request_id: error.requestId,
|
|
5750
|
+
upstream_request_id: error.upstreamRequestId,
|
|
5751
|
+
status: error.status
|
|
5752
|
+
});
|
|
5753
|
+
if (!tryConsumeBudget(options.budget)) {
|
|
5754
|
+
break;
|
|
5755
|
+
}
|
|
5756
|
+
requestsScanned += 1;
|
|
5757
|
+
try {
|
|
5758
|
+
const targetsApi = await client.getProgramTargets(programId);
|
|
5759
|
+
const targetsData = readObject(targetsApi.data);
|
|
5760
|
+
const targets = readArray(targetsData?.targets).map(sanitizeTarget);
|
|
5761
|
+
if (targets.length > 0) {
|
|
5762
|
+
programs.push(syntheticProgramFromProgramId(programId, targets));
|
|
5763
|
+
}
|
|
5764
|
+
sourceRequests.push({
|
|
5765
|
+
source: options.source,
|
|
5766
|
+
endpoint: "program_targets",
|
|
5767
|
+
program_id: programId,
|
|
5768
|
+
request_id: targetsApi.requestId,
|
|
5769
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
5770
|
+
...apiSourceMetadata(targetsApi)
|
|
5771
|
+
});
|
|
5772
|
+
if (targets.length > 0 && options.stopOnFirstMatch) {
|
|
5773
|
+
break;
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
catch (targetError) {
|
|
5777
|
+
if (targetError instanceof BBRadarApiError && targetError.status === 404) {
|
|
5778
|
+
sourceRequests.push({
|
|
5779
|
+
source: options.source,
|
|
5780
|
+
endpoint: "program_targets",
|
|
5781
|
+
program_id: programId,
|
|
5782
|
+
request_id: targetError.requestId,
|
|
5783
|
+
upstream_request_id: targetError.upstreamRequestId,
|
|
5784
|
+
status: targetError.status
|
|
5785
|
+
});
|
|
5786
|
+
continue;
|
|
5787
|
+
}
|
|
5788
|
+
throw targetError;
|
|
5789
|
+
}
|
|
5790
|
+
continue;
|
|
5791
|
+
}
|
|
5792
|
+
throw error;
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
return {
|
|
5796
|
+
programs,
|
|
5797
|
+
sourceRequests,
|
|
5798
|
+
requestsScanned,
|
|
5799
|
+
programsScanned: programs.length,
|
|
5800
|
+
totalPagesBySource: programs.length > 0 ? { [options.source]: 1 } : {}
|
|
5801
|
+
};
|
|
5802
|
+
}
|
|
5803
|
+
function syntheticProgramFromProgramId(programId, targets) {
|
|
5804
|
+
const separatorIndex = programId.indexOf(":");
|
|
5805
|
+
const platform = separatorIndex >= 0 ? programId.slice(0, separatorIndex) : undefined;
|
|
5806
|
+
const handle = separatorIndex >= 0 ? programId.slice(separatorIndex + 1) : programId;
|
|
5807
|
+
const scopeTags = uniqueStrings(targets.flatMap((target) => readStringArrayField(target, "scope_tags")));
|
|
5808
|
+
return stripUndefined({
|
|
5809
|
+
id: programId,
|
|
5810
|
+
handle,
|
|
5811
|
+
name: titleizeProgramHandle(handle),
|
|
5812
|
+
platform,
|
|
5813
|
+
scope_tags: scopeTags,
|
|
5814
|
+
target_counts: {
|
|
5815
|
+
total: targets.length,
|
|
5816
|
+
in_scope: targets.filter((target) => target.in_scope === true).length,
|
|
5817
|
+
out_of_scope: targets.filter((target) => target.in_scope === false).length,
|
|
5818
|
+
eligible_for_bounty: targets.filter((target) => target.eligible_for_bounty === true).length,
|
|
5819
|
+
wildcard: targets.filter(targetIsWildcard).length
|
|
5820
|
+
}
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5823
|
+
function titleizeProgramHandle(handle) {
|
|
5824
|
+
return handle
|
|
5825
|
+
.split(/[-_.\s]+/)
|
|
5826
|
+
.filter(Boolean)
|
|
5827
|
+
.map((part) => part.slice(0, 1).toUpperCase() + part.slice(1))
|
|
5828
|
+
.join(" ");
|
|
5829
|
+
}
|
|
5830
|
+
function directProgramIdCandidates(query, platforms) {
|
|
5831
|
+
const platformGuesses = (platforms.length > 0 ? platforms : PROGRAM_ID_PLATFORM_GUESSES).map(canonicalProgramIdPlatform);
|
|
5832
|
+
const handles = directProgramHandleVariants(query);
|
|
5833
|
+
return uniqueStrings(platformGuesses.flatMap((platform) => handles.map((handle) => `${platform}:${handle}`))).slice(0, MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS);
|
|
5834
|
+
}
|
|
5835
|
+
function canonicalProgramIdPlatform(platform) {
|
|
5836
|
+
const normalized = normalizeTag(platform).replace(/[^a-z0-9]/g, "");
|
|
5837
|
+
const knownPlatforms = {
|
|
5838
|
+
hackerone: "HackerOne",
|
|
5839
|
+
bugcrowd: "Bugcrowd",
|
|
5840
|
+
intigriti: "Intigriti",
|
|
5841
|
+
hackenproof: "HackenProof",
|
|
5842
|
+
yeswehack: "YesWeHack",
|
|
5843
|
+
cantina: "Cantina",
|
|
5844
|
+
code4rena: "Code4rena",
|
|
5845
|
+
immunefi: "Immunefi"
|
|
5846
|
+
};
|
|
5847
|
+
return knownPlatforms[normalized] ?? platform.trim();
|
|
5848
|
+
}
|
|
5849
|
+
function directProgramHandleVariants(query) {
|
|
5850
|
+
const handleText = query.includes(":") ? query.slice(query.indexOf(":") + 1) : query;
|
|
5851
|
+
const normalized = normalizeTargetIdentifierForMatch(handleText);
|
|
5852
|
+
const dashed = normalized.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5853
|
+
const compact = dashed.replace(/-/g, "");
|
|
5854
|
+
const tokens = [...meaningfulProgramQueryTokens(handleText)];
|
|
5855
|
+
return uniqueStrings([
|
|
5856
|
+
...targetBrandTokens(handleText),
|
|
5857
|
+
dashed,
|
|
5858
|
+
compact,
|
|
5859
|
+
tokens.join("-"),
|
|
5860
|
+
tokens.join("")
|
|
5861
|
+
]).filter((handle) => /^[a-z0-9][a-z0-9_.-]{3,100}$/i.test(handle));
|
|
5862
|
+
}
|
|
5124
5863
|
async function collectProgramSources(client, sources, options) {
|
|
5125
5864
|
const pages = [];
|
|
5126
5865
|
await mapWithConcurrency(sources.map((source, sourceIndex) => ({ source, sourceIndex })), PROGRAM_COLLECTION_CONCURRENCY, async ({ source, sourceIndex }) => {
|
|
@@ -5613,7 +6352,7 @@ Find the best BBRadar program candidates.
|
|
|
5613
6352
|
title: "Summarize Program Scope",
|
|
5614
6353
|
description: "Summarize BBRadar target data for one program.",
|
|
5615
6354
|
argsSchema: {
|
|
5616
|
-
program_id:
|
|
6355
|
+
program_id: programIdentifierSchema
|
|
5617
6356
|
}
|
|
5618
6357
|
}, (args) => promptResult(`Use get_program_targets for the program_id in this JSON string: ${promptJson(args.program_id)} with include_out_of_scope=false, include_ineligible=false, and limit=${MAX_TARGETS_PER_PROGRAM}. Do not call get_program first unless program metadata is explicitly needed. Only request out-of-scope or ineligible targets if the user explicitly asks for them.
|
|
5619
6358
|
|
|
@@ -5628,7 +6367,7 @@ Summarize the program scope:
|
|
|
5628
6367
|
title: "Prepare Passive Recon Plan",
|
|
5629
6368
|
description: "Create a passive-only plan from BBRadar program and target data. Use only when planning is explicitly requested.",
|
|
5630
6369
|
argsSchema: {
|
|
5631
|
-
program_id:
|
|
6370
|
+
program_id: programIdentifierSchema.optional(),
|
|
5632
6371
|
focus: z.string().optional().describe("Optional focus area such as APIs, domains, mobile, or cloud.")
|
|
5633
6372
|
}
|
|
5634
6373
|
}, (args) => promptResult(`Prepare a passive-only plan from BBRadar data. Program id JSON string, if present: ${promptJson(args.program_id ?? "")}.
|
|
@@ -5871,32 +6610,183 @@ function targetMatchesRequestedType(target, targetType) {
|
|
|
5871
6610
|
return actualTargetType === normalizedTargetType || readStringArrayField(target, "scope_tags").map(normalizeTag).includes(normalizedTargetType);
|
|
5872
6611
|
}
|
|
5873
6612
|
function targetMatchesQuery(target, query, mode) {
|
|
5874
|
-
const
|
|
5875
|
-
if (
|
|
6613
|
+
const profile = targetSearchProfile(target);
|
|
6614
|
+
if (profile.values.length === 0) {
|
|
6615
|
+
return false;
|
|
6616
|
+
}
|
|
6617
|
+
const queryValues = targetQueryVariants(query);
|
|
6618
|
+
if (queryValues.length === 0) {
|
|
5876
6619
|
return false;
|
|
5877
6620
|
}
|
|
5878
|
-
const normalizedIdentifier = normalizeTargetIdentifierForMatch(identifier);
|
|
5879
|
-
const normalizedQuery = normalizeTargetIdentifierForMatch(query);
|
|
5880
6621
|
if (mode === "exact") {
|
|
5881
|
-
return
|
|
6622
|
+
return profile.values.some((value) => queryValues.some((queryValue) => value === queryValue));
|
|
5882
6623
|
}
|
|
5883
6624
|
if (mode === "suffix") {
|
|
5884
|
-
return (
|
|
5885
|
-
normalizedIdentifier.endsWith(`.${normalizedQuery}`) ||
|
|
5886
|
-
normalizedQuery.endsWith(`.${normalizedIdentifier}`));
|
|
6625
|
+
return profile.values.some((value) => queryValues.some((queryValue) => targetSuffixMatch(value, queryValue)));
|
|
5887
6626
|
}
|
|
5888
6627
|
if (mode === "wildcard") {
|
|
5889
|
-
return
|
|
6628
|
+
return profile.values.some((value) => queryValues.some((queryValue) => targetWildcardMatch(value, queryValue)));
|
|
6629
|
+
}
|
|
6630
|
+
const containsMatch = profile.values.some((value) => queryValues.some((queryValue) => targetContainsMatch(value, queryValue)));
|
|
6631
|
+
if (mode === "contains") {
|
|
6632
|
+
return containsMatch;
|
|
6633
|
+
}
|
|
6634
|
+
return (containsMatch ||
|
|
6635
|
+
profile.values.some((value) => queryValues.some((queryValue) => targetSuffixMatch(value, queryValue) || targetWildcardMatch(value, queryValue))) ||
|
|
6636
|
+
targetTokensMatch(profile.tokens, query));
|
|
6637
|
+
}
|
|
6638
|
+
function upstreamTargetMatchMode(mode) {
|
|
6639
|
+
return mode === "smart" ? "contains" : mode;
|
|
6640
|
+
}
|
|
6641
|
+
function targetSearchProfile(target) {
|
|
6642
|
+
const scalarFields = [
|
|
6643
|
+
"identifier",
|
|
6644
|
+
"display_name",
|
|
6645
|
+
"normalized_identifier",
|
|
6646
|
+
"target_type",
|
|
6647
|
+
"name",
|
|
6648
|
+
"url",
|
|
6649
|
+
"uri",
|
|
6650
|
+
"link",
|
|
6651
|
+
"repository",
|
|
6652
|
+
"repo",
|
|
6653
|
+
"package",
|
|
6654
|
+
"package_name",
|
|
6655
|
+
"bundle_id",
|
|
6656
|
+
"app_id",
|
|
6657
|
+
"asset",
|
|
6658
|
+
"asset_identifier",
|
|
6659
|
+
"technology"
|
|
6660
|
+
];
|
|
6661
|
+
const rawValues = [
|
|
6662
|
+
...scalarFields.map((field) => stringField(target, field)),
|
|
6663
|
+
...readStringArrayField(target, "scope_tags"),
|
|
6664
|
+
...readStringArrayField(target, "language_tags"),
|
|
6665
|
+
...readStringArrayField(target, "tags")
|
|
6666
|
+
].filter((value) => value !== undefined);
|
|
6667
|
+
const values = uniqueStrings(rawValues.flatMap(targetStringVariants));
|
|
6668
|
+
return {
|
|
6669
|
+
values,
|
|
6670
|
+
tokens: targetTokenSet([...rawValues, ...values].join(" "))
|
|
6671
|
+
};
|
|
6672
|
+
}
|
|
6673
|
+
function targetQueryVariants(query) {
|
|
6674
|
+
return targetStringVariants(query);
|
|
6675
|
+
}
|
|
6676
|
+
function targetApiSearchQueries(query) {
|
|
6677
|
+
const normalized = normalizeTargetIdentifierForMatch(query);
|
|
6678
|
+
const brandDomains = targetBrandTokens(query).flatMap((token) => [`${token}.com`, `*.${token}.com`]);
|
|
6679
|
+
return uniqueStrings([
|
|
6680
|
+
normalized,
|
|
6681
|
+
...targetQueryVariants(query),
|
|
6682
|
+
...brandDomains
|
|
6683
|
+
].filter((entry) => entry !== undefined && entry.length >= 2)).slice(0, MAX_RESOLVE_SEARCH_QUERIES);
|
|
6684
|
+
}
|
|
6685
|
+
function queryLooksLikeTargetIdentifier(query) {
|
|
6686
|
+
const normalized = normalizeTargetIdentifierForMatch(query);
|
|
6687
|
+
return /[./*:]/.test(normalized);
|
|
6688
|
+
}
|
|
6689
|
+
function targetStringVariants(value) {
|
|
6690
|
+
const normalized = normalizeTargetIdentifierForMatch(value);
|
|
6691
|
+
if (!normalized) {
|
|
6692
|
+
return [];
|
|
6693
|
+
}
|
|
6694
|
+
const withoutWildcard = stripLeadingTargetWildcard(normalized);
|
|
6695
|
+
const host = targetHostVariant(normalized);
|
|
6696
|
+
const pathVariants = targetPathVariants(normalized);
|
|
6697
|
+
const tokenPhrase = [...targetTokenSet(normalized)].join(" ");
|
|
6698
|
+
return uniqueStrings([
|
|
6699
|
+
normalized,
|
|
6700
|
+
withoutWildcard,
|
|
6701
|
+
host,
|
|
6702
|
+
host ? stripLeadingTargetWildcard(host) : undefined,
|
|
6703
|
+
...pathVariants,
|
|
6704
|
+
tokenPhrase.length >= 2 ? tokenPhrase : undefined
|
|
6705
|
+
].filter((entry) => entry !== undefined && entry.length >= 2));
|
|
6706
|
+
}
|
|
6707
|
+
function targetSuffixMatch(value, query) {
|
|
6708
|
+
const normalizedValue = stripLeadingTargetWildcard(value);
|
|
6709
|
+
const normalizedQuery = stripLeadingTargetWildcard(query);
|
|
6710
|
+
return (normalizedValue === normalizedQuery ||
|
|
6711
|
+
normalizedValue.endsWith(`.${normalizedQuery}`) ||
|
|
6712
|
+
normalizedQuery.endsWith(`.${normalizedValue}`));
|
|
6713
|
+
}
|
|
6714
|
+
function targetWildcardMatch(value, query) {
|
|
6715
|
+
return wildcardTargetMatch(query, value) || wildcardTargetMatch(value, query);
|
|
6716
|
+
}
|
|
6717
|
+
function targetContainsMatch(value, query) {
|
|
6718
|
+
if (value.includes(query)) {
|
|
6719
|
+
return true;
|
|
5890
6720
|
}
|
|
5891
|
-
return
|
|
6721
|
+
return targetValueCanContainQuery(value) && query.includes(value);
|
|
6722
|
+
}
|
|
6723
|
+
function targetValueCanContainQuery(value) {
|
|
6724
|
+
return value.includes(".") || value.includes("/") || value.includes("*") || value.includes(":");
|
|
6725
|
+
}
|
|
6726
|
+
function targetTokensMatch(targetTokens, query) {
|
|
6727
|
+
const queryTokens = [...targetTokenSet(query)];
|
|
6728
|
+
if (queryTokens.length === 0) {
|
|
6729
|
+
return false;
|
|
6730
|
+
}
|
|
6731
|
+
const overlap = queryTokens.filter((token) => targetTokens.has(token)).length;
|
|
6732
|
+
if (queryTokens.length <= 2) {
|
|
6733
|
+
return overlap === queryTokens.length;
|
|
6734
|
+
}
|
|
6735
|
+
return overlap >= Math.ceil(queryTokens.length * 0.67);
|
|
6736
|
+
}
|
|
6737
|
+
function targetTokenSet(value) {
|
|
6738
|
+
const tokens = [...tokenSet(normalizeTargetIdentifierForMatch(value))];
|
|
6739
|
+
const meaningfulTokens = tokens.filter((token) => !TARGET_QUERY_STOP_WORDS.has(token));
|
|
6740
|
+
return new Set(meaningfulTokens.length > 0 ? meaningfulTokens : tokens);
|
|
6741
|
+
}
|
|
6742
|
+
function targetBrandTokens(value) {
|
|
6743
|
+
const normalized = stripLeadingTargetWildcard(normalizeTargetIdentifierForMatch(value));
|
|
6744
|
+
const host = targetHostVariant(normalized) ?? (normalized.includes(".") && !normalized.includes("/") ? normalized : undefined);
|
|
6745
|
+
const hostBrand = host
|
|
6746
|
+
?.split(".")
|
|
6747
|
+
.filter(Boolean)
|
|
6748
|
+
.slice(-2, -1)
|
|
6749
|
+
.find((token) => token.length >= 3 && !TARGET_QUERY_STOP_WORDS.has(token));
|
|
6750
|
+
const textTokens = [...targetTokenSet(normalized)].filter((token) => token.length >= 4);
|
|
6751
|
+
return uniqueStrings([hostBrand, ...textTokens].filter((entry) => entry !== undefined));
|
|
5892
6752
|
}
|
|
5893
6753
|
function normalizeTargetIdentifierForMatch(value) {
|
|
5894
|
-
|
|
6754
|
+
const normalized = value
|
|
5895
6755
|
.trim()
|
|
5896
6756
|
.toLowerCase()
|
|
5897
|
-
.replace(
|
|
5898
|
-
.replace(/^
|
|
5899
|
-
.replace(
|
|
6757
|
+
.replace(/\\/g, "/")
|
|
6758
|
+
.replace(/^['"`<]+|['"`>]+$/g, "")
|
|
6759
|
+
.replace(/^[a-z][a-z0-9+.-]*:\/\//, "")
|
|
6760
|
+
.replace(/^www\./, "");
|
|
6761
|
+
const queryStart = normalized.search(/[?#]/);
|
|
6762
|
+
return (queryStart >= 0 ? normalized.slice(0, queryStart) : normalized).replace(/\/+$/, "");
|
|
6763
|
+
}
|
|
6764
|
+
function stripLeadingTargetWildcard(value) {
|
|
6765
|
+
return value.replace(/^\*\./, "").replace(/^\*\/?/, "");
|
|
6766
|
+
}
|
|
6767
|
+
function targetHostVariant(value) {
|
|
6768
|
+
const withoutWildcard = stripLeadingTargetWildcard(value);
|
|
6769
|
+
const slashIndex = withoutWildcard.indexOf("/");
|
|
6770
|
+
if (slashIndex <= 0) {
|
|
6771
|
+
return undefined;
|
|
6772
|
+
}
|
|
6773
|
+
const host = withoutWildcard.slice(0, slashIndex).replace(/^www\./, "");
|
|
6774
|
+
return host.includes(".") || host === "localhost" ? host : undefined;
|
|
6775
|
+
}
|
|
6776
|
+
function targetPathVariants(value) {
|
|
6777
|
+
const withoutWildcard = stripLeadingTargetWildcard(value);
|
|
6778
|
+
const segments = withoutWildcard.split("/").filter(Boolean);
|
|
6779
|
+
if (segments.length <= 1) {
|
|
6780
|
+
return [];
|
|
6781
|
+
}
|
|
6782
|
+
const pathSegments = targetHostVariant(value) ? segments.slice(1) : segments;
|
|
6783
|
+
const lastSegment = pathSegments.at(-1);
|
|
6784
|
+
const lastWithoutExtension = lastSegment?.replace(/\.[a-z0-9]{1,12}$/i, "");
|
|
6785
|
+
return uniqueStrings([
|
|
6786
|
+
pathSegments.join("/"),
|
|
6787
|
+
lastSegment,
|
|
6788
|
+
lastWithoutExtension && lastWithoutExtension !== lastSegment ? lastWithoutExtension : undefined
|
|
6789
|
+
].filter((entry) => entry !== undefined && entry.length >= 2));
|
|
5900
6790
|
}
|
|
5901
6791
|
function wildcardTargetMatch(pattern, value) {
|
|
5902
6792
|
if (!pattern.includes("*")) {
|