mobbdev 1.2.8 → 1.2.21
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/args/commands/upload_ai_blame.mjs +41 -5
- package/dist/index.mjs +495 -263
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -120,10 +120,13 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
120
120
|
},
|
|
121
121
|
streamCommitBlameRequests(variables, requestHeaders, signal) {
|
|
122
122
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: StreamCommitBlameRequestsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "streamCommitBlameRequests", "subscription", variables);
|
|
123
|
+
},
|
|
124
|
+
ScanSkill(variables, requestHeaders, signal) {
|
|
125
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: ScanSkillDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "ScanSkill", "mutation", variables);
|
|
123
126
|
}
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
|
-
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, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, defaultWrapper;
|
|
129
|
+
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, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, defaultWrapper;
|
|
127
130
|
var init_client_generates = __esm({
|
|
128
131
|
"src/features/analysis/scm/generates/client_generates.ts"() {
|
|
129
132
|
"use strict";
|
|
@@ -277,6 +280,7 @@ var init_client_generates = __esm({
|
|
|
277
280
|
IssueType_Enum2["MissingTemplateStringIndicator"] = "MISSING_TEMPLATE_STRING_INDICATOR";
|
|
278
281
|
IssueType_Enum2["MissingUser"] = "MISSING_USER";
|
|
279
282
|
IssueType_Enum2["MissingWhitespace"] = "MISSING_WHITESPACE";
|
|
283
|
+
IssueType_Enum2["MissingWorkflowPermissions"] = "MISSING_WORKFLOW_PERMISSIONS";
|
|
280
284
|
IssueType_Enum2["ModifiedDefaultParam"] = "MODIFIED_DEFAULT_PARAM";
|
|
281
285
|
IssueType_Enum2["NonFinalPublicStaticField"] = "NON_FINAL_PUBLIC_STATIC_FIELD";
|
|
282
286
|
IssueType_Enum2["NonReadonlyField"] = "NON_READONLY_FIELD";
|
|
@@ -1196,6 +1200,32 @@ var init_client_generates = __esm({
|
|
|
1196
1200
|
completedOn
|
|
1197
1201
|
error
|
|
1198
1202
|
}
|
|
1203
|
+
}
|
|
1204
|
+
`;
|
|
1205
|
+
ScanSkillDocument = `
|
|
1206
|
+
mutation ScanSkill($skillUrl: String!) {
|
|
1207
|
+
scanSkill(skillUrl: $skillUrl) {
|
|
1208
|
+
skillName
|
|
1209
|
+
skillHash
|
|
1210
|
+
skillVersion
|
|
1211
|
+
verdict
|
|
1212
|
+
findingsCount
|
|
1213
|
+
findings {
|
|
1214
|
+
category
|
|
1215
|
+
severity
|
|
1216
|
+
ruleId
|
|
1217
|
+
explanation
|
|
1218
|
+
evidence
|
|
1219
|
+
filePath
|
|
1220
|
+
lineNumber
|
|
1221
|
+
confidence
|
|
1222
|
+
layer
|
|
1223
|
+
}
|
|
1224
|
+
scanDurationMs
|
|
1225
|
+
layersExecuted
|
|
1226
|
+
cached
|
|
1227
|
+
summary
|
|
1228
|
+
}
|
|
1199
1229
|
}
|
|
1200
1230
|
`;
|
|
1201
1231
|
defaultWrapper = (action, _operationName, _operationType, _variables) => action();
|
|
@@ -1380,7 +1410,8 @@ var init_getIssueType = __esm({
|
|
|
1380
1410
|
["RETURN_IN_INIT" /* ReturnInInit */]: "Return in Init",
|
|
1381
1411
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: "Action Not Pinned to Commit Sha",
|
|
1382
1412
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default",
|
|
1383
|
-
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check"
|
|
1413
|
+
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
|
|
1414
|
+
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions"
|
|
1384
1415
|
};
|
|
1385
1416
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1386
1417
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -1507,6 +1538,7 @@ var init_fix = __esm({
|
|
|
1507
1538
|
});
|
|
1508
1539
|
IssueSharedStateZ = z7.object({
|
|
1509
1540
|
id: z7.string(),
|
|
1541
|
+
createdAt: z7.string(),
|
|
1510
1542
|
isArchived: z7.boolean(),
|
|
1511
1543
|
ticketIntegrationId: z7.string().nullable(),
|
|
1512
1544
|
ticketIntegrations: z7.array(
|
|
@@ -3801,7 +3833,7 @@ ${rootContent}`;
|
|
|
3801
3833
|
}
|
|
3802
3834
|
}
|
|
3803
3835
|
/**
|
|
3804
|
-
* Gets local commit data including diff
|
|
3836
|
+
* Gets local commit data including diff and timestamp.
|
|
3805
3837
|
* Used by Tracy extension to send commit data directly without requiring SCM token.
|
|
3806
3838
|
* @param commitSha The commit SHA to get data for
|
|
3807
3839
|
* @param maxDiffSizeBytes Maximum diff size in bytes (default 3MB). Returns null if exceeded.
|
|
@@ -3813,7 +3845,7 @@ ${rootContent}`;
|
|
|
3813
3845
|
const DIFF_DELIMITER = "---MOBB_DIFF_START---";
|
|
3814
3846
|
const output = await this.git.show([
|
|
3815
3847
|
commitSha,
|
|
3816
|
-
`--format=%cI%n
|
|
3848
|
+
`--format=%cI%n${DIFF_DELIMITER}`,
|
|
3817
3849
|
"--patch"
|
|
3818
3850
|
]);
|
|
3819
3851
|
const delimiterIndex = output.indexOf(DIFF_DELIMITER);
|
|
@@ -3868,7 +3900,7 @@ import Debug19 from "debug";
|
|
|
3868
3900
|
import { hideBin } from "yargs/helpers";
|
|
3869
3901
|
|
|
3870
3902
|
// src/args/yargs.ts
|
|
3871
|
-
import
|
|
3903
|
+
import chalk15 from "chalk";
|
|
3872
3904
|
import yargs from "yargs/yargs";
|
|
3873
3905
|
|
|
3874
3906
|
// src/args/commands/convert_to_sarif.ts
|
|
@@ -4241,7 +4273,8 @@ var fixDetailsData = {
|
|
|
4241
4273
|
["RETURN_IN_INIT" /* ReturnInInit */]: void 0,
|
|
4242
4274
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: void 0,
|
|
4243
4275
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0,
|
|
4244
|
-
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0
|
|
4276
|
+
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
|
|
4277
|
+
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0
|
|
4245
4278
|
};
|
|
4246
4279
|
|
|
4247
4280
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -6763,7 +6796,7 @@ async function getAdoSdk(params) {
|
|
|
6763
6796
|
const url = new URL(repoUrl);
|
|
6764
6797
|
const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
6765
6798
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
6766
|
-
const
|
|
6799
|
+
const path26 = [
|
|
6767
6800
|
prefixPath,
|
|
6768
6801
|
owner,
|
|
6769
6802
|
projectName,
|
|
@@ -6774,7 +6807,7 @@ async function getAdoSdk(params) {
|
|
|
6774
6807
|
"items",
|
|
6775
6808
|
"items"
|
|
6776
6809
|
].filter(Boolean).join("/");
|
|
6777
|
-
return new URL(`${
|
|
6810
|
+
return new URL(`${path26}?${params2}`, origin2).toString();
|
|
6778
6811
|
},
|
|
6779
6812
|
async getAdoBranchList({ repoUrl }) {
|
|
6780
6813
|
try {
|
|
@@ -8318,7 +8351,7 @@ function getGithubSdk(params = {}) {
|
|
|
8318
8351
|
if (res.status === 204) {
|
|
8319
8352
|
return true;
|
|
8320
8353
|
}
|
|
8321
|
-
} catch (
|
|
8354
|
+
} catch (_collaboratorError) {
|
|
8322
8355
|
try {
|
|
8323
8356
|
const permissionRes = await octokit.rest.repos.getCollaboratorPermissionLevel({
|
|
8324
8357
|
owner,
|
|
@@ -8328,16 +8361,16 @@ function getGithubSdk(params = {}) {
|
|
|
8328
8361
|
if (permissionRes.data.permission !== "none") {
|
|
8329
8362
|
return true;
|
|
8330
8363
|
}
|
|
8331
|
-
} catch (
|
|
8364
|
+
} catch (_permissionError) {
|
|
8332
8365
|
try {
|
|
8333
8366
|
await octokit.rest.repos.get({ owner, repo });
|
|
8334
8367
|
return true;
|
|
8335
|
-
} catch (
|
|
8368
|
+
} catch (_repoError) {
|
|
8336
8369
|
return false;
|
|
8337
8370
|
}
|
|
8338
8371
|
}
|
|
8339
8372
|
}
|
|
8340
|
-
} catch (
|
|
8373
|
+
} catch (_e) {
|
|
8341
8374
|
return false;
|
|
8342
8375
|
}
|
|
8343
8376
|
return false;
|
|
@@ -8378,7 +8411,7 @@ function getGithubSdk(params = {}) {
|
|
|
8378
8411
|
branch
|
|
8379
8412
|
});
|
|
8380
8413
|
return branch === res.data.name;
|
|
8381
|
-
} catch (
|
|
8414
|
+
} catch (_e) {
|
|
8382
8415
|
return false;
|
|
8383
8416
|
}
|
|
8384
8417
|
},
|
|
@@ -8573,7 +8606,7 @@ function getGithubSdk(params = {}) {
|
|
|
8573
8606
|
const sourceFileContentResponse = await octokit.rest.repos.getContent({
|
|
8574
8607
|
owner: sourceOwner,
|
|
8575
8608
|
repo: sourceRepo,
|
|
8576
|
-
path:
|
|
8609
|
+
path: `/${sourceFilePath}`
|
|
8577
8610
|
});
|
|
8578
8611
|
const { data: repository } = await octokit.rest.repos.get({ owner, repo });
|
|
8579
8612
|
const defaultBranch = repository.default_branch;
|
|
@@ -8601,7 +8634,7 @@ function getGithubSdk(params = {}) {
|
|
|
8601
8634
|
const secondFileContentResponse = await octokit.rest.repos.getContent({
|
|
8602
8635
|
owner: sourceOwner,
|
|
8603
8636
|
repo: sourceRepo,
|
|
8604
|
-
path:
|
|
8637
|
+
path: `/${secondFilePath}`
|
|
8605
8638
|
});
|
|
8606
8639
|
const secondDecodedContent = Buffer.from(
|
|
8607
8640
|
// Check if file content exists and handle different response types
|
|
@@ -10369,6 +10402,7 @@ async function getGitlabMergeRequestDiff({
|
|
|
10369
10402
|
title: mr.title,
|
|
10370
10403
|
description: mr.description || void 0,
|
|
10371
10404
|
commits,
|
|
10405
|
+
headCommitSha: mr.sha,
|
|
10372
10406
|
diffLines
|
|
10373
10407
|
};
|
|
10374
10408
|
}
|
|
@@ -11741,7 +11775,8 @@ var mobbCliCommand = {
|
|
|
11741
11775
|
claudeCodeInstallHook: "claude-code-install-hook",
|
|
11742
11776
|
claudeCodeProcessHook: "claude-code-process-hook",
|
|
11743
11777
|
windsurfIntellijInstallHook: "windsurf-intellij-install-hook",
|
|
11744
|
-
windsurfIntellijProcessHook: "windsurf-intellij-process-hook"
|
|
11778
|
+
windsurfIntellijProcessHook: "windsurf-intellij-process-hook",
|
|
11779
|
+
scanSkill: "scan-skill"
|
|
11745
11780
|
};
|
|
11746
11781
|
var ScanContext = {
|
|
11747
11782
|
FULL_SCAN: "FULL_SCAN",
|
|
@@ -11752,10 +11787,11 @@ var ScanContext = {
|
|
|
11752
11787
|
};
|
|
11753
11788
|
|
|
11754
11789
|
// src/args/commands/analyze.ts
|
|
11755
|
-
import
|
|
11756
|
-
import
|
|
11790
|
+
import fs12 from "fs";
|
|
11791
|
+
import chalk9 from "chalk";
|
|
11757
11792
|
|
|
11758
11793
|
// src/commands/index.ts
|
|
11794
|
+
import chalk7 from "chalk";
|
|
11759
11795
|
import chalkAnimation from "chalk-animation";
|
|
11760
11796
|
|
|
11761
11797
|
// src/features/analysis/index.ts
|
|
@@ -12541,6 +12577,9 @@ var GQLClient = class {
|
|
|
12541
12577
|
async getTracyDiffUploadUrl(variables) {
|
|
12542
12578
|
return await this._clientSdk.GetTracyDiffUploadUrl(variables);
|
|
12543
12579
|
}
|
|
12580
|
+
async scanSkill(variables) {
|
|
12581
|
+
return await this._clientSdk.ScanSkill(variables);
|
|
12582
|
+
}
|
|
12544
12583
|
};
|
|
12545
12584
|
|
|
12546
12585
|
// src/mcp/services/types.ts
|
|
@@ -13146,7 +13185,7 @@ async function postIssueComment(params) {
|
|
|
13146
13185
|
fpDescription
|
|
13147
13186
|
} = params;
|
|
13148
13187
|
const {
|
|
13149
|
-
path:
|
|
13188
|
+
path: path26,
|
|
13150
13189
|
startLine,
|
|
13151
13190
|
vulnerabilityReportIssue: {
|
|
13152
13191
|
vulnerabilityReportIssueTags,
|
|
@@ -13161,7 +13200,7 @@ async function postIssueComment(params) {
|
|
|
13161
13200
|
Refresh the page in order to see the changes.`,
|
|
13162
13201
|
pull_number: pullRequest,
|
|
13163
13202
|
commit_id: commitSha,
|
|
13164
|
-
path:
|
|
13203
|
+
path: path26,
|
|
13165
13204
|
line: startLine
|
|
13166
13205
|
});
|
|
13167
13206
|
const commentId = commentRes.data.id;
|
|
@@ -13195,7 +13234,7 @@ async function postFixComment(params) {
|
|
|
13195
13234
|
scanner
|
|
13196
13235
|
} = params;
|
|
13197
13236
|
const {
|
|
13198
|
-
path:
|
|
13237
|
+
path: path26,
|
|
13199
13238
|
startLine,
|
|
13200
13239
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
13201
13240
|
vulnerabilityReportIssueId
|
|
@@ -13213,7 +13252,7 @@ async function postFixComment(params) {
|
|
|
13213
13252
|
Refresh the page in order to see the changes.`,
|
|
13214
13253
|
pull_number: pullRequest,
|
|
13215
13254
|
commit_id: commitSha,
|
|
13216
|
-
path:
|
|
13255
|
+
path: path26,
|
|
13217
13256
|
line: startLine
|
|
13218
13257
|
});
|
|
13219
13258
|
const commentId = commentRes.data.id;
|
|
@@ -14776,6 +14815,85 @@ async function waitForAnaysisAndReviewPr({
|
|
|
14776
14815
|
}
|
|
14777
14816
|
}
|
|
14778
14817
|
|
|
14818
|
+
// src/commands/scan_skill_input.ts
|
|
14819
|
+
import fs11 from "fs";
|
|
14820
|
+
import path10 from "path";
|
|
14821
|
+
import AdmZip2 from "adm-zip";
|
|
14822
|
+
var LOCAL_SKILL_ZIP_DATA_URL_PREFIX = "data:application/zip;base64,";
|
|
14823
|
+
var MAX_LOCAL_SKILL_ARCHIVE_BYTES = 2 * 1024 * 1024;
|
|
14824
|
+
async function resolveSkillScanInput(skillInput) {
|
|
14825
|
+
const resolvedPath = path10.resolve(skillInput);
|
|
14826
|
+
if (!fs11.existsSync(resolvedPath)) {
|
|
14827
|
+
return skillInput;
|
|
14828
|
+
}
|
|
14829
|
+
const stat = fs11.statSync(resolvedPath);
|
|
14830
|
+
if (!stat.isDirectory()) {
|
|
14831
|
+
throw new CliError(
|
|
14832
|
+
"Local skill input must be a directory containing SKILL.md"
|
|
14833
|
+
);
|
|
14834
|
+
}
|
|
14835
|
+
return packageLocalSkillDirectory(resolvedPath);
|
|
14836
|
+
}
|
|
14837
|
+
function packageLocalSkillDirectory(directoryPath) {
|
|
14838
|
+
const zip = new AdmZip2();
|
|
14839
|
+
const rootPath = path10.resolve(directoryPath);
|
|
14840
|
+
const filePaths = listDirectoryFiles(rootPath);
|
|
14841
|
+
let hasSkillMd = false;
|
|
14842
|
+
for (const relativePath of filePaths) {
|
|
14843
|
+
const fullPath = path10.join(rootPath, relativePath);
|
|
14844
|
+
const normalizedFullPath = path10.resolve(fullPath);
|
|
14845
|
+
if (normalizedFullPath !== rootPath && !normalizedFullPath.startsWith(rootPath + path10.sep)) {
|
|
14846
|
+
continue;
|
|
14847
|
+
}
|
|
14848
|
+
const fileContent = fs11.readFileSync(normalizedFullPath);
|
|
14849
|
+
zip.addFile(relativePath, fileContent);
|
|
14850
|
+
if (path10.basename(relativePath).toLowerCase() === "skill.md") {
|
|
14851
|
+
hasSkillMd = true;
|
|
14852
|
+
}
|
|
14853
|
+
}
|
|
14854
|
+
if (!hasSkillMd) {
|
|
14855
|
+
throw new CliError("Local skill directory must contain a SKILL.md file");
|
|
14856
|
+
}
|
|
14857
|
+
const zipBuffer = zip.toBuffer();
|
|
14858
|
+
if (zipBuffer.length > MAX_LOCAL_SKILL_ARCHIVE_BYTES) {
|
|
14859
|
+
throw new CliError(
|
|
14860
|
+
`Local skill directory is too large to scan via CLI (${zipBuffer.length} bytes > ${MAX_LOCAL_SKILL_ARCHIVE_BYTES} bytes)`
|
|
14861
|
+
);
|
|
14862
|
+
}
|
|
14863
|
+
return `${LOCAL_SKILL_ZIP_DATA_URL_PREFIX}${zipBuffer.toString("base64")}`;
|
|
14864
|
+
}
|
|
14865
|
+
function listDirectoryFiles(rootPath) {
|
|
14866
|
+
const files = [];
|
|
14867
|
+
function walk(relativeDir) {
|
|
14868
|
+
const absoluteDir = path10.join(rootPath, relativeDir);
|
|
14869
|
+
const entries = fs11.readdirSync(absoluteDir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
14870
|
+
for (const entry of entries) {
|
|
14871
|
+
const safeEntryName = path10.basename(entry.name).replace(/\0/g, "");
|
|
14872
|
+
if (!safeEntryName) {
|
|
14873
|
+
continue;
|
|
14874
|
+
}
|
|
14875
|
+
const relativePath = relativeDir ? path10.posix.join(relativeDir, safeEntryName) : safeEntryName;
|
|
14876
|
+
const absolutePath = path10.join(rootPath, relativePath);
|
|
14877
|
+
const normalizedAbsolutePath = path10.resolve(absolutePath);
|
|
14878
|
+
if (normalizedAbsolutePath !== rootPath && !normalizedAbsolutePath.startsWith(rootPath + path10.sep)) {
|
|
14879
|
+
continue;
|
|
14880
|
+
}
|
|
14881
|
+
if (entry.isSymbolicLink()) {
|
|
14882
|
+
continue;
|
|
14883
|
+
}
|
|
14884
|
+
if (entry.isDirectory()) {
|
|
14885
|
+
walk(relativePath);
|
|
14886
|
+
continue;
|
|
14887
|
+
}
|
|
14888
|
+
if (entry.isFile()) {
|
|
14889
|
+
files.push(relativePath);
|
|
14890
|
+
}
|
|
14891
|
+
}
|
|
14892
|
+
}
|
|
14893
|
+
walk("");
|
|
14894
|
+
return files;
|
|
14895
|
+
}
|
|
14896
|
+
|
|
14779
14897
|
// src/commands/index.ts
|
|
14780
14898
|
async function review(params, { skipPrompts = true } = {}) {
|
|
14781
14899
|
const {
|
|
@@ -14901,10 +15019,80 @@ async function showWelcomeMessage(skipPrompts = false) {
|
|
|
14901
15019
|
skipPrompts ? await sleep(100) : await sleep(2e3);
|
|
14902
15020
|
welcome.stop();
|
|
14903
15021
|
}
|
|
15022
|
+
var VERDICT_COLORS = {
|
|
15023
|
+
BENIGN: "green",
|
|
15024
|
+
WARNING: "yellow",
|
|
15025
|
+
SUSPICIOUS: "magenta",
|
|
15026
|
+
MALICIOUS: "red"
|
|
15027
|
+
};
|
|
15028
|
+
var SEVERITY_COLORS = {
|
|
15029
|
+
CRITICAL: "red",
|
|
15030
|
+
HIGH: "magenta",
|
|
15031
|
+
MEDIUM: "yellow",
|
|
15032
|
+
LOW: "cyan"
|
|
15033
|
+
};
|
|
15034
|
+
async function scanSkill(options) {
|
|
15035
|
+
const { url, apiKey, ci } = options;
|
|
15036
|
+
const gqlClient = await getAuthenticatedGQLClient({
|
|
15037
|
+
inputApiKey: apiKey,
|
|
15038
|
+
isSkipPrompts: ci
|
|
15039
|
+
});
|
|
15040
|
+
console.log(chalk7.dim(`Scanning skill: ${url}`));
|
|
15041
|
+
console.log();
|
|
15042
|
+
const skillUrl = await resolveSkillScanInput(url);
|
|
15043
|
+
const result = await gqlClient.scanSkill({ skillUrl });
|
|
15044
|
+
const scan2 = result.scanSkill;
|
|
15045
|
+
const verdictColor = VERDICT_COLORS[scan2.verdict] ?? "white";
|
|
15046
|
+
console.log(
|
|
15047
|
+
chalk7.bold(`Verdict: `) + chalk7[verdictColor](
|
|
15048
|
+
scan2.verdict
|
|
15049
|
+
)
|
|
15050
|
+
);
|
|
15051
|
+
console.log(`Skill: ${scan2.skillName}`);
|
|
15052
|
+
if (scan2.skillVersion) {
|
|
15053
|
+
console.log(`Version: ${scan2.skillVersion}`);
|
|
15054
|
+
}
|
|
15055
|
+
console.log(`Hash: ${scan2.skillHash ?? "N/A"}`);
|
|
15056
|
+
console.log(`Findings: ${scan2.findingsCount}`);
|
|
15057
|
+
console.log(`Duration: ${scan2.scanDurationMs}ms`);
|
|
15058
|
+
if (scan2.cached) {
|
|
15059
|
+
console.log(chalk7.dim("(cached result)"));
|
|
15060
|
+
}
|
|
15061
|
+
console.log();
|
|
15062
|
+
if (scan2.findings.length > 0) {
|
|
15063
|
+
console.log(chalk7.bold("Findings:"));
|
|
15064
|
+
console.log();
|
|
15065
|
+
for (const f of scan2.findings) {
|
|
15066
|
+
const sevColor = SEVERITY_COLORS[f.severity] ?? "white";
|
|
15067
|
+
const location = [f.filePath, f.lineNumber].filter(Boolean).join(":");
|
|
15068
|
+
console.log(
|
|
15069
|
+
` ${chalk7[sevColor](f.severity)} [${f.layer}] ${f.category}${f.ruleId ? ` (${f.ruleId})` : ""}`
|
|
15070
|
+
);
|
|
15071
|
+
if (location) {
|
|
15072
|
+
console.log(` ${chalk7.dim(location)}`);
|
|
15073
|
+
}
|
|
15074
|
+
console.log(` ${f.explanation}`);
|
|
15075
|
+
if (f.evidence) {
|
|
15076
|
+
console.log(
|
|
15077
|
+
` ${String(chalk7.dim("Evidence: " + f.evidence.slice(0, 120))).replace(/\n|\r/g, "")}`
|
|
15078
|
+
);
|
|
15079
|
+
}
|
|
15080
|
+
console.log();
|
|
15081
|
+
}
|
|
15082
|
+
}
|
|
15083
|
+
if (scan2.summary) {
|
|
15084
|
+
console.log(chalk7.bold("Analysis:"));
|
|
15085
|
+
console.log(` ${scan2.summary}`);
|
|
15086
|
+
console.log();
|
|
15087
|
+
}
|
|
15088
|
+
if (scan2.verdict === "MALICIOUS" || scan2.verdict === "SUSPICIOUS") {
|
|
15089
|
+
process.exit(2);
|
|
15090
|
+
}
|
|
15091
|
+
}
|
|
14904
15092
|
|
|
14905
15093
|
// src/args/validation.ts
|
|
14906
|
-
import
|
|
14907
|
-
import
|
|
15094
|
+
import chalk8 from "chalk";
|
|
15095
|
+
import path11 from "path";
|
|
14908
15096
|
import { z as z30 } from "zod";
|
|
14909
15097
|
function throwRepoUrlErrorMessage({
|
|
14910
15098
|
error,
|
|
@@ -14913,11 +15101,11 @@ function throwRepoUrlErrorMessage({
|
|
|
14913
15101
|
}) {
|
|
14914
15102
|
const errorMessage = error.issues[error.issues.length - 1]?.message;
|
|
14915
15103
|
const formattedErrorMessage = `
|
|
14916
|
-
Error: ${
|
|
15104
|
+
Error: ${chalk8.bold(
|
|
14917
15105
|
repoUrl
|
|
14918
15106
|
)} is ${errorMessage}
|
|
14919
15107
|
Example:
|
|
14920
|
-
mobbdev ${command} -r ${
|
|
15108
|
+
mobbdev ${command} -r ${chalk8.bold(
|
|
14921
15109
|
"https://github.com/WebGoat/WebGoat"
|
|
14922
15110
|
)}`;
|
|
14923
15111
|
throw new CliError(formattedErrorMessage);
|
|
@@ -14948,12 +15136,12 @@ function validateRepoUrl(args) {
|
|
|
14948
15136
|
}
|
|
14949
15137
|
var supportExtensions = [".json", ".xml", ".fpr", ".sarif", ".zip"];
|
|
14950
15138
|
function validateReportFileFormat(reportFile) {
|
|
14951
|
-
if (!supportExtensions.includes(
|
|
15139
|
+
if (!supportExtensions.includes(path11.extname(reportFile))) {
|
|
14952
15140
|
throw new CliError(
|
|
14953
15141
|
`
|
|
14954
|
-
${
|
|
15142
|
+
${chalk8.bold(
|
|
14955
15143
|
reportFile
|
|
14956
|
-
)} is not a supported file extension. Supported extensions are: ${
|
|
15144
|
+
)} is not a supported file extension. Supported extensions are: ${chalk8.bold(
|
|
14957
15145
|
supportExtensions.join(", ")
|
|
14958
15146
|
)}
|
|
14959
15147
|
`
|
|
@@ -14966,22 +15154,22 @@ function analyzeBuilder(yargs2) {
|
|
|
14966
15154
|
return yargs2.option("f", {
|
|
14967
15155
|
alias: "scan-file",
|
|
14968
15156
|
type: "string",
|
|
14969
|
-
describe:
|
|
15157
|
+
describe: chalk9.bold(
|
|
14970
15158
|
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog)"
|
|
14971
15159
|
)
|
|
14972
15160
|
}).option("repo", repoOption).option("p", {
|
|
14973
15161
|
alias: "src-path",
|
|
14974
|
-
describe:
|
|
15162
|
+
describe: chalk9.bold(
|
|
14975
15163
|
"Path to the repository folder with the source code; alternatively, you can specify the Fortify FPR file to extract source code out of it"
|
|
14976
15164
|
),
|
|
14977
15165
|
type: "string"
|
|
14978
15166
|
}).option("ref", refOption).option("ch", {
|
|
14979
15167
|
alias: "commit-hash",
|
|
14980
|
-
describe:
|
|
15168
|
+
describe: chalk9.bold("Hash of the commit"),
|
|
14981
15169
|
type: "string"
|
|
14982
15170
|
}).option("mobb-project-name", mobbProjectNameOption).option("y", yesOption).option("ci", ciOption).option("org", organizationIdOptions).option("api-key", apiKeyOption).option("commit-hash", commitHashOption).option("auto-pr", autoPrOption).option("create-one-pr", createOnePrOption).option("commit-directly", commitDirectlyOption).option("pull-request", {
|
|
14983
15171
|
alias: ["pr", "pr-number", "pr-id"],
|
|
14984
|
-
describe:
|
|
15172
|
+
describe: chalk9.bold("Number of the pull request"),
|
|
14985
15173
|
type: "number",
|
|
14986
15174
|
demandOption: false
|
|
14987
15175
|
}).option("polling", pollingOption).example(
|
|
@@ -14990,9 +15178,9 @@ function analyzeBuilder(yargs2) {
|
|
|
14990
15178
|
).help();
|
|
14991
15179
|
}
|
|
14992
15180
|
function validateAnalyzeOptions(argv) {
|
|
14993
|
-
if (argv.f && !
|
|
15181
|
+
if (argv.f && !fs12.existsSync(argv.f)) {
|
|
14994
15182
|
throw new CliError(`
|
|
14995
|
-
Can't access ${
|
|
15183
|
+
Can't access ${chalk9.bold(argv.f)}`);
|
|
14996
15184
|
}
|
|
14997
15185
|
validateOrganizationId(argv.organizationId);
|
|
14998
15186
|
if (!argv.srcPath && !argv.repo) {
|
|
@@ -15039,8 +15227,8 @@ import { z as z32 } from "zod";
|
|
|
15039
15227
|
// src/args/commands/upload_ai_blame.ts
|
|
15040
15228
|
import fsPromises3 from "fs/promises";
|
|
15041
15229
|
import * as os3 from "os";
|
|
15042
|
-
import
|
|
15043
|
-
import
|
|
15230
|
+
import path12 from "path";
|
|
15231
|
+
import chalk10 from "chalk";
|
|
15044
15232
|
import { withFile } from "tmp-promise";
|
|
15045
15233
|
import z31 from "zod";
|
|
15046
15234
|
init_client_generates();
|
|
@@ -15360,33 +15548,33 @@ function uploadAiBlameBuilder(args) {
|
|
|
15360
15548
|
type: "string",
|
|
15361
15549
|
array: true,
|
|
15362
15550
|
demandOption: true,
|
|
15363
|
-
describe:
|
|
15551
|
+
describe: chalk10.bold("Path(s) to prompt artifact(s) (one per session)")
|
|
15364
15552
|
}).option("inference", {
|
|
15365
15553
|
type: "string",
|
|
15366
15554
|
array: true,
|
|
15367
15555
|
demandOption: true,
|
|
15368
|
-
describe:
|
|
15556
|
+
describe: chalk10.bold(
|
|
15369
15557
|
"Path(s) to inference artifact(s) (one per session)"
|
|
15370
15558
|
)
|
|
15371
15559
|
}).option("ai-response-at", {
|
|
15372
15560
|
type: "string",
|
|
15373
15561
|
array: true,
|
|
15374
|
-
describe:
|
|
15562
|
+
describe: chalk10.bold(
|
|
15375
15563
|
"ISO timestamp(s) for AI response (one per session, defaults to now)"
|
|
15376
15564
|
)
|
|
15377
15565
|
}).option("model", {
|
|
15378
15566
|
type: "string",
|
|
15379
15567
|
array: true,
|
|
15380
|
-
describe:
|
|
15568
|
+
describe: chalk10.bold("AI model name(s) (optional, one per session)")
|
|
15381
15569
|
}).option("tool-name", {
|
|
15382
15570
|
type: "string",
|
|
15383
15571
|
array: true,
|
|
15384
|
-
describe:
|
|
15572
|
+
describe: chalk10.bold("Tool/IDE name(s) (optional, one per session)")
|
|
15385
15573
|
}).option("blame-type", {
|
|
15386
15574
|
type: "string",
|
|
15387
15575
|
array: true,
|
|
15388
15576
|
choices: Object.values(AiBlameInferenceType),
|
|
15389
|
-
describe:
|
|
15577
|
+
describe: chalk10.bold(
|
|
15390
15578
|
"Blame type(s) (optional, one per session, defaults to CHAT)"
|
|
15391
15579
|
)
|
|
15392
15580
|
}).strict();
|
|
@@ -15408,7 +15596,7 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
15408
15596
|
await withFile(async (promptFile) => {
|
|
15409
15597
|
const promptsResult = await sanitizeDataWithCounts(args.prompts);
|
|
15410
15598
|
promptsCounts = promptsResult.counts;
|
|
15411
|
-
promptsUUID =
|
|
15599
|
+
promptsUUID = path12.basename(promptFile.path, path12.extname(promptFile.path));
|
|
15412
15600
|
await fsPromises3.writeFile(
|
|
15413
15601
|
promptFile.path,
|
|
15414
15602
|
JSON.stringify(promptsResult.sanitizedData, null, 2),
|
|
@@ -15418,9 +15606,9 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
15418
15606
|
await withFile(async (inferenceFile) => {
|
|
15419
15607
|
const inferenceResult = await sanitizeDataWithCounts(args.inference);
|
|
15420
15608
|
inferenceCounts = inferenceResult.counts;
|
|
15421
|
-
inferenceUUID =
|
|
15609
|
+
inferenceUUID = path12.basename(
|
|
15422
15610
|
inferenceFile.path,
|
|
15423
|
-
|
|
15611
|
+
path12.extname(inferenceFile.path)
|
|
15424
15612
|
);
|
|
15425
15613
|
await fsPromises3.writeFile(
|
|
15426
15614
|
inferenceFile.path,
|
|
@@ -15468,7 +15656,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
15468
15656
|
const sessionIds = args.sessionId || args["session-id"] || [];
|
|
15469
15657
|
if (prompts.length !== inferences.length) {
|
|
15470
15658
|
const errorMsg = "prompt and inference must have the same number of entries";
|
|
15471
|
-
logger2.error(
|
|
15659
|
+
logger2.error(chalk10.red(errorMsg));
|
|
15472
15660
|
if (exitOnError) {
|
|
15473
15661
|
process.exit(1);
|
|
15474
15662
|
}
|
|
@@ -15488,15 +15676,15 @@ async function uploadAiBlameHandler(options) {
|
|
|
15488
15676
|
]);
|
|
15489
15677
|
} catch {
|
|
15490
15678
|
const errorMsg = `File not found for session ${i + 1}`;
|
|
15491
|
-
logger2.error(
|
|
15679
|
+
logger2.error(chalk10.red(errorMsg));
|
|
15492
15680
|
if (exitOnError) {
|
|
15493
15681
|
process.exit(1);
|
|
15494
15682
|
}
|
|
15495
15683
|
throw new Error(errorMsg);
|
|
15496
15684
|
}
|
|
15497
15685
|
sessions.push({
|
|
15498
|
-
promptFileName:
|
|
15499
|
-
inferenceFileName:
|
|
15686
|
+
promptFileName: path12.basename(promptPath),
|
|
15687
|
+
inferenceFileName: path12.basename(inferencePath),
|
|
15500
15688
|
aiResponseAt: responseTimes[i] || nowIso,
|
|
15501
15689
|
model: models[i],
|
|
15502
15690
|
toolName: tools[i],
|
|
@@ -15521,7 +15709,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
15521
15709
|
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
15522
15710
|
if (uploadSessions.length !== sessions.length) {
|
|
15523
15711
|
const errorMsg = "Init failed to return expected number of sessions";
|
|
15524
|
-
logger2.error(
|
|
15712
|
+
logger2.error(chalk10.red(errorMsg));
|
|
15525
15713
|
if (exitOnError) {
|
|
15526
15714
|
process.exit(1);
|
|
15527
15715
|
}
|
|
@@ -15578,7 +15766,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
15578
15766
|
if (status !== "OK") {
|
|
15579
15767
|
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
15580
15768
|
logger2.error(
|
|
15581
|
-
|
|
15769
|
+
chalk10.red(
|
|
15582
15770
|
`[UPLOAD] Finalize failed with status: ${status}, error: ${errorMsg}`
|
|
15583
15771
|
)
|
|
15584
15772
|
);
|
|
@@ -15587,7 +15775,7 @@ async function uploadAiBlameHandler(options) {
|
|
|
15587
15775
|
}
|
|
15588
15776
|
throw new Error(errorMsg);
|
|
15589
15777
|
}
|
|
15590
|
-
logger2.info(
|
|
15778
|
+
logger2.info(chalk10.green("[UPLOAD] AI Blame uploads finalized successfully"));
|
|
15591
15779
|
} catch (error) {
|
|
15592
15780
|
logger2.error("[UPLOAD] Finalize threw error:", error);
|
|
15593
15781
|
throw error;
|
|
@@ -15599,6 +15787,8 @@ async function uploadAiBlameCommandHandler(args) {
|
|
|
15599
15787
|
|
|
15600
15788
|
// src/features/claude_code/data_collector.ts
|
|
15601
15789
|
init_client_generates();
|
|
15790
|
+
init_GitService();
|
|
15791
|
+
init_urlParser2();
|
|
15602
15792
|
|
|
15603
15793
|
// src/features/claude_code/transcript_parser.ts
|
|
15604
15794
|
import fsPromises4 from "fs/promises";
|
|
@@ -15845,8 +16035,23 @@ async function collectHookData() {
|
|
|
15845
16035
|
tracePayload
|
|
15846
16036
|
};
|
|
15847
16037
|
}
|
|
16038
|
+
async function getRepositoryUrl2(cwd) {
|
|
16039
|
+
try {
|
|
16040
|
+
const gitService = new GitService(cwd);
|
|
16041
|
+
const isRepo = await gitService.isGitRepository();
|
|
16042
|
+
if (!isRepo) {
|
|
16043
|
+
return null;
|
|
16044
|
+
}
|
|
16045
|
+
const remoteUrl = await gitService.getRemoteUrl();
|
|
16046
|
+
const parsed = parseScmURL(remoteUrl);
|
|
16047
|
+
return parsed?.scmType === "GitHub" /* GitHub */ || parsed?.scmType === "GitLab" /* GitLab */ ? remoteUrl : null;
|
|
16048
|
+
} catch {
|
|
16049
|
+
return null;
|
|
16050
|
+
}
|
|
16051
|
+
}
|
|
15848
16052
|
async function processAndUploadHookData() {
|
|
15849
16053
|
const result = await collectHookData();
|
|
16054
|
+
const repositoryUrl = await getRepositoryUrl2(result.hookData.cwd);
|
|
15850
16055
|
let uploadSuccess;
|
|
15851
16056
|
try {
|
|
15852
16057
|
await uploadAiBlameHandlerFromExtension({
|
|
@@ -15856,7 +16061,8 @@ async function processAndUploadHookData() {
|
|
|
15856
16061
|
tool: result.tracePayload.tool,
|
|
15857
16062
|
responseTime: result.tracePayload.responseTime,
|
|
15858
16063
|
blameType: "CHAT" /* Chat */,
|
|
15859
|
-
sessionId: result.hookData.session_id
|
|
16064
|
+
sessionId: result.hookData.session_id,
|
|
16065
|
+
repositoryUrl
|
|
15860
16066
|
});
|
|
15861
16067
|
uploadSuccess = true;
|
|
15862
16068
|
} catch (error) {
|
|
@@ -15875,9 +16081,9 @@ async function processAndUploadHookData() {
|
|
|
15875
16081
|
// src/features/claude_code/install_hook.ts
|
|
15876
16082
|
import fsPromises5 from "fs/promises";
|
|
15877
16083
|
import os4 from "os";
|
|
15878
|
-
import
|
|
15879
|
-
import
|
|
15880
|
-
var CLAUDE_SETTINGS_PATH =
|
|
16084
|
+
import path13 from "path";
|
|
16085
|
+
import chalk11 from "chalk";
|
|
16086
|
+
var CLAUDE_SETTINGS_PATH = path13.join(os4.homedir(), ".claude", "settings.json");
|
|
15881
16087
|
async function claudeSettingsExists() {
|
|
15882
16088
|
try {
|
|
15883
16089
|
await fsPromises5.access(CLAUDE_SETTINGS_PATH);
|
|
@@ -15901,12 +16107,12 @@ async function writeClaudeSettings(settings) {
|
|
|
15901
16107
|
);
|
|
15902
16108
|
}
|
|
15903
16109
|
async function installMobbHooks(options = {}) {
|
|
15904
|
-
console.log(
|
|
16110
|
+
console.log(chalk11.blue("Installing Mobb hooks in Claude Code settings..."));
|
|
15905
16111
|
if (!await claudeSettingsExists()) {
|
|
15906
|
-
console.log(
|
|
15907
|
-
console.log(
|
|
15908
|
-
console.log(
|
|
15909
|
-
console.log(
|
|
16112
|
+
console.log(chalk11.red("\u274C Claude Code settings file not found"));
|
|
16113
|
+
console.log(chalk11.yellow(`Expected location: ${CLAUDE_SETTINGS_PATH}`));
|
|
16114
|
+
console.log(chalk11.yellow("Is Claude Code installed on your system?"));
|
|
16115
|
+
console.log(chalk11.yellow("Please install Claude Code and try again."));
|
|
15910
16116
|
throw new Error(
|
|
15911
16117
|
"Claude Code settings file not found. Is Claude Code installed?"
|
|
15912
16118
|
);
|
|
@@ -15930,7 +16136,7 @@ async function installMobbHooks(options = {}) {
|
|
|
15930
16136
|
if (envVars.length > 0) {
|
|
15931
16137
|
command = `${envVars.join(" ")} ${command}`;
|
|
15932
16138
|
console.log(
|
|
15933
|
-
|
|
16139
|
+
chalk11.blue(
|
|
15934
16140
|
`Adding environment variables to hook command: ${envVars.join(", ")}`
|
|
15935
16141
|
)
|
|
15936
16142
|
);
|
|
@@ -15951,15 +16157,15 @@ async function installMobbHooks(options = {}) {
|
|
|
15951
16157
|
)
|
|
15952
16158
|
);
|
|
15953
16159
|
if (existingHookIndex >= 0) {
|
|
15954
|
-
console.log(
|
|
16160
|
+
console.log(chalk11.yellow("Mobb hook already exists, updating..."));
|
|
15955
16161
|
settings.hooks.PostToolUse[existingHookIndex] = mobbHookConfig;
|
|
15956
16162
|
} else {
|
|
15957
|
-
console.log(
|
|
16163
|
+
console.log(chalk11.green("Adding new Mobb hook..."));
|
|
15958
16164
|
settings.hooks.PostToolUse.push(mobbHookConfig);
|
|
15959
16165
|
}
|
|
15960
16166
|
await writeClaudeSettings(settings);
|
|
15961
16167
|
console.log(
|
|
15962
|
-
|
|
16168
|
+
chalk11.green(
|
|
15963
16169
|
`\u2705 Mobb hooks ${options.saveEnv ? "and environment variables " : ""}installed successfully in ${CLAUDE_SETTINGS_PATH}`
|
|
15964
16170
|
)
|
|
15965
16171
|
);
|
|
@@ -16062,8 +16268,8 @@ var WorkspaceService = class {
|
|
|
16062
16268
|
* Sets a known workspace path that was discovered through successful validation
|
|
16063
16269
|
* @param path The validated workspace path to store
|
|
16064
16270
|
*/
|
|
16065
|
-
static setKnownWorkspacePath(
|
|
16066
|
-
this.knownWorkspacePath =
|
|
16271
|
+
static setKnownWorkspacePath(path26) {
|
|
16272
|
+
this.knownWorkspacePath = path26;
|
|
16067
16273
|
}
|
|
16068
16274
|
/**
|
|
16069
16275
|
* Gets the known workspace path that was previously validated
|
|
@@ -17093,9 +17299,9 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
17093
17299
|
|
|
17094
17300
|
// src/mcp/services/McpUsageService/host.ts
|
|
17095
17301
|
import { execSync as execSync2 } from "child_process";
|
|
17096
|
-
import
|
|
17302
|
+
import fs13 from "fs";
|
|
17097
17303
|
import os6 from "os";
|
|
17098
|
-
import
|
|
17304
|
+
import path14 from "path";
|
|
17099
17305
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
17100
17306
|
var runCommand = (cmd) => {
|
|
17101
17307
|
try {
|
|
@@ -17110,17 +17316,17 @@ var gitInfo = {
|
|
|
17110
17316
|
};
|
|
17111
17317
|
var getClaudeWorkspacePaths = () => {
|
|
17112
17318
|
const home = os6.homedir();
|
|
17113
|
-
const claudeIdePath =
|
|
17319
|
+
const claudeIdePath = path14.join(home, ".claude", "ide");
|
|
17114
17320
|
const workspacePaths = [];
|
|
17115
|
-
if (!
|
|
17321
|
+
if (!fs13.existsSync(claudeIdePath)) {
|
|
17116
17322
|
return workspacePaths;
|
|
17117
17323
|
}
|
|
17118
17324
|
try {
|
|
17119
|
-
const lockFiles =
|
|
17325
|
+
const lockFiles = fs13.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
17120
17326
|
for (const lockFile of lockFiles) {
|
|
17121
|
-
const lockFilePath =
|
|
17327
|
+
const lockFilePath = path14.join(claudeIdePath, lockFile);
|
|
17122
17328
|
try {
|
|
17123
|
-
const lockContent = JSON.parse(
|
|
17329
|
+
const lockContent = JSON.parse(fs13.readFileSync(lockFilePath, "utf8"));
|
|
17124
17330
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
17125
17331
|
workspacePaths.push(...lockContent.workspaceFolders);
|
|
17126
17332
|
}
|
|
@@ -17143,24 +17349,24 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
17143
17349
|
switch (hostName.toLowerCase()) {
|
|
17144
17350
|
case "cursor":
|
|
17145
17351
|
return [
|
|
17146
|
-
|
|
17352
|
+
path14.join(currentDir, ".cursor", "mcp.json"),
|
|
17147
17353
|
// local first
|
|
17148
|
-
|
|
17354
|
+
path14.join(home, ".cursor", "mcp.json")
|
|
17149
17355
|
];
|
|
17150
17356
|
case "windsurf":
|
|
17151
17357
|
return [
|
|
17152
|
-
|
|
17358
|
+
path14.join(currentDir, ".codeium", "mcp_config.json"),
|
|
17153
17359
|
// local first
|
|
17154
|
-
|
|
17360
|
+
path14.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
17155
17361
|
];
|
|
17156
17362
|
case "webstorm":
|
|
17157
17363
|
return [];
|
|
17158
17364
|
case "visualstudiocode":
|
|
17159
17365
|
case "vscode":
|
|
17160
17366
|
return [
|
|
17161
|
-
|
|
17367
|
+
path14.join(currentDir, ".vscode", "mcp.json"),
|
|
17162
17368
|
// local first
|
|
17163
|
-
process.platform === "win32" ?
|
|
17369
|
+
process.platform === "win32" ? path14.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path14.join(
|
|
17164
17370
|
home,
|
|
17165
17371
|
"Library",
|
|
17166
17372
|
"Application Support",
|
|
@@ -17171,13 +17377,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
17171
17377
|
];
|
|
17172
17378
|
case "claude": {
|
|
17173
17379
|
const claudePaths = [
|
|
17174
|
-
|
|
17380
|
+
path14.join(currentDir, ".claude.json"),
|
|
17175
17381
|
// local first
|
|
17176
|
-
|
|
17382
|
+
path14.join(home, ".claude.json")
|
|
17177
17383
|
];
|
|
17178
17384
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
17179
17385
|
for (const workspacePath of workspacePaths) {
|
|
17180
|
-
claudePaths.push(
|
|
17386
|
+
claudePaths.push(path14.join(workspacePath, ".mcp.json"));
|
|
17181
17387
|
}
|
|
17182
17388
|
return claudePaths;
|
|
17183
17389
|
}
|
|
@@ -17186,9 +17392,9 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
17186
17392
|
}
|
|
17187
17393
|
};
|
|
17188
17394
|
var readConfigFile = (filePath) => {
|
|
17189
|
-
if (!
|
|
17395
|
+
if (!fs13.existsSync(filePath)) return null;
|
|
17190
17396
|
try {
|
|
17191
|
-
return JSON.parse(
|
|
17397
|
+
return JSON.parse(fs13.readFileSync(filePath, "utf8"));
|
|
17192
17398
|
} catch (error) {
|
|
17193
17399
|
logWarn(`[UsageService] Failed to read MCP config: ${filePath}`);
|
|
17194
17400
|
return null;
|
|
@@ -17338,10 +17544,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
17338
17544
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
17339
17545
|
for (const ide of IDEs) {
|
|
17340
17546
|
const configPaths = getMCPConfigPaths(ide);
|
|
17341
|
-
configPaths.forEach((
|
|
17547
|
+
configPaths.forEach((path26) => ideConfigPaths.add(path26));
|
|
17342
17548
|
}
|
|
17343
17549
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
17344
|
-
(
|
|
17550
|
+
(path26) => !ideConfigPaths.has(path26)
|
|
17345
17551
|
);
|
|
17346
17552
|
for (const ide of IDEs) {
|
|
17347
17553
|
const cfg = readMCPConfig(ide);
|
|
@@ -17461,9 +17667,9 @@ init_configs();
|
|
|
17461
17667
|
|
|
17462
17668
|
// src/mcp/services/McpUsageService/system.ts
|
|
17463
17669
|
init_configs();
|
|
17464
|
-
import
|
|
17670
|
+
import fs14 from "fs";
|
|
17465
17671
|
import os7 from "os";
|
|
17466
|
-
import
|
|
17672
|
+
import path15 from "path";
|
|
17467
17673
|
var MAX_DEPTH = 2;
|
|
17468
17674
|
var patterns = ["mcp", "claude"];
|
|
17469
17675
|
var isFileMatch = (fileName) => {
|
|
@@ -17472,7 +17678,7 @@ var isFileMatch = (fileName) => {
|
|
|
17472
17678
|
};
|
|
17473
17679
|
var safeAccess = async (filePath) => {
|
|
17474
17680
|
try {
|
|
17475
|
-
await
|
|
17681
|
+
await fs14.promises.access(filePath, fs14.constants.R_OK);
|
|
17476
17682
|
return true;
|
|
17477
17683
|
} catch {
|
|
17478
17684
|
return false;
|
|
@@ -17481,9 +17687,9 @@ var safeAccess = async (filePath) => {
|
|
|
17481
17687
|
var searchDir = async (dir, depth = 0) => {
|
|
17482
17688
|
const results = [];
|
|
17483
17689
|
if (depth > MAX_DEPTH) return results;
|
|
17484
|
-
const entries = await
|
|
17690
|
+
const entries = await fs14.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
17485
17691
|
for (const entry of entries) {
|
|
17486
|
-
const fullPath =
|
|
17692
|
+
const fullPath = path15.join(dir, entry.name);
|
|
17487
17693
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
17488
17694
|
results.push(fullPath);
|
|
17489
17695
|
} else if (entry.isDirectory()) {
|
|
@@ -17500,14 +17706,14 @@ var findSystemMCPConfigs = async () => {
|
|
|
17500
17706
|
const home = os7.homedir();
|
|
17501
17707
|
const platform2 = os7.platform();
|
|
17502
17708
|
const knownDirs = platform2 === "win32" ? [
|
|
17503
|
-
|
|
17504
|
-
|
|
17505
|
-
|
|
17709
|
+
path15.join(home, ".cursor"),
|
|
17710
|
+
path15.join(home, "Documents"),
|
|
17711
|
+
path15.join(home, "Downloads")
|
|
17506
17712
|
] : [
|
|
17507
|
-
|
|
17508
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
17509
|
-
|
|
17510
|
-
|
|
17713
|
+
path15.join(home, ".cursor"),
|
|
17714
|
+
process.env["XDG_CONFIG_HOME"] || path15.join(home, ".config"),
|
|
17715
|
+
path15.join(home, "Documents"),
|
|
17716
|
+
path15.join(home, "Downloads")
|
|
17511
17717
|
];
|
|
17512
17718
|
const timeoutPromise = new Promise(
|
|
17513
17719
|
(resolve) => setTimeout(() => {
|
|
@@ -17519,7 +17725,7 @@ var findSystemMCPConfigs = async () => {
|
|
|
17519
17725
|
);
|
|
17520
17726
|
const searchPromise = Promise.all(
|
|
17521
17727
|
knownDirs.map(
|
|
17522
|
-
(dir) =>
|
|
17728
|
+
(dir) => fs14.existsSync(dir) ? searchDir(dir) : Promise.resolve([])
|
|
17523
17729
|
)
|
|
17524
17730
|
).then((results) => results.flat());
|
|
17525
17731
|
return await Promise.race([timeoutPromise, searchPromise]);
|
|
@@ -19921,31 +20127,31 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
19921
20127
|
};
|
|
19922
20128
|
|
|
19923
20129
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
19924
|
-
import * as
|
|
20130
|
+
import * as fs17 from "fs";
|
|
19925
20131
|
import * as os10 from "os";
|
|
19926
|
-
import * as
|
|
20132
|
+
import * as path17 from "path";
|
|
19927
20133
|
|
|
19928
20134
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
19929
20135
|
init_configs();
|
|
19930
|
-
import * as
|
|
20136
|
+
import * as fs16 from "fs";
|
|
19931
20137
|
import fetch6 from "node-fetch";
|
|
19932
|
-
import * as
|
|
20138
|
+
import * as path16 from "path";
|
|
19933
20139
|
|
|
19934
20140
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
19935
|
-
import * as
|
|
20141
|
+
import * as fs15 from "fs";
|
|
19936
20142
|
import * as os9 from "os";
|
|
19937
20143
|
|
|
19938
20144
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
19939
|
-
import * as
|
|
20145
|
+
import * as fs18 from "fs";
|
|
19940
20146
|
import * as os11 from "os";
|
|
19941
|
-
import * as
|
|
20147
|
+
import * as path18 from "path";
|
|
19942
20148
|
|
|
19943
20149
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
19944
20150
|
import { z as z42 } from "zod";
|
|
19945
20151
|
|
|
19946
20152
|
// src/mcp/services/PathValidation.ts
|
|
19947
|
-
import
|
|
19948
|
-
import
|
|
20153
|
+
import fs19 from "fs";
|
|
20154
|
+
import path19 from "path";
|
|
19949
20155
|
async function validatePath(inputPath) {
|
|
19950
20156
|
logDebug("Validating MCP path", { inputPath });
|
|
19951
20157
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -19977,7 +20183,7 @@ async function validatePath(inputPath) {
|
|
|
19977
20183
|
logError(error);
|
|
19978
20184
|
return { isValid: false, error, path: inputPath };
|
|
19979
20185
|
}
|
|
19980
|
-
const normalizedPath =
|
|
20186
|
+
const normalizedPath = path19.normalize(inputPath);
|
|
19981
20187
|
if (normalizedPath.includes("..")) {
|
|
19982
20188
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
19983
20189
|
logError(error);
|
|
@@ -20004,7 +20210,7 @@ async function validatePath(inputPath) {
|
|
|
20004
20210
|
logDebug("Path validation successful", { inputPath });
|
|
20005
20211
|
logDebug("Checking path existence", { inputPath });
|
|
20006
20212
|
try {
|
|
20007
|
-
await
|
|
20213
|
+
await fs19.promises.access(inputPath);
|
|
20008
20214
|
logDebug("Path exists and is accessible", { inputPath });
|
|
20009
20215
|
WorkspaceService.setKnownWorkspacePath(inputPath);
|
|
20010
20216
|
logDebug("Stored validated path in WorkspaceService", { inputPath });
|
|
@@ -20632,10 +20838,10 @@ If you wish to scan files that were recently changed in your git history call th
|
|
|
20632
20838
|
init_FileUtils();
|
|
20633
20839
|
init_GitService();
|
|
20634
20840
|
init_configs();
|
|
20635
|
-
import
|
|
20841
|
+
import fs20 from "fs/promises";
|
|
20636
20842
|
import nodePath from "path";
|
|
20637
20843
|
var getLocalFiles = async ({
|
|
20638
|
-
path:
|
|
20844
|
+
path: path26,
|
|
20639
20845
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
20640
20846
|
maxFiles,
|
|
20641
20847
|
isAllFilesScan,
|
|
@@ -20643,17 +20849,17 @@ var getLocalFiles = async ({
|
|
|
20643
20849
|
scanRecentlyChangedFiles
|
|
20644
20850
|
}) => {
|
|
20645
20851
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
20646
|
-
path:
|
|
20852
|
+
path: path26,
|
|
20647
20853
|
maxFileSize,
|
|
20648
20854
|
maxFiles,
|
|
20649
20855
|
isAllFilesScan,
|
|
20650
20856
|
scanRecentlyChangedFiles
|
|
20651
20857
|
});
|
|
20652
20858
|
try {
|
|
20653
|
-
const resolvedRepoPath = await
|
|
20859
|
+
const resolvedRepoPath = await fs20.realpath(path26);
|
|
20654
20860
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
20655
20861
|
resolvedRepoPath,
|
|
20656
|
-
originalPath:
|
|
20862
|
+
originalPath: path26
|
|
20657
20863
|
});
|
|
20658
20864
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
20659
20865
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -20666,7 +20872,7 @@ var getLocalFiles = async ({
|
|
|
20666
20872
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
20667
20873
|
try {
|
|
20668
20874
|
files = await FileUtils.getLastChangedFiles({
|
|
20669
|
-
dir:
|
|
20875
|
+
dir: path26,
|
|
20670
20876
|
maxFileSize,
|
|
20671
20877
|
maxFiles,
|
|
20672
20878
|
isAllFilesScan
|
|
@@ -20730,7 +20936,7 @@ var getLocalFiles = async ({
|
|
|
20730
20936
|
absoluteFilePath
|
|
20731
20937
|
);
|
|
20732
20938
|
try {
|
|
20733
|
-
const fileStat = await
|
|
20939
|
+
const fileStat = await fs20.stat(absoluteFilePath);
|
|
20734
20940
|
return {
|
|
20735
20941
|
filename: nodePath.basename(absoluteFilePath),
|
|
20736
20942
|
relativePath,
|
|
@@ -20758,7 +20964,7 @@ var getLocalFiles = async ({
|
|
|
20758
20964
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
20759
20965
|
error: error instanceof Error ? error.message : String(error),
|
|
20760
20966
|
stack: error instanceof Error ? error.stack : void 0,
|
|
20761
|
-
path:
|
|
20967
|
+
path: path26
|
|
20762
20968
|
});
|
|
20763
20969
|
throw error;
|
|
20764
20970
|
}
|
|
@@ -20767,8 +20973,8 @@ var getLocalFiles = async ({
|
|
|
20767
20973
|
// src/mcp/services/LocalMobbFolderService.ts
|
|
20768
20974
|
init_client_generates();
|
|
20769
20975
|
init_GitService();
|
|
20770
|
-
import
|
|
20771
|
-
import
|
|
20976
|
+
import fs21 from "fs";
|
|
20977
|
+
import path20 from "path";
|
|
20772
20978
|
import { z as z41 } from "zod";
|
|
20773
20979
|
function extractPathFromPatch(patch) {
|
|
20774
20980
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
@@ -20854,19 +21060,19 @@ var LocalMobbFolderService = class {
|
|
|
20854
21060
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
20855
21061
|
);
|
|
20856
21062
|
}
|
|
20857
|
-
const mobbFolderPath =
|
|
21063
|
+
const mobbFolderPath = path20.join(
|
|
20858
21064
|
this.repoPath,
|
|
20859
21065
|
this.defaultMobbFolderName
|
|
20860
21066
|
);
|
|
20861
|
-
if (!
|
|
21067
|
+
if (!fs21.existsSync(mobbFolderPath)) {
|
|
20862
21068
|
logInfo("[LocalMobbFolderService] Creating .mobb folder", {
|
|
20863
21069
|
mobbFolderPath
|
|
20864
21070
|
});
|
|
20865
|
-
|
|
21071
|
+
fs21.mkdirSync(mobbFolderPath, { recursive: true });
|
|
20866
21072
|
} else {
|
|
20867
21073
|
logDebug("[LocalMobbFolderService] .mobb folder already exists");
|
|
20868
21074
|
}
|
|
20869
|
-
const stats =
|
|
21075
|
+
const stats = fs21.statSync(mobbFolderPath);
|
|
20870
21076
|
if (!stats.isDirectory()) {
|
|
20871
21077
|
throw new Error(`Path exists but is not a directory: ${mobbFolderPath}`);
|
|
20872
21078
|
}
|
|
@@ -20907,13 +21113,13 @@ var LocalMobbFolderService = class {
|
|
|
20907
21113
|
logDebug("[LocalMobbFolderService] Git repository validated successfully");
|
|
20908
21114
|
} else {
|
|
20909
21115
|
try {
|
|
20910
|
-
const stats =
|
|
21116
|
+
const stats = fs21.statSync(this.repoPath);
|
|
20911
21117
|
if (!stats.isDirectory()) {
|
|
20912
21118
|
throw new Error(
|
|
20913
21119
|
`Path exists but is not a directory: ${this.repoPath}`
|
|
20914
21120
|
);
|
|
20915
21121
|
}
|
|
20916
|
-
|
|
21122
|
+
fs21.accessSync(this.repoPath, fs21.constants.R_OK | fs21.constants.W_OK);
|
|
20917
21123
|
logDebug(
|
|
20918
21124
|
"[LocalMobbFolderService] Non-git directory validated successfully"
|
|
20919
21125
|
);
|
|
@@ -21026,8 +21232,8 @@ var LocalMobbFolderService = class {
|
|
|
21026
21232
|
mobbFolderPath,
|
|
21027
21233
|
baseFileName
|
|
21028
21234
|
);
|
|
21029
|
-
const filePath =
|
|
21030
|
-
await
|
|
21235
|
+
const filePath = path20.join(mobbFolderPath, uniqueFileName);
|
|
21236
|
+
await fs21.promises.writeFile(filePath, patch, "utf8");
|
|
21031
21237
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
21032
21238
|
filePath,
|
|
21033
21239
|
fileName: uniqueFileName,
|
|
@@ -21084,11 +21290,11 @@ var LocalMobbFolderService = class {
|
|
|
21084
21290
|
* @returns Unique filename that doesn't conflict with existing files
|
|
21085
21291
|
*/
|
|
21086
21292
|
getUniqueFileName(folderPath, baseFileName) {
|
|
21087
|
-
const baseName =
|
|
21088
|
-
const extension =
|
|
21293
|
+
const baseName = path20.parse(baseFileName).name;
|
|
21294
|
+
const extension = path20.parse(baseFileName).ext;
|
|
21089
21295
|
let uniqueFileName = baseFileName;
|
|
21090
21296
|
let index = 1;
|
|
21091
|
-
while (
|
|
21297
|
+
while (fs21.existsSync(path20.join(folderPath, uniqueFileName))) {
|
|
21092
21298
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
21093
21299
|
index++;
|
|
21094
21300
|
if (index > 1e3) {
|
|
@@ -21119,18 +21325,18 @@ var LocalMobbFolderService = class {
|
|
|
21119
21325
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
21120
21326
|
try {
|
|
21121
21327
|
const mobbFolderPath = await this.getFolder();
|
|
21122
|
-
const patchInfoPath =
|
|
21328
|
+
const patchInfoPath = path20.join(mobbFolderPath, "patchInfo.md");
|
|
21123
21329
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
21124
21330
|
let existingContent = "";
|
|
21125
|
-
if (
|
|
21126
|
-
existingContent = await
|
|
21331
|
+
if (fs21.existsSync(patchInfoPath)) {
|
|
21332
|
+
existingContent = await fs21.promises.readFile(patchInfoPath, "utf8");
|
|
21127
21333
|
logDebug("[LocalMobbFolderService] Existing patchInfo.md found");
|
|
21128
21334
|
} else {
|
|
21129
21335
|
logDebug("[LocalMobbFolderService] Creating new patchInfo.md file");
|
|
21130
21336
|
}
|
|
21131
21337
|
const separator = existingContent ? "\n\n================================================================================\n\n" : "";
|
|
21132
21338
|
const updatedContent = `${markdownContent}${separator}${existingContent}`;
|
|
21133
|
-
await
|
|
21339
|
+
await fs21.promises.writeFile(patchInfoPath, updatedContent, "utf8");
|
|
21134
21340
|
logInfo("[LocalMobbFolderService] Patch info logged successfully", {
|
|
21135
21341
|
patchInfoPath,
|
|
21136
21342
|
fixId: fix.id,
|
|
@@ -21161,7 +21367,7 @@ var LocalMobbFolderService = class {
|
|
|
21161
21367
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
21162
21368
|
const patch = this.extractPatchFromFix(fix);
|
|
21163
21369
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
21164
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
21370
|
+
const patchedFilePath = relativePatchedFilePath ? path20.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
21165
21371
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
21166
21372
|
let markdown = `# Fix ${fixIdentifier}
|
|
21167
21373
|
|
|
@@ -21503,16 +21709,16 @@ import {
|
|
|
21503
21709
|
unlinkSync,
|
|
21504
21710
|
writeFileSync
|
|
21505
21711
|
} from "fs";
|
|
21506
|
-
import
|
|
21712
|
+
import fs22 from "fs/promises";
|
|
21507
21713
|
import parseDiff3 from "parse-diff";
|
|
21508
|
-
import
|
|
21714
|
+
import path21 from "path";
|
|
21509
21715
|
var PatchApplicationService = class {
|
|
21510
21716
|
/**
|
|
21511
21717
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
21512
21718
|
*/
|
|
21513
21719
|
static getCommentSyntax(filePath) {
|
|
21514
|
-
const ext =
|
|
21515
|
-
const basename2 =
|
|
21720
|
+
const ext = path21.extname(filePath).toLowerCase();
|
|
21721
|
+
const basename2 = path21.basename(filePath);
|
|
21516
21722
|
const commentMap = {
|
|
21517
21723
|
// C-style languages (single line comments)
|
|
21518
21724
|
".js": "//",
|
|
@@ -21720,7 +21926,7 @@ var PatchApplicationService = class {
|
|
|
21720
21926
|
}
|
|
21721
21927
|
);
|
|
21722
21928
|
}
|
|
21723
|
-
const dirPath =
|
|
21929
|
+
const dirPath = path21.dirname(normalizedFilePath);
|
|
21724
21930
|
mkdirSync(dirPath, { recursive: true });
|
|
21725
21931
|
writeFileSync(normalizedFilePath, finalContent, "utf8");
|
|
21726
21932
|
return normalizedFilePath;
|
|
@@ -21729,9 +21935,9 @@ var PatchApplicationService = class {
|
|
|
21729
21935
|
repositoryPath,
|
|
21730
21936
|
targetPath
|
|
21731
21937
|
}) {
|
|
21732
|
-
const repoRoot =
|
|
21733
|
-
const normalizedPath =
|
|
21734
|
-
const repoRootWithSep = repoRoot.endsWith(
|
|
21938
|
+
const repoRoot = path21.resolve(repositoryPath);
|
|
21939
|
+
const normalizedPath = path21.resolve(repoRoot, targetPath);
|
|
21940
|
+
const repoRootWithSep = repoRoot.endsWith(path21.sep) ? repoRoot : `${repoRoot}${path21.sep}`;
|
|
21735
21941
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
21736
21942
|
throw new Error(
|
|
21737
21943
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -21740,7 +21946,7 @@ var PatchApplicationService = class {
|
|
|
21740
21946
|
return {
|
|
21741
21947
|
repoRoot,
|
|
21742
21948
|
normalizedPath,
|
|
21743
|
-
relativePath:
|
|
21949
|
+
relativePath: path21.relative(repoRoot, normalizedPath)
|
|
21744
21950
|
};
|
|
21745
21951
|
}
|
|
21746
21952
|
/**
|
|
@@ -22022,9 +22228,9 @@ var PatchApplicationService = class {
|
|
|
22022
22228
|
continue;
|
|
22023
22229
|
}
|
|
22024
22230
|
try {
|
|
22025
|
-
const absolutePath =
|
|
22231
|
+
const absolutePath = path21.resolve(repositoryPath, targetFile);
|
|
22026
22232
|
if (existsSync6(absolutePath)) {
|
|
22027
|
-
const stats = await
|
|
22233
|
+
const stats = await fs22.stat(absolutePath);
|
|
22028
22234
|
const fileModTime = stats.mtime.getTime();
|
|
22029
22235
|
if (fileModTime > scanStartTime) {
|
|
22030
22236
|
logError(
|
|
@@ -22065,7 +22271,7 @@ var PatchApplicationService = class {
|
|
|
22065
22271
|
const appliedFixes = [];
|
|
22066
22272
|
const failedFixes = [];
|
|
22067
22273
|
const skippedFixes = [];
|
|
22068
|
-
const resolvedRepoPath = await
|
|
22274
|
+
const resolvedRepoPath = await fs22.realpath(repositoryPath);
|
|
22069
22275
|
logInfo(
|
|
22070
22276
|
`[${scanContext}] Starting patch application for ${fixes.length} fixes`,
|
|
22071
22277
|
{
|
|
@@ -22248,7 +22454,7 @@ var PatchApplicationService = class {
|
|
|
22248
22454
|
fix,
|
|
22249
22455
|
scanContext
|
|
22250
22456
|
});
|
|
22251
|
-
appliedFiles.push(
|
|
22457
|
+
appliedFiles.push(path21.relative(repositoryPath, actualPath));
|
|
22252
22458
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
22253
22459
|
}
|
|
22254
22460
|
/**
|
|
@@ -22297,7 +22503,7 @@ var PatchApplicationService = class {
|
|
|
22297
22503
|
fix,
|
|
22298
22504
|
scanContext
|
|
22299
22505
|
});
|
|
22300
|
-
appliedFiles.push(
|
|
22506
|
+
appliedFiles.push(path21.relative(repositoryPath, actualPath));
|
|
22301
22507
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
22302
22508
|
}
|
|
22303
22509
|
}
|
|
@@ -22493,9 +22699,9 @@ init_configs();
|
|
|
22493
22699
|
|
|
22494
22700
|
// src/mcp/services/FileOperations.ts
|
|
22495
22701
|
init_FileUtils();
|
|
22496
|
-
import
|
|
22497
|
-
import
|
|
22498
|
-
import
|
|
22702
|
+
import fs23 from "fs";
|
|
22703
|
+
import path22 from "path";
|
|
22704
|
+
import AdmZip3 from "adm-zip";
|
|
22499
22705
|
var FileOperations = class {
|
|
22500
22706
|
/**
|
|
22501
22707
|
* Creates a ZIP archive containing the specified source files
|
|
@@ -22510,14 +22716,14 @@ var FileOperations = class {
|
|
|
22510
22716
|
maxFileSize
|
|
22511
22717
|
}) {
|
|
22512
22718
|
logDebug("[FileOperations] Packing files");
|
|
22513
|
-
const zip = new
|
|
22719
|
+
const zip = new AdmZip3();
|
|
22514
22720
|
let packedFilesCount = 0;
|
|
22515
22721
|
const packedFiles = [];
|
|
22516
22722
|
const excludedFiles = [];
|
|
22517
|
-
const resolvedRepoPath =
|
|
22723
|
+
const resolvedRepoPath = path22.resolve(repositoryPath);
|
|
22518
22724
|
for (const filepath of fileList) {
|
|
22519
|
-
const absoluteFilepath =
|
|
22520
|
-
const resolvedFilePath =
|
|
22725
|
+
const absoluteFilepath = path22.join(repositoryPath, filepath);
|
|
22726
|
+
const resolvedFilePath = path22.resolve(absoluteFilepath);
|
|
22521
22727
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
22522
22728
|
const reason = "potential path traversal security risk";
|
|
22523
22729
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -22564,11 +22770,11 @@ var FileOperations = class {
|
|
|
22564
22770
|
fileList,
|
|
22565
22771
|
repositoryPath
|
|
22566
22772
|
}) {
|
|
22567
|
-
const resolvedRepoPath =
|
|
22773
|
+
const resolvedRepoPath = path22.resolve(repositoryPath);
|
|
22568
22774
|
const validatedPaths = [];
|
|
22569
22775
|
for (const filepath of fileList) {
|
|
22570
|
-
const absoluteFilepath =
|
|
22571
|
-
const resolvedFilePath =
|
|
22776
|
+
const absoluteFilepath = path22.join(repositoryPath, filepath);
|
|
22777
|
+
const resolvedFilePath = path22.resolve(absoluteFilepath);
|
|
22572
22778
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
22573
22779
|
logDebug(
|
|
22574
22780
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -22576,7 +22782,7 @@ var FileOperations = class {
|
|
|
22576
22782
|
continue;
|
|
22577
22783
|
}
|
|
22578
22784
|
try {
|
|
22579
|
-
await
|
|
22785
|
+
await fs23.promises.access(absoluteFilepath, fs23.constants.R_OK);
|
|
22580
22786
|
validatedPaths.push(filepath);
|
|
22581
22787
|
} catch (error) {
|
|
22582
22788
|
logDebug(
|
|
@@ -22595,8 +22801,8 @@ var FileOperations = class {
|
|
|
22595
22801
|
const fileDataArray = [];
|
|
22596
22802
|
for (const absolutePath of filePaths) {
|
|
22597
22803
|
try {
|
|
22598
|
-
const content = await
|
|
22599
|
-
const relativePath =
|
|
22804
|
+
const content = await fs23.promises.readFile(absolutePath);
|
|
22805
|
+
const relativePath = path22.basename(absolutePath);
|
|
22600
22806
|
fileDataArray.push({
|
|
22601
22807
|
relativePath,
|
|
22602
22808
|
absolutePath,
|
|
@@ -22621,7 +22827,7 @@ var FileOperations = class {
|
|
|
22621
22827
|
relativeFilepath
|
|
22622
22828
|
}) {
|
|
22623
22829
|
try {
|
|
22624
|
-
return await
|
|
22830
|
+
return await fs23.promises.readFile(absoluteFilepath);
|
|
22625
22831
|
} catch (fsError) {
|
|
22626
22832
|
logError(
|
|
22627
22833
|
`[FileOperations] Failed to read ${relativeFilepath} from filesystem: ${fsError}`
|
|
@@ -22908,14 +23114,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
22908
23114
|
* since the last scan.
|
|
22909
23115
|
*/
|
|
22910
23116
|
async scanForSecurityVulnerabilities({
|
|
22911
|
-
path:
|
|
23117
|
+
path: path26,
|
|
22912
23118
|
isAllDetectionRulesScan,
|
|
22913
23119
|
isAllFilesScan,
|
|
22914
23120
|
scanContext
|
|
22915
23121
|
}) {
|
|
22916
23122
|
this.hasAuthenticationFailed = false;
|
|
22917
23123
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
22918
|
-
path:
|
|
23124
|
+
path: path26
|
|
22919
23125
|
});
|
|
22920
23126
|
if (!this.gqlClient) {
|
|
22921
23127
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -22931,11 +23137,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
22931
23137
|
}
|
|
22932
23138
|
logDebug(
|
|
22933
23139
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
22934
|
-
{ path:
|
|
23140
|
+
{ path: path26 }
|
|
22935
23141
|
);
|
|
22936
23142
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
22937
23143
|
const files = await getLocalFiles({
|
|
22938
|
-
path:
|
|
23144
|
+
path: path26,
|
|
22939
23145
|
isAllFilesScan,
|
|
22940
23146
|
scanContext,
|
|
22941
23147
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -22961,13 +23167,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
22961
23167
|
});
|
|
22962
23168
|
const { fixReportId, projectId } = await scanFiles({
|
|
22963
23169
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
22964
|
-
repositoryPath:
|
|
23170
|
+
repositoryPath: path26,
|
|
22965
23171
|
gqlClient: this.gqlClient,
|
|
22966
23172
|
isAllDetectionRulesScan,
|
|
22967
23173
|
scanContext
|
|
22968
23174
|
});
|
|
22969
23175
|
logInfo(
|
|
22970
|
-
`[${scanContext}] Security scan completed for ${
|
|
23176
|
+
`[${scanContext}] Security scan completed for ${path26} reportId: ${fixReportId} projectId: ${projectId}`
|
|
22971
23177
|
);
|
|
22972
23178
|
if (isAllFilesScan) {
|
|
22973
23179
|
return;
|
|
@@ -23261,13 +23467,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
23261
23467
|
});
|
|
23262
23468
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
23263
23469
|
}
|
|
23264
|
-
async getFreshFixes({ path:
|
|
23470
|
+
async getFreshFixes({ path: path26 }) {
|
|
23265
23471
|
const scanContext = ScanContext.USER_REQUEST;
|
|
23266
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
23267
|
-
if (this.path !==
|
|
23268
|
-
this.path =
|
|
23472
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path26 });
|
|
23473
|
+
if (this.path !== path26) {
|
|
23474
|
+
this.path = path26;
|
|
23269
23475
|
this.reset();
|
|
23270
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
23476
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path26 });
|
|
23271
23477
|
}
|
|
23272
23478
|
try {
|
|
23273
23479
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -23286,7 +23492,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
23286
23492
|
}
|
|
23287
23493
|
throw error;
|
|
23288
23494
|
}
|
|
23289
|
-
this.triggerScan({ path:
|
|
23495
|
+
this.triggerScan({ path: path26, gqlClient: this.gqlClient });
|
|
23290
23496
|
let isMvsAutoFixEnabled = null;
|
|
23291
23497
|
try {
|
|
23292
23498
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -23320,33 +23526,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
23320
23526
|
return noFreshFixesPrompt;
|
|
23321
23527
|
}
|
|
23322
23528
|
triggerScan({
|
|
23323
|
-
path:
|
|
23529
|
+
path: path26,
|
|
23324
23530
|
gqlClient
|
|
23325
23531
|
}) {
|
|
23326
|
-
if (this.path !==
|
|
23327
|
-
this.path =
|
|
23532
|
+
if (this.path !== path26) {
|
|
23533
|
+
this.path = path26;
|
|
23328
23534
|
this.reset();
|
|
23329
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
23535
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path26 });
|
|
23330
23536
|
}
|
|
23331
23537
|
this.gqlClient = gqlClient;
|
|
23332
23538
|
if (!this.intervalId) {
|
|
23333
|
-
this.startPeriodicScanning(
|
|
23334
|
-
this.executeInitialScan(
|
|
23335
|
-
void this.executeInitialFullScan(
|
|
23539
|
+
this.startPeriodicScanning(path26);
|
|
23540
|
+
this.executeInitialScan(path26);
|
|
23541
|
+
void this.executeInitialFullScan(path26);
|
|
23336
23542
|
}
|
|
23337
23543
|
}
|
|
23338
|
-
startPeriodicScanning(
|
|
23544
|
+
startPeriodicScanning(path26) {
|
|
23339
23545
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
23340
23546
|
logDebug(
|
|
23341
23547
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
23342
23548
|
{
|
|
23343
|
-
path:
|
|
23549
|
+
path: path26
|
|
23344
23550
|
}
|
|
23345
23551
|
);
|
|
23346
23552
|
this.intervalId = setInterval(() => {
|
|
23347
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
23553
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path26 });
|
|
23348
23554
|
this.scanForSecurityVulnerabilities({
|
|
23349
|
-
path:
|
|
23555
|
+
path: path26,
|
|
23350
23556
|
scanContext
|
|
23351
23557
|
}).catch((error) => {
|
|
23352
23558
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -23355,45 +23561,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
23355
23561
|
});
|
|
23356
23562
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
23357
23563
|
}
|
|
23358
|
-
async executeInitialFullScan(
|
|
23564
|
+
async executeInitialFullScan(path26) {
|
|
23359
23565
|
const scanContext = ScanContext.FULL_SCAN;
|
|
23360
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
23566
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path26 });
|
|
23361
23567
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
23362
23568
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
23363
23569
|
});
|
|
23364
|
-
if (this.fullScanPathsScanned.includes(
|
|
23570
|
+
if (this.fullScanPathsScanned.includes(path26)) {
|
|
23365
23571
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
23366
|
-
path:
|
|
23572
|
+
path: path26
|
|
23367
23573
|
});
|
|
23368
23574
|
return;
|
|
23369
23575
|
}
|
|
23370
23576
|
configStore.set("fullScanPathsScanned", [
|
|
23371
23577
|
...this.fullScanPathsScanned,
|
|
23372
|
-
|
|
23578
|
+
path26
|
|
23373
23579
|
]);
|
|
23374
23580
|
try {
|
|
23375
23581
|
await this.scanForSecurityVulnerabilities({
|
|
23376
|
-
path:
|
|
23582
|
+
path: path26,
|
|
23377
23583
|
isAllFilesScan: true,
|
|
23378
23584
|
isAllDetectionRulesScan: true,
|
|
23379
23585
|
scanContext: ScanContext.FULL_SCAN
|
|
23380
23586
|
});
|
|
23381
|
-
if (!this.fullScanPathsScanned.includes(
|
|
23382
|
-
this.fullScanPathsScanned.push(
|
|
23587
|
+
if (!this.fullScanPathsScanned.includes(path26)) {
|
|
23588
|
+
this.fullScanPathsScanned.push(path26);
|
|
23383
23589
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
23384
23590
|
}
|
|
23385
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
23591
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path26 });
|
|
23386
23592
|
} catch (error) {
|
|
23387
23593
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
23388
23594
|
error
|
|
23389
23595
|
});
|
|
23390
23596
|
}
|
|
23391
23597
|
}
|
|
23392
|
-
executeInitialScan(
|
|
23598
|
+
executeInitialScan(path26) {
|
|
23393
23599
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
23394
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
23600
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path26 });
|
|
23395
23601
|
this.scanForSecurityVulnerabilities({
|
|
23396
|
-
path:
|
|
23602
|
+
path: path26,
|
|
23397
23603
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
23398
23604
|
}).catch((error) => {
|
|
23399
23605
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -23490,9 +23696,9 @@ Example payload:
|
|
|
23490
23696
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
23491
23697
|
);
|
|
23492
23698
|
}
|
|
23493
|
-
const
|
|
23699
|
+
const path26 = pathValidationResult.path;
|
|
23494
23700
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
23495
|
-
path:
|
|
23701
|
+
path: path26
|
|
23496
23702
|
});
|
|
23497
23703
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
23498
23704
|
resultText
|
|
@@ -23670,8 +23876,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
23670
23876
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
23671
23877
|
);
|
|
23672
23878
|
}
|
|
23673
|
-
const
|
|
23674
|
-
const gitService = new GitService(
|
|
23879
|
+
const path26 = pathValidationResult.path;
|
|
23880
|
+
const gitService = new GitService(path26, log);
|
|
23675
23881
|
const gitValidation = await gitService.validateRepository();
|
|
23676
23882
|
if (!gitValidation.isValid) {
|
|
23677
23883
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -24056,9 +24262,9 @@ Example payload:
|
|
|
24056
24262
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
24057
24263
|
);
|
|
24058
24264
|
}
|
|
24059
|
-
const
|
|
24265
|
+
const path26 = pathValidationResult.path;
|
|
24060
24266
|
const files = await getLocalFiles({
|
|
24061
|
-
path:
|
|
24267
|
+
path: path26,
|
|
24062
24268
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
24063
24269
|
maxFiles: args.maxFiles,
|
|
24064
24270
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -24078,7 +24284,7 @@ Example payload:
|
|
|
24078
24284
|
try {
|
|
24079
24285
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
24080
24286
|
fileList: files.map((file) => file.relativePath),
|
|
24081
|
-
repositoryPath:
|
|
24287
|
+
repositoryPath: path26,
|
|
24082
24288
|
offset: args.offset,
|
|
24083
24289
|
limit: args.limit,
|
|
24084
24290
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -24182,33 +24388,33 @@ var mcpHandler = async (_args) => {
|
|
|
24182
24388
|
};
|
|
24183
24389
|
|
|
24184
24390
|
// src/args/commands/review.ts
|
|
24185
|
-
import
|
|
24186
|
-
import
|
|
24391
|
+
import fs24 from "fs";
|
|
24392
|
+
import chalk12 from "chalk";
|
|
24187
24393
|
function reviewBuilder(yargs2) {
|
|
24188
24394
|
return yargs2.option("f", {
|
|
24189
24395
|
alias: "scan-file",
|
|
24190
24396
|
demandOption: true,
|
|
24191
24397
|
type: "string",
|
|
24192
|
-
describe:
|
|
24398
|
+
describe: chalk12.bold(
|
|
24193
24399
|
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep)"
|
|
24194
24400
|
)
|
|
24195
24401
|
}).option("repo", { ...repoOption, demandOption: true }).option("scanner", { ...scannerOptions, demandOption: true }).option("ref", { ...refOption, demandOption: true }).option("ch", {
|
|
24196
24402
|
alias: "commit-hash",
|
|
24197
|
-
describe:
|
|
24403
|
+
describe: chalk12.bold("Hash of the commit"),
|
|
24198
24404
|
type: "string",
|
|
24199
24405
|
demandOption: true
|
|
24200
24406
|
}).option("mobb-project-name", mobbProjectNameOption).option("api-key", { ...apiKeyOption, demandOption: true }).option("commit-hash", { ...commitHashOption, demandOption: true }).option("github-token", {
|
|
24201
|
-
describe:
|
|
24407
|
+
describe: chalk12.bold("Github action token"),
|
|
24202
24408
|
type: "string",
|
|
24203
24409
|
demandOption: true
|
|
24204
24410
|
}).option("pull-request", {
|
|
24205
24411
|
alias: ["pr", "pr-number", "pr-id"],
|
|
24206
|
-
describe:
|
|
24412
|
+
describe: chalk12.bold("Number of the pull request"),
|
|
24207
24413
|
type: "number",
|
|
24208
24414
|
demandOption: true
|
|
24209
24415
|
}).option("p", {
|
|
24210
24416
|
alias: "src-path",
|
|
24211
|
-
describe:
|
|
24417
|
+
describe: chalk12.bold(
|
|
24212
24418
|
"Path to the repository folder with the source code"
|
|
24213
24419
|
),
|
|
24214
24420
|
type: "string",
|
|
@@ -24219,9 +24425,9 @@ function reviewBuilder(yargs2) {
|
|
|
24219
24425
|
).help();
|
|
24220
24426
|
}
|
|
24221
24427
|
function validateReviewOptions(argv) {
|
|
24222
|
-
if (!
|
|
24428
|
+
if (!fs24.existsSync(argv.f)) {
|
|
24223
24429
|
throw new CliError(`
|
|
24224
|
-
Can't access ${
|
|
24430
|
+
Can't access ${chalk12.bold(argv.f)}`);
|
|
24225
24431
|
}
|
|
24226
24432
|
validateRepoUrl(argv);
|
|
24227
24433
|
validateReportFileFormat(argv.f);
|
|
@@ -24256,6 +24462,27 @@ async function scanHandler(args) {
|
|
|
24256
24462
|
await scan(args, { skipPrompts: args.yes });
|
|
24257
24463
|
}
|
|
24258
24464
|
|
|
24465
|
+
// src/args/commands/scan_skill.ts
|
|
24466
|
+
import chalk13 from "chalk";
|
|
24467
|
+
function scanSkillBuilder(args) {
|
|
24468
|
+
return args.option("url", {
|
|
24469
|
+
demandOption: true,
|
|
24470
|
+
type: "string",
|
|
24471
|
+
describe: chalk13.bold(
|
|
24472
|
+
"URL or local directory of the skill to scan (GitHub repo, ZIP archive, raw SKILL.md, or folder containing SKILL.md)"
|
|
24473
|
+
)
|
|
24474
|
+
}).option("api-key", apiKeyOption).option("ci", ciOption).example(
|
|
24475
|
+
"npx mobbdev@latest scan-skill --url https://github.com/user/my-skill",
|
|
24476
|
+
"Scan an OpenClaw/ClawHub skill for security threats"
|
|
24477
|
+
).example(
|
|
24478
|
+
"npx mobbdev@latest scan-skill --url ./my-skill-folder",
|
|
24479
|
+
"Scan a local skill directory that contains SKILL.md"
|
|
24480
|
+
).help();
|
|
24481
|
+
}
|
|
24482
|
+
async function scanSkillHandler(args) {
|
|
24483
|
+
await scanSkill(args);
|
|
24484
|
+
}
|
|
24485
|
+
|
|
24259
24486
|
// src/args/commands/token.ts
|
|
24260
24487
|
function addScmTokenBuilder(args) {
|
|
24261
24488
|
return args.option("scm-type", scmTypeOption).option("url", urlOption).option("token", scmTokenOption).option("organization", scmOrgOption).option("refresh-token", scmRefreshTokenOption).option("api-key", apiKeyOption).option("ci", ciOption).example(
|
|
@@ -24297,10 +24524,10 @@ init_client_generates();
|
|
|
24297
24524
|
init_urlParser2();
|
|
24298
24525
|
|
|
24299
24526
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
24300
|
-
import
|
|
24527
|
+
import path23 from "path";
|
|
24301
24528
|
import * as grpc from "@grpc/grpc-js";
|
|
24302
24529
|
import * as protoLoader from "@grpc/proto-loader";
|
|
24303
|
-
var PROTO_PATH =
|
|
24530
|
+
var PROTO_PATH = path23.join(
|
|
24304
24531
|
getModuleRootDir(),
|
|
24305
24532
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
24306
24533
|
);
|
|
@@ -24312,7 +24539,7 @@ function loadProto() {
|
|
|
24312
24539
|
defaults: true,
|
|
24313
24540
|
oneofs: true,
|
|
24314
24541
|
includeDirs: [
|
|
24315
|
-
|
|
24542
|
+
path23.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
24316
24543
|
]
|
|
24317
24544
|
});
|
|
24318
24545
|
return grpc.loadPackageDefinition(
|
|
@@ -24366,30 +24593,30 @@ async function getGrpcClient(port, csrf3) {
|
|
|
24366
24593
|
}
|
|
24367
24594
|
|
|
24368
24595
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
24369
|
-
import
|
|
24596
|
+
import fs25 from "fs";
|
|
24370
24597
|
import os12 from "os";
|
|
24371
|
-
import
|
|
24598
|
+
import path24 from "path";
|
|
24372
24599
|
function getLogsDir() {
|
|
24373
24600
|
if (process.platform === "darwin") {
|
|
24374
|
-
return
|
|
24601
|
+
return path24.join(os12.homedir(), "Library/Logs/JetBrains");
|
|
24375
24602
|
} else if (process.platform === "win32") {
|
|
24376
|
-
return
|
|
24377
|
-
process.env["LOCALAPPDATA"] ||
|
|
24603
|
+
return path24.join(
|
|
24604
|
+
process.env["LOCALAPPDATA"] || path24.join(os12.homedir(), "AppData/Local"),
|
|
24378
24605
|
"JetBrains"
|
|
24379
24606
|
);
|
|
24380
24607
|
} else {
|
|
24381
|
-
return
|
|
24608
|
+
return path24.join(os12.homedir(), ".cache/JetBrains");
|
|
24382
24609
|
}
|
|
24383
24610
|
}
|
|
24384
24611
|
function parseIdeLogDir(ideLogDir) {
|
|
24385
|
-
const logFiles =
|
|
24612
|
+
const logFiles = fs25.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
24386
24613
|
name: f,
|
|
24387
|
-
mtime:
|
|
24614
|
+
mtime: fs25.statSync(path24.join(ideLogDir, f)).mtimeMs
|
|
24388
24615
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
24389
24616
|
let latestCsrf = null;
|
|
24390
24617
|
let latestPort = null;
|
|
24391
24618
|
for (const logFile of logFiles) {
|
|
24392
|
-
const lines =
|
|
24619
|
+
const lines = fs25.readFileSync(path24.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
24393
24620
|
for (const line of lines) {
|
|
24394
24621
|
if (!line.includes(
|
|
24395
24622
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -24415,13 +24642,13 @@ function parseIdeLogDir(ideLogDir) {
|
|
|
24415
24642
|
function findRunningCodeiumLanguageServers() {
|
|
24416
24643
|
const results = [];
|
|
24417
24644
|
const logsDir = getLogsDir();
|
|
24418
|
-
if (!
|
|
24419
|
-
for (const ide of
|
|
24420
|
-
let ideLogDir =
|
|
24645
|
+
if (!fs25.existsSync(logsDir)) return results;
|
|
24646
|
+
for (const ide of fs25.readdirSync(logsDir)) {
|
|
24647
|
+
let ideLogDir = path24.join(logsDir, ide);
|
|
24421
24648
|
if (process.platform !== "darwin") {
|
|
24422
|
-
ideLogDir =
|
|
24649
|
+
ideLogDir = path24.join(ideLogDir, "log");
|
|
24423
24650
|
}
|
|
24424
|
-
if (!
|
|
24651
|
+
if (!fs25.existsSync(ideLogDir) || !fs25.statSync(ideLogDir).isDirectory()) {
|
|
24425
24652
|
continue;
|
|
24426
24653
|
}
|
|
24427
24654
|
const result = parseIdeLogDir(ideLogDir);
|
|
@@ -24602,10 +24829,10 @@ function processChatStepCodeAction(step) {
|
|
|
24602
24829
|
// src/features/codeium_intellij/install_hook.ts
|
|
24603
24830
|
import fsPromises6 from "fs/promises";
|
|
24604
24831
|
import os13 from "os";
|
|
24605
|
-
import
|
|
24606
|
-
import
|
|
24832
|
+
import path25 from "path";
|
|
24833
|
+
import chalk14 from "chalk";
|
|
24607
24834
|
function getCodeiumHooksPath() {
|
|
24608
|
-
return
|
|
24835
|
+
return path25.join(os13.homedir(), ".codeium", "hooks.json");
|
|
24609
24836
|
}
|
|
24610
24837
|
async function readCodeiumHooks() {
|
|
24611
24838
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -24618,7 +24845,7 @@ async function readCodeiumHooks() {
|
|
|
24618
24845
|
}
|
|
24619
24846
|
async function writeCodeiumHooks(config2) {
|
|
24620
24847
|
const hooksPath = getCodeiumHooksPath();
|
|
24621
|
-
const dir =
|
|
24848
|
+
const dir = path25.dirname(hooksPath);
|
|
24622
24849
|
await fsPromises6.mkdir(dir, { recursive: true });
|
|
24623
24850
|
await fsPromises6.writeFile(
|
|
24624
24851
|
hooksPath,
|
|
@@ -24628,7 +24855,7 @@ async function writeCodeiumHooks(config2) {
|
|
|
24628
24855
|
}
|
|
24629
24856
|
async function installWindsurfHooks(options = {}) {
|
|
24630
24857
|
const hooksPath = getCodeiumHooksPath();
|
|
24631
|
-
console.log(
|
|
24858
|
+
console.log(chalk14.blue("Installing Mobb hooks in Windsurf IntelliJ..."));
|
|
24632
24859
|
const config2 = await readCodeiumHooks();
|
|
24633
24860
|
if (!config2.hooks) {
|
|
24634
24861
|
config2.hooks = {};
|
|
@@ -24648,7 +24875,7 @@ async function installWindsurfHooks(options = {}) {
|
|
|
24648
24875
|
if (envVars.length > 0) {
|
|
24649
24876
|
command = `${envVars.join(" ")} ${command}`;
|
|
24650
24877
|
console.log(
|
|
24651
|
-
|
|
24878
|
+
chalk14.blue(
|
|
24652
24879
|
`Adding environment variables to hook command: ${envVars.join(", ")}`
|
|
24653
24880
|
)
|
|
24654
24881
|
);
|
|
@@ -24662,15 +24889,15 @@ async function installWindsurfHooks(options = {}) {
|
|
|
24662
24889
|
(hook) => hook.command?.includes("mobbdev@latest windsurf-intellij-process-hook")
|
|
24663
24890
|
);
|
|
24664
24891
|
if (existingHookIndex >= 0) {
|
|
24665
|
-
console.log(
|
|
24892
|
+
console.log(chalk14.yellow("Mobb hook already exists, updating..."));
|
|
24666
24893
|
config2.hooks.post_write_code[existingHookIndex] = mobbHook;
|
|
24667
24894
|
} else {
|
|
24668
|
-
console.log(
|
|
24895
|
+
console.log(chalk14.green("Adding new Mobb hook..."));
|
|
24669
24896
|
config2.hooks.post_write_code.push(mobbHook);
|
|
24670
24897
|
}
|
|
24671
24898
|
await writeCodeiumHooks(config2);
|
|
24672
24899
|
console.log(
|
|
24673
|
-
|
|
24900
|
+
chalk14.green(
|
|
24674
24901
|
`\u2705 Mobb hooks ${options.saveEnv ? "and environment variables " : ""}installed successfully in ${hooksPath}`
|
|
24675
24902
|
)
|
|
24676
24903
|
);
|
|
@@ -24720,81 +24947,86 @@ var windsurfIntellijProcessHookHandler = async () => {
|
|
|
24720
24947
|
var parseArgs = async (args) => {
|
|
24721
24948
|
const yargsInstance = yargs(args);
|
|
24722
24949
|
return yargsInstance.updateStrings({
|
|
24723
|
-
"Commands:":
|
|
24724
|
-
"Options:":
|
|
24725
|
-
"Examples:":
|
|
24726
|
-
"Show help":
|
|
24950
|
+
"Commands:": chalk15.yellow.underline.bold("Commands:"),
|
|
24951
|
+
"Options:": chalk15.yellow.underline.bold("Options:"),
|
|
24952
|
+
"Examples:": chalk15.yellow.underline.bold("Examples:"),
|
|
24953
|
+
"Show help": chalk15.bold("Show help")
|
|
24727
24954
|
}).usage(
|
|
24728
|
-
`${
|
|
24955
|
+
`${chalk15.bold(
|
|
24729
24956
|
"\n Bugsy - Trusted, Automatic Vulnerability Fixer \u{1F575}\uFE0F\u200D\u2642\uFE0F\n\n"
|
|
24730
|
-
)} ${
|
|
24731
|
-
$0 ${
|
|
24957
|
+
)} ${chalk15.yellow.underline.bold("Usage:")}
|
|
24958
|
+
$0 ${chalk15.green(
|
|
24732
24959
|
"<command>"
|
|
24733
|
-
)} ${
|
|
24960
|
+
)} ${chalk15.dim("[options]")}
|
|
24734
24961
|
`
|
|
24735
24962
|
).version(false).command(
|
|
24736
24963
|
mobbCliCommand.scan,
|
|
24737
|
-
|
|
24964
|
+
chalk15.bold(
|
|
24738
24965
|
"Scan your code for vulnerabilities, get automated fixes right away."
|
|
24739
24966
|
),
|
|
24740
24967
|
scanBuilder,
|
|
24741
24968
|
scanHandler
|
|
24742
24969
|
).command(
|
|
24743
24970
|
mobbCliCommand.analyze,
|
|
24744
|
-
|
|
24971
|
+
chalk15.bold(
|
|
24745
24972
|
"Provide a code repository, get automated fixes right away. You can also provide a vulnerability report to analyze or have Mobb scan the code for you."
|
|
24746
24973
|
),
|
|
24747
24974
|
analyzeBuilder,
|
|
24748
24975
|
analyzeHandler
|
|
24749
24976
|
).command(
|
|
24750
24977
|
mobbCliCommand.review,
|
|
24751
|
-
|
|
24978
|
+
chalk15.bold(
|
|
24752
24979
|
"Mobb will review your github pull requests and provide comments with fixes "
|
|
24753
24980
|
),
|
|
24754
24981
|
reviewBuilder,
|
|
24755
24982
|
reviewHandler
|
|
24756
24983
|
).command(
|
|
24757
24984
|
mobbCliCommand.addScmToken,
|
|
24758
|
-
|
|
24985
|
+
chalk15.bold(
|
|
24759
24986
|
"Add your SCM (Github, Gitlab, Azure DevOps) token to Mobb to enable automated fixes."
|
|
24760
24987
|
),
|
|
24761
24988
|
addScmTokenBuilder,
|
|
24762
24989
|
addScmTokenHandler
|
|
24990
|
+
).command(
|
|
24991
|
+
mobbCliCommand.scanSkill,
|
|
24992
|
+
chalk15.bold("Scan an OpenClaw/ClawHub skill for security threats."),
|
|
24993
|
+
scanSkillBuilder,
|
|
24994
|
+
scanSkillHandler
|
|
24763
24995
|
).command(
|
|
24764
24996
|
mobbCliCommand.convertToSarif,
|
|
24765
|
-
|
|
24997
|
+
chalk15.bold("Convert an existing SAST report to SARIF format."),
|
|
24766
24998
|
convertToSarifBuilder,
|
|
24767
24999
|
convertToSarifHandler
|
|
24768
25000
|
).command(
|
|
24769
25001
|
mobbCliCommand.mcp,
|
|
24770
|
-
|
|
25002
|
+
chalk15.bold("Launch the MCP (Model Context Protocol) server."),
|
|
24771
25003
|
mcpBuilder,
|
|
24772
25004
|
mcpHandler
|
|
24773
25005
|
).command(
|
|
24774
25006
|
mobbCliCommand.uploadAiBlame,
|
|
24775
|
-
|
|
25007
|
+
chalk15.bold(
|
|
24776
25008
|
"Upload AI Blame inference artifacts (prompt + inference) and finalize them."
|
|
24777
25009
|
),
|
|
24778
25010
|
uploadAiBlameBuilder,
|
|
24779
25011
|
uploadAiBlameCommandHandler
|
|
24780
25012
|
).command(
|
|
24781
25013
|
mobbCliCommand.claudeCodeInstallHook,
|
|
24782
|
-
|
|
25014
|
+
chalk15.bold("Install Claude Code hooks for data collection."),
|
|
24783
25015
|
claudeCodeInstallHookBuilder,
|
|
24784
25016
|
claudeCodeInstallHookHandler
|
|
24785
25017
|
).command(
|
|
24786
25018
|
mobbCliCommand.claudeCodeProcessHook,
|
|
24787
|
-
|
|
25019
|
+
chalk15.bold("Process Claude Code hook data and upload to backend."),
|
|
24788
25020
|
claudeCodeProcessHookBuilder,
|
|
24789
25021
|
claudeCodeProcessHookHandler
|
|
24790
25022
|
).command(
|
|
24791
25023
|
mobbCliCommand.windsurfIntellijInstallHook,
|
|
24792
|
-
|
|
25024
|
+
chalk15.bold("Install Windsurf IntelliJ hooks for data collection."),
|
|
24793
25025
|
windsurfIntellijInstallHookBuilder,
|
|
24794
25026
|
windsurfIntellijInstallHookHandler
|
|
24795
25027
|
).command(
|
|
24796
25028
|
mobbCliCommand.windsurfIntellijProcessHook,
|
|
24797
|
-
|
|
25029
|
+
chalk15.bold("Process Windsurf IntelliJ hook data and upload to backend."),
|
|
24798
25030
|
windsurfIntellijProcessHookBuilder,
|
|
24799
25031
|
windsurfIntellijProcessHookHandler
|
|
24800
25032
|
).example(
|
|
@@ -24805,7 +25037,7 @@ var parseArgs = async (args) => {
|
|
|
24805
25037
|
handler() {
|
|
24806
25038
|
yargsInstance.showHelp();
|
|
24807
25039
|
}
|
|
24808
|
-
}).strictOptions().help("h").alias("h", "help").epilog(
|
|
25040
|
+
}).strictOptions().help("h").alias("h", "help").epilog(chalk15.bgBlue("Made with \u2764\uFE0F by Mobb")).showHelpOnFail(true).wrap(Math.min(120, yargsInstance.terminalWidth())).parse();
|
|
24809
25041
|
};
|
|
24810
25042
|
|
|
24811
25043
|
// src/index.ts
|