mobbdev 1.0.186 → 1.0.189
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 +326 -69
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -32,7 +32,7 @@ var init_env = __esm({
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
// src/mcp/core/configs.ts
|
|
35
|
-
var MCP_DEFAULT_API_URL, MCP_API_KEY_HEADER_NAME, MCP_LOGIN_MAX_WAIT, MCP_LOGIN_CHECK_DELAY, MCP_VUL_REPORT_DIGEST_TIMEOUT_MS, MCP_MAX_FILE_SIZE, MCP_PERIODIC_CHECK_INTERVAL, MCP_DEFAULT_MAX_FILES_TO_SCAN, MCP_REPORT_ID_EXPIRATION_MS, MCP_TOOLS_BROWSER_COOLDOWN_MS, MCP_DEFAULT_LIMIT, isAutoScan, MVS_AUTO_FIX_OVERRIDE, MCP_AUTO_FIX_DEBUG_MODE, MCP_PERIODIC_TRACK_INTERVAL, MCP_DEFAULT_REST_API_URL;
|
|
35
|
+
var MCP_DEFAULT_API_URL, MCP_API_KEY_HEADER_NAME, MCP_LOGIN_MAX_WAIT, MCP_LOGIN_CHECK_DELAY, MCP_VUL_REPORT_DIGEST_TIMEOUT_MS, MCP_MAX_FILE_SIZE, MCP_PERIODIC_CHECK_INTERVAL, MCP_DEFAULT_MAX_FILES_TO_SCAN, MCP_REPORT_ID_EXPIRATION_MS, MCP_TOOLS_BROWSER_COOLDOWN_MS, MCP_DEFAULT_LIMIT, isAutoScan, MVS_AUTO_FIX_OVERRIDE, MCP_AUTO_FIX_DEBUG_MODE, MCP_PERIODIC_TRACK_INTERVAL, MCP_DEFAULT_REST_API_URL, MCP_SYSTEM_FIND_TIMEOUT_MS;
|
|
36
36
|
var init_configs = __esm({
|
|
37
37
|
"src/mcp/core/configs.ts"() {
|
|
38
38
|
"use strict";
|
|
@@ -53,6 +53,7 @@ var init_configs = __esm({
|
|
|
53
53
|
MCP_AUTO_FIX_DEBUG_MODE = true;
|
|
54
54
|
MCP_PERIODIC_TRACK_INTERVAL = 60 * 60 * 1e3;
|
|
55
55
|
MCP_DEFAULT_REST_API_URL = "https://api.mobb.ai/api/rest/mcp/track";
|
|
56
|
+
MCP_SYSTEM_FIND_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
56
57
|
}
|
|
57
58
|
});
|
|
58
59
|
|
|
@@ -1766,6 +1767,7 @@ var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
|
|
|
1766
1767
|
IssueType_Enum2["Pt"] = "PT";
|
|
1767
1768
|
IssueType_Enum2["RaceConditionFormatFlaw"] = "RACE_CONDITION_FORMAT_FLAW";
|
|
1768
1769
|
IssueType_Enum2["Redos"] = "REDOS";
|
|
1770
|
+
IssueType_Enum2["RedundantNilErrorCheck"] = "REDUNDANT_NIL_ERROR_CHECK";
|
|
1769
1771
|
IssueType_Enum2["RegexInjection"] = "REGEX_INJECTION";
|
|
1770
1772
|
IssueType_Enum2["RegexMissingTimeout"] = "REGEX_MISSING_TIMEOUT";
|
|
1771
1773
|
IssueType_Enum2["RequestParametersBoundViaInput"] = "REQUEST_PARAMETERS_BOUND_VIA_INPUT";
|
|
@@ -2918,7 +2920,8 @@ var fixDetailsData = {
|
|
|
2918
2920
|
["SPRING_DEFAULT_PERMIT" /* SpringDefaultPermit */]: void 0,
|
|
2919
2921
|
["RETURN_IN_INIT" /* ReturnInInit */]: void 0,
|
|
2920
2922
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: void 0,
|
|
2921
|
-
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0
|
|
2923
|
+
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0,
|
|
2924
|
+
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0
|
|
2922
2925
|
};
|
|
2923
2926
|
|
|
2924
2927
|
// src/features/analysis/scm/shared/src/getIssueType.ts
|
|
@@ -3057,7 +3060,8 @@ var issueTypeMap = {
|
|
|
3057
3060
|
["SPRING_DEFAULT_PERMIT" /* SpringDefaultPermit */]: "Spring Default Permit",
|
|
3058
3061
|
["RETURN_IN_INIT" /* ReturnInInit */]: "Return in Init",
|
|
3059
3062
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: "Action Not Pinned to Commit Sha",
|
|
3060
|
-
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default"
|
|
3063
|
+
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default",
|
|
3064
|
+
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check"
|
|
3061
3065
|
};
|
|
3062
3066
|
var issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
3063
3067
|
var getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -5412,9 +5416,7 @@ var VulnerabilityReportIssueZ = BaseVulnerabilityReportIssueZ.merge(
|
|
|
5412
5416
|
);
|
|
5413
5417
|
var VulnerabilityReportIssueWithCodeFilePathZ = BaseVulnerabilityReportIssueZ.merge(
|
|
5414
5418
|
z11.object({
|
|
5415
|
-
codeFilePath: z11.string().nullable()
|
|
5416
|
-
//TODO: REMOVE THIS once we flush out all the reports that don't have codeFilePath
|
|
5417
|
-
codeNodes: z11.array(z11.object({ path: z11.string() }))
|
|
5419
|
+
codeFilePath: z11.string().nullable()
|
|
5418
5420
|
})
|
|
5419
5421
|
);
|
|
5420
5422
|
var GetReportIssuesQueryZ = z11.object({
|
|
@@ -7622,14 +7624,38 @@ function getOctoKit(options) {
|
|
|
7622
7624
|
//to debug the performance of these API calls.
|
|
7623
7625
|
log: GITHUB_API_TOKEN ? console : void 0,
|
|
7624
7626
|
request: {
|
|
7625
|
-
fetch: getFetch(baseUrl)
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
enabled: false
|
|
7627
|
+
fetch: getFetch(baseUrl),
|
|
7628
|
+
timeout: 1e4
|
|
7629
|
+
// 10 second timeout
|
|
7629
7630
|
},
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7631
|
+
retry: options?.isEnableRetries ? {
|
|
7632
|
+
doNotRetry: [400, 401, 403, 404, 422],
|
|
7633
|
+
// Don't retry on these status codes
|
|
7634
|
+
retries: 3
|
|
7635
|
+
// Retry up to 3 times
|
|
7636
|
+
} : { enabled: false },
|
|
7637
|
+
throttle: options?.isEnableRetries ? {
|
|
7638
|
+
onRateLimit: (retryAfter, options2, octokit, retryCount) => {
|
|
7639
|
+
octokit.log.warn(
|
|
7640
|
+
`Request quota exhausted for request ${options2.method} ${options2.url}`
|
|
7641
|
+
);
|
|
7642
|
+
if (retryCount === 0) {
|
|
7643
|
+
octokit.log.info(`Retrying after ${retryAfter} seconds!`);
|
|
7644
|
+
return true;
|
|
7645
|
+
}
|
|
7646
|
+
return false;
|
|
7647
|
+
},
|
|
7648
|
+
onSecondaryRateLimit: (retryAfter, options2, octokit, retryCount) => {
|
|
7649
|
+
octokit.log.warn(
|
|
7650
|
+
`SecondaryRateLimit detected for request ${options2.method} ${options2.url}`
|
|
7651
|
+
);
|
|
7652
|
+
if (retryCount === 0) {
|
|
7653
|
+
octokit.log.info(`Retrying after ${retryAfter} seconds!`);
|
|
7654
|
+
return true;
|
|
7655
|
+
}
|
|
7656
|
+
return false;
|
|
7657
|
+
}
|
|
7658
|
+
} : { enabled: false }
|
|
7633
7659
|
});
|
|
7634
7660
|
}
|
|
7635
7661
|
function isGithubActionActionToken(token) {
|
|
@@ -11107,8 +11133,8 @@ import path8 from "path";
|
|
|
11107
11133
|
var debug15 = Debug15("mobbdev:checkmarx");
|
|
11108
11134
|
var require2 = createRequire(import.meta.url);
|
|
11109
11135
|
var getCheckmarxPath = () => {
|
|
11110
|
-
const
|
|
11111
|
-
const cxFileName =
|
|
11136
|
+
const os6 = type();
|
|
11137
|
+
const cxFileName = os6 === "Windows_NT" ? "cx.exe" : "cx";
|
|
11112
11138
|
try {
|
|
11113
11139
|
return require2.resolve(`.bin/${cxFileName}`);
|
|
11114
11140
|
} catch (e) {
|
|
@@ -13198,13 +13224,15 @@ var McpGQLClient = class {
|
|
|
13198
13224
|
async getLatestReportByRepoUrl({
|
|
13199
13225
|
repoUrl,
|
|
13200
13226
|
limit = MCP_DEFAULT_LIMIT,
|
|
13201
|
-
offset = 0
|
|
13227
|
+
offset = 0,
|
|
13228
|
+
fileFilter
|
|
13202
13229
|
}) {
|
|
13203
13230
|
try {
|
|
13204
13231
|
logDebug("[GraphQL] Calling GetLatestReportByRepoUrl query", {
|
|
13205
13232
|
repoUrl,
|
|
13206
13233
|
limit,
|
|
13207
|
-
offset
|
|
13234
|
+
offset,
|
|
13235
|
+
fileFilter
|
|
13208
13236
|
});
|
|
13209
13237
|
let currentUserEmail = "%@%";
|
|
13210
13238
|
try {
|
|
@@ -13217,11 +13245,18 @@ var McpGQLClient = class {
|
|
|
13217
13245
|
error: err
|
|
13218
13246
|
});
|
|
13219
13247
|
}
|
|
13248
|
+
const filters = {};
|
|
13249
|
+
if (fileFilter && fileFilter.length > 0) {
|
|
13250
|
+
filters["vulnerabilityReportIssues"] = {
|
|
13251
|
+
codeNodes: { path: { _in: fileFilter } }
|
|
13252
|
+
};
|
|
13253
|
+
}
|
|
13220
13254
|
const resp = await this.clientSdk.GetLatestReportByRepoUrl({
|
|
13221
13255
|
repoUrl,
|
|
13222
13256
|
limit,
|
|
13223
13257
|
offset,
|
|
13224
|
-
currentUserEmail
|
|
13258
|
+
currentUserEmail,
|
|
13259
|
+
filters
|
|
13225
13260
|
});
|
|
13226
13261
|
logDebug("[GraphQL] GetLatestReportByRepoUrl successful", {
|
|
13227
13262
|
result: resp,
|
|
@@ -13253,7 +13288,8 @@ var McpGQLClient = class {
|
|
|
13253
13288
|
limit = MCP_DEFAULT_LIMIT,
|
|
13254
13289
|
offset = 0,
|
|
13255
13290
|
issueType,
|
|
13256
|
-
severity
|
|
13291
|
+
severity,
|
|
13292
|
+
fileFilter
|
|
13257
13293
|
}) {
|
|
13258
13294
|
const filters = {};
|
|
13259
13295
|
if (issueType && issueType.length > 0) {
|
|
@@ -13262,6 +13298,11 @@ var McpGQLClient = class {
|
|
|
13262
13298
|
if (severity && severity.length > 0) {
|
|
13263
13299
|
filters["severityText"] = { _in: severity };
|
|
13264
13300
|
}
|
|
13301
|
+
if (fileFilter && fileFilter.length > 0) {
|
|
13302
|
+
filters["vulnerabilityReportIssues"] = {
|
|
13303
|
+
codeNodes: { path: { _in: fileFilter } }
|
|
13304
|
+
};
|
|
13305
|
+
}
|
|
13265
13306
|
try {
|
|
13266
13307
|
logDebug("[GraphQL] Calling GetReportFixes query", {
|
|
13267
13308
|
reportId,
|
|
@@ -13269,7 +13310,8 @@ var McpGQLClient = class {
|
|
|
13269
13310
|
offset,
|
|
13270
13311
|
filters,
|
|
13271
13312
|
issueType,
|
|
13272
|
-
severity
|
|
13313
|
+
severity,
|
|
13314
|
+
fileFilter
|
|
13273
13315
|
});
|
|
13274
13316
|
let currentUserEmail = "%@%";
|
|
13275
13317
|
try {
|
|
@@ -13452,30 +13494,34 @@ var readConfigFile = (filePath) => {
|
|
|
13452
13494
|
return null;
|
|
13453
13495
|
}
|
|
13454
13496
|
};
|
|
13497
|
+
var mergeConfigIntoResult = (config4, mergedConfig) => {
|
|
13498
|
+
if (config4?.projects) {
|
|
13499
|
+
const allMcpServers = {};
|
|
13500
|
+
for (const projectPath in config4.projects) {
|
|
13501
|
+
const project = config4.projects[projectPath];
|
|
13502
|
+
if (project?.mcpServers) {
|
|
13503
|
+
Object.assign(allMcpServers, project.mcpServers);
|
|
13504
|
+
}
|
|
13505
|
+
}
|
|
13506
|
+
mergedConfig.mcpServers = { ...mergedConfig.mcpServers, ...allMcpServers };
|
|
13507
|
+
}
|
|
13508
|
+
if (config4?.mcpServers) {
|
|
13509
|
+
mergedConfig.mcpServers = {
|
|
13510
|
+
...mergedConfig.mcpServers,
|
|
13511
|
+
...config4.mcpServers
|
|
13512
|
+
};
|
|
13513
|
+
}
|
|
13514
|
+
if (config4?.servers) {
|
|
13515
|
+
mergedConfig.servers = { ...mergedConfig.servers, ...config4.servers };
|
|
13516
|
+
}
|
|
13517
|
+
};
|
|
13455
13518
|
var readMCPConfig = (hostName) => {
|
|
13456
13519
|
const configPaths = getMCPConfigPaths(hostName);
|
|
13457
13520
|
const mergedConfig = {};
|
|
13458
13521
|
for (const configPath of configPaths) {
|
|
13459
13522
|
const config4 = readConfigFile(configPath);
|
|
13460
|
-
if (
|
|
13461
|
-
|
|
13462
|
-
for (const projectPath in config4.projects) {
|
|
13463
|
-
const project = config4.projects[projectPath];
|
|
13464
|
-
if (project?.mcpServers) {
|
|
13465
|
-
Object.assign(allMcpServers, project.mcpServers);
|
|
13466
|
-
}
|
|
13467
|
-
}
|
|
13468
|
-
mergedConfig.mcpServers = { ...mergedConfig.mcpServers, ...allMcpServers };
|
|
13469
|
-
continue;
|
|
13470
|
-
}
|
|
13471
|
-
if (config4?.mcpServers) {
|
|
13472
|
-
mergedConfig.mcpServers = {
|
|
13473
|
-
...mergedConfig.mcpServers,
|
|
13474
|
-
...config4.mcpServers
|
|
13475
|
-
};
|
|
13476
|
-
}
|
|
13477
|
-
if (config4?.servers) {
|
|
13478
|
-
mergedConfig.servers = { ...mergedConfig.servers, ...config4.servers };
|
|
13523
|
+
if (config4) {
|
|
13524
|
+
mergeConfigIntoResult(config4, mergedConfig);
|
|
13479
13525
|
}
|
|
13480
13526
|
}
|
|
13481
13527
|
return Object.keys(mergedConfig).length > 0 ? mergedConfig : null;
|
|
@@ -13585,14 +13631,31 @@ var getProcessInfo = (pid) => {
|
|
|
13585
13631
|
return null;
|
|
13586
13632
|
}
|
|
13587
13633
|
};
|
|
13588
|
-
var getHostInfo = () => {
|
|
13634
|
+
var getHostInfo = (additionalMcpList) => {
|
|
13589
13635
|
const runningProcesses = getRunningProcesses().toLowerCase();
|
|
13590
13636
|
const results = [];
|
|
13591
13637
|
const allConfigs = {};
|
|
13638
|
+
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
13639
|
+
for (const ide of IDEs) {
|
|
13640
|
+
const configPaths = getMCPConfigPaths(ide);
|
|
13641
|
+
configPaths.forEach((path17) => ideConfigPaths.add(path17));
|
|
13642
|
+
}
|
|
13643
|
+
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
13644
|
+
(path17) => !ideConfigPaths.has(path17)
|
|
13645
|
+
);
|
|
13592
13646
|
for (const ide of IDEs) {
|
|
13593
13647
|
const cfg = readMCPConfig(ide);
|
|
13594
13648
|
if (cfg) allConfigs[ide] = cfg;
|
|
13595
13649
|
}
|
|
13650
|
+
for (const additionalPath of uniqueAdditionalPaths) {
|
|
13651
|
+
const config4 = readConfigFile(additionalPath);
|
|
13652
|
+
if (!config4) continue;
|
|
13653
|
+
const mergedConfig = {};
|
|
13654
|
+
mergeConfigIntoResult(config4, mergedConfig);
|
|
13655
|
+
if (Object.keys(mergedConfig).length > 0) {
|
|
13656
|
+
allConfigs["system"] = mergedConfig;
|
|
13657
|
+
}
|
|
13658
|
+
}
|
|
13596
13659
|
const servers = [];
|
|
13597
13660
|
for (const [ide, cfg] of Object.entries(allConfigs)) {
|
|
13598
13661
|
for (const [name, server] of Object.entries(
|
|
@@ -13693,8 +13756,90 @@ var getHostInfo = () => {
|
|
|
13693
13756
|
// src/mcp/services/McpUsageService/McpUsageService.ts
|
|
13694
13757
|
init_configs();
|
|
13695
13758
|
import fetch5 from "node-fetch";
|
|
13696
|
-
import
|
|
13759
|
+
import os5 from "os";
|
|
13697
13760
|
import { v4 as uuidv43, v5 as uuidv5 } from "uuid";
|
|
13761
|
+
|
|
13762
|
+
// src/mcp/services/McpUsageService/system.ts
|
|
13763
|
+
init_configs();
|
|
13764
|
+
import { spawn } from "child_process";
|
|
13765
|
+
import os4 from "os";
|
|
13766
|
+
var findSystemMCPConfigs = async () => {
|
|
13767
|
+
try {
|
|
13768
|
+
const platform = os4.platform();
|
|
13769
|
+
let command;
|
|
13770
|
+
let args;
|
|
13771
|
+
if (platform === "win32") {
|
|
13772
|
+
command = "powershell";
|
|
13773
|
+
args = [
|
|
13774
|
+
"-NoProfile",
|
|
13775
|
+
"-Command",
|
|
13776
|
+
"Get-ChildItem -Path $env:USERPROFILE -Recurse -Include *mcp*.json,*claude*.json -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName }"
|
|
13777
|
+
];
|
|
13778
|
+
} else {
|
|
13779
|
+
const home = os4.homedir();
|
|
13780
|
+
command = "find";
|
|
13781
|
+
args = [
|
|
13782
|
+
home,
|
|
13783
|
+
"-type",
|
|
13784
|
+
"f",
|
|
13785
|
+
"(",
|
|
13786
|
+
"-iname",
|
|
13787
|
+
"*mcp*.json",
|
|
13788
|
+
"-o",
|
|
13789
|
+
"-iname",
|
|
13790
|
+
"*claude*.json",
|
|
13791
|
+
")"
|
|
13792
|
+
];
|
|
13793
|
+
}
|
|
13794
|
+
return await new Promise((resolve) => {
|
|
13795
|
+
const child = spawn(command, args, {
|
|
13796
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13797
|
+
shell: platform === "win32"
|
|
13798
|
+
// needed for PowerShell
|
|
13799
|
+
});
|
|
13800
|
+
let output = "";
|
|
13801
|
+
let errorOutput = "";
|
|
13802
|
+
const timer = setTimeout(() => {
|
|
13803
|
+
child.kill("SIGTERM");
|
|
13804
|
+
logWarn(
|
|
13805
|
+
`MCP config search timed out after ${MCP_SYSTEM_FIND_TIMEOUT_MS / 1e3}s`
|
|
13806
|
+
);
|
|
13807
|
+
resolve([]);
|
|
13808
|
+
}, MCP_SYSTEM_FIND_TIMEOUT_MS);
|
|
13809
|
+
child.stdout.on("data", (data) => {
|
|
13810
|
+
output += data.toString();
|
|
13811
|
+
});
|
|
13812
|
+
child.stderr.on("data", (data) => {
|
|
13813
|
+
const msg = data.toString();
|
|
13814
|
+
if (!msg.includes("Operation not permitted") && !msg.includes("Permission denied") && !msg.includes("Access is denied")) {
|
|
13815
|
+
errorOutput += msg;
|
|
13816
|
+
}
|
|
13817
|
+
});
|
|
13818
|
+
child.on("error", (err) => {
|
|
13819
|
+
clearTimeout(timer);
|
|
13820
|
+
logWarn("MCP config search failed to start", { err });
|
|
13821
|
+
resolve([]);
|
|
13822
|
+
});
|
|
13823
|
+
child.on("close", (code) => {
|
|
13824
|
+
clearTimeout(timer);
|
|
13825
|
+
if (code === 0 || output.trim().length > 0) {
|
|
13826
|
+
const files = output.split(/\r?\n/).map((f) => f.trim()).filter(Boolean);
|
|
13827
|
+
resolve(files);
|
|
13828
|
+
} else {
|
|
13829
|
+
if (errorOutput.trim().length > 0) {
|
|
13830
|
+
logWarn("MCP config search finished with warnings", { errorOutput });
|
|
13831
|
+
}
|
|
13832
|
+
resolve([]);
|
|
13833
|
+
}
|
|
13834
|
+
});
|
|
13835
|
+
});
|
|
13836
|
+
} catch (err) {
|
|
13837
|
+
logWarn("MCP config search unexpected error", { err });
|
|
13838
|
+
return [];
|
|
13839
|
+
}
|
|
13840
|
+
};
|
|
13841
|
+
|
|
13842
|
+
// src/mcp/services/McpUsageService/McpUsageService.ts
|
|
13698
13843
|
var McpUsageService = class {
|
|
13699
13844
|
constructor(govOrgId) {
|
|
13700
13845
|
__publicField(this, "configKey", "mcpUsage");
|
|
@@ -13709,6 +13854,10 @@ var McpUsageService = class {
|
|
|
13709
13854
|
this.REST_API_URL = `${domain}/api/rest/mcp/track`;
|
|
13710
13855
|
}
|
|
13711
13856
|
}
|
|
13857
|
+
async performSystemSearchAndTracking() {
|
|
13858
|
+
const additionalMcpList = await findSystemMCPConfigs();
|
|
13859
|
+
await this.trackServerStart(additionalMcpList);
|
|
13860
|
+
}
|
|
13712
13861
|
startPeriodicTracking() {
|
|
13713
13862
|
if (!this.hasOrganizationId()) {
|
|
13714
13863
|
logDebug(
|
|
@@ -13717,17 +13866,21 @@ var McpUsageService = class {
|
|
|
13717
13866
|
return;
|
|
13718
13867
|
}
|
|
13719
13868
|
logDebug(`[UsageService] Starting periodic tracking for mcps`, {});
|
|
13869
|
+
if (this.intervalId) {
|
|
13870
|
+
return;
|
|
13871
|
+
}
|
|
13872
|
+
setTimeout(() => this.performSystemSearchAndTracking(), 0);
|
|
13720
13873
|
this.intervalId = setInterval(async () => {
|
|
13721
13874
|
logDebug(`[UsageService] Triggering periodic usage service`, {
|
|
13722
13875
|
MCP_PERIODIC_TRACK_INTERVAL
|
|
13723
13876
|
});
|
|
13724
|
-
await this.
|
|
13725
|
-
},
|
|
13877
|
+
await this.performSystemSearchAndTracking();
|
|
13878
|
+
}, MCP_PERIODIC_TRACK_INTERVAL);
|
|
13726
13879
|
}
|
|
13727
13880
|
generateHostId() {
|
|
13728
13881
|
const stored = configStore.get(this.configKey);
|
|
13729
13882
|
if (stored?.mcpHostId) return stored.mcpHostId;
|
|
13730
|
-
const interfaces =
|
|
13883
|
+
const interfaces = os5.networkInterfaces();
|
|
13731
13884
|
const macs = [];
|
|
13732
13885
|
for (const iface of Object.values(interfaces)) {
|
|
13733
13886
|
if (!iface) continue;
|
|
@@ -13735,7 +13888,7 @@ var McpUsageService = class {
|
|
|
13735
13888
|
if (net.mac && net.mac !== "00:00:00:00:00:00") macs.push(net.mac);
|
|
13736
13889
|
}
|
|
13737
13890
|
}
|
|
13738
|
-
const macString = macs.length ? macs.sort().join(",") : `${
|
|
13891
|
+
const macString = macs.length ? macs.sort().join(",") : `${os5.hostname()}-${uuidv43()}`;
|
|
13739
13892
|
const hostId = uuidv5(macString, uuidv5.DNS);
|
|
13740
13893
|
logDebug("[UsageService] Generated new host ID", { hostId });
|
|
13741
13894
|
return hostId;
|
|
@@ -13752,13 +13905,13 @@ var McpUsageService = class {
|
|
|
13752
13905
|
hasOrganizationId() {
|
|
13753
13906
|
return !!this.govOrgId;
|
|
13754
13907
|
}
|
|
13755
|
-
createUsageData(mcpHostId, organizationId, status) {
|
|
13756
|
-
const { user, mcps } = getHostInfo();
|
|
13908
|
+
createUsageData(mcpHostId, organizationId, status, additionalMcpList) {
|
|
13909
|
+
const { user, mcps } = getHostInfo(additionalMcpList);
|
|
13757
13910
|
return {
|
|
13758
13911
|
mcpHostId,
|
|
13759
13912
|
organizationId,
|
|
13760
13913
|
mcpVersion: packageJson.version,
|
|
13761
|
-
mcpOsName:
|
|
13914
|
+
mcpOsName: os5.platform(),
|
|
13762
13915
|
mcps: JSON.stringify(mcps),
|
|
13763
13916
|
status,
|
|
13764
13917
|
userName: user.name,
|
|
@@ -13767,7 +13920,10 @@ var McpUsageService = class {
|
|
|
13767
13920
|
// it's used to make sure we track the mcp usage daily
|
|
13768
13921
|
};
|
|
13769
13922
|
}
|
|
13770
|
-
async trackUsage(
|
|
13923
|
+
async trackUsage({
|
|
13924
|
+
status,
|
|
13925
|
+
additionalMcpList
|
|
13926
|
+
}) {
|
|
13771
13927
|
try {
|
|
13772
13928
|
const hostId = this.generateHostId();
|
|
13773
13929
|
const organizationId = this.getOrganizationId();
|
|
@@ -13777,7 +13933,12 @@ var McpUsageService = class {
|
|
|
13777
13933
|
);
|
|
13778
13934
|
return;
|
|
13779
13935
|
}
|
|
13780
|
-
const usageData = this.createUsageData(
|
|
13936
|
+
const usageData = this.createUsageData(
|
|
13937
|
+
hostId,
|
|
13938
|
+
organizationId,
|
|
13939
|
+
status,
|
|
13940
|
+
additionalMcpList
|
|
13941
|
+
);
|
|
13781
13942
|
const stored = configStore.get(this.configKey);
|
|
13782
13943
|
const hasChanges = !stored || Object.keys(usageData).some(
|
|
13783
13944
|
(key) => usageData[key] !== stored[key]
|
|
@@ -13822,11 +13983,11 @@ var McpUsageService = class {
|
|
|
13822
13983
|
);
|
|
13823
13984
|
}
|
|
13824
13985
|
}
|
|
13825
|
-
async trackServerStart() {
|
|
13826
|
-
await this.trackUsage("ACTIVE");
|
|
13986
|
+
async trackServerStart(additionalMcpList = []) {
|
|
13987
|
+
await this.trackUsage({ status: "ACTIVE", additionalMcpList });
|
|
13827
13988
|
}
|
|
13828
13989
|
async trackServerStop() {
|
|
13829
|
-
await this.trackUsage("INACTIVE");
|
|
13990
|
+
await this.trackUsage({ status: "INACTIVE", additionalMcpList: [] });
|
|
13830
13991
|
}
|
|
13831
13992
|
reset() {
|
|
13832
13993
|
if (!this.intervalId) {
|
|
@@ -14959,6 +15120,35 @@ ${autoFixSettingsSection}
|
|
|
14959
15120
|
|
|
14960
15121
|
${whatHappensNextSection}`;
|
|
14961
15122
|
};
|
|
15123
|
+
var noChangedFilesFoundPrompt = `\u{1F50D} **MOBB SECURITY SCAN: NO CHANGED FILES DETECTED**
|
|
15124
|
+
|
|
15125
|
+
## \u{1F4CB} Current Status
|
|
15126
|
+
|
|
15127
|
+
No changed files were found in the working directory for security scanning.
|
|
15128
|
+
|
|
15129
|
+
## \u{1F914} What This Means
|
|
15130
|
+
|
|
15131
|
+
This situation occurs when:
|
|
15132
|
+
\u2022 **Clean Working Directory**: All files are committed and there are no uncommitted changes
|
|
15133
|
+
\u2022 **Fresh Repository**: The repository has been recently cloned or initialized
|
|
15134
|
+
\u2022 **All Changes Committed**: Recent modifications have already been committed to git
|
|
15135
|
+
|
|
15136
|
+
If you wish to scan files that were recently changed in your git history call the tool with the following parameters:
|
|
15137
|
+
|
|
15138
|
+
\`\`\`json
|
|
15139
|
+
{
|
|
15140
|
+
"path": "/path/to/your/repository",
|
|
15141
|
+
"maxFiles": 50,
|
|
15142
|
+
"rescan": true,
|
|
15143
|
+
"scanRecentlyChangedFiles": true
|
|
15144
|
+
}
|
|
15145
|
+
\`\`\`
|
|
15146
|
+
|
|
15147
|
+
|
|
15148
|
+
\u2022 **scanRecentlyChangedFiles**: Set to \`true\` to automatically scan recently modified files from git history
|
|
15149
|
+
\u2022 **maxFiles**: Specify the maximum number of files to scan (higher = more comprehensive) (default: ${MCP_DEFAULT_MAX_FILES_TO_SCAN})
|
|
15150
|
+
\u2022 **rescan**: Set to \`true\` to force a complete fresh analysis
|
|
15151
|
+
`;
|
|
14962
15152
|
|
|
14963
15153
|
// src/mcp/services/GetLocalFiles.ts
|
|
14964
15154
|
init_FileUtils();
|
|
@@ -14971,13 +15161,15 @@ var getLocalFiles = async ({
|
|
|
14971
15161
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
14972
15162
|
maxFiles,
|
|
14973
15163
|
isAllFilesScan,
|
|
14974
|
-
scanContext
|
|
15164
|
+
scanContext,
|
|
15165
|
+
scanRecentlyChangedFiles
|
|
14975
15166
|
}) => {
|
|
14976
15167
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
14977
15168
|
path: path17,
|
|
14978
15169
|
maxFileSize,
|
|
14979
15170
|
maxFiles,
|
|
14980
|
-
isAllFilesScan
|
|
15171
|
+
isAllFilesScan,
|
|
15172
|
+
scanRecentlyChangedFiles
|
|
14981
15173
|
});
|
|
14982
15174
|
try {
|
|
14983
15175
|
const resolvedRepoPath = await fs12.realpath(path17);
|
|
@@ -15015,10 +15207,10 @@ var getLocalFiles = async ({
|
|
|
15015
15207
|
try {
|
|
15016
15208
|
const gitResult = await gitService.getChangedFiles();
|
|
15017
15209
|
files = gitResult.files;
|
|
15018
|
-
if (files.length === 0 || maxFiles) {
|
|
15210
|
+
if ((files.length === 0 || maxFiles) && (scanRecentlyChangedFiles || maxFiles)) {
|
|
15019
15211
|
logDebug(
|
|
15020
15212
|
`[${scanContext}] No changes found or maxFiles specified, getting recently changed files`,
|
|
15021
|
-
{ maxFiles }
|
|
15213
|
+
{ maxFiles, scanRecentlyChangedFiles }
|
|
15022
15214
|
);
|
|
15023
15215
|
const recentResult = await gitService.getRecentlyChangedFiles({
|
|
15024
15216
|
maxFiles
|
|
@@ -17240,10 +17432,12 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
17240
17432
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
17241
17433
|
{ path: path17 }
|
|
17242
17434
|
);
|
|
17435
|
+
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
17243
17436
|
const files = await getLocalFiles({
|
|
17244
17437
|
path: path17,
|
|
17245
17438
|
isAllFilesScan,
|
|
17246
|
-
scanContext
|
|
17439
|
+
scanContext,
|
|
17440
|
+
scanRecentlyChangedFiles: !isBackgroundScan
|
|
17247
17441
|
});
|
|
17248
17442
|
const scanStartTime = Date.now();
|
|
17249
17443
|
logDebug(
|
|
@@ -17834,19 +18028,21 @@ var _FetchAvailableFixesService = class _FetchAvailableFixesService {
|
|
|
17834
18028
|
async checkForAvailableFixes({
|
|
17835
18029
|
repoUrl,
|
|
17836
18030
|
limit = MCP_DEFAULT_LIMIT,
|
|
17837
|
-
offset
|
|
18031
|
+
offset,
|
|
18032
|
+
fileFilter
|
|
17838
18033
|
}) {
|
|
17839
18034
|
try {
|
|
17840
|
-
logDebug("Checking for available fixes", { repoUrl, limit });
|
|
18035
|
+
logDebug("Checking for available fixes", { repoUrl, limit, fileFilter });
|
|
17841
18036
|
const gqlClient = await this.initializeGqlClient();
|
|
17842
18037
|
logDebug("GQL client initialized");
|
|
17843
|
-
logDebug("querying for latest report", { repoUrl, limit });
|
|
18038
|
+
logDebug("querying for latest report", { repoUrl, limit, fileFilter });
|
|
17844
18039
|
const effectiveOffset = offset ?? (this.currentOffset || 0);
|
|
17845
18040
|
logDebug("effectiveOffset", { effectiveOffset });
|
|
17846
18041
|
const { fixReport, expiredReport } = await gqlClient.getLatestReportByRepoUrl({
|
|
17847
18042
|
repoUrl,
|
|
17848
18043
|
limit,
|
|
17849
|
-
offset: effectiveOffset
|
|
18044
|
+
offset: effectiveOffset,
|
|
18045
|
+
fileFilter
|
|
17850
18046
|
});
|
|
17851
18047
|
logDebug("received latest report result", { fixReport, expiredReport });
|
|
17852
18048
|
if (!fixReport) {
|
|
@@ -17901,11 +18097,20 @@ Required argument:
|
|
|
17901
18097
|
Optional arguments:
|
|
17902
18098
|
\u2022 offset \u2013 pagination offset (integer).
|
|
17903
18099
|
\u2022 limit \u2013 maximum number of fixes to return (integer).
|
|
18100
|
+
\u2022 fileFilter \u2013 list of file paths relative to the path parameter to filter fixes by. Only fixes affecting these files will be returned. INCOMPATIBLE with fetchFixesFromAnyFile.
|
|
18101
|
+
\u2022 fetchFixesFromAnyFile \u2013 if true, fetches fixes for all files in the repository. If false or not set (default), filters fixes to only those affecting files with changes in git status. INCOMPATIBLE with fileFilter.
|
|
17904
18102
|
|
|
17905
18103
|
The tool will:
|
|
17906
18104
|
1. Validate that the provided path is secure and exists.
|
|
17907
18105
|
2. Verify that the directory is a valid Git repository with an "origin" remote.
|
|
17908
|
-
3.
|
|
18106
|
+
3. Apply file filtering based on parameters (see below).
|
|
18107
|
+
4. 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.
|
|
18108
|
+
|
|
18109
|
+
File Filtering Behavior:
|
|
18110
|
+
\u2022 If fetchFixesFromAnyFile is true: Returns fixes for all files (no filtering).
|
|
18111
|
+
\u2022 If fileFilter is provided: Returns only fixes affecting the specified files.
|
|
18112
|
+
\u2022 If neither is provided (default): Returns only fixes affecting files with changes in git status.
|
|
18113
|
+
\u2022 If BOTH are provided: Returns an error (parameters are mutually exclusive).
|
|
17909
18114
|
|
|
17910
18115
|
Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only need a fixes summary and do NOT want to perform scanning or code modifications.`);
|
|
17911
18116
|
__publicField(this, "hasAuthentication", true);
|
|
@@ -17923,6 +18128,17 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
17923
18128
|
limit: {
|
|
17924
18129
|
type: "number",
|
|
17925
18130
|
description: "[Optional] maximum number of results to return"
|
|
18131
|
+
},
|
|
18132
|
+
fileFilter: {
|
|
18133
|
+
type: "array",
|
|
18134
|
+
items: {
|
|
18135
|
+
type: "string"
|
|
18136
|
+
},
|
|
18137
|
+
description: "[Optional] list of file paths relative to the path parameter to filter fixes by. Only fixes affecting these files will be returned. INCOMPATIBLE with fetchFixesFromAnyFile"
|
|
18138
|
+
},
|
|
18139
|
+
fetchFixesFromAnyFile: {
|
|
18140
|
+
type: "boolean",
|
|
18141
|
+
description: "[Optional] if true, fetches fixes for all files in the repository. If false or not set, filters fixes to only those affecting files with changes in git status. INCOMPATIBLE with fileFilter"
|
|
17926
18142
|
}
|
|
17927
18143
|
},
|
|
17928
18144
|
required: ["path"]
|
|
@@ -17932,7 +18148,13 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
17932
18148
|
"Full local path to the cloned git repository to check for available fixes"
|
|
17933
18149
|
),
|
|
17934
18150
|
offset: z35.number().optional().describe("Optional offset for pagination"),
|
|
17935
|
-
limit: z35.number().optional().describe("Optional maximum number of fixes to return")
|
|
18151
|
+
limit: z35.number().optional().describe("Optional maximum number of fixes to return"),
|
|
18152
|
+
fileFilter: z35.array(z35.string()).optional().describe(
|
|
18153
|
+
"Optional list of file paths relative to the path parameter to filter fixes by. INCOMPATIBLE with fetchFixesFromAnyFile"
|
|
18154
|
+
),
|
|
18155
|
+
fetchFixesFromAnyFile: z35.boolean().optional().describe(
|
|
18156
|
+
"Optional boolean to fetch fixes for all files. INCOMPATIBLE with fileFilter"
|
|
18157
|
+
)
|
|
17936
18158
|
}));
|
|
17937
18159
|
__publicField(this, "availableFixesService");
|
|
17938
18160
|
this.availableFixesService = FetchAvailableFixesService.getInstance();
|
|
@@ -17959,10 +18181,37 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
17959
18181
|
if (!originUrl) {
|
|
17960
18182
|
throw new Error("No origin URL found for the repository");
|
|
17961
18183
|
}
|
|
18184
|
+
if (args.fileFilter && args.fetchFixesFromAnyFile) {
|
|
18185
|
+
throw new Error(
|
|
18186
|
+
'Parameters "fileFilter" and "fetchFixesFromAnyFile" are mutually exclusive. Please provide only one of these parameters:\n - Use "fileFilter" to specify a custom list of files to filter by\n - Use "fetchFixesFromAnyFile: true" to fetch fixes for all files without filtering\n - Use neither to automatically filter by files with changes in git status (default behavior)'
|
|
18187
|
+
);
|
|
18188
|
+
}
|
|
18189
|
+
let actualFileFilter;
|
|
18190
|
+
if (args.fetchFixesFromAnyFile === true) {
|
|
18191
|
+
actualFileFilter = void 0;
|
|
18192
|
+
logDebug("Fetching fixes for all files (no filtering)");
|
|
18193
|
+
} else if (args.fileFilter && args.fileFilter.length > 0) {
|
|
18194
|
+
actualFileFilter = args.fileFilter;
|
|
18195
|
+
logDebug("Using provided file filter", { fileFilter: actualFileFilter });
|
|
18196
|
+
} else {
|
|
18197
|
+
logDebug("Getting files from git status for filtering");
|
|
18198
|
+
const gitStatusResult = await gitService.getChangedFiles();
|
|
18199
|
+
if (gitStatusResult.files.length === 0) {
|
|
18200
|
+
logDebug("No changed files found in git status");
|
|
18201
|
+
actualFileFilter = void 0;
|
|
18202
|
+
} else {
|
|
18203
|
+
actualFileFilter = gitStatusResult.files;
|
|
18204
|
+
logDebug("Using files from git status as filter", {
|
|
18205
|
+
fileCount: actualFileFilter.length,
|
|
18206
|
+
files: actualFileFilter
|
|
18207
|
+
});
|
|
18208
|
+
}
|
|
18209
|
+
}
|
|
17962
18210
|
const fixResult = await this.availableFixesService.checkForAvailableFixes({
|
|
17963
18211
|
repoUrl: originUrl,
|
|
17964
18212
|
limit: args.limit,
|
|
17965
|
-
offset: args.offset
|
|
18213
|
+
offset: args.offset,
|
|
18214
|
+
fileFilter: actualFileFilter
|
|
17966
18215
|
});
|
|
17967
18216
|
logDebug("FetchAvailableFixesTool execution completed successfully", {
|
|
17968
18217
|
fixResult
|
|
@@ -17998,7 +18247,7 @@ var _McpCheckerService = class _McpCheckerService {
|
|
|
17998
18247
|
};
|
|
17999
18248
|
}
|
|
18000
18249
|
logInfo("Executing built-in mcp_checker tool");
|
|
18001
|
-
const hostInfo = getHostInfo();
|
|
18250
|
+
const hostInfo = getHostInfo([]);
|
|
18002
18251
|
const mcpServersInfo = hostInfo.mcps.filter((mcp) => mcp.mcpName !== "unknown").map(
|
|
18003
18252
|
(mcp) => `- ${mcp.mcpName} (${mcp.ideName} ${mcp.ideVersion}): ${mcp.isRunning ? "\u2705 Running" : "\u274C Not Running"}`
|
|
18004
18253
|
).join("\n");
|
|
@@ -18255,7 +18504,10 @@ Example payload:
|
|
|
18255
18504
|
maxFiles: z37.number().optional().describe(
|
|
18256
18505
|
`Optional maximum number of files to scan (default: ${MCP_DEFAULT_MAX_FILES_TO_SCAN}). Increase for comprehensive scans of larger codebases or decrease for faster focused scans.`
|
|
18257
18506
|
),
|
|
18258
|
-
rescan: z37.boolean().optional().describe("Optional whether to rescan the repository")
|
|
18507
|
+
rescan: z37.boolean().optional().describe("Optional whether to rescan the repository"),
|
|
18508
|
+
scanRecentlyChangedFiles: z37.boolean().optional().describe(
|
|
18509
|
+
"Optional whether to automatically scan recently changed files when no changed files are found in git status. If false, the tool will prompt the user instead."
|
|
18510
|
+
)
|
|
18259
18511
|
}));
|
|
18260
18512
|
__publicField(this, "inputSchema", {
|
|
18261
18513
|
type: "object",
|
|
@@ -18279,6 +18531,10 @@ Example payload:
|
|
|
18279
18531
|
rescan: {
|
|
18280
18532
|
type: "boolean",
|
|
18281
18533
|
description: "[Optional] whether to rescan the repository"
|
|
18534
|
+
},
|
|
18535
|
+
scanRecentlyChangedFiles: {
|
|
18536
|
+
type: "boolean",
|
|
18537
|
+
description: "[Optional] whether to automatically scan recently changed files when no changed files are found in git status. If false, the tool will prompt the user instead."
|
|
18282
18538
|
}
|
|
18283
18539
|
},
|
|
18284
18540
|
required: ["path"]
|
|
@@ -18305,7 +18561,8 @@ Example payload:
|
|
|
18305
18561
|
path: path17,
|
|
18306
18562
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
18307
18563
|
maxFiles: args.maxFiles,
|
|
18308
|
-
scanContext: ScanContext.USER_REQUEST
|
|
18564
|
+
scanContext: ScanContext.USER_REQUEST,
|
|
18565
|
+
scanRecentlyChangedFiles: args.scanRecentlyChangedFiles
|
|
18309
18566
|
});
|
|
18310
18567
|
logDebug("Files", { files });
|
|
18311
18568
|
if (files.length === 0) {
|
|
@@ -18313,7 +18570,7 @@ Example payload:
|
|
|
18313
18570
|
content: [
|
|
18314
18571
|
{
|
|
18315
18572
|
type: "text",
|
|
18316
|
-
text:
|
|
18573
|
+
text: noChangedFilesFoundPrompt
|
|
18317
18574
|
}
|
|
18318
18575
|
]
|
|
18319
18576
|
};
|