mobbdev 1.0.106 → 1.0.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +295 -153
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -1062,19 +1062,33 @@ var AutoPrAnalysisDocument = `
|
|
|
1062
1062
|
var GetLatestReportByRepoUrlDocument = `
|
|
1063
1063
|
query GetLatestReportByRepoUrl($repoUrl: String!, $filters: fix_bool_exp = {}, $limit: Int!, $offset: Int!) {
|
|
1064
1064
|
fixReport(
|
|
1065
|
-
where: {repo: {originalUrl: {_eq: $repoUrl}}}
|
|
1065
|
+
where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Finished}}]}
|
|
1066
1066
|
order_by: {createdOn: desc}
|
|
1067
1067
|
limit: 1
|
|
1068
1068
|
) {
|
|
1069
1069
|
...FixReportSummaryFields
|
|
1070
1070
|
}
|
|
1071
|
+
expiredReport: fixReport(
|
|
1072
|
+
where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Expired}}]}
|
|
1073
|
+
order_by: {createdOn: desc}
|
|
1074
|
+
limit: 1
|
|
1075
|
+
) {
|
|
1076
|
+
id
|
|
1077
|
+
expirationOn
|
|
1078
|
+
}
|
|
1071
1079
|
}
|
|
1072
1080
|
${FixReportSummaryFieldsFragmentDoc}`;
|
|
1073
1081
|
var GetReportFixesDocument = `
|
|
1074
1082
|
query GetReportFixes($reportId: uuid!, $filters: fix_bool_exp = {}, $limit: Int!, $offset: Int!) {
|
|
1075
|
-
fixReport(where: {id: {_eq: $reportId}}) {
|
|
1083
|
+
fixReport(where: {_and: [{id: {_eq: $reportId}}, {state: {_eq: Finished}}]}) {
|
|
1076
1084
|
...FixReportSummaryFields
|
|
1077
1085
|
}
|
|
1086
|
+
expiredReport: fixReport(
|
|
1087
|
+
where: {_and: [{id: {_eq: $reportId}}, {state: {_eq: Expired}}]}
|
|
1088
|
+
) {
|
|
1089
|
+
id
|
|
1090
|
+
expirationOn
|
|
1091
|
+
}
|
|
1078
1092
|
}
|
|
1079
1093
|
${FixReportSummaryFieldsFragmentDoc}`;
|
|
1080
1094
|
var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
|
|
@@ -4824,7 +4838,7 @@ async function getAdoSdk(params) {
|
|
|
4824
4838
|
const url = new URL(repoUrl);
|
|
4825
4839
|
const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
4826
4840
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
4827
|
-
const
|
|
4841
|
+
const path14 = [
|
|
4828
4842
|
prefixPath,
|
|
4829
4843
|
owner,
|
|
4830
4844
|
projectName,
|
|
@@ -4835,7 +4849,7 @@ async function getAdoSdk(params) {
|
|
|
4835
4849
|
"items",
|
|
4836
4850
|
"items"
|
|
4837
4851
|
].filter(Boolean).join("/");
|
|
4838
|
-
return new URL(`${
|
|
4852
|
+
return new URL(`${path14}?${params2}`, origin2).toString();
|
|
4839
4853
|
},
|
|
4840
4854
|
async getAdoBranchList({ repoUrl }) {
|
|
4841
4855
|
try {
|
|
@@ -5492,7 +5506,6 @@ var GitService = class {
|
|
|
5492
5506
|
date: "%ai",
|
|
5493
5507
|
message: "%s",
|
|
5494
5508
|
//the field name author_name can't follow the naming convention as we are using the git log command
|
|
5495
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
5496
5509
|
author_name: "%an"
|
|
5497
5510
|
}
|
|
5498
5511
|
});
|
|
@@ -6874,14 +6887,14 @@ function getGithubSdk(params = {}) {
|
|
|
6874
6887
|
};
|
|
6875
6888
|
},
|
|
6876
6889
|
async getGithubBlameRanges(params2) {
|
|
6877
|
-
const { ref, gitHubUrl, path:
|
|
6890
|
+
const { ref, gitHubUrl, path: path14 } = params2;
|
|
6878
6891
|
const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
|
|
6879
6892
|
const res = await octokit.graphql(
|
|
6880
6893
|
GET_BLAME_DOCUMENT,
|
|
6881
6894
|
{
|
|
6882
6895
|
owner,
|
|
6883
6896
|
repo,
|
|
6884
|
-
path:
|
|
6897
|
+
path: path14,
|
|
6885
6898
|
ref
|
|
6886
6899
|
}
|
|
6887
6900
|
);
|
|
@@ -7190,11 +7203,11 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
7190
7203
|
markdownComment: comment
|
|
7191
7204
|
});
|
|
7192
7205
|
}
|
|
7193
|
-
async getRepoBlameRanges(ref,
|
|
7206
|
+
async getRepoBlameRanges(ref, path14) {
|
|
7194
7207
|
this._validateUrl();
|
|
7195
7208
|
return await this.githubSdk.getGithubBlameRanges({
|
|
7196
7209
|
ref,
|
|
7197
|
-
path:
|
|
7210
|
+
path: path14,
|
|
7198
7211
|
gitHubUrl: this.url
|
|
7199
7212
|
});
|
|
7200
7213
|
}
|
|
@@ -7600,13 +7613,13 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
|
|
|
7600
7613
|
const { organization, repoName, projectPath } = parsingResult;
|
|
7601
7614
|
return { owner: organization, repo: repoName, projectPath };
|
|
7602
7615
|
}
|
|
7603
|
-
async function getGitlabBlameRanges({ ref, gitlabUrl, path:
|
|
7616
|
+
async function getGitlabBlameRanges({ ref, gitlabUrl, path: path14 }, options) {
|
|
7604
7617
|
const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
|
|
7605
7618
|
const api2 = getGitBeaker({
|
|
7606
7619
|
url: gitlabUrl,
|
|
7607
7620
|
gitlabAuthToken: options?.gitlabAuthToken
|
|
7608
7621
|
});
|
|
7609
|
-
const resp = await api2.RepositoryFiles.allFileBlames(projectPath,
|
|
7622
|
+
const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path14, ref);
|
|
7610
7623
|
let lineNumber = 1;
|
|
7611
7624
|
return resp.filter((range) => range.lines).map((range) => {
|
|
7612
7625
|
const oldLineNumber = lineNumber;
|
|
@@ -7782,10 +7795,10 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
7782
7795
|
markdownComment: comment
|
|
7783
7796
|
});
|
|
7784
7797
|
}
|
|
7785
|
-
async getRepoBlameRanges(ref,
|
|
7798
|
+
async getRepoBlameRanges(ref, path14) {
|
|
7786
7799
|
this._validateUrl();
|
|
7787
7800
|
return await getGitlabBlameRanges(
|
|
7788
|
-
{ ref, path:
|
|
7801
|
+
{ ref, path: path14, gitlabUrl: this.url },
|
|
7789
7802
|
{
|
|
7790
7803
|
url: this.url,
|
|
7791
7804
|
gitlabAuthToken: this.accessToken
|
|
@@ -8784,7 +8797,7 @@ async function postIssueComment(params) {
|
|
|
8784
8797
|
fpDescription
|
|
8785
8798
|
} = params;
|
|
8786
8799
|
const {
|
|
8787
|
-
path:
|
|
8800
|
+
path: path14,
|
|
8788
8801
|
startLine,
|
|
8789
8802
|
vulnerabilityReportIssue: {
|
|
8790
8803
|
vulnerabilityReportIssueTags,
|
|
@@ -8799,7 +8812,7 @@ async function postIssueComment(params) {
|
|
|
8799
8812
|
Refresh the page in order to see the changes.`,
|
|
8800
8813
|
pull_number: pullRequest,
|
|
8801
8814
|
commit_id: commitSha,
|
|
8802
|
-
path:
|
|
8815
|
+
path: path14,
|
|
8803
8816
|
line: startLine
|
|
8804
8817
|
});
|
|
8805
8818
|
const commentId = commentRes.data.id;
|
|
@@ -8833,7 +8846,7 @@ async function postFixComment(params) {
|
|
|
8833
8846
|
scanner
|
|
8834
8847
|
} = params;
|
|
8835
8848
|
const {
|
|
8836
|
-
path:
|
|
8849
|
+
path: path14,
|
|
8837
8850
|
startLine,
|
|
8838
8851
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
8839
8852
|
vulnerabilityReportIssueId
|
|
@@ -8851,7 +8864,7 @@ async function postFixComment(params) {
|
|
|
8851
8864
|
Refresh the page in order to see the changes.`,
|
|
8852
8865
|
pull_number: pullRequest,
|
|
8853
8866
|
commit_id: commitSha,
|
|
8854
|
-
path:
|
|
8867
|
+
path: path14,
|
|
8855
8868
|
line: startLine
|
|
8856
8869
|
});
|
|
8857
8870
|
const commentId = commentRes.data.id;
|
|
@@ -11222,7 +11235,8 @@ var Logger = class {
|
|
|
11222
11235
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11223
11236
|
level: logEntry.level,
|
|
11224
11237
|
message: logEntry.message,
|
|
11225
|
-
data: logEntry.data
|
|
11238
|
+
data: logEntry.data,
|
|
11239
|
+
version: packageJson.version
|
|
11226
11240
|
};
|
|
11227
11241
|
const controller = new AbortController();
|
|
11228
11242
|
const timeoutId = setTimeout(() => {
|
|
@@ -11279,7 +11293,7 @@ import { v4 as uuidv42 } from "uuid";
|
|
|
11279
11293
|
var DEFAULT_API_URL2 = "https://api.mobb.ai/v1/graphql";
|
|
11280
11294
|
var API_KEY_HEADER_NAME2 = "x-mobb-key";
|
|
11281
11295
|
|
|
11282
|
-
// src/mcp/
|
|
11296
|
+
// src/mcp/core/Errors.ts
|
|
11283
11297
|
var ApiConnectionError = class extends Error {
|
|
11284
11298
|
constructor(message = "Failed to connect to the API") {
|
|
11285
11299
|
super(message);
|
|
@@ -11354,6 +11368,7 @@ var McpGQLClient = class {
|
|
|
11354
11368
|
__publicField(this, "_auth");
|
|
11355
11369
|
this._auth = args;
|
|
11356
11370
|
const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL2;
|
|
11371
|
+
logDebug("creating graphql client", { API_URL: API_URL2, args });
|
|
11357
11372
|
this.client = new GraphQLClient2(API_URL2, {
|
|
11358
11373
|
headers: args.type === "apiKey" ? { [API_KEY_HEADER_NAME2]: args.apiKey || "" } : {
|
|
11359
11374
|
Authorization: `Bearer ${args.token}`
|
|
@@ -11385,11 +11400,13 @@ var McpGQLClient = class {
|
|
|
11385
11400
|
try {
|
|
11386
11401
|
logDebug("GraphQL: Calling Me query for connection verification");
|
|
11387
11402
|
const result = await this.clientSdk.Me();
|
|
11388
|
-
|
|
11403
|
+
logDebug("GraphQL: Me query successful", { result });
|
|
11389
11404
|
return true;
|
|
11390
11405
|
} catch (e) {
|
|
11391
|
-
|
|
11392
|
-
|
|
11406
|
+
const error = e;
|
|
11407
|
+
logDebug(`verify connection failed ${error.toString()}`);
|
|
11408
|
+
if (error?.toString().includes("FetchError")) {
|
|
11409
|
+
logError("verify connection failed", { error });
|
|
11393
11410
|
return false;
|
|
11394
11411
|
}
|
|
11395
11412
|
}
|
|
@@ -11606,7 +11623,10 @@ var McpGQLClient = class {
|
|
|
11606
11623
|
result: res,
|
|
11607
11624
|
reportCount: res.fixReport?.length || 0
|
|
11608
11625
|
});
|
|
11609
|
-
return
|
|
11626
|
+
return {
|
|
11627
|
+
fixReport: res.fixReport?.[0] || null,
|
|
11628
|
+
expiredReport: res.expiredReport?.[0] || null
|
|
11629
|
+
};
|
|
11610
11630
|
} catch (e) {
|
|
11611
11631
|
logError("GraphQL: GetLatestReportByRepoUrl failed", {
|
|
11612
11632
|
error: e,
|
|
@@ -11655,7 +11675,8 @@ var McpGQLClient = class {
|
|
|
11655
11675
|
}
|
|
11656
11676
|
return {
|
|
11657
11677
|
fixes: res.fixReport?.[0]?.fixes || [],
|
|
11658
|
-
totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0
|
|
11678
|
+
totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0,
|
|
11679
|
+
expiredReport: res.expiredReport?.[0] || null
|
|
11659
11680
|
};
|
|
11660
11681
|
} catch (e) {
|
|
11661
11682
|
logError("GraphQL: GetReportFixes failed", {
|
|
@@ -11680,17 +11701,21 @@ async function openBrowser(url) {
|
|
|
11680
11701
|
async function getMcpGQLClient() {
|
|
11681
11702
|
logDebug("getting config", { apiToken: config4.get("apiToken") });
|
|
11682
11703
|
const inGqlClient = new McpGQLClient({
|
|
11683
|
-
apiKey:
|
|
11704
|
+
apiKey: process.env["MOBB_API_KEY"] || process.env["API_KEY"] || // fallback for backward compatibility
|
|
11705
|
+
config4.get("apiToken") || "",
|
|
11684
11706
|
type: "apiKey"
|
|
11685
11707
|
});
|
|
11686
11708
|
const isConnected = await inGqlClient.verifyConnection();
|
|
11709
|
+
logDebug("isConnected", { isConnected });
|
|
11687
11710
|
if (!isConnected) {
|
|
11688
11711
|
throw new ApiConnectionError("Error: failed to connect to Mobb API");
|
|
11689
11712
|
}
|
|
11713
|
+
logDebug("verifying token");
|
|
11690
11714
|
const userVerify = await inGqlClient.verifyToken();
|
|
11691
11715
|
if (userVerify) {
|
|
11692
11716
|
return inGqlClient;
|
|
11693
11717
|
}
|
|
11718
|
+
logDebug("verifying token failed");
|
|
11694
11719
|
const { publicKey, privateKey } = crypto2.generateKeyPairSync("rsa", {
|
|
11695
11720
|
modulusLength: 2048
|
|
11696
11721
|
});
|
|
@@ -11838,15 +11863,9 @@ var McpServer = class {
|
|
|
11838
11863
|
}
|
|
11839
11864
|
async handleListToolsRequest(request) {
|
|
11840
11865
|
logInfo("Received list_tools request", { params: request.params });
|
|
11841
|
-
logInfo("Environment", {
|
|
11842
|
-
env: process.env
|
|
11843
|
-
});
|
|
11844
11866
|
logInfo("Request", {
|
|
11845
11867
|
request: JSON.parse(JSON.stringify(request))
|
|
11846
11868
|
});
|
|
11847
|
-
logInfo("Server", {
|
|
11848
|
-
server: this.server
|
|
11849
|
-
});
|
|
11850
11869
|
void getMcpGQLClient();
|
|
11851
11870
|
const toolsDefinitions = this.toolRegistry.getAllTools();
|
|
11852
11871
|
const response = {
|
|
@@ -11867,15 +11886,9 @@ var McpServer = class {
|
|
|
11867
11886
|
async handleCallToolRequest(request) {
|
|
11868
11887
|
const { name, arguments: args } = request.params;
|
|
11869
11888
|
logInfo(`Received call tool request for ${name}`, { name, args });
|
|
11870
|
-
logInfo("Environment", {
|
|
11871
|
-
env: process.env
|
|
11872
|
-
});
|
|
11873
11889
|
logInfo("Request", {
|
|
11874
11890
|
request: JSON.parse(JSON.stringify(request))
|
|
11875
11891
|
});
|
|
11876
|
-
logInfo("Server", {
|
|
11877
|
-
server: this.server
|
|
11878
|
-
});
|
|
11879
11892
|
try {
|
|
11880
11893
|
const tool = this.toolRegistry.getTool(name);
|
|
11881
11894
|
if (!tool) {
|
|
@@ -11938,53 +11951,71 @@ var McpServer = class {
|
|
|
11938
11951
|
}
|
|
11939
11952
|
};
|
|
11940
11953
|
|
|
11941
|
-
// src/mcp/tools/
|
|
11954
|
+
// src/mcp/tools/fetchAvailableFixes/FetchAvailableFixesTool.ts
|
|
11942
11955
|
import { z as z32 } from "zod";
|
|
11943
11956
|
|
|
11944
11957
|
// src/mcp/services/PathValidation.ts
|
|
11945
11958
|
import fs9 from "fs";
|
|
11946
11959
|
import path11 from "path";
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
const error = `
|
|
11961
|
-
logError(error);
|
|
11962
|
-
return { isValid: false, error };
|
|
11963
|
-
}
|
|
11964
|
-
const decodedPath = decodeURIComponent(inputPath);
|
|
11965
|
-
if (decodedPath.includes("..") || decodedPath !== inputPath) {
|
|
11966
|
-
const error = `Path contains encoded traversal attempts: ${inputPath}`;
|
|
11967
|
-
logError(error);
|
|
11968
|
-
return { isValid: false, error };
|
|
11969
|
-
}
|
|
11970
|
-
if (inputPath.includes("\0") || inputPath.includes("\0")) {
|
|
11971
|
-
const error = `Path contains dangerous characters: ${inputPath}`;
|
|
11960
|
+
async function validatePath(inputPath) {
|
|
11961
|
+
logDebug("Validating MCP path", { inputPath });
|
|
11962
|
+
if (inputPath === ".") {
|
|
11963
|
+
if (process.env["WORKSPACE_FOLDER_PATHS"]) {
|
|
11964
|
+
logDebug("Fallback to workspace folder path", {
|
|
11965
|
+
inputPath,
|
|
11966
|
+
workspaceFolderPaths: process.env["WORKSPACE_FOLDER_PATHS"]
|
|
11967
|
+
});
|
|
11968
|
+
return {
|
|
11969
|
+
isValid: true,
|
|
11970
|
+
path: process.env["WORKSPACE_FOLDER_PATHS"]
|
|
11971
|
+
};
|
|
11972
|
+
} else {
|
|
11973
|
+
const error = `"." is not a valid path, please provide a full localpath to the repository`;
|
|
11972
11974
|
logError(error);
|
|
11973
|
-
return { isValid: false, error };
|
|
11974
|
-
}
|
|
11975
|
-
logDebug("Path validation successful", { inputPath });
|
|
11976
|
-
logDebug("Checking path existence", { inputPath });
|
|
11977
|
-
try {
|
|
11978
|
-
await fs9.promises.access(inputPath);
|
|
11979
|
-
logDebug("Path exists and is accessible", { inputPath });
|
|
11980
|
-
return { isValid: true };
|
|
11981
|
-
} catch (error) {
|
|
11982
|
-
const errorMessage = `Path does not exist or is not accessible: ${inputPath}`;
|
|
11983
|
-
logError(errorMessage, { error });
|
|
11984
|
-
return { isValid: false, error: errorMessage };
|
|
11975
|
+
return { isValid: false, error, path: inputPath };
|
|
11985
11976
|
}
|
|
11986
11977
|
}
|
|
11987
|
-
|
|
11978
|
+
if (inputPath.includes("..")) {
|
|
11979
|
+
const error = `Path contains path traversal patterns: ${inputPath}`;
|
|
11980
|
+
logError(error);
|
|
11981
|
+
return { isValid: false, error, path: inputPath };
|
|
11982
|
+
}
|
|
11983
|
+
const normalizedPath = path11.normalize(inputPath);
|
|
11984
|
+
if (normalizedPath.includes("..")) {
|
|
11985
|
+
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
11986
|
+
logError(error);
|
|
11987
|
+
return { isValid: false, error, path: inputPath };
|
|
11988
|
+
}
|
|
11989
|
+
let decodedPath;
|
|
11990
|
+
try {
|
|
11991
|
+
decodedPath = decodeURIComponent(inputPath);
|
|
11992
|
+
} catch (err) {
|
|
11993
|
+
const error = `Failed to decode path: ${inputPath}`;
|
|
11994
|
+
logError(error, { err });
|
|
11995
|
+
return { isValid: false, error, path: inputPath };
|
|
11996
|
+
}
|
|
11997
|
+
if (decodedPath.includes("..") || decodedPath !== inputPath) {
|
|
11998
|
+
const error = `Path contains encoded traversal attempts: ${inputPath}`;
|
|
11999
|
+
logError(error);
|
|
12000
|
+
return { isValid: false, error, path: inputPath };
|
|
12001
|
+
}
|
|
12002
|
+
if (inputPath.includes("\0") || inputPath.includes("\0")) {
|
|
12003
|
+
const error = `Path contains dangerous characters: ${inputPath}`;
|
|
12004
|
+
logError(error);
|
|
12005
|
+
return { isValid: false, error, path: inputPath };
|
|
12006
|
+
}
|
|
12007
|
+
logDebug("Path validation successful", { inputPath });
|
|
12008
|
+
logDebug("Checking path existence", { inputPath });
|
|
12009
|
+
try {
|
|
12010
|
+
await fs9.promises.access(inputPath);
|
|
12011
|
+
logDebug("Path exists and is accessible", { inputPath });
|
|
12012
|
+
return { isValid: true, path: inputPath };
|
|
12013
|
+
} catch (error) {
|
|
12014
|
+
const errorMessage = `Path does not exist or is not accessible: ${inputPath}`;
|
|
12015
|
+
logError(errorMessage, { error });
|
|
12016
|
+
return { isValid: false, error: errorMessage, path: inputPath };
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
11988
12019
|
|
|
11989
12020
|
// src/mcp/tools/base/BaseTool.ts
|
|
11990
12021
|
import { z as z31 } from "zod";
|
|
@@ -12053,7 +12084,8 @@ var applyFixesPrompt = ({
|
|
|
12053
12084
|
totalCount,
|
|
12054
12085
|
nextOffset,
|
|
12055
12086
|
shownCount,
|
|
12056
|
-
currentTool
|
|
12087
|
+
currentTool,
|
|
12088
|
+
offset = 0
|
|
12057
12089
|
}) => {
|
|
12058
12090
|
if (fixes.length === 0) {
|
|
12059
12091
|
return noFixesReturnedForParameters;
|
|
@@ -12111,7 +12143,7 @@ If you cannot apply a patch:
|
|
|
12111
12143
|
|
|
12112
12144
|
${fixList.map(
|
|
12113
12145
|
(fix, index) => `
|
|
12114
|
-
## Fix ${index + 1}: ${fix.vulnerabilityType}
|
|
12146
|
+
## Fix ${offset + index + 1}: ${fix.vulnerabilityType}
|
|
12115
12147
|
|
|
12116
12148
|
**\u{1F3AF} Target:** Apply this patch to fix a ${fix.vulnerabilityType.toLowerCase()} vulnerability
|
|
12117
12149
|
|
|
@@ -12164,7 +12196,7 @@ We were unable to find a previous vulnerability report for this repository. This
|
|
|
12164
12196
|
|
|
12165
12197
|
### \u{1F3AF} Recommended Actions
|
|
12166
12198
|
1. **Run a new security scan** to analyze your codebase
|
|
12167
|
-
- Use the \`
|
|
12199
|
+
- Use the \`scan_and_fix_vulnerabilities\` tool to start a fresh scan
|
|
12168
12200
|
- This will analyze your current code for security issues
|
|
12169
12201
|
|
|
12170
12202
|
2. **Verify repository access**
|
|
@@ -12178,6 +12210,27 @@ We were unable to find a previous vulnerability report for this repository. This
|
|
|
12178
12210
|
For assistance, please:
|
|
12179
12211
|
- Visit our documentation at https://docs.mobb.ai
|
|
12180
12212
|
- Contact support at support@mobb.ai`;
|
|
12213
|
+
var expiredReportPrompt = ({
|
|
12214
|
+
lastReportDate
|
|
12215
|
+
}) => `\u{1F50D} **MOBB SECURITY SCAN STATUS**
|
|
12216
|
+
|
|
12217
|
+
## Out-of-Date Vulnerability Report
|
|
12218
|
+
|
|
12219
|
+
Your most recent vulnerability report for this repository **expired on ${lastReportDate}** and is no longer available for fetching automated fixes.
|
|
12220
|
+
|
|
12221
|
+
### \u{1F4CB} Why Did This Happen?
|
|
12222
|
+
- Reports are automatically purged after a retention period for security and storage optimisation.
|
|
12223
|
+
- No new scans have been run since the last report expired.
|
|
12224
|
+
|
|
12225
|
+
### \u{1F3AF} Recommended Actions
|
|
12226
|
+
1. **Run a fresh security scan** to generate an up-to-date vulnerability report.
|
|
12227
|
+
- Use the \`scan_and_fix_vulnerabilities\` tool.
|
|
12228
|
+
2. **Verify repository access** if scans fail to run or the repository has moved.
|
|
12229
|
+
3. **Review your CI/CD pipeline** to ensure regular scans are triggered.
|
|
12230
|
+
|
|
12231
|
+
For more help:
|
|
12232
|
+
- Documentation: https://docs.mobb.ai
|
|
12233
|
+
- Support: support@mobb.ai`;
|
|
12181
12234
|
var noFixesAvailablePrompt = `There are no fixes available for this repository at this time.
|
|
12182
12235
|
`;
|
|
12183
12236
|
var fixesFoundPrompt = ({
|
|
@@ -12225,7 +12278,8 @@ ${applyFixesPrompt({
|
|
|
12225
12278
|
hasMore,
|
|
12226
12279
|
nextOffset: 0,
|
|
12227
12280
|
shownCount: fixReport.fixes.length,
|
|
12228
|
-
currentTool: "
|
|
12281
|
+
currentTool: "fetch_available_fixes",
|
|
12282
|
+
offset
|
|
12229
12283
|
})}`;
|
|
12230
12284
|
};
|
|
12231
12285
|
var noFixesFoundPrompt = `\u{1F50D} **MOBB SECURITY SCAN COMPLETED**
|
|
@@ -12235,7 +12289,8 @@ Mobb security scan completed successfully but found no automated fixes available
|
|
|
12235
12289
|
var fixesPrompt = ({
|
|
12236
12290
|
fixes,
|
|
12237
12291
|
totalCount,
|
|
12238
|
-
offset
|
|
12292
|
+
offset,
|
|
12293
|
+
scannedFiles
|
|
12239
12294
|
}) => {
|
|
12240
12295
|
if (totalCount === 0) {
|
|
12241
12296
|
return noFixesFoundPrompt;
|
|
@@ -12251,9 +12306,13 @@ ${applyFixesPrompt({
|
|
|
12251
12306
|
hasMore,
|
|
12252
12307
|
nextOffset,
|
|
12253
12308
|
shownCount,
|
|
12254
|
-
currentTool: "
|
|
12309
|
+
currentTool: "scan_and_fix_vulnerabilities",
|
|
12310
|
+
offset
|
|
12255
12311
|
})}
|
|
12256
12312
|
|
|
12313
|
+
### \u{1F4C1} Scanned Files
|
|
12314
|
+
${scannedFiles.map((file) => `- ${file}`).join("\n")}
|
|
12315
|
+
|
|
12257
12316
|
### \u{1F504} Running a Fresh Scan
|
|
12258
12317
|
|
|
12259
12318
|
To perform a **rescan** of your repository (fetching a brand-new vulnerability report and updated fixes), include the additional parameter:
|
|
@@ -12267,12 +12326,21 @@ This will start a new analysis, discard any cached results.
|
|
|
12267
12326
|
`;
|
|
12268
12327
|
};
|
|
12269
12328
|
|
|
12270
|
-
// src/mcp/tools/
|
|
12271
|
-
var
|
|
12329
|
+
// src/mcp/tools/fetchAvailableFixes/FetchAvailableFixesService.ts
|
|
12330
|
+
var _FetchAvailableFixesService = class _FetchAvailableFixesService {
|
|
12272
12331
|
constructor() {
|
|
12273
12332
|
__publicField(this, "gqlClient", null);
|
|
12274
12333
|
__publicField(this, "currentOffset", 0);
|
|
12275
12334
|
}
|
|
12335
|
+
static getInstance() {
|
|
12336
|
+
if (!_FetchAvailableFixesService.instance) {
|
|
12337
|
+
_FetchAvailableFixesService.instance = new _FetchAvailableFixesService();
|
|
12338
|
+
}
|
|
12339
|
+
return _FetchAvailableFixesService.instance;
|
|
12340
|
+
}
|
|
12341
|
+
reset() {
|
|
12342
|
+
this.currentOffset = 0;
|
|
12343
|
+
}
|
|
12276
12344
|
async initializeGqlClient() {
|
|
12277
12345
|
if (!this.gqlClient) {
|
|
12278
12346
|
this.gqlClient = await getMcpGQLClient();
|
|
@@ -12282,40 +12350,44 @@ var AvailableFixesService = class {
|
|
|
12282
12350
|
async checkForAvailableFixes({
|
|
12283
12351
|
repoUrl,
|
|
12284
12352
|
limit = 3,
|
|
12285
|
-
offset
|
|
12353
|
+
offset
|
|
12286
12354
|
}) {
|
|
12287
12355
|
try {
|
|
12288
12356
|
logDebug("Checking for available fixes", { repoUrl, limit });
|
|
12289
12357
|
const gqlClient = await this.initializeGqlClient();
|
|
12290
12358
|
logDebug("GQL client initialized");
|
|
12291
12359
|
logDebug("querying for latest report", { repoUrl, limit });
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
} else if (this.currentOffset) {
|
|
12296
|
-
effectiveOffset = this.currentOffset ?? 0;
|
|
12297
|
-
} else {
|
|
12298
|
-
effectiveOffset = 0;
|
|
12299
|
-
}
|
|
12300
|
-
logDebug("effectiveOffset", { test: "j", effectiveOffset });
|
|
12301
|
-
const result = await gqlClient.getLatestReportByRepoUrl({
|
|
12360
|
+
const effectiveOffset = offset ?? (this.currentOffset || 0);
|
|
12361
|
+
logDebug("effectiveOffset", { effectiveOffset });
|
|
12362
|
+
const { fixReport, expiredReport } = await gqlClient.getLatestReportByRepoUrl({
|
|
12302
12363
|
repoUrl,
|
|
12303
12364
|
limit,
|
|
12304
12365
|
offset: effectiveOffset
|
|
12305
12366
|
});
|
|
12306
|
-
logDebug("received latest report result", {
|
|
12307
|
-
if (!
|
|
12308
|
-
|
|
12367
|
+
logDebug("received latest report result", { fixReport, expiredReport });
|
|
12368
|
+
if (!fixReport) {
|
|
12369
|
+
if (expiredReport) {
|
|
12370
|
+
const lastReportDate = expiredReport.expirationOn ? new Date(expiredReport.expirationOn).toLocaleString() : "Unknown date";
|
|
12371
|
+
logInfo("Expired report found", {
|
|
12372
|
+
repoUrl,
|
|
12373
|
+
expirationOn: expiredReport.expirationOn
|
|
12374
|
+
});
|
|
12375
|
+
return expiredReportPrompt({ lastReportDate });
|
|
12376
|
+
}
|
|
12377
|
+
logInfo("No report (active or expired) found for repository", {
|
|
12378
|
+
repoUrl
|
|
12379
|
+
});
|
|
12309
12380
|
return noReportFoundPrompt;
|
|
12310
12381
|
}
|
|
12311
12382
|
logInfo("Successfully retrieved available fixes", {
|
|
12312
12383
|
reportFound: true
|
|
12313
12384
|
});
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
offset: this.currentOffset
|
|
12385
|
+
const prompt = fixesFoundPrompt({
|
|
12386
|
+
fixReport,
|
|
12387
|
+
offset: effectiveOffset
|
|
12318
12388
|
});
|
|
12389
|
+
this.currentOffset = effectiveOffset + (fixReport.fixes?.length || 0);
|
|
12390
|
+
return prompt;
|
|
12319
12391
|
} catch (error) {
|
|
12320
12392
|
logError("Failed to check for available fixes", {
|
|
12321
12393
|
error,
|
|
@@ -12325,20 +12397,40 @@ var AvailableFixesService = class {
|
|
|
12325
12397
|
}
|
|
12326
12398
|
}
|
|
12327
12399
|
};
|
|
12400
|
+
__publicField(_FetchAvailableFixesService, "instance");
|
|
12401
|
+
var FetchAvailableFixesService = _FetchAvailableFixesService;
|
|
12328
12402
|
|
|
12329
|
-
// src/mcp/tools/
|
|
12330
|
-
var
|
|
12403
|
+
// src/mcp/tools/fetchAvailableFixes/FetchAvailableFixesTool.ts
|
|
12404
|
+
var FetchAvailableFixesTool = class extends BaseTool {
|
|
12331
12405
|
constructor() {
|
|
12332
|
-
super(
|
|
12333
|
-
__publicField(this, "name", "
|
|
12334
|
-
__publicField(this, "displayName", "
|
|
12335
|
-
__publicField(this, "description",
|
|
12406
|
+
super();
|
|
12407
|
+
__publicField(this, "name", "fetch_available_fixes");
|
|
12408
|
+
__publicField(this, "displayName", "Fetch Available Fixes");
|
|
12409
|
+
__publicField(this, "description", `Check the MOBB backend for pre-generated fixes (patch sets) that correspond to vulnerabilities detected in the supplied Git repository.
|
|
12410
|
+
|
|
12411
|
+
Use when:
|
|
12412
|
+
\u2022 You already have a local clone of a Git repository and want to know if MOBB has fixes available for it.
|
|
12413
|
+
\u2022 A vulnerability scan has been run previously and uploaded to the MOBB backend and you want to fetch the list or count of ready-to-apply fixes before triggering a full scan-and-fix flow.
|
|
12414
|
+
|
|
12415
|
+
Required argument:
|
|
12416
|
+
\u2022 path \u2013 absolute path to the local Git repository clone.
|
|
12417
|
+
|
|
12418
|
+
Optional arguments:
|
|
12419
|
+
\u2022 offset \u2013 pagination offset (integer).
|
|
12420
|
+
\u2022 limit \u2013 maximum number of fixes to return (integer).
|
|
12421
|
+
|
|
12422
|
+
The tool will:
|
|
12423
|
+
1. Validate that the provided path is secure and exists.
|
|
12424
|
+
2. Verify that the directory is a valid Git repository with an "origin" remote.
|
|
12425
|
+
3. Query the MOBB service by the origin remote URL and return a textual summary of available fixes (total and by severity) or a message if none are found.
|
|
12426
|
+
|
|
12427
|
+
Call this tool instead of scan_and_fix_vulnerabilities when you only need a fixes summary and do NOT want to perform scanning or code modifications.`);
|
|
12336
12428
|
__publicField(this, "inputSchema", {
|
|
12337
12429
|
type: "object",
|
|
12338
12430
|
properties: {
|
|
12339
12431
|
path: {
|
|
12340
12432
|
type: "string",
|
|
12341
|
-
description: "
|
|
12433
|
+
description: "Full local path to the cloned git repository to check for available fixes"
|
|
12342
12434
|
},
|
|
12343
12435
|
offset: {
|
|
12344
12436
|
type: "number",
|
|
@@ -12353,21 +12445,24 @@ var CheckForAvailableFixesTool = class extends BaseTool {
|
|
|
12353
12445
|
});
|
|
12354
12446
|
__publicField(this, "inputValidationSchema", z32.object({
|
|
12355
12447
|
path: z32.string().describe(
|
|
12356
|
-
"
|
|
12448
|
+
"Full local path to the cloned git repository to check for available fixes"
|
|
12357
12449
|
),
|
|
12358
12450
|
offset: z32.number().optional().describe("Optional offset for pagination"),
|
|
12359
12451
|
limit: z32.number().optional().describe("Optional maximum number of fixes to return")
|
|
12360
12452
|
}));
|
|
12453
|
+
__publicField(this, "availableFixesService");
|
|
12454
|
+
this.availableFixesService = FetchAvailableFixesService.getInstance();
|
|
12455
|
+
this.availableFixesService.reset();
|
|
12361
12456
|
}
|
|
12362
12457
|
async executeInternal(args) {
|
|
12363
|
-
const
|
|
12364
|
-
const pathValidationResult = await pathValidation.validatePath(args.path);
|
|
12458
|
+
const pathValidationResult = await validatePath(args.path);
|
|
12365
12459
|
if (!pathValidationResult.isValid) {
|
|
12366
12460
|
throw new Error(
|
|
12367
12461
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
12368
12462
|
);
|
|
12369
12463
|
}
|
|
12370
|
-
const
|
|
12464
|
+
const path14 = pathValidationResult.path;
|
|
12465
|
+
const gitService = new GitService(path14, log);
|
|
12371
12466
|
const gitValidation = await gitService.validateRepository();
|
|
12372
12467
|
if (!gitValidation.isValid) {
|
|
12373
12468
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -12380,13 +12475,12 @@ var CheckForAvailableFixesTool = class extends BaseTool {
|
|
|
12380
12475
|
if (!originUrl) {
|
|
12381
12476
|
throw new Error("No origin URL found for the repository");
|
|
12382
12477
|
}
|
|
12383
|
-
const
|
|
12384
|
-
const fixResult = await availableFixesService.checkForAvailableFixes({
|
|
12478
|
+
const fixResult = await this.availableFixesService.checkForAvailableFixes({
|
|
12385
12479
|
repoUrl: originUrl,
|
|
12386
12480
|
limit: args.limit,
|
|
12387
12481
|
offset: args.offset
|
|
12388
12482
|
});
|
|
12389
|
-
logInfo("
|
|
12483
|
+
logInfo("FetchAvailableFixesTool execution completed successfully", {
|
|
12390
12484
|
fixResult
|
|
12391
12485
|
});
|
|
12392
12486
|
return {
|
|
@@ -12400,9 +12494,12 @@ var CheckForAvailableFixesTool = class extends BaseTool {
|
|
|
12400
12494
|
}
|
|
12401
12495
|
};
|
|
12402
12496
|
|
|
12403
|
-
// src/mcp/tools/
|
|
12497
|
+
// src/mcp/tools/scanAndFixVulnerabilities/ScanAndFixVulnerabilitiesTool.ts
|
|
12404
12498
|
import z33 from "zod";
|
|
12405
12499
|
|
|
12500
|
+
// src/mcp/tools/scanAndFixVulnerabilities/ScanAndFixVulnerabilitiesService.ts
|
|
12501
|
+
import path13 from "path";
|
|
12502
|
+
|
|
12406
12503
|
// src/mcp/services/FilePacking.ts
|
|
12407
12504
|
import fs10 from "fs";
|
|
12408
12505
|
import path12 from "path";
|
|
@@ -12443,9 +12540,9 @@ var FilePacking = class {
|
|
|
12443
12540
|
}
|
|
12444
12541
|
};
|
|
12445
12542
|
|
|
12446
|
-
// src/mcp/tools/
|
|
12543
|
+
// src/mcp/tools/scanAndFixVulnerabilities/ScanAndFixVulnerabilitiesService.ts
|
|
12447
12544
|
var VUL_REPORT_DIGEST_TIMEOUT_MS2 = 1e3 * 60 * 5;
|
|
12448
|
-
var
|
|
12545
|
+
var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService {
|
|
12449
12546
|
constructor() {
|
|
12450
12547
|
__publicField(this, "gqlClient");
|
|
12451
12548
|
__publicField(this, "filePacking");
|
|
@@ -12458,6 +12555,16 @@ var VulnerabilityFixService = class {
|
|
|
12458
12555
|
__publicField(this, "currentOffset", 0);
|
|
12459
12556
|
this.filePacking = new FilePacking();
|
|
12460
12557
|
}
|
|
12558
|
+
static getInstance() {
|
|
12559
|
+
if (!_ScanAndFixVulnerabilitiesService.instance) {
|
|
12560
|
+
_ScanAndFixVulnerabilitiesService.instance = new _ScanAndFixVulnerabilitiesService();
|
|
12561
|
+
}
|
|
12562
|
+
return _ScanAndFixVulnerabilitiesService.instance;
|
|
12563
|
+
}
|
|
12564
|
+
reset() {
|
|
12565
|
+
this.storedFixReportId = void 0;
|
|
12566
|
+
this.currentOffset = void 0;
|
|
12567
|
+
}
|
|
12461
12568
|
async processVulnerabilities({
|
|
12462
12569
|
fileList,
|
|
12463
12570
|
repositoryPath,
|
|
@@ -12467,8 +12574,13 @@ var VulnerabilityFixService = class {
|
|
|
12467
12574
|
}) {
|
|
12468
12575
|
try {
|
|
12469
12576
|
this.gqlClient = await this.initializeGqlClient();
|
|
12577
|
+
logInfo("storedFixReportId", {
|
|
12578
|
+
storedFixReportId: this.storedFixReportId,
|
|
12579
|
+
currentOffset: this.currentOffset
|
|
12580
|
+
});
|
|
12470
12581
|
let fixReportId = this.storedFixReportId;
|
|
12471
12582
|
if (!fixReportId || isRescan) {
|
|
12583
|
+
this.reset();
|
|
12472
12584
|
this.validateFiles(fileList);
|
|
12473
12585
|
const repoUploadInfo = await this.initializeReport();
|
|
12474
12586
|
fixReportId = repoUploadInfo.fixReportId;
|
|
@@ -12478,26 +12590,21 @@ var VulnerabilityFixService = class {
|
|
|
12478
12590
|
const projectId = await this.getProjectId();
|
|
12479
12591
|
await this.runScan({ fixReportId, projectId });
|
|
12480
12592
|
}
|
|
12481
|
-
|
|
12482
|
-
if (offset !== void 0) {
|
|
12483
|
-
effectiveOffset = offset;
|
|
12484
|
-
} else if (fixReportId) {
|
|
12485
|
-
effectiveOffset = this.currentOffset ?? 0;
|
|
12486
|
-
} else {
|
|
12487
|
-
effectiveOffset = 0;
|
|
12488
|
-
}
|
|
12593
|
+
const effectiveOffset = offset ?? (this.currentOffset || 0);
|
|
12489
12594
|
logDebug("effectiveOffset", { effectiveOffset });
|
|
12490
12595
|
const fixes = await this.getReportFixes(
|
|
12491
12596
|
fixReportId,
|
|
12492
12597
|
effectiveOffset,
|
|
12493
12598
|
limit
|
|
12494
12599
|
);
|
|
12495
|
-
|
|
12496
|
-
return fixesPrompt({
|
|
12600
|
+
const prompt = fixesPrompt({
|
|
12497
12601
|
fixes: fixes.fixes,
|
|
12498
12602
|
totalCount: fixes.totalCount,
|
|
12499
|
-
offset: effectiveOffset
|
|
12603
|
+
offset: effectiveOffset,
|
|
12604
|
+
scannedFiles: fileList.map((file) => path13.basename(file))
|
|
12500
12605
|
});
|
|
12606
|
+
this.currentOffset = effectiveOffset + (fixes.fixes?.length || 0);
|
|
12607
|
+
return prompt;
|
|
12501
12608
|
} catch (error) {
|
|
12502
12609
|
const message = error.message;
|
|
12503
12610
|
logError("Vulnerability processing failed", { error: message });
|
|
@@ -12514,7 +12621,7 @@ var VulnerabilityFixService = class {
|
|
|
12514
12621
|
const isConnected = await gqlClient.verifyConnection();
|
|
12515
12622
|
if (!isConnected) {
|
|
12516
12623
|
throw new ApiConnectionError(
|
|
12517
|
-
"Failed to connect to the API. Please check your
|
|
12624
|
+
"Failed to connect to the API. Please check your MOBB_API_KEY"
|
|
12518
12625
|
);
|
|
12519
12626
|
}
|
|
12520
12627
|
return gqlClient;
|
|
@@ -12631,17 +12738,50 @@ var VulnerabilityFixService = class {
|
|
|
12631
12738
|
};
|
|
12632
12739
|
}
|
|
12633
12740
|
};
|
|
12741
|
+
__publicField(_ScanAndFixVulnerabilitiesService, "instance");
|
|
12742
|
+
var ScanAndFixVulnerabilitiesService = _ScanAndFixVulnerabilitiesService;
|
|
12634
12743
|
|
|
12635
|
-
// src/mcp/tools/
|
|
12636
|
-
var
|
|
12744
|
+
// src/mcp/tools/scanAndFixVulnerabilities/ScanAndFixVulnerabilitiesTool.ts
|
|
12745
|
+
var ScanAndFixVulnerabilitiesTool = class extends BaseTool {
|
|
12637
12746
|
constructor() {
|
|
12638
|
-
super(
|
|
12639
|
-
__publicField(this, "name", "
|
|
12640
|
-
__publicField(this, "displayName", "Fix Vulnerabilities");
|
|
12641
|
-
|
|
12747
|
+
super();
|
|
12748
|
+
__publicField(this, "name", "scan_and_fix_vulnerabilities");
|
|
12749
|
+
__publicField(this, "displayName", "Scan and Fix Vulnerabilities");
|
|
12750
|
+
// A detailed description to guide the LLM on when and how to invoke this tool.
|
|
12751
|
+
__publicField(this, "description", `Scans a given local repository for security vulnerabilities and returns auto-generated code fixes.
|
|
12752
|
+
|
|
12753
|
+
When to invoke:
|
|
12754
|
+
\u2022 Use when the user explicitly asks to "scan for vulnerabilities", "run a security check", or "test for security issues" in a local repository.
|
|
12755
|
+
\u2022 The repository must exist on disk; supply its absolute path with the required "path" argument.
|
|
12756
|
+
\u2022 Ideal after the user makes code changes (added/modified/staged files) but before committing, or whenever they request a full rescan.
|
|
12757
|
+
|
|
12758
|
+
How to invoke:
|
|
12759
|
+
\u2022 Required argument:
|
|
12760
|
+
\u2013 path (string): absolute path to the repository root.
|
|
12761
|
+
\u2022 Optional arguments:
|
|
12762
|
+
\u2013 offset (number): pagination offset used when the result set is large.
|
|
12763
|
+
\u2013 limit (number): maximum number of fixes to include in the response.
|
|
12764
|
+
\u2013 rescan (boolean): true to force a complete rescan even if cached results exist.
|
|
12765
|
+
|
|
12766
|
+
Behaviour:
|
|
12767
|
+
\u2022 If the directory is not a valid Git repository, the tool falls back to scanning recently changed files in the folder.
|
|
12768
|
+
\u2022 By default, only new, modified, or staged files are scanned; if none are found, it checks recently changed files.
|
|
12769
|
+
\u2022 The tool NEVER commits or pushes changes; it only returns proposed diffs/fixes as text.
|
|
12770
|
+
|
|
12771
|
+
Return value:
|
|
12772
|
+
The response is an object with a single "content" array containing one text element. The text is either:
|
|
12773
|
+
\u2022 A human-readable summary of the fixes / patches, or
|
|
12774
|
+
\u2022 A diagnostic or error message if the scan fails or finds nothing to fix.
|
|
12775
|
+
|
|
12776
|
+
Example payload:
|
|
12777
|
+
{
|
|
12778
|
+
"path": "/home/user/my-project",
|
|
12779
|
+
"limit": 20,
|
|
12780
|
+
"rescan": false
|
|
12781
|
+
}`);
|
|
12642
12782
|
__publicField(this, "inputValidationSchema", z33.object({
|
|
12643
12783
|
path: z33.string().describe(
|
|
12644
|
-
"
|
|
12784
|
+
"Full local path to repository to scan and fix vulnerabilities"
|
|
12645
12785
|
),
|
|
12646
12786
|
offset: z33.number().optional().describe("Optional offset for pagination"),
|
|
12647
12787
|
limit: z33.number().optional().describe("Optional maximum number of results to return"),
|
|
@@ -12652,7 +12792,7 @@ var FixVulnerabilitiesTool = class extends BaseTool {
|
|
|
12652
12792
|
properties: {
|
|
12653
12793
|
path: {
|
|
12654
12794
|
type: "string",
|
|
12655
|
-
description: "
|
|
12795
|
+
description: "Full local path to repository to scan and fix vulnerabilities"
|
|
12656
12796
|
},
|
|
12657
12797
|
offset: {
|
|
12658
12798
|
type: "number",
|
|
@@ -12669,30 +12809,33 @@ var FixVulnerabilitiesTool = class extends BaseTool {
|
|
|
12669
12809
|
},
|
|
12670
12810
|
required: ["path"]
|
|
12671
12811
|
});
|
|
12812
|
+
__publicField(this, "vulnerabilityFixService");
|
|
12813
|
+
this.vulnerabilityFixService = ScanAndFixVulnerabilitiesService.getInstance();
|
|
12814
|
+
this.vulnerabilityFixService.reset();
|
|
12672
12815
|
}
|
|
12673
12816
|
async executeInternal(args) {
|
|
12674
|
-
logInfo("Executing tool:
|
|
12817
|
+
logInfo("Executing tool: scan_and_fix_vulnerabilities", { path: args.path });
|
|
12675
12818
|
if (!args.path) {
|
|
12676
12819
|
throw new Error("Invalid arguments: Missing required parameter 'path'");
|
|
12677
12820
|
}
|
|
12678
|
-
const
|
|
12679
|
-
const pathValidationResult = await pathValidation.validatePath(args.path);
|
|
12821
|
+
const pathValidationResult = await validatePath(args.path);
|
|
12680
12822
|
if (!pathValidationResult.isValid) {
|
|
12681
12823
|
throw new Error(
|
|
12682
12824
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
12683
12825
|
);
|
|
12684
12826
|
}
|
|
12685
|
-
const
|
|
12827
|
+
const path14 = pathValidationResult.path;
|
|
12828
|
+
const gitService = new GitService(path14, log);
|
|
12686
12829
|
const gitValidation = await gitService.validateRepository();
|
|
12687
12830
|
let files = [];
|
|
12688
12831
|
if (!gitValidation.isValid) {
|
|
12689
12832
|
logDebug(
|
|
12690
12833
|
"Git repository validation failed, using all files in the repository",
|
|
12691
12834
|
{
|
|
12692
|
-
path:
|
|
12835
|
+
path: path14
|
|
12693
12836
|
}
|
|
12694
12837
|
);
|
|
12695
|
-
files = FileUtils.getLastChangedFiles(
|
|
12838
|
+
files = FileUtils.getLastChangedFiles(path14);
|
|
12696
12839
|
logDebug("Found files in the repository", {
|
|
12697
12840
|
files,
|
|
12698
12841
|
fileCount: files.length
|
|
@@ -12729,10 +12872,9 @@ var FixVulnerabilitiesTool = class extends BaseTool {
|
|
|
12729
12872
|
};
|
|
12730
12873
|
}
|
|
12731
12874
|
try {
|
|
12732
|
-
const
|
|
12733
|
-
const fixResult = await vulnerabilityFixService.processVulnerabilities({
|
|
12875
|
+
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
12734
12876
|
fileList: files,
|
|
12735
|
-
repositoryPath:
|
|
12877
|
+
repositoryPath: path14,
|
|
12736
12878
|
offset: args.offset,
|
|
12737
12879
|
limit: args.limit,
|
|
12738
12880
|
isRescan: args.rescan
|
|
@@ -12774,7 +12916,7 @@ function createMcpServer() {
|
|
|
12774
12916
|
logDebug("Creating MCP server");
|
|
12775
12917
|
const server = new McpServer({
|
|
12776
12918
|
name: "mobb-mcp",
|
|
12777
|
-
version:
|
|
12919
|
+
version: packageJson.version
|
|
12778
12920
|
});
|
|
12779
12921
|
const enabledToolsEnv = process.env["TOOLS_ENABLED"];
|
|
12780
12922
|
const enabledToolsSet = enabledToolsEnv ? new Set(
|
|
@@ -12788,10 +12930,10 @@ function createMcpServer() {
|
|
|
12788
12930
|
logDebug(`Skipping tool (disabled): ${tool.name}`);
|
|
12789
12931
|
}
|
|
12790
12932
|
};
|
|
12791
|
-
const
|
|
12792
|
-
const
|
|
12793
|
-
registerIfEnabled(
|
|
12794
|
-
registerIfEnabled(
|
|
12933
|
+
const scanAndFixVulnerabilitiesTool = new ScanAndFixVulnerabilitiesTool();
|
|
12934
|
+
const fetchAvailableFixesTool = new FetchAvailableFixesTool();
|
|
12935
|
+
registerIfEnabled(scanAndFixVulnerabilitiesTool);
|
|
12936
|
+
registerIfEnabled(fetchAvailableFixesTool);
|
|
12795
12937
|
logInfo("MCP server created and configured");
|
|
12796
12938
|
return server;
|
|
12797
12939
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobbdev",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.107",
|
|
4
4
|
"description": "Automated secure code remediation tool",
|
|
5
5
|
"repository": "git+https://github.com/mobb-dev/bugsy.git",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"test:mcp": "pnpm run build && pnpm run test:mcp:unit",
|
|
16
16
|
"test:mcp:watch": "vitest watch __tests__/mcp/mcp.unit.test.ts",
|
|
17
17
|
"test:mcp:verbose": "pnpm run build && NODE_ENV=test VERBOSE=true vitest run __tests__/mcp/",
|
|
18
|
+
"test:mcp:integration": "pnpm run build && GIT_PROXY_HOST=http://tinyproxy:8888 TOKEN=$(../../scripts/login_auth0.sh) VERBOSE=1 vitest run __tests__/mcp/mcp.integration.test.ts",
|
|
18
19
|
"test:unit": "GIT_PROXY_HOST=http://tinyproxy:8888 TOKEN=$(../../scripts/login_auth0.sh) vitest run --exclude='**/__tests__/integration.test.ts' --exclude='**/__tests__/mcp/**'",
|
|
19
20
|
"test:integration": "GIT_PROXY_HOST=http://tinyproxy:8888 TOKEN=$(../../scripts/login_auth0.sh) vitest run __tests__/integration.test.ts",
|
|
20
21
|
"test:integration:watch": "GIT_PROXY_HOST=http://tinyproxy:8888 TOKEN=$(../../scripts/login_auth0.sh) vitest watch run __tests__/integration.test.ts",
|
|
@@ -34,7 +35,8 @@
|
|
|
34
35
|
"dev:mcp": "pnpm run build && node dist/index.mjs mcp",
|
|
35
36
|
"debug:mcp": "pnpm run build && node dist/index.mjs mcp --debug",
|
|
36
37
|
"generate": "pnpm run env -- graphql-codegen -r dotenv/config --config client_codegen.ts",
|
|
37
|
-
"test:e2e": "cd ./__e2e__ && npm i && npm run test"
|
|
38
|
+
"test:e2e": "cd ./__e2e__ && npm i && npm run test && npm run test:mcp",
|
|
39
|
+
"test:e2e:mcp": "cd ./__e2e__ && npm i && npm run test:mcp"
|
|
38
40
|
},
|
|
39
41
|
"bin": {
|
|
40
42
|
"mobbdev": "bin/cli.mjs"
|
|
@@ -47,7 +49,6 @@
|
|
|
47
49
|
"@modelcontextprotocol/sdk": "1.12.1",
|
|
48
50
|
"@octokit/core": "5.2.0",
|
|
49
51
|
"@octokit/request-error": "5.1.1",
|
|
50
|
-
"@types/libsodium-wrappers": "0.7.14",
|
|
51
52
|
"adm-zip": "0.5.16",
|
|
52
53
|
"axios": "1.9.0",
|
|
53
54
|
"azure-devops-node-api": "12.1.0",
|
|
@@ -118,11 +119,11 @@
|
|
|
118
119
|
"eslint-plugin-simple-import-sort": "10.0.0",
|
|
119
120
|
"msw": "2.8.5",
|
|
120
121
|
"nock": "14.0.5",
|
|
121
|
-
"pino-pretty": "13.0.0",
|
|
122
122
|
"prettier": "3.5.3",
|
|
123
123
|
"tsup": "8.5.0",
|
|
124
124
|
"typescript": "4.9.5",
|
|
125
|
-
"vitest": "3.2.3"
|
|
125
|
+
"vitest": "3.2.3",
|
|
126
|
+
"@types/libsodium-wrappers": "0.7.14"
|
|
126
127
|
},
|
|
127
128
|
"engines": {
|
|
128
129
|
"node": ">=18.20.0"
|