@bbradar/mcp 0.1.16 → 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 +112 -73
- 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
|
@@ -34,9 +34,31 @@ const STALE_PROGRAM_ID_RESOLVE_PAGES = 1;
|
|
|
34
34
|
const STALE_PROGRAM_ID_RESOLVE_BUDGET = 8;
|
|
35
35
|
const PROGRAM_FALLBACK_CACHE_MAX_ENTRIES = 200;
|
|
36
36
|
const MAX_RESOLVE_SEARCH_QUERIES = 4;
|
|
37
|
-
const MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS =
|
|
37
|
+
const MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS = 96;
|
|
38
38
|
const SDK_VERSION = "1.29.0";
|
|
39
|
-
const PROGRAM_ID_PLATFORM_GUESSES = [
|
|
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);
|
|
40
62
|
const WEB3_TAGS = ["web3", "crypto", "blockchain", "smart-contract", "smart contract", "defi", "nft", "ethereum", "solana", "solidity"];
|
|
41
63
|
const WEB3_CONTEST_SIGNALS = ["contest", "audit", "competitive", "code4rena", "sherlock", "cantina", "codehawks", "hats", "spearbit", "warden"];
|
|
42
64
|
const VDP_SIGNALS = ["vdp", "disclosure", "responsible disclosure", "vulnerability disclosure", "no bounty", "non-bounty", "no reward"];
|
|
@@ -150,7 +172,7 @@ function stringListSchema(description = "Optional filter values. Empty or omitte
|
|
|
150
172
|
.describe(description);
|
|
151
173
|
}
|
|
152
174
|
function platformListSchema() {
|
|
153
|
-
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.");
|
|
154
176
|
}
|
|
155
177
|
function tagListSchema() {
|
|
156
178
|
return stringListSchema("Optional combined scope/language/target-type tags. Empty or omitted means all tags.");
|
|
@@ -206,7 +228,7 @@ const programIdSchema = z
|
|
|
206
228
|
.trim()
|
|
207
229
|
.min(3)
|
|
208
230
|
.max(256)
|
|
209
|
-
.regex(/^[
|
|
231
|
+
.regex(/^[^:/?#\u0000-\u001F\u007F]+:[^?#\u0000-\u001F\u007F]+$/, "Use platform:handle, for example HackerOne:example-handle.")
|
|
210
232
|
.refine((value) => !/[\u0000-\u001F\u007F]/.test(value), {
|
|
211
233
|
message: "program_id cannot contain control characters."
|
|
212
234
|
})
|
|
@@ -3162,7 +3184,8 @@ async function findProgramsByTarget(client, config, input) {
|
|
|
3162
3184
|
budget,
|
|
3163
3185
|
warnings,
|
|
3164
3186
|
source: "target_query_program_id_guess",
|
|
3165
|
-
maxProgramIds:
|
|
3187
|
+
maxProgramIds: queryLooksLikeTargetIdentifier(input.target_query) || looksLikeDirectProgramHandleQuery(input.target_query) ? MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS : 4,
|
|
3188
|
+
maxHandleVariants: 1,
|
|
3166
3189
|
stopOnFirstMatch: true
|
|
3167
3190
|
});
|
|
3168
3191
|
sourceRequests.push(...directHandles.sourceRequests);
|
|
@@ -5346,7 +5369,8 @@ function programResolutionMatch(candidate, query) {
|
|
|
5346
5369
|
};
|
|
5347
5370
|
}
|
|
5348
5371
|
function hasHighConfidenceProgramResolutionMatch(programs, webBaseUrl, query) {
|
|
5349
|
-
|
|
5372
|
+
const threshold = looksLikeDirectProgramHandleQuery(query) ? 95 : 85;
|
|
5373
|
+
return programs.some((program) => programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score >= threshold);
|
|
5350
5374
|
}
|
|
5351
5375
|
function bestProgramResolutionScore(programs, webBaseUrl, query) {
|
|
5352
5376
|
return programs.reduce((best, program) => Math.max(best, programResolutionMatch(toProgramCandidate(program, webBaseUrl), query).score), 0);
|
|
@@ -5357,6 +5381,7 @@ function programResolutionSearchQueries(query) {
|
|
|
5357
5381
|
function programResolutionQueryVariants(query) {
|
|
5358
5382
|
const normalized = normalizeTag(query);
|
|
5359
5383
|
const meaningfulTokens = [...meaningfulProgramQueryTokens(query)];
|
|
5384
|
+
const searchTokens = meaningfulTokens.filter((token) => token.length >= 3);
|
|
5360
5385
|
const meaningfulPhrase = meaningfulTokens.join(" ");
|
|
5361
5386
|
const singleTokenNameVariants = meaningfulTokens.length === 1
|
|
5362
5387
|
? [
|
|
@@ -5370,7 +5395,7 @@ function programResolutionQueryVariants(query) {
|
|
|
5370
5395
|
normalized,
|
|
5371
5396
|
meaningfulPhrase,
|
|
5372
5397
|
meaningfulTokens.join("-"),
|
|
5373
|
-
...
|
|
5398
|
+
...searchTokens,
|
|
5374
5399
|
...singleTokenNameVariants
|
|
5375
5400
|
];
|
|
5376
5401
|
return uniqueStrings(values.filter((value) => value.length >= 2));
|
|
@@ -5598,15 +5623,18 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5598
5623
|
const collections = [];
|
|
5599
5624
|
const queryCollections = [];
|
|
5600
5625
|
const queries = options.searchQueries.length > 0 ? options.searchQueries : [undefined];
|
|
5601
|
-
const
|
|
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 }));
|
|
5602
5628
|
if (primaryDirectProgramIds.size > 0 && options.budget.remaining > 0) {
|
|
5603
5629
|
const primaryDirectHandles = await collectDirectHandlePrograms(client, {
|
|
5604
5630
|
query: options.query,
|
|
5605
|
-
platforms:
|
|
5631
|
+
platforms: primaryDirectPlatforms,
|
|
5606
5632
|
budget: options.budget,
|
|
5607
5633
|
warnings: options.warnings,
|
|
5608
5634
|
source: "program_id_handle_guess",
|
|
5609
|
-
maxProgramIds:
|
|
5635
|
+
maxProgramIds: MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS,
|
|
5636
|
+
maxHandleVariants: 1,
|
|
5637
|
+
acceptEmptyTargetsAsPrograms: true,
|
|
5610
5638
|
stopOnFirstMatch: true
|
|
5611
5639
|
});
|
|
5612
5640
|
if (primaryDirectHandles.programs.length > 0 || primaryDirectHandles.requestsScanned > 0) {
|
|
@@ -5645,6 +5673,8 @@ async function collectProgramsForResolution(client, options) {
|
|
|
5645
5673
|
warnings: options.warnings,
|
|
5646
5674
|
source: "program_id_handle_guess",
|
|
5647
5675
|
skipProgramIds: primaryDirectProgramIds,
|
|
5676
|
+
maxHandleVariants: 4,
|
|
5677
|
+
acceptEmptyTargetsAsPrograms: true,
|
|
5648
5678
|
stopOnFirstMatch: true
|
|
5649
5679
|
});
|
|
5650
5680
|
if (directHandles.programs.length > 0 || directHandles.requestsScanned > 0) {
|
|
@@ -5715,28 +5745,32 @@ function mergeProgramCollections(collections) {
|
|
|
5715
5745
|
};
|
|
5716
5746
|
}
|
|
5717
5747
|
async function collectDirectHandlePrograms(client, options) {
|
|
5718
|
-
const programIds = directProgramIdCandidates(options.query, options.platforms)
|
|
5748
|
+
const programIds = directProgramIdCandidates(options.query, options.platforms, { maxHandleVariants: options.maxHandleVariants })
|
|
5719
5749
|
.filter((programId) => !options.skipProgramIds?.has(programId))
|
|
5720
5750
|
.slice(0, options.maxProgramIds ?? MAX_DIRECT_HANDLE_RESOLUTION_REQUESTS);
|
|
5721
5751
|
const programs = [];
|
|
5722
5752
|
const sourceRequests = [];
|
|
5723
5753
|
let requestsScanned = 0;
|
|
5724
5754
|
for (const programId of programIds) {
|
|
5725
|
-
if (!tryConsumeBudget(options.budget)) {
|
|
5726
|
-
break;
|
|
5727
|
-
}
|
|
5728
5755
|
requestsScanned += 1;
|
|
5729
5756
|
try {
|
|
5730
|
-
const
|
|
5731
|
-
|
|
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
|
+
}
|
|
5732
5765
|
sourceRequests.push({
|
|
5733
5766
|
source: options.source,
|
|
5767
|
+
endpoint: "program_targets",
|
|
5734
5768
|
program_id: programId,
|
|
5735
|
-
request_id:
|
|
5736
|
-
upstream_request_id:
|
|
5737
|
-
...apiSourceMetadata(
|
|
5769
|
+
request_id: targetsApi.requestId,
|
|
5770
|
+
upstream_request_id: targetsApi.upstreamRequestId,
|
|
5771
|
+
...apiSourceMetadata(targetsApi)
|
|
5738
5772
|
});
|
|
5739
|
-
if (options.stopOnFirstMatch) {
|
|
5773
|
+
if (programEndpointExists && options.stopOnFirstMatch) {
|
|
5740
5774
|
break;
|
|
5741
5775
|
}
|
|
5742
5776
|
}
|
|
@@ -5744,49 +5778,12 @@ async function collectDirectHandlePrograms(client, options) {
|
|
|
5744
5778
|
if (error instanceof BBRadarApiError && error.status === 404) {
|
|
5745
5779
|
sourceRequests.push({
|
|
5746
5780
|
source: options.source,
|
|
5747
|
-
endpoint: "
|
|
5781
|
+
endpoint: "program_targets",
|
|
5748
5782
|
program_id: programId,
|
|
5749
5783
|
request_id: error.requestId,
|
|
5750
5784
|
upstream_request_id: error.upstreamRequestId,
|
|
5751
5785
|
status: error.status
|
|
5752
5786
|
});
|
|
5753
|
-
if (!tryConsumeBudget(options.budget)) {
|
|
5754
|
-
break;
|
|
5755
|
-
}
|
|
5756
|
-
requestsScanned += 1;
|
|
5757
|
-
try {
|
|
5758
|
-
const targetsApi = await client.getProgramTargets(programId);
|
|
5759
|
-
const targetsData = readObject(targetsApi.data);
|
|
5760
|
-
const targets = readArray(targetsData?.targets).map(sanitizeTarget);
|
|
5761
|
-
if (targets.length > 0) {
|
|
5762
|
-
programs.push(syntheticProgramFromProgramId(programId, targets));
|
|
5763
|
-
}
|
|
5764
|
-
sourceRequests.push({
|
|
5765
|
-
source: options.source,
|
|
5766
|
-
endpoint: "program_targets",
|
|
5767
|
-
program_id: programId,
|
|
5768
|
-
request_id: targetsApi.requestId,
|
|
5769
|
-
upstream_request_id: targetsApi.upstreamRequestId,
|
|
5770
|
-
...apiSourceMetadata(targetsApi)
|
|
5771
|
-
});
|
|
5772
|
-
if (targets.length > 0 && options.stopOnFirstMatch) {
|
|
5773
|
-
break;
|
|
5774
|
-
}
|
|
5775
|
-
}
|
|
5776
|
-
catch (targetError) {
|
|
5777
|
-
if (targetError instanceof BBRadarApiError && targetError.status === 404) {
|
|
5778
|
-
sourceRequests.push({
|
|
5779
|
-
source: options.source,
|
|
5780
|
-
endpoint: "program_targets",
|
|
5781
|
-
program_id: programId,
|
|
5782
|
-
request_id: targetError.requestId,
|
|
5783
|
-
upstream_request_id: targetError.upstreamRequestId,
|
|
5784
|
-
status: targetError.status
|
|
5785
|
-
});
|
|
5786
|
-
continue;
|
|
5787
|
-
}
|
|
5788
|
-
throw targetError;
|
|
5789
|
-
}
|
|
5790
5787
|
continue;
|
|
5791
5788
|
}
|
|
5792
5789
|
throw error;
|
|
@@ -5804,11 +5801,12 @@ function syntheticProgramFromProgramId(programId, targets) {
|
|
|
5804
5801
|
const separatorIndex = programId.indexOf(":");
|
|
5805
5802
|
const platform = separatorIndex >= 0 ? programId.slice(0, separatorIndex) : undefined;
|
|
5806
5803
|
const handle = separatorIndex >= 0 ? programId.slice(separatorIndex + 1) : programId;
|
|
5804
|
+
const displayHandle = handle.split("/").filter(Boolean).at(-1) ?? handle;
|
|
5807
5805
|
const scopeTags = uniqueStrings(targets.flatMap((target) => readStringArrayField(target, "scope_tags")));
|
|
5808
5806
|
return stripUndefined({
|
|
5809
5807
|
id: programId,
|
|
5810
5808
|
handle,
|
|
5811
|
-
name: titleizeProgramHandle(
|
|
5809
|
+
name: titleizeProgramHandle(displayHandle),
|
|
5812
5810
|
platform,
|
|
5813
5811
|
scope_tags: scopeTags,
|
|
5814
5812
|
target_counts: {
|
|
@@ -5827,38 +5825,75 @@ function titleizeProgramHandle(handle) {
|
|
|
5827
5825
|
.map((part) => part.slice(0, 1).toUpperCase() + part.slice(1))
|
|
5828
5826
|
.join(" ");
|
|
5829
5827
|
}
|
|
5830
|
-
function directProgramIdCandidates(query, platforms) {
|
|
5828
|
+
function directProgramIdCandidates(query, platforms, options = {}) {
|
|
5831
5829
|
const platformGuesses = (platforms.length > 0 ? platforms : PROGRAM_ID_PLATFORM_GUESSES).map(canonicalProgramIdPlatform);
|
|
5832
|
-
const handles = directProgramHandleVariants(query);
|
|
5833
|
-
return uniqueStrings(
|
|
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
5834
|
}
|
|
5835
5835
|
function canonicalProgramIdPlatform(platform) {
|
|
5836
5836
|
const normalized = normalizeTag(platform).replace(/[^a-z0-9]/g, "");
|
|
5837
5837
|
const knownPlatforms = {
|
|
5838
5838
|
hackerone: "HackerOne",
|
|
5839
|
-
bugcrowd: "
|
|
5839
|
+
bugcrowd: "BugCrowd",
|
|
5840
5840
|
intigriti: "Intigriti",
|
|
5841
5841
|
hackenproof: "HackenProof",
|
|
5842
5842
|
yeswehack: "YesWeHack",
|
|
5843
|
+
immunefi: "Immunefi",
|
|
5843
5844
|
cantina: "Cantina",
|
|
5844
5845
|
code4rena: "Code4rena",
|
|
5845
|
-
|
|
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"
|
|
5846
5858
|
};
|
|
5847
5859
|
return knownPlatforms[normalized] ?? platform.trim();
|
|
5848
5860
|
}
|
|
5849
5861
|
function directProgramHandleVariants(query) {
|
|
5850
5862
|
const handleText = query.includes(":") ? query.slice(query.indexOf(":") + 1) : query;
|
|
5851
5863
|
const normalized = normalizeTargetIdentifierForMatch(handleText);
|
|
5864
|
+
const literal = directProgramLiteralHandleVariant(handleText);
|
|
5852
5865
|
const dashed = normalized.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5853
5866
|
const compact = dashed.replace(/-/g, "");
|
|
5854
5867
|
const tokens = [...meaningfulProgramQueryTokens(handleText)];
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
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));
|
|
5862
5897
|
}
|
|
5863
5898
|
async function collectProgramSources(client, sources, options) {
|
|
5864
5899
|
const pages = [];
|
|
@@ -6633,7 +6668,7 @@ function targetMatchesQuery(target, query, mode) {
|
|
|
6633
6668
|
}
|
|
6634
6669
|
return (containsMatch ||
|
|
6635
6670
|
profile.values.some((value) => queryValues.some((queryValue) => targetSuffixMatch(value, queryValue) || targetWildcardMatch(value, queryValue))) ||
|
|
6636
|
-
targetTokensMatch(profile.tokens, query));
|
|
6671
|
+
(!queryLooksLikeTargetIdentifier(query) && targetTokensMatch(profile.tokens, query)));
|
|
6637
6672
|
}
|
|
6638
6673
|
function upstreamTargetMatchMode(mode) {
|
|
6639
6674
|
return mode === "smart" ? "contains" : mode;
|
|
@@ -6671,7 +6706,11 @@ function targetSearchProfile(target) {
|
|
|
6671
6706
|
};
|
|
6672
6707
|
}
|
|
6673
6708
|
function targetQueryVariants(query) {
|
|
6674
|
-
|
|
6709
|
+
const variants = targetStringVariants(query);
|
|
6710
|
+
if (!queryLooksLikeTargetIdentifier(query)) {
|
|
6711
|
+
return variants;
|
|
6712
|
+
}
|
|
6713
|
+
return variants.filter(targetValueCanContainQuery);
|
|
6675
6714
|
}
|
|
6676
6715
|
function targetApiSearchQueries(query) {
|
|
6677
6716
|
const normalized = normalizeTargetIdentifierForMatch(query);
|