@bbradar/mcp 0.1.8 → 0.1.11
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 +6 -5
- package/dist/server.js +454 -77
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BBRadarApiError } from "./bbradarClient.js";
|
|
5
|
-
import { stripUndefined } from "./json.js";
|
|
5
|
+
import { stableStringify, stripUndefined } from "./json.js";
|
|
6
6
|
import { FixedWindowRateLimiter } from "./rateLimit.js";
|
|
7
7
|
import { readArray, readBoolean, readNumber, readObject, readString, sanitizeJson, sanitizeChange, sanitizeProgram, sanitizeTarget, sanitizeTargetsForExport } from "./sanitize.js";
|
|
8
8
|
const SERVER_VERSION = "0.1.0";
|
|
@@ -94,7 +94,7 @@ const filterValueSchema = z
|
|
|
94
94
|
});
|
|
95
95
|
const stringListSchema = z
|
|
96
96
|
.preprocess(normalizeStringListInput, z.array(filterValueSchema).max(50).default([]))
|
|
97
|
-
.describe("
|
|
97
|
+
.describe("Optional filter values. Empty or omitted means all values; do not infer a platform filter.");
|
|
98
98
|
const acceptedTargetLimitSchema = z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM);
|
|
99
99
|
const searchTextSchema = z
|
|
100
100
|
.string()
|
|
@@ -228,7 +228,8 @@ export function createBbradarServer(client, config) {
|
|
|
228
228
|
"For BBRadar asks, use these tools first and avoid external skills unless methodology is explicitly requested.",
|
|
229
229
|
"Partial program name + action: run_program_name_action. Partial program name only: resolve_program.",
|
|
230
230
|
"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
|
-
"
|
|
231
|
+
"Recently updated programs across platforms: find_recently_updated_programs. Do not infer a platform such as Immunefi unless the user explicitly names it.",
|
|
232
|
+
"Freshness: find_recently_updated_programs, get_latest_added_targets, check_program_new_targets, check_watchlist_new_targets, find_recent_by_type, get_program_scope_delta.",
|
|
232
233
|
"Discovery: find_program_candidates, find_stack_matches, find_language_programs, find_target_type_programs, find_reward_programs, find_paid_programs, find_vdp_programs, find_web3_contests, find_low_noise_programs.",
|
|
233
234
|
"Details: get_program_brief, get_program_scope_summary, get_program_target_breakdown, find_programs_by_target, compare_programs_compact.",
|
|
234
235
|
"Prefer compact/identifier outputs and disclose sampled ranking scope when present."
|
|
@@ -276,10 +277,18 @@ export function createBbradarServer(client, config) {
|
|
|
276
277
|
annotations: readOnlyAnnotations
|
|
277
278
|
}, (args) => runTool("get_program", config, rateLimiter, async () => {
|
|
278
279
|
return runProgramIdTool(client, config, args.program_id, async (programId) => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
try {
|
|
281
|
+
const api = await client.getProgram(programId);
|
|
282
|
+
return withApiMetadata(api, {
|
|
283
|
+
program: addProgramResourceLinks(sanitizeProgram(api.data, config.webBaseUrl))
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
if (!isProgramNotFoundError(error)) {
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
return getProgramFromTargetsOnlyFallback(client, programId, error);
|
|
291
|
+
}
|
|
283
292
|
}, async (indexFallback) => ({
|
|
284
293
|
request_id: randomUUID(),
|
|
285
294
|
warnings: [`Returned program metadata from the BBRadar program index because the per-program detail endpoint returned 404 for ${indexFallback.resolvedProgramId}.`],
|
|
@@ -348,9 +357,40 @@ export function createBbradarServer(client, config) {
|
|
|
348
357
|
},
|
|
349
358
|
annotations: readOnlyAnnotations
|
|
350
359
|
}, (args) => runTool("run_program_name_action", config, rateLimiter, async () => runProgramNameAction(client, config, args)));
|
|
360
|
+
server.registerTool("get_program_eligible_targets_by_name", {
|
|
361
|
+
title: "Get Eligible Targets By Program Name",
|
|
362
|
+
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
|
+
inputSchema: {
|
|
364
|
+
query: searchTextSchema,
|
|
365
|
+
platforms: stringListSchema,
|
|
366
|
+
tags: stringListSchema,
|
|
367
|
+
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
368
|
+
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(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET),
|
|
370
|
+
include_out_of_scope: z.boolean().default(false),
|
|
371
|
+
include_ineligible: z.boolean().default(false),
|
|
372
|
+
strict_scope_filter: z.boolean().default(true),
|
|
373
|
+
offset: z.number().int().min(0).default(0),
|
|
374
|
+
limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(MAX_TARGETS_PER_PROGRAM),
|
|
375
|
+
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
376
|
+
output_mode: programListModeSchema.default("compact")
|
|
377
|
+
},
|
|
378
|
+
outputSchema: {
|
|
379
|
+
...apiEnvelopeOutputShape,
|
|
380
|
+
source_requests: z.array(jsonRecordSchema).optional(),
|
|
381
|
+
resolved_program: jsonRecordSchema.optional(),
|
|
382
|
+
program_id: z.string().optional(),
|
|
383
|
+
targets: z.array(z.unknown()).optional(),
|
|
384
|
+
warnings: z.array(z.string()).optional(),
|
|
385
|
+
upstream_budget: jsonRecordSchema.optional(),
|
|
386
|
+
ranking_scope: jsonRecordSchema.optional(),
|
|
387
|
+
meta: jsonRecordSchema.optional()
|
|
388
|
+
},
|
|
389
|
+
annotations: readOnlyAnnotations
|
|
390
|
+
}, (args) => runTool("get_program_eligible_targets_by_name", config, rateLimiter, async () => getProgramEligibleTargetsByName(client, config, args)));
|
|
351
391
|
server.registerTool("get_program_targets", {
|
|
352
392
|
title: "Get Program Targets",
|
|
353
|
-
description: `Get active targets for one program. limit max ${MAX_TARGETS_PER_PROGRAM}.`,
|
|
393
|
+
description: `Get active targets for one known program_id. This is a final source for eligible target lists; do not call summary/breakdown unless grouped counts are needed. For a program name query, prefer get_program_eligible_targets_by_name. limit max ${MAX_TARGETS_PER_PROGRAM}.`,
|
|
354
394
|
inputSchema: {
|
|
355
395
|
program_id: programIdSchema,
|
|
356
396
|
include_out_of_scope: z.boolean().default(false),
|
|
@@ -1107,6 +1147,31 @@ export function createBbradarServer(client, config) {
|
|
|
1107
1147
|
output_mode: args.output_mode,
|
|
1108
1148
|
upstream_request_budget: args.upstream_request_budget
|
|
1109
1149
|
})));
|
|
1150
|
+
server.registerTool("find_recently_updated_programs", {
|
|
1151
|
+
title: "Find Recently Updated Programs",
|
|
1152
|
+
description: "Recently updated program shortlist across all platforms by default. Platform filters are applied only when explicitly provided.",
|
|
1153
|
+
inputSchema: {
|
|
1154
|
+
platforms: stringListSchema,
|
|
1155
|
+
tags: stringListSchema,
|
|
1156
|
+
target_types: stringListSchema,
|
|
1157
|
+
language_tags: stringListSchema,
|
|
1158
|
+
web3: z.boolean().default(false),
|
|
1159
|
+
updated_since: isoDateTimeSchema.optional(),
|
|
1160
|
+
require_bounty: z.boolean().default(false),
|
|
1161
|
+
min_eligible_targets: z.number().int().min(0).optional(),
|
|
1162
|
+
exclude_ctf: z.boolean().default(true),
|
|
1163
|
+
vdp_mode: vdpModeSchema.default("exclude").describe("VDP/no-bounty filter mode."),
|
|
1164
|
+
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
1165
|
+
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1166
|
+
include_target_samples: z.boolean().default(false),
|
|
1167
|
+
target_sample_programs: z.number().int().min(0).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
1168
|
+
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
1169
|
+
output_mode: programListModeSchema.default("compact"),
|
|
1170
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1171
|
+
},
|
|
1172
|
+
outputSchema: findProgramsOutputShape,
|
|
1173
|
+
annotations: readOnlyAnnotations
|
|
1174
|
+
}, (args) => runTool("find_recently_updated_programs", config, rateLimiter, async () => findRecentlyUpdatedPrograms(client, config, args)));
|
|
1110
1175
|
server.registerTool("list_filters", {
|
|
1111
1176
|
title: "List BBRadar Filters",
|
|
1112
1177
|
description: "Discover observed filter values.",
|
|
@@ -1491,6 +1556,7 @@ function registerResources(server, client, config, exportStore) {
|
|
|
1491
1556
|
}
|
|
1492
1557
|
const programFallbackCache = new Map();
|
|
1493
1558
|
const programTargetsFallbackCaches = new WeakMap();
|
|
1559
|
+
const resolveProgramCache = new Map();
|
|
1494
1560
|
async function getProgramWithIdFallback(client, config, requestedProgramId) {
|
|
1495
1561
|
const cachedFallback = getCachedProgramFallback(config, requestedProgramId);
|
|
1496
1562
|
if (cachedFallback && !isProgramIndexFallback(cachedFallback)) {
|
|
@@ -1873,6 +1939,10 @@ function withProgramIndexFallbackMetadata(payload, fallback) {
|
|
|
1873
1939
|
};
|
|
1874
1940
|
}
|
|
1875
1941
|
async function resolveProgram(client, config, input) {
|
|
1942
|
+
const cached = getCachedResolveProgram(config, input);
|
|
1943
|
+
if (cached) {
|
|
1944
|
+
return cached;
|
|
1945
|
+
}
|
|
1876
1946
|
const warnings = [];
|
|
1877
1947
|
const budget = {
|
|
1878
1948
|
initial: input.upstream_request_budget,
|
|
@@ -1882,6 +1952,8 @@ async function resolveProgram(client, config, input) {
|
|
|
1882
1952
|
const searchQueries = programResolutionSearchQueries(input.query);
|
|
1883
1953
|
const collected = await collectProgramsForResolution(client, {
|
|
1884
1954
|
searchQueries,
|
|
1955
|
+
isHighConfidenceMatch: (programs) => hasHighConfidenceProgramResolutionMatch(programs, config.webBaseUrl, input.query),
|
|
1956
|
+
scorePrograms: (programs) => bestProgramResolutionScore(programs, config.webBaseUrl, input.query),
|
|
1885
1957
|
platforms: input.platforms,
|
|
1886
1958
|
tags: input.tags,
|
|
1887
1959
|
opportunity_levels: input.opportunity_levels,
|
|
@@ -1899,7 +1971,7 @@ async function resolveProgram(client, config, input) {
|
|
|
1899
1971
|
.sort((left, right) => right.match.score - left.match.score || compareProgramCandidates(left.candidate, right.candidate, "best_hunt_value"))
|
|
1900
1972
|
.slice(0, input.max_results);
|
|
1901
1973
|
const programs = matches.map((entry) => formatProgram(entry.candidate.program, input.output_mode));
|
|
1902
|
-
|
|
1974
|
+
const payload = stripUndefined({
|
|
1903
1975
|
request_id: randomUUID(),
|
|
1904
1976
|
source_requests: collected.sourceRequests,
|
|
1905
1977
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
@@ -1933,6 +2005,75 @@ async function resolveProgram(client, config, input) {
|
|
|
1933
2005
|
upstream_requests_scanned: collected.requestsScanned
|
|
1934
2006
|
}
|
|
1935
2007
|
});
|
|
2008
|
+
rememberResolveProgram(config, input, payload);
|
|
2009
|
+
return payload;
|
|
2010
|
+
}
|
|
2011
|
+
function getCachedResolveProgram(config, input) {
|
|
2012
|
+
if (config.cacheTtlMs <= 0 || config.cacheMaxEntries <= 0) {
|
|
2013
|
+
return undefined;
|
|
2014
|
+
}
|
|
2015
|
+
const key = resolveProgramCacheKey(config, input);
|
|
2016
|
+
const cached = resolveProgramCache.get(key);
|
|
2017
|
+
if (!cached) {
|
|
2018
|
+
return undefined;
|
|
2019
|
+
}
|
|
2020
|
+
if (cached.expiresAt <= Date.now()) {
|
|
2021
|
+
resolveProgramCache.delete(key);
|
|
2022
|
+
return undefined;
|
|
2023
|
+
}
|
|
2024
|
+
const sourceRequests = readArray(cached.payload.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2025
|
+
const warnings = readArray(cached.payload.warnings).filter((warning) => typeof warning === "string");
|
|
2026
|
+
return stripUndefined({
|
|
2027
|
+
...cached.payload,
|
|
2028
|
+
request_id: randomUUID(),
|
|
2029
|
+
cache: {
|
|
2030
|
+
hit: true,
|
|
2031
|
+
expires_at: new Date(cached.expiresAt).toISOString()
|
|
2032
|
+
},
|
|
2033
|
+
source_requests: sourceRequests.map((request) => ({
|
|
2034
|
+
...request,
|
|
2035
|
+
cache_hit: true,
|
|
2036
|
+
cache_expires_at: new Date(cached.expiresAt).toISOString()
|
|
2037
|
+
})),
|
|
2038
|
+
warnings: uniqueStrings([...warnings, `Reused cached program resolution for ${input.query}.`])
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
function rememberResolveProgram(config, input, payload) {
|
|
2042
|
+
if (config.cacheTtlMs <= 0 || config.cacheMaxEntries <= 0) {
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
pruneResolveProgramCache();
|
|
2046
|
+
resolveProgramCache.set(resolveProgramCacheKey(config, input), {
|
|
2047
|
+
expiresAt: Date.now() + config.cacheTtlMs,
|
|
2048
|
+
payload: { ...payload }
|
|
2049
|
+
});
|
|
2050
|
+
if (resolveProgramCache.size > PROGRAM_FALLBACK_CACHE_MAX_ENTRIES) {
|
|
2051
|
+
const oldestKey = resolveProgramCache.keys().next().value;
|
|
2052
|
+
if (oldestKey) {
|
|
2053
|
+
resolveProgramCache.delete(oldestKey);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
function pruneResolveProgramCache() {
|
|
2058
|
+
const now = Date.now();
|
|
2059
|
+
for (const [key, cached] of resolveProgramCache) {
|
|
2060
|
+
if (cached.expiresAt <= now) {
|
|
2061
|
+
resolveProgramCache.delete(key);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
function resolveProgramCacheKey(config, input) {
|
|
2066
|
+
return stableStringify({
|
|
2067
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
2068
|
+
query: normalizeTag(input.query),
|
|
2069
|
+
platforms: input.platforms.map(normalizeTag).sort(),
|
|
2070
|
+
tags: input.tags.map(normalizeTag).sort(),
|
|
2071
|
+
opportunity_levels: [...input.opportunity_levels].sort(),
|
|
2072
|
+
max_pages: input.max_pages,
|
|
2073
|
+
max_results: input.max_results,
|
|
2074
|
+
output_mode: input.output_mode,
|
|
2075
|
+
upstream_request_budget: input.upstream_request_budget
|
|
2076
|
+
});
|
|
1936
2077
|
}
|
|
1937
2078
|
async function runProgramNameAction(client, config, input) {
|
|
1938
2079
|
const resolution = await resolveProgram(client, config, {
|
|
@@ -1977,6 +2118,76 @@ async function runProgramNameAction(client, config, input) {
|
|
|
1977
2118
|
ranking_scope: readObject(resolution.ranking_scope)
|
|
1978
2119
|
});
|
|
1979
2120
|
}
|
|
2121
|
+
async function getProgramEligibleTargetsByName(client, config, input) {
|
|
2122
|
+
const resolution = await resolveProgram(client, config, {
|
|
2123
|
+
query: input.query,
|
|
2124
|
+
platforms: input.platforms,
|
|
2125
|
+
tags: input.tags,
|
|
2126
|
+
opportunity_levels: input.opportunity_levels,
|
|
2127
|
+
max_pages: input.max_resolve_pages,
|
|
2128
|
+
max_results: 1,
|
|
2129
|
+
output_mode: input.output_mode,
|
|
2130
|
+
upstream_request_budget: input.upstream_request_budget
|
|
2131
|
+
});
|
|
2132
|
+
const bestMatch = readObject(readArray(resolution.matches)[0]);
|
|
2133
|
+
const programId = stringField(bestMatch, "program_id");
|
|
2134
|
+
const resolvedProgram = readObject(bestMatch?.program);
|
|
2135
|
+
const resolutionSourceRequests = readArray(resolution.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2136
|
+
const resolutionWarnings = readArray(resolution.warnings).filter((warning) => typeof warning === "string");
|
|
2137
|
+
if (!programId) {
|
|
2138
|
+
return stripUndefined({
|
|
2139
|
+
request_id: randomUUID(),
|
|
2140
|
+
source_requests: resolutionSourceRequests,
|
|
2141
|
+
warnings: uniqueStrings([...resolutionWarnings, "No matching program_id was resolved for the requested program name."]),
|
|
2142
|
+
resolved_program: undefined,
|
|
2143
|
+
targets: [],
|
|
2144
|
+
upstream_budget: readObject(resolution.upstream_budget),
|
|
2145
|
+
ranking_scope: readObject(resolution.ranking_scope),
|
|
2146
|
+
meta: {
|
|
2147
|
+
found: false,
|
|
2148
|
+
returned: 0,
|
|
2149
|
+
query: input.query
|
|
2150
|
+
}
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
const targetsPayload = await runProgramIdTool(client, config, programId, (resolvedProgramId) => getProgramTargetsPayload(client, {
|
|
2154
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
2155
|
+
include_ineligible: input.include_ineligible,
|
|
2156
|
+
strict_scope_filter: input.strict_scope_filter,
|
|
2157
|
+
offset: input.offset,
|
|
2158
|
+
limit: input.limit,
|
|
2159
|
+
output_mode: input.target_list_mode
|
|
2160
|
+
}, resolvedProgramId), (indexFallback) => getProgramTargetsExportFallbackPayload(client, {
|
|
2161
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
2162
|
+
include_ineligible: input.include_ineligible,
|
|
2163
|
+
strict_scope_filter: input.strict_scope_filter,
|
|
2164
|
+
offset: input.offset,
|
|
2165
|
+
limit: input.limit,
|
|
2166
|
+
output_mode: input.target_list_mode
|
|
2167
|
+
}, indexFallback.resolvedProgramId));
|
|
2168
|
+
const targetSourceRequests = readArray(targetsPayload.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2169
|
+
const targetWarnings = readArray(targetsPayload.warnings).filter((warning) => typeof warning === "string");
|
|
2170
|
+
const targetMeta = readObject(targetsPayload.meta);
|
|
2171
|
+
return stripUndefined({
|
|
2172
|
+
request_id: randomUUID(),
|
|
2173
|
+
source_requests: [...resolutionSourceRequests, ...targetSourceRequests],
|
|
2174
|
+
warnings: uniqueStrings([...resolutionWarnings, ...targetWarnings]),
|
|
2175
|
+
resolved_program: resolvedProgram,
|
|
2176
|
+
program_id: stringField(targetsPayload, "program_id") ?? programId,
|
|
2177
|
+
targets: readArray(targetsPayload.targets),
|
|
2178
|
+
upstream_budget: readObject(resolution.upstream_budget),
|
|
2179
|
+
ranking_scope: readObject(resolution.ranking_scope),
|
|
2180
|
+
program_id_resolution: readObject(targetsPayload.program_id_resolution),
|
|
2181
|
+
meta: stripUndefined({
|
|
2182
|
+
...(targetMeta ?? {}),
|
|
2183
|
+
found: true,
|
|
2184
|
+
query: input.query,
|
|
2185
|
+
target_filter: "in_scope_and_eligible_by_default",
|
|
2186
|
+
resolve_request_id: stringField(resolution, "request_id"),
|
|
2187
|
+
targets_request_id: stringField(targetsPayload, "request_id")
|
|
2188
|
+
})
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
1980
2191
|
async function runResolvedProgramAction(client, config, programId, input) {
|
|
1981
2192
|
if (input.action === "scope_delta") {
|
|
1982
2193
|
return getProgramScopeDelta(client, config, {
|
|
@@ -2095,6 +2306,48 @@ function findProgramCandidates(client, config, input) {
|
|
|
2095
2306
|
upstream_request_budget: input.upstream_request_budget
|
|
2096
2307
|
});
|
|
2097
2308
|
}
|
|
2309
|
+
function findRecentlyUpdatedPrograms(client, config, input) {
|
|
2310
|
+
return findPrograms(client, config, {
|
|
2311
|
+
platforms: input.platforms,
|
|
2312
|
+
tags: input.tags,
|
|
2313
|
+
scope_tags: [],
|
|
2314
|
+
target_types: input.target_types,
|
|
2315
|
+
language_tags: input.language_tags,
|
|
2316
|
+
web3: input.web3,
|
|
2317
|
+
opportunity_levels: [],
|
|
2318
|
+
updated_since: input.updated_since,
|
|
2319
|
+
min_bounty_min: undefined,
|
|
2320
|
+
min_bounty_max: undefined,
|
|
2321
|
+
max_bounty_max: undefined,
|
|
2322
|
+
require_bounty: input.require_bounty,
|
|
2323
|
+
max_public_report_count: undefined,
|
|
2324
|
+
require_known_report_count: false,
|
|
2325
|
+
include_unknown_report_count: true,
|
|
2326
|
+
exclude_ctf: input.exclude_ctf,
|
|
2327
|
+
exclude_mobile_only: false,
|
|
2328
|
+
exclude_non_web: false,
|
|
2329
|
+
has_api_scope: input.target_types.some((targetType) => normalizeTag(targetType).includes("api")),
|
|
2330
|
+
contest_like: false,
|
|
2331
|
+
vdp_mode: input.vdp_mode,
|
|
2332
|
+
fresh_launch_days: undefined,
|
|
2333
|
+
min_wildcards: 0,
|
|
2334
|
+
min_eligible_targets: input.min_eligible_targets,
|
|
2335
|
+
min_total_targets: undefined,
|
|
2336
|
+
min_added_24h: undefined,
|
|
2337
|
+
min_added_7d: undefined,
|
|
2338
|
+
max_pages: input.max_pages,
|
|
2339
|
+
max_results: input.max_results,
|
|
2340
|
+
sort_by: "recently_updated",
|
|
2341
|
+
include_target_samples: input.include_target_samples,
|
|
2342
|
+
target_sample_programs: input.target_sample_programs,
|
|
2343
|
+
target_sample_size: input.target_sample_size,
|
|
2344
|
+
only_in_scope_targets: true,
|
|
2345
|
+
only_bounty_eligible_targets: true,
|
|
2346
|
+
target_samples_only_wildcards: false,
|
|
2347
|
+
output_mode: input.output_mode ?? "compact",
|
|
2348
|
+
upstream_request_budget: input.upstream_request_budget
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2098
2351
|
function findRewardPrograms(client, config, input) {
|
|
2099
2352
|
return findPrograms(client, config, {
|
|
2100
2353
|
platforms: input.platforms,
|
|
@@ -3159,6 +3412,93 @@ async function fetchProgramTargetsFromExportFallback(client, programId) {
|
|
|
3159
3412
|
};
|
|
3160
3413
|
}
|
|
3161
3414
|
}
|
|
3415
|
+
async function getProgramFromTargetsOnlyFallback(client, programId, programError) {
|
|
3416
|
+
const targetsApi = await client.getProgramTargets(programId);
|
|
3417
|
+
const programApiError = programError instanceof BBRadarApiError ? programError : undefined;
|
|
3418
|
+
return stripUndefined({
|
|
3419
|
+
request_id: targetsApi.requestId,
|
|
3420
|
+
fetched_at: targetsApi.fetchedAt,
|
|
3421
|
+
cache: readObject(stripUndefined({
|
|
3422
|
+
hit: targetsApi.cached,
|
|
3423
|
+
coalesced_live_request: targetsApi.coalesced,
|
|
3424
|
+
expires_at: targetsApi.cacheExpiresAt
|
|
3425
|
+
})),
|
|
3426
|
+
source_requests: [
|
|
3427
|
+
stripUndefined({
|
|
3428
|
+
source: "program",
|
|
3429
|
+
program_id: programId,
|
|
3430
|
+
failed: true,
|
|
3431
|
+
request_id: programApiError?.requestId,
|
|
3432
|
+
upstream_request_id: programApiError?.upstreamRequestId,
|
|
3433
|
+
status: programApiError?.status
|
|
3434
|
+
}),
|
|
3435
|
+
{
|
|
3436
|
+
source: "program_targets",
|
|
3437
|
+
program_id: programId,
|
|
3438
|
+
request_id: targetsApi.requestId,
|
|
3439
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3440
|
+
...apiSourceMetadata(targetsApi)
|
|
3441
|
+
}
|
|
3442
|
+
],
|
|
3443
|
+
warnings: [`Returned minimal program metadata because the per-program detail endpoint returned 404 for ${programId}, but the targets endpoint is available.`],
|
|
3444
|
+
program: addProgramResourceLinks({ id: programId }),
|
|
3445
|
+
meta: {
|
|
3446
|
+
detail_source: "program_targets",
|
|
3447
|
+
detail_endpoint_unavailable: true
|
|
3448
|
+
}
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
async function fetchProgramDetailsAndTargets(client, config, programId) {
|
|
3452
|
+
const [programResult, targetsResult] = await Promise.allSettled([client.getProgram(programId), client.getProgramTargets(programId)]);
|
|
3453
|
+
if (targetsResult.status === "rejected") {
|
|
3454
|
+
throw targetsResult.reason;
|
|
3455
|
+
}
|
|
3456
|
+
const targetsApi = targetsResult.value;
|
|
3457
|
+
const targetSourceRequest = {
|
|
3458
|
+
source: "program_targets",
|
|
3459
|
+
request_id: targetsApi.requestId,
|
|
3460
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3461
|
+
...apiSourceMetadata(targetsApi)
|
|
3462
|
+
};
|
|
3463
|
+
if (programResult.status === "fulfilled") {
|
|
3464
|
+
return {
|
|
3465
|
+
program: addProgramResourceLinks(sanitizeProgram(programResult.value.data, config.webBaseUrl)),
|
|
3466
|
+
targetsApi,
|
|
3467
|
+
sourceRequests: [
|
|
3468
|
+
{
|
|
3469
|
+
source: "program",
|
|
3470
|
+
request_id: programResult.value.requestId,
|
|
3471
|
+
upstream_request_id: programResult.value.upstreamRequestId,
|
|
3472
|
+
...apiSourceMetadata(programResult.value)
|
|
3473
|
+
},
|
|
3474
|
+
targetSourceRequest
|
|
3475
|
+
],
|
|
3476
|
+
warnings: [],
|
|
3477
|
+
programDetailUnavailable: false
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
if (!isProgramNotFoundError(programResult.reason)) {
|
|
3481
|
+
throw programResult.reason;
|
|
3482
|
+
}
|
|
3483
|
+
const programApiError = programResult.reason instanceof BBRadarApiError ? programResult.reason : undefined;
|
|
3484
|
+
return {
|
|
3485
|
+
program: addProgramResourceLinks({ id: programId }),
|
|
3486
|
+
targetsApi,
|
|
3487
|
+
sourceRequests: [
|
|
3488
|
+
stripUndefined({
|
|
3489
|
+
source: "program",
|
|
3490
|
+
program_id: programId,
|
|
3491
|
+
failed: true,
|
|
3492
|
+
request_id: programApiError?.requestId,
|
|
3493
|
+
upstream_request_id: programApiError?.upstreamRequestId,
|
|
3494
|
+
status: programApiError?.status
|
|
3495
|
+
}),
|
|
3496
|
+
targetSourceRequest
|
|
3497
|
+
],
|
|
3498
|
+
warnings: [`Used targets endpoint with minimal program metadata because the per-program detail endpoint returned 404 for ${programId}.`],
|
|
3499
|
+
programDetailUnavailable: true
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3162
3502
|
async function exportTargetsFromProgramTargetsFallback(client, config, exportStore, input, exportError) {
|
|
3163
3503
|
const warnings = ["Target export endpoint is unavailable; assembled a fallback export from per-program target endpoints."];
|
|
3164
3504
|
const sourceRequests = [
|
|
@@ -3282,8 +3622,8 @@ function readExportTargetRows(data) {
|
|
|
3282
3622
|
.find((rows) => rows.length > 0) ?? [];
|
|
3283
3623
|
}
|
|
3284
3624
|
async function getProgramScopeSummary(client, config, input) {
|
|
3285
|
-
const
|
|
3286
|
-
const targetsData = readObject(targetsApi.data);
|
|
3625
|
+
const lookup = await fetchProgramDetailsAndTargets(client, config, input.program_id);
|
|
3626
|
+
const targetsData = readObject(lookup.targetsApi.data);
|
|
3287
3627
|
const rawTargets = readArray(targetsData?.targets);
|
|
3288
3628
|
const targets = rawTargets
|
|
3289
3629
|
.map(sanitizeTarget)
|
|
@@ -3292,31 +3632,21 @@ async function getProgramScopeSummary(client, config, input) {
|
|
|
3292
3632
|
include_ineligible: input.include_ineligible,
|
|
3293
3633
|
strict_scope_filter: false
|
|
3294
3634
|
}));
|
|
3295
|
-
return {
|
|
3635
|
+
return stripUndefined({
|
|
3296
3636
|
request_id: randomUUID(),
|
|
3297
|
-
source_requests:
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
request_id: programApi.requestId,
|
|
3301
|
-
upstream_request_id: programApi.upstreamRequestId,
|
|
3302
|
-
...apiSourceMetadata(programApi)
|
|
3303
|
-
},
|
|
3304
|
-
{
|
|
3305
|
-
source: "program_targets",
|
|
3306
|
-
request_id: targetsApi.requestId,
|
|
3307
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3308
|
-
...apiSourceMetadata(targetsApi)
|
|
3309
|
-
}
|
|
3310
|
-
],
|
|
3311
|
-
program: formatProgram(addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl)), "compact"),
|
|
3637
|
+
source_requests: lookup.sourceRequests,
|
|
3638
|
+
warnings: lookup.warnings.length > 0 ? lookup.warnings : undefined,
|
|
3639
|
+
program: formatProgram(lookup.program, "compact"),
|
|
3312
3640
|
scope: buildTargetScopeSummary(targets, input.target_list_mode, input.group_limit, input.include_out_of_scope, input.include_ineligible),
|
|
3313
3641
|
meta: {
|
|
3314
3642
|
total_active_targets: rawTargets.length,
|
|
3315
3643
|
total_after_filters: targets.length,
|
|
3316
3644
|
target_list_mode: input.target_list_mode,
|
|
3317
|
-
group_limit: input.group_limit
|
|
3645
|
+
group_limit: input.group_limit,
|
|
3646
|
+
target_source: "program_targets",
|
|
3647
|
+
detail_endpoint_unavailable: lookup.programDetailUnavailable || undefined
|
|
3318
3648
|
}
|
|
3319
|
-
};
|
|
3649
|
+
});
|
|
3320
3650
|
}
|
|
3321
3651
|
async function getProgramScopeSummaryFromIndexFallback(client, indexFallback, input) {
|
|
3322
3652
|
const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, input.program_id);
|
|
@@ -3346,8 +3676,8 @@ async function getProgramScopeSummaryFromIndexFallback(client, indexFallback, in
|
|
|
3346
3676
|
});
|
|
3347
3677
|
}
|
|
3348
3678
|
async function getProgramTargetBreakdown(client, config, input) {
|
|
3349
|
-
const
|
|
3350
|
-
const targetsData = readObject(targetsApi.data);
|
|
3679
|
+
const lookup = await fetchProgramDetailsAndTargets(client, config, input.program_id);
|
|
3680
|
+
const targetsData = readObject(lookup.targetsApi.data);
|
|
3351
3681
|
const rawTargets = readArray(targetsData?.targets);
|
|
3352
3682
|
const targets = rawTargets
|
|
3353
3683
|
.map(sanitizeTarget)
|
|
@@ -3357,23 +3687,11 @@ async function getProgramTargetBreakdown(client, config, input) {
|
|
|
3357
3687
|
strict_scope_filter: false
|
|
3358
3688
|
}));
|
|
3359
3689
|
const scope = buildTargetScopeSummary(targets, input.target_list_mode, input.group_limit, input.include_out_of_scope, input.include_ineligible);
|
|
3360
|
-
return {
|
|
3690
|
+
return stripUndefined({
|
|
3361
3691
|
request_id: randomUUID(),
|
|
3362
|
-
source_requests:
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
request_id: programApi.requestId,
|
|
3366
|
-
upstream_request_id: programApi.upstreamRequestId,
|
|
3367
|
-
...apiSourceMetadata(programApi)
|
|
3368
|
-
},
|
|
3369
|
-
{
|
|
3370
|
-
source: "program_targets",
|
|
3371
|
-
request_id: targetsApi.requestId,
|
|
3372
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3373
|
-
...apiSourceMetadata(targetsApi)
|
|
3374
|
-
}
|
|
3375
|
-
],
|
|
3376
|
-
program: formatProgram(addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl)), "compact"),
|
|
3692
|
+
source_requests: lookup.sourceRequests,
|
|
3693
|
+
warnings: lookup.warnings.length > 0 ? lookup.warnings : undefined,
|
|
3694
|
+
program: formatProgram(lookup.program, "compact"),
|
|
3377
3695
|
breakdown: {
|
|
3378
3696
|
target_counts: readObject(scope.target_counts),
|
|
3379
3697
|
by_target_type: countTargetValues(targets, (target) => stringField(target, "target_type")),
|
|
@@ -3387,9 +3705,11 @@ async function getProgramTargetBreakdown(client, config, input) {
|
|
|
3387
3705
|
total_active_targets: rawTargets.length,
|
|
3388
3706
|
total_after_filters: targets.length,
|
|
3389
3707
|
target_list_mode: input.target_list_mode,
|
|
3390
|
-
group_limit: input.group_limit
|
|
3708
|
+
group_limit: input.group_limit,
|
|
3709
|
+
target_source: "program_targets",
|
|
3710
|
+
detail_endpoint_unavailable: lookup.programDetailUnavailable || undefined
|
|
3391
3711
|
}
|
|
3392
|
-
};
|
|
3712
|
+
});
|
|
3393
3713
|
}
|
|
3394
3714
|
async function getProgramTargetBreakdownFromIndexFallback(client, indexFallback, input) {
|
|
3395
3715
|
const exportLookup = await fetchProgramTargetsWithEndpointFallback(client, input.program_id);
|
|
@@ -3839,14 +4159,13 @@ async function checkWatchlistNewTargets(client, config, input) {
|
|
|
3839
4159
|
});
|
|
3840
4160
|
}
|
|
3841
4161
|
async function getProgramBrief(client, config, input) {
|
|
3842
|
-
const
|
|
3843
|
-
const targetsPromise = client.getProgramTargets(input.program_id);
|
|
4162
|
+
const detailsAndTargetsPromise = fetchProgramDetailsAndTargets(client, config, input.program_id);
|
|
3844
4163
|
const changesPromise = input.include_recent_changes && input.recent_changes_limit > 0
|
|
3845
4164
|
? fetchProgramChanges(client, config, input.program_id, input.recent_changes_limit, true, input.include_ineligible, input.include_out_of_scope)
|
|
3846
4165
|
: Promise.resolve(undefined);
|
|
3847
|
-
const [
|
|
3848
|
-
const candidate = toProgramCandidate(
|
|
3849
|
-
const targetsData = readObject(targetsApi.data);
|
|
4166
|
+
const [lookup, changes] = await Promise.all([detailsAndTargetsPromise, changesPromise]);
|
|
4167
|
+
const candidate = toProgramCandidate(lookup.program, config.webBaseUrl);
|
|
4168
|
+
const targetsData = readObject(lookup.targetsApi.data);
|
|
3850
4169
|
const rawTargets = readArray(targetsData?.targets);
|
|
3851
4170
|
const targets = rawTargets
|
|
3852
4171
|
.map(sanitizeTarget)
|
|
@@ -3858,21 +4177,8 @@ async function getProgramBrief(client, config, input) {
|
|
|
3858
4177
|
const scope = buildTargetScopeSummary(targets, input.target_list_mode, input.target_sample_size, input.include_out_of_scope, input.include_ineligible);
|
|
3859
4178
|
return stripUndefined({
|
|
3860
4179
|
request_id: randomUUID(),
|
|
3861
|
-
source_requests: [
|
|
3862
|
-
|
|
3863
|
-
source: "program",
|
|
3864
|
-
request_id: programApi.requestId,
|
|
3865
|
-
upstream_request_id: programApi.upstreamRequestId,
|
|
3866
|
-
...apiSourceMetadata(programApi)
|
|
3867
|
-
},
|
|
3868
|
-
{
|
|
3869
|
-
source: "program_targets",
|
|
3870
|
-
request_id: targetsApi.requestId,
|
|
3871
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3872
|
-
...apiSourceMetadata(targetsApi)
|
|
3873
|
-
},
|
|
3874
|
-
...(changes?.sourceRequests ?? [])
|
|
3875
|
-
],
|
|
4180
|
+
source_requests: [...lookup.sourceRequests, ...(changes?.sourceRequests ?? [])],
|
|
4181
|
+
warnings: lookup.warnings.length > 0 ? lookup.warnings : undefined,
|
|
3876
4182
|
program: formatProgram(candidate.program, "compact"),
|
|
3877
4183
|
brief: {
|
|
3878
4184
|
rank_factors: rankFactors(candidate),
|
|
@@ -3888,7 +4194,9 @@ async function getProgramBrief(client, config, input) {
|
|
|
3888
4194
|
total_active_targets: rawTargets.length,
|
|
3889
4195
|
total_targets_after_filters: targets.length,
|
|
3890
4196
|
recent_changes_returned: changes?.changes.length ?? 0,
|
|
3891
|
-
target_list_mode: input.target_list_mode
|
|
4197
|
+
target_list_mode: input.target_list_mode,
|
|
4198
|
+
target_source: "program_targets",
|
|
4199
|
+
detail_endpoint_unavailable: lookup.programDetailUnavailable || undefined
|
|
3892
4200
|
}
|
|
3893
4201
|
});
|
|
3894
4202
|
}
|
|
@@ -4485,6 +4793,12 @@ function programResolutionMatch(candidate, query) {
|
|
|
4485
4793
|
reasons: [...reasons]
|
|
4486
4794
|
};
|
|
4487
4795
|
}
|
|
4796
|
+
function hasHighConfidenceProgramResolutionMatch(programs, webBaseUrl, query) {
|
|
4797
|
+
return programs.some((program) => programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score >= 85);
|
|
4798
|
+
}
|
|
4799
|
+
function bestProgramResolutionScore(programs, webBaseUrl, query) {
|
|
4800
|
+
return programs.reduce((best, program) => Math.max(best, programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score), 0);
|
|
4801
|
+
}
|
|
4488
4802
|
function programResolutionSearchQueries(query) {
|
|
4489
4803
|
return programResolutionQueryVariants(query).slice(0, MAX_RESOLVE_SEARCH_QUERIES);
|
|
4490
4804
|
}
|
|
@@ -4492,11 +4806,20 @@ function programResolutionQueryVariants(query) {
|
|
|
4492
4806
|
const normalized = normalizeTag(query);
|
|
4493
4807
|
const meaningfulTokens = [...meaningfulProgramQueryTokens(query)];
|
|
4494
4808
|
const meaningfulPhrase = meaningfulTokens.join(" ");
|
|
4809
|
+
const singleTokenNameVariants = meaningfulTokens.length === 1
|
|
4810
|
+
? [
|
|
4811
|
+
`${meaningfulTokens[0]} network`,
|
|
4812
|
+
`${meaningfulTokens[0]} protocol`,
|
|
4813
|
+
`${meaningfulTokens[0]} bug bounty`,
|
|
4814
|
+
`${meaningfulTokens[0]} bounty`
|
|
4815
|
+
]
|
|
4816
|
+
: [];
|
|
4495
4817
|
const values = [
|
|
4496
4818
|
normalized,
|
|
4497
4819
|
meaningfulPhrase,
|
|
4498
4820
|
meaningfulTokens.join("-"),
|
|
4499
|
-
...meaningfulTokens
|
|
4821
|
+
...meaningfulTokens,
|
|
4822
|
+
...singleTokenNameVariants
|
|
4500
4823
|
];
|
|
4501
4824
|
return uniqueStrings(values.filter((value) => value.length >= 2));
|
|
4502
4825
|
}
|
|
@@ -4621,6 +4944,9 @@ async function findPrograms(client, config, input) {
|
|
|
4621
4944
|
.sort((left, right) => compareProgramCandidates(left, right, input.sort_by));
|
|
4622
4945
|
const candidates = filteredCandidates.slice(0, input.max_results);
|
|
4623
4946
|
const targetSamplePrograms = Math.min(input.target_sample_programs, candidates.length, Math.max(0, budget.remaining));
|
|
4947
|
+
if (input.sort_by === "recently_updated" && input.platforms.length === 0) {
|
|
4948
|
+
addUniqueWarning(warnings, "No platform filter was provided; recently updated ranking considered all platforms returned by the scanned BBRadar pages.");
|
|
4949
|
+
}
|
|
4624
4950
|
if (input.include_target_samples && input.target_sample_programs > targetSamplePrograms) {
|
|
4625
4951
|
warnings.push(`target_sample_programs was reduced from ${input.target_sample_programs} to ${targetSamplePrograms} to stay within the upstream request budget.`);
|
|
4626
4952
|
}
|
|
@@ -4668,6 +4994,7 @@ async function findPrograms(client, config, input) {
|
|
|
4668
4994
|
},
|
|
4669
4995
|
ranking_scope: {
|
|
4670
4996
|
mode: "sampled",
|
|
4997
|
+
platform_filter: input.platforms.length > 0 ? input.platforms : "all_platforms",
|
|
4671
4998
|
max_pages_scanned_per_source: input.max_pages,
|
|
4672
4999
|
upstream_total_pages: collected.totalPagesBySource,
|
|
4673
5000
|
possibly_incomplete: isRankingPossiblyIncomplete(collected.totalPagesBySource, input.max_pages)
|
|
@@ -4711,22 +5038,48 @@ async function collectPrograms(client, options) {
|
|
|
4711
5038
|
}
|
|
4712
5039
|
async function collectProgramsForResolution(client, options) {
|
|
4713
5040
|
const collections = [];
|
|
5041
|
+
const queryCollections = [];
|
|
4714
5042
|
const queries = options.searchQueries.length > 0 ? options.searchQueries : [undefined];
|
|
4715
5043
|
for (const search of queries) {
|
|
4716
5044
|
if (options.budget.remaining <= 0) {
|
|
4717
5045
|
addUniqueWarning(options.warnings, "Upstream request budget was exhausted before all program name search variants could be checked.");
|
|
4718
5046
|
break;
|
|
4719
5047
|
}
|
|
4720
|
-
|
|
5048
|
+
const collection = await collectPrograms(client, {
|
|
4721
5049
|
search,
|
|
4722
5050
|
platforms: options.platforms,
|
|
4723
5051
|
tags: options.tags,
|
|
4724
5052
|
updated_since: undefined,
|
|
4725
5053
|
opportunity_levels: options.opportunity_levels,
|
|
4726
|
-
max_pages:
|
|
5054
|
+
max_pages: 1,
|
|
4727
5055
|
budget: options.budget,
|
|
4728
5056
|
warnings: options.warnings
|
|
4729
|
-
})
|
|
5057
|
+
});
|
|
5058
|
+
collections.push(collection);
|
|
5059
|
+
queryCollections.push({ search, collection });
|
|
5060
|
+
if (options.isHighConfidenceMatch?.(mergeProgramCollections(collections).programs)) {
|
|
5061
|
+
return mergeProgramCollections(collections);
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
if (options.max_pages > 1) {
|
|
5065
|
+
const search = bestResolutionDeepSearch(queryCollections, options.scorePrograms) ?? queries[0];
|
|
5066
|
+
if (options.budget.remaining <= 0) {
|
|
5067
|
+
addUniqueWarning(options.warnings, "Upstream request budget was exhausted before more program name result pages could be checked.");
|
|
5068
|
+
}
|
|
5069
|
+
else if (search !== undefined || queries.length > 0) {
|
|
5070
|
+
const collection = await collectPrograms(client, {
|
|
5071
|
+
search,
|
|
5072
|
+
platforms: options.platforms,
|
|
5073
|
+
tags: options.tags,
|
|
5074
|
+
updated_since: undefined,
|
|
5075
|
+
opportunity_levels: options.opportunity_levels,
|
|
5076
|
+
max_pages: options.max_pages,
|
|
5077
|
+
start_page: 2,
|
|
5078
|
+
budget: options.budget,
|
|
5079
|
+
warnings: options.warnings
|
|
5080
|
+
});
|
|
5081
|
+
collections.push(collection);
|
|
5082
|
+
}
|
|
4730
5083
|
}
|
|
4731
5084
|
if (collections.length === 0) {
|
|
4732
5085
|
return {
|
|
@@ -4739,6 +5092,16 @@ async function collectProgramsForResolution(client, options) {
|
|
|
4739
5092
|
}
|
|
4740
5093
|
return mergeProgramCollections(collections);
|
|
4741
5094
|
}
|
|
5095
|
+
function bestResolutionDeepSearch(queryCollections, scorePrograms) {
|
|
5096
|
+
if (queryCollections.length === 0) {
|
|
5097
|
+
return undefined;
|
|
5098
|
+
}
|
|
5099
|
+
if (!scorePrograms) {
|
|
5100
|
+
return queryCollections[0]?.search;
|
|
5101
|
+
}
|
|
5102
|
+
return [...queryCollections]
|
|
5103
|
+
.sort((left, right) => scorePrograms(right.collection.programs) - scorePrograms(left.collection.programs))[0]?.search;
|
|
5104
|
+
}
|
|
4742
5105
|
function mergeProgramCollections(collections) {
|
|
4743
5106
|
const programs = new Map();
|
|
4744
5107
|
const totalPagesBySource = {};
|
|
@@ -4766,6 +5129,18 @@ async function collectProgramSources(client, sources, options) {
|
|
|
4766
5129
|
return pages;
|
|
4767
5130
|
}
|
|
4768
5131
|
async function collectProgramSource(client, source, sourceIndex, options) {
|
|
5132
|
+
const startPage = Math.max(1, options.start_page ?? 1);
|
|
5133
|
+
if (startPage > 1) {
|
|
5134
|
+
const pageNumbers = Array.from({ length: Math.max(0, options.max_pages - startPage + 1) }, (_, index) => startPage + index);
|
|
5135
|
+
const pages = [];
|
|
5136
|
+
await mapWithConcurrency(pageNumbers, PROGRAM_COLLECTION_CONCURRENCY, async (page) => {
|
|
5137
|
+
const result = await fetchProgramPage(client, source, sourceIndex, page, options);
|
|
5138
|
+
if (result) {
|
|
5139
|
+
pages.push(result);
|
|
5140
|
+
}
|
|
5141
|
+
});
|
|
5142
|
+
return pages;
|
|
5143
|
+
}
|
|
4769
5144
|
const firstPage = await fetchProgramPage(client, source, sourceIndex, 1, options);
|
|
4770
5145
|
if (!firstPage) {
|
|
4771
5146
|
return [];
|
|
@@ -5230,7 +5605,8 @@ Find the best BBRadar program candidates.
|
|
|
5230
5605
|
- Apply tag filters from this JSON string if provided: ${promptJson(args.tags ?? "")}.
|
|
5231
5606
|
- Keep the shortlist to this JSON value: ${promptJson(args.max_programs ?? "10")}.
|
|
5232
5607
|
- Compare freshness, opportunity score, reward range, public report count, target counts, and scope tags.
|
|
5233
|
-
- For
|
|
5608
|
+
- For a user asking for an in-scope or bounty-eligible target list by program name, call get_program_eligible_targets_by_name once and treat its target list as final.
|
|
5609
|
+
- For an already known program_id, call get_program_targets directly for exact target lists. Use get_program_scope_summary or get_program_target_breakdown only when grouped counts are needed.
|
|
5234
5610
|
- Return a ranked list with concise rationale, likely target themes, and BBRadar URLs.
|
|
5235
5611
|
- Stay passive-only; do not recommend scanning, probing, exploit attempts, or direct contact with targets.`));
|
|
5236
5612
|
server.registerPrompt("summarize_program_scope", {
|
|
@@ -5239,7 +5615,7 @@ Find the best BBRadar program candidates.
|
|
|
5239
5615
|
argsSchema: {
|
|
5240
5616
|
program_id: programIdSchema
|
|
5241
5617
|
}
|
|
5242
|
-
}, (args) => promptResult(`Use
|
|
5618
|
+
}, (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.
|
|
5243
5619
|
|
|
5244
5620
|
Summarize the program scope:
|
|
5245
5621
|
- Program name, platform, reward range, public report count, first seen date, last updated date, and BBRadar URL.
|
|
@@ -5260,7 +5636,8 @@ Summarize the program scope:
|
|
|
5260
5636
|
Do not invoke local bug bounty skills, methodology files, worklogs, or non-BBRadar tools unless the user explicitly asks for external methodology.
|
|
5261
5637
|
|
|
5262
5638
|
Use BBRadar MCP data first:
|
|
5263
|
-
- If a program_id is provided, call get_program
|
|
5639
|
+
- If a program_id is provided, call get_program_targets first. Call get_program only if metadata is explicitly needed.
|
|
5640
|
+
- If the user provides a program name and asks for in-scope or bounty-eligible targets, call get_program_eligible_targets_by_name once.
|
|
5264
5641
|
- If no program_id is provided, call get_opportunities and select a small set of candidate programs before planning.
|
|
5265
5642
|
- Focus area JSON string: ${promptJson(args.focus ?? "highest-signal in-scope bounty-eligible targets")}.
|
|
5266
5643
|
|