@bbradar/mcp 0.1.13 → 0.1.19
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/dist/server.js +488 -88
- package/dist/server.js.map +1 -1
- package/dist/url.js +1 -1
- package/dist/url.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,34 @@ 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 = 96;
|
|
36
38
|
const SDK_VERSION = "1.29.0";
|
|
39
|
+
const PROGRAM_ID_PLATFORM_GUESSES = [
|
|
40
|
+
"HackerOne",
|
|
41
|
+
"BugCrowd",
|
|
42
|
+
"Intigriti",
|
|
43
|
+
"HackenProof",
|
|
44
|
+
"YesWeHack",
|
|
45
|
+
"Immunefi",
|
|
46
|
+
"Cantina",
|
|
47
|
+
"Code4rena",
|
|
48
|
+
"Huntr",
|
|
49
|
+
"GObugfree",
|
|
50
|
+
"Bug Bounty Switzerland",
|
|
51
|
+
"Inspectiv",
|
|
52
|
+
"Compass Security",
|
|
53
|
+
"BugRap",
|
|
54
|
+
"IssueHunt",
|
|
55
|
+
"PatchDay",
|
|
56
|
+
"Remedy",
|
|
57
|
+
"Standoff365",
|
|
58
|
+
"Sherlock",
|
|
59
|
+
"CodeHawks"
|
|
60
|
+
];
|
|
61
|
+
const PRIMARY_PROGRAM_ID_PLATFORM_GUESSES = PROGRAM_ID_PLATFORM_GUESSES.slice(0, 8);
|
|
37
62
|
const WEB3_TAGS = ["web3", "crypto", "blockchain", "smart-contract", "smart contract", "defi", "nft", "ethereum", "solana", "solidity"];
|
|
38
63
|
const WEB3_CONTEST_SIGNALS = ["contest", "audit", "competitive", "code4rena", "sherlock", "cantina", "codehawks", "hats", "spearbit", "warden"];
|
|
39
64
|
const VDP_SIGNALS = ["vdp", "disclosure", "responsible disclosure", "vulnerability disclosure", "no bounty", "non-bounty", "no reward"];
|
|
@@ -44,6 +69,15 @@ const PROGRAM_RESOLUTION_STOP_WORDS = new Set([
|
|
|
44
69
|
"targets",
|
|
45
70
|
"scope",
|
|
46
71
|
"scopes",
|
|
72
|
+
"network",
|
|
73
|
+
"networks",
|
|
74
|
+
"protocol",
|
|
75
|
+
"protocols",
|
|
76
|
+
"bug",
|
|
77
|
+
"bugs",
|
|
78
|
+
"bounty",
|
|
79
|
+
"bounties",
|
|
80
|
+
"security",
|
|
47
81
|
"new",
|
|
48
82
|
"latest",
|
|
49
83
|
"recent",
|
|
@@ -138,7 +172,7 @@ function stringListSchema(description = "Optional filter values. Empty or omitte
|
|
|
138
172
|
.describe(description);
|
|
139
173
|
}
|
|
140
174
|
function platformListSchema() {
|
|
141
|
-
return stringListSchema("Optional platform filters such as HackerOne,
|
|
175
|
+
return stringListSchema("Optional platform filters such as HackerOne, BugCrowd, Intigriti, HackenProof, Immunefi, YesWeHack, Cantina, or GObugfree. Empty or omitted means all platforms.");
|
|
142
176
|
}
|
|
143
177
|
function tagListSchema() {
|
|
144
178
|
return stringListSchema("Optional combined scope/language/target-type tags. Empty or omitted means all tags.");
|
|
@@ -194,7 +228,7 @@ const programIdSchema = z
|
|
|
194
228
|
.trim()
|
|
195
229
|
.min(3)
|
|
196
230
|
.max(256)
|
|
197
|
-
.regex(/^[
|
|
231
|
+
.regex(/^[^:/?#\u0000-\u001F\u007F]+:[^?#\u0000-\u001F\u007F]+$/, "Use platform:handle, for example HackerOne:example-handle.")
|
|
198
232
|
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
199
233
|
message: "program_id cannot contain control characters."
|
|
200
234
|
})
|
|
@@ -406,7 +440,7 @@ export function createBbradarServer(client, config) {
|
|
|
406
440
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
407
441
|
max_results: z.number().int().min(1).max(25).default(10),
|
|
408
442
|
output_mode: programListModeSchema.default("compact"),
|
|
409
|
-
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(
|
|
443
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET)
|
|
410
444
|
},
|
|
411
445
|
outputSchema: {
|
|
412
446
|
...programListOutputShape(),
|
|
@@ -439,7 +473,7 @@ export function createBbradarServer(client, config) {
|
|
|
439
473
|
target_list_mode: targetListModeSchema.default("identifiers"),
|
|
440
474
|
max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
441
475
|
output_mode: programListModeSchema.default("compact"),
|
|
442
|
-
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(
|
|
476
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET)
|
|
443
477
|
},
|
|
444
478
|
outputSchema: {
|
|
445
479
|
...apiEnvelopeOutputShape(),
|
|
@@ -462,7 +496,7 @@ export function createBbradarServer(client, config) {
|
|
|
462
496
|
tags: tagListSchema(),
|
|
463
497
|
opportunity_levels: z.array(opportunityLevelSchema).max(4).default([]),
|
|
464
498
|
max_resolve_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
465
|
-
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(
|
|
499
|
+
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET),
|
|
466
500
|
include_out_of_scope: z.boolean().default(false),
|
|
467
501
|
include_ineligible: z.boolean().default(false),
|
|
468
502
|
strict_scope_filter: z.boolean().default(true),
|
|
@@ -2126,14 +2160,13 @@ async function resolveProgramNameIdentifier(client, config, requestedProgramName
|
|
|
2126
2160
|
}
|
|
2127
2161
|
const sourceRequests = readArray(resolution.source_requests).filter((request) => readObject(request) !== undefined);
|
|
2128
2162
|
const warnings = readArray(resolution.warnings).filter((warning) => typeof warning === "string");
|
|
2129
|
-
const fallbackWarning = `Resolved program name ${requestedProgramName} to program_id ${match.resolvedProgramId}.`;
|
|
2130
2163
|
return {
|
|
2131
2164
|
requestedProgramId: requestedProgramName,
|
|
2132
2165
|
resolvedProgramId: match.resolvedProgramId,
|
|
2133
2166
|
query,
|
|
2134
2167
|
reason: "program_name",
|
|
2135
2168
|
sourceRequests,
|
|
2136
|
-
warnings: uniqueStrings(
|
|
2169
|
+
warnings: uniqueStrings(warnings),
|
|
2137
2170
|
resolution
|
|
2138
2171
|
};
|
|
2139
2172
|
}
|
|
@@ -2347,6 +2380,7 @@ async function resolveProgram(client, config, input) {
|
|
|
2347
2380
|
const maxPages = clampLimit("max_pages", input.max_pages, MAX_FIND_PROGRAM_PAGES, warnings);
|
|
2348
2381
|
const searchQueries = programResolutionSearchQueries(input.query);
|
|
2349
2382
|
const collected = await collectProgramsForResolution(client, {
|
|
2383
|
+
query: input.query,
|
|
2350
2384
|
searchQueries,
|
|
2351
2385
|
isHighConfidenceMatch: (programs) => hasHighConfidenceProgramResolutionMatch(programs, config.webBaseUrl, input.query),
|
|
2352
2386
|
scorePrograms: (programs) => bestProgramResolutionScore(programs, config.webBaseUrl, input.query),
|
|
@@ -2357,7 +2391,9 @@ async function resolveProgram(client, config, input) {
|
|
|
2357
2391
|
budget,
|
|
2358
2392
|
warnings
|
|
2359
2393
|
});
|
|
2360
|
-
const
|
|
2394
|
+
const sourceRequests = [...collected.sourceRequests];
|
|
2395
|
+
let targetLookup;
|
|
2396
|
+
let matches = collected.programs
|
|
2361
2397
|
.map((program) => {
|
|
2362
2398
|
const candidate = toProgramCandidate(program, config.webBaseUrl);
|
|
2363
2399
|
const match = programResolutionMatch(candidate, input.query);
|
|
@@ -2366,14 +2402,58 @@ async function resolveProgram(client, config, input) {
|
|
|
2366
2402
|
.filter((entry) => entry.match.score > 0)
|
|
2367
2403
|
.sort((left, right) => right.match.score - left.match.score || compareProgramCandidates(left.candidate, right.candidate, "best_hunt_value"))
|
|
2368
2404
|
.slice(0, input.max_results);
|
|
2405
|
+
if (matches.length === 0 && queryLooksLikeTargetIdentifier(input.query)) {
|
|
2406
|
+
targetLookup = await findProgramsByTarget(client, config, {
|
|
2407
|
+
target_query: input.query,
|
|
2408
|
+
match_mode: "smart",
|
|
2409
|
+
platforms: input.platforms,
|
|
2410
|
+
tags: input.tags,
|
|
2411
|
+
use_api_search: true,
|
|
2412
|
+
max_pages: 1,
|
|
2413
|
+
max_results: input.max_results,
|
|
2414
|
+
include_out_of_scope: false,
|
|
2415
|
+
include_ineligible: false,
|
|
2416
|
+
max_targets_per_program: 3,
|
|
2417
|
+
target_list_mode: "identifiers",
|
|
2418
|
+
upstream_request_budget: Math.max(1, budget.remaining)
|
|
2419
|
+
});
|
|
2420
|
+
sourceRequests.push(...readArray(targetLookup.source_requests).filter((request) => readObject(request) !== undefined));
|
|
2421
|
+
for (const warning of readArray(targetLookup.warnings)) {
|
|
2422
|
+
if (typeof warning === "string") {
|
|
2423
|
+
addUniqueWarning(warnings, warning);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
const targetBudget = readObject(targetLookup.upstream_budget);
|
|
2427
|
+
const targetBudgetUsed = numberField(targetBudget, "used") ?? 0;
|
|
2428
|
+
budget.remaining = Math.max(0, budget.remaining - targetBudgetUsed);
|
|
2429
|
+
matches = readArray(targetLookup.matches)
|
|
2430
|
+
.map((entry) => {
|
|
2431
|
+
const matchEntry = readObject(entry);
|
|
2432
|
+
const program = readObject(matchEntry?.program);
|
|
2433
|
+
if (!program) {
|
|
2434
|
+
return undefined;
|
|
2435
|
+
}
|
|
2436
|
+
return {
|
|
2437
|
+
candidate: toProgramCandidate(program, config.webBaseUrl),
|
|
2438
|
+
match: {
|
|
2439
|
+
score: 75,
|
|
2440
|
+
fields: ["targets"],
|
|
2441
|
+
reasons: ["target search matched query"]
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
})
|
|
2445
|
+
.filter((entry) => entry !== undefined)
|
|
2446
|
+
.slice(0, input.max_results);
|
|
2447
|
+
}
|
|
2369
2448
|
const programs = matches.map((entry) => formatProgram(entry.candidate.program, input.output_mode));
|
|
2370
2449
|
const payload = stripUndefined({
|
|
2371
2450
|
request_id: randomUUID(),
|
|
2372
|
-
source_requests:
|
|
2451
|
+
source_requests: sourceRequests,
|
|
2373
2452
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
2374
2453
|
query: {
|
|
2375
2454
|
text: input.query,
|
|
2376
2455
|
search_queries: searchQueries,
|
|
2456
|
+
target_search_queries: readObject(targetLookup?.filters)?.target_search_queries,
|
|
2377
2457
|
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
2378
2458
|
tags: input.tags.length > 0 ? input.tags : undefined
|
|
2379
2459
|
},
|
|
@@ -2398,7 +2478,8 @@ async function resolveProgram(client, config, input) {
|
|
|
2398
2478
|
meta: {
|
|
2399
2479
|
returned: programs.length,
|
|
2400
2480
|
programs_scanned: collected.programsScanned,
|
|
2401
|
-
upstream_requests_scanned: collected.requestsScanned
|
|
2481
|
+
upstream_requests_scanned: collected.requestsScanned,
|
|
2482
|
+
target_lookup_returned: numberField(readObject(targetLookup?.meta), "returned")
|
|
2402
2483
|
}
|
|
2403
2484
|
});
|
|
2404
2485
|
rememberResolveProgram(config, input, payload);
|
|
@@ -3091,16 +3172,44 @@ function findRecentByType(client, config, input) {
|
|
|
3091
3172
|
}
|
|
3092
3173
|
async function findProgramsByTarget(client, config, input) {
|
|
3093
3174
|
const warnings = [];
|
|
3175
|
+
const budget = {
|
|
3176
|
+
initial: input.upstream_request_budget,
|
|
3177
|
+
remaining: input.upstream_request_budget
|
|
3178
|
+
};
|
|
3179
|
+
const sourceRequests = [];
|
|
3180
|
+
const matches = [];
|
|
3181
|
+
const directHandles = await collectDirectHandlePrograms(client, {
|
|
3182
|
+
query: input.target_query,
|
|
3183
|
+
platforms: input.platforms,
|
|
3184
|
+
budget,
|
|
3185
|
+
warnings,
|
|
3186
|
+
source: "target_query_program_id_guess",
|
|
3187
|
+
maxProgramIds: queryLooksLikeTargetIdentifier(input.target_query) || looksLikeDirectProgramHandleQuery(input.target_query) ? MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS : 4,
|
|
3188
|
+
maxHandleVariants: 1,
|
|
3189
|
+
stopOnFirstMatch: true
|
|
3190
|
+
});
|
|
3191
|
+
sourceRequests.push(...directHandles.sourceRequests);
|
|
3192
|
+
await appendProgramTargetMatches(client, input, budget, warnings, sourceRequests, matches, directHandles.programs.map((program) => toProgramCandidate(program, config.webBaseUrl)));
|
|
3193
|
+
if (matches.length > 0 && input.match_mode === "smart") {
|
|
3194
|
+
return programsByTargetResult(input, warnings, sourceRequests, matches, budget, {
|
|
3195
|
+
source: "direct_program_handle_targets",
|
|
3196
|
+
rankingScope: {
|
|
3197
|
+
mode: "direct_program_handle",
|
|
3198
|
+
possibly_incomplete: false
|
|
3199
|
+
},
|
|
3200
|
+
meta: {
|
|
3201
|
+
returned: matches.length,
|
|
3202
|
+
programs_scanned: directHandles.programsScanned,
|
|
3203
|
+
target_lists_checked: sourceRequests.filter((request) => request.source === "program_targets").length
|
|
3204
|
+
}
|
|
3205
|
+
});
|
|
3206
|
+
}
|
|
3094
3207
|
if (input.use_api_search) {
|
|
3095
3208
|
const directResult = await searchProgramsByTargetApi(client, config, input, warnings);
|
|
3096
3209
|
if (directResult) {
|
|
3097
3210
|
return directResult;
|
|
3098
3211
|
}
|
|
3099
3212
|
}
|
|
3100
|
-
const budget = {
|
|
3101
|
-
initial: input.upstream_request_budget,
|
|
3102
|
-
remaining: input.upstream_request_budget
|
|
3103
|
-
};
|
|
3104
3213
|
const maxPages = clampLimit("max_pages", input.max_pages, MAX_FIND_PROGRAM_PAGES, warnings);
|
|
3105
3214
|
const collected = await collectPrograms(client, {
|
|
3106
3215
|
platforms: input.platforms,
|
|
@@ -3111,11 +3220,27 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3111
3220
|
budget,
|
|
3112
3221
|
warnings
|
|
3113
3222
|
});
|
|
3114
|
-
|
|
3115
|
-
const matches = [];
|
|
3223
|
+
sourceRequests.push(...collected.sourceRequests);
|
|
3116
3224
|
const candidates = collected.programs
|
|
3117
3225
|
.map((program) => toProgramCandidate(program, config.webBaseUrl))
|
|
3118
3226
|
.sort((left, right) => compareProgramCandidates(left, right, "best_hunt_value"));
|
|
3227
|
+
await appendProgramTargetMatches(client, input, budget, warnings, sourceRequests, matches, candidates);
|
|
3228
|
+
return programsByTargetResult(input, warnings, sourceRequests, matches, budget, {
|
|
3229
|
+
source: "sampled_program_targets",
|
|
3230
|
+
rankingScope: {
|
|
3231
|
+
mode: "sampled",
|
|
3232
|
+
max_pages_scanned_per_source: maxPages,
|
|
3233
|
+
upstream_total_pages: collected.totalPagesBySource,
|
|
3234
|
+
possibly_incomplete: isRankingPossiblyIncomplete(collected.totalPagesBySource, maxPages)
|
|
3235
|
+
},
|
|
3236
|
+
meta: {
|
|
3237
|
+
returned: matches.length,
|
|
3238
|
+
programs_scanned: directHandles.programsScanned + collected.programsScanned,
|
|
3239
|
+
target_lists_checked: sourceRequests.filter((request) => request.source === "program_targets").length
|
|
3240
|
+
}
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
async function appendProgramTargetMatches(client, input, budget, warnings, sourceRequests, matches, candidates) {
|
|
3119
3244
|
for (const candidate of candidates) {
|
|
3120
3245
|
if (matches.length >= input.max_results) {
|
|
3121
3246
|
break;
|
|
@@ -3125,7 +3250,9 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3125
3250
|
continue;
|
|
3126
3251
|
}
|
|
3127
3252
|
if (!tryConsumeBudget(budget)) {
|
|
3128
|
-
|
|
3253
|
+
if (matches.length === 0) {
|
|
3254
|
+
addUniqueWarning(warnings, "Upstream request budget was exhausted before all candidate target lists could be checked.");
|
|
3255
|
+
}
|
|
3129
3256
|
break;
|
|
3130
3257
|
}
|
|
3131
3258
|
const targetsApi = await client.getProgramTargets(programId);
|
|
@@ -3157,6 +3284,8 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3157
3284
|
total_targets_checked: targets.length
|
|
3158
3285
|
}));
|
|
3159
3286
|
}
|
|
3287
|
+
}
|
|
3288
|
+
function programsByTargetResult(input, warnings, sourceRequests, matches, budget, options) {
|
|
3160
3289
|
return stripUndefined({
|
|
3161
3290
|
request_id: randomUUID(),
|
|
3162
3291
|
source_requests: sourceRequests,
|
|
@@ -3166,7 +3295,7 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3166
3295
|
match_mode: input.match_mode,
|
|
3167
3296
|
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
3168
3297
|
tags: input.tags.length > 0 ? input.tags : undefined,
|
|
3169
|
-
source:
|
|
3298
|
+
source: options.source,
|
|
3170
3299
|
include_out_of_scope: input.include_out_of_scope,
|
|
3171
3300
|
include_ineligible: input.include_ineligible,
|
|
3172
3301
|
target_list_mode: input.target_list_mode
|
|
@@ -3177,63 +3306,79 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3177
3306
|
used: budget.initial - budget.remaining,
|
|
3178
3307
|
remaining: budget.remaining
|
|
3179
3308
|
},
|
|
3180
|
-
ranking_scope:
|
|
3181
|
-
|
|
3182
|
-
max_pages_scanned_per_source: maxPages,
|
|
3183
|
-
upstream_total_pages: collected.totalPagesBySource,
|
|
3184
|
-
possibly_incomplete: isRankingPossiblyIncomplete(collected.totalPagesBySource, maxPages)
|
|
3185
|
-
},
|
|
3186
|
-
meta: {
|
|
3187
|
-
returned: matches.length,
|
|
3188
|
-
programs_scanned: collected.programsScanned,
|
|
3189
|
-
target_lists_checked: sourceRequests.filter((request) => request.source === "program_targets").length
|
|
3190
|
-
}
|
|
3309
|
+
ranking_scope: options.rankingScope,
|
|
3310
|
+
meta: options.meta
|
|
3191
3311
|
});
|
|
3192
3312
|
}
|
|
3193
3313
|
async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
3194
|
-
|
|
3195
|
-
try {
|
|
3196
|
-
api = await client.searchTargets({
|
|
3197
|
-
q: input.target_query,
|
|
3198
|
-
match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
3199
|
-
platforms: input.platforms,
|
|
3200
|
-
tags: input.tags,
|
|
3201
|
-
include_out_of_scope: input.include_out_of_scope,
|
|
3202
|
-
include_ineligible: input.include_ineligible,
|
|
3203
|
-
page: 1,
|
|
3204
|
-
page_size: Math.max(input.max_results * input.max_targets_per_program, input.max_results)
|
|
3205
|
-
});
|
|
3206
|
-
}
|
|
3207
|
-
catch (error) {
|
|
3208
|
-
if (error instanceof BBRadarApiError && (error.status === 404 || error.status === 405)) {
|
|
3209
|
-
warnings.push("Direct target search API is unavailable; fell back to sampled program target lists.");
|
|
3210
|
-
return undefined;
|
|
3211
|
-
}
|
|
3212
|
-
throw error;
|
|
3213
|
-
}
|
|
3214
|
-
const data = readObject(api.data);
|
|
3215
|
-
const rawMatches = readTargetSearchRows(data);
|
|
3314
|
+
const searchQueries = targetApiSearchQueries(input.target_query);
|
|
3216
3315
|
const grouped = new Map();
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3316
|
+
const sourceRequests = [];
|
|
3317
|
+
let rawMatchesScanned = 0;
|
|
3318
|
+
let lastMeta;
|
|
3319
|
+
for (const searchQuery of searchQueries) {
|
|
3320
|
+
let api;
|
|
3321
|
+
try {
|
|
3322
|
+
api = await client.searchTargets({
|
|
3323
|
+
q: searchQuery,
|
|
3324
|
+
match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
3325
|
+
platforms: input.platforms,
|
|
3326
|
+
tags: input.tags,
|
|
3327
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
3328
|
+
include_ineligible: input.include_ineligible,
|
|
3329
|
+
page: 1,
|
|
3330
|
+
page_size: Math.max(input.max_results * input.max_targets_per_program, input.max_results)
|
|
3331
|
+
});
|
|
3223
3332
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3333
|
+
catch (error) {
|
|
3334
|
+
if (error instanceof BBRadarApiError && (error.status === 404 || error.status === 405)) {
|
|
3335
|
+
warnings.push("Direct target search API is unavailable; fell back to sampled program target lists.");
|
|
3336
|
+
return undefined;
|
|
3337
|
+
}
|
|
3338
|
+
throw error;
|
|
3230
3339
|
}
|
|
3231
|
-
|
|
3232
|
-
|
|
3340
|
+
const data = readObject(api.data);
|
|
3341
|
+
const rawMatches = readTargetSearchRows(data);
|
|
3342
|
+
rawMatchesScanned += rawMatches.length;
|
|
3343
|
+
lastMeta = readObject(sanitizeJson(data?.meta)) ?? lastMeta;
|
|
3344
|
+
sourceRequests.push({
|
|
3345
|
+
source: "target_search",
|
|
3346
|
+
query: searchQuery,
|
|
3347
|
+
api_match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
3348
|
+
request_id: api.requestId,
|
|
3349
|
+
upstream_request_id: api.upstreamRequestId,
|
|
3350
|
+
...apiSourceMetadata(api)
|
|
3351
|
+
});
|
|
3352
|
+
for (const row of rawMatches) {
|
|
3353
|
+
const rowProgram = sanitizeProgram(readObject(row.program) ?? row, config.webBaseUrl);
|
|
3354
|
+
const rowProgramId = stringField(rowProgram, "id") ?? stringField(row, "program_id");
|
|
3355
|
+
const targets = targetSearchRowTargets(row);
|
|
3356
|
+
for (const targetSource of targets) {
|
|
3357
|
+
const target = sanitizeTarget(targetSource);
|
|
3358
|
+
const nestedProgram = sanitizeProgram(readObject(targetSource.program), config.webBaseUrl);
|
|
3359
|
+
const program = stringField(nestedProgram, "id") ? nestedProgram : rowProgram;
|
|
3360
|
+
const programId = stringField(program, "id") ?? rowProgramId ?? stringField(targetSource, "program_id");
|
|
3361
|
+
if (!programId || !targetIdentifier(target)) {
|
|
3362
|
+
continue;
|
|
3363
|
+
}
|
|
3364
|
+
if (!targetHasAllowedScope(target, {
|
|
3365
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
3366
|
+
include_ineligible: input.include_ineligible,
|
|
3367
|
+
strict_scope_filter: false
|
|
3368
|
+
})) {
|
|
3369
|
+
continue;
|
|
3370
|
+
}
|
|
3371
|
+
if (!targetMatchesQuery(target, input.target_query, input.match_mode)) {
|
|
3372
|
+
continue;
|
|
3373
|
+
}
|
|
3374
|
+
const existing = grouped.get(programId) ?? { program: addProgramResourceLinks({ ...program, id: programId }), targets: [] };
|
|
3375
|
+
existing.targets.push(target);
|
|
3376
|
+
grouped.set(programId, existing);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
if (grouped.size >= input.max_results) {
|
|
3380
|
+
break;
|
|
3233
3381
|
}
|
|
3234
|
-
const existing = grouped.get(programId) ?? { program: { ...program, id: programId }, targets: [] };
|
|
3235
|
-
existing.targets.push(target);
|
|
3236
|
-
grouped.set(programId, existing);
|
|
3237
3382
|
}
|
|
3238
3383
|
const matches = [...grouped.values()].slice(0, input.max_results).map((entry) => stripUndefined({
|
|
3239
3384
|
program: formatProgram(entry.program, "compact"),
|
|
@@ -3245,21 +3390,13 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
3245
3390
|
warnings.push("Direct target search returned no smart matches; fell back to sampled program target lists.");
|
|
3246
3391
|
return undefined;
|
|
3247
3392
|
}
|
|
3248
|
-
const sanitizedMeta = readObject(sanitizeJson(data?.meta));
|
|
3249
3393
|
return stripUndefined({
|
|
3250
3394
|
request_id: randomUUID(),
|
|
3251
|
-
source_requests:
|
|
3252
|
-
{
|
|
3253
|
-
source: "target_search",
|
|
3254
|
-
api_match_mode: upstreamTargetMatchMode(input.match_mode),
|
|
3255
|
-
request_id: api.requestId,
|
|
3256
|
-
upstream_request_id: api.upstreamRequestId,
|
|
3257
|
-
...apiSourceMetadata(api)
|
|
3258
|
-
}
|
|
3259
|
-
],
|
|
3395
|
+
source_requests: sourceRequests,
|
|
3260
3396
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
3261
3397
|
filters: {
|
|
3262
3398
|
target_query: input.target_query,
|
|
3399
|
+
target_search_queries: searchQueries.length > 1 ? searchQueries : undefined,
|
|
3263
3400
|
match_mode: input.match_mode,
|
|
3264
3401
|
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
3265
3402
|
tags: input.tags.length > 0 ? input.tags : undefined,
|
|
@@ -3271,19 +3408,52 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
3271
3408
|
matches,
|
|
3272
3409
|
programs: matches.map((match) => readObject(match.program)).filter((program) => program !== undefined),
|
|
3273
3410
|
meta: stripUndefined({
|
|
3274
|
-
...(
|
|
3411
|
+
...(lastMeta ?? {}),
|
|
3275
3412
|
returned: matches.length,
|
|
3276
|
-
target_search_rows_scanned:
|
|
3413
|
+
target_search_rows_scanned: rawMatchesScanned
|
|
3277
3414
|
})
|
|
3278
3415
|
});
|
|
3279
3416
|
}
|
|
3280
3417
|
function readTargetSearchRows(data) {
|
|
3281
|
-
return [data?.results, data?.matches, data?.targets]
|
|
3418
|
+
return [data?.results, data?.matches, data?.programs, data?.targets]
|
|
3282
3419
|
.map(readArray)
|
|
3283
3420
|
.find((rows) => rows.length > 0)
|
|
3284
3421
|
?.map((row) => readObject(row))
|
|
3285
3422
|
.filter((row) => row !== undefined) ?? [];
|
|
3286
3423
|
}
|
|
3424
|
+
function targetSearchRowTargets(row) {
|
|
3425
|
+
const directTarget = readObject(row.target);
|
|
3426
|
+
const arrays = [
|
|
3427
|
+
row.targets,
|
|
3428
|
+
row.matched_targets,
|
|
3429
|
+
row.matching_targets,
|
|
3430
|
+
row.target_matches,
|
|
3431
|
+
row.scope
|
|
3432
|
+
]
|
|
3433
|
+
.flatMap(readArray)
|
|
3434
|
+
.map(targetSearchEntryToTarget)
|
|
3435
|
+
.filter((target) => target !== undefined);
|
|
3436
|
+
if (directTarget) {
|
|
3437
|
+
return [directTarget, ...arrays];
|
|
3438
|
+
}
|
|
3439
|
+
if (arrays.length > 0) {
|
|
3440
|
+
return arrays;
|
|
3441
|
+
}
|
|
3442
|
+
if (targetIdentifier(row)) {
|
|
3443
|
+
return [row];
|
|
3444
|
+
}
|
|
3445
|
+
return [];
|
|
3446
|
+
}
|
|
3447
|
+
function targetSearchEntryToTarget(entry) {
|
|
3448
|
+
if (typeof entry === "string") {
|
|
3449
|
+
return { identifier: entry };
|
|
3450
|
+
}
|
|
3451
|
+
const object = readObject(entry);
|
|
3452
|
+
if (!object) {
|
|
3453
|
+
return undefined;
|
|
3454
|
+
}
|
|
3455
|
+
return readObject(object.target) ?? object;
|
|
3456
|
+
}
|
|
3287
3457
|
function rewardThresholdFilters(minReward, mode) {
|
|
3288
3458
|
return {
|
|
3289
3459
|
min_bounty_min: minReward !== undefined && mode === "min_at_least" ? minReward : undefined,
|
|
@@ -5199,7 +5369,8 @@ function programResolutionMatch(candidate, query) {
|
|
|
5199
5369
|
};
|
|
5200
5370
|
}
|
|
5201
5371
|
function hasHighConfidenceProgramResolutionMatch(programs, webBaseUrl, query) {
|
|
5202
|
-
|
|
5372
|
+
const threshold = looksLikeDirectProgramHandleQuery(query) ? 95 : 85;
|
|
5373
|
+
return programs.some((program) => programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score >= threshold);
|
|
5203
5374
|
}
|
|
5204
5375
|
function bestProgramResolutionScore(programs, webBaseUrl, query) {
|
|
5205
5376
|
return programs.reduce((best, program) => Math.max(best, programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score), 0);
|
|
@@ -5210,6 +5381,7 @@ function programResolutionSearchQueries(query) {
|
|
|
5210
5381
|
function programResolutionQueryVariants(query) {
|
|
5211
5382
|
const normalized = normalizeTag(query);
|
|
5212
5383
|
const meaningfulTokens = [...meaningfulProgramQueryTokens(query)];
|
|
5384
|
+
const searchTokens = meaningfulTokens.filter((token) => token.length >= 3);
|
|
5213
5385
|
const meaningfulPhrase = meaningfulTokens.join(" ");
|
|
5214
5386
|
const singleTokenNameVariants = meaningfulTokens.length === 1
|
|
5215
5387
|
? [
|
|
@@ -5223,7 +5395,7 @@ function programResolutionQueryVariants(query) {
|
|
|
5223
5395
|
normalized,
|
|
5224
5396
|
meaningfulPhrase,
|
|
5225
5397
|
meaningfulTokens.join("-"),
|
|
5226
|
-
...
|
|
5398
|
+
...searchTokens,
|
|
5227
5399
|
...singleTokenNameVariants
|
|
5228
5400
|
];
|
|
5229
5401
|
return uniqueStrings(values.filter((value) => value.length >= 2));
|
|
@@ -5451,9 +5623,30 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5451
5623
|
const collections = [];
|
|
5452
5624
|
const queryCollections = [];
|
|
5453
5625
|
const queries = options.searchQueries.length > 0 ? options.searchQueries : [undefined];
|
|
5626
|
+
const primaryDirectPlatforms = options.platforms.length > 0 ? options.platforms : PRIMARY_PROGRAM_ID_PLATFORM_GUESSES;
|
|
5627
|
+
const primaryDirectProgramIds = new Set(directProgramIdCandidates(options.query, primaryDirectPlatforms, { maxHandleVariants: 1 }));
|
|
5628
|
+
if (primaryDirectProgramIds.size > 0 && options.budget.remaining > 0) {
|
|
5629
|
+
const primaryDirectHandles = await collectDirectHandlePrograms(client, {
|
|
5630
|
+
query: options.query,
|
|
5631
|
+
platforms: primaryDirectPlatforms,
|
|
5632
|
+
budget: options.budget,
|
|
5633
|
+
warnings: options.warnings,
|
|
5634
|
+
source: "program_id_handle_guess",
|
|
5635
|
+
maxProgramIds: MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS,
|
|
5636
|
+
maxHandleVariants: 1,
|
|
5637
|
+
acceptEmptyTargetsAsPrograms: true,
|
|
5638
|
+
stopOnFirstMatch: true
|
|
5639
|
+
});
|
|
5640
|
+
if (primaryDirectHandles.programs.length > 0 || primaryDirectHandles.requestsScanned > 0) {
|
|
5641
|
+
collections.push(primaryDirectHandles);
|
|
5642
|
+
}
|
|
5643
|
+
if (primaryDirectHandles.programs.length > 0 && options.isHighConfidenceMatch?.(mergeProgramCollections(collections).programs)) {
|
|
5644
|
+
return mergeProgramCollections(collections);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5454
5647
|
for (const search of queries) {
|
|
5455
5648
|
if (options.budget.remaining <= 0) {
|
|
5456
|
-
addUniqueWarning(options.warnings, "
|
|
5649
|
+
addUniqueWarning(options.warnings, "Program name search stopped at the request budget; pass a higher upstream_request_budget for a deeper resolution attempt.");
|
|
5457
5650
|
break;
|
|
5458
5651
|
}
|
|
5459
5652
|
const collection = await collectPrograms(client, {
|
|
@@ -5472,6 +5665,25 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5472
5665
|
return mergeProgramCollections(collections);
|
|
5473
5666
|
}
|
|
5474
5667
|
}
|
|
5668
|
+
if (options.budget.remaining > 0) {
|
|
5669
|
+
const directHandles = await collectDirectHandlePrograms(client, {
|
|
5670
|
+
query: options.query,
|
|
5671
|
+
platforms: options.platforms,
|
|
5672
|
+
budget: options.budget,
|
|
5673
|
+
warnings: options.warnings,
|
|
5674
|
+
source: "program_id_handle_guess",
|
|
5675
|
+
skipProgramIds: primaryDirectProgramIds,
|
|
5676
|
+
maxHandleVariants: 4,
|
|
5677
|
+
acceptEmptyTargetsAsPrograms: true,
|
|
5678
|
+
stopOnFirstMatch: true
|
|
5679
|
+
});
|
|
5680
|
+
if (directHandles.programs.length > 0 || directHandles.requestsScanned > 0) {
|
|
5681
|
+
collections.push(directHandles);
|
|
5682
|
+
}
|
|
5683
|
+
if (directHandles.programs.length > 0 && options.isHighConfidenceMatch?.(mergeProgramCollections(collections).programs)) {
|
|
5684
|
+
return mergeProgramCollections(collections);
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5475
5687
|
if (options.max_pages > 1) {
|
|
5476
5688
|
const search = bestResolutionDeepSearch(queryCollections, options.scorePrograms) ?? queries[0];
|
|
5477
5689
|
if (options.budget.remaining <= 0) {
|
|
@@ -5532,6 +5744,157 @@ function mergeProgramCollections(collections) {
|
|
|
5532
5744
|
totalPagesBySource
|
|
5533
5745
|
};
|
|
5534
5746
|
}
|
|
5747
|
+
async function collectDirectHandlePrograms(client, options) {
|
|
5748
|
+
const programIds = directProgramIdCandidates(options.query, options.platforms, { maxHandleVariants: options.maxHandleVariants })
|
|
5749
|
+
.filter((programId) => !options.skipProgramIds?.has(programId))
|
|
5750
|
+
.slice(0, options.maxProgramIds ?? MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS);
|
|
5751
|
+
const programs = [];
|
|
5752
|
+
const sourceRequests = [];
|
|
5753
|
+
let requestsScanned = 0;
|
|
5754
|
+
for (const programId of programIds) {
|
|
5755
|
+
requestsScanned += 1;
|
|
5756
|
+
try {
|
|
5757
|
+
const targetsApi = await client.getProgramTargets(programId);
|
|
5758
|
+
const targetsData = readObject(targetsApi.data);
|
|
5759
|
+
const hasTargetsArray = Array.isArray(targetsData?.targets);
|
|
5760
|
+
const targets = readArray(targetsData?.targets).map(sanitizeTarget);
|
|
5761
|
+
const programEndpointExists = targets.length > 0 || (options.acceptEmptyTargetsAsPrograms === true && hasTargetsArray);
|
|
5762
|
+
if (programEndpointExists) {
|
|
5763
|
+
programs.push(syntheticProgramFromProgramId(programId, targets));
|
|
5764
|
+
}
|
|
5765
|
+
sourceRequests.push({
|
|
5766
|
+
source: options.source,
|
|
5767
|
+
endpoint: "program_targets",
|
|
5768
|
+
program_id: programId,
|
|
5769
|
+
request_id: targetsApi.requestId,
|
|
5770
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
5771
|
+
...apiSourceMetadata(targetsApi)
|
|
5772
|
+
});
|
|
5773
|
+
if (programEndpointExists && options.stopOnFirstMatch) {
|
|
5774
|
+
break;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
catch (error) {
|
|
5778
|
+
if (error instanceof BBRadarApiError && error.status === 404) {
|
|
5779
|
+
sourceRequests.push({
|
|
5780
|
+
source: options.source,
|
|
5781
|
+
endpoint: "program_targets",
|
|
5782
|
+
program_id: programId,
|
|
5783
|
+
request_id: error.requestId,
|
|
5784
|
+
upstream_request_id: error.upstreamRequestId,
|
|
5785
|
+
status: error.status
|
|
5786
|
+
});
|
|
5787
|
+
continue;
|
|
5788
|
+
}
|
|
5789
|
+
throw error;
|
|
5790
|
+
}
|
|
5791
|
+
}
|
|
5792
|
+
return {
|
|
5793
|
+
programs,
|
|
5794
|
+
sourceRequests,
|
|
5795
|
+
requestsScanned,
|
|
5796
|
+
programsScanned: programs.length,
|
|
5797
|
+
totalPagesBySource: programs.length > 0 ? { [options.source]: 1 } : {}
|
|
5798
|
+
};
|
|
5799
|
+
}
|
|
5800
|
+
function syntheticProgramFromProgramId(programId, targets) {
|
|
5801
|
+
const separatorIndex = programId.indexOf(":");
|
|
5802
|
+
const platform = separatorIndex >= 0 ? programId.slice(0, separatorIndex) : undefined;
|
|
5803
|
+
const handle = separatorIndex >= 0 ? programId.slice(separatorIndex + 1) : programId;
|
|
5804
|
+
const displayHandle = handle.split("/").filter(Boolean).at(-1) ?? handle;
|
|
5805
|
+
const scopeTags = uniqueStrings(targets.flatMap((target) => readStringArrayField(target, "scope_tags")));
|
|
5806
|
+
return stripUndefined({
|
|
5807
|
+
id: programId,
|
|
5808
|
+
handle,
|
|
5809
|
+
name: titleizeProgramHandle(displayHandle),
|
|
5810
|
+
platform,
|
|
5811
|
+
scope_tags: scopeTags,
|
|
5812
|
+
target_counts: {
|
|
5813
|
+
total: targets.length,
|
|
5814
|
+
in_scope: targets.filter((target) => target.in_scope === true).length,
|
|
5815
|
+
out_of_scope: targets.filter((target) => target.in_scope === false).length,
|
|
5816
|
+
eligible_for_bounty: targets.filter((target) => target.eligible_for_bounty === true).length,
|
|
5817
|
+
wildcard: targets.filter(targetIsWildcard).length
|
|
5818
|
+
}
|
|
5819
|
+
});
|
|
5820
|
+
}
|
|
5821
|
+
function titleizeProgramHandle(handle) {
|
|
5822
|
+
return handle
|
|
5823
|
+
.split(/[-_.\s]+/)
|
|
5824
|
+
.filter(Boolean)
|
|
5825
|
+
.map((part) => part.slice(0, 1).toUpperCase() + part.slice(1))
|
|
5826
|
+
.join(" ");
|
|
5827
|
+
}
|
|
5828
|
+
function directProgramIdCandidates(query, platforms, options = {}) {
|
|
5829
|
+
const platformGuesses = (platforms.length > 0 ? platforms : PROGRAM_ID_PLATFORM_GUESSES).map(canonicalProgramIdPlatform);
|
|
5830
|
+
const handles = directProgramHandleVariants(query).slice(0, options.maxHandleVariants ?? Number.POSITIVE_INFINITY);
|
|
5831
|
+
return uniqueStrings(handles.flatMap((handle) => platformGuesses.flatMap((platform) => normalizeTag(platform) === "bugcrowd"
|
|
5832
|
+
? [`${platform}:engagements/${handle}`, `${platform}:${handle}`]
|
|
5833
|
+
: [`${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
|
+
immunefi: "Immunefi",
|
|
5844
|
+
cantina: "Cantina",
|
|
5845
|
+
code4rena: "Code4rena",
|
|
5846
|
+
huntr: "Huntr",
|
|
5847
|
+
gobugfree: "GObugfree",
|
|
5848
|
+
bugbountyswitzerland: "Bug Bounty Switzerland",
|
|
5849
|
+
inspectiv: "Inspectiv",
|
|
5850
|
+
compasssecurity: "Compass Security",
|
|
5851
|
+
bugrap: "BugRap",
|
|
5852
|
+
issuehunt: "IssueHunt",
|
|
5853
|
+
patchday: "PatchDay",
|
|
5854
|
+
remedy: "Remedy",
|
|
5855
|
+
standoff365: "Standoff365",
|
|
5856
|
+
sherlock: "Sherlock",
|
|
5857
|
+
codehawks: "CodeHawks"
|
|
5858
|
+
};
|
|
5859
|
+
return knownPlatforms[normalized] ?? platform.trim();
|
|
5860
|
+
}
|
|
5861
|
+
function directProgramHandleVariants(query) {
|
|
5862
|
+
const handleText = query.includes(":") ? query.slice(query.indexOf(":") + 1) : query;
|
|
5863
|
+
const normalized = normalizeTargetIdentifierForMatch(handleText);
|
|
5864
|
+
const literal = directProgramLiteralHandleVariant(handleText);
|
|
5865
|
+
const dashed = normalized.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5866
|
+
const compact = dashed.replace(/-/g, "");
|
|
5867
|
+
const tokens = [...meaningfulProgramQueryTokens(handleText)];
|
|
5868
|
+
const brandTokens = targetBrandTokens(handleText);
|
|
5869
|
+
const literalVariants = [literal, dashed, compact, tokens.join("-"), tokens.join("")];
|
|
5870
|
+
const values = queryLooksLikeTargetIdentifier(handleText) ? [...brandTokens, ...literalVariants] : [...literalVariants, ...brandTokens];
|
|
5871
|
+
return uniqueStrings(values).filter(isDirectProgramHandleVariant);
|
|
5872
|
+
}
|
|
5873
|
+
function looksLikeDirectProgramHandleQuery(query) {
|
|
5874
|
+
const handleText = query.includes(":") ? query.slice(query.indexOf(":") + 1) : query;
|
|
5875
|
+
return isDirectProgramHandleVariant(directProgramLiteralHandleVariant(handleText));
|
|
5876
|
+
}
|
|
5877
|
+
function directProgramLiteralHandleVariant(value) {
|
|
5878
|
+
const normalized = value
|
|
5879
|
+
.trim()
|
|
5880
|
+
.replace(/\\/g, "/")
|
|
5881
|
+
.replace(/^['"`<]+|['"`>]+$/g, "")
|
|
5882
|
+
.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "")
|
|
5883
|
+
.replace(/^www\./i, "");
|
|
5884
|
+
const queryStart = normalized.search(/[?#]/);
|
|
5885
|
+
return (queryStart >= 0 ? normalized.slice(0, queryStart) : normalized).replace(/\/+$/, "");
|
|
5886
|
+
}
|
|
5887
|
+
function isDirectProgramHandleVariant(value) {
|
|
5888
|
+
if (!value) {
|
|
5889
|
+
return false;
|
|
5890
|
+
}
|
|
5891
|
+
if (value.length < 2 || value.length > 150 || /[\u0000-\u001F\u007F?#:]/.test(value)) {
|
|
5892
|
+
return false;
|
|
5893
|
+
}
|
|
5894
|
+
return value
|
|
5895
|
+
.split("/")
|
|
5896
|
+
.every((segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/^[\s.]+$/.test(segment));
|
|
5897
|
+
}
|
|
5535
5898
|
async function collectProgramSources(client, sources, options) {
|
|
5536
5899
|
const pages = [];
|
|
5537
5900
|
await mapWithConcurrency(sources.map((source, sourceIndex) => ({ source, sourceIndex })), PROGRAM_COLLECTION_CONCURRENCY, async ({ source, sourceIndex }) => {
|
|
@@ -6299,13 +6662,13 @@ function targetMatchesQuery(target, query, mode) {
|
|
|
6299
6662
|
if (mode === "wildcard") {
|
|
6300
6663
|
return profile.values.some((value) => queryValues.some((queryValue) => targetWildcardMatch(value, queryValue)));
|
|
6301
6664
|
}
|
|
6302
|
-
const containsMatch = profile.values.some((value) => queryValues.some((queryValue) => value
|
|
6665
|
+
const containsMatch = profile.values.some((value) => queryValues.some((queryValue) => targetContainsMatch(value, queryValue)));
|
|
6303
6666
|
if (mode === "contains") {
|
|
6304
6667
|
return containsMatch;
|
|
6305
6668
|
}
|
|
6306
6669
|
return (containsMatch ||
|
|
6307
6670
|
profile.values.some((value) => queryValues.some((queryValue) => targetSuffixMatch(value, queryValue) || targetWildcardMatch(value, queryValue))) ||
|
|
6308
|
-
targetTokensMatch(profile.tokens, query));
|
|
6671
|
+
(!queryLooksLikeTargetIdentifier(query) && targetTokensMatch(profile.tokens, query)));
|
|
6309
6672
|
}
|
|
6310
6673
|
function upstreamTargetMatchMode(mode) {
|
|
6311
6674
|
return mode === "smart" ? "contains" : mode;
|
|
@@ -6343,7 +6706,24 @@ function targetSearchProfile(target) {
|
|
|
6343
6706
|
};
|
|
6344
6707
|
}
|
|
6345
6708
|
function targetQueryVariants(query) {
|
|
6346
|
-
|
|
6709
|
+
const variants = targetStringVariants(query);
|
|
6710
|
+
if (!queryLooksLikeTargetIdentifier(query)) {
|
|
6711
|
+
return variants;
|
|
6712
|
+
}
|
|
6713
|
+
return variants.filter(targetValueCanContainQuery);
|
|
6714
|
+
}
|
|
6715
|
+
function targetApiSearchQueries(query) {
|
|
6716
|
+
const normalized = normalizeTargetIdentifierForMatch(query);
|
|
6717
|
+
const brandDomains = targetBrandTokens(query).flatMap((token) => [`${token}.com`, `*.${token}.com`]);
|
|
6718
|
+
return uniqueStrings([
|
|
6719
|
+
normalized,
|
|
6720
|
+
...targetQueryVariants(query),
|
|
6721
|
+
...brandDomains
|
|
6722
|
+
].filter((entry) => entry !== undefined && entry.length >= 2)).slice(0, MAX_RESOLVE_SEARCH_QUERIES);
|
|
6723
|
+
}
|
|
6724
|
+
function queryLooksLikeTargetIdentifier(query) {
|
|
6725
|
+
const normalized = normalizeTargetIdentifierForMatch(query);
|
|
6726
|
+
return /[./*:]/.test(normalized);
|
|
6347
6727
|
}
|
|
6348
6728
|
function targetStringVariants(value) {
|
|
6349
6729
|
const normalized = normalizeTargetIdentifierForMatch(value);
|
|
@@ -6373,6 +6753,15 @@ function targetSuffixMatch(value, query) {
|
|
|
6373
6753
|
function targetWildcardMatch(value, query) {
|
|
6374
6754
|
return wildcardTargetMatch(query, value) || wildcardTargetMatch(value, query);
|
|
6375
6755
|
}
|
|
6756
|
+
function targetContainsMatch(value, query) {
|
|
6757
|
+
if (value.includes(query)) {
|
|
6758
|
+
return true;
|
|
6759
|
+
}
|
|
6760
|
+
return targetValueCanContainQuery(value) && query.includes(value);
|
|
6761
|
+
}
|
|
6762
|
+
function targetValueCanContainQuery(value) {
|
|
6763
|
+
return value.includes(".") || value.includes("/") || value.includes("*") || value.includes(":");
|
|
6764
|
+
}
|
|
6376
6765
|
function targetTokensMatch(targetTokens, query) {
|
|
6377
6766
|
const queryTokens = [...targetTokenSet(query)];
|
|
6378
6767
|
if (queryTokens.length === 0) {
|
|
@@ -6389,6 +6778,17 @@ function targetTokenSet(value) {
|
|
|
6389
6778
|
const meaningfulTokens = tokens.filter((token) => !TARGET_QUERY_STOP_WORDS.has(token));
|
|
6390
6779
|
return new Set(meaningfulTokens.length > 0 ? meaningfulTokens : tokens);
|
|
6391
6780
|
}
|
|
6781
|
+
function targetBrandTokens(value) {
|
|
6782
|
+
const normalized = stripLeadingTargetWildcard(normalizeTargetIdentifierForMatch(value));
|
|
6783
|
+
const host = targetHostVariant(normalized) ?? (normalized.includes(".") && !normalized.includes("/") ? normalized : undefined);
|
|
6784
|
+
const hostBrand = host
|
|
6785
|
+
?.split(".")
|
|
6786
|
+
.filter(Boolean)
|
|
6787
|
+
.slice(-2, -1)
|
|
6788
|
+
.find((token) => token.length >= 3 && !TARGET_QUERY_STOP_WORDS.has(token));
|
|
6789
|
+
const textTokens = [...targetTokenSet(normalized)].filter((token) => token.length >= 4);
|
|
6790
|
+
return uniqueStrings([hostBrand, ...textTokens].filter((entry) => entry !== undefined));
|
|
6791
|
+
}
|
|
6392
6792
|
function normalizeTargetIdentifierForMatch(value) {
|
|
6393
6793
|
const normalized = value
|
|
6394
6794
|
.trim()
|