@bbradar/mcp 0.1.19 → 0.1.20
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 +37 -0
- package/dist/bbradarClient.d.ts +108 -2
- package/dist/bbradarClient.js +490 -45
- package/dist/bbradarClient.js.map +1 -1
- package/dist/cache.d.ts +5 -1
- package/dist/cache.js +22 -4
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/server.js +533 -113
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -5,7 +5,7 @@ import { BBRadarApiError } from "./bbradarClient.js";
|
|
|
5
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
|
-
const SERVER_VERSION = "0.1.
|
|
8
|
+
const SERVER_VERSION = "0.1.19";
|
|
9
9
|
const MAX_PROGRAMS_PER_SEARCH = 100;
|
|
10
10
|
const MAX_TARGET_EXPORT = 500;
|
|
11
11
|
const MAX_RECENT_CHANGES = 100;
|
|
@@ -20,7 +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 =
|
|
23
|
+
const DEFAULT_RESOLVE_UPSTREAM_REQUEST_BUDGET = 12;
|
|
24
24
|
const MAX_ACCEPTED_NUMERIC_LIMIT = 1_000;
|
|
25
25
|
const PROGRAM_COLLECTION_CONCURRENCY = 4;
|
|
26
26
|
const TARGET_SAMPLE_CONCURRENCY = 3;
|
|
@@ -31,11 +31,29 @@ const MAX_FILTER_TARGET_SAMPLE_PROGRAMS = 25;
|
|
|
31
31
|
const MAX_LOCAL_EXPORT_RESOURCES = 25;
|
|
32
32
|
const EXPORT_PREVIEW_LIMIT = 25;
|
|
33
33
|
const STALE_PROGRAM_ID_RESOLVE_PAGES = 1;
|
|
34
|
-
const STALE_PROGRAM_ID_RESOLVE_BUDGET =
|
|
34
|
+
const STALE_PROGRAM_ID_RESOLVE_BUDGET = 12;
|
|
35
35
|
const PROGRAM_FALLBACK_CACHE_MAX_ENTRIES = 200;
|
|
36
36
|
const MAX_RESOLVE_SEARCH_QUERIES = 4;
|
|
37
|
+
const FAST_RESOLVE_SEARCH_QUERIES = 2;
|
|
37
38
|
const MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS = 96;
|
|
38
39
|
const SDK_VERSION = "1.29.0";
|
|
40
|
+
const CORE_TOOL_NAMES = new Set([
|
|
41
|
+
"search_programs",
|
|
42
|
+
"get_program",
|
|
43
|
+
"resolve_program",
|
|
44
|
+
"run_program_name_action",
|
|
45
|
+
"get_program_eligible_targets_by_name",
|
|
46
|
+
"get_program_targets",
|
|
47
|
+
"get_recent_changes",
|
|
48
|
+
"get_latest_added_targets",
|
|
49
|
+
"get_opportunities",
|
|
50
|
+
"find_programs",
|
|
51
|
+
"find_programs_by_target",
|
|
52
|
+
"list_filters",
|
|
53
|
+
"get_bbradar_guide",
|
|
54
|
+
"get_mcp_status",
|
|
55
|
+
"export_targets"
|
|
56
|
+
]);
|
|
39
57
|
const PROGRAM_ID_PLATFORM_GUESSES = [
|
|
40
58
|
"HackerOne",
|
|
41
59
|
"BugCrowd",
|
|
@@ -346,12 +364,26 @@ const programCandidatePresetInputSchema = {
|
|
|
346
364
|
output_mode: programListModeSchema.default("compact"),
|
|
347
365
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(30)
|
|
348
366
|
};
|
|
367
|
+
function createProfiledToolRegistrar(server, profile) {
|
|
368
|
+
return (name, toolConfig, callback) => {
|
|
369
|
+
const registeredTool = server.registerTool(name, toolConfig, callback);
|
|
370
|
+
if (profile === "core" && !CORE_TOOL_NAMES.has(name)) {
|
|
371
|
+
registeredTool.disable();
|
|
372
|
+
}
|
|
373
|
+
return registeredTool;
|
|
374
|
+
};
|
|
375
|
+
}
|
|
349
376
|
export function createBbradarServer(client, config) {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
377
|
+
const instructions = config.toolProfile === "core"
|
|
378
|
+
? [
|
|
379
|
+
"Use BBRadar only for passive bug bounty intelligence; never scan or contact targets.",
|
|
380
|
+
"This server uses the compact core tool profile. Start with get_bbradar_guide when unsure.",
|
|
381
|
+
"Use resolve_program or run_program_name_action for a named program.",
|
|
382
|
+
"Use find_programs for general discovery and find_programs_by_target for reverse target lookup.",
|
|
383
|
+
"Default to bounty-eligible in-scope data unless the user explicitly requests broader results.",
|
|
384
|
+
"Prefer compact/identifier outputs and disclose sampled ranking scope when present."
|
|
385
|
+
]
|
|
386
|
+
: [
|
|
355
387
|
"Use BBRadar only for passive bug bounty intelligence; never scan or contact targets.",
|
|
356
388
|
"When unsure which BBRadar tool to use, call get_bbradar_guide first. It is local-only and cheap.",
|
|
357
389
|
"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.",
|
|
@@ -360,14 +392,20 @@ export function createBbradarServer(client, config) {
|
|
|
360
392
|
"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.",
|
|
361
393
|
"Recently updated programs across platforms: find_recently_updated_programs. Do not infer a platform such as Immunefi unless the user explicitly names it.",
|
|
362
394
|
"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.",
|
|
363
|
-
"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.",
|
|
395
|
+
"Discovery: find_program_candidates, find_wildcard_programs, find_recently_added_wildcards, find_low_competition_high_reward, 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.",
|
|
364
396
|
"Details: get_program_brief, get_program_scope_summary, get_program_target_breakdown, find_programs_by_target, compare_programs_compact.",
|
|
365
397
|
"Prefer compact/identifier outputs and disclose sampled ranking scope when present."
|
|
366
|
-
]
|
|
398
|
+
];
|
|
399
|
+
const server = new McpServer({
|
|
400
|
+
name: "bbradar.io",
|
|
401
|
+
version: SERVER_VERSION
|
|
402
|
+
}, {
|
|
403
|
+
instructions: instructions.join("\n")
|
|
367
404
|
});
|
|
405
|
+
const registerTool = createProfiledToolRegistrar(server, config.toolProfile);
|
|
368
406
|
const rateLimiter = new FixedWindowRateLimiter();
|
|
369
407
|
const exportStore = new Map();
|
|
370
|
-
|
|
408
|
+
registerTool("search_programs", {
|
|
371
409
|
title: "Search BBRadar Programs",
|
|
372
410
|
description: `List active programs. page_size max ${MAX_PROGRAMS_PER_SEARCH}.`,
|
|
373
411
|
inputSchema: {
|
|
@@ -393,7 +431,7 @@ export function createBbradarServer(client, config) {
|
|
|
393
431
|
meta: sanitizeJson(data?.meta)
|
|
394
432
|
});
|
|
395
433
|
}));
|
|
396
|
-
|
|
434
|
+
registerTool("get_program", {
|
|
397
435
|
title: "Get BBRadar Program",
|
|
398
436
|
description: "Get one program by platform:handle program_id, handle, or human program name. Names are resolved automatically when unambiguous.",
|
|
399
437
|
inputSchema: {
|
|
@@ -429,7 +467,7 @@ export function createBbradarServer(client, config) {
|
|
|
429
467
|
}
|
|
430
468
|
}));
|
|
431
469
|
}));
|
|
432
|
-
|
|
470
|
+
registerTool("resolve_program", {
|
|
433
471
|
title: "Resolve Program",
|
|
434
472
|
description: "Resolve partial name/handle text to likely program_ids. Use this when the user gives a program name fragment like Nado.",
|
|
435
473
|
inputSchema: {
|
|
@@ -453,7 +491,7 @@ export function createBbradarServer(client, config) {
|
|
|
453
491
|
},
|
|
454
492
|
annotations: readOnlyAnnotations
|
|
455
493
|
}, (args) => runTool("resolve_program", config, rateLimiter, async () => resolveProgram(client, config, args)));
|
|
456
|
-
|
|
494
|
+
registerTool("run_program_name_action", {
|
|
457
495
|
title: "Run Program Name Action",
|
|
458
496
|
description: "Start here for one named program. Resolves a partial program name, then runs targets, new_targets, scope_delta, brief, or target_breakdown.",
|
|
459
497
|
inputSchema: {
|
|
@@ -487,7 +525,7 @@ export function createBbradarServer(client, config) {
|
|
|
487
525
|
},
|
|
488
526
|
annotations: readOnlyAnnotations
|
|
489
527
|
}, (args) => runTool("run_program_name_action", config, rateLimiter, async () => runProgramNameAction(client, config, args)));
|
|
490
|
-
|
|
528
|
+
registerTool("get_program_eligible_targets_by_name", {
|
|
491
529
|
title: "Get Eligible Targets By Program Name",
|
|
492
530
|
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.",
|
|
493
531
|
inputSchema: {
|
|
@@ -518,7 +556,7 @@ export function createBbradarServer(client, config) {
|
|
|
518
556
|
},
|
|
519
557
|
annotations: readOnlyAnnotations
|
|
520
558
|
}, (args) => runTool("get_program_eligible_targets_by_name", config, rateLimiter, async () => getProgramEligibleTargetsByName(client, config, args)));
|
|
521
|
-
|
|
559
|
+
registerTool("get_program_targets", {
|
|
522
560
|
title: "Get Program Targets",
|
|
523
561
|
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}.`,
|
|
524
562
|
inputSchema: {
|
|
@@ -540,7 +578,7 @@ export function createBbradarServer(client, config) {
|
|
|
540
578
|
}, (args) => runTool("get_program_targets", config, rateLimiter, async () => {
|
|
541
579
|
return runProgramIdTool(client, config, args.program_id, async (programId) => getProgramTargetsPayload(client, args, programId), async (indexFallback) => getProgramTargetsExportFallbackPayload(client, args, indexFallback.resolvedProgramId));
|
|
542
580
|
}));
|
|
543
|
-
|
|
581
|
+
registerTool("get_recent_changes", {
|
|
544
582
|
title: "Get Recent Target Changes",
|
|
545
583
|
description: `List recent target changes. page_size max ${MAX_RECENT_CHANGES}.`,
|
|
546
584
|
inputSchema: {
|
|
@@ -579,15 +617,22 @@ export function createBbradarServer(client, config) {
|
|
|
579
617
|
facets: sanitizeJson(data?.facets)
|
|
580
618
|
});
|
|
581
619
|
}));
|
|
582
|
-
|
|
620
|
+
registerTool("get_latest_added_targets", {
|
|
583
621
|
title: "Get Latest Added Targets",
|
|
584
|
-
description: "Latest added targets by type,
|
|
622
|
+
description: "Latest added targets by type with optional platform, reward, and report-count filters. For fast wildcard discovery use target_type=wildcard and include_full_target_list=false.",
|
|
585
623
|
inputSchema: {
|
|
586
624
|
target_type: filterValueSchema().default("domain"),
|
|
625
|
+
platforms: platformListSchema(),
|
|
626
|
+
min_reward: z.number().int().min(0).optional(),
|
|
627
|
+
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
628
|
+
max_public_report_count: z.number().int().min(0).optional(),
|
|
629
|
+
require_known_report_count: z.boolean().default(false),
|
|
630
|
+
include_unknown_report_count: z.boolean().default(true),
|
|
587
631
|
include_out_of_scope: z.boolean().default(false).describe("Include out-of-scope."),
|
|
588
632
|
include_ineligible: z.boolean().default(false).describe("Include ineligible."),
|
|
589
633
|
recent_changes_page_size: recentChangesPageSizeSchema.default(MAX_RECENT_CHANGES),
|
|
590
|
-
|
|
634
|
+
max_results: z.number().int().min(1).max(MAX_RECENT_CHANGES).default(10),
|
|
635
|
+
include_full_target_list: z.boolean().default(true).describe("Fetch the newest matching program's complete target list. Set false for the fastest cross-program discovery."),
|
|
591
636
|
full_target_list_mode: targetListModeSchema.default("identifiers"),
|
|
592
637
|
full_target_limit: z.number().int().min(1).max(MAX_TARGETS_PER_PROGRAM).default(MAX_TARGETS_PER_PROGRAM),
|
|
593
638
|
full_list_include_out_of_scope: z.boolean().default(false),
|
|
@@ -599,13 +644,14 @@ export function createBbradarServer(client, config) {
|
|
|
599
644
|
query: jsonRecordSchema().optional(),
|
|
600
645
|
latest_added_at: z.string().optional(),
|
|
601
646
|
program: programOutputSchema().optional(),
|
|
647
|
+
matches: z.array(jsonRecordSchema()).optional(),
|
|
602
648
|
new_targets: z.array(z.unknown()).optional(),
|
|
603
649
|
full_targets: z.array(z.unknown()).optional(),
|
|
604
650
|
meta: jsonRecordSchema().optional()
|
|
605
651
|
},
|
|
606
652
|
annotations: readOnlyAnnotations
|
|
607
653
|
}, (args) => runTool("get_latest_added_targets", config, rateLimiter, async () => getLatestAddedTargets(client, config, args)));
|
|
608
|
-
|
|
654
|
+
registerTool("get_program_scope_summary", {
|
|
609
655
|
title: "Get Program Scope Summary",
|
|
610
656
|
description: "Grouped compact scope for one program.",
|
|
611
657
|
inputSchema: {
|
|
@@ -624,7 +670,7 @@ export function createBbradarServer(client, config) {
|
|
|
624
670
|
},
|
|
625
671
|
annotations: readOnlyAnnotations
|
|
626
672
|
}, (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 }))));
|
|
627
|
-
|
|
673
|
+
registerTool("get_program_target_breakdown", {
|
|
628
674
|
title: "Get Program Target Breakdown",
|
|
629
675
|
description: "Target type, scope tag, language tag, and bucket counts for one program.",
|
|
630
676
|
inputSchema: {
|
|
@@ -643,7 +689,7 @@ export function createBbradarServer(client, config) {
|
|
|
643
689
|
},
|
|
644
690
|
annotations: readOnlyAnnotations
|
|
645
691
|
}, (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 }))));
|
|
646
|
-
|
|
692
|
+
registerTool("get_program_scope_delta", {
|
|
647
693
|
title: "Get Program Scope Delta",
|
|
648
694
|
description: "Compact recent scope changes for one program.",
|
|
649
695
|
inputSchema: {
|
|
@@ -669,7 +715,7 @@ export function createBbradarServer(client, config) {
|
|
|
669
715
|
},
|
|
670
716
|
annotations: readOnlyAnnotations
|
|
671
717
|
}, (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))));
|
|
672
|
-
|
|
718
|
+
registerTool("get_recent_target_activity", {
|
|
673
719
|
title: "Get Recent Target Activity",
|
|
674
720
|
description: "Recent target changes grouped by program.",
|
|
675
721
|
inputSchema: {
|
|
@@ -695,7 +741,7 @@ export function createBbradarServer(client, config) {
|
|
|
695
741
|
},
|
|
696
742
|
annotations: readOnlyAnnotations
|
|
697
743
|
}, (args) => runTool("get_recent_target_activity", config, rateLimiter, async () => getRecentTargetActivity(client, config, args)));
|
|
698
|
-
|
|
744
|
+
registerTool("check_program_new_targets", {
|
|
699
745
|
title: "Check Program New Targets",
|
|
700
746
|
description: "Yes/no new in-scope targets for one program.",
|
|
701
747
|
inputSchema: {
|
|
@@ -720,7 +766,7 @@ export function createBbradarServer(client, config) {
|
|
|
720
766
|
},
|
|
721
767
|
annotations: readOnlyAnnotations
|
|
722
768
|
}, (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))));
|
|
723
|
-
|
|
769
|
+
registerTool("check_watchlist_new_targets", {
|
|
724
770
|
title: "Check Watchlist New Targets",
|
|
725
771
|
description: "New in-scope targets across many known program_ids.",
|
|
726
772
|
inputSchema: {
|
|
@@ -741,7 +787,7 @@ export function createBbradarServer(client, config) {
|
|
|
741
787
|
},
|
|
742
788
|
annotations: readOnlyAnnotations
|
|
743
789
|
}, (args) => runTool("check_watchlist_new_targets", config, rateLimiter, async () => checkWatchlistNewTargets(client, config, args)));
|
|
744
|
-
|
|
790
|
+
registerTool("get_opportunities", {
|
|
745
791
|
title: "Get BBRadar Opportunities",
|
|
746
792
|
description: `List programs in one opportunity tier. page_size max ${MAX_PROGRAMS_PER_SEARCH}.`,
|
|
747
793
|
inputSchema: {
|
|
@@ -772,7 +818,7 @@ export function createBbradarServer(client, config) {
|
|
|
772
818
|
meta: sanitizeJson(data?.meta)
|
|
773
819
|
});
|
|
774
820
|
}));
|
|
775
|
-
|
|
821
|
+
registerTool("find_programs", {
|
|
776
822
|
title: "Find BBRadar Programs",
|
|
777
823
|
description: "Ranked program discovery with filters and compact output.",
|
|
778
824
|
inputSchema: {
|
|
@@ -820,24 +866,30 @@ export function createBbradarServer(client, config) {
|
|
|
820
866
|
...args,
|
|
821
867
|
target_samples_only_wildcards: args.min_wildcards > 0
|
|
822
868
|
})));
|
|
823
|
-
|
|
869
|
+
registerTool("find_wildcard_programs", {
|
|
824
870
|
title: "Find Wildcard Programs",
|
|
825
|
-
description: "Wildcard program shortlist.",
|
|
871
|
+
description: "Wildcard program shortlist with platform, minimum reward, freshness, and report-count filters.",
|
|
826
872
|
inputSchema: {
|
|
827
873
|
platforms: platformListSchema(),
|
|
828
874
|
tags: tagListSchema(),
|
|
829
875
|
updated_since: isoDateTimeSchema.optional(),
|
|
876
|
+
min_reward: z.number().int().min(0).optional(),
|
|
877
|
+
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
830
878
|
max_public_report_count: z.number().int().min(0).default(25),
|
|
831
879
|
require_known_report_count: z.boolean().default(true),
|
|
832
880
|
include_unknown_report_count: z.boolean().default(false),
|
|
833
881
|
exclude_ctf: z.boolean().default(true),
|
|
834
882
|
require_bounty: z.boolean().default(true),
|
|
835
883
|
min_wildcards: z.number().int().min(1).max(100).default(1),
|
|
884
|
+
min_added_24h: z.number().int().min(0).optional(),
|
|
885
|
+
min_added_7d: z.number().int().min(0).optional(),
|
|
886
|
+
sort_by: programSortSchema.default("best_hunt_value"),
|
|
836
887
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
837
888
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
838
889
|
include_target_samples: z.boolean().default(false),
|
|
839
890
|
target_sample_programs: z.number().int().min(0).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
840
891
|
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
892
|
+
output_mode: programListModeSchema.default("compact"),
|
|
841
893
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
842
894
|
},
|
|
843
895
|
outputSchema: findProgramsOutputShape(),
|
|
@@ -851,8 +903,8 @@ export function createBbradarServer(client, config) {
|
|
|
851
903
|
web3: false,
|
|
852
904
|
opportunity_levels: [],
|
|
853
905
|
updated_since: args.updated_since,
|
|
854
|
-
min_bounty_min: undefined,
|
|
855
|
-
min_bounty_max: undefined,
|
|
906
|
+
min_bounty_min: args.min_reward !== undefined && args.reward_threshold_mode === "min_at_least" ? args.min_reward : undefined,
|
|
907
|
+
min_bounty_max: args.min_reward !== undefined && args.reward_threshold_mode === "max_at_least" ? args.min_reward : undefined,
|
|
856
908
|
max_bounty_max: undefined,
|
|
857
909
|
require_bounty: args.require_bounty,
|
|
858
910
|
max_public_report_count: args.max_public_report_count,
|
|
@@ -867,20 +919,21 @@ export function createBbradarServer(client, config) {
|
|
|
867
919
|
min_wildcards: args.min_wildcards,
|
|
868
920
|
min_eligible_targets: undefined,
|
|
869
921
|
min_total_targets: undefined,
|
|
870
|
-
min_added_24h:
|
|
871
|
-
min_added_7d:
|
|
922
|
+
min_added_24h: args.min_added_24h,
|
|
923
|
+
min_added_7d: args.min_added_7d,
|
|
872
924
|
max_pages: args.max_pages,
|
|
873
925
|
max_results: args.max_results,
|
|
874
|
-
sort_by:
|
|
926
|
+
sort_by: args.sort_by,
|
|
875
927
|
include_target_samples: args.include_target_samples,
|
|
876
928
|
target_sample_programs: args.target_sample_programs,
|
|
877
929
|
target_sample_size: args.target_sample_size,
|
|
878
930
|
only_in_scope_targets: true,
|
|
879
931
|
only_bounty_eligible_targets: true,
|
|
880
932
|
target_samples_only_wildcards: true,
|
|
933
|
+
output_mode: args.output_mode,
|
|
881
934
|
upstream_request_budget: args.upstream_request_budget
|
|
882
935
|
})));
|
|
883
|
-
|
|
936
|
+
registerTool("find_web3_programs", {
|
|
884
937
|
title: "Find Web3 Programs",
|
|
885
938
|
description: "Web3/blockchain/smart-contract program shortlist.",
|
|
886
939
|
inputSchema: {
|
|
@@ -941,7 +994,7 @@ export function createBbradarServer(client, config) {
|
|
|
941
994
|
target_samples_only_wildcards: args.min_wildcards > 0,
|
|
942
995
|
upstream_request_budget: args.upstream_request_budget
|
|
943
996
|
})));
|
|
944
|
-
|
|
997
|
+
registerTool("find_reward_programs", {
|
|
945
998
|
title: "Find Reward Programs",
|
|
946
999
|
description: "Programs matching reward thresholds.",
|
|
947
1000
|
inputSchema: {
|
|
@@ -970,7 +1023,7 @@ export function createBbradarServer(client, config) {
|
|
|
970
1023
|
outputSchema: findProgramsOutputShape(),
|
|
971
1024
|
annotations: readOnlyAnnotations
|
|
972
1025
|
}, (args) => runTool("find_reward_programs", config, rateLimiter, async () => findRewardPrograms(client, config, args)));
|
|
973
|
-
|
|
1026
|
+
registerTool("find_web3_contests", {
|
|
974
1027
|
title: "Find Web3 Contests",
|
|
975
1028
|
description: "Web3 contest or competitive audit shortlist.",
|
|
976
1029
|
inputSchema: {
|
|
@@ -993,7 +1046,7 @@ export function createBbradarServer(client, config) {
|
|
|
993
1046
|
outputSchema: findProgramsOutputShape(),
|
|
994
1047
|
annotations: readOnlyAnnotations
|
|
995
1048
|
}, (args) => runTool("find_web3_contests", config, rateLimiter, async () => findWeb3Contests(client, config, args)));
|
|
996
|
-
|
|
1049
|
+
registerTool("find_language_programs", {
|
|
997
1050
|
title: "Find Language Programs",
|
|
998
1051
|
description: "Language-specific program shortlist.",
|
|
999
1052
|
inputSchema: {
|
|
@@ -1020,7 +1073,7 @@ export function createBbradarServer(client, config) {
|
|
|
1020
1073
|
outputSchema: findProgramsOutputShape(),
|
|
1021
1074
|
annotations: readOnlyAnnotations
|
|
1022
1075
|
}, (args) => runTool("find_language_programs", config, rateLimiter, async () => findLanguagePrograms(client, config, args)));
|
|
1023
|
-
|
|
1076
|
+
registerTool("find_target_type_programs", {
|
|
1024
1077
|
title: "Find Target Type Programs",
|
|
1025
1078
|
description: "Target-type program shortlist.",
|
|
1026
1079
|
inputSchema: {
|
|
@@ -1046,7 +1099,7 @@ export function createBbradarServer(client, config) {
|
|
|
1046
1099
|
outputSchema: findProgramsOutputShape(),
|
|
1047
1100
|
annotations: readOnlyAnnotations
|
|
1048
1101
|
}, (args) => runTool("find_target_type_programs", config, rateLimiter, async () => findTargetTypePrograms(client, config, args)));
|
|
1049
|
-
|
|
1102
|
+
registerTool("find_vdp_programs", {
|
|
1050
1103
|
title: "Find VDP Programs",
|
|
1051
1104
|
description: "VDP/disclosure-only/no-bounty program shortlist.",
|
|
1052
1105
|
inputSchema: {
|
|
@@ -1066,7 +1119,7 @@ export function createBbradarServer(client, config) {
|
|
|
1066
1119
|
outputSchema: findProgramsOutputShape(),
|
|
1067
1120
|
annotations: readOnlyAnnotations
|
|
1068
1121
|
}, (args) => runTool("find_vdp_programs", config, rateLimiter, async () => findVdpPrograms(client, config, args)));
|
|
1069
|
-
|
|
1122
|
+
registerTool("find_paid_programs", {
|
|
1070
1123
|
title: "Find Paid Programs",
|
|
1071
1124
|
description: "Paid bounty program shortlist.",
|
|
1072
1125
|
inputSchema: {
|
|
@@ -1089,7 +1142,7 @@ export function createBbradarServer(client, config) {
|
|
|
1089
1142
|
outputSchema: findProgramsOutputShape(),
|
|
1090
1143
|
annotations: readOnlyAnnotations
|
|
1091
1144
|
}, (args) => runTool("find_paid_programs", config, rateLimiter, async () => findPaidPrograms(client, config, args)));
|
|
1092
|
-
|
|
1145
|
+
registerTool("find_stack_matches", {
|
|
1093
1146
|
title: "Find Stack Matches",
|
|
1094
1147
|
description: "Combined stack/language/type/reward shortlist.",
|
|
1095
1148
|
inputSchema: {
|
|
@@ -1119,7 +1172,7 @@ export function createBbradarServer(client, config) {
|
|
|
1119
1172
|
outputSchema: findProgramsOutputShape(),
|
|
1120
1173
|
annotations: readOnlyAnnotations
|
|
1121
1174
|
}, (args) => runTool("find_stack_matches", config, rateLimiter, async () => findStackMatches(client, config, args)));
|
|
1122
|
-
|
|
1175
|
+
registerTool("find_low_noise_programs", {
|
|
1123
1176
|
title: "Find Low Noise Programs",
|
|
1124
1177
|
description: "Low-public-report program shortlist.",
|
|
1125
1178
|
inputSchema: {
|
|
@@ -1145,7 +1198,7 @@ export function createBbradarServer(client, config) {
|
|
|
1145
1198
|
outputSchema: findProgramsOutputShape(),
|
|
1146
1199
|
annotations: readOnlyAnnotations
|
|
1147
1200
|
}, (args) => runTool("find_low_noise_programs", config, rateLimiter, async () => findLowNoisePrograms(client, config, args)));
|
|
1148
|
-
|
|
1201
|
+
registerTool("find_recent_by_type", {
|
|
1149
1202
|
title: "Find Recent By Type",
|
|
1150
1203
|
description: "Recent changes by target type grouped by program.",
|
|
1151
1204
|
inputSchema: {
|
|
@@ -1171,7 +1224,7 @@ export function createBbradarServer(client, config) {
|
|
|
1171
1224
|
},
|
|
1172
1225
|
annotations: readOnlyAnnotations
|
|
1173
1226
|
}, (args) => runTool("find_recent_by_type", config, rateLimiter, async () => findRecentByType(client, config, args)));
|
|
1174
|
-
|
|
1227
|
+
registerTool("find_programs_by_target", {
|
|
1175
1228
|
title: "Find Programs By Target",
|
|
1176
1229
|
description: "Reverse lookup target/domain ownership. Accepts domains, URLs, wildcards, target names, and partial target text.",
|
|
1177
1230
|
inputSchema: {
|
|
@@ -1194,21 +1247,21 @@ export function createBbradarServer(client, config) {
|
|
|
1194
1247
|
},
|
|
1195
1248
|
annotations: readOnlyAnnotations
|
|
1196
1249
|
}, (args) => runTool("find_programs_by_target", config, rateLimiter, async () => findProgramsByTarget(client, config, args)));
|
|
1197
|
-
|
|
1250
|
+
registerTool("find_program_candidates", {
|
|
1198
1251
|
title: "Find BBRadar Program Candidates",
|
|
1199
1252
|
description: "Start here for broad program discovery. Default ranked hunt-candidate preset with safe filters and compact output.",
|
|
1200
1253
|
inputSchema: programCandidatePresetInputSchema,
|
|
1201
1254
|
outputSchema: findProgramsOutputShape(),
|
|
1202
1255
|
annotations: readOnlyAnnotations
|
|
1203
1256
|
}, (args) => runTool("find_program_candidates", config, rateLimiter, async () => findProgramCandidates(client, config, args)));
|
|
1204
|
-
|
|
1257
|
+
registerTool("find_hunt_candidates", {
|
|
1205
1258
|
title: "Find BBRadar Hunt Candidates",
|
|
1206
1259
|
description: "Legacy alias for find_program_candidates. Prefer find_program_candidates for new calls.",
|
|
1207
1260
|
inputSchema: programCandidatePresetInputSchema,
|
|
1208
1261
|
outputSchema: findProgramsOutputShape(),
|
|
1209
1262
|
annotations: readOnlyAnnotations
|
|
1210
1263
|
}, (args) => runTool("find_hunt_candidates", config, rateLimiter, async () => findProgramCandidates(client, config, args)));
|
|
1211
|
-
|
|
1264
|
+
registerTool("find_fresh_hunt_candidates", {
|
|
1212
1265
|
title: "Find Fresh Hunt Candidates",
|
|
1213
1266
|
description: "Freshness-first hunt shortlist.",
|
|
1214
1267
|
inputSchema: {
|
|
@@ -1277,7 +1330,7 @@ export function createBbradarServer(client, config) {
|
|
|
1277
1330
|
output_mode: args.output_mode,
|
|
1278
1331
|
upstream_request_budget: args.upstream_request_budget
|
|
1279
1332
|
})));
|
|
1280
|
-
|
|
1333
|
+
registerTool("find_recently_updated_programs", {
|
|
1281
1334
|
title: "Find Recently Updated Programs",
|
|
1282
1335
|
description: "Recently updated program shortlist across all platforms by default. Platform filters are applied only when explicitly provided.",
|
|
1283
1336
|
inputSchema: {
|
|
@@ -1302,7 +1355,7 @@ export function createBbradarServer(client, config) {
|
|
|
1302
1355
|
outputSchema: findProgramsOutputShape(),
|
|
1303
1356
|
annotations: readOnlyAnnotations
|
|
1304
1357
|
}, (args) => runTool("find_recently_updated_programs", config, rateLimiter, async () => findRecentlyUpdatedPrograms(client, config, args)));
|
|
1305
|
-
|
|
1358
|
+
registerTool("list_filters", {
|
|
1306
1359
|
title: "List BBRadar Filters",
|
|
1307
1360
|
description: "Discover observed filter values.",
|
|
1308
1361
|
inputSchema: {
|
|
@@ -1321,7 +1374,7 @@ export function createBbradarServer(client, config) {
|
|
|
1321
1374
|
},
|
|
1322
1375
|
annotations: readOnlyAnnotations
|
|
1323
1376
|
}, (args) => runTool("list_filters", config, rateLimiter, async () => listFilters(client, args)));
|
|
1324
|
-
|
|
1377
|
+
registerTool("get_bbradar_guide", {
|
|
1325
1378
|
title: "Get BBRadar Tool Guide",
|
|
1326
1379
|
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.",
|
|
1327
1380
|
inputSchema: {
|
|
@@ -1334,9 +1387,9 @@ export function createBbradarServer(client, config) {
|
|
|
1334
1387
|
annotations: readOnlyAnnotations
|
|
1335
1388
|
}, (args) => runTool("get_bbradar_guide", config, rateLimiter, async () => ({
|
|
1336
1389
|
request_id: randomUUID(),
|
|
1337
|
-
guide: bbradarGuide(args.topic)
|
|
1390
|
+
guide: bbradarGuide(args.topic, config.toolProfile)
|
|
1338
1391
|
})));
|
|
1339
|
-
|
|
1392
|
+
registerTool("get_mcp_status", {
|
|
1340
1393
|
title: "Get BBRadar MCP Status",
|
|
1341
1394
|
description: "Local MCP status and timing metrics.",
|
|
1342
1395
|
inputSchema: {},
|
|
@@ -1355,10 +1408,14 @@ export function createBbradarServer(client, config) {
|
|
|
1355
1408
|
web_base_url: config.webBaseUrl,
|
|
1356
1409
|
response_cache_enabled: config.cacheTtlMs > 0 && config.cacheMaxEntries > 0,
|
|
1357
1410
|
cache_ttl_ms: config.cacheTtlMs,
|
|
1411
|
+
negative_cache_ttl_ms: config.negativeCacheTtlMs,
|
|
1358
1412
|
cache_max_entries: config.cacheMaxEntries,
|
|
1359
1413
|
local_program_index_enabled: false,
|
|
1360
1414
|
default_rate_limit_per_minute: config.defaultRateLimitPerMinute,
|
|
1361
1415
|
export_rate_limit_per_minute: config.exportRateLimitPerMinute,
|
|
1416
|
+
max_concurrent_upstream_requests: config.maxConcurrentRequests,
|
|
1417
|
+
compact_tool_text: config.compactToolText,
|
|
1418
|
+
tool_profile: config.toolProfile,
|
|
1362
1419
|
request_timeout_ms: config.requestTimeoutMs,
|
|
1363
1420
|
validate_on_startup: config.validateOnStartup,
|
|
1364
1421
|
transport: "stdio",
|
|
@@ -1367,7 +1424,7 @@ export function createBbradarServer(client, config) {
|
|
|
1367
1424
|
client: client.diagnostics()
|
|
1368
1425
|
}
|
|
1369
1426
|
})));
|
|
1370
|
-
|
|
1427
|
+
registerTool("compare_programs", {
|
|
1371
1428
|
title: "Compare BBRadar Programs",
|
|
1372
1429
|
description: "Compare programs.",
|
|
1373
1430
|
inputSchema: {
|
|
@@ -1381,7 +1438,7 @@ export function createBbradarServer(client, config) {
|
|
|
1381
1438
|
outputSchema: decisionOutputShape(),
|
|
1382
1439
|
annotations: readOnlyAnnotations
|
|
1383
1440
|
}, (args) => runTool("compare_programs", config, rateLimiter, async () => comparePrograms(client, config, args)));
|
|
1384
|
-
|
|
1441
|
+
registerTool("compare_programs_compact", {
|
|
1385
1442
|
title: "Compare Programs Compact",
|
|
1386
1443
|
description: "Compact program comparison.",
|
|
1387
1444
|
inputSchema: {
|
|
@@ -1394,7 +1451,7 @@ export function createBbradarServer(client, config) {
|
|
|
1394
1451
|
outputSchema: decisionOutputShape(),
|
|
1395
1452
|
annotations: readOnlyAnnotations
|
|
1396
1453
|
}, (args) => runTool("compare_programs_compact", config, rateLimiter, async () => comparePrograms(client, config, { ...args, output_mode: "compact" })));
|
|
1397
|
-
|
|
1454
|
+
registerTool("summarize_program_activity", {
|
|
1398
1455
|
title: "Summarize Program Activity",
|
|
1399
1456
|
description: "Summarize one program's activity.",
|
|
1400
1457
|
inputSchema: {
|
|
@@ -1409,7 +1466,7 @@ export function createBbradarServer(client, config) {
|
|
|
1409
1466
|
},
|
|
1410
1467
|
annotations: readOnlyAnnotations
|
|
1411
1468
|
}, (args) => runTool("summarize_program_activity", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => summarizeProgramActivity(client, config, programId, args.recent_changes_limit))));
|
|
1412
|
-
|
|
1469
|
+
registerTool("get_program_brief", {
|
|
1413
1470
|
title: "Get Program Brief",
|
|
1414
1471
|
description: "Compact worth-hunting brief for one program.",
|
|
1415
1472
|
inputSchema: {
|
|
@@ -1432,17 +1489,28 @@ export function createBbradarServer(client, config) {
|
|
|
1432
1489
|
},
|
|
1433
1490
|
annotations: readOnlyAnnotations
|
|
1434
1491
|
}, (args) => runTool("get_program_brief", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramBrief(client, config, { ...args, program_id: programId }))));
|
|
1435
|
-
|
|
1492
|
+
registerTool("find_recently_added_wildcards", {
|
|
1436
1493
|
title: "Find Recently Added Wildcards",
|
|
1437
|
-
description: "Recently added wildcard programs.",
|
|
1494
|
+
description: "Recently added wildcard programs with platform, minimum reward, and report-count filters. Returns bounded wildcard target samples.",
|
|
1438
1495
|
inputSchema: {
|
|
1439
1496
|
platforms: platformListSchema(),
|
|
1440
1497
|
tags: tagListSchema(),
|
|
1498
|
+
min_added_24h: z.number().int().min(0).optional(),
|
|
1441
1499
|
min_added_7d: z.number().int().min(1).default(1),
|
|
1500
|
+
min_reward: z.number().int().min(0).optional(),
|
|
1501
|
+
reward_threshold_mode: rewardThresholdModeSchema.default("max_at_least"),
|
|
1442
1502
|
max_public_report_count: z.number().int().min(0).optional(),
|
|
1503
|
+
require_known_report_count: z.boolean().default(false),
|
|
1504
|
+
include_unknown_report_count: z.boolean().default(false),
|
|
1443
1505
|
require_bounty: z.boolean().default(true),
|
|
1506
|
+
min_wildcards: z.number().int().min(1).max(100).default(1),
|
|
1507
|
+
sort_by: programSortSchema.default("most_new_targets"),
|
|
1444
1508
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
1445
1509
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1510
|
+
include_target_samples: z.boolean().default(true),
|
|
1511
|
+
target_sample_programs: z.number().int().min(0).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
1512
|
+
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
1513
|
+
output_mode: programListModeSchema.default("compact"),
|
|
1446
1514
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1447
1515
|
},
|
|
1448
1516
|
outputSchema: findProgramsOutputShape(),
|
|
@@ -1456,37 +1524,38 @@ export function createBbradarServer(client, config) {
|
|
|
1456
1524
|
web3: false,
|
|
1457
1525
|
opportunity_levels: [],
|
|
1458
1526
|
updated_since: undefined,
|
|
1459
|
-
min_bounty_min: undefined,
|
|
1460
|
-
min_bounty_max: undefined,
|
|
1527
|
+
min_bounty_min: args.min_reward !== undefined && args.reward_threshold_mode === "min_at_least" ? args.min_reward : undefined,
|
|
1528
|
+
min_bounty_max: args.min_reward !== undefined && args.reward_threshold_mode === "max_at_least" ? args.min_reward : undefined,
|
|
1461
1529
|
max_bounty_max: undefined,
|
|
1462
1530
|
require_bounty: args.require_bounty,
|
|
1463
1531
|
max_public_report_count: args.max_public_report_count,
|
|
1464
|
-
require_known_report_count:
|
|
1465
|
-
include_unknown_report_count:
|
|
1532
|
+
require_known_report_count: args.require_known_report_count,
|
|
1533
|
+
include_unknown_report_count: args.include_unknown_report_count,
|
|
1466
1534
|
exclude_ctf: true,
|
|
1467
1535
|
exclude_mobile_only: false,
|
|
1468
1536
|
exclude_non_web: false,
|
|
1469
1537
|
has_api_scope: false,
|
|
1470
1538
|
fresh_launch_days: undefined,
|
|
1471
|
-
min_wildcards:
|
|
1539
|
+
min_wildcards: args.min_wildcards,
|
|
1472
1540
|
min_eligible_targets: undefined,
|
|
1473
1541
|
min_total_targets: undefined,
|
|
1474
|
-
min_added_24h:
|
|
1542
|
+
min_added_24h: args.min_added_24h,
|
|
1475
1543
|
min_added_7d: args.min_added_7d,
|
|
1476
1544
|
max_pages: args.max_pages,
|
|
1477
1545
|
max_results: args.max_results,
|
|
1478
|
-
sort_by:
|
|
1479
|
-
include_target_samples:
|
|
1480
|
-
target_sample_programs:
|
|
1481
|
-
target_sample_size:
|
|
1546
|
+
sort_by: args.sort_by,
|
|
1547
|
+
include_target_samples: args.include_target_samples,
|
|
1548
|
+
target_sample_programs: args.target_sample_programs,
|
|
1549
|
+
target_sample_size: args.target_sample_size,
|
|
1482
1550
|
only_in_scope_targets: true,
|
|
1483
1551
|
only_bounty_eligible_targets: true,
|
|
1484
1552
|
target_samples_only_wildcards: true,
|
|
1553
|
+
output_mode: args.output_mode,
|
|
1485
1554
|
upstream_request_budget: args.upstream_request_budget
|
|
1486
1555
|
})));
|
|
1487
|
-
|
|
1556
|
+
registerTool("find_low_competition_high_reward", {
|
|
1488
1557
|
title: "Find Low Competition High Reward",
|
|
1489
|
-
description: "
|
|
1558
|
+
description: "Low-report, high-reward shortlist with optional wildcard and technology filters.",
|
|
1490
1559
|
inputSchema: {
|
|
1491
1560
|
platforms: platformListSchema(),
|
|
1492
1561
|
tags: tagListSchema(),
|
|
@@ -1495,6 +1564,7 @@ export function createBbradarServer(client, config) {
|
|
|
1495
1564
|
max_public_report_count: z.number().int().min(0).default(10),
|
|
1496
1565
|
min_bounty_max: z.number().int().min(0).default(5_000),
|
|
1497
1566
|
min_eligible_targets: z.number().int().min(0).default(1),
|
|
1567
|
+
min_wildcards: z.number().int().min(0).max(100).default(0),
|
|
1498
1568
|
exclude_ctf: z.boolean().default(true),
|
|
1499
1569
|
exclude_mobile_only: z.boolean().default(false),
|
|
1500
1570
|
exclude_non_web: z.boolean().default(false),
|
|
@@ -1502,6 +1572,10 @@ export function createBbradarServer(client, config) {
|
|
|
1502
1572
|
fresh_launch_days: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).optional(),
|
|
1503
1573
|
max_pages: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
1504
1574
|
max_results: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(10),
|
|
1575
|
+
include_target_samples: z.boolean().default(true),
|
|
1576
|
+
target_sample_programs: z.number().int().min(0).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(3),
|
|
1577
|
+
target_sample_size: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(5),
|
|
1578
|
+
output_mode: programListModeSchema.default("compact"),
|
|
1505
1579
|
upstream_request_budget: z.number().int().min(1).max(MAX_ACCEPTED_NUMERIC_LIMIT).default(DEFAULT_FIND_UPSTREAM_REQUEST_BUDGET)
|
|
1506
1580
|
},
|
|
1507
1581
|
outputSchema: findProgramsOutputShape(),
|
|
@@ -1527,7 +1601,7 @@ export function createBbradarServer(client, config) {
|
|
|
1527
1601
|
exclude_non_web: args.exclude_non_web,
|
|
1528
1602
|
has_api_scope: args.has_api_scope,
|
|
1529
1603
|
fresh_launch_days: args.fresh_launch_days,
|
|
1530
|
-
min_wildcards:
|
|
1604
|
+
min_wildcards: args.min_wildcards,
|
|
1531
1605
|
min_eligible_targets: args.min_eligible_targets,
|
|
1532
1606
|
min_total_targets: undefined,
|
|
1533
1607
|
min_added_24h: undefined,
|
|
@@ -1535,15 +1609,16 @@ export function createBbradarServer(client, config) {
|
|
|
1535
1609
|
max_pages: args.max_pages,
|
|
1536
1610
|
max_results: args.max_results,
|
|
1537
1611
|
sort_by: "best_hunt_value",
|
|
1538
|
-
include_target_samples:
|
|
1539
|
-
target_sample_programs:
|
|
1540
|
-
target_sample_size:
|
|
1612
|
+
include_target_samples: args.include_target_samples,
|
|
1613
|
+
target_sample_programs: args.target_sample_programs,
|
|
1614
|
+
target_sample_size: args.target_sample_size,
|
|
1541
1615
|
only_in_scope_targets: true,
|
|
1542
1616
|
only_bounty_eligible_targets: true,
|
|
1543
|
-
target_samples_only_wildcards:
|
|
1617
|
+
target_samples_only_wildcards: args.min_wildcards > 0,
|
|
1618
|
+
output_mode: args.output_mode,
|
|
1544
1619
|
upstream_request_budget: args.upstream_request_budget
|
|
1545
1620
|
})));
|
|
1546
|
-
|
|
1621
|
+
registerTool("get_program_delta", {
|
|
1547
1622
|
title: "Get Program Delta",
|
|
1548
1623
|
description: "Recent target changes for one program.",
|
|
1549
1624
|
inputSchema: {
|
|
@@ -1561,7 +1636,7 @@ export function createBbradarServer(client, config) {
|
|
|
1561
1636
|
},
|
|
1562
1637
|
annotations: readOnlyAnnotations
|
|
1563
1638
|
}, (args) => runTool("get_program_delta", config, rateLimiter, async () => runProgramIdTool(client, config, args.program_id, (programId) => getProgramDelta(client, config, { ...args, program_id: programId }))));
|
|
1564
|
-
|
|
1639
|
+
registerTool("export_targets", {
|
|
1565
1640
|
title: "Export BBRadar Targets",
|
|
1566
1641
|
description: `Read-only target export. limit max ${MAX_TARGET_EXPORT}.`,
|
|
1567
1642
|
inputSchema: {
|
|
@@ -1618,11 +1693,14 @@ export function createBbradarServer(client, config) {
|
|
|
1618
1693
|
});
|
|
1619
1694
|
}
|
|
1620
1695
|
}));
|
|
1621
|
-
registerPrompts(server);
|
|
1696
|
+
registerPrompts(server, config.toolProfile);
|
|
1622
1697
|
registerResources(server, client, config, exportStore);
|
|
1623
1698
|
return server;
|
|
1624
1699
|
}
|
|
1625
|
-
function bbradarGuide(topic) {
|
|
1700
|
+
function bbradarGuide(topic, profile) {
|
|
1701
|
+
if (profile === "core") {
|
|
1702
|
+
return coreBbradarGuide(topic);
|
|
1703
|
+
}
|
|
1626
1704
|
const primaryEntryPoints = [
|
|
1627
1705
|
{
|
|
1628
1706
|
tool: "find_program_candidates",
|
|
@@ -1674,6 +1752,27 @@ function bbradarGuide(topic) {
|
|
|
1674
1752
|
first_tool: "find_recently_updated_programs",
|
|
1675
1753
|
then: "Use get_latest_added_targets for global added targets, or check_program_new_targets when the user names one program."
|
|
1676
1754
|
},
|
|
1755
|
+
{
|
|
1756
|
+
user_goal: "latest added wildcard targets with platform, reward, or report-count filters",
|
|
1757
|
+
first_tool: "get_latest_added_targets",
|
|
1758
|
+
arguments: {
|
|
1759
|
+
target_type: "wildcard",
|
|
1760
|
+
platforms: ["requested platform"],
|
|
1761
|
+
min_reward: 5000,
|
|
1762
|
+
max_public_report_count: 10,
|
|
1763
|
+
include_full_target_list: false
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
user_goal: "wildcard programs with low report count and good rewards",
|
|
1768
|
+
first_tool: "find_wildcard_programs",
|
|
1769
|
+
arguments: {
|
|
1770
|
+
platforms: ["requested platform"],
|
|
1771
|
+
min_reward: 5000,
|
|
1772
|
+
max_public_report_count: 10,
|
|
1773
|
+
include_target_samples: true
|
|
1774
|
+
}
|
|
1775
|
+
},
|
|
1677
1776
|
{
|
|
1678
1777
|
user_goal: "compare a short list",
|
|
1679
1778
|
first_tool: "compare_programs_compact",
|
|
@@ -1691,8 +1790,8 @@ function bbradarGuide(topic) {
|
|
|
1691
1790
|
}
|
|
1692
1791
|
];
|
|
1693
1792
|
const advancedTools = {
|
|
1694
|
-
discovery: ["find_stack_matches", "find_language_programs", "find_target_type_programs", "find_reward_programs", "find_paid_programs", "find_web3_contests", "find_low_noise_programs"],
|
|
1695
|
-
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"],
|
|
1793
|
+
discovery: ["find_wildcard_programs", "find_low_competition_high_reward", "find_stack_matches", "find_language_programs", "find_target_type_programs", "find_reward_programs", "find_paid_programs", "find_web3_contests", "find_low_noise_programs"],
|
|
1794
|
+
freshness: ["find_recently_added_wildcards", "find_recently_updated_programs", "find_fresh_hunt_candidates", "get_latest_added_targets", "get_recent_target_activity", "get_program_scope_delta", "check_watchlist_new_targets"],
|
|
1696
1795
|
details: ["get_program_brief", "get_program_scope_summary", "get_program_target_breakdown", "find_programs_by_target"],
|
|
1697
1796
|
exports: ["export_targets"]
|
|
1698
1797
|
};
|
|
@@ -1724,6 +1823,20 @@ function bbradarGuide(topic) {
|
|
|
1724
1823
|
{
|
|
1725
1824
|
user_says: "who owns https://api.example.com/v1/users",
|
|
1726
1825
|
call: { tool: "find_programs_by_target", arguments: { target_query: "https://api.example.com/v1/users" } }
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
1828
|
+
user_says: "latest HackerOne wildcard targets with rewards over 5000",
|
|
1829
|
+
call: {
|
|
1830
|
+
tool: "get_latest_added_targets",
|
|
1831
|
+
arguments: { target_type: "wildcard", platforms: ["HackerOne"], min_reward: 5000, include_full_target_list: false }
|
|
1832
|
+
}
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
user_says: "wildcard programs with under 10 reports and good rewards",
|
|
1836
|
+
call: {
|
|
1837
|
+
tool: "find_wildcard_programs",
|
|
1838
|
+
arguments: { max_public_report_count: 10, min_reward: 5000, include_target_samples: true }
|
|
1839
|
+
}
|
|
1727
1840
|
}
|
|
1728
1841
|
];
|
|
1729
1842
|
return {
|
|
@@ -1744,6 +1857,67 @@ function bbradarGuide(topic) {
|
|
|
1744
1857
|
examples
|
|
1745
1858
|
};
|
|
1746
1859
|
}
|
|
1860
|
+
function coreBbradarGuide(topic) {
|
|
1861
|
+
const decisionTree = [
|
|
1862
|
+
{
|
|
1863
|
+
user_goal: "find programs to hunt",
|
|
1864
|
+
first_tool: "find_programs",
|
|
1865
|
+
arguments: { max_results: 10, output_mode: "compact" }
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
user_goal: "named program summary or action",
|
|
1869
|
+
first_tool: "run_program_name_action",
|
|
1870
|
+
arguments: { query: "program name", action: "brief" }
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
user_goal: "named program targets",
|
|
1874
|
+
first_tool: "get_program_eligible_targets_by_name",
|
|
1875
|
+
arguments: { query: "program name", target_list_mode: "identifiers" }
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
user_goal: "find which program owns a target",
|
|
1879
|
+
first_tool: "find_programs_by_target",
|
|
1880
|
+
arguments: { target_query: "domain, URL, wildcard, package/repo name, or short target phrase", match_mode: "smart" }
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
user_goal: "fresh or recently changed scope",
|
|
1884
|
+
first_tool: "get_recent_changes",
|
|
1885
|
+
then: "Use get_latest_added_targets when only newly added targets are needed."
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
user_goal: "export targets",
|
|
1889
|
+
first_tool: "export_targets",
|
|
1890
|
+
then: "Use the returned bbradar://exports resource_uri for the full local export payload."
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
user_goal: "unknown filter values",
|
|
1894
|
+
first_tool: "list_filters",
|
|
1895
|
+
then: "Reuse observed platforms, tags, target types, and language tags in find_programs."
|
|
1896
|
+
}
|
|
1897
|
+
];
|
|
1898
|
+
return {
|
|
1899
|
+
topic,
|
|
1900
|
+
tool_profile: "core",
|
|
1901
|
+
rule_zero: "BBRadar is passive intelligence only. Never scan or contact targets.",
|
|
1902
|
+
defaults: {
|
|
1903
|
+
output_mode: "compact",
|
|
1904
|
+
target_list_mode: "identifiers",
|
|
1905
|
+
target_match_mode: "smart",
|
|
1906
|
+
include_out_of_scope: false,
|
|
1907
|
+
include_ineligible: false,
|
|
1908
|
+
require_bounty: true
|
|
1909
|
+
},
|
|
1910
|
+
primary_entry_points: [
|
|
1911
|
+
"find_programs",
|
|
1912
|
+
"run_program_name_action",
|
|
1913
|
+
"get_program_eligible_targets_by_name",
|
|
1914
|
+
"find_programs_by_target",
|
|
1915
|
+
"get_bbradar_guide"
|
|
1916
|
+
],
|
|
1917
|
+
decision_tree: decisionTree.filter((entry) => topic === "start" || guideEntryMatchesTopic(topic, entry)),
|
|
1918
|
+
available_tools: [...CORE_TOOL_NAMES]
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1747
1921
|
function guideEntryMatchesTopic(topic, entry) {
|
|
1748
1922
|
const goal = typeof entry.user_goal === "string" ? entry.user_goal : "";
|
|
1749
1923
|
const firstTool = typeof entry.first_tool === "string" ? entry.first_tool : "";
|
|
@@ -3178,13 +3352,26 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3178
3352
|
};
|
|
3179
3353
|
const sourceRequests = [];
|
|
3180
3354
|
const matches = [];
|
|
3355
|
+
const directHandleFirst = !queryLooksLikeTargetIdentifier(input.target_query) &&
|
|
3356
|
+
!/\s/.test(input.target_query.trim()) &&
|
|
3357
|
+
looksLikeDirectProgramHandleQuery(input.target_query);
|
|
3358
|
+
if (input.use_api_search && !directHandleFirst) {
|
|
3359
|
+
const directResult = await searchProgramsByTargetApi(client, config, input, budget, warnings, sourceRequests);
|
|
3360
|
+
if (directResult) {
|
|
3361
|
+
return directResult;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3181
3364
|
const directHandles = await collectDirectHandlePrograms(client, {
|
|
3182
3365
|
query: input.target_query,
|
|
3183
3366
|
platforms: input.platforms,
|
|
3184
3367
|
budget,
|
|
3185
3368
|
warnings,
|
|
3186
3369
|
source: "target_query_program_id_guess",
|
|
3187
|
-
maxProgramIds:
|
|
3370
|
+
maxProgramIds: input.use_api_search && directHandleFirst
|
|
3371
|
+
? 4
|
|
3372
|
+
: queryLooksLikeTargetIdentifier(input.target_query) || looksLikeDirectProgramHandleQuery(input.target_query)
|
|
3373
|
+
? MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS
|
|
3374
|
+
: 4,
|
|
3188
3375
|
maxHandleVariants: 1,
|
|
3189
3376
|
stopOnFirstMatch: true
|
|
3190
3377
|
});
|
|
@@ -3204,8 +3391,8 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3204
3391
|
}
|
|
3205
3392
|
});
|
|
3206
3393
|
}
|
|
3207
|
-
if (input.use_api_search) {
|
|
3208
|
-
const directResult = await searchProgramsByTargetApi(client, config, input, warnings);
|
|
3394
|
+
if (input.use_api_search && directHandleFirst) {
|
|
3395
|
+
const directResult = await searchProgramsByTargetApi(client, config, input, budget, warnings, sourceRequests);
|
|
3209
3396
|
if (directResult) {
|
|
3210
3397
|
return directResult;
|
|
3211
3398
|
}
|
|
@@ -3310,13 +3497,18 @@ function programsByTargetResult(input, warnings, sourceRequests, matches, budget
|
|
|
3310
3497
|
meta: options.meta
|
|
3311
3498
|
});
|
|
3312
3499
|
}
|
|
3313
|
-
async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
3500
|
+
async function searchProgramsByTargetApi(client, config, input, budget, warnings, sourceRequests) {
|
|
3314
3501
|
const searchQueries = targetApiSearchQueries(input.target_query);
|
|
3315
3502
|
const grouped = new Map();
|
|
3316
|
-
const sourceRequests = [];
|
|
3317
3503
|
let rawMatchesScanned = 0;
|
|
3504
|
+
let searchQueriesChecked = 0;
|
|
3318
3505
|
let lastMeta;
|
|
3319
3506
|
for (const searchQuery of searchQueries) {
|
|
3507
|
+
if (!tryConsumeBudget(budget)) {
|
|
3508
|
+
addUniqueWarning(warnings, "Upstream request budget was exhausted before all target search variants could be checked.");
|
|
3509
|
+
break;
|
|
3510
|
+
}
|
|
3511
|
+
searchQueriesChecked += 1;
|
|
3320
3512
|
let api;
|
|
3321
3513
|
try {
|
|
3322
3514
|
api = await client.searchTargets({
|
|
@@ -3329,8 +3521,10 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
3329
3521
|
page: 1,
|
|
3330
3522
|
page_size: Math.max(input.max_results * input.max_targets_per_program, input.max_results)
|
|
3331
3523
|
});
|
|
3524
|
+
refundBudgetIfNoUpstreamRequest(budget, api);
|
|
3332
3525
|
}
|
|
3333
3526
|
catch (error) {
|
|
3527
|
+
refundBudgetIfNoUpstreamError(budget, error);
|
|
3334
3528
|
if (error instanceof BBRadarApiError && (error.status === 404 || error.status === 405)) {
|
|
3335
3529
|
warnings.push("Direct target search API is unavailable; fell back to sampled program target lists.");
|
|
3336
3530
|
return undefined;
|
|
@@ -3371,15 +3565,93 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
3371
3565
|
if (!targetMatchesQuery(target, input.target_query, input.match_mode)) {
|
|
3372
3566
|
continue;
|
|
3373
3567
|
}
|
|
3374
|
-
const existing = grouped.get(programId) ?? {
|
|
3375
|
-
|
|
3568
|
+
const existing = grouped.get(programId) ?? {
|
|
3569
|
+
program: addProgramResourceLinks({ ...program, id: programId }),
|
|
3570
|
+
targets: [],
|
|
3571
|
+
targetKeys: new Set()
|
|
3572
|
+
};
|
|
3573
|
+
const targetKey = stableStringify({
|
|
3574
|
+
identifier: targetIdentifier(target),
|
|
3575
|
+
target_type: stringField(target, "target_type")
|
|
3576
|
+
});
|
|
3577
|
+
if (!existing.targetKeys.has(targetKey)) {
|
|
3578
|
+
existing.targetKeys.add(targetKey);
|
|
3579
|
+
existing.targets.push(target);
|
|
3580
|
+
}
|
|
3376
3581
|
grouped.set(programId, existing);
|
|
3377
3582
|
}
|
|
3378
3583
|
}
|
|
3379
|
-
if (grouped.size >= input.max_results
|
|
3584
|
+
if (grouped.size >= input.max_results ||
|
|
3585
|
+
(grouped.size > 0 && input.match_mode === "smart")) {
|
|
3380
3586
|
break;
|
|
3381
3587
|
}
|
|
3382
3588
|
}
|
|
3589
|
+
if (grouped.size > 0 && input.match_mode === "smart") {
|
|
3590
|
+
await mapWithConcurrency([...grouped.entries()].slice(0, input.max_results), TARGET_SAMPLE_CONCURRENCY, async ([programId, entry]) => {
|
|
3591
|
+
if (!tryConsumeBudget(budget)) {
|
|
3592
|
+
addUniqueWarning(warnings, "Upstream request budget was exhausted before matched program target lists could be hydrated.");
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
try {
|
|
3596
|
+
const targetsApi = await client.getProgramTargets(programId);
|
|
3597
|
+
refundBudgetIfNoUpstreamRequest(budget, targetsApi);
|
|
3598
|
+
sourceRequests.push({
|
|
3599
|
+
source: "program_targets",
|
|
3600
|
+
program_id: programId,
|
|
3601
|
+
request_id: targetsApi.requestId,
|
|
3602
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
3603
|
+
...apiSourceMetadata(targetsApi)
|
|
3604
|
+
});
|
|
3605
|
+
const targetsData = readObject(targetsApi.data);
|
|
3606
|
+
const hydratedTargets = readArray(targetsData?.targets).map(sanitizeTarget);
|
|
3607
|
+
const hydratedTargetOrder = new Map(hydratedTargets.map((target, index) => [
|
|
3608
|
+
stableStringify({
|
|
3609
|
+
identifier: targetIdentifier(target),
|
|
3610
|
+
target_type: stringField(target, "target_type")
|
|
3611
|
+
}),
|
|
3612
|
+
index
|
|
3613
|
+
]));
|
|
3614
|
+
for (const target of hydratedTargets) {
|
|
3615
|
+
if (!targetIdentifier(target) ||
|
|
3616
|
+
!targetHasAllowedScope(target, {
|
|
3617
|
+
include_out_of_scope: input.include_out_of_scope,
|
|
3618
|
+
include_ineligible: input.include_ineligible,
|
|
3619
|
+
strict_scope_filter: false
|
|
3620
|
+
}) ||
|
|
3621
|
+
!targetMatchesQuery(target, input.target_query, input.match_mode)) {
|
|
3622
|
+
continue;
|
|
3623
|
+
}
|
|
3624
|
+
const targetKey = stableStringify({
|
|
3625
|
+
identifier: targetIdentifier(target),
|
|
3626
|
+
target_type: stringField(target, "target_type")
|
|
3627
|
+
});
|
|
3628
|
+
if (!entry.targetKeys.has(targetKey)) {
|
|
3629
|
+
entry.targetKeys.add(targetKey);
|
|
3630
|
+
entry.targets.push(target);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
entry.targets.sort((left, right) => {
|
|
3634
|
+
const leftKey = stableStringify({
|
|
3635
|
+
identifier: targetIdentifier(left),
|
|
3636
|
+
target_type: stringField(left, "target_type")
|
|
3637
|
+
});
|
|
3638
|
+
const rightKey = stableStringify({
|
|
3639
|
+
identifier: targetIdentifier(right),
|
|
3640
|
+
target_type: stringField(right, "target_type")
|
|
3641
|
+
});
|
|
3642
|
+
return ((hydratedTargetOrder.get(leftKey) ?? Number.MAX_SAFE_INTEGER) -
|
|
3643
|
+
(hydratedTargetOrder.get(rightKey) ?? Number.MAX_SAFE_INTEGER));
|
|
3644
|
+
});
|
|
3645
|
+
}
|
|
3646
|
+
catch (error) {
|
|
3647
|
+
refundBudgetIfNoUpstreamError(budget, error);
|
|
3648
|
+
if (!isProgramNotFoundError(error)) {
|
|
3649
|
+
throw error;
|
|
3650
|
+
}
|
|
3651
|
+
addUniqueWarning(warnings, `Could not hydrate target matches for ${programId}; returned direct target-search matches only.`);
|
|
3652
|
+
}
|
|
3653
|
+
});
|
|
3654
|
+
}
|
|
3383
3655
|
const matches = [...grouped.values()].slice(0, input.max_results).map((entry) => stripUndefined({
|
|
3384
3656
|
program: formatProgram(entry.program, "compact"),
|
|
3385
3657
|
match_count: entry.targets.length,
|
|
@@ -3407,6 +3679,14 @@ async function searchProgramsByTargetApi(client, config, input, warnings) {
|
|
|
3407
3679
|
},
|
|
3408
3680
|
matches,
|
|
3409
3681
|
programs: matches.map((match) => readObject(match.program)).filter((program) => program !== undefined),
|
|
3682
|
+
upstream_budget: {
|
|
3683
|
+
used: budget.initial - budget.remaining,
|
|
3684
|
+
remaining: budget.remaining
|
|
3685
|
+
},
|
|
3686
|
+
ranking_scope: {
|
|
3687
|
+
mode: "target_search_api",
|
|
3688
|
+
possibly_incomplete: grouped.size < input.max_results && searchQueriesChecked < searchQueries.length
|
|
3689
|
+
},
|
|
3410
3690
|
meta: stripUndefined({
|
|
3411
3691
|
...(lastMeta ?? {}),
|
|
3412
3692
|
returned: matches.length,
|
|
@@ -3503,7 +3783,8 @@ async function listFilters(client, input) {
|
|
|
3503
3783
|
}
|
|
3504
3784
|
}
|
|
3505
3785
|
}
|
|
3506
|
-
catch {
|
|
3786
|
+
catch (error) {
|
|
3787
|
+
refundBudgetIfNoUpstreamError(budget, error);
|
|
3507
3788
|
// Facets are best-effort; list_filters should still return observed program filters.
|
|
3508
3789
|
}
|
|
3509
3790
|
if (input.include_target_facets && input.target_sample_programs > 0) {
|
|
@@ -3639,11 +3920,15 @@ async function comparePrograms(client, config, input) {
|
|
|
3639
3920
|
});
|
|
3640
3921
|
}
|
|
3641
3922
|
async function summarizeProgramActivity(client, config, programId, recentChangesLimit) {
|
|
3642
|
-
const programApi = await
|
|
3923
|
+
const [programApi, changes] = await Promise.all([
|
|
3924
|
+
client.getProgram(programId),
|
|
3925
|
+
recentChangesLimit > 0
|
|
3926
|
+
? fetchProgramChanges(client, config, programId, recentChangesLimit, true, true, true)
|
|
3927
|
+
: Promise.resolve(undefined)
|
|
3928
|
+
]);
|
|
3643
3929
|
const program = addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl));
|
|
3644
3930
|
const scopeSummary = readObject(program.scope_summary);
|
|
3645
3931
|
const targetActivity = readObject(scopeSummary?.target_activity);
|
|
3646
|
-
const changes = recentChangesLimit > 0 ? await fetchProgramChanges(client, config, programId, recentChangesLimit, true, true, true) : undefined;
|
|
3647
3932
|
return {
|
|
3648
3933
|
request_id: randomUUID(),
|
|
3649
3934
|
source_requests: [
|
|
@@ -3670,12 +3955,14 @@ async function getLatestAddedTargets(client, config, input) {
|
|
|
3670
3955
|
include_removed: false,
|
|
3671
3956
|
include_ineligible: input.include_ineligible,
|
|
3672
3957
|
include_out_of_scope: input.include_out_of_scope,
|
|
3958
|
+
platforms: input.platforms,
|
|
3673
3959
|
tags: [input.target_type],
|
|
3674
3960
|
page: 1,
|
|
3675
3961
|
page_size: input.recent_changes_page_size
|
|
3676
3962
|
});
|
|
3677
3963
|
const recentData = readObject(recentApi.data);
|
|
3678
3964
|
const changes = readArray(recentData?.results).map((change) => sanitizeChange(change, config.webBaseUrl));
|
|
3965
|
+
const platformFilters = new Set(input.platforms.map(normalizeTag));
|
|
3679
3966
|
const matchingChanges = changes
|
|
3680
3967
|
.filter((change) => {
|
|
3681
3968
|
const target = readObject(change.target);
|
|
@@ -3684,6 +3971,7 @@ async function getLatestAddedTargets(client, config, input) {
|
|
|
3684
3971
|
programId !== undefined &&
|
|
3685
3972
|
target !== undefined &&
|
|
3686
3973
|
targetMatchesRequestedType(target, input.target_type) &&
|
|
3974
|
+
latestAddedProgramMatches(change, input, platformFilters) &&
|
|
3687
3975
|
targetHasAllowedScope(target, {
|
|
3688
3976
|
include_out_of_scope: input.include_out_of_scope,
|
|
3689
3977
|
include_ineligible: input.include_ineligible,
|
|
@@ -3691,6 +3979,11 @@ async function getLatestAddedTargets(client, config, input) {
|
|
|
3691
3979
|
}));
|
|
3692
3980
|
})
|
|
3693
3981
|
.sort((left, right) => timestampField(right, "changed_at") - timestampField(left, "changed_at"));
|
|
3982
|
+
const limitedMatches = matchingChanges.slice(0, input.max_results);
|
|
3983
|
+
const matches = limitedMatches.map((change, index) => ({
|
|
3984
|
+
rank: index + 1,
|
|
3985
|
+
...formatChange(change, "compact")
|
|
3986
|
+
}));
|
|
3694
3987
|
const latest = matchingChanges[0];
|
|
3695
3988
|
const sourceRequests = [
|
|
3696
3989
|
{
|
|
@@ -3705,9 +3998,12 @@ async function getLatestAddedTargets(client, config, input) {
|
|
|
3705
3998
|
request_id: randomUUID(),
|
|
3706
3999
|
source_requests: sourceRequests,
|
|
3707
4000
|
query: latestAddedTargetsQuery(input),
|
|
4001
|
+
matches: [],
|
|
3708
4002
|
new_targets: [],
|
|
3709
4003
|
meta: {
|
|
3710
4004
|
recent_changes_scanned: changes.length,
|
|
4005
|
+
matching_changes_scanned: 0,
|
|
4006
|
+
matches_returned: 0,
|
|
3711
4007
|
no_match: true
|
|
3712
4008
|
}
|
|
3713
4009
|
};
|
|
@@ -3750,11 +4046,14 @@ async function getLatestAddedTargets(client, config, input) {
|
|
|
3750
4046
|
query: latestAddedTargetsQuery(input),
|
|
3751
4047
|
latest_added_at: latestAddedAt,
|
|
3752
4048
|
program,
|
|
4049
|
+
matches,
|
|
3753
4050
|
new_targets: formatTargetList(latestNewTargets, input.full_target_list_mode),
|
|
3754
4051
|
full_targets: input.include_full_target_list ? formatTargetList(limitedFullTargets, input.full_target_list_mode) : undefined,
|
|
3755
4052
|
meta: stripUndefined({
|
|
3756
4053
|
recent_changes_scanned: changes.length,
|
|
3757
4054
|
matching_changes_scanned: matchingChanges.length,
|
|
4055
|
+
matches_returned: matches.length,
|
|
4056
|
+
matches_has_more: matches.length < matchingChanges.length,
|
|
3758
4057
|
new_targets_returned: formatTargetList(latestNewTargets, input.full_target_list_mode).length,
|
|
3759
4058
|
full_targets_total_active: totalActiveTargets,
|
|
3760
4059
|
full_targets_total_after_filters: totalAfterFilters,
|
|
@@ -3763,6 +4062,39 @@ async function getLatestAddedTargets(client, config, input) {
|
|
|
3763
4062
|
})
|
|
3764
4063
|
});
|
|
3765
4064
|
}
|
|
4065
|
+
function latestAddedProgramMatches(change, input, platformFilters) {
|
|
4066
|
+
const rawProgram = readObject(change.program);
|
|
4067
|
+
if (!rawProgram) {
|
|
4068
|
+
return false;
|
|
4069
|
+
}
|
|
4070
|
+
const program = addProgramResourceLinks(rawProgram);
|
|
4071
|
+
const rewardRange = readObject(program.reward_range);
|
|
4072
|
+
const platform = normalizeTag(stringField(program, "platform") ?? "");
|
|
4073
|
+
const bountyMin = numberField(rewardRange, "min");
|
|
4074
|
+
const bountyMax = numberField(rewardRange, "max");
|
|
4075
|
+
const publicReportCount = numberField(program, "public_report_count");
|
|
4076
|
+
if (platformFilters.size > 0 && !platformFilters.has(platform)) {
|
|
4077
|
+
return false;
|
|
4078
|
+
}
|
|
4079
|
+
if (input.min_reward !== undefined) {
|
|
4080
|
+
const reward = input.reward_threshold_mode === "min_at_least" ? bountyMin : bountyMax;
|
|
4081
|
+
if ((reward ?? 0) < input.min_reward) {
|
|
4082
|
+
return false;
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
if (input.require_known_report_count && publicReportCount === undefined) {
|
|
4086
|
+
return false;
|
|
4087
|
+
}
|
|
4088
|
+
if (input.max_public_report_count !== undefined) {
|
|
4089
|
+
if (publicReportCount === undefined) {
|
|
4090
|
+
return input.include_unknown_report_count;
|
|
4091
|
+
}
|
|
4092
|
+
if (publicReportCount > input.max_public_report_count) {
|
|
4093
|
+
return false;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
return true;
|
|
4097
|
+
}
|
|
3766
4098
|
async function getProgramTargetsPayload(client, input, programId) {
|
|
3767
4099
|
const api = await client.getProgramTargets(programId);
|
|
3768
4100
|
const data = readObject(api.data);
|
|
@@ -4319,9 +4651,11 @@ async function getProgramTargetBreakdownFromIndexFallback(client, indexFallback,
|
|
|
4319
4651
|
});
|
|
4320
4652
|
}
|
|
4321
4653
|
async function getProgramScopeDelta(client, config, input) {
|
|
4322
|
-
const programApi = await
|
|
4654
|
+
const [programApi, changesPayload] = await Promise.all([
|
|
4655
|
+
client.getProgram(input.program_id),
|
|
4656
|
+
fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed || input.change_type === "removed", input.include_ineligible, input.include_out_of_scope, input.max_targets)
|
|
4657
|
+
]);
|
|
4323
4658
|
const program = addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl));
|
|
4324
|
-
const changesPayload = await fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed || input.change_type === "removed", input.include_ineligible, input.include_out_of_scope, input.max_targets);
|
|
4325
4659
|
const filteredChanges = filterProgramScopeDeltaChanges(changesPayload.changes, input);
|
|
4326
4660
|
const limitedChanges = filteredChanges.slice(0, input.max_targets);
|
|
4327
4661
|
return stripUndefined({
|
|
@@ -4776,9 +5110,11 @@ async function getProgramBrief(client, config, input) {
|
|
|
4776
5110
|
});
|
|
4777
5111
|
}
|
|
4778
5112
|
async function getProgramDelta(client, config, input) {
|
|
4779
|
-
const programApi = await
|
|
5113
|
+
const [programApi, changes] = await Promise.all([
|
|
5114
|
+
client.getProgram(input.program_id),
|
|
5115
|
+
fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed, input.include_ineligible, input.include_out_of_scope)
|
|
5116
|
+
]);
|
|
4780
5117
|
const program = addProgramResourceLinks(sanitizeProgram(programApi.data, config.webBaseUrl));
|
|
4781
|
-
const changes = await fetchProgramChanges(client, config, input.program_id, input.page_size, input.include_removed, input.include_ineligible, input.include_out_of_scope);
|
|
4782
5118
|
return {
|
|
4783
5119
|
request_id: randomUUID(),
|
|
4784
5120
|
source_requests: [
|
|
@@ -5372,6 +5708,9 @@ function hasHighConfidenceProgramResolutionMatch(programs, webBaseUrl, query) {
|
|
|
5372
5708
|
const threshold = looksLikeDirectProgramHandleQuery(query) ? 95 : 85;
|
|
5373
5709
|
return programs.some((program) => programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score >= threshold);
|
|
5374
5710
|
}
|
|
5711
|
+
function hasViableProgramResolutionMatch(programs, scorePrograms) {
|
|
5712
|
+
return scorePrograms ? scorePrograms(programs) >= 80 : programs.length > 0;
|
|
5713
|
+
}
|
|
5375
5714
|
function bestProgramResolutionScore(programs, webBaseUrl, query) {
|
|
5376
5715
|
return programs.reduce((best, program) => Math.max(best, programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score), 0);
|
|
5377
5716
|
}
|
|
@@ -5511,7 +5850,13 @@ async function findPrograms(client, config, input) {
|
|
|
5511
5850
|
initial: input.upstream_request_budget,
|
|
5512
5851
|
remaining: input.upstream_request_budget
|
|
5513
5852
|
};
|
|
5514
|
-
const upstreamTags = uniqueStrings([
|
|
5853
|
+
const upstreamTags = uniqueStrings([
|
|
5854
|
+
...input.tags,
|
|
5855
|
+
...input.scope_tags,
|
|
5856
|
+
...input.target_types,
|
|
5857
|
+
...input.language_tags,
|
|
5858
|
+
...(input.min_wildcards > 0 ? ["wildcard"] : [])
|
|
5859
|
+
]);
|
|
5515
5860
|
const collected = await collectPrograms(client, {
|
|
5516
5861
|
platforms: input.platforms,
|
|
5517
5862
|
tags: upstreamTags,
|
|
@@ -5623,8 +5968,33 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5623
5968
|
const collections = [];
|
|
5624
5969
|
const queryCollections = [];
|
|
5625
5970
|
const queries = options.searchQueries.length > 0 ? options.searchQueries : [undefined];
|
|
5971
|
+
const fastQueries = queries.slice(0, FAST_RESOLVE_SEARCH_QUERIES);
|
|
5972
|
+
const remainingQueries = queries.slice(FAST_RESOLVE_SEARCH_QUERIES);
|
|
5626
5973
|
const primaryDirectPlatforms = options.platforms.length > 0 ? options.platforms : PRIMARY_PROGRAM_ID_PLATFORM_GUESSES;
|
|
5627
5974
|
const primaryDirectProgramIds = new Set(directProgramIdCandidates(options.query, primaryDirectPlatforms, { maxHandleVariants: 1 }));
|
|
5975
|
+
for (const search of fastQueries) {
|
|
5976
|
+
if (options.budget.remaining <= 0) {
|
|
5977
|
+
addUniqueWarning(options.warnings, "Program name search stopped at the request budget; pass a higher upstream_request_budget for a deeper resolution attempt.");
|
|
5978
|
+
break;
|
|
5979
|
+
}
|
|
5980
|
+
const collection = await collectPrograms(client, {
|
|
5981
|
+
search,
|
|
5982
|
+
platforms: options.platforms,
|
|
5983
|
+
tags: options.tags,
|
|
5984
|
+
updated_since: undefined,
|
|
5985
|
+
opportunity_levels: options.opportunity_levels,
|
|
5986
|
+
max_pages: 1,
|
|
5987
|
+
budget: options.budget,
|
|
5988
|
+
warnings: options.warnings
|
|
5989
|
+
});
|
|
5990
|
+
collections.push(collection);
|
|
5991
|
+
queryCollections.push({ search, collection });
|
|
5992
|
+
const merged = mergeProgramCollections(collections);
|
|
5993
|
+
if (options.isHighConfidenceMatch?.(merged.programs) ||
|
|
5994
|
+
hasViableProgramResolutionMatch(merged.programs, options.scorePrograms)) {
|
|
5995
|
+
return merged;
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5628
5998
|
if (primaryDirectProgramIds.size > 0 && options.budget.remaining > 0) {
|
|
5629
5999
|
const primaryDirectHandles = await collectDirectHandlePrograms(client, {
|
|
5630
6000
|
query: options.query,
|
|
@@ -5644,7 +6014,7 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5644
6014
|
return mergeProgramCollections(collections);
|
|
5645
6015
|
}
|
|
5646
6016
|
}
|
|
5647
|
-
for (const search of
|
|
6017
|
+
for (const search of remainingQueries) {
|
|
5648
6018
|
if (options.budget.remaining <= 0) {
|
|
5649
6019
|
addUniqueWarning(options.warnings, "Program name search stopped at the request budget; pass a higher upstream_request_budget for a deeper resolution attempt.");
|
|
5650
6020
|
break;
|
|
@@ -5665,7 +6035,9 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5665
6035
|
return mergeProgramCollections(collections);
|
|
5666
6036
|
}
|
|
5667
6037
|
}
|
|
5668
|
-
|
|
6038
|
+
const searchedCollection = collections.length > 0 ? mergeProgramCollections(collections) : undefined;
|
|
6039
|
+
if (options.budget.remaining > 0 &&
|
|
6040
|
+
(!searchedCollection || !hasViableProgramResolutionMatch(searchedCollection.programs, options.scorePrograms))) {
|
|
5669
6041
|
const directHandles = await collectDirectHandlePrograms(client, {
|
|
5670
6042
|
query: options.query,
|
|
5671
6043
|
platforms: options.platforms,
|
|
@@ -5752,9 +6124,14 @@ async function collectDirectHandlePrograms(client, options) {
|
|
|
5752
6124
|
const sourceRequests = [];
|
|
5753
6125
|
let requestsScanned = 0;
|
|
5754
6126
|
for (const programId of programIds) {
|
|
6127
|
+
if (!tryConsumeBudget(options.budget)) {
|
|
6128
|
+
addUniqueWarning(options.warnings, "Upstream request budget was exhausted before all direct program handle guesses could be checked.");
|
|
6129
|
+
break;
|
|
6130
|
+
}
|
|
5755
6131
|
requestsScanned += 1;
|
|
5756
6132
|
try {
|
|
5757
6133
|
const targetsApi = await client.getProgramTargets(programId);
|
|
6134
|
+
refundBudgetIfNoUpstreamRequest(options.budget, targetsApi);
|
|
5758
6135
|
const targetsData = readObject(targetsApi.data);
|
|
5759
6136
|
const hasTargetsArray = Array.isArray(targetsData?.targets);
|
|
5760
6137
|
const targets = readArray(targetsData?.targets).map(sanitizeTarget);
|
|
@@ -5775,6 +6152,7 @@ async function collectDirectHandlePrograms(client, options) {
|
|
|
5775
6152
|
}
|
|
5776
6153
|
}
|
|
5777
6154
|
catch (error) {
|
|
6155
|
+
refundBudgetIfNoUpstreamError(options.budget, error);
|
|
5778
6156
|
if (error instanceof BBRadarApiError && error.status === 404) {
|
|
5779
6157
|
sourceRequests.push({
|
|
5780
6158
|
source: options.source,
|
|
@@ -5782,7 +6160,9 @@ async function collectDirectHandlePrograms(client, options) {
|
|
|
5782
6160
|
program_id: programId,
|
|
5783
6161
|
request_id: error.requestId,
|
|
5784
6162
|
upstream_request_id: error.upstreamRequestId,
|
|
5785
|
-
status: error.status
|
|
6163
|
+
status: error.status,
|
|
6164
|
+
cache_hit: error.cached,
|
|
6165
|
+
cache_expires_at: error.cacheExpiresAt
|
|
5786
6166
|
});
|
|
5787
6167
|
continue;
|
|
5788
6168
|
}
|
|
@@ -6195,7 +6575,8 @@ async function collectTargetSamples(client, candidates, input, budget) {
|
|
|
6195
6575
|
.slice(0, input.target_sample_size);
|
|
6196
6576
|
samples[programId] = targets;
|
|
6197
6577
|
}
|
|
6198
|
-
catch {
|
|
6578
|
+
catch (error) {
|
|
6579
|
+
refundBudgetIfNoUpstreamError(budget, error);
|
|
6199
6580
|
samples[programId] = [];
|
|
6200
6581
|
}
|
|
6201
6582
|
});
|
|
@@ -6258,6 +6639,11 @@ function refundBudgetIfNoUpstreamRequest(budget, api, cost = 1) {
|
|
|
6258
6639
|
budget.remaining = Math.min(budget.initial, budget.remaining + cost);
|
|
6259
6640
|
}
|
|
6260
6641
|
}
|
|
6642
|
+
function refundBudgetIfNoUpstreamError(budget, error, cost = 1) {
|
|
6643
|
+
if (error instanceof BBRadarApiError && (error.cached || error.coalesced)) {
|
|
6644
|
+
budget.remaining = Math.min(budget.initial, budget.remaining + cost);
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6261
6647
|
function addUniqueWarning(warnings, warning) {
|
|
6262
6648
|
if (warnings && !warnings.includes(warning)) {
|
|
6263
6649
|
warnings.push(warning);
|
|
@@ -6360,7 +6746,12 @@ function readStringArrayField(value, field) {
|
|
|
6360
6746
|
const fieldValue = value[field];
|
|
6361
6747
|
return Array.isArray(fieldValue) ? fieldValue.filter((entry) => typeof entry === "string") : [];
|
|
6362
6748
|
}
|
|
6363
|
-
function registerPrompts(server) {
|
|
6749
|
+
function registerPrompts(server, profile) {
|
|
6750
|
+
const discoveryToolInstructions = profile === "core"
|
|
6751
|
+
? `- Start with find_programs and pass opportunity_levels from the requested value below unless the user asked for a broader search.
|
|
6752
|
+
- Express language, target type, reward, Web3, wildcard, VDP, and platform needs through find_programs filters.`
|
|
6753
|
+
: `- Start with find_program_candidates unless the user needs custom filters; pass opportunity_levels from the requested value below.
|
|
6754
|
+
- Use find_language_programs for language asks, find_target_type_programs for API/mobile/source-code/domain asks, find_reward_programs or find_paid_programs for reward asks, find_vdp_programs only for explicit VDP/no-bounty asks, find_web3_contests for Web3 contest/audit asks, find_web3_programs for general Web3 requests, and find_wildcard_programs for wildcard-specific requests.`;
|
|
6364
6755
|
server.registerPrompt("find_best_programs_to_hunt", {
|
|
6365
6756
|
title: "Find Best Programs To Hunt",
|
|
6366
6757
|
description: "Rank fresh BBRadar program data only. Do not invoke external bug bounty skills unless the user explicitly asks for methodology.",
|
|
@@ -6373,8 +6764,8 @@ function registerPrompts(server) {
|
|
|
6373
6764
|
}, (args) => promptResult(`Use the BBRadar MCP tools only. Do not invoke local bug bounty skills, methodology files, worklogs, or non-BBRadar tools unless the user explicitly asks for methodology. Do not perform active scanning or contact third-party targets. Treat results as fresh API reads; do not describe them as cached.
|
|
6374
6765
|
|
|
6375
6766
|
Find the best BBRadar program candidates.
|
|
6376
|
-
|
|
6377
|
-
- Use
|
|
6767
|
+
${discoveryToolInstructions}
|
|
6768
|
+
- Use this opportunity level JSON array: ${promptJson([args.opportunity_level ?? "elite"])}.
|
|
6378
6769
|
- Apply platform filters from this JSON string if provided: ${promptJson(args.platforms ?? "")}.
|
|
6379
6770
|
- Apply tag filters from this JSON string if provided: ${promptJson(args.tags ?? "")}.
|
|
6380
6771
|
- Keep the shortlist to this JSON value: ${promptJson(args.max_programs ?? "10")}.
|
|
@@ -6434,7 +6825,7 @@ async function runTool(toolName, config, rateLimiter, callback) {
|
|
|
6434
6825
|
},
|
|
6435
6826
|
mcp_rate_limit: toRateLimitPayload(decision),
|
|
6436
6827
|
mcp_timing: toolTimingPayload(toolName, durationMs)
|
|
6437
|
-
}, true);
|
|
6828
|
+
}, true, config.compactToolText);
|
|
6438
6829
|
}
|
|
6439
6830
|
try {
|
|
6440
6831
|
const payload = await callback();
|
|
@@ -6444,21 +6835,21 @@ async function runTool(toolName, config, rateLimiter, callback) {
|
|
|
6444
6835
|
...payload,
|
|
6445
6836
|
mcp_rate_limit: toRateLimitPayload(decision),
|
|
6446
6837
|
mcp_timing: toolTimingPayload(toolName, durationMs)
|
|
6447
|
-
});
|
|
6838
|
+
}, false, config.compactToolText);
|
|
6448
6839
|
}
|
|
6449
6840
|
catch (error) {
|
|
6450
6841
|
const durationMs = Date.now() - startedAt;
|
|
6451
6842
|
recordToolMetric(toolName, durationMs, "error");
|
|
6452
|
-
return toolResult(errorPayload(error, decision, toolName, durationMs), true);
|
|
6843
|
+
return toolResult(errorPayload(error, decision, toolName, durationMs), true, config.compactToolText);
|
|
6453
6844
|
}
|
|
6454
6845
|
}
|
|
6455
|
-
function toolResult(payload, isError = false) {
|
|
6846
|
+
function toolResult(payload, isError = false, compactText = false) {
|
|
6456
6847
|
const resourceLinks = collectResourceLinks(payload);
|
|
6457
6848
|
return {
|
|
6458
6849
|
content: [
|
|
6459
6850
|
{
|
|
6460
6851
|
type: "text",
|
|
6461
|
-
text: JSON.stringify(payload)
|
|
6852
|
+
text: compactText ? compactToolText(payload, isError) : JSON.stringify(payload)
|
|
6462
6853
|
},
|
|
6463
6854
|
...resourceLinks
|
|
6464
6855
|
],
|
|
@@ -6466,6 +6857,28 @@ function toolResult(payload, isError = false) {
|
|
|
6466
6857
|
...(isError ? { isError: true } : {})
|
|
6467
6858
|
};
|
|
6468
6859
|
}
|
|
6860
|
+
function compactToolText(payload, isError) {
|
|
6861
|
+
if (isError) {
|
|
6862
|
+
return JSON.stringify(stripUndefined({
|
|
6863
|
+
request_id: payload.request_id,
|
|
6864
|
+
error: payload.error,
|
|
6865
|
+
mcp_timing: payload.mcp_timing
|
|
6866
|
+
}));
|
|
6867
|
+
}
|
|
6868
|
+
const meta = readObject(payload.meta);
|
|
6869
|
+
const returned = numberField(meta, "returned") ??
|
|
6870
|
+
[
|
|
6871
|
+
readArray(payload.programs).length,
|
|
6872
|
+
readArray(payload.targets).length,
|
|
6873
|
+
readArray(payload.changes).length
|
|
6874
|
+
].find((count) => count > 0);
|
|
6875
|
+
return JSON.stringify(stripUndefined({
|
|
6876
|
+
request_id: payload.request_id,
|
|
6877
|
+
summary: "Full tool result is available in structuredContent.",
|
|
6878
|
+
returned,
|
|
6879
|
+
mcp_timing: payload.mcp_timing
|
|
6880
|
+
}));
|
|
6881
|
+
}
|
|
6469
6882
|
function promptResult(text) {
|
|
6470
6883
|
return {
|
|
6471
6884
|
messages: [
|
|
@@ -6605,18 +7018,25 @@ function compactRecord(values) {
|
|
|
6605
7018
|
}));
|
|
6606
7019
|
}
|
|
6607
7020
|
function latestAddedTargetsQuery(input) {
|
|
6608
|
-
return {
|
|
7021
|
+
return stripUndefined({
|
|
6609
7022
|
change_type: "added",
|
|
6610
7023
|
target_type: input.target_type,
|
|
7024
|
+
platforms: input.platforms.length > 0 ? input.platforms : undefined,
|
|
7025
|
+
min_reward: input.min_reward,
|
|
7026
|
+
reward_threshold_mode: input.reward_threshold_mode,
|
|
7027
|
+
max_public_report_count: input.max_public_report_count,
|
|
7028
|
+
require_known_report_count: input.require_known_report_count,
|
|
7029
|
+
include_unknown_report_count: input.include_unknown_report_count,
|
|
6611
7030
|
include_out_of_scope: input.include_out_of_scope,
|
|
6612
7031
|
include_ineligible: input.include_ineligible,
|
|
6613
7032
|
recent_changes_page_size: input.recent_changes_page_size,
|
|
7033
|
+
max_results: input.max_results,
|
|
6614
7034
|
include_full_target_list: input.include_full_target_list,
|
|
6615
7035
|
full_target_list_mode: input.full_target_list_mode,
|
|
6616
7036
|
full_target_limit: input.full_target_limit,
|
|
6617
7037
|
full_list_include_out_of_scope: input.full_list_include_out_of_scope,
|
|
6618
7038
|
full_list_include_ineligible: input.full_list_include_ineligible
|
|
6619
|
-
};
|
|
7039
|
+
});
|
|
6620
7040
|
}
|
|
6621
7041
|
function formatTargetList(targets, mode) {
|
|
6622
7042
|
if (mode === "identifiers") {
|