mobbdev 1.4.11 → 1.4.12
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.
|
@@ -56,17 +56,17 @@ declare const PromptItemZ: z.ZodObject<{
|
|
|
56
56
|
name: string;
|
|
57
57
|
parameters: string;
|
|
58
58
|
result: string;
|
|
59
|
+
mcpServer?: string | undefined;
|
|
59
60
|
accepted?: boolean | undefined;
|
|
60
61
|
rawArguments?: string | undefined;
|
|
61
|
-
mcpServer?: string | undefined;
|
|
62
62
|
mcpToolName?: string | undefined;
|
|
63
63
|
}, {
|
|
64
64
|
name: string;
|
|
65
65
|
parameters: string;
|
|
66
66
|
result: string;
|
|
67
|
+
mcpServer?: string | undefined;
|
|
67
68
|
accepted?: boolean | undefined;
|
|
68
69
|
rawArguments?: string | undefined;
|
|
69
|
-
mcpServer?: string | undefined;
|
|
70
70
|
mcpToolName?: string | undefined;
|
|
71
71
|
}>>;
|
|
72
72
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -75,9 +75,9 @@ declare const PromptItemZ: z.ZodObject<{
|
|
|
75
75
|
name: string;
|
|
76
76
|
parameters: string;
|
|
77
77
|
result: string;
|
|
78
|
+
mcpServer?: string | undefined;
|
|
78
79
|
accepted?: boolean | undefined;
|
|
79
80
|
rawArguments?: string | undefined;
|
|
80
|
-
mcpServer?: string | undefined;
|
|
81
81
|
mcpToolName?: string | undefined;
|
|
82
82
|
} | undefined;
|
|
83
83
|
date?: Date | undefined;
|
|
@@ -96,9 +96,9 @@ declare const PromptItemZ: z.ZodObject<{
|
|
|
96
96
|
name: string;
|
|
97
97
|
parameters: string;
|
|
98
98
|
result: string;
|
|
99
|
+
mcpServer?: string | undefined;
|
|
99
100
|
accepted?: boolean | undefined;
|
|
100
101
|
rawArguments?: string | undefined;
|
|
101
|
-
mcpServer?: string | undefined;
|
|
102
102
|
mcpToolName?: string | undefined;
|
|
103
103
|
} | undefined;
|
|
104
104
|
date?: Date | undefined;
|
|
@@ -149,17 +149,17 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
|
149
149
|
name: string;
|
|
150
150
|
parameters: string;
|
|
151
151
|
result: string;
|
|
152
|
+
mcpServer?: string | undefined;
|
|
152
153
|
accepted?: boolean | undefined;
|
|
153
154
|
rawArguments?: string | undefined;
|
|
154
|
-
mcpServer?: string | undefined;
|
|
155
155
|
mcpToolName?: string | undefined;
|
|
156
156
|
}, {
|
|
157
157
|
name: string;
|
|
158
158
|
parameters: string;
|
|
159
159
|
result: string;
|
|
160
|
+
mcpServer?: string | undefined;
|
|
160
161
|
accepted?: boolean | undefined;
|
|
161
162
|
rawArguments?: string | undefined;
|
|
162
|
-
mcpServer?: string | undefined;
|
|
163
163
|
mcpToolName?: string | undefined;
|
|
164
164
|
}>>;
|
|
165
165
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -168,9 +168,9 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
|
168
168
|
name: string;
|
|
169
169
|
parameters: string;
|
|
170
170
|
result: string;
|
|
171
|
+
mcpServer?: string | undefined;
|
|
171
172
|
accepted?: boolean | undefined;
|
|
172
173
|
rawArguments?: string | undefined;
|
|
173
|
-
mcpServer?: string | undefined;
|
|
174
174
|
mcpToolName?: string | undefined;
|
|
175
175
|
} | undefined;
|
|
176
176
|
date?: Date | undefined;
|
|
@@ -189,9 +189,9 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
|
189
189
|
name: string;
|
|
190
190
|
parameters: string;
|
|
191
191
|
result: string;
|
|
192
|
+
mcpServer?: string | undefined;
|
|
192
193
|
accepted?: boolean | undefined;
|
|
193
194
|
rawArguments?: string | undefined;
|
|
194
|
-
mcpServer?: string | undefined;
|
|
195
195
|
mcpToolName?: string | undefined;
|
|
196
196
|
} | undefined;
|
|
197
197
|
date?: Date | undefined;
|
|
@@ -109,6 +109,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
109
109
|
autoPrAnalysis(variables, requestHeaders, signal) {
|
|
110
110
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: AutoPrAnalysisDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "autoPrAnalysis", "mutation", variables);
|
|
111
111
|
},
|
|
112
|
+
getFixWithAnswers(variables, requestHeaders, signal) {
|
|
113
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetFixWithAnswersDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getFixWithAnswers", "query", variables);
|
|
114
|
+
},
|
|
112
115
|
GetFixReportsByRepoUrl(variables, requestHeaders, signal) {
|
|
113
116
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetFixReportsByRepoUrlDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetFixReportsByRepoUrl", "query", variables);
|
|
114
117
|
},
|
|
@@ -138,7 +141,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
138
141
|
}
|
|
139
142
|
};
|
|
140
143
|
}
|
|
141
|
-
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
|
|
144
|
+
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixWithAnswersDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
|
|
142
145
|
var init_client_generates = __esm({
|
|
143
146
|
"src/features/analysis/scm/generates/client_generates.ts"() {
|
|
144
147
|
"use strict";
|
|
@@ -312,6 +315,7 @@ var init_client_generates = __esm({
|
|
|
312
315
|
IssueType_Enum2["NoReturnInFinally"] = "NO_RETURN_IN_FINALLY";
|
|
313
316
|
IssueType_Enum2["NoVar"] = "NO_VAR";
|
|
314
317
|
IssueType_Enum2["NullDereference"] = "NULL_DEREFERENCE";
|
|
318
|
+
IssueType_Enum2["OftenMisusedBooleanGetBoolean"] = "OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN";
|
|
315
319
|
IssueType_Enum2["OpenRedirect"] = "OPEN_REDIRECT";
|
|
316
320
|
IssueType_Enum2["OverlyBroadCatch"] = "OVERLY_BROAD_CATCH";
|
|
317
321
|
IssueType_Enum2["OverlyLargeRange"] = "OVERLY_LARGE_RANGE";
|
|
@@ -442,6 +446,7 @@ var init_client_generates = __esm({
|
|
|
442
446
|
id
|
|
443
447
|
confidence
|
|
444
448
|
safeIssueType
|
|
449
|
+
safeIssueLanguage
|
|
445
450
|
severityText
|
|
446
451
|
gitBlameLogin
|
|
447
452
|
severityValue
|
|
@@ -465,7 +470,17 @@ var init_client_generates = __esm({
|
|
|
465
470
|
patch
|
|
466
471
|
patchOriginalEncodingBase64
|
|
467
472
|
questions {
|
|
473
|
+
key
|
|
468
474
|
name
|
|
475
|
+
defaultValue
|
|
476
|
+
value
|
|
477
|
+
inputType
|
|
478
|
+
options
|
|
479
|
+
index
|
|
480
|
+
extraContext {
|
|
481
|
+
key
|
|
482
|
+
value
|
|
483
|
+
}
|
|
469
484
|
}
|
|
470
485
|
extraContext {
|
|
471
486
|
extraContext {
|
|
@@ -1182,6 +1197,37 @@ var init_client_generates = __esm({
|
|
|
1182
1197
|
error
|
|
1183
1198
|
}
|
|
1184
1199
|
}
|
|
1200
|
+
}
|
|
1201
|
+
`;
|
|
1202
|
+
GetFixWithAnswersDocument = `
|
|
1203
|
+
query getFixWithAnswers($fixId: uuid!, $userInput: [QuestionAnswer!]!) {
|
|
1204
|
+
fixData: getFix(fixId: $fixId, userInput: $userInput, loadAnswers: false) {
|
|
1205
|
+
__typename
|
|
1206
|
+
... on FixData {
|
|
1207
|
+
patch
|
|
1208
|
+
patchOriginalEncodingBase64
|
|
1209
|
+
questions {
|
|
1210
|
+
key
|
|
1211
|
+
name
|
|
1212
|
+
defaultValue
|
|
1213
|
+
value
|
|
1214
|
+
inputType
|
|
1215
|
+
options
|
|
1216
|
+
index
|
|
1217
|
+
extraContext {
|
|
1218
|
+
key
|
|
1219
|
+
value
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
extraContext {
|
|
1223
|
+
extraContext {
|
|
1224
|
+
key
|
|
1225
|
+
value
|
|
1226
|
+
}
|
|
1227
|
+
fixDescription
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1185
1231
|
}
|
|
1186
1232
|
`;
|
|
1187
1233
|
GetFixReportsByRepoUrlDocument = `
|
|
@@ -1830,7 +1876,8 @@ var init_getIssueType = __esm({
|
|
|
1830
1876
|
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
|
|
1831
1877
|
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
|
|
1832
1878
|
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion",
|
|
1833
|
-
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation"
|
|
1879
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation",
|
|
1880
|
+
["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: "Often Misused: Boolean.getBoolean()"
|
|
1834
1881
|
};
|
|
1835
1882
|
issueTypeZ = z5.nativeEnum(IssueType_Enum);
|
|
1836
1883
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -5037,7 +5084,8 @@ var fixDetailsData = {
|
|
|
5037
5084
|
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
|
|
5038
5085
|
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
|
|
5039
5086
|
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
|
|
5040
|
-
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0
|
|
5087
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0,
|
|
5088
|
+
["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: void 0
|
|
5041
5089
|
};
|
|
5042
5090
|
|
|
5043
5091
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -7437,8 +7485,16 @@ var ADO_PAT_PATTERN = {
|
|
|
7437
7485
|
severity: "high",
|
|
7438
7486
|
validator: (match) => match.length >= 52 && match.length <= 100
|
|
7439
7487
|
};
|
|
7488
|
+
var DATADOG_APP_KEY_PATTERN = {
|
|
7489
|
+
type: "DATADOG_APP_KEY",
|
|
7490
|
+
regex: /\bddapp_[a-zA-Z0-9]{30,}\b/g,
|
|
7491
|
+
priority: 95,
|
|
7492
|
+
placeholder: "[DATADOG_APP_KEY_{n}]",
|
|
7493
|
+
description: "Datadog Application Key",
|
|
7494
|
+
severity: "high"
|
|
7495
|
+
};
|
|
7440
7496
|
var openRedaction = new OpenRedaction({
|
|
7441
|
-
customPatterns: [ADO_PAT_PATTERN],
|
|
7497
|
+
customPatterns: [ADO_PAT_PATTERN, DATADOG_APP_KEY_PATTERN],
|
|
7442
7498
|
patterns: [
|
|
7443
7499
|
// Core Personal Data
|
|
7444
7500
|
// Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
|
package/dist/index.mjs
CHANGED
|
@@ -109,6 +109,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
109
109
|
autoPrAnalysis(variables, requestHeaders, signal) {
|
|
110
110
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: AutoPrAnalysisDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "autoPrAnalysis", "mutation", variables);
|
|
111
111
|
},
|
|
112
|
+
getFixWithAnswers(variables, requestHeaders, signal) {
|
|
113
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetFixWithAnswersDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getFixWithAnswers", "query", variables);
|
|
114
|
+
},
|
|
112
115
|
GetFixReportsByRepoUrl(variables, requestHeaders, signal) {
|
|
113
116
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetFixReportsByRepoUrlDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetFixReportsByRepoUrl", "query", variables);
|
|
114
117
|
},
|
|
@@ -138,7 +141,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
138
141
|
}
|
|
139
142
|
};
|
|
140
143
|
}
|
|
141
|
-
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
|
|
144
|
+
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixWithAnswersDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
|
|
142
145
|
var init_client_generates = __esm({
|
|
143
146
|
"src/features/analysis/scm/generates/client_generates.ts"() {
|
|
144
147
|
"use strict";
|
|
@@ -312,6 +315,7 @@ var init_client_generates = __esm({
|
|
|
312
315
|
IssueType_Enum2["NoReturnInFinally"] = "NO_RETURN_IN_FINALLY";
|
|
313
316
|
IssueType_Enum2["NoVar"] = "NO_VAR";
|
|
314
317
|
IssueType_Enum2["NullDereference"] = "NULL_DEREFERENCE";
|
|
318
|
+
IssueType_Enum2["OftenMisusedBooleanGetBoolean"] = "OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN";
|
|
315
319
|
IssueType_Enum2["OpenRedirect"] = "OPEN_REDIRECT";
|
|
316
320
|
IssueType_Enum2["OverlyBroadCatch"] = "OVERLY_BROAD_CATCH";
|
|
317
321
|
IssueType_Enum2["OverlyLargeRange"] = "OVERLY_LARGE_RANGE";
|
|
@@ -442,6 +446,7 @@ var init_client_generates = __esm({
|
|
|
442
446
|
id
|
|
443
447
|
confidence
|
|
444
448
|
safeIssueType
|
|
449
|
+
safeIssueLanguage
|
|
445
450
|
severityText
|
|
446
451
|
gitBlameLogin
|
|
447
452
|
severityValue
|
|
@@ -465,7 +470,17 @@ var init_client_generates = __esm({
|
|
|
465
470
|
patch
|
|
466
471
|
patchOriginalEncodingBase64
|
|
467
472
|
questions {
|
|
473
|
+
key
|
|
468
474
|
name
|
|
475
|
+
defaultValue
|
|
476
|
+
value
|
|
477
|
+
inputType
|
|
478
|
+
options
|
|
479
|
+
index
|
|
480
|
+
extraContext {
|
|
481
|
+
key
|
|
482
|
+
value
|
|
483
|
+
}
|
|
469
484
|
}
|
|
470
485
|
extraContext {
|
|
471
486
|
extraContext {
|
|
@@ -1182,6 +1197,37 @@ var init_client_generates = __esm({
|
|
|
1182
1197
|
error
|
|
1183
1198
|
}
|
|
1184
1199
|
}
|
|
1200
|
+
}
|
|
1201
|
+
`;
|
|
1202
|
+
GetFixWithAnswersDocument = `
|
|
1203
|
+
query getFixWithAnswers($fixId: uuid!, $userInput: [QuestionAnswer!]!) {
|
|
1204
|
+
fixData: getFix(fixId: $fixId, userInput: $userInput, loadAnswers: false) {
|
|
1205
|
+
__typename
|
|
1206
|
+
... on FixData {
|
|
1207
|
+
patch
|
|
1208
|
+
patchOriginalEncodingBase64
|
|
1209
|
+
questions {
|
|
1210
|
+
key
|
|
1211
|
+
name
|
|
1212
|
+
defaultValue
|
|
1213
|
+
value
|
|
1214
|
+
inputType
|
|
1215
|
+
options
|
|
1216
|
+
index
|
|
1217
|
+
extraContext {
|
|
1218
|
+
key
|
|
1219
|
+
value
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
extraContext {
|
|
1223
|
+
extraContext {
|
|
1224
|
+
key
|
|
1225
|
+
value
|
|
1226
|
+
}
|
|
1227
|
+
fixDescription
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1185
1231
|
}
|
|
1186
1232
|
`;
|
|
1187
1233
|
GetFixReportsByRepoUrlDocument = `
|
|
@@ -1502,7 +1548,8 @@ var init_getIssueType = __esm({
|
|
|
1502
1548
|
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
|
|
1503
1549
|
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
|
|
1504
1550
|
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion",
|
|
1505
|
-
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation"
|
|
1551
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation",
|
|
1552
|
+
["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: "Often Misused: Boolean.getBoolean()"
|
|
1506
1553
|
};
|
|
1507
1554
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1508
1555
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -4753,7 +4800,8 @@ var fixDetailsData = {
|
|
|
4753
4800
|
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
|
|
4754
4801
|
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
|
|
4755
4802
|
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
|
|
4756
|
-
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0
|
|
4803
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0,
|
|
4804
|
+
["OFTEN_MISUSED_BOOLEAN_GET_BOOLEAN" /* OftenMisusedBooleanGetBoolean */]: void 0
|
|
4757
4805
|
};
|
|
4758
4806
|
|
|
4759
4807
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -14106,8 +14154,16 @@ var ADO_PAT_PATTERN = {
|
|
|
14106
14154
|
severity: "high",
|
|
14107
14155
|
validator: (match) => match.length >= 52 && match.length <= 100
|
|
14108
14156
|
};
|
|
14157
|
+
var DATADOG_APP_KEY_PATTERN = {
|
|
14158
|
+
type: "DATADOG_APP_KEY",
|
|
14159
|
+
regex: /\bddapp_[a-zA-Z0-9]{30,}\b/g,
|
|
14160
|
+
priority: 95,
|
|
14161
|
+
placeholder: "[DATADOG_APP_KEY_{n}]",
|
|
14162
|
+
description: "Datadog Application Key",
|
|
14163
|
+
severity: "high"
|
|
14164
|
+
};
|
|
14109
14165
|
var openRedaction = new OpenRedaction({
|
|
14110
|
-
customPatterns: [ADO_PAT_PATTERN],
|
|
14166
|
+
customPatterns: [ADO_PAT_PATTERN, DATADOG_APP_KEY_PATTERN],
|
|
14111
14167
|
patterns: [
|
|
14112
14168
|
// Core Personal Data
|
|
14113
14169
|
// Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
|
|
@@ -19313,7 +19369,7 @@ function createLogger(config2) {
|
|
|
19313
19369
|
|
|
19314
19370
|
// src/features/claude_code/hook_logger.ts
|
|
19315
19371
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
19316
|
-
var CLI_VERSION = true ? "1.4.
|
|
19372
|
+
var CLI_VERSION = true ? "1.4.12" : "unknown";
|
|
19317
19373
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
19318
19374
|
var claudeCodeVersion;
|
|
19319
19375
|
function buildDdTags() {
|
|
@@ -20801,7 +20857,14 @@ var FixExtraContextResponseSchema = z33.object({
|
|
|
20801
20857
|
});
|
|
20802
20858
|
var FixQuestionSchema = z33.object({
|
|
20803
20859
|
__typename: z33.literal("FixQuestion").optional(),
|
|
20804
|
-
|
|
20860
|
+
key: z33.string(),
|
|
20861
|
+
name: z33.string(),
|
|
20862
|
+
defaultValue: z33.string(),
|
|
20863
|
+
value: z33.string().nullable().optional(),
|
|
20864
|
+
inputType: z33.nativeEnum(FixQuestionInputType),
|
|
20865
|
+
options: z33.array(z33.string()),
|
|
20866
|
+
index: z33.number(),
|
|
20867
|
+
extraContext: z33.array(UnstructuredFixExtraContextSchema)
|
|
20805
20868
|
});
|
|
20806
20869
|
var FixDataSchema = z33.object({
|
|
20807
20870
|
__typename: z33.literal("FixData"),
|
|
@@ -20820,6 +20883,7 @@ var McpFixSchema = z33.object({
|
|
|
20820
20883
|
// GraphQL uses `any` type for UUID
|
|
20821
20884
|
confidence: z33.number(),
|
|
20822
20885
|
safeIssueType: z33.string().nullable(),
|
|
20886
|
+
safeIssueLanguage: z33.string().nullable().optional(),
|
|
20823
20887
|
severityText: z33.string().nullable(),
|
|
20824
20888
|
gitBlameLogin: z33.string().nullable().optional(),
|
|
20825
20889
|
// Optional in GraphQL
|
|
@@ -20900,10 +20964,6 @@ var GetLatestReportByRepoUrlResponseSchema = z33.object({
|
|
|
20900
20964
|
});
|
|
20901
20965
|
|
|
20902
20966
|
// src/mcp/services/InteractiveFixFilter.ts
|
|
20903
|
-
var isFilterDisabled = () => {
|
|
20904
|
-
const raw = process.env["MOBB_MCP_DISABLE_INTERACTIVE_FILTER"];
|
|
20905
|
-
return raw === "1" || raw === "true";
|
|
20906
|
-
};
|
|
20907
20967
|
var isInteractiveFix = (fix) => {
|
|
20908
20968
|
if (fix.patchAndQuestions.__typename !== "FixData") {
|
|
20909
20969
|
return false;
|
|
@@ -20918,27 +20978,46 @@ var countByRule = (ruleIds) => {
|
|
|
20918
20978
|
}
|
|
20919
20979
|
return counts;
|
|
20920
20980
|
};
|
|
20981
|
+
var MOBB_MCP_DISABLE_INTERACTIVE_FILTER_DEFAULT = false;
|
|
20982
|
+
var isInteractiveRoutingDisabled = () => {
|
|
20983
|
+
const raw = process.env["MOBB_MCP_DISABLE_INTERACTIVE_FILTER"];
|
|
20984
|
+
if (!raw) return MOBB_MCP_DISABLE_INTERACTIVE_FILTER_DEFAULT;
|
|
20985
|
+
const normalized = raw.toLowerCase();
|
|
20986
|
+
return normalized === "1" || normalized === "true";
|
|
20987
|
+
};
|
|
20921
20988
|
var partitionInteractiveFixes = (fixes) => {
|
|
20922
|
-
|
|
20923
|
-
return { applicableFixes: fixes, skippedRuleIds: [] };
|
|
20924
|
-
}
|
|
20989
|
+
const disabled = isInteractiveRoutingDisabled();
|
|
20925
20990
|
const applicableFixes = [];
|
|
20926
|
-
const
|
|
20991
|
+
const interactiveFixes = [];
|
|
20992
|
+
const droppedInteractive = [];
|
|
20927
20993
|
for (const fix of fixes) {
|
|
20928
20994
|
if (isInteractiveFix(fix)) {
|
|
20929
|
-
|
|
20995
|
+
if (disabled) {
|
|
20996
|
+
droppedInteractive.push(fix);
|
|
20997
|
+
} else {
|
|
20998
|
+
interactiveFixes.push(fix);
|
|
20999
|
+
}
|
|
20930
21000
|
} else {
|
|
20931
21001
|
applicableFixes.push(fix);
|
|
20932
21002
|
}
|
|
20933
21003
|
}
|
|
20934
|
-
if (
|
|
20935
|
-
logInfo(
|
|
21004
|
+
if (disabled && droppedInteractive.length > 0) {
|
|
21005
|
+
logInfo(
|
|
21006
|
+
"[InteractiveFixFilter] Dropping interactive fixes (MOBB_MCP_DISABLE_INTERACTIVE_FILTER=true)",
|
|
21007
|
+
{
|
|
21008
|
+
totalFixes: fixes.length,
|
|
21009
|
+
droppedCount: droppedInteractive.length,
|
|
21010
|
+
droppedByRule: countByRule(droppedInteractive.map(ruleIdFor))
|
|
21011
|
+
}
|
|
21012
|
+
);
|
|
21013
|
+
} else if (interactiveFixes.length > 0) {
|
|
21014
|
+
logInfo("[InteractiveFixFilter] Routing interactive fixes to LLM", {
|
|
20936
21015
|
totalFixes: fixes.length,
|
|
20937
|
-
|
|
20938
|
-
|
|
21016
|
+
interactiveCount: interactiveFixes.length,
|
|
21017
|
+
interactiveByRule: countByRule(interactiveFixes.map(ruleIdFor))
|
|
20939
21018
|
});
|
|
20940
21019
|
}
|
|
20941
|
-
return { applicableFixes,
|
|
21020
|
+
return { applicableFixes, interactiveFixes };
|
|
20942
21021
|
};
|
|
20943
21022
|
|
|
20944
21023
|
// src/mcp/services/McpGQLClient.ts
|
|
@@ -21223,7 +21302,7 @@ var McpGQLClient = class extends GQLClient {
|
|
|
21223
21302
|
reportData,
|
|
21224
21303
|
limit
|
|
21225
21304
|
}) {
|
|
21226
|
-
if (!reportData) return { applicableFixes: [],
|
|
21305
|
+
if (!reportData) return { applicableFixes: [], interactiveFixes: [] };
|
|
21227
21306
|
const reportMetadata = {
|
|
21228
21307
|
id: reportData.id,
|
|
21229
21308
|
organizationId: reportData.vulnerabilityReport?.project?.organizationId,
|
|
@@ -21260,10 +21339,10 @@ var McpGQLClient = class extends GQLClient {
|
|
|
21260
21339
|
}
|
|
21261
21340
|
}
|
|
21262
21341
|
const merged = Array.from(fixMap.values());
|
|
21263
|
-
const { applicableFixes,
|
|
21342
|
+
const { applicableFixes, interactiveFixes } = partitionInteractiveFixes(merged);
|
|
21264
21343
|
return {
|
|
21265
21344
|
applicableFixes: applicableFixes.slice(0, limit),
|
|
21266
|
-
|
|
21345
|
+
interactiveFixes: interactiveFixes.slice(0, limit)
|
|
21267
21346
|
};
|
|
21268
21347
|
}
|
|
21269
21348
|
async updateFixesDownloadStatus(fixIds) {
|
|
@@ -21368,7 +21447,7 @@ var McpGQLClient = class extends GQLClient {
|
|
|
21368
21447
|
reportCount: resp.fixReport?.length || 0
|
|
21369
21448
|
});
|
|
21370
21449
|
const latestReport = resp.fixReport?.[0] && FixReportSummarySchema.parse(resp.fixReport?.[0]);
|
|
21371
|
-
const { applicableFixes,
|
|
21450
|
+
const { applicableFixes, interactiveFixes } = this.mergeUserAndSystemFixes({
|
|
21372
21451
|
reportData: latestReport,
|
|
21373
21452
|
limit
|
|
21374
21453
|
});
|
|
@@ -21376,7 +21455,7 @@ var McpGQLClient = class extends GQLClient {
|
|
|
21376
21455
|
fixReport: latestReport ? {
|
|
21377
21456
|
...latestReport,
|
|
21378
21457
|
fixes: applicableFixes,
|
|
21379
|
-
|
|
21458
|
+
interactiveFixes
|
|
21380
21459
|
} : null,
|
|
21381
21460
|
expiredReport: resp.expiredReport?.[0] || null
|
|
21382
21461
|
};
|
|
@@ -21446,17 +21525,17 @@ var McpGQLClient = class extends GQLClient {
|
|
|
21446
21525
|
return null;
|
|
21447
21526
|
}
|
|
21448
21527
|
const latestReport = FixReportSummarySchema.parse(res.fixReport?.[0]);
|
|
21449
|
-
const { applicableFixes,
|
|
21528
|
+
const { applicableFixes, interactiveFixes } = this.mergeUserAndSystemFixes({
|
|
21450
21529
|
reportData: latestReport,
|
|
21451
21530
|
limit
|
|
21452
21531
|
});
|
|
21453
21532
|
logDebug("[GraphQL] GetReportFixes response parsed", {
|
|
21454
21533
|
fixes: applicableFixes,
|
|
21455
|
-
|
|
21534
|
+
interactiveCount: interactiveFixes.length
|
|
21456
21535
|
});
|
|
21457
21536
|
return {
|
|
21458
21537
|
fixes: applicableFixes,
|
|
21459
|
-
|
|
21538
|
+
interactiveFixes,
|
|
21460
21539
|
totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0,
|
|
21461
21540
|
expiredReport: res.expiredReport?.[0] || null,
|
|
21462
21541
|
fixReport: res.fixReport?.[0] ? {
|
|
@@ -21474,6 +21553,36 @@ var McpGQLClient = class extends GQLClient {
|
|
|
21474
21553
|
throw e;
|
|
21475
21554
|
}
|
|
21476
21555
|
}
|
|
21556
|
+
/** Root getFix recomputes the patch; fix_by_pk.patchAndQuestions(userInput) does not (stale questions looked like cascading). */
|
|
21557
|
+
async getFixWithAnswers({
|
|
21558
|
+
fixId,
|
|
21559
|
+
answers
|
|
21560
|
+
}) {
|
|
21561
|
+
try {
|
|
21562
|
+
logDebug("[GraphQL] Calling getFixWithAnswers query", {
|
|
21563
|
+
fixId,
|
|
21564
|
+
answerCount: answers.length,
|
|
21565
|
+
userInput: answers
|
|
21566
|
+
});
|
|
21567
|
+
const resp = await this._clientSdk.getFixWithAnswers({
|
|
21568
|
+
fixId,
|
|
21569
|
+
userInput: answers
|
|
21570
|
+
});
|
|
21571
|
+
logDebug("[GraphQL] getFixWithAnswers successful", {
|
|
21572
|
+
fixId,
|
|
21573
|
+
responseTypename: resp.fixData?.__typename,
|
|
21574
|
+
remainingQuestionKeys: resp.fixData?.__typename === "FixData" ? resp.fixData.questions.map((q) => q.key) : void 0
|
|
21575
|
+
});
|
|
21576
|
+
return { fixData: resp.fixData ?? null };
|
|
21577
|
+
} catch (e) {
|
|
21578
|
+
logError("[GraphQL] getFixWithAnswers failed", {
|
|
21579
|
+
error: e,
|
|
21580
|
+
fixId,
|
|
21581
|
+
...this.getErrorContext()
|
|
21582
|
+
});
|
|
21583
|
+
throw e;
|
|
21584
|
+
}
|
|
21585
|
+
}
|
|
21477
21586
|
};
|
|
21478
21587
|
async function createAuthenticatedMcpGQLClient({
|
|
21479
21588
|
isBackgroundCall = false,
|
|
@@ -24516,6 +24625,7 @@ init_client_generates();
|
|
|
24516
24625
|
init_configs();
|
|
24517
24626
|
|
|
24518
24627
|
// src/mcp/core/prompts.ts
|
|
24628
|
+
init_client_generates();
|
|
24519
24629
|
init_configs();
|
|
24520
24630
|
function friendlyType(s) {
|
|
24521
24631
|
const withoutUnderscores = s.replace(/_/g, " ");
|
|
@@ -24524,17 +24634,133 @@ function friendlyType(s) {
|
|
|
24524
24634
|
}
|
|
24525
24635
|
var noFixesReturnedForParameters = `No fixes returned for the given offset and limit parameters.
|
|
24526
24636
|
`;
|
|
24527
|
-
var
|
|
24528
|
-
|
|
24529
|
-
|
|
24530
|
-
|
|
24531
|
-
const
|
|
24637
|
+
var resolveQuestionText = ({
|
|
24638
|
+
fix,
|
|
24639
|
+
question
|
|
24640
|
+
}) => {
|
|
24641
|
+
const language = fix.safeIssueLanguage ?? void 0;
|
|
24642
|
+
const issueType = fix.safeIssueType ?? void 0;
|
|
24643
|
+
if (!language || !issueType) {
|
|
24644
|
+
return { content: question.name, description: "" };
|
|
24645
|
+
}
|
|
24646
|
+
const item = storedQuestionData_default[language]?.[issueType]?.[question.name];
|
|
24647
|
+
if (!item) {
|
|
24648
|
+
return { content: question.name, description: "" };
|
|
24649
|
+
}
|
|
24650
|
+
const args = question.extraContext.reduce(
|
|
24651
|
+
(acc, ctx) => {
|
|
24652
|
+
acc[ctx.key] = ctx.value;
|
|
24653
|
+
return acc;
|
|
24654
|
+
},
|
|
24655
|
+
{}
|
|
24656
|
+
);
|
|
24657
|
+
try {
|
|
24658
|
+
return {
|
|
24659
|
+
content: item.content(args) || question.name,
|
|
24660
|
+
description: item.description(args) || ""
|
|
24661
|
+
};
|
|
24662
|
+
} catch {
|
|
24663
|
+
return { content: question.name, description: "" };
|
|
24664
|
+
}
|
|
24665
|
+
};
|
|
24666
|
+
var formatQuestionInputContract = (question) => {
|
|
24667
|
+
switch (question.inputType) {
|
|
24668
|
+
case "SELECT" /* Select */:
|
|
24669
|
+
return `Pick exactly ONE of: ${question.options.map((o) => `\`${o}\``).join(", ")}`;
|
|
24670
|
+
case "NUMBER" /* Number */:
|
|
24671
|
+
return 'Provide a numeric string (e.g. "60").';
|
|
24672
|
+
case "TEXT" /* Text */:
|
|
24673
|
+
return "Provide a free-form string (or an empty string to accept the default).";
|
|
24674
|
+
}
|
|
24675
|
+
};
|
|
24676
|
+
var renderInteractiveFix = (fix, index) => {
|
|
24677
|
+
if (fix.patchAndQuestions.__typename !== "FixData") return "";
|
|
24678
|
+
const { questions, extraContext } = fix.patchAndQuestions;
|
|
24679
|
+
const vulnerabilityType = friendlyType(fix.safeIssueType ?? "Unknown");
|
|
24680
|
+
const questionsBlock = questions.slice().sort((a, b) => a.index - b.index).map((q, qIdx) => {
|
|
24681
|
+
const { content, description } = resolveQuestionText({ fix, question: q });
|
|
24682
|
+
const desc = description ? `
|
|
24683
|
+
*Why it matters:* ${description}` : "";
|
|
24684
|
+
const defaultLine = q.defaultValue ? `
|
|
24685
|
+
*Default if you don't decide:* \`${q.defaultValue}\`` : "";
|
|
24686
|
+
return `${qIdx + 1}. **\`${q.key}\`** \u2014 ${content}
|
|
24687
|
+
*Input:* ${formatQuestionInputContract(q)}${defaultLine}${desc}`;
|
|
24688
|
+
}).join("\n\n");
|
|
24689
|
+
return `### Interactive fix ${index + 1}: ${vulnerabilityType}
|
|
24690
|
+
|
|
24691
|
+
**Fix id:** \`${fix.id}\`
|
|
24692
|
+
**Description:** ${extraContext?.fixDescription || "Security vulnerability fix that requires answers before it can be tailored."}
|
|
24693
|
+
|
|
24694
|
+
**Questions you must answer before this fix can be applied:**
|
|
24695
|
+
|
|
24696
|
+
${questionsBlock}
|
|
24697
|
+
`;
|
|
24698
|
+
};
|
|
24699
|
+
var interactiveFixesPrompt = ({
|
|
24700
|
+
interactiveFixes,
|
|
24701
|
+
repositoryPath
|
|
24702
|
+
}) => {
|
|
24703
|
+
if (interactiveFixes.length === 0) return "";
|
|
24704
|
+
const fixesBlock = interactiveFixes.map((fix, idx) => renderInteractiveFix(fix, idx)).join("\n---\n");
|
|
24532
24705
|
return `
|
|
24533
|
-
##
|
|
24706
|
+
## Interactive fixes \u2014 decide from code, then apply or abstain
|
|
24707
|
+
|
|
24708
|
+
The Mobb backend produced ${interactiveFixes.length} fix${interactiveFixes.length === 1 ? "" : "es"} that need context before the patch can be tailored. **Infer answers only from the real code**: touched files, imports, framework, and how untrusted data flows (SSR/XSS/PT/SSRF, etc.). **Do NOT ask the end user** \u2014 use tooling/read-only repo inspection only.
|
|
24709
|
+
|
|
24710
|
+
### CRITICAL INSTRUCTIONS \u2014 READ CAREFULLY
|
|
24711
|
+
|
|
24712
|
+
1. **Defaults are hints from Mobb, not autopilot.** The line *Default if you don't decide* shows what the backend would fall back to. Prefer answers **consistent with the codebase**. Do **not** pick an answer **only** because it equals that default \u2014 a mismatched default can produce the wrong tailored patch.
|
|
24713
|
+
2. **Time budget \u2014 ~5 seconds per interactive fix, as one shared pool.** With ${interactiveFixes.length} interactive fix${interactiveFixes.length === 1 ? "" : "es"} above, spend **at most ~${interactiveFixes.length * 5} seconds total** inspecting the codebase before re-calling \`${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES}\` with \`interactiveAnswers\`. The budget is a pool \u2014 one fix may take 8s if it's genuinely ambiguous while another takes 2s if the call site is obvious, as long as the total stays near the bound. If a fix is still uncertain when its share runs out, **omit it from \`interactiveAnswers\`** (rule 4) rather than over-deliberating.
|
|
24714
|
+
3. **Confidence required.** Include in \`interactiveAnswers\` **only** fixes where your answers are justified by what you see in code (exact SELECT strings where applicable).
|
|
24715
|
+
4. **Abstain rather than guess.** If you **cannot** justify any responsible answer after inspecting the code (ambiguous flows, missing callers, isomorphic bundles, unclear SSRF allowlists, etc.), **omit that fix id entirely** from \`interactiveAnswers\`. Tell the user in prose what was skipped and why so they can fix manually or follow up later \u2014 **do not fabricate answers**.
|
|
24716
|
+
5. **Skipping everything.** If you skip **all** interactive fixes, still call **\`${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES}\`** once with \`"interactiveAnswers": []\` (empty array) together with \`path\`. That acknowledges abstention **without** starting a new scan. Omitting \`interactiveAnswers\` entirely falls back to scan mode instead \u2014 avoid that when you intend purely to abstain.
|
|
24717
|
+
6. **Use exact option strings for SELECT questions.** Copy them character-for-character from the option list. Do **not** append explanation, rationale, or commentary to the value \u2014 that turns the answer into a non-matching string and the backend silently falls back to its default.
|
|
24718
|
+
7. **Use the \`key\` verbatim, not the human label.** Each question shows a backtick-quoted key (e.g. \`is_server_side_code\`, \`tainted_term_type\`). That exact string goes into \`answers[].key\`. The display name (e.g. \`isServerSideCode\`, \`taintedTermType\`) is for humans only \u2014 sending it as a key means the backend won't recognise the answer and falls back to the default.
|
|
24719
|
+
8. **After the tool call**, summarize: fixes applied with reasoning; fixes skipped (confidence/abstention/time-budget); tool failures.
|
|
24720
|
+
|
|
24721
|
+
### Decision heuristics for common questions
|
|
24722
|
+
|
|
24723
|
+
(Keys shown in snake_case \u2014 copy them verbatim from each fix's question block.)
|
|
24724
|
+
|
|
24725
|
+
- **\`is_server_side_code\` (XSS)** \u2014 \`yes\` when server-render or Node HTTP handlers dominate; \`no\` when clearly browser-only. If bundle/context is genuinely ambiguous after inspection, **omit** this fix from \`interactiveAnswers\`.
|
|
24726
|
+
- **\`tainted_term_type\` (Path Traversal)** \u2014 match how user input is joined/consumed (single filename vs path segments vs absolute). If usage cannot be determined, **omit**.
|
|
24727
|
+
- **\`iframe_restrictions\`** \u2014 strict sandbox (\`""\`) unless embedded content clearly needs listed capabilities.
|
|
24534
24728
|
|
|
24535
|
-
|
|
24729
|
+
### How to call \`${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES}\`
|
|
24730
|
+
|
|
24731
|
+
Apply fixes you are confident about (subset allowed):
|
|
24732
|
+
|
|
24733
|
+
\`\`\`json
|
|
24734
|
+
{
|
|
24735
|
+
"path": "${repositoryPath}",
|
|
24736
|
+
"interactiveAnswers": [
|
|
24737
|
+
{
|
|
24738
|
+
"fixId": "<fix id from below>",
|
|
24739
|
+
"answers": [
|
|
24740
|
+
{ "key": "<question key, exactly as shown>", "value": "<your decided value>" }
|
|
24741
|
+
]
|
|
24742
|
+
}
|
|
24743
|
+
]
|
|
24744
|
+
}
|
|
24745
|
+
\`\`\`
|
|
24746
|
+
|
|
24747
|
+
Explicit abstention \u2014 skip **all** interactive fixes without rescanning:
|
|
24748
|
+
|
|
24749
|
+
\`\`\`json
|
|
24750
|
+
{
|
|
24751
|
+
"path": "${repositoryPath}",
|
|
24752
|
+
"interactiveAnswers": []
|
|
24753
|
+
}
|
|
24754
|
+
\`\`\`
|
|
24755
|
+
|
|
24756
|
+
${fixesBlock}
|
|
24536
24757
|
`;
|
|
24537
24758
|
};
|
|
24759
|
+
var interactiveAnswersAbstainAllToolResponse = `## Interactive fixes \u2014 none applied
|
|
24760
|
+
|
|
24761
|
+
\`interactiveAnswers\` was an empty array: **no** tailored patches were requested and **no** scan was run.
|
|
24762
|
+
|
|
24763
|
+
State clearly for the user which interactive fixes you **skipped**, why the code did not support a confident answer, and that they can apply those manually or re-run after clarifying the codebase.`;
|
|
24538
24764
|
var noFixesReturnedForParametersWithGuidance = ({
|
|
24539
24765
|
offset,
|
|
24540
24766
|
limit,
|
|
@@ -24593,9 +24819,13 @@ var applyFixesPrompt = ({
|
|
|
24593
24819
|
currentTool,
|
|
24594
24820
|
offset,
|
|
24595
24821
|
limit,
|
|
24596
|
-
gqlClient
|
|
24822
|
+
gqlClient,
|
|
24823
|
+
hasInteractiveFixes = false
|
|
24597
24824
|
}) => {
|
|
24598
24825
|
if (fixes.length === 0) {
|
|
24826
|
+
if (hasInteractiveFixes) {
|
|
24827
|
+
return "";
|
|
24828
|
+
}
|
|
24599
24829
|
if (totalCount > 0) {
|
|
24600
24830
|
return noFixesReturnedForParametersWithGuidance({
|
|
24601
24831
|
offset,
|
|
@@ -24782,11 +25012,16 @@ var fixesFoundPrompt = ({
|
|
|
24782
25012
|
offset,
|
|
24783
25013
|
limit,
|
|
24784
25014
|
gqlClient,
|
|
24785
|
-
|
|
25015
|
+
interactiveFixes = [],
|
|
25016
|
+
repositoryPath
|
|
24786
25017
|
}) => {
|
|
24787
25018
|
const totalFixes = fixReport.filteredFixesCount.aggregate?.count || 0;
|
|
25019
|
+
const interactiveBlock = interactiveFixesPrompt({
|
|
25020
|
+
interactiveFixes,
|
|
25021
|
+
repositoryPath
|
|
25022
|
+
});
|
|
24788
25023
|
if (totalFixes === 0) {
|
|
24789
|
-
return noFixesAvailablePrompt +
|
|
25024
|
+
return noFixesAvailablePrompt + interactiveBlock;
|
|
24790
25025
|
}
|
|
24791
25026
|
const criticalFixes = fixReport.CRITICAL?.aggregate?.count || 0;
|
|
24792
25027
|
const highFixes = fixReport.HIGH?.aggregate?.count || 0;
|
|
@@ -24828,8 +25063,9 @@ ${applyFixesPrompt({
|
|
|
24828
25063
|
currentTool: MCP_TOOL_FETCH_AVAILABLE_FIXES,
|
|
24829
25064
|
offset,
|
|
24830
25065
|
limit,
|
|
24831
|
-
gqlClient
|
|
24832
|
-
|
|
25066
|
+
gqlClient,
|
|
25067
|
+
hasInteractiveFixes: interactiveFixes.length > 0
|
|
25068
|
+
})}${interactiveBlock}`;
|
|
24833
25069
|
};
|
|
24834
25070
|
var nextStepsPrompt = ({ scannedFiles }) => `
|
|
24835
25071
|
### \u{1F4C1} Scanned Files
|
|
@@ -24876,10 +25112,15 @@ var fixesPrompt = ({
|
|
|
24876
25112
|
scannedFiles,
|
|
24877
25113
|
limit,
|
|
24878
25114
|
gqlClient,
|
|
24879
|
-
|
|
25115
|
+
interactiveFixes = [],
|
|
25116
|
+
repositoryPath
|
|
24880
25117
|
}) => {
|
|
25118
|
+
const interactiveBlock = interactiveFixesPrompt({
|
|
25119
|
+
interactiveFixes,
|
|
25120
|
+
repositoryPath
|
|
25121
|
+
});
|
|
24881
25122
|
if (totalCount === 0) {
|
|
24882
|
-
return noFixesFoundPrompt({ scannedFiles }) +
|
|
25123
|
+
return noFixesFoundPrompt({ scannedFiles }) + interactiveBlock;
|
|
24883
25124
|
}
|
|
24884
25125
|
const shownCount = fixes.length;
|
|
24885
25126
|
const nextOffset = offset + shownCount;
|
|
@@ -24895,9 +25136,10 @@ ${applyFixesPrompt({
|
|
|
24895
25136
|
currentTool: MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES,
|
|
24896
25137
|
offset,
|
|
24897
25138
|
limit,
|
|
24898
|
-
gqlClient
|
|
25139
|
+
gqlClient,
|
|
25140
|
+
hasInteractiveFixes: interactiveFixes.length > 0
|
|
24899
25141
|
})}
|
|
24900
|
-
${
|
|
25142
|
+
${interactiveBlock}
|
|
24901
25143
|
${nextStepsPrompt({ scannedFiles })}
|
|
24902
25144
|
`;
|
|
24903
25145
|
};
|
|
@@ -24971,7 +25213,8 @@ var freshFixesPrompt = ({
|
|
|
24971
25213
|
fixes,
|
|
24972
25214
|
limit,
|
|
24973
25215
|
gqlClient,
|
|
24974
|
-
|
|
25216
|
+
interactiveFixes = [],
|
|
25217
|
+
repositoryPath
|
|
24975
25218
|
}) => {
|
|
24976
25219
|
return `Here are the fresh fixes to the vulnerabilities discovered by Mobb MCP
|
|
24977
25220
|
|
|
@@ -24984,9 +25227,10 @@ ${applyFixesPrompt({
|
|
|
24984
25227
|
currentTool: MCP_TOOL_FETCH_AVAILABLE_FIXES,
|
|
24985
25228
|
offset: 0,
|
|
24986
25229
|
limit,
|
|
24987
|
-
gqlClient
|
|
25230
|
+
gqlClient,
|
|
25231
|
+
hasInteractiveFixes: interactiveFixes.length > 0
|
|
24988
25232
|
})}
|
|
24989
|
-
${
|
|
25233
|
+
${interactiveFixesPrompt({ interactiveFixes, repositoryPath })}
|
|
24990
25234
|
`;
|
|
24991
25235
|
};
|
|
24992
25236
|
function extractTargetFileFromPatch(patch) {
|
|
@@ -25006,7 +25250,8 @@ function formatSeverity(severityText, severityValue) {
|
|
|
25006
25250
|
var appliedFixesSummaryPrompt = ({
|
|
25007
25251
|
fixes,
|
|
25008
25252
|
gqlClient,
|
|
25009
|
-
|
|
25253
|
+
interactiveFixes = [],
|
|
25254
|
+
repositoryPath
|
|
25010
25255
|
}) => {
|
|
25011
25256
|
const fixIds = fixes.map((fix) => fix.id);
|
|
25012
25257
|
void gqlClient.updateFixesDownloadStatus(fixIds);
|
|
@@ -25041,7 +25286,7 @@ ${fixes.map((fix, index) => {
|
|
|
25041
25286
|
${continuousMonitoringSection}
|
|
25042
25287
|
|
|
25043
25288
|
${autoFixSettingsSection}
|
|
25044
|
-
${
|
|
25289
|
+
${interactiveFixesPrompt({ interactiveFixes, repositoryPath })}
|
|
25045
25290
|
## \u{1F4CB} Next Steps
|
|
25046
25291
|
|
|
25047
25292
|
1. **Review the changes** - Check the modified files to understand what was fixed
|
|
@@ -25948,6 +26193,7 @@ var LocalMobbFolderService = class {
|
|
|
25948
26193
|
};
|
|
25949
26194
|
|
|
25950
26195
|
// src/mcp/services/PatchApplicationService.ts
|
|
26196
|
+
init_client_generates();
|
|
25951
26197
|
init_configs();
|
|
25952
26198
|
import {
|
|
25953
26199
|
existsSync as existsSync6,
|
|
@@ -26513,7 +26759,8 @@ var PatchApplicationService = class {
|
|
|
26513
26759
|
repositoryPath,
|
|
26514
26760
|
scanStartTime,
|
|
26515
26761
|
gqlClient,
|
|
26516
|
-
scanContext
|
|
26762
|
+
scanContext,
|
|
26763
|
+
downloadSource = "AUTO_MVS" /* AutoMvs */
|
|
26517
26764
|
}) {
|
|
26518
26765
|
const appliedFixes = [];
|
|
26519
26766
|
const failedFixes = [];
|
|
@@ -26572,20 +26819,26 @@ var PatchApplicationService = class {
|
|
|
26572
26819
|
if (appliedFixes.length > 0 && gqlClient) {
|
|
26573
26820
|
try {
|
|
26574
26821
|
const appliedFixIds = appliedFixes.map((fix) => fix.id).filter(Boolean);
|
|
26575
|
-
|
|
26822
|
+
if (downloadSource === "MCP" /* Mcp */) {
|
|
26823
|
+
await gqlClient.updateFixesDownloadStatus(appliedFixIds);
|
|
26824
|
+
} else {
|
|
26825
|
+
await gqlClient.updateAutoAppliedFixesStatus(appliedFixIds);
|
|
26826
|
+
}
|
|
26576
26827
|
logDebug(
|
|
26577
|
-
`[${scanContext}] Successfully updated download status for
|
|
26828
|
+
`[${scanContext}] Successfully updated download status for applied fixes`,
|
|
26578
26829
|
{
|
|
26579
26830
|
appliedFixIds,
|
|
26580
|
-
count: appliedFixIds.length
|
|
26831
|
+
count: appliedFixIds.length,
|
|
26832
|
+
downloadSource
|
|
26581
26833
|
}
|
|
26582
26834
|
);
|
|
26583
26835
|
} catch (error) {
|
|
26584
26836
|
logError(
|
|
26585
|
-
`[${scanContext}] Failed to update download status for
|
|
26837
|
+
`[${scanContext}] Failed to update download status for applied fixes`,
|
|
26586
26838
|
{
|
|
26587
26839
|
error: error instanceof Error ? error.message : String(error),
|
|
26588
|
-
appliedFixCount: appliedFixes.length
|
|
26840
|
+
appliedFixCount: appliedFixes.length,
|
|
26841
|
+
downloadSource
|
|
26589
26842
|
}
|
|
26590
26843
|
);
|
|
26591
26844
|
}
|
|
@@ -27327,6 +27580,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27327
27580
|
__publicField(this, "path", "");
|
|
27328
27581
|
__publicField(this, "filesLastScanned", {});
|
|
27329
27582
|
__publicField(this, "freshFixes", []);
|
|
27583
|
+
__publicField(this, "interactiveFixes", []);
|
|
27330
27584
|
__publicField(this, "reportedFixes", []);
|
|
27331
27585
|
__publicField(this, "intervalId", null);
|
|
27332
27586
|
__publicField(this, "isInitialScanComplete", false);
|
|
@@ -27348,6 +27602,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27348
27602
|
reset() {
|
|
27349
27603
|
this.filesLastScanned = {};
|
|
27350
27604
|
this.freshFixes = [];
|
|
27605
|
+
this.interactiveFixes = [];
|
|
27351
27606
|
this.reportedFixes = [];
|
|
27352
27607
|
this.hasAuthenticationFailed = false;
|
|
27353
27608
|
this.fullScanPathsScanned = configStore.get("fullScanPathsScanned") || [];
|
|
@@ -27433,6 +27688,16 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27433
27688
|
const newFixes = fixes?.fixes?.filter(
|
|
27434
27689
|
(fix) => !this.isFixAlreadyReported(fix)
|
|
27435
27690
|
);
|
|
27691
|
+
const newInteractiveFixes = fixes?.interactiveFixes?.filter(
|
|
27692
|
+
(fix) => !this.isFixAlreadyReported(fix)
|
|
27693
|
+
) ?? [];
|
|
27694
|
+
if (newInteractiveFixes.length > 0) {
|
|
27695
|
+
this.interactiveFixes.push(...newInteractiveFixes);
|
|
27696
|
+
logInfo(
|
|
27697
|
+
`[${scanContext}] Buffered ${newInteractiveFixes.length} interactive fixes for next response`,
|
|
27698
|
+
{ totalBuffered: this.interactiveFixes.length }
|
|
27699
|
+
);
|
|
27700
|
+
}
|
|
27436
27701
|
logInfo(
|
|
27437
27702
|
`[${scanContext}] Security fixes retrieved, total: ${fixes?.fixes?.length || 0}, new: ${newFixes?.length || 0}`
|
|
27438
27703
|
);
|
|
@@ -27862,7 +28127,9 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27862
28127
|
return freshFixesPrompt({
|
|
27863
28128
|
fixes: freshFixes,
|
|
27864
28129
|
limit: MCP_DEFAULT_LIMIT,
|
|
27865
|
-
gqlClient: this.gqlClient
|
|
28130
|
+
gqlClient: this.gqlClient,
|
|
28131
|
+
interactiveFixes: this.interactiveFixes.splice(0, MCP_DEFAULT_LIMIT),
|
|
28132
|
+
repositoryPath: this.path
|
|
27866
28133
|
});
|
|
27867
28134
|
}
|
|
27868
28135
|
logInfo(`[${scanContext}] No fresh fixes to report`);
|
|
@@ -27876,7 +28143,9 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27876
28143
|
);
|
|
27877
28144
|
return appliedFixesSummaryPrompt({
|
|
27878
28145
|
fixes: appliedFixesToShow,
|
|
27879
|
-
gqlClient: this.gqlClient
|
|
28146
|
+
gqlClient: this.gqlClient,
|
|
28147
|
+
interactiveFixes: this.interactiveFixes.splice(0, MCP_DEFAULT_LIMIT),
|
|
28148
|
+
repositoryPath: this.path
|
|
27880
28149
|
});
|
|
27881
28150
|
}
|
|
27882
28151
|
logInfo(`[${scanContext}] No applied fixes to report`);
|
|
@@ -27983,6 +28252,7 @@ var _FetchAvailableFixesService = class _FetchAvailableFixesService {
|
|
|
27983
28252
|
}
|
|
27984
28253
|
async checkForAvailableFixes({
|
|
27985
28254
|
repoUrl,
|
|
28255
|
+
repositoryPath,
|
|
27986
28256
|
limit = MCP_DEFAULT_LIMIT,
|
|
27987
28257
|
offset,
|
|
27988
28258
|
fileFilter
|
|
@@ -28020,7 +28290,8 @@ var _FetchAvailableFixesService = class _FetchAvailableFixesService {
|
|
|
28020
28290
|
offset: effectiveOffset,
|
|
28021
28291
|
limit,
|
|
28022
28292
|
gqlClient,
|
|
28023
|
-
|
|
28293
|
+
interactiveFixes: fixReport.interactiveFixes ?? [],
|
|
28294
|
+
repositoryPath
|
|
28024
28295
|
});
|
|
28025
28296
|
this.currentOffset = effectiveOffset + (fixReport.fixes?.length || 0);
|
|
28026
28297
|
return prompt;
|
|
@@ -28162,6 +28433,7 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
28162
28433
|
}
|
|
28163
28434
|
const fixResult = await this.availableFixesService.checkForAvailableFixes({
|
|
28164
28435
|
repoUrl: originUrl,
|
|
28436
|
+
repositoryPath: path37,
|
|
28165
28437
|
limit: args.limit,
|
|
28166
28438
|
offset: args.offset,
|
|
28167
28439
|
fileFilter: actualFileFilter
|
|
@@ -28265,6 +28537,7 @@ import z45 from "zod";
|
|
|
28265
28537
|
init_configs();
|
|
28266
28538
|
|
|
28267
28539
|
// src/mcp/tools/scanAndFixVulnerabilities/ScanAndFixVulnerabilitiesService.ts
|
|
28540
|
+
init_client_generates();
|
|
28268
28541
|
init_configs();
|
|
28269
28542
|
var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService {
|
|
28270
28543
|
constructor() {
|
|
@@ -28356,7 +28629,8 @@ var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService
|
|
|
28356
28629
|
scannedFiles: [...fileList],
|
|
28357
28630
|
limit: effectiveLimit,
|
|
28358
28631
|
gqlClient: this.gqlClient,
|
|
28359
|
-
|
|
28632
|
+
interactiveFixes: fixes.interactiveFixes,
|
|
28633
|
+
repositoryPath
|
|
28360
28634
|
});
|
|
28361
28635
|
this.currentOffset = effectiveOffset + (fixes.fixes?.length || 0);
|
|
28362
28636
|
return prompt;
|
|
@@ -28401,12 +28675,164 @@ var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService
|
|
|
28401
28675
|
return {
|
|
28402
28676
|
fixes: fixes?.fixes || [],
|
|
28403
28677
|
totalCount: fixes?.totalCount || 0,
|
|
28404
|
-
|
|
28678
|
+
interactiveFixes: fixes?.interactiveFixes || []
|
|
28405
28679
|
};
|
|
28406
28680
|
}
|
|
28681
|
+
/** Applies patches from interactiveAnswers only (no scan). */
|
|
28682
|
+
async applyInteractiveAnswers({
|
|
28683
|
+
interactiveAnswers,
|
|
28684
|
+
repositoryPath
|
|
28685
|
+
}) {
|
|
28686
|
+
this.gqlClient = await this.initializeGqlClient();
|
|
28687
|
+
logInfo(
|
|
28688
|
+
`Applying ${interactiveAnswers.length} interactive fix(es) with LLM-supplied answers`,
|
|
28689
|
+
{ repositoryPath }
|
|
28690
|
+
);
|
|
28691
|
+
const applied = [];
|
|
28692
|
+
const failed = [];
|
|
28693
|
+
const skipped = [];
|
|
28694
|
+
for (const entry of interactiveAnswers) {
|
|
28695
|
+
try {
|
|
28696
|
+
const { fixData } = await this.gqlClient.getFixWithAnswers({
|
|
28697
|
+
fixId: entry.fixId,
|
|
28698
|
+
answers: entry.answers
|
|
28699
|
+
});
|
|
28700
|
+
if (!fixData) {
|
|
28701
|
+
failed.push({
|
|
28702
|
+
fixId: entry.fixId,
|
|
28703
|
+
reason: "Fix not found on the server (may have expired)"
|
|
28704
|
+
});
|
|
28705
|
+
continue;
|
|
28706
|
+
}
|
|
28707
|
+
if (fixData.__typename !== "FixData") {
|
|
28708
|
+
failed.push({
|
|
28709
|
+
fixId: entry.fixId,
|
|
28710
|
+
reason: `Backend returned ${fixData.__typename} \u2014 could not produce a patch with the supplied answers`
|
|
28711
|
+
});
|
|
28712
|
+
continue;
|
|
28713
|
+
}
|
|
28714
|
+
if (!fixData.patch) {
|
|
28715
|
+
failed.push({
|
|
28716
|
+
fixId: entry.fixId,
|
|
28717
|
+
reason: "Backend returned FixData with no patch \u2014 answers did not yield an applicable fix"
|
|
28718
|
+
});
|
|
28719
|
+
continue;
|
|
28720
|
+
}
|
|
28721
|
+
const sentByKey = new Map(entry.answers.map((a) => [a.key, a.value]));
|
|
28722
|
+
const invalidSelectAnswers = [];
|
|
28723
|
+
for (const q of fixData.questions) {
|
|
28724
|
+
if (q.inputType !== "SELECT" /* Select */) continue;
|
|
28725
|
+
const sentValue = sentByKey.get(q.key);
|
|
28726
|
+
if (sentValue === void 0) continue;
|
|
28727
|
+
if (!q.options.includes(sentValue)) {
|
|
28728
|
+
invalidSelectAnswers.push({
|
|
28729
|
+
key: q.key,
|
|
28730
|
+
sentValue,
|
|
28731
|
+
options: [...q.options]
|
|
28732
|
+
});
|
|
28733
|
+
}
|
|
28734
|
+
}
|
|
28735
|
+
if (invalidSelectAnswers.length > 0) {
|
|
28736
|
+
skipped.push({
|
|
28737
|
+
fixId: entry.fixId,
|
|
28738
|
+
invalidSelectAnswers
|
|
28739
|
+
});
|
|
28740
|
+
continue;
|
|
28741
|
+
}
|
|
28742
|
+
const newPendingKeys = fixData.questions.map((q) => q.key).filter((k) => !sentByKey.has(k));
|
|
28743
|
+
const mcpFix = McpFixSchema.parse({
|
|
28744
|
+
__typename: "fix",
|
|
28745
|
+
id: entry.fixId,
|
|
28746
|
+
confidence: 0,
|
|
28747
|
+
safeIssueType: null,
|
|
28748
|
+
safeIssueLanguage: null,
|
|
28749
|
+
severityText: null,
|
|
28750
|
+
severityValue: null,
|
|
28751
|
+
vulnerabilityReportIssues: [],
|
|
28752
|
+
patchAndQuestions: fixData
|
|
28753
|
+
});
|
|
28754
|
+
const result = await PatchApplicationService.applyFixes({
|
|
28755
|
+
fixes: [mcpFix],
|
|
28756
|
+
repositoryPath,
|
|
28757
|
+
gqlClient: this.gqlClient,
|
|
28758
|
+
scanContext: ScanContext.USER_REQUEST,
|
|
28759
|
+
downloadSource: "MCP" /* Mcp */
|
|
28760
|
+
});
|
|
28761
|
+
if (result.appliedFixes.length > 0) {
|
|
28762
|
+
const targetFile = extractTargetFile(fixData.patch) ?? "unknown file";
|
|
28763
|
+
applied.push({
|
|
28764
|
+
fixId: entry.fixId,
|
|
28765
|
+
targetFile,
|
|
28766
|
+
newPendingKeys
|
|
28767
|
+
});
|
|
28768
|
+
} else {
|
|
28769
|
+
failed.push({
|
|
28770
|
+
fixId: entry.fixId,
|
|
28771
|
+
reason: result.failedFixes[0]?.error ?? "patch application failed"
|
|
28772
|
+
});
|
|
28773
|
+
}
|
|
28774
|
+
} catch (error) {
|
|
28775
|
+
failed.push({
|
|
28776
|
+
fixId: entry.fixId,
|
|
28777
|
+
reason: error.message
|
|
28778
|
+
});
|
|
28779
|
+
}
|
|
28780
|
+
}
|
|
28781
|
+
return formatApplyAnswersSummary({ applied, failed, skipped });
|
|
28782
|
+
}
|
|
28407
28783
|
};
|
|
28408
28784
|
__publicField(_ScanAndFixVulnerabilitiesService, "instance");
|
|
28409
28785
|
var ScanAndFixVulnerabilitiesService = _ScanAndFixVulnerabilitiesService;
|
|
28786
|
+
function extractTargetFile(patch) {
|
|
28787
|
+
const match = patch.match(/^\+\+\+ b\/(.+)$/m);
|
|
28788
|
+
return match?.[1] ?? null;
|
|
28789
|
+
}
|
|
28790
|
+
function formatApplyAnswersSummary({
|
|
28791
|
+
applied,
|
|
28792
|
+
failed,
|
|
28793
|
+
skipped
|
|
28794
|
+
}) {
|
|
28795
|
+
const sections = [];
|
|
28796
|
+
if (applied.length > 0) {
|
|
28797
|
+
sections.push(
|
|
28798
|
+
`## \u2705 Applied ${applied.length} fix${applied.length === 1 ? "" : "es"}
|
|
28799
|
+
|
|
28800
|
+
` + applied.map((a) => {
|
|
28801
|
+
const hint = a.newPendingKeys.length > 0 ? `
|
|
28802
|
+
\u26A0\uFE0F The backend returned additional question key(s) we didn't send: [${a.newPendingKeys.map((k) => `\`${k}\``).join(
|
|
28803
|
+
", "
|
|
28804
|
+
)}]. This is either (a) a true cascading question \u2014 re-call \`scan_and_fix_vulnerabilities\` with those added to \`interactiveAnswers\` if you have a confident answer; or (b) the key we sent was wrong (e.g. camelCase vs snake_case) and the backend fell back to its default \u2014 copy the echoed key verbatim and retry. The patch above was already applied using defaults.` : "";
|
|
28805
|
+
return `- **\`${a.fixId}\`** \u2192 \`${a.targetFile}\`${hint}`;
|
|
28806
|
+
}).join("\n")
|
|
28807
|
+
);
|
|
28808
|
+
}
|
|
28809
|
+
if (skipped.length > 0) {
|
|
28810
|
+
sections.push(
|
|
28811
|
+
`## \u23ED\uFE0F Skipped ${skipped.length} fix${skipped.length === 1 ? "" : "es"} \u2014 invalid SELECT answer value(s)
|
|
28812
|
+
|
|
28813
|
+
` + skipped.map((s) => {
|
|
28814
|
+
const detail = s.invalidSelectAnswers.map(
|
|
28815
|
+
(a) => `\`${a.key}\` got \`"${a.sentValue}"\` \u2014 allowed options: [${a.options.map((o) => `\`"${o}"\``).join(", ")}]`
|
|
28816
|
+
).join("; ");
|
|
28817
|
+
return `- **\`${s.fixId}\`** \u2014 ${detail}
|
|
28818
|
+
The file was **not modified**. Re-call \`scan_and_fix_vulnerabilities\` with one of the exact allowed option strings (copy character-for-character, no commentary appended) or omit this fix entirely if no option is justified by the code.`;
|
|
28819
|
+
}).join("\n") + `
|
|
28820
|
+
|
|
28821
|
+
This guardrail exists to prevent the backend from silently falling back to its default sanitizer for an answer it didn't recognize \u2014 which could apply a semantically-wrong patch and break legitimate code paths.`
|
|
28822
|
+
);
|
|
28823
|
+
}
|
|
28824
|
+
if (failed.length > 0) {
|
|
28825
|
+
sections.push(
|
|
28826
|
+
`## \u274C Failed
|
|
28827
|
+
|
|
28828
|
+
` + failed.map((f) => `- **\`${f.fixId}\`** \u2014 ${f.reason}`).join("\n")
|
|
28829
|
+
);
|
|
28830
|
+
}
|
|
28831
|
+
if (sections.length === 0) {
|
|
28832
|
+
return "No fixes were processed.";
|
|
28833
|
+
}
|
|
28834
|
+
return sections.join("\n\n");
|
|
28835
|
+
}
|
|
28410
28836
|
|
|
28411
28837
|
// src/mcp/tools/scanAndFixVulnerabilities/ScanAndFixVulnerabilitiesTool.ts
|
|
28412
28838
|
var ScanAndFixVulnerabilitiesTool = class extends BaseTool {
|
|
@@ -28414,13 +28840,23 @@ var ScanAndFixVulnerabilitiesTool = class extends BaseTool {
|
|
|
28414
28840
|
super();
|
|
28415
28841
|
__publicField(this, "name", MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES);
|
|
28416
28842
|
__publicField(this, "displayName", "Scan and Fix Vulnerabilities");
|
|
28417
|
-
|
|
28418
|
-
|
|
28843
|
+
__publicField(this, "description", `Scans a given local repository for security vulnerabilities, applies the auto-fixable ones, and surfaces any fix that needs your input as an "Interactive fix". Re-invoke with "interactiveAnswers" to apply those.
|
|
28844
|
+
|
|
28845
|
+
Two modes of operation:
|
|
28846
|
+
|
|
28847
|
+
A) SCAN MODE (default \u2014 interactiveAnswers omitted)
|
|
28848
|
+
- Scans changed/recent files at "path"
|
|
28849
|
+
- Auto-applies fixes that need no input
|
|
28850
|
+
- Returns "Interactive fix" entries for fixes that need decisions; you (the AI) decide answers from the surrounding code
|
|
28851
|
+
|
|
28852
|
+
B) APPLY-WITH-ANSWERS MODE (interactiveAnswers field supplied \u2014 array may be empty or partial)
|
|
28853
|
+
- SKIPS scanning entirely (does NOT fall back to scan mode just because some fixes were skipped)
|
|
28854
|
+
- Include ONLY fixes where answers are justified by code/context; omit fix IDs you are not confident about
|
|
28855
|
+
- Empty array []: abstain from applying ALL interactive fixes (no patches fetched \u2014 summarize skips for the user)
|
|
28419
28856
|
|
|
28420
28857
|
When to invoke:
|
|
28421
|
-
\u2022
|
|
28422
|
-
\u2022
|
|
28423
|
-
\u2022 Ideal after the user makes code changes (added/modified/staged files) but before committing, or whenever they request a full rescan.
|
|
28858
|
+
\u2022 Mode A \u2014 when the user asks to "scan for vulnerabilities", "run a security check", or after they make code changes.
|
|
28859
|
+
\u2022 Mode B \u2014 immediately after Mode A returns interactive fixes; pass confident answers only; use [] only when abstaining from every interactive fix.
|
|
28424
28860
|
|
|
28425
28861
|
How to invoke:
|
|
28426
28862
|
\u2022 Required argument:
|
|
@@ -28430,25 +28866,32 @@ How to invoke:
|
|
|
28430
28866
|
\u2013 limit (number): maximum number of fixes to include in the response.
|
|
28431
28867
|
\u2013 maxFiles (number): maximum number of files to scan (default: ${MCP_DEFAULT_MAX_FILES_TO_SCAN}). Provide this value to increase the scope of the scan.
|
|
28432
28868
|
\u2013 rescan (boolean): true to force a complete rescan even if cached results exist.
|
|
28869
|
+
\u2013 interactiveAnswers (array): triggers Mode B. Each entry: { fixId, answers: [{ key, value }] }. SELECT values MUST be exact strings from the option list. Omit fixes you cannot answer confidently. Use [] to abstain from all interactive fixes without rescanning.
|
|
28433
28870
|
|
|
28434
|
-
Behaviour:
|
|
28435
|
-
\u2022 If the directory is a valid Git repository, the tool scans the changed files in the repository. If there are no changes, it scans the files included in the
|
|
28871
|
+
Behaviour (Mode A):
|
|
28872
|
+
\u2022 If the directory is a valid Git repository, the tool scans the changed files in the repository. If there are no changes, it scans the files included in the last commit.
|
|
28436
28873
|
\u2022 If the directory is not a valid Git repository, the tool falls back to scanning recently changed files in the folder.
|
|
28437
28874
|
\u2022 If maxFiles is provided, the tool scans the maxFiles most recently changed files in the repository.
|
|
28438
|
-
\u2022
|
|
28439
|
-
\u2022 The tool NEVER commits or pushes changes; it only returns proposed diffs/fixes as text.
|
|
28875
|
+
\u2022 The tool NEVER commits or pushes changes.
|
|
28440
28876
|
|
|
28441
28877
|
Return value:
|
|
28442
|
-
|
|
28443
|
-
\u2022 A human-readable summary of the fixes / patches, or
|
|
28444
|
-
\u2022 A diagnostic or error message if the scan fails or finds nothing to fix.
|
|
28878
|
+
A "content" array with one text element. Either a human-readable summary of fixes/patches, an interactive-fix prompt, an apply-with-answers result, or an error message.
|
|
28445
28879
|
|
|
28446
|
-
Example payload:
|
|
28880
|
+
Example payload (Mode A):
|
|
28881
|
+
{ "path": "/home/user/my-project", "limit": 20, "maxFiles": 50 }
|
|
28882
|
+
|
|
28883
|
+
Example payload (Mode B \u2014 subset or abstain):
|
|
28447
28884
|
{
|
|
28448
28885
|
"path": "/home/user/my-project",
|
|
28449
|
-
"
|
|
28450
|
-
|
|
28451
|
-
|
|
28886
|
+
"interactiveAnswers": [
|
|
28887
|
+
{ "fixId": "abc-123", "answers": [{ "key": "isServerSideCode", "value": "yes" }] }
|
|
28888
|
+
]
|
|
28889
|
+
}
|
|
28890
|
+
|
|
28891
|
+
Example payload (Mode B \u2014 abstain from every interactive fix, no rescan):
|
|
28892
|
+
{
|
|
28893
|
+
"path": "/home/user/my-project",
|
|
28894
|
+
"interactiveAnswers": []
|
|
28452
28895
|
}`);
|
|
28453
28896
|
__publicField(this, "hasAuthentication", true);
|
|
28454
28897
|
__publicField(this, "inputValidationSchema", z45.object({
|
|
@@ -28463,6 +28906,21 @@ Example payload:
|
|
|
28463
28906
|
rescan: z45.boolean().optional().describe("Optional whether to rescan the repository"),
|
|
28464
28907
|
scanRecentlyChangedFiles: z45.boolean().optional().describe(
|
|
28465
28908
|
"Optional whether to automatically scan recently changed files when no changed files are found in git status. If false, the tool will prompt the user instead."
|
|
28909
|
+
),
|
|
28910
|
+
interactiveAnswers: z45.array(
|
|
28911
|
+
z45.object({
|
|
28912
|
+
fixId: z45.string().min(1).describe('Fix id from a previous "Interactive fix" prompt block.'),
|
|
28913
|
+
answers: z45.array(
|
|
28914
|
+
z45.object({
|
|
28915
|
+
key: z45.string().min(1).describe("FixQuestion key."),
|
|
28916
|
+
value: z45.string().describe(
|
|
28917
|
+
"For SELECT questions MUST be one of the listed options; for TEXT/NUMBER, a free-form value."
|
|
28918
|
+
)
|
|
28919
|
+
})
|
|
28920
|
+
).min(1)
|
|
28921
|
+
})
|
|
28922
|
+
).optional().describe(
|
|
28923
|
+
"When supplied (including []), SKIPS scanning. Non-empty: apply each listed interactive fix. Empty []: abstain from all interactive fixes \u2014 no patches applied. Omit entirely for scan mode."
|
|
28466
28924
|
)
|
|
28467
28925
|
}));
|
|
28468
28926
|
__publicField(this, "inputSchema", {
|
|
@@ -28491,6 +28949,34 @@ Example payload:
|
|
|
28491
28949
|
scanRecentlyChangedFiles: {
|
|
28492
28950
|
type: "boolean",
|
|
28493
28951
|
description: "[Optional] whether to automatically scan recently changed files when no changed files are found in git status. If false, the tool will prompt the user instead."
|
|
28952
|
+
},
|
|
28953
|
+
interactiveAnswers: {
|
|
28954
|
+
type: "array",
|
|
28955
|
+
items: {
|
|
28956
|
+
type: "object",
|
|
28957
|
+
properties: {
|
|
28958
|
+
fixId: {
|
|
28959
|
+
type: "string",
|
|
28960
|
+
description: 'Fix id from a previous "Interactive fix" prompt.'
|
|
28961
|
+
},
|
|
28962
|
+
answers: {
|
|
28963
|
+
type: "array",
|
|
28964
|
+
items: {
|
|
28965
|
+
type: "object",
|
|
28966
|
+
properties: {
|
|
28967
|
+
key: { type: "string", description: "FixQuestion key." },
|
|
28968
|
+
value: {
|
|
28969
|
+
type: "string",
|
|
28970
|
+
description: "Decided value (SELECT must match an option exactly)."
|
|
28971
|
+
}
|
|
28972
|
+
},
|
|
28973
|
+
required: ["key", "value"]
|
|
28974
|
+
}
|
|
28975
|
+
}
|
|
28976
|
+
},
|
|
28977
|
+
required: ["fixId", "answers"]
|
|
28978
|
+
},
|
|
28979
|
+
description: "[Optional] When supplied (including []), skips scanning. Non-empty: apply interactive fixes with answers. Empty []: abstain from all interactive fixes without rescanning. Omit for scan mode."
|
|
28494
28980
|
}
|
|
28495
28981
|
},
|
|
28496
28982
|
required: ["path"]
|
|
@@ -28501,7 +28987,8 @@ Example payload:
|
|
|
28501
28987
|
}
|
|
28502
28988
|
async executeInternal(args) {
|
|
28503
28989
|
logDebug(`Executing tool: ${this.name}`, {
|
|
28504
|
-
path: args.path
|
|
28990
|
+
path: args.path,
|
|
28991
|
+
mode: args.interactiveAnswers === void 0 ? "scan" : args.interactiveAnswers.length === 0 ? "apply-interactive-abstain-all" : "apply-with-answers"
|
|
28505
28992
|
});
|
|
28506
28993
|
if (!args.path) {
|
|
28507
28994
|
throw new Error("Invalid arguments: Missing required parameter 'path'");
|
|
@@ -28513,6 +29000,26 @@ Example payload:
|
|
|
28513
29000
|
);
|
|
28514
29001
|
}
|
|
28515
29002
|
const path37 = pathValidationResult.path;
|
|
29003
|
+
if (args.interactiveAnswers !== void 0) {
|
|
29004
|
+
if (args.interactiveAnswers.length === 0) {
|
|
29005
|
+
return this.createSuccessResponse(
|
|
29006
|
+
interactiveAnswersAbstainAllToolResponse
|
|
29007
|
+
);
|
|
29008
|
+
}
|
|
29009
|
+
try {
|
|
29010
|
+
const result = await this.vulnerabilityFixService.applyInteractiveAnswers({
|
|
29011
|
+
interactiveAnswers: args.interactiveAnswers,
|
|
29012
|
+
repositoryPath: path37
|
|
29013
|
+
});
|
|
29014
|
+
return this.createSuccessResponse(result);
|
|
29015
|
+
} catch (error) {
|
|
29016
|
+
const message = error.message;
|
|
29017
|
+
logError("Tool execution failed (apply-with-answers)", {
|
|
29018
|
+
error: message
|
|
29019
|
+
});
|
|
29020
|
+
return this.createSuccessResponse(message);
|
|
29021
|
+
}
|
|
29022
|
+
}
|
|
28516
29023
|
const files = await getLocalFiles({
|
|
28517
29024
|
path: path37,
|
|
28518
29025
|
maxFileSize: MCP_MAX_FILE_SIZE,
|