mobbdev 1.0.102 → 1.0.104
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/index.mjs +510 -380
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -501,6 +501,7 @@ var Vulnerability_Report_Issue_Tag_Enum = /* @__PURE__ */ ((Vulnerability_Report
|
|
|
501
501
|
Vulnerability_Report_Issue_Tag_Enum3["AutogeneratedCode"] = "AUTOGENERATED_CODE";
|
|
502
502
|
Vulnerability_Report_Issue_Tag_Enum3["AuxiliaryCode"] = "AUXILIARY_CODE";
|
|
503
503
|
Vulnerability_Report_Issue_Tag_Enum3["FalsePositive"] = "FALSE_POSITIVE";
|
|
504
|
+
Vulnerability_Report_Issue_Tag_Enum3["Suppressed"] = "SUPPRESSED";
|
|
504
505
|
Vulnerability_Report_Issue_Tag_Enum3["TestCode"] = "TEST_CODE";
|
|
505
506
|
Vulnerability_Report_Issue_Tag_Enum3["Unfixable"] = "UNFIXABLE";
|
|
506
507
|
Vulnerability_Report_Issue_Tag_Enum3["VendorCode"] = "VENDOR_CODE";
|
|
@@ -555,6 +556,80 @@ var FixDetailsFragmentDoc = `
|
|
|
555
556
|
}
|
|
556
557
|
}
|
|
557
558
|
`;
|
|
559
|
+
var FixReportSummaryFieldsFragmentDoc = `
|
|
560
|
+
fragment FixReportSummaryFields on fixReport {
|
|
561
|
+
id
|
|
562
|
+
createdOn
|
|
563
|
+
repo {
|
|
564
|
+
originalUrl
|
|
565
|
+
}
|
|
566
|
+
issueTypes
|
|
567
|
+
CRITICAL: fixes_aggregate(
|
|
568
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "critical"}}]}
|
|
569
|
+
) {
|
|
570
|
+
aggregate {
|
|
571
|
+
count
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
HIGH: fixes_aggregate(
|
|
575
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "high"}}]}
|
|
576
|
+
) {
|
|
577
|
+
aggregate {
|
|
578
|
+
count
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
MEDIUM: fixes_aggregate(
|
|
582
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "medium"}}]}
|
|
583
|
+
) {
|
|
584
|
+
aggregate {
|
|
585
|
+
count
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
LOW: fixes_aggregate(
|
|
589
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "low"}}]}
|
|
590
|
+
) {
|
|
591
|
+
aggregate {
|
|
592
|
+
count
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
fixes(
|
|
596
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, $filters]}
|
|
597
|
+
order_by: {severityValue: desc}
|
|
598
|
+
limit: $limit
|
|
599
|
+
offset: $offset
|
|
600
|
+
) {
|
|
601
|
+
...FixDetails
|
|
602
|
+
}
|
|
603
|
+
filteredFixesCount: fixes_aggregate(
|
|
604
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, $filters]}
|
|
605
|
+
) {
|
|
606
|
+
aggregate {
|
|
607
|
+
count
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
totalFixesCount: fixes_aggregate {
|
|
611
|
+
aggregate {
|
|
612
|
+
count
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
vulnerabilityReport {
|
|
616
|
+
scanDate
|
|
617
|
+
vendor
|
|
618
|
+
totalVulnerabilityReportIssuesCount: vulnerabilityReportIssues_aggregate {
|
|
619
|
+
aggregate {
|
|
620
|
+
count
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
notFixableVulnerabilityReportIssuesCount: vulnerabilityReportIssues_aggregate(
|
|
624
|
+
where: {category: {_neq: "Fixable"}}
|
|
625
|
+
) {
|
|
626
|
+
aggregate {
|
|
627
|
+
count
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
${FixDetailsFragmentDoc}`;
|
|
558
633
|
var MeDocument = `
|
|
559
634
|
query Me {
|
|
560
635
|
me {
|
|
@@ -984,80 +1059,24 @@ var AutoPrAnalysisDocument = `
|
|
|
984
1059
|
}
|
|
985
1060
|
}
|
|
986
1061
|
`;
|
|
987
|
-
var GetMcpFixesDocument = `
|
|
988
|
-
query GetMCPFixes($fixReportId: uuid!) {
|
|
989
|
-
fix(where: {fixReportId: {_eq: $fixReportId}}) {
|
|
990
|
-
...FixDetails
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
${FixDetailsFragmentDoc}`;
|
|
994
1062
|
var GetLatestReportByRepoUrlDocument = `
|
|
995
|
-
query GetLatestReportByRepoUrl($repoUrl: String!, $limit: Int
|
|
1063
|
+
query GetLatestReportByRepoUrl($repoUrl: String!, $filters: fix_bool_exp = {}, $limit: Int!, $offset: Int!) {
|
|
996
1064
|
fixReport(
|
|
997
1065
|
where: {repo: {originalUrl: {_eq: $repoUrl}}}
|
|
998
1066
|
order_by: {createdOn: desc}
|
|
999
1067
|
limit: 1
|
|
1000
1068
|
) {
|
|
1001
|
-
|
|
1002
|
-
createdOn
|
|
1003
|
-
repo {
|
|
1004
|
-
originalUrl
|
|
1005
|
-
}
|
|
1006
|
-
issueTypes
|
|
1007
|
-
fixes_aggregate(
|
|
1008
|
-
where: {vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}
|
|
1009
|
-
) {
|
|
1010
|
-
aggregate {
|
|
1011
|
-
count
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
CRITICAL: fixes_aggregate(
|
|
1015
|
-
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "critical"}}]}
|
|
1016
|
-
) {
|
|
1017
|
-
aggregate {
|
|
1018
|
-
count
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
HIGH: fixes_aggregate(
|
|
1022
|
-
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "high"}}]}
|
|
1023
|
-
) {
|
|
1024
|
-
aggregate {
|
|
1025
|
-
count
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
MEDIUM: fixes_aggregate(
|
|
1029
|
-
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "medium"}}]}
|
|
1030
|
-
) {
|
|
1031
|
-
aggregate {
|
|
1032
|
-
count
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
LOW: fixes_aggregate(
|
|
1036
|
-
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "low"}}]}
|
|
1037
|
-
) {
|
|
1038
|
-
aggregate {
|
|
1039
|
-
count
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
fixes(
|
|
1043
|
-
where: {vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}
|
|
1044
|
-
order_by: {severityValue: desc}
|
|
1045
|
-
limit: $limit
|
|
1046
|
-
) {
|
|
1047
|
-
...FixDetails
|
|
1048
|
-
}
|
|
1049
|
-
vulnerabilityReport {
|
|
1050
|
-
scanDate
|
|
1051
|
-
vendor
|
|
1052
|
-
vulnerabilityReportIssues_aggregate(where: {category: {_eq: "Fixable"}}) {
|
|
1053
|
-
aggregate {
|
|
1054
|
-
count
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1069
|
+
...FixReportSummaryFields
|
|
1058
1070
|
}
|
|
1059
1071
|
}
|
|
1060
|
-
${
|
|
1072
|
+
${FixReportSummaryFieldsFragmentDoc}`;
|
|
1073
|
+
var GetReportFixesDocument = `
|
|
1074
|
+
query GetReportFixes($reportId: uuid!, $filters: fix_bool_exp = {}, $limit: Int!, $offset: Int!) {
|
|
1075
|
+
fixReport(where: {id: {_eq: $reportId}}) {
|
|
1076
|
+
...FixReportSummaryFields
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
${FixReportSummaryFieldsFragmentDoc}`;
|
|
1061
1080
|
var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
|
|
1062
1081
|
function getSdk(client, withWrapper = defaultWrapper) {
|
|
1063
1082
|
return {
|
|
@@ -1124,11 +1143,11 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
1124
1143
|
autoPrAnalysis(variables, requestHeaders, signal) {
|
|
1125
1144
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: AutoPrAnalysisDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "autoPrAnalysis", "mutation", variables);
|
|
1126
1145
|
},
|
|
1127
|
-
GetMCPFixes(variables, requestHeaders, signal) {
|
|
1128
|
-
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetMcpFixesDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetMCPFixes", "query", variables);
|
|
1129
|
-
},
|
|
1130
1146
|
GetLatestReportByRepoUrl(variables, requestHeaders, signal) {
|
|
1131
1147
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetLatestReportByRepoUrlDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetLatestReportByRepoUrl", "query", variables);
|
|
1148
|
+
},
|
|
1149
|
+
GetReportFixes(variables, requestHeaders, signal) {
|
|
1150
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetReportFixesDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetReportFixes", "query", variables);
|
|
1132
1151
|
}
|
|
1133
1152
|
};
|
|
1134
1153
|
}
|
|
@@ -1534,6 +1553,8 @@ function getTagTooltip(tag) {
|
|
|
1534
1553
|
return "Issue found in supporting files that don't impact core functionality";
|
|
1535
1554
|
case "Filtered":
|
|
1536
1555
|
return "Issue was filtered by user in the Fix Policy";
|
|
1556
|
+
case "SUPPRESSED":
|
|
1557
|
+
return "Suppressed in the scan report";
|
|
1537
1558
|
default:
|
|
1538
1559
|
return tag;
|
|
1539
1560
|
}
|
|
@@ -1544,7 +1565,8 @@ var issueDescription = {
|
|
|
1544
1565
|
["FALSE_POSITIVE" /* FalsePositive */]: "The flagged code **does not represent an actual vulnerability within the application's context.** This categorization indicates that the issue is either misidentified by the scanner or deemed irrelevant to the application's functionality.",
|
|
1545
1566
|
["TEST_CODE" /* TestCode */]: "The flagged code resides in a test-specific path or context. This categorization indicates that **it supports testing scenarios and is isolated from production use**.",
|
|
1546
1567
|
["UNFIXABLE" /* Unfixable */]: "The flagged code cannot be fixed",
|
|
1547
|
-
["VENDOR_CODE" /* VendorCode */]: "The flagged code originates from a third-party library or dependency maintained externally. This categorization suggests that **the issue lies outside the application's direct control** and should be addressed by the vendor if necessary."
|
|
1568
|
+
["VENDOR_CODE" /* VendorCode */]: "The flagged code originates from a third-party library or dependency maintained externally. This categorization suggests that **the issue lies outside the application's direct control** and should be addressed by the vendor if necessary.",
|
|
1569
|
+
["SUPPRESSED" /* Suppressed */]: "Suppressed in the scan report."
|
|
1548
1570
|
};
|
|
1549
1571
|
function replaceKeysWithValues(fixDescription, extraContext) {
|
|
1550
1572
|
let result = fixDescription;
|
|
@@ -3925,7 +3947,7 @@ var GetProjectMembersDataZ = z11.object({
|
|
|
3925
3947
|
}),
|
|
3926
3948
|
user: z11.object({
|
|
3927
3949
|
id: z11.string().uuid(),
|
|
3928
|
-
picture: z11.string().optional(),
|
|
3950
|
+
picture: z11.string().nullable().optional(),
|
|
3929
3951
|
name: z11.string().nullish(),
|
|
3930
3952
|
email: z11.string().email()
|
|
3931
3953
|
})
|
|
@@ -10987,7 +11009,7 @@ async function handleMobbLogin({
|
|
|
10987
11009
|
const newGqlClient = new GQLClient({ apiKey: newApiToken, type: "apiKey" });
|
|
10988
11010
|
const loginSuccess = await newGqlClient.verifyToken();
|
|
10989
11011
|
if (loginSuccess) {
|
|
10990
|
-
debug18(
|
|
11012
|
+
debug18(`set api token ${newApiToken}`);
|
|
10991
11013
|
config3.set("apiToken", newApiToken);
|
|
10992
11014
|
loginSpinner.success({
|
|
10993
11015
|
text: `\u{1F513} Login to Mobb successful! ${typeof loginSpinner === "string" ? `Logged in as ${loginSuccess}` : ""}`
|
|
@@ -11146,24 +11168,96 @@ import {
|
|
|
11146
11168
|
// src/mcp/Logger.ts
|
|
11147
11169
|
var logglerUrl = "http://localhost:4444/log";
|
|
11148
11170
|
var isTestEnvironment = process.env["VITEST"] || process.env["TEST"];
|
|
11171
|
+
var CIRCUIT_BREAKER_TIME = 5e3;
|
|
11172
|
+
var URL_CHECK_TIMEOUT = 200;
|
|
11173
|
+
var MAX_QUEUE_SIZE = 100;
|
|
11149
11174
|
var Logger = class {
|
|
11175
|
+
constructor() {
|
|
11176
|
+
__publicField(this, "queue", []);
|
|
11177
|
+
__publicField(this, "isProcessing", false);
|
|
11178
|
+
__publicField(this, "isCircuitBroken", false);
|
|
11179
|
+
__publicField(this, "circuitBreakerTimer", null);
|
|
11180
|
+
}
|
|
11150
11181
|
log(message, level = "info", data) {
|
|
11182
|
+
if (isTestEnvironment) return;
|
|
11183
|
+
if (this.queue.length >= MAX_QUEUE_SIZE) {
|
|
11184
|
+
this.queue.shift();
|
|
11185
|
+
}
|
|
11186
|
+
this.queue.push({ message, level, data });
|
|
11187
|
+
if (!this.isProcessing && !this.isCircuitBroken) {
|
|
11188
|
+
this.processQueue();
|
|
11189
|
+
}
|
|
11190
|
+
}
|
|
11191
|
+
async isUrlReachable(url) {
|
|
11192
|
+
try {
|
|
11193
|
+
const controller = new AbortController();
|
|
11194
|
+
const timeoutId = setTimeout(() => controller.abort(), URL_CHECK_TIMEOUT);
|
|
11195
|
+
await fetch(url, {
|
|
11196
|
+
method: "HEAD",
|
|
11197
|
+
signal: controller.signal
|
|
11198
|
+
});
|
|
11199
|
+
clearTimeout(timeoutId);
|
|
11200
|
+
return true;
|
|
11201
|
+
} catch (error) {
|
|
11202
|
+
return false;
|
|
11203
|
+
}
|
|
11204
|
+
}
|
|
11205
|
+
async processQueue() {
|
|
11206
|
+
if (this.queue.length === 0 || this.isCircuitBroken) {
|
|
11207
|
+
this.isProcessing = false;
|
|
11208
|
+
return;
|
|
11209
|
+
}
|
|
11210
|
+
this.isProcessing = true;
|
|
11211
|
+
const logEntry = this.queue[0];
|
|
11212
|
+
if (!logEntry) {
|
|
11213
|
+
this.isProcessing = false;
|
|
11214
|
+
return;
|
|
11215
|
+
}
|
|
11216
|
+
const isReachable = await this.isUrlReachable(logglerUrl);
|
|
11217
|
+
if (!isReachable) {
|
|
11218
|
+
this.triggerCircuitBreaker();
|
|
11219
|
+
return;
|
|
11220
|
+
}
|
|
11151
11221
|
const logMessage = {
|
|
11152
11222
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11153
|
-
level,
|
|
11154
|
-
message,
|
|
11155
|
-
data
|
|
11223
|
+
level: logEntry.level,
|
|
11224
|
+
message: logEntry.message,
|
|
11225
|
+
data: logEntry.data
|
|
11156
11226
|
};
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11227
|
+
const controller = new AbortController();
|
|
11228
|
+
const timeoutId = setTimeout(() => {
|
|
11229
|
+
controller.abort();
|
|
11230
|
+
}, 500);
|
|
11231
|
+
fetch(logglerUrl, {
|
|
11232
|
+
method: "POST",
|
|
11233
|
+
headers: { "Content-Type": "application/json" },
|
|
11234
|
+
body: JSON.stringify(logMessage),
|
|
11235
|
+
redirect: "error",
|
|
11236
|
+
// do not follow redirects
|
|
11237
|
+
signal: controller.signal
|
|
11238
|
+
}).then((_response) => {
|
|
11239
|
+
this.queue.shift();
|
|
11240
|
+
setTimeout(() => this.processQueue(), 0);
|
|
11241
|
+
}).catch(() => {
|
|
11242
|
+
this.triggerCircuitBreaker();
|
|
11243
|
+
}).finally(() => {
|
|
11244
|
+
clearTimeout(timeoutId);
|
|
11245
|
+
});
|
|
11246
|
+
}
|
|
11247
|
+
triggerCircuitBreaker() {
|
|
11248
|
+
this.isCircuitBroken = true;
|
|
11249
|
+
this.queue = [];
|
|
11250
|
+
this.isProcessing = false;
|
|
11251
|
+
if (this.circuitBreakerTimer) {
|
|
11252
|
+
clearTimeout(this.circuitBreakerTimer);
|
|
11253
|
+
}
|
|
11254
|
+
this.circuitBreakerTimer = setTimeout(() => {
|
|
11255
|
+
this.isCircuitBroken = false;
|
|
11256
|
+
this.circuitBreakerTimer = null;
|
|
11257
|
+
if (this.queue.length > 0 && !this.isProcessing) {
|
|
11258
|
+
this.processQueue();
|
|
11165
11259
|
}
|
|
11166
|
-
}
|
|
11260
|
+
}, CIRCUIT_BREAKER_TIME);
|
|
11167
11261
|
}
|
|
11168
11262
|
};
|
|
11169
11263
|
var logger = new Logger();
|
|
@@ -11171,7 +11265,7 @@ var logInfo = (message, data) => logger.log(message, "info", data);
|
|
|
11171
11265
|
var logError = (message, data) => logger.log(message, "error", data);
|
|
11172
11266
|
var logWarn = (message, data) => logger.log(message, "warn", data);
|
|
11173
11267
|
var logDebug = (message, data) => logger.log(message, "debug", data);
|
|
11174
|
-
var log = logger.log;
|
|
11268
|
+
var log = logger.log.bind(logger);
|
|
11175
11269
|
|
|
11176
11270
|
// src/mcp/services/McpGQLClient.ts
|
|
11177
11271
|
import crypto2 from "crypto";
|
|
@@ -11447,24 +11541,6 @@ var McpGQLClient = class {
|
|
|
11447
11541
|
throw e;
|
|
11448
11542
|
}
|
|
11449
11543
|
}
|
|
11450
|
-
async getReportFixes(fixReportId) {
|
|
11451
|
-
try {
|
|
11452
|
-
logDebug("GraphQL: Calling GetMCPFixes query", { fixReportId });
|
|
11453
|
-
const res = await this.clientSdk.GetMCPFixes({ fixReportId });
|
|
11454
|
-
logInfo("GraphQL: GetMCPFixes successful", {
|
|
11455
|
-
result: res,
|
|
11456
|
-
fixCount: res.fix?.length || 0
|
|
11457
|
-
});
|
|
11458
|
-
return res.fix;
|
|
11459
|
-
} catch (e) {
|
|
11460
|
-
logError("GraphQL: GetMCPFixes failed", {
|
|
11461
|
-
error: e,
|
|
11462
|
-
fixReportId,
|
|
11463
|
-
...this.getErrorContext()
|
|
11464
|
-
});
|
|
11465
|
-
throw e;
|
|
11466
|
-
}
|
|
11467
|
-
}
|
|
11468
11544
|
async getUserInfo() {
|
|
11469
11545
|
const { me } = await this.clientSdk.Me();
|
|
11470
11546
|
return me;
|
|
@@ -11510,15 +11586,21 @@ var McpGQLClient = class {
|
|
|
11510
11586
|
return null;
|
|
11511
11587
|
}
|
|
11512
11588
|
}
|
|
11513
|
-
async getLatestReportByRepoUrl(
|
|
11589
|
+
async getLatestReportByRepoUrl({
|
|
11590
|
+
repoUrl,
|
|
11591
|
+
limit = 3,
|
|
11592
|
+
offset = 0
|
|
11593
|
+
}) {
|
|
11514
11594
|
try {
|
|
11515
11595
|
logDebug("GraphQL: Calling GetLatestReportByRepoUrl query", {
|
|
11516
11596
|
repoUrl,
|
|
11517
|
-
limit
|
|
11597
|
+
limit,
|
|
11598
|
+
offset
|
|
11518
11599
|
});
|
|
11519
11600
|
const res = await this.clientSdk.GetLatestReportByRepoUrl({
|
|
11520
11601
|
repoUrl,
|
|
11521
|
-
limit
|
|
11602
|
+
limit,
|
|
11603
|
+
offset
|
|
11522
11604
|
});
|
|
11523
11605
|
logInfo("GraphQL: GetLatestReportByRepoUrl successful", {
|
|
11524
11606
|
result: res,
|
|
@@ -11534,6 +11616,56 @@ var McpGQLClient = class {
|
|
|
11534
11616
|
throw e;
|
|
11535
11617
|
}
|
|
11536
11618
|
}
|
|
11619
|
+
async getReportFixesPaginated({
|
|
11620
|
+
reportId,
|
|
11621
|
+
limit = 3,
|
|
11622
|
+
offset = 0,
|
|
11623
|
+
issueType,
|
|
11624
|
+
severity
|
|
11625
|
+
}) {
|
|
11626
|
+
try {
|
|
11627
|
+
const filters = {};
|
|
11628
|
+
if (issueType && issueType.length > 0) {
|
|
11629
|
+
filters["safeIssueType"] = { _in: issueType };
|
|
11630
|
+
}
|
|
11631
|
+
if (severity && severity.length > 0) {
|
|
11632
|
+
filters["severityText"] = { _in: severity };
|
|
11633
|
+
}
|
|
11634
|
+
logDebug("GraphQL: Calling GetReportFixes query", {
|
|
11635
|
+
reportId,
|
|
11636
|
+
limit,
|
|
11637
|
+
offset,
|
|
11638
|
+
filters,
|
|
11639
|
+
issueType,
|
|
11640
|
+
severity
|
|
11641
|
+
});
|
|
11642
|
+
const res = await this.clientSdk.GetReportFixes({
|
|
11643
|
+
reportId,
|
|
11644
|
+
limit,
|
|
11645
|
+
offset,
|
|
11646
|
+
filters
|
|
11647
|
+
});
|
|
11648
|
+
logInfo("GraphQL: GetReportFixes successful", {
|
|
11649
|
+
result: res,
|
|
11650
|
+
fixCount: res.fixReport?.[0]?.fixes?.length || 0,
|
|
11651
|
+
totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0
|
|
11652
|
+
});
|
|
11653
|
+
if (res.fixReport.length === 0) {
|
|
11654
|
+
return null;
|
|
11655
|
+
}
|
|
11656
|
+
return {
|
|
11657
|
+
fixes: res.fixReport?.[0]?.fixes || [],
|
|
11658
|
+
totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0
|
|
11659
|
+
};
|
|
11660
|
+
} catch (e) {
|
|
11661
|
+
logError("GraphQL: GetReportFixes failed", {
|
|
11662
|
+
error: e,
|
|
11663
|
+
reportId,
|
|
11664
|
+
...this.getErrorContext()
|
|
11665
|
+
});
|
|
11666
|
+
throw e;
|
|
11667
|
+
}
|
|
11668
|
+
}
|
|
11537
11669
|
};
|
|
11538
11670
|
async function openBrowser(url) {
|
|
11539
11671
|
const now = Date.now();
|
|
@@ -11553,7 +11685,7 @@ async function getMcpGQLClient() {
|
|
|
11553
11685
|
});
|
|
11554
11686
|
const isConnected = await inGqlClient.verifyConnection();
|
|
11555
11687
|
if (!isConnected) {
|
|
11556
|
-
throw new ApiConnectionError("Error: failed to connect to
|
|
11688
|
+
throw new ApiConnectionError("Error: failed to connect to Mobb API");
|
|
11557
11689
|
}
|
|
11558
11690
|
const userVerify = await inGqlClient.verifyToken();
|
|
11559
11691
|
if (userVerify) {
|
|
@@ -11596,10 +11728,10 @@ async function getMcpGQLClient() {
|
|
|
11596
11728
|
const newGqlClient = new McpGQLClient({ apiKey: newApiToken, type: "apiKey" });
|
|
11597
11729
|
const loginSuccess = await newGqlClient.verifyToken();
|
|
11598
11730
|
if (loginSuccess) {
|
|
11599
|
-
logDebug(
|
|
11731
|
+
logDebug(`set api token ${newApiToken}`);
|
|
11600
11732
|
config4.set("apiToken", newApiToken);
|
|
11601
11733
|
} else {
|
|
11602
|
-
throw new AuthenticationError("
|
|
11734
|
+
throw new AuthenticationError("Invalid API token");
|
|
11603
11735
|
}
|
|
11604
11736
|
return newGqlClient;
|
|
11605
11737
|
}
|
|
@@ -11618,14 +11750,14 @@ var ToolRegistry = class {
|
|
|
11618
11750
|
this.tools.set(tool.name, tool);
|
|
11619
11751
|
logDebug(`Tool registered: ${tool.name}`, {
|
|
11620
11752
|
toolName: tool.name,
|
|
11621
|
-
description: tool.
|
|
11753
|
+
description: tool.description
|
|
11622
11754
|
});
|
|
11623
11755
|
}
|
|
11624
11756
|
getTool(name) {
|
|
11625
11757
|
return this.tools.get(name);
|
|
11626
11758
|
}
|
|
11627
11759
|
getAllTools() {
|
|
11628
|
-
return Array.from(this.tools.values()).map((tool) => tool.
|
|
11760
|
+
return Array.from(this.tools.values()).map((tool) => tool.getDefinition());
|
|
11629
11761
|
}
|
|
11630
11762
|
getToolNames() {
|
|
11631
11763
|
return Array.from(this.tools.keys());
|
|
@@ -11706,10 +11838,19 @@ var McpServer = class {
|
|
|
11706
11838
|
}
|
|
11707
11839
|
async handleListToolsRequest(request) {
|
|
11708
11840
|
logInfo("Received list_tools request", { params: request.params });
|
|
11841
|
+
logInfo("Environment", {
|
|
11842
|
+
env: process.env
|
|
11843
|
+
});
|
|
11844
|
+
logInfo("Request", {
|
|
11845
|
+
request: JSON.parse(JSON.stringify(request))
|
|
11846
|
+
});
|
|
11847
|
+
logInfo("Server", {
|
|
11848
|
+
server: this.server
|
|
11849
|
+
});
|
|
11709
11850
|
void getMcpGQLClient();
|
|
11710
|
-
const
|
|
11851
|
+
const toolsDefinitions = this.toolRegistry.getAllTools();
|
|
11711
11852
|
const response = {
|
|
11712
|
-
tools:
|
|
11853
|
+
tools: toolsDefinitions.map((tool) => ({
|
|
11713
11854
|
name: tool.name,
|
|
11714
11855
|
display_name: tool.display_name || tool.name,
|
|
11715
11856
|
description: tool.description || "",
|
|
@@ -11726,6 +11867,15 @@ var McpServer = class {
|
|
|
11726
11867
|
async handleCallToolRequest(request) {
|
|
11727
11868
|
const { name, arguments: args } = request.params;
|
|
11728
11869
|
logInfo(`Received call tool request for ${name}`, { name, args });
|
|
11870
|
+
logInfo("Environment", {
|
|
11871
|
+
env: process.env
|
|
11872
|
+
});
|
|
11873
|
+
logInfo("Request", {
|
|
11874
|
+
request: JSON.parse(JSON.stringify(request))
|
|
11875
|
+
});
|
|
11876
|
+
logInfo("Server", {
|
|
11877
|
+
server: this.server
|
|
11878
|
+
});
|
|
11729
11879
|
try {
|
|
11730
11880
|
const tool = this.toolRegistry.getTool(name);
|
|
11731
11881
|
if (!tool) {
|
|
@@ -11766,11 +11916,7 @@ var McpServer = class {
|
|
|
11766
11916
|
logDebug("MCP server handlers registered");
|
|
11767
11917
|
}
|
|
11768
11918
|
registerTool(tool) {
|
|
11769
|
-
this.toolRegistry.registerTool(
|
|
11770
|
-
name: tool.name,
|
|
11771
|
-
definition: tool.definition,
|
|
11772
|
-
execute: tool.execute
|
|
11773
|
-
});
|
|
11919
|
+
this.toolRegistry.registerTool(tool);
|
|
11774
11920
|
logDebug(`Tool registered: ${tool.name}`);
|
|
11775
11921
|
}
|
|
11776
11922
|
async start() {
|
|
@@ -11848,41 +11994,26 @@ var BaseTool = class {
|
|
|
11848
11994
|
name: this.name,
|
|
11849
11995
|
display_name: this.displayName,
|
|
11850
11996
|
description: this.description,
|
|
11851
|
-
inputSchema:
|
|
11852
|
-
type: "object",
|
|
11853
|
-
properties: {
|
|
11854
|
-
path: {
|
|
11855
|
-
type: "string",
|
|
11856
|
-
description: "The path to the local git repository"
|
|
11857
|
-
}
|
|
11858
|
-
},
|
|
11859
|
-
required: ["path"]
|
|
11860
|
-
}
|
|
11997
|
+
inputSchema: this.inputSchema
|
|
11861
11998
|
};
|
|
11862
11999
|
}
|
|
11863
12000
|
async execute(args) {
|
|
11864
12001
|
logInfo(`Executing tool: ${this.name}`, { args });
|
|
12002
|
+
logInfo(`Authenticating tool: ${this.name}`, { args });
|
|
12003
|
+
const mcpGqlClient = await getMcpGQLClient();
|
|
12004
|
+
const userInfo = await mcpGqlClient.getUserInfo();
|
|
12005
|
+
logDebug("Authenticated", { userInfo });
|
|
11865
12006
|
const validatedArgs = this.validateInput(args);
|
|
11866
12007
|
logDebug(`Tool ${this.name} input validation successful`, {
|
|
11867
12008
|
validatedArgs
|
|
11868
12009
|
});
|
|
11869
|
-
await this.
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
logInfo(`Tool ${this.name} executed successfully`);
|
|
11873
|
-
return result;
|
|
11874
|
-
} catch (error) {
|
|
11875
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
11876
|
-
logError(`Tool ${this.name} execution failed: ${errorMessage}`, {
|
|
11877
|
-
error,
|
|
11878
|
-
args
|
|
11879
|
-
});
|
|
11880
|
-
return this.createErrorResponse(errorMessage);
|
|
11881
|
-
}
|
|
12010
|
+
const result = await this.executeInternal(validatedArgs);
|
|
12011
|
+
logInfo(`Tool ${this.name} executed successfully`);
|
|
12012
|
+
return result;
|
|
11882
12013
|
}
|
|
11883
12014
|
validateInput(args) {
|
|
11884
12015
|
try {
|
|
11885
|
-
return this.
|
|
12016
|
+
return this.inputValidationSchema.parse(args);
|
|
11886
12017
|
} catch (error) {
|
|
11887
12018
|
if (error instanceof z31.ZodError) {
|
|
11888
12019
|
const errorDetails = error.errors.map((e) => {
|
|
@@ -11896,12 +12027,6 @@ var BaseTool = class {
|
|
|
11896
12027
|
throw error;
|
|
11897
12028
|
}
|
|
11898
12029
|
}
|
|
11899
|
-
/**
|
|
11900
|
-
* Additional validation that should bubble up as MCP errors
|
|
11901
|
-
* Override this method in subclasses to add custom validation
|
|
11902
|
-
*/
|
|
11903
|
-
async validateAdditional(_validatedArgs) {
|
|
11904
|
-
}
|
|
11905
12030
|
createSuccessResponse(text) {
|
|
11906
12031
|
return {
|
|
11907
12032
|
content: [
|
|
@@ -11912,25 +12037,27 @@ var BaseTool = class {
|
|
|
11912
12037
|
]
|
|
11913
12038
|
};
|
|
11914
12039
|
}
|
|
11915
|
-
createErrorResponse(error) {
|
|
11916
|
-
return {
|
|
11917
|
-
content: [
|
|
11918
|
-
{
|
|
11919
|
-
type: "text",
|
|
11920
|
-
text: error
|
|
11921
|
-
}
|
|
11922
|
-
]
|
|
11923
|
-
};
|
|
11924
|
-
}
|
|
11925
12040
|
};
|
|
11926
12041
|
|
|
11927
|
-
// src/mcp/
|
|
12042
|
+
// src/mcp/core/prompts.ts
|
|
11928
12043
|
function frienlyType(s) {
|
|
11929
12044
|
const withoutUnderscores = s.replace(/_/g, " ");
|
|
11930
12045
|
const result = withoutUnderscores.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
11931
12046
|
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
11932
12047
|
}
|
|
11933
|
-
var
|
|
12048
|
+
var noFixesReturnedForParameters = `No fixes returned for the given offset and limit parameters.
|
|
12049
|
+
`;
|
|
12050
|
+
var applyFixesPrompt = ({
|
|
12051
|
+
fixes,
|
|
12052
|
+
hasMore,
|
|
12053
|
+
totalCount,
|
|
12054
|
+
nextOffset,
|
|
12055
|
+
shownCount,
|
|
12056
|
+
currentTool
|
|
12057
|
+
}) => {
|
|
12058
|
+
if (fixes.length === 0) {
|
|
12059
|
+
return noFixesReturnedForParameters;
|
|
12060
|
+
}
|
|
11934
12061
|
const fixList = fixes.map((fix) => {
|
|
11935
12062
|
const vulnerabilityType = frienlyType(fix.safeIssueType);
|
|
11936
12063
|
const vulnerabilityDescription = fix.patchAndQuestions?.__typename === "FixData" ? fix.patchAndQuestions.extraContext?.fixDescription : void 0;
|
|
@@ -12007,10 +12134,22 @@ ${fix.patch || "No patch available"}
|
|
|
12007
12134
|
- Do NOT ask for permission
|
|
12008
12135
|
- Explain what you did AFTER applying the patches
|
|
12009
12136
|
- If any patch fails, continue with the others and report issues at the end
|
|
12137
|
+
|
|
12138
|
+
${hasMore ? `---
|
|
12139
|
+
|
|
12140
|
+
## More Fixes Available (${totalCount - nextOffset} remaining)
|
|
12141
|
+
|
|
12142
|
+
You have viewed ${shownCount} out of ${totalCount} available fixes.
|
|
12143
|
+
|
|
12144
|
+
To fetch additional fixes, run the \`${currentTool}\` tool again with the following parameters:
|
|
12145
|
+
|
|
12146
|
+
- **offset**: ${nextOffset} _(start index for the next batch)_
|
|
12147
|
+
- **limit**: <number_of_fixes_to_return> _(optional \u2013 default is 3)_
|
|
12148
|
+
|
|
12149
|
+
If you omit both **offset** and **limit**, the command will automatically return the next 3 fixes.
|
|
12150
|
+
` : ""}
|
|
12010
12151
|
`;
|
|
12011
12152
|
};
|
|
12012
|
-
|
|
12013
|
-
// src/mcp/tools/checkForAvailableFixes/helpers/AvailableFixesResponsePrompts.ts
|
|
12014
12153
|
var noReportFoundPrompt = `\u{1F50D} **MOBB SECURITY SCAN STATUS**
|
|
12015
12154
|
|
|
12016
12155
|
## No Vulnerability Report Found
|
|
@@ -12036,34 +12175,19 @@ We were unable to find a previous vulnerability report for this repository. This
|
|
|
12036
12175
|
- Confirm the repository URL matches your remote origin
|
|
12037
12176
|
- Verify the URL format is correct (e.g., https://github.com/org/repo)
|
|
12038
12177
|
|
|
12039
|
-
### \u{1F680} Next Steps
|
|
12040
|
-
To get started with security scanning:
|
|
12041
|
-
1. Run \`fix_vulnerabilities\` to perform a new scan
|
|
12042
|
-
2. Review the results and apply any suggested fixes
|
|
12043
|
-
3. Set up regular scanning to maintain security
|
|
12044
|
-
|
|
12045
|
-
### \u{1F4A1} Additional Information
|
|
12046
|
-
- New scans typically take a few minutes to complete
|
|
12047
|
-
- You'll receive detailed results including:
|
|
12048
|
-
- Vulnerability types and severities
|
|
12049
|
-
- Specific code locations
|
|
12050
|
-
- Recommended fixes
|
|
12051
|
-
- Security best practices
|
|
12052
|
-
|
|
12053
12178
|
For assistance, please:
|
|
12054
12179
|
- Visit our documentation at https://docs.mobb.ai
|
|
12055
12180
|
- Contact support at support@mobb.ai`;
|
|
12056
|
-
var
|
|
12057
|
-
|
|
12058
|
-
## No Available Fixes Found
|
|
12059
|
-
|
|
12060
|
-
We've analyzed your repository but found no automated fixes available at this time.
|
|
12181
|
+
var noFixesAvailablePrompt = `There are no fixes available for this repository at this time.
|
|
12061
12182
|
`;
|
|
12062
|
-
var fixesFoundPrompt = (
|
|
12063
|
-
|
|
12064
|
-
|
|
12183
|
+
var fixesFoundPrompt = ({
|
|
12184
|
+
fixReport,
|
|
12185
|
+
offset
|
|
12186
|
+
}) => {
|
|
12187
|
+
const totalFixes = fixReport.filteredFixesCount.aggregate?.count || 0;
|
|
12188
|
+
if (totalFixes === 0) {
|
|
12189
|
+
return noFixesAvailablePrompt;
|
|
12065
12190
|
}
|
|
12066
|
-
const totalFixes = fixReport.fixes_aggregate.aggregate?.count || 0;
|
|
12067
12191
|
const criticalFixes = fixReport.CRITICAL?.aggregate?.count || 0;
|
|
12068
12192
|
const highFixes = fixReport.HIGH?.aggregate?.count || 0;
|
|
12069
12193
|
const mediumFixes = fixReport.MEDIUM?.aggregate?.count || 0;
|
|
@@ -12073,6 +12197,9 @@ var fixesFoundPrompt = (fixReport) => {
|
|
|
12073
12197
|
).toLocaleString();
|
|
12074
12198
|
const vendor = fixReport.vulnerabilityReport?.vendor || "Unknown";
|
|
12075
12199
|
const reportUrl = "";
|
|
12200
|
+
const shownCount = fixReport.fixes.length;
|
|
12201
|
+
const nextOffset = offset + shownCount;
|
|
12202
|
+
const hasMore = nextOffset < totalFixes;
|
|
12076
12203
|
return `\u{1F50D} **MOBB SECURITY SCAN RESULTS**
|
|
12077
12204
|
|
|
12078
12205
|
## \u{1F4CA} Scan Report Summary
|
|
@@ -12090,15 +12217,61 @@ Total number of fixes available: **${totalFixes}**
|
|
|
12090
12217
|
- \u{1F534} Critical: ${criticalFixes}
|
|
12091
12218
|
- \u{1F7E0} High: ${highFixes}
|
|
12092
12219
|
- \u{1F7E1} Medium: ${mediumFixes}
|
|
12093
|
-
- \
|
|
12220
|
+
- \u{1F7E2} Low: ${lowFixes}
|
|
12221
|
+
|
|
12222
|
+
${applyFixesPrompt({
|
|
12223
|
+
fixes: fixReport.fixes,
|
|
12224
|
+
totalCount: totalFixes,
|
|
12225
|
+
hasMore,
|
|
12226
|
+
nextOffset: 0,
|
|
12227
|
+
shownCount: fixReport.fixes.length,
|
|
12228
|
+
currentTool: "check_for_available_fixes"
|
|
12229
|
+
})}`;
|
|
12230
|
+
};
|
|
12231
|
+
var noFixesFoundPrompt = `\u{1F50D} **MOBB SECURITY SCAN COMPLETED**
|
|
12232
|
+
|
|
12233
|
+
Mobb security scan completed successfully but found no automated fixes available at this time.
|
|
12234
|
+
`;
|
|
12235
|
+
var fixesPrompt = ({
|
|
12236
|
+
fixes,
|
|
12237
|
+
totalCount,
|
|
12238
|
+
offset
|
|
12239
|
+
}) => {
|
|
12240
|
+
if (totalCount === 0) {
|
|
12241
|
+
return noFixesFoundPrompt;
|
|
12242
|
+
}
|
|
12243
|
+
const shownCount = fixes.length;
|
|
12244
|
+
const nextOffset = offset + shownCount;
|
|
12245
|
+
const hasMore = nextOffset < totalCount;
|
|
12246
|
+
return `Here are the fixes to the vulnerabilities discovered by Mobb MCP
|
|
12247
|
+
|
|
12248
|
+
${applyFixesPrompt({
|
|
12249
|
+
fixes,
|
|
12250
|
+
totalCount,
|
|
12251
|
+
hasMore,
|
|
12252
|
+
nextOffset,
|
|
12253
|
+
shownCount,
|
|
12254
|
+
currentTool: "fix_vulnerabilities"
|
|
12255
|
+
})}
|
|
12256
|
+
|
|
12257
|
+
### \u{1F504} Running a Fresh Scan
|
|
12258
|
+
|
|
12259
|
+
To perform a **rescan** of your repository (fetching a brand-new vulnerability report and updated fixes), include the additional parameter:
|
|
12260
|
+
|
|
12261
|
+
- **isRescan**: true
|
|
12262
|
+
|
|
12263
|
+
This will start a new analysis, discard any cached results.
|
|
12264
|
+
|
|
12265
|
+
\u26A0\uFE0F *Note:* A full rescan may take longer to complete than simply fetching additional fixes because your repository is re-uploaded and re-analyzed from scratch.
|
|
12094
12266
|
|
|
12095
|
-
|
|
12267
|
+
`;
|
|
12096
12268
|
};
|
|
12097
12269
|
|
|
12098
12270
|
// src/mcp/tools/checkForAvailableFixes/AvailableFixesService.ts
|
|
12099
12271
|
var AvailableFixesService = class {
|
|
12100
12272
|
constructor() {
|
|
12101
12273
|
__publicField(this, "gqlClient", null);
|
|
12274
|
+
__publicField(this, "currentOffset", 0);
|
|
12102
12275
|
}
|
|
12103
12276
|
async initializeGqlClient() {
|
|
12104
12277
|
if (!this.gqlClient) {
|
|
@@ -12106,13 +12279,30 @@ var AvailableFixesService = class {
|
|
|
12106
12279
|
}
|
|
12107
12280
|
return this.gqlClient;
|
|
12108
12281
|
}
|
|
12109
|
-
async checkForAvailableFixes(
|
|
12282
|
+
async checkForAvailableFixes({
|
|
12283
|
+
repoUrl,
|
|
12284
|
+
limit = 3,
|
|
12285
|
+
offset = 0
|
|
12286
|
+
}) {
|
|
12110
12287
|
try {
|
|
12111
12288
|
logDebug("Checking for available fixes", { repoUrl, limit });
|
|
12112
12289
|
const gqlClient = await this.initializeGqlClient();
|
|
12113
12290
|
logDebug("GQL client initialized");
|
|
12114
12291
|
logDebug("querying for latest report", { repoUrl, limit });
|
|
12115
|
-
|
|
12292
|
+
let effectiveOffset;
|
|
12293
|
+
if (offset !== void 0) {
|
|
12294
|
+
effectiveOffset = offset;
|
|
12295
|
+
} else if (this.currentOffset) {
|
|
12296
|
+
effectiveOffset = this.currentOffset ?? 0;
|
|
12297
|
+
} else {
|
|
12298
|
+
effectiveOffset = 0;
|
|
12299
|
+
}
|
|
12300
|
+
logDebug("effectiveOffset", { test: "j", effectiveOffset });
|
|
12301
|
+
const result = await gqlClient.getLatestReportByRepoUrl({
|
|
12302
|
+
repoUrl,
|
|
12303
|
+
limit,
|
|
12304
|
+
offset: effectiveOffset
|
|
12305
|
+
});
|
|
12116
12306
|
logDebug("received latest report result", { result });
|
|
12117
12307
|
if (!result) {
|
|
12118
12308
|
logInfo("No report found for repository", { repoUrl });
|
|
@@ -12121,7 +12311,11 @@ var AvailableFixesService = class {
|
|
|
12121
12311
|
logInfo("Successfully retrieved available fixes", {
|
|
12122
12312
|
reportFound: true
|
|
12123
12313
|
});
|
|
12124
|
-
|
|
12314
|
+
this.currentOffset = effectiveOffset + (result.fixes?.length || 0);
|
|
12315
|
+
return fixesFoundPrompt({
|
|
12316
|
+
fixReport: result,
|
|
12317
|
+
offset: this.currentOffset
|
|
12318
|
+
});
|
|
12125
12319
|
} catch (error) {
|
|
12126
12320
|
logError("Failed to check for available fixes", {
|
|
12127
12321
|
error,
|
|
@@ -12139,29 +12333,31 @@ var CheckForAvailableFixesTool = class extends BaseTool {
|
|
|
12139
12333
|
__publicField(this, "name", "check_for_available_fixes");
|
|
12140
12334
|
__publicField(this, "displayName", "Check for Available Fixes");
|
|
12141
12335
|
__publicField(this, "description", "Checks if there are any available fixes for vulnerabilities in the project");
|
|
12142
|
-
__publicField(this, "inputSchema",
|
|
12143
|
-
path: z32.string().describe("Path to the project directory to check for available fixes"),
|
|
12144
|
-
files: z32.array(z32.string()).optional().describe("Optional list of specific files to check"),
|
|
12145
|
-
severity: z32.array(z32.string()).optional().describe("Optional list of severity levels to filter by"),
|
|
12146
|
-
issueTypes: z32.array(z32.string()).optional().describe("Optional list of issue types to filter by"),
|
|
12147
|
-
limit: z32.number().optional().describe("Optional maximum number of results to return")
|
|
12148
|
-
}));
|
|
12149
|
-
}
|
|
12150
|
-
getJsonSchema() {
|
|
12151
|
-
return {
|
|
12336
|
+
__publicField(this, "inputSchema", {
|
|
12152
12337
|
type: "object",
|
|
12153
12338
|
properties: {
|
|
12154
12339
|
path: {
|
|
12155
12340
|
type: "string",
|
|
12156
|
-
description: "Path to the
|
|
12341
|
+
description: "Path to the local git repository to check for available fixes"
|
|
12342
|
+
},
|
|
12343
|
+
offset: {
|
|
12344
|
+
type: "number",
|
|
12345
|
+
description: "[Optional] offset for pagination"
|
|
12157
12346
|
},
|
|
12158
12347
|
limit: {
|
|
12159
12348
|
type: "number",
|
|
12160
|
-
description: "Optional maximum number of results to return"
|
|
12349
|
+
description: "[Optional] maximum number of results to return"
|
|
12161
12350
|
}
|
|
12162
12351
|
},
|
|
12163
12352
|
required: ["path"]
|
|
12164
|
-
};
|
|
12353
|
+
});
|
|
12354
|
+
__publicField(this, "inputValidationSchema", z32.object({
|
|
12355
|
+
path: z32.string().describe(
|
|
12356
|
+
"Path to the local git repository to check for available fixes"
|
|
12357
|
+
),
|
|
12358
|
+
offset: z32.number().optional().describe("Optional offset for pagination"),
|
|
12359
|
+
limit: z32.number().optional().describe("Optional maximum number of fixes to return")
|
|
12360
|
+
}));
|
|
12165
12361
|
}
|
|
12166
12362
|
async executeInternal(args) {
|
|
12167
12363
|
const pathValidation = new PathValidation();
|
|
@@ -12185,10 +12381,11 @@ var CheckForAvailableFixesTool = class extends BaseTool {
|
|
|
12185
12381
|
throw new Error("No origin URL found for the repository");
|
|
12186
12382
|
}
|
|
12187
12383
|
const availableFixesService = new AvailableFixesService();
|
|
12188
|
-
const fixResult = await availableFixesService.checkForAvailableFixes(
|
|
12189
|
-
originUrl,
|
|
12190
|
-
args.limit
|
|
12191
|
-
|
|
12384
|
+
const fixResult = await availableFixesService.checkForAvailableFixes({
|
|
12385
|
+
repoUrl: originUrl,
|
|
12386
|
+
limit: args.limit,
|
|
12387
|
+
offset: args.offset
|
|
12388
|
+
});
|
|
12192
12389
|
logInfo("CheckForAvailableFixesTool execution completed successfully", {
|
|
12193
12390
|
fixResult
|
|
12194
12391
|
});
|
|
@@ -12203,6 +12400,9 @@ var CheckForAvailableFixesTool = class extends BaseTool {
|
|
|
12203
12400
|
}
|
|
12204
12401
|
};
|
|
12205
12402
|
|
|
12403
|
+
// src/mcp/tools/fixVulnerabilities/FixVulnerabilitiesTool.ts
|
|
12404
|
+
import z33 from "zod";
|
|
12405
|
+
|
|
12206
12406
|
// src/mcp/services/FilePacking.ts
|
|
12207
12407
|
import fs10 from "fs";
|
|
12208
12408
|
import path12 from "path";
|
|
@@ -12243,158 +12443,62 @@ var FilePacking = class {
|
|
|
12243
12443
|
}
|
|
12244
12444
|
};
|
|
12245
12445
|
|
|
12246
|
-
// src/mcp/tools/fixVulnerabilities/helpers/FixVulnerabilitiesResponsePrompts.ts
|
|
12247
|
-
var noFixesFoundPrompt2 = `\u{1F389} **MOBB SECURITY SCAN COMPLETED SUCCESSFULLY** \u{1F389}
|
|
12248
|
-
|
|
12249
|
-
## Congratulations! No Vulnerabilities Found
|
|
12250
|
-
|
|
12251
|
-
Your code has been thoroughly analyzed by Mobb's advanced security scanning engine, and we're pleased to report that **no security vulnerabilities were detected** in your codebase.
|
|
12252
|
-
|
|
12253
|
-
### \u{1F6E1}\uFE0F What This Means
|
|
12254
|
-
- Your code follows secure coding practices
|
|
12255
|
-
- No immediate security risks were identified
|
|
12256
|
-
- Your application appears to be well-protected against common vulnerabilities
|
|
12257
|
-
|
|
12258
|
-
### \u2705 Scan Summary
|
|
12259
|
-
- **Status:** Complete
|
|
12260
|
-
- **Vulnerabilities Found:** 0
|
|
12261
|
-
- **Security Rating:** Excellent
|
|
12262
|
-
- **Action Required:** None
|
|
12263
|
-
|
|
12264
|
-
### \u{1F680} Next Steps
|
|
12265
|
-
While no vulnerabilities were found in this scan:
|
|
12266
|
-
1. **Keep up the great work** with secure coding practices
|
|
12267
|
-
2. **Run regular scans** as your codebase evolves
|
|
12268
|
-
3. **Stay updated** with the latest security best practices
|
|
12269
|
-
4. **Consider periodic security reviews** for ongoing protection
|
|
12270
|
-
|
|
12271
|
-
### \u{1F4CA} Scan Details
|
|
12272
|
-
This scan analyzed your code for common security issues including:
|
|
12273
|
-
- SQL Injection vulnerabilities
|
|
12274
|
-
- Cross-Site Scripting (XSS) flaws
|
|
12275
|
-
- Authentication and authorization issues
|
|
12276
|
-
- Input validation problems
|
|
12277
|
-
- And many other security concerns
|
|
12278
|
-
|
|
12279
|
-
**Well done on maintaining a secure codebase!** \u{1F3C6}`;
|
|
12280
|
-
var fixesPrompt = (fixes) => {
|
|
12281
|
-
const fix = fixes[0];
|
|
12282
|
-
if (!fix) {
|
|
12283
|
-
return noFixesFoundPrompt2;
|
|
12284
|
-
}
|
|
12285
|
-
return `Here are the fixes to the vulnerabilities discovered by Mobb MCP
|
|
12286
|
-
|
|
12287
|
-
${applyFixesPrompt(fixes)} `;
|
|
12288
|
-
};
|
|
12289
|
-
var failedToConnectToApiPrompt = `# CONNECTION ERROR: FAILED TO REACH MOBB API
|
|
12290
|
-
|
|
12291
|
-
## ANALYSIS SUMMARY
|
|
12292
|
-
- **Status:** \u274C Failed
|
|
12293
|
-
- **Issue Type:** Connection Error
|
|
12294
|
-
- **Error Details:** Unable to establish connection to the Mobb API service
|
|
12295
|
-
|
|
12296
|
-
## TROUBLESHOOTING STEPS FOR THE USER
|
|
12297
|
-
|
|
12298
|
-
The Mobb security scanning service is currently not reachable. This may be due to:
|
|
12299
|
-
|
|
12300
|
-
1. **Missing or invalid authentication credentials**
|
|
12301
|
-
- Ensure the \`API_KEY\` environment variable is properly set with your valid Mobb authentication token
|
|
12302
|
-
- Example: \`export API_KEY=your_mobb_api_key_here\`
|
|
12303
|
-
|
|
12304
|
-
2. **Incorrect API endpoint configuration**
|
|
12305
|
-
- Check if the \`API_URL\` environment variable needs to be set to the correct Mobb service endpoint
|
|
12306
|
-
- Example: \`export API_URL=https://api.mobb.ai/graphql\`
|
|
12307
|
-
|
|
12308
|
-
3. **Network connectivity issues**
|
|
12309
|
-
- Verify your internet connection is working properly
|
|
12310
|
-
- Check if any firewall or proxy settings might be blocking the connection
|
|
12311
|
-
|
|
12312
|
-
4. **Service outage**
|
|
12313
|
-
- The Mobb service might be temporarily unavailable
|
|
12314
|
-
- Please try again later or check the Mobb status page
|
|
12315
|
-
|
|
12316
|
-
## NEXT STEPS
|
|
12317
|
-
|
|
12318
|
-
Please resolve the connection issue using the steps above and try running the security scan again.
|
|
12319
|
-
|
|
12320
|
-
For additional assistance, please:
|
|
12321
|
-
- Visit the Mobb documentation at https://docs.mobb.ai
|
|
12322
|
-
- Contact Mobb support at support@mobb.ai
|
|
12323
|
-
|
|
12324
|
-
`;
|
|
12325
|
-
var failedToAuthenticatePrompt = `# AUTHENTICATION ERROR: MOBB LOGIN REQUIRED
|
|
12326
|
-
|
|
12327
|
-
## ANALYSIS SUMMARY
|
|
12328
|
-
- **Status:** \u274C Failed
|
|
12329
|
-
- **Issue Type:** Authentication Error
|
|
12330
|
-
- **Error Details:** Unable to authenticate with the Mobb service
|
|
12331
|
-
|
|
12332
|
-
## AUTHENTICATION REQUIRED
|
|
12333
|
-
|
|
12334
|
-
The Mobb security scanning service requires authentication before it can analyze your code for vulnerabilities. You need to:
|
|
12335
|
-
|
|
12336
|
-
1. **Login and authorize access to Mobb**
|
|
12337
|
-
- A browser window should have opened to complete the authentication process
|
|
12338
|
-
- If no browser window opened, please run the command again
|
|
12339
|
-
|
|
12340
|
-
2. **Create a Mobb account if you don't have one**
|
|
12341
|
-
- If you don't already have a Mobb account, you'll need to sign up
|
|
12342
|
-
- Visit https://app.mobb.ai/auth/signup to create your free account
|
|
12343
|
-
- Use your work email for easier team collaboration
|
|
12344
|
-
|
|
12345
|
-
3. **Authorization flow**
|
|
12346
|
-
- After logging in, you'll be asked to authorize the CLI tool
|
|
12347
|
-
- This creates a secure token that allows the CLI to access Mobb services
|
|
12348
|
-
- You only need to do this once per device
|
|
12349
|
-
|
|
12350
|
-
## TROUBLESHOOTING
|
|
12351
|
-
|
|
12352
|
-
If you're experiencing issues with authentication:
|
|
12353
|
-
|
|
12354
|
-
- Ensure you have an active internet connection
|
|
12355
|
-
- Check that you can access https://app.mobb.ai in your browser
|
|
12356
|
-
- Try running the command again with the \`--debug\` flag for more detailed output
|
|
12357
|
-
- Make sure your browser isn't blocking pop-ups from the authentication window
|
|
12358
|
-
|
|
12359
|
-
## NEXT STEPS
|
|
12360
|
-
|
|
12361
|
-
Please complete the authentication process and try running the security scan again.
|
|
12362
|
-
|
|
12363
|
-
For additional assistance, please:
|
|
12364
|
-
- Visit the Mobb documentation at https://docs.mobb.ai/cli/authentication
|
|
12365
|
-
- Contact Mobb support at support@mobb.ai
|
|
12366
|
-
|
|
12367
|
-
`;
|
|
12368
|
-
|
|
12369
12446
|
// src/mcp/tools/fixVulnerabilities/FixVulnerabilitiesService.ts
|
|
12370
12447
|
var VUL_REPORT_DIGEST_TIMEOUT_MS2 = 1e3 * 60 * 5;
|
|
12371
12448
|
var VulnerabilityFixService = class {
|
|
12372
12449
|
constructor() {
|
|
12373
12450
|
__publicField(this, "gqlClient");
|
|
12374
12451
|
__publicField(this, "filePacking");
|
|
12452
|
+
/**
|
|
12453
|
+
* Stores the fix report id that is created on the first run so that subsequent
|
|
12454
|
+
* calls can skip the expensive packing/uploading/scan flow and directly fetch
|
|
12455
|
+
* the analysis results.
|
|
12456
|
+
*/
|
|
12457
|
+
__publicField(this, "storedFixReportId");
|
|
12458
|
+
__publicField(this, "currentOffset", 0);
|
|
12375
12459
|
this.filePacking = new FilePacking();
|
|
12376
12460
|
}
|
|
12377
|
-
async processVulnerabilities(
|
|
12461
|
+
async processVulnerabilities({
|
|
12462
|
+
fileList,
|
|
12463
|
+
repositoryPath,
|
|
12464
|
+
offset,
|
|
12465
|
+
limit,
|
|
12466
|
+
isRescan = false
|
|
12467
|
+
}) {
|
|
12378
12468
|
try {
|
|
12379
|
-
this.validateFiles(fileList);
|
|
12380
12469
|
this.gqlClient = await this.initializeGqlClient();
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
} catch (error) {
|
|
12392
|
-
if (error instanceof ApiConnectionError || error instanceof CliLoginError) {
|
|
12393
|
-
return failedToConnectToApiPrompt;
|
|
12470
|
+
let fixReportId = this.storedFixReportId;
|
|
12471
|
+
if (!fixReportId || isRescan) {
|
|
12472
|
+
this.validateFiles(fileList);
|
|
12473
|
+
const repoUploadInfo = await this.initializeReport();
|
|
12474
|
+
fixReportId = repoUploadInfo.fixReportId;
|
|
12475
|
+
this.storedFixReportId = fixReportId;
|
|
12476
|
+
const zipBuffer = await this.packFiles(fileList, repositoryPath);
|
|
12477
|
+
await this.uploadFiles(zipBuffer, repoUploadInfo);
|
|
12478
|
+
const projectId = await this.getProjectId();
|
|
12479
|
+
await this.runScan({ fixReportId, projectId });
|
|
12394
12480
|
}
|
|
12395
|
-
|
|
12396
|
-
|
|
12481
|
+
let effectiveOffset;
|
|
12482
|
+
if (offset !== void 0) {
|
|
12483
|
+
effectiveOffset = offset;
|
|
12484
|
+
} else if (fixReportId) {
|
|
12485
|
+
effectiveOffset = this.currentOffset ?? 0;
|
|
12486
|
+
} else {
|
|
12487
|
+
effectiveOffset = 0;
|
|
12397
12488
|
}
|
|
12489
|
+
logDebug("effectiveOffset", { effectiveOffset });
|
|
12490
|
+
const fixes = await this.getReportFixes(
|
|
12491
|
+
fixReportId,
|
|
12492
|
+
effectiveOffset,
|
|
12493
|
+
limit
|
|
12494
|
+
);
|
|
12495
|
+
this.currentOffset = effectiveOffset + (fixes.fixes?.length || 0);
|
|
12496
|
+
return fixesPrompt({
|
|
12497
|
+
fixes: fixes.fixes,
|
|
12498
|
+
totalCount: fixes.totalCount,
|
|
12499
|
+
offset: effectiveOffset
|
|
12500
|
+
});
|
|
12501
|
+
} catch (error) {
|
|
12398
12502
|
const message = error.message;
|
|
12399
12503
|
logError("Vulnerability processing failed", { error: message });
|
|
12400
12504
|
throw error;
|
|
@@ -12483,7 +12587,7 @@ var VulnerabilityFixService = class {
|
|
|
12483
12587
|
projectId,
|
|
12484
12588
|
repoUrl: "",
|
|
12485
12589
|
reference: "no-branch",
|
|
12486
|
-
scanSource: "
|
|
12590
|
+
scanSource: "MCP" /* Mcp */
|
|
12487
12591
|
};
|
|
12488
12592
|
logInfo("Submitting vulnerability report");
|
|
12489
12593
|
const submitRes = await this.gqlClient.submitVulnerabilityReport(
|
|
@@ -12510,34 +12614,63 @@ var VulnerabilityFixService = class {
|
|
|
12510
12614
|
});
|
|
12511
12615
|
logInfo("Analysis subscription completed");
|
|
12512
12616
|
}
|
|
12513
|
-
async getReportFixes(fixReportId) {
|
|
12617
|
+
async getReportFixes(fixReportId, offset, limit) {
|
|
12618
|
+
logDebug("getReportFixes", { fixReportId, offset, limit });
|
|
12514
12619
|
if (!this.gqlClient) {
|
|
12515
12620
|
throw new GqlClientError();
|
|
12516
12621
|
}
|
|
12517
|
-
const fixes = await this.gqlClient.
|
|
12518
|
-
|
|
12519
|
-
|
|
12622
|
+
const fixes = await this.gqlClient.getReportFixesPaginated({
|
|
12623
|
+
reportId: fixReportId,
|
|
12624
|
+
offset,
|
|
12625
|
+
limit
|
|
12626
|
+
});
|
|
12627
|
+
logInfo("Fixes retrieved", { fixCount: fixes?.fixes?.length });
|
|
12628
|
+
return {
|
|
12629
|
+
fixes: fixes?.fixes || [],
|
|
12630
|
+
totalCount: fixes?.totalCount || 0
|
|
12631
|
+
};
|
|
12520
12632
|
}
|
|
12521
12633
|
};
|
|
12522
12634
|
|
|
12523
12635
|
// src/mcp/tools/fixVulnerabilities/FixVulnerabilitiesTool.ts
|
|
12524
|
-
var FixVulnerabilitiesTool = class {
|
|
12636
|
+
var FixVulnerabilitiesTool = class extends BaseTool {
|
|
12525
12637
|
constructor() {
|
|
12638
|
+
super(...arguments);
|
|
12526
12639
|
__publicField(this, "name", "fix_vulnerabilities");
|
|
12527
|
-
__publicField(this, "
|
|
12640
|
+
__publicField(this, "displayName", "Fix Vulnerabilities");
|
|
12528
12641
|
__publicField(this, "description", "Scans the current code changes and returns fixes for potential vulnerabilities");
|
|
12642
|
+
__publicField(this, "inputValidationSchema", z33.object({
|
|
12643
|
+
path: z33.string().describe(
|
|
12644
|
+
"Path to the local git repository to check for available fixes"
|
|
12645
|
+
),
|
|
12646
|
+
offset: z33.number().optional().describe("Optional offset for pagination"),
|
|
12647
|
+
limit: z33.number().optional().describe("Optional maximum number of results to return"),
|
|
12648
|
+
rescan: z33.boolean().optional().describe("Optional whether to rescan the repository")
|
|
12649
|
+
}));
|
|
12529
12650
|
__publicField(this, "inputSchema", {
|
|
12530
12651
|
type: "object",
|
|
12531
12652
|
properties: {
|
|
12532
12653
|
path: {
|
|
12533
12654
|
type: "string",
|
|
12534
|
-
description: "
|
|
12655
|
+
description: "Path to the project directory to check for available fixes"
|
|
12656
|
+
},
|
|
12657
|
+
offset: {
|
|
12658
|
+
type: "number",
|
|
12659
|
+
description: "[Optional] offset for pagination"
|
|
12660
|
+
},
|
|
12661
|
+
limit: {
|
|
12662
|
+
type: "number",
|
|
12663
|
+
description: "[Optional] maximum number of results to return"
|
|
12664
|
+
},
|
|
12665
|
+
rescan: {
|
|
12666
|
+
type: "boolean",
|
|
12667
|
+
description: "[Optional] whether to rescan the repository"
|
|
12535
12668
|
}
|
|
12536
12669
|
},
|
|
12537
12670
|
required: ["path"]
|
|
12538
12671
|
});
|
|
12539
12672
|
}
|
|
12540
|
-
async
|
|
12673
|
+
async executeInternal(args) {
|
|
12541
12674
|
logInfo("Executing tool: fix_vulnerabilities", { path: args.path });
|
|
12542
12675
|
if (!args.path) {
|
|
12543
12676
|
throw new Error("Invalid arguments: Missing required parameter 'path'");
|
|
@@ -12597,10 +12730,13 @@ var FixVulnerabilitiesTool = class {
|
|
|
12597
12730
|
}
|
|
12598
12731
|
try {
|
|
12599
12732
|
const vulnerabilityFixService = new VulnerabilityFixService();
|
|
12600
|
-
const fixResult = await vulnerabilityFixService.processVulnerabilities(
|
|
12601
|
-
files,
|
|
12602
|
-
args.path
|
|
12603
|
-
|
|
12733
|
+
const fixResult = await vulnerabilityFixService.processVulnerabilities({
|
|
12734
|
+
fileList: files,
|
|
12735
|
+
repositoryPath: args.path,
|
|
12736
|
+
offset: args.offset,
|
|
12737
|
+
limit: args.limit,
|
|
12738
|
+
isRescan: args.rescan
|
|
12739
|
+
});
|
|
12604
12740
|
const result = {
|
|
12605
12741
|
content: [
|
|
12606
12742
|
{
|
|
@@ -12640,28 +12776,22 @@ function createMcpServer() {
|
|
|
12640
12776
|
name: "mobb-mcp",
|
|
12641
12777
|
version: "1.0.0"
|
|
12642
12778
|
});
|
|
12779
|
+
const enabledToolsEnv = process.env["TOOLS_ENABLED"];
|
|
12780
|
+
const enabledToolsSet = enabledToolsEnv ? new Set(
|
|
12781
|
+
enabledToolsEnv.split(",").map((t) => t.trim()).filter((t) => t.length > 0)
|
|
12782
|
+
) : null;
|
|
12783
|
+
const registerIfEnabled = (tool) => {
|
|
12784
|
+
if (!enabledToolsSet || enabledToolsSet.has(tool.name)) {
|
|
12785
|
+
server.registerTool(tool);
|
|
12786
|
+
logDebug(`Registered tool: ${tool.name}`);
|
|
12787
|
+
} else {
|
|
12788
|
+
logDebug(`Skipping tool (disabled): ${tool.name}`);
|
|
12789
|
+
}
|
|
12790
|
+
};
|
|
12643
12791
|
const fixVulnerabilitiesTool = new FixVulnerabilitiesTool();
|
|
12644
12792
|
const checkForAvailableFixesTool = new CheckForAvailableFixesTool();
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
definition: {
|
|
12648
|
-
name: fixVulnerabilitiesTool.name,
|
|
12649
|
-
display_name: fixVulnerabilitiesTool.display_name,
|
|
12650
|
-
description: fixVulnerabilitiesTool.description,
|
|
12651
|
-
inputSchema: fixVulnerabilitiesTool.inputSchema
|
|
12652
|
-
},
|
|
12653
|
-
execute: (args) => fixVulnerabilitiesTool.execute(args)
|
|
12654
|
-
});
|
|
12655
|
-
server.registerTool({
|
|
12656
|
-
name: checkForAvailableFixesTool.name,
|
|
12657
|
-
definition: {
|
|
12658
|
-
name: checkForAvailableFixesTool.name,
|
|
12659
|
-
display_name: checkForAvailableFixesTool.displayName,
|
|
12660
|
-
description: checkForAvailableFixesTool.description,
|
|
12661
|
-
inputSchema: checkForAvailableFixesTool.getJsonSchema()
|
|
12662
|
-
},
|
|
12663
|
-
execute: (args) => checkForAvailableFixesTool.execute(args)
|
|
12664
|
-
});
|
|
12793
|
+
registerIfEnabled(fixVulnerabilitiesTool);
|
|
12794
|
+
registerIfEnabled(checkForAvailableFixesTool);
|
|
12665
12795
|
logInfo("MCP server created and configured");
|
|
12666
12796
|
return server;
|
|
12667
12797
|
}
|