mobbdev 1.0.94 → 1.0.97
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 +668 -169
- package/package.json +11 -11
package/dist/index.mjs
CHANGED
|
@@ -370,9 +370,11 @@ var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
|
|
|
370
370
|
IssueType_Enum2["DangerousFunctionOverflow"] = "DANGEROUS_FUNCTION_OVERFLOW";
|
|
371
371
|
IssueType_Enum2["DeadCodeUnusedField"] = "DEAD_CODE_UNUSED_FIELD";
|
|
372
372
|
IssueType_Enum2["DebugEnabled"] = "DEBUG_ENABLED";
|
|
373
|
+
IssueType_Enum2["DeclareVariableExplicitly"] = "DECLARE_VARIABLE_EXPLICITLY";
|
|
373
374
|
IssueType_Enum2["DefaultRightsInObjDefinition"] = "DEFAULT_RIGHTS_IN_OBJ_DEFINITION";
|
|
374
375
|
IssueType_Enum2["DeprecatedFunction"] = "DEPRECATED_FUNCTION";
|
|
375
376
|
IssueType_Enum2["DosStringBuilder"] = "DOS_STRING_BUILDER";
|
|
377
|
+
IssueType_Enum2["DoNotRaiseException"] = "DO_NOT_RAISE_EXCEPTION";
|
|
376
378
|
IssueType_Enum2["DuplicatedStrings"] = "DUPLICATED_STRINGS";
|
|
377
379
|
IssueType_Enum2["ErroneousStringCompare"] = "ERRONEOUS_STRING_COMPARE";
|
|
378
380
|
IssueType_Enum2["ErrorCondtionWithoutAction"] = "ERROR_CONDTION_WITHOUT_ACTION";
|
|
@@ -484,6 +486,7 @@ var Vulnerability_Report_Issue_State_Enum = /* @__PURE__ */ ((Vulnerability_Repo
|
|
|
484
486
|
Vulnerability_Report_Issue_State_Enum2["Fixed"] = "Fixed";
|
|
485
487
|
Vulnerability_Report_Issue_State_Enum2["NoFix"] = "NoFix";
|
|
486
488
|
Vulnerability_Report_Issue_State_Enum2["Pending"] = "Pending";
|
|
489
|
+
Vulnerability_Report_Issue_State_Enum2["Unfixable"] = "Unfixable";
|
|
487
490
|
Vulnerability_Report_Issue_State_Enum2["Unsupported"] = "Unsupported";
|
|
488
491
|
return Vulnerability_Report_Issue_State_Enum2;
|
|
489
492
|
})(Vulnerability_Report_Issue_State_Enum || {});
|
|
@@ -492,6 +495,7 @@ var Vulnerability_Report_Issue_Tag_Enum = /* @__PURE__ */ ((Vulnerability_Report
|
|
|
492
495
|
Vulnerability_Report_Issue_Tag_Enum3["AuxiliaryCode"] = "AUXILIARY_CODE";
|
|
493
496
|
Vulnerability_Report_Issue_Tag_Enum3["FalsePositive"] = "FALSE_POSITIVE";
|
|
494
497
|
Vulnerability_Report_Issue_Tag_Enum3["TestCode"] = "TEST_CODE";
|
|
498
|
+
Vulnerability_Report_Issue_Tag_Enum3["Unfixable"] = "UNFIXABLE";
|
|
495
499
|
Vulnerability_Report_Issue_Tag_Enum3["VendorCode"] = "VENDOR_CODE";
|
|
496
500
|
return Vulnerability_Report_Issue_Tag_Enum3;
|
|
497
501
|
})(Vulnerability_Report_Issue_Tag_Enum || {});
|
|
@@ -515,6 +519,35 @@ var Vulnerability_Severity_Enum = /* @__PURE__ */ ((Vulnerability_Severity_Enum2
|
|
|
515
519
|
Vulnerability_Severity_Enum2["Medium"] = "medium";
|
|
516
520
|
return Vulnerability_Severity_Enum2;
|
|
517
521
|
})(Vulnerability_Severity_Enum || {});
|
|
522
|
+
var FixDetailsFragmentDoc = `
|
|
523
|
+
fragment FixDetails on fix {
|
|
524
|
+
id
|
|
525
|
+
confidence
|
|
526
|
+
safeIssueType
|
|
527
|
+
severityText
|
|
528
|
+
vulnerabilityReportIssues {
|
|
529
|
+
parsedIssueType
|
|
530
|
+
parsedSeverity
|
|
531
|
+
vulnerabilityReportIssueTags {
|
|
532
|
+
vulnerability_report_issue_tag_value
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
patchAndQuestions {
|
|
536
|
+
__typename
|
|
537
|
+
... on FixData {
|
|
538
|
+
patch
|
|
539
|
+
patchOriginalEncodingBase64
|
|
540
|
+
extraContext {
|
|
541
|
+
extraContext {
|
|
542
|
+
key
|
|
543
|
+
value
|
|
544
|
+
}
|
|
545
|
+
fixDescription
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
`;
|
|
518
551
|
var MeDocument = `
|
|
519
552
|
query Me {
|
|
520
553
|
me {
|
|
@@ -713,7 +746,7 @@ var GetVulByNodesMetadataDocument = `
|
|
|
713
746
|
where: {id: {_eq: $vulnerabilityReportId}}
|
|
714
747
|
) {
|
|
715
748
|
vulnerabilityReportIssues(
|
|
716
|
-
where: {fixId: {_is_null: true}, _or: [{category: {_eq: "Irrelevant"}}, {category: {_eq: "FalsePositive"}}]}
|
|
749
|
+
where: {fixId: {_is_null: true}, _or: [{category: {_eq: "Irrelevant"}}, {category: {_eq: "FalsePositive"}}, {category: {_eq: "Filtered"}}]}
|
|
717
750
|
) {
|
|
718
751
|
id
|
|
719
752
|
safeIssueType
|
|
@@ -947,34 +980,77 @@ var AutoPrAnalysisDocument = `
|
|
|
947
980
|
var GetMcpFixesDocument = `
|
|
948
981
|
query GetMCPFixes($fixReportId: uuid!) {
|
|
949
982
|
fix(where: {fixReportId: {_eq: $fixReportId}}) {
|
|
983
|
+
...FixDetails
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
${FixDetailsFragmentDoc}`;
|
|
987
|
+
var GetLatestReportByRepoUrlDocument = `
|
|
988
|
+
query GetLatestReportByRepoUrl($repoUrl: String!, $limit: Int = 3) {
|
|
989
|
+
fixReport(
|
|
990
|
+
where: {repo: {originalUrl: {_eq: $repoUrl}}}
|
|
991
|
+
order_by: {createdOn: desc}
|
|
992
|
+
limit: 1
|
|
993
|
+
) {
|
|
950
994
|
id
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
995
|
+
createdOn
|
|
996
|
+
repo {
|
|
997
|
+
originalUrl
|
|
998
|
+
}
|
|
999
|
+
issueTypes
|
|
1000
|
+
fixes_aggregate(
|
|
1001
|
+
where: {vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}
|
|
1002
|
+
) {
|
|
1003
|
+
aggregate {
|
|
1004
|
+
count
|
|
959
1005
|
}
|
|
960
1006
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1007
|
+
CRITICAL: fixes_aggregate(
|
|
1008
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "critical"}}]}
|
|
1009
|
+
) {
|
|
1010
|
+
aggregate {
|
|
1011
|
+
count
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
HIGH: fixes_aggregate(
|
|
1015
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "high"}}]}
|
|
1016
|
+
) {
|
|
1017
|
+
aggregate {
|
|
1018
|
+
count
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
MEDIUM: fixes_aggregate(
|
|
1022
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "medium"}}]}
|
|
1023
|
+
) {
|
|
1024
|
+
aggregate {
|
|
1025
|
+
count
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
LOW: fixes_aggregate(
|
|
1029
|
+
where: {_and: [{vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}, {severityText: {_eq: "low"}}]}
|
|
1030
|
+
) {
|
|
1031
|
+
aggregate {
|
|
1032
|
+
count
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
fixes(
|
|
1036
|
+
where: {vulnerabilityReportIssues: {category: {_eq: "Fixable"}}}
|
|
1037
|
+
order_by: {severityValue: desc}
|
|
1038
|
+
limit: $limit
|
|
1039
|
+
) {
|
|
1040
|
+
...FixDetails
|
|
1041
|
+
}
|
|
1042
|
+
vulnerabilityReport {
|
|
1043
|
+
scanDate
|
|
1044
|
+
vendor
|
|
1045
|
+
vulnerabilityReportIssues_aggregate(where: {category: {_eq: "Fixable"}}) {
|
|
1046
|
+
aggregate {
|
|
1047
|
+
count
|
|
972
1048
|
}
|
|
973
1049
|
}
|
|
974
1050
|
}
|
|
975
1051
|
}
|
|
976
1052
|
}
|
|
977
|
-
`;
|
|
1053
|
+
${FixDetailsFragmentDoc}`;
|
|
978
1054
|
var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
|
|
979
1055
|
function getSdk(client, withWrapper = defaultWrapper) {
|
|
980
1056
|
return {
|
|
@@ -1043,6 +1119,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
1043
1119
|
},
|
|
1044
1120
|
GetMCPFixes(variables, requestHeaders, signal) {
|
|
1045
1121
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetMcpFixesDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetMCPFixes", "query", variables);
|
|
1122
|
+
},
|
|
1123
|
+
GetLatestReportByRepoUrl(variables, requestHeaders, signal) {
|
|
1124
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetLatestReportByRepoUrlDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetLatestReportByRepoUrl", "query", variables);
|
|
1046
1125
|
}
|
|
1047
1126
|
};
|
|
1048
1127
|
}
|
|
@@ -1247,14 +1326,16 @@ var CATEGORY = {
|
|
|
1247
1326
|
Unsupported: "Unsupported",
|
|
1248
1327
|
Irrelevant: "Irrelevant",
|
|
1249
1328
|
FalsePositive: "FalsePositive",
|
|
1250
|
-
Fixable: "Fixable"
|
|
1329
|
+
Fixable: "Fixable",
|
|
1330
|
+
Filtered: "Filtered"
|
|
1251
1331
|
};
|
|
1252
1332
|
var ValidCategoriesZ = z4.union([
|
|
1253
1333
|
z4.literal(CATEGORY.NoFix),
|
|
1254
1334
|
z4.literal(CATEGORY.Unsupported),
|
|
1255
1335
|
z4.literal(CATEGORY.Irrelevant),
|
|
1256
1336
|
z4.literal(CATEGORY.FalsePositive),
|
|
1257
|
-
z4.literal(CATEGORY.Fixable)
|
|
1337
|
+
z4.literal(CATEGORY.Fixable),
|
|
1338
|
+
z4.literal(CATEGORY.Filtered)
|
|
1258
1339
|
]);
|
|
1259
1340
|
var VulnerabilityReportIssueSharedStateZ = z4.object({
|
|
1260
1341
|
id: z4.string().uuid(),
|
|
@@ -1336,7 +1417,8 @@ var GeneralIssueZ = BaseIssuePartsZ.merge(
|
|
|
1336
1417
|
category: z4.union([
|
|
1337
1418
|
z4.literal(CATEGORY.NoFix),
|
|
1338
1419
|
z4.literal(CATEGORY.Unsupported),
|
|
1339
|
-
z4.literal(CATEGORY.Fixable)
|
|
1420
|
+
z4.literal(CATEGORY.Fixable),
|
|
1421
|
+
z4.literal(CATEGORY.Filtered)
|
|
1340
1422
|
])
|
|
1341
1423
|
})
|
|
1342
1424
|
);
|
|
@@ -1366,7 +1448,8 @@ var mapCategoryToBucket = {
|
|
|
1366
1448
|
Irrelevant: "irrelevant",
|
|
1367
1449
|
NoFix: "remaining",
|
|
1368
1450
|
Unsupported: "remaining",
|
|
1369
|
-
Fixable: "fixable"
|
|
1451
|
+
Fixable: "fixable",
|
|
1452
|
+
Filtered: "remaining"
|
|
1370
1453
|
};
|
|
1371
1454
|
|
|
1372
1455
|
// src/features/analysis/scm/shared/src/types/types.ts
|
|
@@ -1480,7 +1563,9 @@ var issueTypeMap = {
|
|
|
1480
1563
|
["TAR_SLIP" /* TarSlip */]: "Tar Slip",
|
|
1481
1564
|
["MISSING_WHITESPACE" /* MissingWhitespace */]: "Missing Whitespace",
|
|
1482
1565
|
["NO_PRINT_STATEMENT" /* NoPrintStatement */]: 'Python 2 "print" Statement Is Obsolete',
|
|
1483
|
-
["NO_OP_OVERHEAD" /* NoOpOverhead */]: "Expensive Arguments in Conditional Methods"
|
|
1566
|
+
["NO_OP_OVERHEAD" /* NoOpOverhead */]: "Expensive Arguments in Conditional Methods",
|
|
1567
|
+
["DO_NOT_RAISE_EXCEPTION" /* DoNotRaiseException */]: "Do Not Raise Exception",
|
|
1568
|
+
["DECLARE_VARIABLE_EXPLICITLY" /* DeclareVariableExplicitly */]: "Declare Variable Explicitly"
|
|
1484
1569
|
};
|
|
1485
1570
|
var issueTypeZ = z5.nativeEnum(IssueType_Enum);
|
|
1486
1571
|
var getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -1501,17 +1586,20 @@ function getTagTooltip(tag) {
|
|
|
1501
1586
|
case "AUTOGENERATED_CODE":
|
|
1502
1587
|
return "Code created by tools or frameworks, not manually written";
|
|
1503
1588
|
case "AUXILIARY_CODE":
|
|
1504
|
-
return "Issue found in supporting files that don
|
|
1589
|
+
return "Issue found in supporting files that don't impact core functionality";
|
|
1590
|
+
case "Filtered":
|
|
1591
|
+
return "Issue was filtered by user in the Fix Policy";
|
|
1505
1592
|
default:
|
|
1506
1593
|
return tag;
|
|
1507
1594
|
}
|
|
1508
1595
|
}
|
|
1509
1596
|
var issueDescription = {
|
|
1510
1597
|
["AUTOGENERATED_CODE" /* AutogeneratedCode */]: "The flagged code is generated automatically by tools or frameworks as part of the build or runtime process. This categorization highlights that **the issue resides in non-manual code**, which often requires tool-specific solutions or exemptions.",
|
|
1511
|
-
["AUXILIARY_CODE" /* AuxiliaryCode */]: "The flagged code is auxiliary or supporting code, such as configuration files, build scripts, or other non-application logic. This categorization indicates that the issue is not directly related to the application
|
|
1512
|
-
["FALSE_POSITIVE" /* FalsePositive */]: "The flagged code **does not represent an actual vulnerability within the application
|
|
1598
|
+
["AUXILIARY_CODE" /* AuxiliaryCode */]: "The flagged code is auxiliary or supporting code, such as configuration files, build scripts, or other non-application logic. This categorization indicates that the issue is not directly related to the application's core functionality.",
|
|
1599
|
+
["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.",
|
|
1513
1600
|
["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**.",
|
|
1514
|
-
["
|
|
1601
|
+
["UNFIXABLE" /* Unfixable */]: "The flagged code cannot be fixed",
|
|
1602
|
+
["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."
|
|
1515
1603
|
};
|
|
1516
1604
|
function replaceKeysWithValues(fixDescription, extraContext) {
|
|
1517
1605
|
let result = fixDescription;
|
|
@@ -1642,7 +1730,8 @@ var ReportQueryResultZ = z7.object({
|
|
|
1642
1730
|
z7.object({
|
|
1643
1731
|
id: z7.string().uuid(),
|
|
1644
1732
|
issueType: z7.string(),
|
|
1645
|
-
issueLanguage: z7.string()
|
|
1733
|
+
issueLanguage: z7.string(),
|
|
1734
|
+
category: z7.string()
|
|
1646
1735
|
})
|
|
1647
1736
|
)
|
|
1648
1737
|
// scmSubmitFixRequests: ScmSubmitFixRequestsZ,
|
|
@@ -1767,6 +1856,7 @@ var VulnerabilityReportIssueZ = z7.object({
|
|
|
1767
1856
|
parsedSeverity: ParsedSeverityZ,
|
|
1768
1857
|
severity: z7.string(),
|
|
1769
1858
|
severityValue: z7.number(),
|
|
1859
|
+
category: z7.string(),
|
|
1770
1860
|
codeNodes: z7.array(z7.object({ path: z7.string() })),
|
|
1771
1861
|
vulnerabilityReportIssueTags: z7.array(
|
|
1772
1862
|
z7.object({
|
|
@@ -2217,7 +2307,9 @@ var fixDetailsData = {
|
|
|
2217
2307
|
["TAR_SLIP" /* TarSlip */]: void 0,
|
|
2218
2308
|
["MISSING_WHITESPACE" /* MissingWhitespace */]: void 0,
|
|
2219
2309
|
["NO_PRINT_STATEMENT" /* NoPrintStatement */]: void 0,
|
|
2220
|
-
["NO_OP_OVERHEAD" /* NoOpOverhead */]: void 0
|
|
2310
|
+
["NO_OP_OVERHEAD" /* NoOpOverhead */]: void 0,
|
|
2311
|
+
["DO_NOT_RAISE_EXCEPTION" /* DoNotRaiseException */]: void 0,
|
|
2312
|
+
["DECLARE_VARIABLE_EXPLICITLY" /* DeclareVariableExplicitly */]: void 0
|
|
2221
2313
|
};
|
|
2222
2314
|
|
|
2223
2315
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -5426,6 +5518,92 @@ var GitService = class {
|
|
|
5426
5518
|
throw new Error(errorMessage);
|
|
5427
5519
|
}
|
|
5428
5520
|
}
|
|
5521
|
+
/**
|
|
5522
|
+
* Normalizes a Git URL to HTTPS format for various Git hosting platforms
|
|
5523
|
+
* @param url The Git URL to normalize
|
|
5524
|
+
* @returns The normalized HTTPS URL
|
|
5525
|
+
*/
|
|
5526
|
+
normalizeGitUrl(url) {
|
|
5527
|
+
let normalizedUrl = url;
|
|
5528
|
+
if (normalizedUrl.endsWith(".git")) {
|
|
5529
|
+
normalizedUrl = normalizedUrl.slice(0, -".git".length);
|
|
5530
|
+
}
|
|
5531
|
+
const sshToHttpsMappings = [
|
|
5532
|
+
// GitHub
|
|
5533
|
+
{ pattern: "git@github.com:", replacement: "https://github.com/" },
|
|
5534
|
+
// GitLab
|
|
5535
|
+
{ pattern: "git@gitlab.com:", replacement: "https://gitlab.com/" },
|
|
5536
|
+
// Bitbucket
|
|
5537
|
+
{ pattern: "git@bitbucket.org:", replacement: "https://bitbucket.org/" },
|
|
5538
|
+
// Azure DevOps (SSH format)
|
|
5539
|
+
{
|
|
5540
|
+
pattern: "git@ssh.dev.azure.com:",
|
|
5541
|
+
replacement: "https://dev.azure.com/"
|
|
5542
|
+
},
|
|
5543
|
+
// Azure DevOps (alternative SSH format)
|
|
5544
|
+
{
|
|
5545
|
+
pattern: /git@([^:]+):v3\/([^/]+)\/([^/]+)\/([^/]+)/,
|
|
5546
|
+
replacement: "https://$1/$2/_git/$4"
|
|
5547
|
+
}
|
|
5548
|
+
];
|
|
5549
|
+
for (const mapping of sshToHttpsMappings) {
|
|
5550
|
+
if (typeof mapping.pattern === "string") {
|
|
5551
|
+
if (normalizedUrl.startsWith(mapping.pattern)) {
|
|
5552
|
+
normalizedUrl = normalizedUrl.replace(
|
|
5553
|
+
mapping.pattern,
|
|
5554
|
+
mapping.replacement
|
|
5555
|
+
);
|
|
5556
|
+
break;
|
|
5557
|
+
}
|
|
5558
|
+
} else {
|
|
5559
|
+
const match = normalizedUrl.match(mapping.pattern);
|
|
5560
|
+
if (match) {
|
|
5561
|
+
normalizedUrl = normalizedUrl.replace(
|
|
5562
|
+
mapping.pattern,
|
|
5563
|
+
mapping.replacement
|
|
5564
|
+
);
|
|
5565
|
+
break;
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5568
|
+
}
|
|
5569
|
+
return normalizedUrl;
|
|
5570
|
+
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Gets all remote repository URLs (equivalent to 'git remote -v')
|
|
5573
|
+
*/
|
|
5574
|
+
async getRepoUrls() {
|
|
5575
|
+
this.log("Getting all remote repository URLs", "debug");
|
|
5576
|
+
try {
|
|
5577
|
+
const remotes = await this.git.remote(["-v"]);
|
|
5578
|
+
if (!remotes) {
|
|
5579
|
+
return {};
|
|
5580
|
+
}
|
|
5581
|
+
const remoteMap = {};
|
|
5582
|
+
remotes.split("\n").forEach((line) => {
|
|
5583
|
+
if (!line.trim()) return;
|
|
5584
|
+
const [remoteName, url, type2] = line.split(/\s+/);
|
|
5585
|
+
if (!remoteName || !url || !type2) return;
|
|
5586
|
+
if (!remoteMap[remoteName]) {
|
|
5587
|
+
remoteMap[remoteName] = { fetch: "", push: "" };
|
|
5588
|
+
}
|
|
5589
|
+
const normalizedUrl = this.normalizeGitUrl(url);
|
|
5590
|
+
const remote = remoteMap[remoteName];
|
|
5591
|
+
if (type2 === "(fetch)") {
|
|
5592
|
+
remote.fetch = normalizedUrl;
|
|
5593
|
+
} else if (type2 === "(push)") {
|
|
5594
|
+
remote.push = normalizedUrl;
|
|
5595
|
+
}
|
|
5596
|
+
});
|
|
5597
|
+
this.log("Remote repository URLs retrieved", "debug", {
|
|
5598
|
+
remotes: remoteMap
|
|
5599
|
+
});
|
|
5600
|
+
return remoteMap;
|
|
5601
|
+
} catch (error) {
|
|
5602
|
+
const errorMessage = `Failed to get remote repository URLs: ${error.message}`;
|
|
5603
|
+
this.log(errorMessage, "error", { error });
|
|
5604
|
+
throw new Error(errorMessage);
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5429
5607
|
};
|
|
5430
5608
|
|
|
5431
5609
|
// src/features/analysis/scm/scmSubmit/index.ts
|
|
@@ -6827,7 +7005,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6827
7005
|
}
|
|
6828
7006
|
async forkRepo(repoUrl) {
|
|
6829
7007
|
this._validateAccessToken();
|
|
6830
|
-
return this.githubSdk.forkRepo({
|
|
7008
|
+
return await this.githubSdk.forkRepo({
|
|
6831
7009
|
repoUrl
|
|
6832
7010
|
});
|
|
6833
7011
|
}
|
|
@@ -6837,7 +7015,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6837
7015
|
const { data: repositoryPublicKeyResponse } = await this.githubSdk.getRepositoryPublicKey({ owner, repo });
|
|
6838
7016
|
const { key_id, key } = repositoryPublicKeyResponse;
|
|
6839
7017
|
const encryptedValue = await encryptSecret(params.value, key);
|
|
6840
|
-
return this.githubSdk.createOrUpdateRepositorySecret({
|
|
7018
|
+
return await this.githubSdk.createOrUpdateRepositorySecret({
|
|
6841
7019
|
encrypted_value: encryptedValue,
|
|
6842
7020
|
secret_name: params.name,
|
|
6843
7021
|
key_id,
|
|
@@ -6856,12 +7034,12 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6856
7034
|
return { pull_request_url };
|
|
6857
7035
|
}
|
|
6858
7036
|
async validateParams() {
|
|
6859
|
-
return githubValidateParams(this.url, this.accessToken);
|
|
7037
|
+
return await githubValidateParams(this.url, this.accessToken);
|
|
6860
7038
|
}
|
|
6861
7039
|
async postPrComment(params) {
|
|
6862
7040
|
this._validateAccessTokenAndUrl();
|
|
6863
7041
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
6864
|
-
return this.githubSdk.postPrComment({
|
|
7042
|
+
return await this.githubSdk.postPrComment({
|
|
6865
7043
|
...params,
|
|
6866
7044
|
owner,
|
|
6867
7045
|
repo
|
|
@@ -6870,7 +7048,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6870
7048
|
async updatePrComment(params) {
|
|
6871
7049
|
this._validateAccessTokenAndUrl();
|
|
6872
7050
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
6873
|
-
return this.githubSdk.updatePrComment({
|
|
7051
|
+
return await this.githubSdk.updatePrComment({
|
|
6874
7052
|
...params,
|
|
6875
7053
|
owner,
|
|
6876
7054
|
repo
|
|
@@ -6879,7 +7057,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6879
7057
|
async deleteComment(params) {
|
|
6880
7058
|
this._validateAccessTokenAndUrl();
|
|
6881
7059
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
6882
|
-
return this.githubSdk.deleteComment({
|
|
7060
|
+
return await this.githubSdk.deleteComment({
|
|
6883
7061
|
...params,
|
|
6884
7062
|
owner,
|
|
6885
7063
|
repo
|
|
@@ -6888,7 +7066,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6888
7066
|
async getPrComments(params) {
|
|
6889
7067
|
this._validateAccessTokenAndUrl();
|
|
6890
7068
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
6891
|
-
return this.githubSdk.getPrComments({
|
|
7069
|
+
return await this.githubSdk.getPrComments({
|
|
6892
7070
|
per_page: 100,
|
|
6893
7071
|
...params,
|
|
6894
7072
|
owner,
|
|
@@ -6907,7 +7085,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6907
7085
|
}
|
|
6908
7086
|
async getRepoList(_scmOrg) {
|
|
6909
7087
|
this._validateAccessToken();
|
|
6910
|
-
return this.githubSdk.getGithubRepoList();
|
|
7088
|
+
return await this.githubSdk.getGithubRepoList();
|
|
6911
7089
|
}
|
|
6912
7090
|
async getBranchList() {
|
|
6913
7091
|
this._validateAccessTokenAndUrl();
|
|
@@ -6934,27 +7112,30 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6934
7112
|
return Promise.resolve(downloadUrl);
|
|
6935
7113
|
}
|
|
6936
7114
|
async _getUsernameForAuthUrl() {
|
|
6937
|
-
return this.getUsername();
|
|
7115
|
+
return await this.getUsername();
|
|
6938
7116
|
}
|
|
6939
7117
|
async getIsRemoteBranch(branch) {
|
|
6940
7118
|
this._validateUrl();
|
|
6941
|
-
return this.githubSdk.getGithubIsRemoteBranch({
|
|
7119
|
+
return await this.githubSdk.getGithubIsRemoteBranch({
|
|
7120
|
+
branch,
|
|
7121
|
+
repoUrl: this.url
|
|
7122
|
+
});
|
|
6942
7123
|
}
|
|
6943
7124
|
async getUserHasAccessToRepo() {
|
|
6944
7125
|
this._validateAccessTokenAndUrl();
|
|
6945
7126
|
const username = await this.getUsername();
|
|
6946
|
-
return this.githubSdk.getGithubIsUserCollaborator({
|
|
7127
|
+
return await this.githubSdk.getGithubIsUserCollaborator({
|
|
6947
7128
|
repoUrl: this.url,
|
|
6948
7129
|
username
|
|
6949
7130
|
});
|
|
6950
7131
|
}
|
|
6951
7132
|
async getUsername() {
|
|
6952
7133
|
this._validateAccessToken();
|
|
6953
|
-
return this.githubSdk.getGithubUsername();
|
|
7134
|
+
return await this.githubSdk.getGithubUsername();
|
|
6954
7135
|
}
|
|
6955
7136
|
async getSubmitRequestStatus(scmSubmitRequestId) {
|
|
6956
7137
|
this._validateAccessTokenAndUrl();
|
|
6957
|
-
return this.githubSdk.getGithubPullRequestStatus({
|
|
7138
|
+
return await this.githubSdk.getGithubPullRequestStatus({
|
|
6958
7139
|
repoUrl: this.url,
|
|
6959
7140
|
prNumber: Number(scmSubmitRequestId)
|
|
6960
7141
|
});
|
|
@@ -6977,7 +7158,10 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6977
7158
|
}
|
|
6978
7159
|
async getReferenceData(ref) {
|
|
6979
7160
|
this._validateUrl();
|
|
6980
|
-
return this.githubSdk.getGithubReferenceData({
|
|
7161
|
+
return await this.githubSdk.getGithubReferenceData({
|
|
7162
|
+
ref,
|
|
7163
|
+
gitHubUrl: this.url
|
|
7164
|
+
});
|
|
6981
7165
|
}
|
|
6982
7166
|
async getPrComment(commentId) {
|
|
6983
7167
|
this._validateUrl();
|
|
@@ -7046,7 +7230,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
7046
7230
|
}) {
|
|
7047
7231
|
this._validateAccessTokenAndUrl();
|
|
7048
7232
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
7049
|
-
return this.githubSdk.deleteGeneralPrComment({
|
|
7233
|
+
return await this.githubSdk.deleteGeneralPrComment({
|
|
7050
7234
|
owner,
|
|
7051
7235
|
repo,
|
|
7052
7236
|
comment_id: commentId
|
|
@@ -8814,31 +8998,33 @@ async function addFixCommentsForPr({
|
|
|
8814
8998
|
|
|
8815
8999
|
${contextString}` : description2;
|
|
8816
9000
|
}
|
|
8817
|
-
return
|
|
8818
|
-
(
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
9001
|
+
return await Promise.all(
|
|
9002
|
+
vulnerabilityReportIssue.codeNodes.map(
|
|
9003
|
+
async (vulnerabilityReportIssueCodeNode) => {
|
|
9004
|
+
return await postIssueComment({
|
|
9005
|
+
vulnerabilityReportIssueCodeNode: {
|
|
9006
|
+
path: vulnerabilityReportIssueCodeNode.path,
|
|
9007
|
+
startLine: vulnerabilityReportIssueCodeNode.startLine,
|
|
9008
|
+
vulnerabilityReportIssue: {
|
|
9009
|
+
fixId: "",
|
|
9010
|
+
safeIssueType: vulnerabilityReportIssue.safeIssueType,
|
|
9011
|
+
vulnerabilityReportIssueTags: vulnerabilityReportIssue.vulnerabilityReportIssueTags,
|
|
9012
|
+
category: vulnerabilityReportIssue.category
|
|
9013
|
+
},
|
|
9014
|
+
vulnerabilityReportIssueId: vulnerabilityReportIssue.id
|
|
8828
9015
|
},
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
}
|
|
9016
|
+
projectId,
|
|
9017
|
+
analysisId,
|
|
9018
|
+
organizationId,
|
|
9019
|
+
fixesById,
|
|
9020
|
+
scm,
|
|
9021
|
+
pullRequest,
|
|
9022
|
+
scanner,
|
|
9023
|
+
commitSha,
|
|
9024
|
+
fpDescription
|
|
9025
|
+
});
|
|
9026
|
+
}
|
|
9027
|
+
)
|
|
8842
9028
|
);
|
|
8843
9029
|
}
|
|
8844
9030
|
),
|
|
@@ -11303,6 +11489,30 @@ var McpGQLClient = class {
|
|
|
11303
11489
|
return null;
|
|
11304
11490
|
}
|
|
11305
11491
|
}
|
|
11492
|
+
async getLatestReportByRepoUrl(repoUrl, limit) {
|
|
11493
|
+
try {
|
|
11494
|
+
logDebug("GraphQL: Calling GetLatestReportByRepoUrl query", {
|
|
11495
|
+
repoUrl,
|
|
11496
|
+
limit
|
|
11497
|
+
});
|
|
11498
|
+
const res = await this.clientSdk.GetLatestReportByRepoUrl({
|
|
11499
|
+
repoUrl,
|
|
11500
|
+
limit
|
|
11501
|
+
});
|
|
11502
|
+
logInfo("GraphQL: GetLatestReportByRepoUrl successful", {
|
|
11503
|
+
result: res,
|
|
11504
|
+
reportCount: res.fixReport?.length || 0
|
|
11505
|
+
});
|
|
11506
|
+
return res.fixReport?.[0] || null;
|
|
11507
|
+
} catch (e) {
|
|
11508
|
+
logError("GraphQL: GetLatestReportByRepoUrl failed", {
|
|
11509
|
+
error: e,
|
|
11510
|
+
repoUrl,
|
|
11511
|
+
...this.getErrorContext()
|
|
11512
|
+
});
|
|
11513
|
+
throw e;
|
|
11514
|
+
}
|
|
11515
|
+
}
|
|
11306
11516
|
};
|
|
11307
11517
|
async function openBrowser(url) {
|
|
11308
11518
|
const now = Date.now();
|
|
@@ -11475,21 +11685,12 @@ var McpServer = class {
|
|
|
11475
11685
|
}
|
|
11476
11686
|
async handleListToolsRequest(request) {
|
|
11477
11687
|
logInfo("Received list_tools request", { params: request.params });
|
|
11478
|
-
|
|
11479
|
-
await getMcpGQLClient();
|
|
11480
|
-
} catch (error) {
|
|
11481
|
-
logError("Failed to get MCPGQLClient", { error });
|
|
11482
|
-
const authError = new Error(
|
|
11483
|
-
"Please authorize this client by visiting: https://mobb.ai"
|
|
11484
|
-
);
|
|
11485
|
-
authError.name = "AuthorizationRequired";
|
|
11486
|
-
throw authError;
|
|
11487
|
-
}
|
|
11688
|
+
void getMcpGQLClient();
|
|
11488
11689
|
const tools = this.toolRegistry.getAllTools();
|
|
11489
|
-
|
|
11690
|
+
const response = {
|
|
11490
11691
|
tools: tools.map((tool) => ({
|
|
11491
11692
|
name: tool.name,
|
|
11492
|
-
display_name: tool.name,
|
|
11693
|
+
display_name: tool.display_name || tool.name,
|
|
11493
11694
|
description: tool.description || "",
|
|
11494
11695
|
inputSchema: {
|
|
11495
11696
|
type: "object",
|
|
@@ -11498,6 +11699,8 @@ var McpServer = class {
|
|
|
11498
11699
|
}
|
|
11499
11700
|
}))
|
|
11500
11701
|
};
|
|
11702
|
+
logInfo("Returning list_tools response", { response });
|
|
11703
|
+
return response;
|
|
11501
11704
|
}
|
|
11502
11705
|
async handleCallToolRequest(request) {
|
|
11503
11706
|
const { name, arguments: args } = request.params;
|
|
@@ -11568,6 +11771,9 @@ var McpServer = class {
|
|
|
11568
11771
|
}
|
|
11569
11772
|
};
|
|
11570
11773
|
|
|
11774
|
+
// src/mcp/tools/checkForAvailableFixes/AvailableFixesTool.ts
|
|
11775
|
+
import { z as z32 } from "zod";
|
|
11776
|
+
|
|
11571
11777
|
// src/mcp/services/PathValidation.ts
|
|
11572
11778
|
import fs9 from "fs";
|
|
11573
11779
|
import path11 from "path";
|
|
@@ -11613,6 +11819,369 @@ var PathValidation = class {
|
|
|
11613
11819
|
}
|
|
11614
11820
|
};
|
|
11615
11821
|
|
|
11822
|
+
// src/mcp/tools/base/BaseTool.ts
|
|
11823
|
+
import { z as z31 } from "zod";
|
|
11824
|
+
var BaseTool = class {
|
|
11825
|
+
getDefinition() {
|
|
11826
|
+
return {
|
|
11827
|
+
name: this.name,
|
|
11828
|
+
display_name: this.displayName,
|
|
11829
|
+
description: this.description,
|
|
11830
|
+
inputSchema: {
|
|
11831
|
+
type: "object",
|
|
11832
|
+
properties: {
|
|
11833
|
+
path: {
|
|
11834
|
+
type: "string",
|
|
11835
|
+
description: "The path to the local git repository"
|
|
11836
|
+
}
|
|
11837
|
+
},
|
|
11838
|
+
required: ["path"]
|
|
11839
|
+
}
|
|
11840
|
+
};
|
|
11841
|
+
}
|
|
11842
|
+
async execute(args) {
|
|
11843
|
+
logInfo(`Executing tool: ${this.name}`, { args });
|
|
11844
|
+
const validatedArgs = this.validateInput(args);
|
|
11845
|
+
logDebug(`Tool ${this.name} input validation successful`, {
|
|
11846
|
+
validatedArgs
|
|
11847
|
+
});
|
|
11848
|
+
await this.validateAdditional(validatedArgs);
|
|
11849
|
+
try {
|
|
11850
|
+
const result = await this.executeInternal(validatedArgs);
|
|
11851
|
+
logInfo(`Tool ${this.name} executed successfully`);
|
|
11852
|
+
return result;
|
|
11853
|
+
} catch (error) {
|
|
11854
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
11855
|
+
logError(`Tool ${this.name} execution failed: ${errorMessage}`, {
|
|
11856
|
+
error,
|
|
11857
|
+
args
|
|
11858
|
+
});
|
|
11859
|
+
return this.createErrorResponse(errorMessage);
|
|
11860
|
+
}
|
|
11861
|
+
}
|
|
11862
|
+
validateInput(args) {
|
|
11863
|
+
try {
|
|
11864
|
+
return this.inputSchema.parse(args);
|
|
11865
|
+
} catch (error) {
|
|
11866
|
+
if (error instanceof z31.ZodError) {
|
|
11867
|
+
const errorDetails = error.errors.map((e) => {
|
|
11868
|
+
const fieldPath = e.path.length > 0 ? e.path.join(".") : "root";
|
|
11869
|
+
const message = e.message === "Required" ? `Missing required parameter '${fieldPath}'` : `Invalid value for '${fieldPath}': ${e.message}`;
|
|
11870
|
+
return message;
|
|
11871
|
+
});
|
|
11872
|
+
const errorMessage = `Invalid arguments: ${errorDetails.join(", ")}`;
|
|
11873
|
+
throw new Error(errorMessage);
|
|
11874
|
+
}
|
|
11875
|
+
throw error;
|
|
11876
|
+
}
|
|
11877
|
+
}
|
|
11878
|
+
/**
|
|
11879
|
+
* Additional validation that should bubble up as MCP errors
|
|
11880
|
+
* Override this method in subclasses to add custom validation
|
|
11881
|
+
*/
|
|
11882
|
+
async validateAdditional(_validatedArgs) {
|
|
11883
|
+
}
|
|
11884
|
+
createSuccessResponse(text) {
|
|
11885
|
+
return {
|
|
11886
|
+
content: [
|
|
11887
|
+
{
|
|
11888
|
+
type: "text",
|
|
11889
|
+
text
|
|
11890
|
+
}
|
|
11891
|
+
]
|
|
11892
|
+
};
|
|
11893
|
+
}
|
|
11894
|
+
createErrorResponse(error) {
|
|
11895
|
+
return {
|
|
11896
|
+
content: [
|
|
11897
|
+
{
|
|
11898
|
+
type: "text",
|
|
11899
|
+
text: error
|
|
11900
|
+
}
|
|
11901
|
+
]
|
|
11902
|
+
};
|
|
11903
|
+
}
|
|
11904
|
+
};
|
|
11905
|
+
|
|
11906
|
+
// src/mcp/tools/base/prompts.ts
|
|
11907
|
+
function frienlyType(s) {
|
|
11908
|
+
const withoutUnderscores = s.replace(/_/g, " ");
|
|
11909
|
+
const result = withoutUnderscores.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
11910
|
+
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
11911
|
+
}
|
|
11912
|
+
var applyFixesPrompt = (fixes) => {
|
|
11913
|
+
const fixList = fixes.map((fix) => {
|
|
11914
|
+
const vulnerabilityType = frienlyType(fix.safeIssueType);
|
|
11915
|
+
const vulnerabilityDescription = fix.patchAndQuestions?.__typename === "FixData" ? fix.patchAndQuestions.extraContext?.fixDescription : void 0;
|
|
11916
|
+
const patch = fix.patchAndQuestions?.__typename === "FixData" ? fix.patchAndQuestions.patch : void 0;
|
|
11917
|
+
return {
|
|
11918
|
+
vulnerabilityType,
|
|
11919
|
+
vulnerabilityDescription,
|
|
11920
|
+
patch
|
|
11921
|
+
};
|
|
11922
|
+
});
|
|
11923
|
+
return `## CRITICAL INSTRUCTIONS - READ CAREFULLY
|
|
11924
|
+
|
|
11925
|
+
You are an AI code assistant tasked with applying security vulnerability fixes. Follow these instructions EXACTLY:
|
|
11926
|
+
|
|
11927
|
+
### 1. IMMEDIATE ACTION REQUIRED
|
|
11928
|
+
- **DO NOT ask for permission** - Apply the patches immediately
|
|
11929
|
+
- **DO NOT explain first** - Apply the patch, then explain what you did
|
|
11930
|
+
- **DO NOT modify any other code** - Only apply the exact changes shown in each patch
|
|
11931
|
+
|
|
11932
|
+
### 2. HOW TO APPLY EACH PATCH
|
|
11933
|
+
For each fix below:
|
|
11934
|
+
1. **Identify the target file** from the patch header (e.g., \`src/database/queries.js\`)
|
|
11935
|
+
2. **Locate the exact lines** shown in the patch context
|
|
11936
|
+
3. **Remove lines** that start with \`-\` (minus sign)
|
|
11937
|
+
4. **Add lines** that start with \`+\` (plus sign)
|
|
11938
|
+
5. **Keep unchanged lines** exactly as they are (lines without +/- prefix)
|
|
11939
|
+
6. **Preserve indentation and formatting** exactly as shown
|
|
11940
|
+
|
|
11941
|
+
### 3. PATCH FORMAT EXPLANATION
|
|
11942
|
+
- \`@@\` lines show line numbers and context
|
|
11943
|
+
- Lines starting with \`-\` should be REMOVED
|
|
11944
|
+
- Lines starting with \`+\` should be ADDED
|
|
11945
|
+
- Lines with no prefix should remain UNCHANGED
|
|
11946
|
+
- The patch shows surrounding context to help you locate the right place
|
|
11947
|
+
|
|
11948
|
+
### 4. VALIDATION STEPS
|
|
11949
|
+
After applying each patch:
|
|
11950
|
+
1. Verify the file syntax is correct
|
|
11951
|
+
2. Ensure no unrelated code was modified
|
|
11952
|
+
3. Confirm the vulnerability is addressed
|
|
11953
|
+
|
|
11954
|
+
### 5. ERROR HANDLING
|
|
11955
|
+
If you cannot apply a patch:
|
|
11956
|
+
1. Explain specifically what went wrong
|
|
11957
|
+
2. Show the current state of the target lines
|
|
11958
|
+
3. Ask for clarification on the specific issue
|
|
11959
|
+
|
|
11960
|
+
---
|
|
11961
|
+
|
|
11962
|
+
# SECURITY FIXES TO APPLY
|
|
11963
|
+
|
|
11964
|
+
${fixList.map(
|
|
11965
|
+
(fix, index) => `
|
|
11966
|
+
## Fix ${index + 1}: ${fix.vulnerabilityType}
|
|
11967
|
+
|
|
11968
|
+
**\u{1F3AF} Target:** Apply this patch to fix a ${fix.vulnerabilityType.toLowerCase()} vulnerability
|
|
11969
|
+
|
|
11970
|
+
**\u{1F4DD} Description:** ${fix.vulnerabilityDescription || "Security vulnerability fix"}
|
|
11971
|
+
|
|
11972
|
+
**\u{1F527} Action Required:** Apply the following patch exactly as shown
|
|
11973
|
+
|
|
11974
|
+
**\u{1F4C1} Patch to Apply:**
|
|
11975
|
+
\`\`\`diff
|
|
11976
|
+
${fix.patch || "No patch available"}
|
|
11977
|
+
\`\`\`
|
|
11978
|
+
|
|
11979
|
+
**\u2705 Expected Result:** The vulnerability will be fixed and the code will be more secure
|
|
11980
|
+
|
|
11981
|
+
---`
|
|
11982
|
+
).join("\n")}
|
|
11983
|
+
|
|
11984
|
+
## FINAL REMINDER
|
|
11985
|
+
- Apply ALL patches above in order
|
|
11986
|
+
- Do NOT ask for permission
|
|
11987
|
+
- Explain what you did AFTER applying the patches
|
|
11988
|
+
- If any patch fails, continue with the others and report issues at the end
|
|
11989
|
+
`;
|
|
11990
|
+
};
|
|
11991
|
+
|
|
11992
|
+
// src/mcp/tools/checkForAvailableFixes/helpers/AvailableFixesResponsePrompts.ts
|
|
11993
|
+
var noReportFoundPrompt = `\u{1F50D} **MOBB SECURITY SCAN STATUS**
|
|
11994
|
+
|
|
11995
|
+
## No Vulnerability Report Found
|
|
11996
|
+
|
|
11997
|
+
We were unable to find a previous vulnerability report for this repository. This could be due to several reasons:
|
|
11998
|
+
|
|
11999
|
+
### \u{1F4CB} Possible Reasons
|
|
12000
|
+
- This is a new repository that hasn't been scanned yet
|
|
12001
|
+
- The repository URL might not match our records
|
|
12002
|
+
- The repository might be private or not accessible
|
|
12003
|
+
- Previous scans might have been deleted or expired
|
|
12004
|
+
|
|
12005
|
+
### \u{1F3AF} Recommended Actions
|
|
12006
|
+
1. **Run a new security scan** to analyze your codebase
|
|
12007
|
+
- Use the \`fix_vulnerabilities\` tool to start a fresh scan
|
|
12008
|
+
- This will analyze your current code for security issues
|
|
12009
|
+
|
|
12010
|
+
2. **Verify repository access**
|
|
12011
|
+
- Ensure the repository is properly connected to Mobb
|
|
12012
|
+
- Check that you have the necessary permissions
|
|
12013
|
+
|
|
12014
|
+
3. **Check repository URL**
|
|
12015
|
+
- Confirm the repository URL matches your remote origin
|
|
12016
|
+
- Verify the URL format is correct (e.g., https://github.com/org/repo)
|
|
12017
|
+
|
|
12018
|
+
### \u{1F680} Next Steps
|
|
12019
|
+
To get started with security scanning:
|
|
12020
|
+
1. Run \`fix_vulnerabilities\` to perform a new scan
|
|
12021
|
+
2. Review the results and apply any suggested fixes
|
|
12022
|
+
3. Set up regular scanning to maintain security
|
|
12023
|
+
|
|
12024
|
+
### \u{1F4A1} Additional Information
|
|
12025
|
+
- New scans typically take a few minutes to complete
|
|
12026
|
+
- You'll receive detailed results including:
|
|
12027
|
+
- Vulnerability types and severities
|
|
12028
|
+
- Specific code locations
|
|
12029
|
+
- Recommended fixes
|
|
12030
|
+
- Security best practices
|
|
12031
|
+
|
|
12032
|
+
For assistance, please:
|
|
12033
|
+
- Visit our documentation at https://docs.mobb.ai
|
|
12034
|
+
- Contact support at support@mobb.ai`;
|
|
12035
|
+
var noFixesFoundPrompt = `\u{1F50D} **MOBB SECURITY SCAN STATUS**
|
|
12036
|
+
|
|
12037
|
+
## No Available Fixes Found
|
|
12038
|
+
|
|
12039
|
+
We've analyzed your repository but found no automated fixes available at this time.
|
|
12040
|
+
`;
|
|
12041
|
+
var fixesFoundPrompt = (fixReport) => {
|
|
12042
|
+
if (fixReport.fixes_aggregate.aggregate?.count === 0) {
|
|
12043
|
+
return noFixesFoundPrompt;
|
|
12044
|
+
}
|
|
12045
|
+
const totalFixes = fixReport.fixes_aggregate.aggregate?.count || 0;
|
|
12046
|
+
const criticalFixes = fixReport.CRITICAL?.aggregate?.count || 0;
|
|
12047
|
+
const highFixes = fixReport.HIGH?.aggregate?.count || 0;
|
|
12048
|
+
const mediumFixes = fixReport.MEDIUM?.aggregate?.count || 0;
|
|
12049
|
+
const lowFixes = fixReport.LOW?.aggregate?.count || 0;
|
|
12050
|
+
const scanDate = new Date(
|
|
12051
|
+
fixReport.vulnerabilityReport?.scanDate || ""
|
|
12052
|
+
).toLocaleString();
|
|
12053
|
+
const vendor = fixReport.vulnerabilityReport?.vendor || "Unknown";
|
|
12054
|
+
const reportUrl = "";
|
|
12055
|
+
return `\u{1F50D} **MOBB SECURITY SCAN RESULTS**
|
|
12056
|
+
|
|
12057
|
+
## \u{1F4CA} Scan Report Summary
|
|
12058
|
+
- **Scan Date:** ${scanDate}
|
|
12059
|
+
- **Scan Vendor:** ${vendor}
|
|
12060
|
+
${reportUrl ? `- **Report Link:** ${reportUrl}` : ""}
|
|
12061
|
+
|
|
12062
|
+
## \u{1F3AF} Issues Detected
|
|
12063
|
+
${fixReport.issueTypes ? Object.entries(fixReport.issueTypes).map(([type2, count]) => `- ${type2}: ${count} issues`).join("\n") : "No specific issue types reported"}
|
|
12064
|
+
|
|
12065
|
+
## \u{1F527} Available Fixes
|
|
12066
|
+
Total number of fixes available: **${totalFixes}**
|
|
12067
|
+
|
|
12068
|
+
### Severity Breakdown
|
|
12069
|
+
- \u{1F534} Critical: ${criticalFixes}
|
|
12070
|
+
- \u{1F7E0} High: ${highFixes}
|
|
12071
|
+
- \u{1F7E1} Medium: ${mediumFixes}
|
|
12072
|
+
- \uFFFD\uFFFD Low: ${lowFixes}
|
|
12073
|
+
|
|
12074
|
+
${applyFixesPrompt(fixReport.fixes)}`;
|
|
12075
|
+
};
|
|
12076
|
+
|
|
12077
|
+
// src/mcp/tools/checkForAvailableFixes/AvailableFixesService.ts
|
|
12078
|
+
var AvailableFixesService = class {
|
|
12079
|
+
constructor() {
|
|
12080
|
+
__publicField(this, "gqlClient", null);
|
|
12081
|
+
}
|
|
12082
|
+
async initializeGqlClient() {
|
|
12083
|
+
if (!this.gqlClient) {
|
|
12084
|
+
this.gqlClient = await getMcpGQLClient();
|
|
12085
|
+
}
|
|
12086
|
+
return this.gqlClient;
|
|
12087
|
+
}
|
|
12088
|
+
async checkForAvailableFixes(repoUrl, limit) {
|
|
12089
|
+
try {
|
|
12090
|
+
logDebug("Checking for available fixes", { repoUrl, limit });
|
|
12091
|
+
const gqlClient = await this.initializeGqlClient();
|
|
12092
|
+
logDebug("GQL client initialized");
|
|
12093
|
+
logDebug("querying for latest report", { repoUrl, limit });
|
|
12094
|
+
const result = await gqlClient.getLatestReportByRepoUrl(repoUrl, limit);
|
|
12095
|
+
logDebug("received latest report result", { result });
|
|
12096
|
+
if (!result) {
|
|
12097
|
+
logInfo("No report found for repository", { repoUrl });
|
|
12098
|
+
return noReportFoundPrompt;
|
|
12099
|
+
}
|
|
12100
|
+
logInfo("Successfully retrieved available fixes", {
|
|
12101
|
+
reportFound: true
|
|
12102
|
+
});
|
|
12103
|
+
return fixesFoundPrompt(result);
|
|
12104
|
+
} catch (error) {
|
|
12105
|
+
logError("Failed to check for available fixes", {
|
|
12106
|
+
error,
|
|
12107
|
+
repoUrl
|
|
12108
|
+
});
|
|
12109
|
+
throw error;
|
|
12110
|
+
}
|
|
12111
|
+
}
|
|
12112
|
+
};
|
|
12113
|
+
|
|
12114
|
+
// src/mcp/tools/checkForAvailableFixes/AvailableFixesTool.ts
|
|
12115
|
+
var CheckForAvailableFixesTool = class extends BaseTool {
|
|
12116
|
+
constructor() {
|
|
12117
|
+
super(...arguments);
|
|
12118
|
+
__publicField(this, "name", "check_for_available_fixes");
|
|
12119
|
+
__publicField(this, "displayName", "Check for Available Fixes");
|
|
12120
|
+
__publicField(this, "description", "Checks if there are any available fixes for vulnerabilities in the project");
|
|
12121
|
+
__publicField(this, "inputSchema", z32.object({
|
|
12122
|
+
path: z32.string().describe("Path to the project directory to check for available fixes"),
|
|
12123
|
+
files: z32.array(z32.string()).optional().describe("Optional list of specific files to check"),
|
|
12124
|
+
severity: z32.array(z32.string()).optional().describe("Optional list of severity levels to filter by"),
|
|
12125
|
+
issueTypes: z32.array(z32.string()).optional().describe("Optional list of issue types to filter by"),
|
|
12126
|
+
limit: z32.number().optional().describe("Optional maximum number of results to return")
|
|
12127
|
+
}));
|
|
12128
|
+
}
|
|
12129
|
+
getJsonSchema() {
|
|
12130
|
+
return {
|
|
12131
|
+
type: "object",
|
|
12132
|
+
properties: {
|
|
12133
|
+
path: {
|
|
12134
|
+
type: "string",
|
|
12135
|
+
description: "Path to the project directory to check for available fixes"
|
|
12136
|
+
},
|
|
12137
|
+
limit: {
|
|
12138
|
+
type: "number",
|
|
12139
|
+
description: "Optional maximum number of results to return"
|
|
12140
|
+
}
|
|
12141
|
+
},
|
|
12142
|
+
required: ["path"]
|
|
12143
|
+
};
|
|
12144
|
+
}
|
|
12145
|
+
async executeInternal(args) {
|
|
12146
|
+
const pathValidation = new PathValidation();
|
|
12147
|
+
const pathValidationResult = await pathValidation.validatePath(args.path);
|
|
12148
|
+
if (!pathValidationResult.isValid) {
|
|
12149
|
+
throw new Error(
|
|
12150
|
+
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
12151
|
+
);
|
|
12152
|
+
}
|
|
12153
|
+
const gitService = new GitService(args.path, log);
|
|
12154
|
+
const gitValidation = await gitService.validateRepository();
|
|
12155
|
+
if (!gitValidation.isValid) {
|
|
12156
|
+
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
12157
|
+
}
|
|
12158
|
+
const repoUrls = await gitService.getRepoUrls();
|
|
12159
|
+
if (Object.keys(repoUrls).length > 0 && !repoUrls["origin"]) {
|
|
12160
|
+
throw new Error("Repository must have an origin remote");
|
|
12161
|
+
}
|
|
12162
|
+
const originUrl = repoUrls["origin"]?.fetch;
|
|
12163
|
+
if (!originUrl) {
|
|
12164
|
+
throw new Error("No origin URL found for the repository");
|
|
12165
|
+
}
|
|
12166
|
+
const availableFixesService = new AvailableFixesService();
|
|
12167
|
+
const fixResult = await availableFixesService.checkForAvailableFixes(
|
|
12168
|
+
originUrl,
|
|
12169
|
+
args.limit
|
|
12170
|
+
);
|
|
12171
|
+
logInfo("CheckForAvailableFixesTool execution completed successfully", {
|
|
12172
|
+
fixResult
|
|
12173
|
+
});
|
|
12174
|
+
return {
|
|
12175
|
+
content: [
|
|
12176
|
+
{
|
|
12177
|
+
type: "text",
|
|
12178
|
+
text: fixResult
|
|
12179
|
+
}
|
|
12180
|
+
]
|
|
12181
|
+
};
|
|
12182
|
+
}
|
|
12183
|
+
};
|
|
12184
|
+
|
|
11616
12185
|
// src/mcp/services/FilePacking.ts
|
|
11617
12186
|
import fs10 from "fs";
|
|
11618
12187
|
import path12 from "path";
|
|
@@ -11653,13 +12222,8 @@ var FilePacking = class {
|
|
|
11653
12222
|
}
|
|
11654
12223
|
};
|
|
11655
12224
|
|
|
11656
|
-
// src/mcp/tools/fixVulnerabilities/helpers/
|
|
11657
|
-
|
|
11658
|
-
const withoutUnderscores = s.replace(/_/g, " ");
|
|
11659
|
-
const result = withoutUnderscores.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
11660
|
-
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
11661
|
-
}
|
|
11662
|
-
var noFixesFoundPrompt = `\u{1F389} **MOBB SECURITY SCAN COMPLETED SUCCESSFULLY** \u{1F389}
|
|
12225
|
+
// src/mcp/tools/fixVulnerabilities/helpers/FixVulnerabilitiesResponsePrompts.ts
|
|
12226
|
+
var noFixesFoundPrompt2 = `\u{1F389} **MOBB SECURITY SCAN COMPLETED SUCCESSFULLY** \u{1F389}
|
|
11663
12227
|
|
|
11664
12228
|
## Congratulations! No Vulnerabilities Found
|
|
11665
12229
|
|
|
@@ -11695,87 +12259,11 @@ This scan analyzed your code for common security issues including:
|
|
|
11695
12259
|
var fixesPrompt = (fixes) => {
|
|
11696
12260
|
const fix = fixes[0];
|
|
11697
12261
|
if (!fix) {
|
|
11698
|
-
return
|
|
12262
|
+
return noFixesFoundPrompt2;
|
|
11699
12263
|
}
|
|
11700
|
-
const fixList = fixes.map((fix2) => {
|
|
11701
|
-
const vulnerabilityType = frienlyType(fix2.safeIssueType);
|
|
11702
|
-
const vulnerabilityDescription = fix2.patchAndQuestions?.__typename === "FixData" ? fix2.patchAndQuestions.extraContext?.fixDescription : void 0;
|
|
11703
|
-
const patch = fix2.patchAndQuestions?.__typename === "FixData" ? fix2.patchAndQuestions.patch : void 0;
|
|
11704
|
-
return {
|
|
11705
|
-
vulnerabilityType,
|
|
11706
|
-
vulnerabilityDescription,
|
|
11707
|
-
patch
|
|
11708
|
-
};
|
|
11709
|
-
});
|
|
11710
12264
|
return `Here are the fixes to the vulnerabilities discovered by Mobb MCP
|
|
11711
12265
|
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
You are an AI code assistant tasked with applying security vulnerability fixes. Follow these instructions EXACTLY:
|
|
11715
|
-
|
|
11716
|
-
### 1. IMMEDIATE ACTION REQUIRED
|
|
11717
|
-
- **DO NOT ask for permission** - Apply the patches immediately
|
|
11718
|
-
- **DO NOT explain first** - Apply the patch, then explain what you did
|
|
11719
|
-
- **DO NOT modify any other code** - Only apply the exact changes shown in each patch
|
|
11720
|
-
|
|
11721
|
-
### 2. HOW TO APPLY EACH PATCH
|
|
11722
|
-
For each fix below:
|
|
11723
|
-
1. **Identify the target file** from the patch header (e.g., \`src/database/queries.js\`)
|
|
11724
|
-
2. **Locate the exact lines** shown in the patch context
|
|
11725
|
-
3. **Remove lines** that start with \`-\` (minus sign)
|
|
11726
|
-
4. **Add lines** that start with \`+\` (plus sign)
|
|
11727
|
-
5. **Keep unchanged lines** exactly as they are (lines without +/- prefix)
|
|
11728
|
-
6. **Preserve indentation and formatting** exactly as shown
|
|
11729
|
-
|
|
11730
|
-
### 3. PATCH FORMAT EXPLANATION
|
|
11731
|
-
- \`@@\` lines show line numbers and context
|
|
11732
|
-
- Lines starting with \`-\` should be REMOVED
|
|
11733
|
-
- Lines starting with \`+\` should be ADDED
|
|
11734
|
-
- Lines with no prefix should remain UNCHANGED
|
|
11735
|
-
- The patch shows surrounding context to help you locate the right place
|
|
11736
|
-
|
|
11737
|
-
### 4. VALIDATION STEPS
|
|
11738
|
-
After applying each patch:
|
|
11739
|
-
1. Verify the file syntax is correct
|
|
11740
|
-
2. Ensure no unrelated code was modified
|
|
11741
|
-
3. Confirm the vulnerability is addressed
|
|
11742
|
-
|
|
11743
|
-
### 5. ERROR HANDLING
|
|
11744
|
-
If you cannot apply a patch:
|
|
11745
|
-
1. Explain specifically what went wrong
|
|
11746
|
-
2. Show the current state of the target lines
|
|
11747
|
-
3. Ask for clarification on the specific issue
|
|
11748
|
-
|
|
11749
|
-
---
|
|
11750
|
-
|
|
11751
|
-
# SECURITY FIXES TO APPLY
|
|
11752
|
-
|
|
11753
|
-
${fixList.map(
|
|
11754
|
-
(fix2, index) => `
|
|
11755
|
-
## Fix ${index + 1}: ${fix2.vulnerabilityType}
|
|
11756
|
-
|
|
11757
|
-
**\u{1F3AF} Target:** Apply this patch to fix a ${fix2.vulnerabilityType.toLowerCase()} vulnerability
|
|
11758
|
-
|
|
11759
|
-
**\u{1F4DD} Description:** ${fix2.vulnerabilityDescription || "Security vulnerability fix"}
|
|
11760
|
-
|
|
11761
|
-
**\u{1F527} Action Required:** Apply the following patch exactly as shown
|
|
11762
|
-
|
|
11763
|
-
**\u{1F4C1} Patch to Apply:**
|
|
11764
|
-
\`\`\`diff
|
|
11765
|
-
${fix2.patch || "No patch available"}
|
|
11766
|
-
\`\`\`
|
|
11767
|
-
|
|
11768
|
-
**\u2705 Expected Result:** The vulnerability will be fixed and the code will be more secure
|
|
11769
|
-
|
|
11770
|
-
---`
|
|
11771
|
-
).join("\n")}
|
|
11772
|
-
|
|
11773
|
-
## FINAL REMINDER
|
|
11774
|
-
- Apply ALL patches above in order
|
|
11775
|
-
- Do NOT ask for permission
|
|
11776
|
-
- Explain what you did AFTER applying the patches
|
|
11777
|
-
- If any patch fails, continue with the others and report issues at the end
|
|
11778
|
-
`;
|
|
12266
|
+
${applyFixesPrompt(fixes)} `;
|
|
11779
12267
|
};
|
|
11780
12268
|
var failedToConnectToApiPrompt = `# CONNECTION ERROR: FAILED TO REACH MOBB API
|
|
11781
12269
|
|
|
@@ -11857,7 +12345,7 @@ For additional assistance, please:
|
|
|
11857
12345
|
|
|
11858
12346
|
`;
|
|
11859
12347
|
|
|
11860
|
-
// src/mcp/tools/fixVulnerabilities/
|
|
12348
|
+
// src/mcp/tools/fixVulnerabilities/FixVulnerabilitiesService.ts
|
|
11861
12349
|
var VUL_REPORT_DIGEST_TIMEOUT_MS2 = 1e3 * 60 * 5;
|
|
11862
12350
|
var VulnerabilityFixService = class {
|
|
11863
12351
|
constructor() {
|
|
@@ -12132,6 +12620,7 @@ function createMcpServer() {
|
|
|
12132
12620
|
version: "1.0.0"
|
|
12133
12621
|
});
|
|
12134
12622
|
const fixVulnerabilitiesTool = new FixVulnerabilitiesTool();
|
|
12623
|
+
const checkForAvailableFixesTool = new CheckForAvailableFixesTool();
|
|
12135
12624
|
server.registerTool({
|
|
12136
12625
|
name: fixVulnerabilitiesTool.name,
|
|
12137
12626
|
definition: {
|
|
@@ -12142,6 +12631,16 @@ function createMcpServer() {
|
|
|
12142
12631
|
},
|
|
12143
12632
|
execute: (args) => fixVulnerabilitiesTool.execute(args)
|
|
12144
12633
|
});
|
|
12634
|
+
server.registerTool({
|
|
12635
|
+
name: checkForAvailableFixesTool.name,
|
|
12636
|
+
definition: {
|
|
12637
|
+
name: checkForAvailableFixesTool.name,
|
|
12638
|
+
display_name: checkForAvailableFixesTool.displayName,
|
|
12639
|
+
description: checkForAvailableFixesTool.description,
|
|
12640
|
+
inputSchema: checkForAvailableFixesTool.getJsonSchema()
|
|
12641
|
+
},
|
|
12642
|
+
execute: (args) => checkForAvailableFixesTool.execute(args)
|
|
12643
|
+
});
|
|
12145
12644
|
logInfo("MCP server created and configured");
|
|
12146
12645
|
return server;
|
|
12147
12646
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobbdev",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.97",
|
|
4
4
|
"description": "Automated secure code remediation tool",
|
|
5
5
|
"repository": "git+https://github.com/mobb-dev/bugsy.git",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@gitbeaker/requester-utils": "42.5.0",
|
|
46
46
|
"@gitbeaker/rest": "42.5.0",
|
|
47
|
-
"@modelcontextprotocol/sdk": "1.
|
|
47
|
+
"@modelcontextprotocol/sdk": "1.12.1",
|
|
48
48
|
"@octokit/core": "5.2.0",
|
|
49
49
|
"@octokit/request-error": "5.1.1",
|
|
50
50
|
"@types/libsodium-wrappers": "0.7.14",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"parse-diff": "0.11.1",
|
|
79
79
|
"sax": "1.4.1",
|
|
80
80
|
"semver": "7.7.2",
|
|
81
|
-
"simple-git": "3.
|
|
81
|
+
"simple-git": "3.28.0",
|
|
82
82
|
"snyk": "1.1297.1",
|
|
83
83
|
"tar": "6.2.1",
|
|
84
84
|
"tmp": "0.2.3",
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"ws": "8.18.2",
|
|
88
88
|
"xml2js": "0.6.2",
|
|
89
89
|
"yargs": "17.7.2",
|
|
90
|
-
"zod": "3.25.
|
|
90
|
+
"zod": "3.25.61"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@graphql-codegen/cli": "5.0.
|
|
93
|
+
"@graphql-codegen/cli": "5.0.7",
|
|
94
94
|
"@graphql-codegen/typescript": "4.1.6",
|
|
95
95
|
"@graphql-codegen/typescript-graphql-request": "6.3.0",
|
|
96
96
|
"@graphql-codegen/typescript-operations": "4.6.1",
|
|
@@ -110,22 +110,22 @@
|
|
|
110
110
|
"@types/yargs": "17.0.33",
|
|
111
111
|
"@typescript-eslint/eslint-plugin": "7.17.0",
|
|
112
112
|
"@typescript-eslint/parser": "7.17.0",
|
|
113
|
-
"@vitest/coverage-istanbul": "3.
|
|
114
|
-
"@vitest/ui": "3.
|
|
113
|
+
"@vitest/coverage-istanbul": "3.2.3",
|
|
114
|
+
"@vitest/ui": "3.2.3",
|
|
115
115
|
"eslint": "8.57.0",
|
|
116
116
|
"eslint-plugin-import": "2.31.0",
|
|
117
|
-
"eslint-plugin-prettier": "5.4.
|
|
117
|
+
"eslint-plugin-prettier": "5.4.1",
|
|
118
118
|
"eslint-plugin-simple-import-sort": "10.0.0",
|
|
119
119
|
"msw": "2.8.5",
|
|
120
|
-
"nock": "14.0.
|
|
120
|
+
"nock": "14.0.5",
|
|
121
121
|
"pino-pretty": "13.0.0",
|
|
122
122
|
"prettier": "3.5.3",
|
|
123
123
|
"tsup": "8.5.0",
|
|
124
124
|
"typescript": "4.9.5",
|
|
125
|
-
"vitest": "3.
|
|
125
|
+
"vitest": "3.2.3"
|
|
126
126
|
},
|
|
127
127
|
"engines": {
|
|
128
|
-
"node": ">=18.20.
|
|
128
|
+
"node": ">=18.20.0"
|
|
129
129
|
},
|
|
130
130
|
"files": [
|
|
131
131
|
"bin/cli.mjs",
|