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.
Files changed (2) hide show
  1. package/dist/index.mjs +326 -69
  2. 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
- retry: {
7628
- enabled: false
7627
+ fetch: getFetch(baseUrl),
7628
+ timeout: 1e4
7629
+ // 10 second timeout
7629
7630
  },
7630
- throttle: {
7631
- enabled: false
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 os5 = type();
11111
- const cxFileName = os5 === "Windows_NT" ? "cx.exe" : "cx";
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 (hostName === "claude" && config4?.projects) {
13461
- const allMcpServers = {};
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 os4 from "os";
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.trackServerStart();
13725
- }, 1e4);
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 = os4.networkInterfaces();
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(",") : `${os4.hostname()}-${uuidv43()}`;
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: os4.platform(),
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(status) {
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(hostId, organizationId, status);
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. 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.
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: "No changed files found in the repository. The vulnerability scanner analyzes modified, added, or staged files. Make some changes to your code and try again."
18573
+ text: noChangedFilesFoundPrompt
18317
18574
  }
18318
18575
  ]
18319
18576
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "1.0.186",
3
+ "version": "1.0.189",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "git+https://github.com/mobb-dev/bugsy.git",
6
6
  "main": "dist/index.mjs",