mobbdev 1.0.90 → 1.0.91

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 +1426 -1055
  2. package/package.json +4 -3
package/dist/index.mjs CHANGED
@@ -7,15 +7,15 @@ var __export = (target, all) => {
7
7
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
8
 
9
9
  // src/index.ts
10
- import Debug21 from "debug";
10
+ import Debug20 from "debug";
11
11
  import { hideBin } from "yargs/helpers";
12
12
 
13
13
  // src/args/commands/convert_to_sarif.ts
14
- import fs4 from "fs";
14
+ import fs5 from "fs";
15
15
 
16
16
  // src/commands/convert_to_sarif.ts
17
- import fs3 from "fs";
18
- import path3 from "path";
17
+ import fs4 from "fs";
18
+ import path5 from "path";
19
19
 
20
20
  // src/commands/fpr_stream_parser.ts
21
21
  import fs from "fs";
@@ -4658,7 +4658,7 @@ async function getAdoSdk(params) {
4658
4658
  const url = new URL(repoUrl);
4659
4659
  const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
4660
4660
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
4661
- const path12 = [
4661
+ const path13 = [
4662
4662
  prefixPath,
4663
4663
  owner,
4664
4664
  projectName,
@@ -4669,7 +4669,7 @@ async function getAdoSdk(params) {
4669
4669
  "items",
4670
4670
  "items"
4671
4671
  ].filter(Boolean).join("/");
4672
- return new URL(`${path12}?${params2}`, origin2).toString();
4672
+ return new URL(`${path13}?${params2}`, origin2).toString();
4673
4673
  },
4674
4674
  async getAdoBranchList({ repoUrl }) {
4675
4675
  try {
@@ -4892,106 +4892,599 @@ async function getAdoRepoList({
4892
4892
  // src/features/analysis/scm/ado/AdoSCMLib.ts
4893
4893
  import { setTimeout as setTimeout2 } from "timers/promises";
4894
4894
 
4895
- // src/features/analysis/scm/scmSubmit/index.ts
4895
+ // src/features/analysis/scm/git/GitService.ts
4896
+ import * as path2 from "path";
4896
4897
  import { simpleGit } from "simple-git";
4897
- var isValidBranchName = async (branchName) => {
4898
- const git = simpleGit();
4899
- try {
4900
- const res = await git.raw(["check-ref-format", "--branch", branchName]);
4901
- if (res) {
4898
+
4899
+ // src/features/analysis/scm/FileUtils.ts
4900
+ import fs2 from "fs";
4901
+ import { isBinary } from "istextorbinary";
4902
+ import path from "path";
4903
+ var EXCLUDED_FILE_PATTERNS = [
4904
+ // ... (copy the full array from FilePacking.ts)
4905
+ ".json",
4906
+ ".yaml",
4907
+ ".yml",
4908
+ ".toml",
4909
+ ".ini",
4910
+ ".conf",
4911
+ ".config",
4912
+ ".xml",
4913
+ ".env",
4914
+ ".md",
4915
+ ".txt",
4916
+ ".rst",
4917
+ ".adoc",
4918
+ ".lock",
4919
+ ".png",
4920
+ ".jpg",
4921
+ ".jpeg",
4922
+ ".gif",
4923
+ ".svg",
4924
+ ".ico",
4925
+ ".webp",
4926
+ ".bmp",
4927
+ ".tiff",
4928
+ ".ttf",
4929
+ ".otf",
4930
+ ".woff",
4931
+ ".woff2",
4932
+ ".eot",
4933
+ ".zip",
4934
+ ".tar",
4935
+ ".gz",
4936
+ ".rar",
4937
+ ".7z",
4938
+ ".log",
4939
+ ".db",
4940
+ ".sqlite",
4941
+ ".sql",
4942
+ ".pem",
4943
+ ".crt",
4944
+ ".key",
4945
+ ".p12",
4946
+ ".pfx",
4947
+ ".editorconfig",
4948
+ ".sublime-project",
4949
+ ".sublime-workspace",
4950
+ ".DS_Store",
4951
+ "Thumbs.db",
4952
+ ".lcov",
4953
+ ".exe",
4954
+ ".dll",
4955
+ ".so",
4956
+ ".dylib",
4957
+ ".class",
4958
+ ".pyc",
4959
+ ".pyo",
4960
+ ".o",
4961
+ ".obj",
4962
+ ".min.js",
4963
+ ".min.css",
4964
+ ".min.html",
4965
+ ".test.js",
4966
+ ".test.ts",
4967
+ ".test.jsx",
4968
+ ".test.tsx",
4969
+ ".spec.js",
4970
+ ".spec.ts",
4971
+ ".spec.jsx",
4972
+ ".spec.tsx",
4973
+ ".d.ts",
4974
+ ".bundle.js",
4975
+ ".chunk.js",
4976
+ "dockerfile",
4977
+ "jenkinsfile",
4978
+ "go.sum",
4979
+ ".gitignore",
4980
+ ".gitattributes",
4981
+ ".gitmodules",
4982
+ ".gitkeep",
4983
+ ".keep",
4984
+ ".hgignore",
4985
+ ".nvmrc",
4986
+ ".node-version",
4987
+ ".npmrc",
4988
+ ".yarnrc",
4989
+ ".pnpmfile.cjs",
4990
+ ".ruby-version",
4991
+ ".python-version",
4992
+ ".rvmrc",
4993
+ ".rbenv-version",
4994
+ ".gvmrc",
4995
+ "makefile",
4996
+ "rakefile",
4997
+ "gulpfile.js",
4998
+ "gruntfile.js",
4999
+ "webpack.config.js",
5000
+ "webpack.config.ts",
5001
+ "rollup.config.js",
5002
+ "vite.config.js",
5003
+ "vite.config.ts",
5004
+ "next.config.js",
5005
+ "nuxt.config.js",
5006
+ "tailwind.config.js",
5007
+ "postcss.config.js",
5008
+ ".babelrc",
5009
+ ".babelrc.js",
5010
+ ".swcrc",
5011
+ ".browserslistrc",
5012
+ "jest.config.js",
5013
+ "jest.config.ts",
5014
+ "vitest.config.js",
5015
+ "karma.conf.js",
5016
+ "protractor.conf.js",
5017
+ "cypress.config.js",
5018
+ "playwright.config.js",
5019
+ ".nycrc",
5020
+ ".c8rc",
5021
+ ".eslintrc",
5022
+ ".eslintrc.js",
5023
+ ".prettierrc",
5024
+ ".prettierrc.js",
5025
+ ".stylelintrc",
5026
+ ".stylelintrc.js",
5027
+ "pipfile",
5028
+ "gemfile",
5029
+ "go.mod",
5030
+ "project.clj",
5031
+ "setup.py",
5032
+ "setup.cfg",
5033
+ "manifest.in",
5034
+ ".pythonrc",
5035
+ "readme",
5036
+ "changelog",
5037
+ "authors",
5038
+ "contributors",
5039
+ "license",
5040
+ "notice",
5041
+ "copyright",
5042
+ ".htaccess"
5043
+ ];
5044
+ var FileUtils = class {
5045
+ static isExcludedFileType(filepath) {
5046
+ const basename = path.basename(filepath).toLowerCase();
5047
+ if (basename === ".env" || basename.startsWith(".env.")) {
5048
+ return true;
5049
+ }
5050
+ if (EXCLUDED_FILE_PATTERNS.some((pattern) => basename.endsWith(pattern))) {
4902
5051
  return true;
4903
5052
  }
4904
- return false;
4905
- } catch (e) {
4906
5053
  return false;
4907
5054
  }
4908
- };
4909
-
4910
- // src/features/analysis/scm/scm.ts
4911
- var SCMLib = class {
4912
- constructor(url, accessToken, scmOrg) {
4913
- __publicField(this, "url");
4914
- __publicField(this, "accessToken");
4915
- __publicField(this, "scmOrg");
4916
- this.accessToken = accessToken;
4917
- this.url = url;
4918
- this.scmOrg = scmOrg;
4919
- }
4920
- async getUrlWithCredentials() {
4921
- if (!this.url) {
4922
- console.error("no url for getUrlWithCredentials()");
4923
- throw new Error("no url");
5055
+ static shouldPackFile(filepath, maxFileSize = 1024 * 1024 * 5) {
5056
+ const absoluteFilepath = path.resolve(filepath);
5057
+ if (this.isExcludedFileType(filepath)) return false;
5058
+ if (!fs2.existsSync(absoluteFilepath)) return false;
5059
+ if (fs2.lstatSync(absoluteFilepath).size > maxFileSize) return false;
5060
+ let data;
5061
+ try {
5062
+ data = fs2.readFileSync(absoluteFilepath);
5063
+ } catch {
5064
+ return false;
4924
5065
  }
4925
- const trimmedUrl = this.url.trim().replace(/\/$/, "");
4926
- const accessToken = this.getAccessToken();
4927
- if (!accessToken) {
4928
- return trimmedUrl;
5066
+ if (isBinary(null, data)) return false;
5067
+ return true;
5068
+ }
5069
+ static getAllFiles(dir, rootDir) {
5070
+ const root = rootDir || dir;
5071
+ const results = [];
5072
+ const relativeDepth = path.relative(root, dir).split(path.sep).length;
5073
+ if (relativeDepth > 20) {
5074
+ return [];
4929
5075
  }
4930
- if (this.scmLibType === "ADO" /* ADO */) {
4931
- const { host, protocol, pathname } = new URL(trimmedUrl);
4932
- return `${protocol}//${accessToken}@${host}${pathname}`;
5076
+ if (results.length > 1e5) {
5077
+ return [];
4933
5078
  }
4934
- const finalUrl = this.scmLibType === "GITLAB" /* GITLAB */ ? `${trimmedUrl}.git` : trimmedUrl;
4935
- const username = await this._getUsernameForAuthUrl();
4936
- return buildAuthorizedRepoUrl({
4937
- url: finalUrl,
4938
- username,
4939
- password: accessToken
4940
- });
4941
- }
4942
- getAccessToken() {
4943
- return this.accessToken || "";
4944
- }
4945
- getUrl() {
4946
- return this.url;
4947
- }
4948
- getName() {
4949
- if (!this.url) {
4950
- return "";
5079
+ try {
5080
+ fs2.accessSync(dir, fs2.constants.R_OK);
5081
+ } catch {
5082
+ return [];
4951
5083
  }
4952
- return this.url.split("/").at(-1) || "";
4953
- }
4954
- _validateAccessToken() {
4955
- if (!this.accessToken) {
4956
- console.error("no access token");
4957
- throw new Error("no access token");
5084
+ const items = fs2.readdirSync(dir);
5085
+ for (const item of items) {
5086
+ const fullPath = path.join(dir, item);
5087
+ try {
5088
+ fs2.accessSync(fullPath, fs2.constants.R_OK);
5089
+ } catch {
5090
+ continue;
5091
+ }
5092
+ const stat = fs2.statSync(fullPath);
5093
+ if (stat.isDirectory()) {
5094
+ results.push(...this.getAllFiles(fullPath, root));
5095
+ } else {
5096
+ results.push({
5097
+ name: item,
5098
+ fullPath,
5099
+ relativePath: path.relative(root, fullPath),
5100
+ time: stat.mtime.getTime(),
5101
+ isFile: true
5102
+ });
5103
+ }
4958
5104
  }
5105
+ return results;
4959
5106
  }
4960
- static async getIsValidBranchName(branchName) {
4961
- return isValidBranchName(branchName);
4962
- }
4963
- _validateAccessTokenAndUrl() {
4964
- this._validateAccessToken();
4965
- this._validateUrl();
4966
- }
4967
- _validateUrl() {
4968
- if (!this.url) {
4969
- console.error("no url");
4970
- throw new InvalidRepoUrlError("no url");
4971
- }
5107
+ static getLastChangedFiles(dir, maxFileSize = 1024 * 1024 * 5, count = 10) {
5108
+ if (!fs2.existsSync(dir) || !fs2.lstatSync(dir).isDirectory()) return [];
5109
+ const files = this.getAllFiles(dir);
5110
+ return files.filter((file) => this.shouldPackFile(file.fullPath, maxFileSize)).sort((a, b) => b.time - a.time).slice(0, count).map((file) => file.relativePath);
4972
5111
  }
4973
5112
  };
4974
5113
 
4975
- // src/features/analysis/scm/ado/AdoSCMLib.ts
4976
- async function initAdoSdk(params) {
4977
- const { url, accessToken, scmOrg } = params;
4978
- const adoClientParams = await getAdoClientParams({
4979
- tokenOrg: scmOrg,
4980
- accessToken,
4981
- url
4982
- });
4983
- return getAdoSdk(adoClientParams);
4984
- }
4985
- var AdoSCMLib = class extends SCMLib {
4986
- constructor(url, accessToken, scmOrg) {
4987
- super(url, accessToken, scmOrg);
4988
- __publicField(this, "_adoSdkPromise");
4989
- this._adoSdkPromise = initAdoSdk({ accessToken, url, scmOrg });
5114
+ // src/features/analysis/scm/git/GitService.ts
5115
+ var GitService = class {
5116
+ constructor(repositoryPath, log2) {
5117
+ __publicField(this, "git");
5118
+ __publicField(this, "repositoryPath");
5119
+ __publicField(this, "log");
5120
+ const noopLog = (_message, _level, _data) => {
5121
+ };
5122
+ this.log = log2 || noopLog;
5123
+ this.git = simpleGit(repositoryPath, { binary: "git" });
5124
+ this.repositoryPath = repositoryPath;
5125
+ this.log("Git service initialized", "debug", { repositoryPath });
4990
5126
  }
4991
- async getAdoSdk() {
4992
- if (!this._adoSdkPromise) {
4993
- console.error("ado sdk was not initialized");
4994
- throw new InvalidAccessTokenError("ado sdk was not initialized");
5127
+ /**
5128
+ * Validates that the path is a valid git repository
5129
+ */
5130
+ async validateRepository() {
5131
+ this.log("Validating git repository", "debug");
5132
+ try {
5133
+ const isRepo = await this.git.checkIsRepo();
5134
+ if (!isRepo) {
5135
+ const error = "Path is not a valid git repository";
5136
+ this.log(error, "error");
5137
+ return { isValid: false, error };
5138
+ }
5139
+ this.log("Git repository validation successful", "debug");
5140
+ return { isValid: true };
5141
+ } catch (error) {
5142
+ const errorMessage = `Failed to verify git repository: ${error.message}`;
5143
+ this.log(errorMessage, "error", { error });
5144
+ return { isValid: false, error: errorMessage };
5145
+ }
5146
+ }
5147
+ /**
5148
+ * Gets the current git status and returns changed files
5149
+ */
5150
+ async getChangedFiles() {
5151
+ this.log("Getting git status", "debug");
5152
+ try {
5153
+ const status = await this.git.status();
5154
+ const gitRoot = await this.git.revparse(["--show-toplevel"]);
5155
+ const relativePathFromGitRoot = path2.relative(
5156
+ gitRoot,
5157
+ this.repositoryPath
5158
+ );
5159
+ const files = status.files.map((file) => {
5160
+ const gitRelativePath = file.path;
5161
+ if (relativePathFromGitRoot === "") {
5162
+ return gitRelativePath;
5163
+ }
5164
+ if (gitRelativePath.startsWith(relativePathFromGitRoot + "/")) {
5165
+ return gitRelativePath.substring(relativePathFromGitRoot.length + 1);
5166
+ }
5167
+ return path2.relative(
5168
+ this.repositoryPath,
5169
+ path2.join(gitRoot, gitRelativePath)
5170
+ );
5171
+ });
5172
+ this.log("Git status retrieved", "info", {
5173
+ fileCount: files.length,
5174
+ files: files.slice(0, 10),
5175
+ // Log first 10 files to avoid spam
5176
+ gitRoot,
5177
+ workingDir: this.repositoryPath,
5178
+ relativePathFromGitRoot
5179
+ });
5180
+ return { files, status };
5181
+ } catch (error) {
5182
+ const errorMessage = `Failed to get git status: ${error.message}`;
5183
+ this.log(errorMessage, "error", { error });
5184
+ throw new Error(errorMessage);
5185
+ }
5186
+ }
5187
+ /**
5188
+ * Gets git repository information including remote URL, current commit hash, and branch name
5189
+ */
5190
+ async getGitInfo() {
5191
+ this.log("Getting git repository information", "debug");
5192
+ try {
5193
+ const [repoUrl, hash, reference] = await Promise.all([
5194
+ this.git.getConfig("remote.origin.url"),
5195
+ this.git.revparse(["HEAD"]),
5196
+ this.git.revparse(["--abbrev-ref", "HEAD"])
5197
+ ]);
5198
+ let normalizedRepoUrl = repoUrl.value || "";
5199
+ if (normalizedRepoUrl.endsWith(".git")) {
5200
+ normalizedRepoUrl = normalizedRepoUrl.slice(0, -".git".length);
5201
+ }
5202
+ if (normalizedRepoUrl.startsWith("git@github.com:")) {
5203
+ normalizedRepoUrl = normalizedRepoUrl.replace(
5204
+ "git@github.com:",
5205
+ "https://github.com/"
5206
+ );
5207
+ }
5208
+ this.log("Git repository information retrieved", "debug", {
5209
+ repoUrl: normalizedRepoUrl,
5210
+ hash,
5211
+ reference
5212
+ });
5213
+ return {
5214
+ repoUrl: normalizedRepoUrl,
5215
+ hash,
5216
+ reference
5217
+ };
5218
+ } catch (error) {
5219
+ const errorMessage = `Failed to get git repository information: ${error.message}`;
5220
+ this.log(errorMessage, "error", { error });
5221
+ throw new Error(errorMessage);
5222
+ }
5223
+ }
5224
+ /**
5225
+ * Validates if a branch name is valid according to git's rules
5226
+ */
5227
+ async isValidBranchName(branchName) {
5228
+ this.log("Validating branch name", "debug", { branchName });
5229
+ try {
5230
+ const result = await this.git.raw([
5231
+ "check-ref-format",
5232
+ "--branch",
5233
+ branchName
5234
+ ]);
5235
+ const isValid = Boolean(result);
5236
+ this.log("Branch name validation result", "debug", {
5237
+ branchName,
5238
+ isValid
5239
+ });
5240
+ return isValid;
5241
+ } catch (error) {
5242
+ this.log("Branch name validation failed", "debug", { branchName, error });
5243
+ return false;
5244
+ }
5245
+ }
5246
+ /**
5247
+ * Gets the current branch name
5248
+ */
5249
+ async getCurrentBranch() {
5250
+ this.log("Getting current branch name", "debug");
5251
+ try {
5252
+ const branch = await this.git.revparse(["--abbrev-ref", "HEAD"]);
5253
+ this.log("Current branch retrieved", "debug", { branch });
5254
+ return branch;
5255
+ } catch (error) {
5256
+ const errorMessage = `Failed to get current branch: ${error.message}`;
5257
+ this.log(errorMessage, "error", { error });
5258
+ throw new Error(errorMessage);
5259
+ }
5260
+ }
5261
+ /**
5262
+ * Gets the current commit hash
5263
+ */
5264
+ async getCurrentCommitHash() {
5265
+ this.log("Getting current commit hash", "debug");
5266
+ try {
5267
+ const hash = await this.git.revparse(["HEAD"]);
5268
+ this.log("Current commit hash retrieved", "debug", { hash });
5269
+ return hash;
5270
+ } catch (error) {
5271
+ const errorMessage = `Failed to get current commit hash: ${error.message}`;
5272
+ this.log(errorMessage, "error", { error });
5273
+ throw new Error(errorMessage);
5274
+ }
5275
+ }
5276
+ /**
5277
+ * Gets the remote repository URL
5278
+ */
5279
+ async getRemoteUrl() {
5280
+ this.log("Getting remote repository URL", "debug");
5281
+ try {
5282
+ const remoteUrl = await this.git.getConfig("remote.origin.url");
5283
+ const url = remoteUrl.value || "";
5284
+ let normalizedUrl = url;
5285
+ if (normalizedUrl.endsWith(".git")) {
5286
+ normalizedUrl = normalizedUrl.slice(0, -".git".length);
5287
+ }
5288
+ if (normalizedUrl.startsWith("git@github.com:")) {
5289
+ normalizedUrl = normalizedUrl.replace(
5290
+ "git@github.com:",
5291
+ "https://github.com/"
5292
+ );
5293
+ }
5294
+ this.log("Remote repository URL retrieved", "debug", {
5295
+ url: normalizedUrl
5296
+ });
5297
+ return normalizedUrl;
5298
+ } catch (error) {
5299
+ const errorMessage = `Failed to get remote repository URL: ${error.message}`;
5300
+ this.log(errorMessage, "error", { error });
5301
+ throw new Error(errorMessage);
5302
+ }
5303
+ }
5304
+ /**
5305
+ * Gets the 10 most recently changed files based on commit history
5306
+ */
5307
+ async getRecentlyChangedFiles() {
5308
+ this.log(
5309
+ "Getting the 10 most recently changed files from commit history",
5310
+ "debug"
5311
+ );
5312
+ try {
5313
+ const gitRoot = await this.git.revparse(["--show-toplevel"]);
5314
+ const relativePathFromGitRoot = path2.relative(
5315
+ gitRoot,
5316
+ this.repositoryPath
5317
+ );
5318
+ const fileSet = /* @__PURE__ */ new Set();
5319
+ const files = [];
5320
+ let commitsProcessed = 0;
5321
+ const logResult = await this.git.log({
5322
+ maxCount: 100,
5323
+ // Get last 100 commits - should be enough to find 10 unique files
5324
+ format: {
5325
+ hash: "%H",
5326
+ date: "%ai",
5327
+ message: "%s",
5328
+ //the field name author_name can't follow the naming convention as we are using the git log command
5329
+ // eslint-disable-next-line @typescript-eslint/naming-convention
5330
+ author_name: "%an"
5331
+ }
5332
+ });
5333
+ for (const commit of logResult.all) {
5334
+ if (files.length >= 10) {
5335
+ break;
5336
+ }
5337
+ commitsProcessed++;
5338
+ try {
5339
+ const filesOutput = await this.git.show([
5340
+ "--name-only",
5341
+ "--pretty=format:",
5342
+ commit.hash
5343
+ ]);
5344
+ const commitFiles = filesOutput.split("\n").filter((file) => file.trim() !== "");
5345
+ for (const file of commitFiles) {
5346
+ if (files.length >= 10) {
5347
+ break;
5348
+ }
5349
+ const gitRelativePath = file.trim();
5350
+ let adjustedPath;
5351
+ if (relativePathFromGitRoot === "") {
5352
+ adjustedPath = gitRelativePath;
5353
+ } else if (gitRelativePath.startsWith(relativePathFromGitRoot + "/")) {
5354
+ adjustedPath = gitRelativePath.substring(
5355
+ relativePathFromGitRoot.length + 1
5356
+ );
5357
+ } else {
5358
+ adjustedPath = path2.relative(
5359
+ this.repositoryPath,
5360
+ path2.join(gitRoot, gitRelativePath)
5361
+ );
5362
+ }
5363
+ this.log(`Considering file: ${adjustedPath}`, "debug");
5364
+ if (!fileSet.has(adjustedPath) && FileUtils.shouldPackFile(path2.join(gitRoot, gitRelativePath))) {
5365
+ fileSet.add(adjustedPath);
5366
+ files.push(adjustedPath);
5367
+ }
5368
+ }
5369
+ } catch (showError) {
5370
+ this.log(`Could not get files for commit ${commit.hash}`, "debug", {
5371
+ error: showError
5372
+ });
5373
+ }
5374
+ }
5375
+ this.log("Recently changed files retrieved", "info", {
5376
+ fileCount: files.length,
5377
+ commitsProcessed,
5378
+ totalCommitsAvailable: logResult.all.length,
5379
+ files: files.slice(0, 10),
5380
+ // Log the files (should be all of them since we limit to 10)
5381
+ gitRoot,
5382
+ workingDir: this.repositoryPath,
5383
+ relativePathFromGitRoot
5384
+ });
5385
+ return {
5386
+ files,
5387
+ commitCount: commitsProcessed
5388
+ };
5389
+ } catch (error) {
5390
+ const errorMessage = `Failed to get recently changed files: ${error.message}`;
5391
+ this.log(errorMessage, "error", { error });
5392
+ throw new Error(errorMessage);
5393
+ }
5394
+ }
5395
+ };
5396
+
5397
+ // src/features/analysis/scm/scmSubmit/index.ts
5398
+ var isValidBranchName = async (branchName) => {
5399
+ const gitService = new GitService(process.cwd());
5400
+ return gitService.isValidBranchName(branchName);
5401
+ };
5402
+
5403
+ // src/features/analysis/scm/scm.ts
5404
+ var SCMLib = class {
5405
+ constructor(url, accessToken, scmOrg) {
5406
+ __publicField(this, "url");
5407
+ __publicField(this, "accessToken");
5408
+ __publicField(this, "scmOrg");
5409
+ this.accessToken = accessToken;
5410
+ this.url = url;
5411
+ this.scmOrg = scmOrg;
5412
+ }
5413
+ async getUrlWithCredentials() {
5414
+ if (!this.url) {
5415
+ console.error("no url for getUrlWithCredentials()");
5416
+ throw new Error("no url");
5417
+ }
5418
+ const trimmedUrl = this.url.trim().replace(/\/$/, "");
5419
+ const accessToken = this.getAccessToken();
5420
+ if (!accessToken) {
5421
+ return trimmedUrl;
5422
+ }
5423
+ if (this.scmLibType === "ADO" /* ADO */) {
5424
+ const { host, protocol, pathname } = new URL(trimmedUrl);
5425
+ return `${protocol}//${accessToken}@${host}${pathname}`;
5426
+ }
5427
+ const finalUrl = this.scmLibType === "GITLAB" /* GITLAB */ ? `${trimmedUrl}.git` : trimmedUrl;
5428
+ const username = await this._getUsernameForAuthUrl();
5429
+ return buildAuthorizedRepoUrl({
5430
+ url: finalUrl,
5431
+ username,
5432
+ password: accessToken
5433
+ });
5434
+ }
5435
+ getAccessToken() {
5436
+ return this.accessToken || "";
5437
+ }
5438
+ getUrl() {
5439
+ return this.url;
5440
+ }
5441
+ getName() {
5442
+ if (!this.url) {
5443
+ return "";
5444
+ }
5445
+ return this.url.split("/").at(-1) || "";
5446
+ }
5447
+ _validateAccessToken() {
5448
+ if (!this.accessToken) {
5449
+ console.error("no access token");
5450
+ throw new Error("no access token");
5451
+ }
5452
+ }
5453
+ static async getIsValidBranchName(branchName) {
5454
+ return isValidBranchName(branchName);
5455
+ }
5456
+ _validateAccessTokenAndUrl() {
5457
+ this._validateAccessToken();
5458
+ this._validateUrl();
5459
+ }
5460
+ _validateUrl() {
5461
+ if (!this.url) {
5462
+ console.error("no url");
5463
+ throw new InvalidRepoUrlError("no url");
5464
+ }
5465
+ }
5466
+ };
5467
+
5468
+ // src/features/analysis/scm/ado/AdoSCMLib.ts
5469
+ async function initAdoSdk(params) {
5470
+ const { url, accessToken, scmOrg } = params;
5471
+ const adoClientParams = await getAdoClientParams({
5472
+ tokenOrg: scmOrg,
5473
+ accessToken,
5474
+ url
5475
+ });
5476
+ return getAdoSdk(adoClientParams);
5477
+ }
5478
+ var AdoSCMLib = class extends SCMLib {
5479
+ constructor(url, accessToken, scmOrg) {
5480
+ super(url, accessToken, scmOrg);
5481
+ __publicField(this, "_adoSdkPromise");
5482
+ this._adoSdkPromise = initAdoSdk({ accessToken, url, scmOrg });
5483
+ }
5484
+ async getAdoSdk() {
5485
+ if (!this._adoSdkPromise) {
5486
+ console.error("ado sdk was not initialized");
5487
+ throw new InvalidAccessTokenError("ado sdk was not initialized");
4995
5488
  }
4996
5489
  return this._adoSdkPromise;
4997
5490
  }
@@ -6127,14 +6620,14 @@ function getGithubSdk(params = {}) {
6127
6620
  };
6128
6621
  },
6129
6622
  async getGithubBlameRanges(params2) {
6130
- const { ref, gitHubUrl, path: path12 } = params2;
6623
+ const { ref, gitHubUrl, path: path13 } = params2;
6131
6624
  const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
6132
6625
  const res = await octokit.graphql(
6133
6626
  GET_BLAME_DOCUMENT,
6134
6627
  {
6135
6628
  owner,
6136
6629
  repo,
6137
- path: path12,
6630
+ path: path13,
6138
6631
  ref
6139
6632
  }
6140
6633
  );
@@ -6440,11 +6933,11 @@ var GithubSCMLib = class extends SCMLib {
6440
6933
  markdownComment: comment
6441
6934
  });
6442
6935
  }
6443
- async getRepoBlameRanges(ref, path12) {
6936
+ async getRepoBlameRanges(ref, path13) {
6444
6937
  this._validateUrl();
6445
6938
  return await this.githubSdk.getGithubBlameRanges({
6446
6939
  ref,
6447
- path: path12,
6940
+ path: path13,
6448
6941
  gitHubUrl: this.url
6449
6942
  });
6450
6943
  }
@@ -6846,13 +7339,13 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
6846
7339
  const { organization, repoName, projectPath } = parsingResult;
6847
7340
  return { owner: organization, repo: repoName, projectPath };
6848
7341
  }
6849
- async function getGitlabBlameRanges({ ref, gitlabUrl, path: path12 }, options) {
7342
+ async function getGitlabBlameRanges({ ref, gitlabUrl, path: path13 }, options) {
6850
7343
  const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
6851
7344
  const api2 = getGitBeaker({
6852
7345
  url: gitlabUrl,
6853
7346
  gitlabAuthToken: options?.gitlabAuthToken
6854
7347
  });
6855
- const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path12, ref);
7348
+ const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path13, ref);
6856
7349
  let lineNumber = 1;
6857
7350
  return resp.filter((range) => range.lines).map((range) => {
6858
7351
  const oldLineNumber = lineNumber;
@@ -7028,10 +7521,10 @@ var GitlabSCMLib = class extends SCMLib {
7028
7521
  markdownComment: comment
7029
7522
  });
7030
7523
  }
7031
- async getRepoBlameRanges(ref, path12) {
7524
+ async getRepoBlameRanges(ref, path13) {
7032
7525
  this._validateUrl();
7033
7526
  return await getGitlabBlameRanges(
7034
- { ref, path: path12, gitlabUrl: this.url },
7527
+ { ref, path: path13, gitlabUrl: this.url },
7035
7528
  {
7036
7529
  url: this.url,
7037
7530
  gitlabAuthToken: this.accessToken
@@ -7234,13 +7727,13 @@ __export(utils_exports, {
7234
7727
  });
7235
7728
 
7236
7729
  // src/utils/dirname.ts
7237
- import path from "path";
7730
+ import path3 from "path";
7238
7731
  import { fileURLToPath } from "url";
7239
7732
  function getDirName() {
7240
- return path.dirname(fileURLToPath(import.meta.url));
7733
+ return path3.dirname(fileURLToPath(import.meta.url));
7241
7734
  }
7242
7735
  function getTopLevelDirName(fullPath) {
7243
- return path.parse(fullPath).name;
7736
+ return path3.parse(fullPath).name;
7244
7737
  }
7245
7738
 
7246
7739
  // src/utils/keypress.ts
@@ -7303,15 +7796,15 @@ function Spinner({ ci = false } = {}) {
7303
7796
  }
7304
7797
 
7305
7798
  // src/utils/check_node_version.ts
7306
- import fs2 from "fs";
7307
- import path2 from "path";
7799
+ import fs3 from "fs";
7800
+ import path4 from "path";
7308
7801
  import semver from "semver";
7309
7802
  function getPackageJson() {
7310
- let manifestPath = path2.join(getDirName(), "../package.json");
7311
- if (!fs2.existsSync(manifestPath)) {
7312
- manifestPath = path2.join(getDirName(), "../../package.json");
7803
+ let manifestPath = path4.join(getDirName(), "../package.json");
7804
+ if (!fs3.existsSync(manifestPath)) {
7805
+ manifestPath = path4.join(getDirName(), "../../package.json");
7313
7806
  }
7314
- return JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
7807
+ return JSON.parse(fs3.readFileSync(manifestPath, "utf8"));
7315
7808
  }
7316
7809
  var packageJson = getPackageJson();
7317
7810
  if (!semver.satisfies(process.version, packageJson.engines.node)) {
@@ -7354,12 +7847,12 @@ async function convertFprToSarif(inputFilePath, outputFilePath, codePathPatterns
7354
7847
  unsafeCleanup: true
7355
7848
  });
7356
7849
  try {
7357
- const auditFvdlPath = path3.join(tmpObj.name, "audit.fvdl");
7850
+ const auditFvdlPath = path5.join(tmpObj.name, "audit.fvdl");
7358
7851
  await zipIn.extract("audit.fvdl", auditFvdlPath);
7359
7852
  const auditFvdlSaxParser = initSaxParser(auditFvdlPath);
7360
7853
  const vulnerabilityParser = new VulnerabilityParser(
7361
7854
  auditFvdlSaxParser.parser,
7362
- path3.join(tmpObj.name, "vulns.json")
7855
+ path5.join(tmpObj.name, "vulns.json")
7363
7856
  );
7364
7857
  const unifiedNodePoolParser = new UnifiedNodePoolParser(
7365
7858
  auditFvdlSaxParser.parser
@@ -7370,14 +7863,14 @@ async function convertFprToSarif(inputFilePath, outputFilePath, codePathPatterns
7370
7863
  let auditMetadataParser = null;
7371
7864
  await auditFvdlSaxParser.parse();
7372
7865
  if ("audit.xml" in zipInEntries) {
7373
- const auditXmlPath = path3.join(tmpObj.name, "audit.xml");
7866
+ const auditXmlPath = path5.join(tmpObj.name, "audit.xml");
7374
7867
  await zipIn.extract("audit.xml", auditXmlPath);
7375
7868
  const auditXmlSaxParser = initSaxParser(auditXmlPath);
7376
7869
  auditMetadataParser = new AuditMetadataParser(auditXmlSaxParser.parser);
7377
7870
  await auditXmlSaxParser.parse();
7378
7871
  }
7379
7872
  await zipIn.close();
7380
- const writer = fs3.createWriteStream(outputFilePath);
7873
+ const writer = fs4.createWriteStream(outputFilePath);
7381
7874
  writer.write(`{
7382
7875
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
7383
7876
  "version": "2.1.0",
@@ -7482,15 +7975,15 @@ function fortifyNodesToSarifLocations(nodes, unifiedNodePoolParser) {
7482
7975
  import chalk2 from "chalk";
7483
7976
 
7484
7977
  // src/constants.ts
7485
- import path4 from "path";
7978
+ import path6 from "path";
7486
7979
  import { fileURLToPath as fileURLToPath2 } from "url";
7487
7980
  import chalk from "chalk";
7488
7981
  import Debug4 from "debug";
7489
7982
  import * as dotenv from "dotenv";
7490
7983
  import { z as z24 } from "zod";
7491
7984
  var debug4 = Debug4("mobbdev:constants");
7492
- var __dirname = path4.dirname(fileURLToPath2(import.meta.url));
7493
- dotenv.config({ path: path4.join(__dirname, "../.env") });
7985
+ var __dirname = path6.dirname(fileURLToPath2(import.meta.url));
7986
+ dotenv.config({ path: path6.join(__dirname, "../.env") });
7494
7987
  var scmFriendlyText = {
7495
7988
  ["Ado" /* Ado */]: "Azure DevOps",
7496
7989
  ["Bitbucket" /* Bitbucket */]: "Bitbucket",
@@ -7711,7 +8204,7 @@ function convertToSarifBuilder(args) {
7711
8204
  ).help().demandOption(["input-file-path", "input-file-format", "output-file-path"]);
7712
8205
  }
7713
8206
  async function validateConvertToSarifOptions(args) {
7714
- if (!fs4.existsSync(args.inputFilePath)) {
8207
+ if (!fs5.existsSync(args.inputFilePath)) {
7715
8208
  throw new CliError(
7716
8209
  "\nError: --input-file-path flag should point to an existing file"
7717
8210
  );
@@ -7737,16 +8230,16 @@ import chalk10 from "chalk";
7737
8230
  import yargs from "yargs/yargs";
7738
8231
 
7739
8232
  // src/args/commands/analyze.ts
7740
- import fs7 from "fs";
8233
+ import fs8 from "fs";
7741
8234
 
7742
8235
  // src/commands/index.ts
7743
8236
  import crypto from "crypto";
7744
8237
  import os from "os";
7745
8238
 
7746
8239
  // src/features/analysis/index.ts
7747
- import fs6 from "fs";
8240
+ import fs7 from "fs";
7748
8241
  import fsPromises from "fs/promises";
7749
- import path7 from "path";
8242
+ import path9 from "path";
7750
8243
  import { env as env2 } from "process";
7751
8244
  import { pipeline } from "stream/promises";
7752
8245
  import chalk5 from "chalk";
@@ -8030,7 +8523,7 @@ async function postIssueComment(params) {
8030
8523
  fpDescription
8031
8524
  } = params;
8032
8525
  const {
8033
- path: path12,
8526
+ path: path13,
8034
8527
  startLine,
8035
8528
  vulnerabilityReportIssue: {
8036
8529
  vulnerabilityReportIssueTags,
@@ -8045,7 +8538,7 @@ async function postIssueComment(params) {
8045
8538
  Refresh the page in order to see the changes.`,
8046
8539
  pull_number: pullRequest,
8047
8540
  commit_id: commitSha,
8048
- path: path12,
8541
+ path: path13,
8049
8542
  line: startLine
8050
8543
  });
8051
8544
  const commentId = commentRes.data.id;
@@ -8079,7 +8572,7 @@ async function postFixComment(params) {
8079
8572
  scanner
8080
8573
  } = params;
8081
8574
  const {
8082
- path: path12,
8575
+ path: path13,
8083
8576
  startLine,
8084
8577
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
8085
8578
  vulnerabilityReportIssueId
@@ -8097,7 +8590,7 @@ async function postFixComment(params) {
8097
8590
  Refresh the page in order to see the changes.`,
8098
8591
  pull_number: pullRequest,
8099
8592
  commit_id: commitSha,
8100
- path: path12,
8593
+ path: path13,
8101
8594
  line: startLine
8102
8595
  });
8103
8596
  const commentId = commentRes.data.id;
@@ -8376,54 +8869,37 @@ async function handleAutoPr(params) {
8376
8869
 
8377
8870
  // src/features/analysis/git.ts
8378
8871
  import Debug10 from "debug";
8379
- import { simpleGit as simpleGit2 } from "simple-git";
8380
8872
  var debug10 = Debug10("mobbdev:git");
8381
- var GIT_NOT_INITIALIZED_ERROR_MESSAGE = "not a git repository";
8382
8873
  async function getGitInfo(srcDirPath) {
8383
8874
  debug10("getting git info for %s", srcDirPath);
8384
- const git = simpleGit2({
8385
- baseDir: srcDirPath,
8386
- maxConcurrentProcesses: 1,
8387
- trimmed: true
8388
- });
8389
- let repoUrl = "";
8390
- let hash = "";
8391
- let reference = "";
8875
+ const gitService = new GitService(srcDirPath);
8392
8876
  try {
8393
- repoUrl = (await git.getConfig("remote.origin.url")).value || "";
8394
- hash = await git.revparse(["HEAD"]) || "";
8395
- reference = await git.revparse(["--abbrev-ref", "HEAD"]) || "";
8877
+ const validationResult = await gitService.validateRepository();
8878
+ if (!validationResult.isValid) {
8879
+ debug10("folder is not a git repo");
8880
+ return {
8881
+ success: false,
8882
+ hash: void 0,
8883
+ reference: void 0,
8884
+ repoUrl: void 0
8885
+ };
8886
+ }
8887
+ const gitInfo = await gitService.getGitInfo();
8888
+ return {
8889
+ success: true,
8890
+ ...gitInfo
8891
+ };
8396
8892
  } catch (e) {
8397
8893
  if (e instanceof Error) {
8398
8894
  debug10("failed to run git %o", e);
8399
8895
  if (e.message.includes(" spawn ")) {
8400
8896
  debug10("git cli not installed");
8401
- } else if (e.message.includes(GIT_NOT_INITIALIZED_ERROR_MESSAGE)) {
8402
- debug10("folder is not a git repo");
8403
- return {
8404
- success: false,
8405
- hash: void 0,
8406
- reference: void 0,
8407
- repoUrl: void 0
8408
- };
8409
8897
  } else {
8410
8898
  throw e;
8411
8899
  }
8412
8900
  }
8413
8901
  throw e;
8414
8902
  }
8415
- if (repoUrl.endsWith(".git")) {
8416
- repoUrl = repoUrl.slice(0, -".git".length);
8417
- }
8418
- if (repoUrl.startsWith("git@github.com:")) {
8419
- repoUrl = repoUrl.replace("git@github.com:", "https://github.com/");
8420
- }
8421
- return {
8422
- success: true,
8423
- repoUrl,
8424
- hash,
8425
- reference
8426
- };
8427
8903
  }
8428
8904
 
8429
8905
  // src/features/analysis/graphql/gql.ts
@@ -8439,6 +8915,7 @@ import Debug11 from "debug";
8439
8915
  import { createClient } from "graphql-ws";
8440
8916
  import { HttpsProxyAgent } from "https-proxy-agent";
8441
8917
  import WebSocket from "ws";
8918
+ var DEFAULT_API_URL = "https://api.mobb.ai/v1/graphql";
8442
8919
  var debug11 = Debug11("mobbdev:subscribe");
8443
8920
  var SUBSCRIPTION_TIMEOUT_MS = 30 * 60 * 1e3;
8444
8921
  function createWSClient(options) {
@@ -8470,10 +8947,11 @@ function subscribe(query, variables, callback, wsClientOptions) {
8470
8947
  return new Promise((resolve, reject) => {
8471
8948
  let timer = null;
8472
8949
  const { timeoutInMs = SUBSCRIPTION_TIMEOUT_MS } = wsClientOptions;
8950
+ const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
8473
8951
  const client = createWSClient({
8474
8952
  ...wsClientOptions,
8475
8953
  websocket: WebSocket,
8476
- url: API_URL.replace("http", "ws")
8954
+ url: API_URL2.replace("http", "ws")
8477
8955
  });
8478
8956
  const unsubscribe = client.subscribe(
8479
8957
  { query, variables },
@@ -8936,13 +9414,13 @@ var GQLClient = class {
8936
9414
  };
8937
9415
 
8938
9416
  // src/features/analysis/pack.ts
8939
- import fs5 from "fs";
8940
- import path5 from "path";
9417
+ import fs6 from "fs";
9418
+ import path7 from "path";
8941
9419
  import AdmZip from "adm-zip";
8942
9420
  import Debug13 from "debug";
8943
9421
  import { globby } from "globby";
8944
- import { isBinary } from "istextorbinary";
8945
- import { simpleGit as simpleGit3 } from "simple-git";
9422
+ import { isBinary as isBinary2 } from "istextorbinary";
9423
+ import { simpleGit as simpleGit2 } from "simple-git";
8946
9424
  import { parseStringPromise } from "xml2js";
8947
9425
  import { z as z28 } from "zod";
8948
9426
  var debug13 = Debug13("mobbdev:pack");
@@ -8971,7 +9449,7 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
8971
9449
  debug13("pack folder %s", srcDirPath);
8972
9450
  let git = void 0;
8973
9451
  try {
8974
- git = simpleGit3({
9452
+ git = simpleGit2({
8975
9453
  baseDir: srcDirPath,
8976
9454
  maxConcurrentProcesses: 1,
8977
9455
  trimmed: true
@@ -9003,23 +9481,23 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
9003
9481
  const zip = new AdmZip();
9004
9482
  debug13("compressing files");
9005
9483
  for (const filepath of filepaths) {
9006
- const absFilepath = path5.join(srcDirPath, filepath.toString());
9484
+ const absFilepath = path7.join(srcDirPath, filepath.toString());
9007
9485
  if (!isIncludeAllFiles) {
9008
9486
  vulnFiles = vulnFiles.concat(getManifestFilesSuffixes());
9009
9487
  if (!endsWithAny(
9010
- absFilepath.toString().replaceAll(path5.win32.sep, path5.posix.sep),
9488
+ absFilepath.toString().replaceAll(path7.win32.sep, path7.posix.sep),
9011
9489
  vulnFiles
9012
9490
  )) {
9013
9491
  debug13("ignoring %s because it is not a vulnerability file", filepath);
9014
9492
  continue;
9015
9493
  }
9016
9494
  }
9017
- if (fs5.lstatSync(absFilepath).size > MAX_FILE_SIZE) {
9495
+ if (fs6.lstatSync(absFilepath).size > MAX_FILE_SIZE) {
9018
9496
  debug13("ignoring %s because the size is > 5MB", filepath);
9019
9497
  continue;
9020
9498
  }
9021
- const data = git ? await git.showBuffer([`HEAD:./${filepath}`]) : fs5.readFileSync(absFilepath);
9022
- if (isBinary(null, data)) {
9499
+ const data = git ? await git.showBuffer([`HEAD:./${filepath}`]) : fs6.readFileSync(absFilepath);
9500
+ if (isBinary2(null, data)) {
9023
9501
  debug13("ignoring %s because is seems to be a binary file", filepath);
9024
9502
  continue;
9025
9503
  }
@@ -9139,16 +9617,16 @@ function createSpawn({ args, processPath, name, cwd }, options) {
9139
9617
  return createChildProcess({ childProcess: child, name }, options);
9140
9618
  }
9141
9619
  function createChildProcess({ childProcess, name }, options) {
9142
- const debug21 = Debug14(`mobbdev:${name}`);
9620
+ const debug20 = Debug14(`mobbdev:${name}`);
9143
9621
  const { display } = options;
9144
9622
  return new Promise((resolve, reject) => {
9145
9623
  let out = "";
9146
9624
  const onData = (chunk) => {
9147
- debug21(`chunk received from ${name} std ${chunk}`);
9625
+ debug20(`chunk received from ${name} std ${chunk}`);
9148
9626
  out += chunk;
9149
9627
  };
9150
9628
  if (!childProcess?.stdout || !childProcess?.stderr) {
9151
- debug21(`unable to fork ${name}`);
9629
+ debug20(`unable to fork ${name}`);
9152
9630
  reject(new Error(`unable to fork ${name}`));
9153
9631
  }
9154
9632
  childProcess.stdout?.on("data", onData);
@@ -9158,11 +9636,11 @@ function createChildProcess({ childProcess, name }, options) {
9158
9636
  childProcess.stderr?.pipe(process2.stderr);
9159
9637
  }
9160
9638
  childProcess.on("exit", (code) => {
9161
- debug21(`${name} exit code ${code}`);
9639
+ debug20(`${name} exit code ${code}`);
9162
9640
  resolve({ message: out, code });
9163
9641
  });
9164
9642
  childProcess.on("error", (err) => {
9165
- debug21(`${name} error %o`, err);
9643
+ debug20(`${name} error %o`, err);
9166
9644
  reject(err);
9167
9645
  });
9168
9646
  });
@@ -9174,12 +9652,12 @@ import Debug15 from "debug";
9174
9652
  import { existsSync } from "fs";
9175
9653
  import { createSpinner as createSpinner2 } from "nanospinner";
9176
9654
  import { type } from "os";
9177
- import path6 from "path";
9655
+ import path8 from "path";
9178
9656
  var debug14 = Debug15("mobbdev:checkmarx");
9179
9657
  var require2 = createRequire(import.meta.url);
9180
9658
  var getCheckmarxPath = () => {
9181
- const os2 = type();
9182
- const cxFileName = os2 === "Windows_NT" ? "cx.exe" : "cx";
9659
+ const os3 = type();
9660
+ const cxFileName = os3 === "Windows_NT" ? "cx.exe" : "cx";
9183
9661
  try {
9184
9662
  return require2.resolve(`.bin/${cxFileName}`);
9185
9663
  } catch (e) {
@@ -9234,9 +9712,9 @@ async function getCheckmarxReport({ reportPath, repositoryRoot, branch, projectN
9234
9712
  await startCheckmarxConfigationPrompt();
9235
9713
  await validateCheckamxCredentials();
9236
9714
  }
9237
- const extension = path6.extname(reportPath);
9238
- const filePath = path6.dirname(reportPath);
9239
- const fileName = path6.basename(reportPath, extension);
9715
+ const extension = path8.extname(reportPath);
9716
+ const filePath = path8.dirname(reportPath);
9717
+ const fileName = path8.basename(reportPath, extension);
9240
9718
  const checkmarxCommandArgs = getCheckmarxCommandArgs({
9241
9719
  repoPath: repositoryRoot,
9242
9720
  branch,
@@ -9305,8 +9783,8 @@ async function forkSnyk(args, { display }) {
9305
9783
  }
9306
9784
  async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
9307
9785
  debug15("get snyk report start %s %s", reportPath, repoRoot);
9308
- const config4 = await forkSnyk(["config"], { display: false });
9309
- const { message: configMessage } = config4;
9786
+ const config5 = await forkSnyk(["config"], { display: false });
9787
+ const { message: configMessage } = config5;
9310
9788
  if (!configMessage.includes("api: ")) {
9311
9789
  const snykLoginSpinner = createSpinner3().start();
9312
9790
  if (!skipPrompts) {
@@ -9318,7 +9796,7 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
9318
9796
  snykLoginSpinner.update({
9319
9797
  text: "\u{1F513} Waiting for Snyk login to complete"
9320
9798
  });
9321
- debug15("no token in the config %s", config4);
9799
+ debug15("no token in the config %s", config5);
9322
9800
  await forkSnyk(["auth"], { display: true });
9323
9801
  snykLoginSpinner.success({ text: "\u{1F513} Login to Snyk Successful" });
9324
9802
  }
@@ -9355,8 +9833,14 @@ async function uploadFile({
9355
9833
  file,
9356
9834
  url,
9357
9835
  uploadKey,
9358
- uploadFields
9836
+ uploadFields,
9837
+ logger: logger2
9359
9838
  }) {
9839
+ const logInfo2 = logger2 || ((_message, _data) => {
9840
+ });
9841
+ logInfo2(`FileUpload: upload file start ${url}`);
9842
+ logInfo2(`FileUpload: upload fields`, uploadFields);
9843
+ logInfo2(`FileUpload: upload key ${uploadKey}`);
9360
9844
  debug16("upload file start %s", url);
9361
9845
  debug16("upload fields %o", uploadFields);
9362
9846
  debug16("upload key %s", uploadKey);
@@ -9369,9 +9853,11 @@ async function uploadFile({
9369
9853
  }
9370
9854
  if (typeof file === "string") {
9371
9855
  debug16("upload file from path %s", file);
9856
+ logInfo2(`FileUpload: upload file from path ${file}`);
9372
9857
  form.append("file", await fileFrom(file));
9373
9858
  } else {
9374
9859
  debug16("upload file from buffer");
9860
+ logInfo2(`FileUpload: upload file from buffer`);
9375
9861
  form.append("file", new File([file], "file"));
9376
9862
  }
9377
9863
  const agent = getProxyAgent(url);
@@ -9382,9 +9868,11 @@ async function uploadFile({
9382
9868
  });
9383
9869
  if (!response.ok) {
9384
9870
  debug16("error from S3 %s %s", response.body, response.status);
9871
+ logInfo2(`FileUpload: error from S3 ${response.body} ${response.status}`);
9385
9872
  throw new Error(`Failed to upload the file: ${response.status}`);
9386
9873
  }
9387
9874
  debug16("upload file done");
9875
+ logInfo2(`FileUpload: upload file done`);
9388
9876
  }
9389
9877
 
9390
9878
  // src/features/analysis/index.ts
@@ -9419,7 +9907,7 @@ async function downloadRepo({
9419
9907
  const { createSpinner: createSpinner5 } = Spinner2({ ci });
9420
9908
  const repoSpinner = createSpinner5("\u{1F4BE} Downloading Repo").start();
9421
9909
  debug17("download repo %s %s %s", repoUrl, dirname);
9422
- const zipFilePath = path7.join(dirname, "repo.zip");
9910
+ const zipFilePath = path9.join(dirname, "repo.zip");
9423
9911
  debug17("download URL: %s auth headers: %o", downloadUrl, authHeaders);
9424
9912
  const response = await fetch4(downloadUrl, {
9425
9913
  method: "GET",
@@ -9432,19 +9920,19 @@ async function downloadRepo({
9432
9920
  repoSpinner.error({ text: "\u{1F4BE} Repo download failed" });
9433
9921
  throw new Error(`Can't access ${chalk5.bold(repoUrl)}`);
9434
9922
  }
9435
- const fileWriterStream = fs6.createWriteStream(zipFilePath);
9923
+ const fileWriterStream = fs7.createWriteStream(zipFilePath);
9436
9924
  if (!response.body) {
9437
9925
  throw new Error("Response body is empty");
9438
9926
  }
9439
9927
  await pipeline(response.body, fileWriterStream);
9440
9928
  await extract(zipFilePath, { dir: dirname });
9441
- const repoRoot = fs6.readdirSync(dirname, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name)[0];
9929
+ const repoRoot = fs7.readdirSync(dirname, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name)[0];
9442
9930
  if (!repoRoot) {
9443
9931
  throw new Error("Repo root not found");
9444
9932
  }
9445
9933
  debug17("repo root %s", repoRoot);
9446
9934
  repoSpinner.success({ text: "\u{1F4BE} Repo downloaded successfully" });
9447
- return path7.join(dirname, repoRoot);
9935
+ return path9.join(dirname, repoRoot);
9448
9936
  }
9449
9937
  var getReportUrl = ({
9450
9938
  organizationId,
@@ -9554,7 +10042,7 @@ async function getReport(params, { skipPrompts }) {
9554
10042
  authHeaders: scm.getAuthHeaders(),
9555
10043
  downloadUrl
9556
10044
  });
9557
- const reportPath = path7.join(dirname, REPORT_DEFAULT_FILE_NAME);
10045
+ const reportPath = path9.join(dirname, REPORT_DEFAULT_FILE_NAME);
9558
10046
  switch (scanner) {
9559
10047
  case "snyk":
9560
10048
  await getSnykReport(reportPath, repositoryRoot, { skipPrompts });
@@ -9930,7 +10418,7 @@ async function _zipAndUploadRepo({
9930
10418
  const zippingSpinner = createSpinner4("\u{1F4E6} Zipping repo").start();
9931
10419
  let zipBuffer;
9932
10420
  let gitInfo = { success: false };
9933
- if (srcFileStatus.isFile() && path7.extname(srcPath).toLowerCase() === ".fpr") {
10421
+ if (srcFileStatus.isFile() && path9.extname(srcPath).toLowerCase() === ".fpr") {
9934
10422
  zipBuffer = await repackFpr(srcPath);
9935
10423
  } else {
9936
10424
  gitInfo = await getGitInfo(srcPath);
@@ -10277,7 +10765,7 @@ import chalk8 from "chalk";
10277
10765
 
10278
10766
  // src/args/validation.ts
10279
10767
  import chalk7 from "chalk";
10280
- import path8 from "path";
10768
+ import path10 from "path";
10281
10769
  import { z as z30 } from "zod";
10282
10770
  function throwRepoUrlErrorMessage({
10283
10771
  error,
@@ -10321,7 +10809,7 @@ function validateRepoUrl(args) {
10321
10809
  }
10322
10810
  var supportExtensions = [".json", ".xml", ".fpr", ".sarif"];
10323
10811
  function validateReportFileFormat(reportFile) {
10324
- if (!supportExtensions.includes(path8.extname(reportFile))) {
10812
+ if (!supportExtensions.includes(path10.extname(reportFile))) {
10325
10813
  throw new CliError(
10326
10814
  `
10327
10815
  ${chalk7.bold(
@@ -10363,7 +10851,7 @@ function analyzeBuilder(yargs2) {
10363
10851
  ).help();
10364
10852
  }
10365
10853
  function validateAnalyzeOptions(argv) {
10366
- if (argv.f && !fs7.existsSync(argv.f)) {
10854
+ if (argv.f && !fs8.existsSync(argv.f)) {
10367
10855
  throw new CliError(`
10368
10856
  Can't access ${chalk8.bold(argv.f)}`);
10369
10857
  }
@@ -10416,6 +10904,7 @@ import {
10416
10904
 
10417
10905
  // src/mcp/Logger.ts
10418
10906
  var logglerUrl = "http://localhost:4444/log";
10907
+ var isTestEnvironment = process.env["VITEST"] || process.env["TEST"];
10419
10908
  var Logger = class {
10420
10909
  log(message, level = "info", data) {
10421
10910
  const logMessage = {
@@ -10424,13 +10913,15 @@ var Logger = class {
10424
10913
  message,
10425
10914
  data
10426
10915
  };
10427
- try {
10428
- fetch(logglerUrl, {
10429
- method: "POST",
10430
- headers: { "Content-Type": "application/json" },
10431
- body: JSON.stringify(logMessage)
10432
- });
10433
- } catch (error) {
10916
+ if (!isTestEnvironment) {
10917
+ try {
10918
+ fetch(logglerUrl, {
10919
+ method: "POST",
10920
+ headers: { "Content-Type": "application/json" },
10921
+ body: JSON.stringify(logMessage)
10922
+ });
10923
+ } catch (error) {
10924
+ }
10434
10925
  }
10435
10926
  }
10436
10927
  };
@@ -10439,775 +10930,150 @@ var logInfo = (message, data) => logger.log(message, "info", data);
10439
10930
  var logError = (message, data) => logger.log(message, "error", data);
10440
10931
  var logWarn = (message, data) => logger.log(message, "warn", data);
10441
10932
  var logDebug = (message, data) => logger.log(message, "debug", data);
10442
- var Logger_default = logger.log;
10933
+ var log = logger.log;
10443
10934
 
10444
- // src/mcp/core/ToolRegistry.ts
10445
- var ToolRegistry = class {
10446
- constructor() {
10447
- __publicField(this, "tools", /* @__PURE__ */ new Map());
10935
+ // src/mcp/services/McpGQLClient.ts
10936
+ import crypto2 from "crypto";
10937
+ import os2 from "os";
10938
+ import Configstore3 from "configstore";
10939
+ import { GraphQLClient as GraphQLClient2 } from "graphql-request";
10940
+ import open4 from "open";
10941
+ import { v4 as uuidv42 } from "uuid";
10942
+
10943
+ // src/mcp/constants.ts
10944
+ var DEFAULT_API_URL2 = "https://api.mobb.ai/v1/graphql";
10945
+ var API_KEY_HEADER_NAME2 = "x-mobb-key";
10946
+
10947
+ // src/mcp/tools/fixVulnerabilities/errors/VulnerabilityFixErrors.ts
10948
+ var ApiConnectionError = class extends Error {
10949
+ constructor(message = "Failed to connect to the API") {
10950
+ super(message);
10951
+ this.name = "ApiConnectionError";
10448
10952
  }
10449
- registerTool(tool) {
10450
- if (this.tools.has(tool.name)) {
10451
- logWarn(`Tool ${tool.name} is already registered, overwriting`, {
10452
- toolName: tool.name
10453
- });
10454
- }
10455
- this.tools.set(tool.name, tool);
10456
- logDebug(`Tool registered: ${tool.name}`, {
10457
- toolName: tool.name,
10458
- description: tool.definition.description
10459
- });
10953
+ };
10954
+ var CliLoginError = class extends Error {
10955
+ constructor(message = "CLI login failed") {
10956
+ super(message);
10957
+ this.name = "CliLoginError";
10460
10958
  }
10461
- getTool(name) {
10462
- return this.tools.get(name);
10959
+ };
10960
+ var AuthenticationError = class extends Error {
10961
+ constructor(message = "Authentication failed") {
10962
+ super(message);
10963
+ this.name = "AuthenticationError";
10463
10964
  }
10464
- getAllTools() {
10465
- return Array.from(this.tools.values()).map((tool) => tool.definition);
10965
+ };
10966
+ var NoFilesError = class extends Error {
10967
+ constructor(message = "No files to fix") {
10968
+ super(message);
10969
+ this.name = "NoFilesError";
10466
10970
  }
10467
- getToolNames() {
10468
- return Array.from(this.tools.keys());
10971
+ };
10972
+ var GqlClientError = class extends Error {
10973
+ constructor(message = "GraphQL client not initialized") {
10974
+ super(message);
10975
+ this.name = "GqlClientError";
10469
10976
  }
10470
- hasTool(name) {
10471
- return this.tools.has(name);
10977
+ };
10978
+ var FileProcessingError = class extends Error {
10979
+ constructor(message) {
10980
+ super(message);
10981
+ this.name = "FileProcessingError";
10472
10982
  }
10473
- getToolCount() {
10474
- return this.tools.size;
10983
+ };
10984
+ var ReportInitializationError = class extends Error {
10985
+ constructor(message) {
10986
+ super(message);
10987
+ this.name = "ReportInitializationError";
10475
10988
  }
10476
10989
  };
10477
-
10478
- // src/mcp/core/McpServer.ts
10479
- var McpServer = class {
10480
- constructor(config4) {
10481
- __publicField(this, "server");
10482
- __publicField(this, "toolRegistry");
10483
- __publicField(this, "isEventHandlersSetup", false);
10484
- this.server = new Server(
10485
- {
10486
- name: config4.name,
10487
- version: config4.version
10488
- },
10489
- {
10490
- capabilities: {
10491
- tools: {}
10492
- }
10493
- }
10494
- );
10495
- this.toolRegistry = new ToolRegistry();
10496
- this.setupHandlers();
10497
- this.setupProcessEventHandlers();
10498
- logInfo("MCP server instance created", config4);
10990
+ var FileUploadError = class extends Error {
10991
+ constructor(message) {
10992
+ super(message);
10993
+ this.name = "FileUploadError";
10499
10994
  }
10500
- setupProcessEventHandlers() {
10501
- if (this.isEventHandlersSetup) {
10502
- logDebug("Process event handlers already setup, skipping");
10503
- return;
10504
- }
10505
- const signals = {
10506
- SIGINT: "MCP server interrupted",
10507
- SIGTERM: "MCP server terminated",
10508
- exit: "MCP server exiting",
10509
- uncaughtException: "Uncaught exception in MCP server",
10510
- unhandledRejection: "Unhandled promise rejection in MCP server",
10511
- warning: "Warning in MCP server"
10512
- };
10513
- Object.entries(signals).forEach(([signal, message]) => {
10514
- process.on(
10515
- signal,
10516
- (error) => {
10517
- if (error && signal !== "exit") {
10518
- logError(`${message}`, { error, signal });
10519
- } else {
10520
- logInfo(message, { signal });
10521
- }
10522
- if (signal === "SIGINT" || signal === "SIGTERM") {
10523
- process.exit(0);
10524
- }
10525
- if (signal === "uncaughtException") {
10526
- process.exit(1);
10527
- }
10528
- }
10529
- );
10530
- });
10531
- this.isEventHandlersSetup = true;
10532
- logDebug("Process event handlers registered");
10995
+ };
10996
+ var ScanError = class extends Error {
10997
+ constructor(message) {
10998
+ super(message);
10999
+ this.name = "ScanError";
10533
11000
  }
10534
- createShutdownPromise() {
10535
- return new Promise((resolve) => {
10536
- const cleanup = () => {
10537
- logInfo("Process shutdown initiated");
10538
- resolve();
10539
- };
10540
- process.once("SIGINT", cleanup);
10541
- process.once("SIGTERM", cleanup);
10542
- });
11001
+ };
11002
+ var FailedToGetApiTokenError = class extends Error {
11003
+ constructor(message) {
11004
+ super(message);
11005
+ this.name = "FailedToGetApiTokenError";
10543
11006
  }
10544
- setupHandlers() {
10545
- this.server.setRequestHandler(
10546
- ListToolsRequestSchema,
10547
- async (request) => {
10548
- logInfo("Received list_tools request", { params: request.params });
10549
- const tools = this.toolRegistry.getAllTools();
10550
- const response = { tools };
10551
- logInfo("Returning list_tools response", {
10552
- toolCount: tools.length,
10553
- toolNames: tools.map((t) => t.name),
10554
- response
10555
- });
10556
- return response;
10557
- }
10558
- );
10559
- this.server.setRequestHandler(
10560
- CallToolRequestSchema,
10561
- async (request) => {
10562
- const { name, arguments: args } = request.params;
10563
- logInfo(`Received call tool request for ${name}`, { name, args });
10564
- try {
10565
- const tool = this.toolRegistry.getTool(name);
10566
- if (!tool) {
10567
- const errorMsg = `Unknown tool: ${name}`;
10568
- logWarn(errorMsg, {
10569
- name,
10570
- availableTools: this.toolRegistry.getToolNames()
10571
- });
10572
- throw new Error(errorMsg);
11007
+ };
11008
+
11009
+ // src/mcp/services/McpGQLClient.ts
11010
+ var LOGIN_MAX_WAIT2 = 10 * 1e3;
11011
+ var LOGIN_CHECK_DELAY2 = 1 * 1e3;
11012
+ var config4 = new Configstore3(packageJson.name, { apiToken: "" });
11013
+ var BROWSER_COOLDOWN_MS = 5e3;
11014
+ var lastBrowserOpenTime = 0;
11015
+ var McpGQLClient = class {
11016
+ constructor(args) {
11017
+ __publicField(this, "client");
11018
+ __publicField(this, "clientSdk");
11019
+ __publicField(this, "_auth");
11020
+ this._auth = args;
11021
+ const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL2;
11022
+ this.client = new GraphQLClient2(API_URL2, {
11023
+ headers: args.type === "apiKey" ? { [API_KEY_HEADER_NAME2]: args.apiKey || "" } : {
11024
+ Authorization: `Bearer ${args.token}`
11025
+ },
11026
+ requestMiddleware: (request) => {
11027
+ const requestId = uuidv42();
11028
+ return {
11029
+ ...request,
11030
+ headers: {
11031
+ ...request.headers,
11032
+ "x-hasura-request-id": requestId
10573
11033
  }
10574
- logDebug(`Executing tool: ${name}`, { args });
10575
- const response = await tool.execute(args);
10576
- const serializedResponse = JSON.parse(JSON.stringify(response));
10577
- logInfo(`Tool ${name} executed successfully`, {
10578
- responseType: typeof response,
10579
- hasContent: !!serializedResponse.content
10580
- });
10581
- return serializedResponse;
10582
- } catch (error) {
10583
- const errorMessage = error instanceof Error ? error.message : String(error);
10584
- logError(`Error executing tool ${name}: ${errorMessage}`, {
10585
- error,
10586
- toolName: name,
10587
- args
10588
- });
10589
- throw error;
10590
- }
11034
+ };
10591
11035
  }
10592
- );
10593
- logDebug("MCP server handlers registered");
10594
- }
10595
- registerTool(tool) {
10596
- this.toolRegistry.registerTool({
10597
- name: tool.name,
10598
- definition: tool.definition,
10599
- execute: tool.execute
10600
11036
  });
10601
- logDebug(`Tool registered: ${tool.name}`);
10602
- }
10603
- async start() {
10604
- try {
10605
- logDebug("Starting MCP server");
10606
- const transport = new StdioServerTransport();
10607
- await this.server.connect(transport);
10608
- logInfo("MCP server is running on stdin/stdout");
10609
- process.stdin.resume();
10610
- await this.createShutdownPromise();
10611
- await this.stop();
10612
- } catch (error) {
10613
- logError("Failed to start MCP server", { error });
10614
- throw error;
10615
- }
10616
- }
10617
- async stop() {
10618
- logInfo("MCP server shutting down");
11037
+ this.clientSdk = getSdk(this.client);
10619
11038
  }
10620
- };
10621
-
10622
- // src/mcp/services/GitService.ts
10623
- import * as path9 from "path";
10624
- import { simpleGit as simpleGit4 } from "simple-git";
10625
- var GitService = class {
10626
- constructor(repositoryPath) {
10627
- __publicField(this, "git");
10628
- __publicField(this, "repositoryPath");
10629
- this.git = simpleGit4(repositoryPath, { binary: "git" });
10630
- this.repositoryPath = repositoryPath;
10631
- logDebug("Git service initialized", { repositoryPath });
11039
+ getErrorContext() {
11040
+ return {
11041
+ endpoint: process.env["API_URL"] || DEFAULT_API_URL2,
11042
+ apiKey: this._auth.type === "apiKey" ? this._auth.apiKey : "",
11043
+ headers: {
11044
+ [API_KEY_HEADER_NAME2]: this._auth.type === "apiKey" ? "[REDACTED]" : "undefined",
11045
+ "x-hasura-request-id": "[DYNAMIC]"
11046
+ }
11047
+ };
10632
11048
  }
10633
- /**
10634
- * Validates that the path is a valid git repository
10635
- */
10636
- async validateRepository() {
10637
- logDebug("Validating git repository");
11049
+ async verifyConnection() {
10638
11050
  try {
10639
- const isRepo = await this.git.checkIsRepo();
10640
- if (!isRepo) {
10641
- const error = "Path is not a valid git repository";
10642
- logError(error);
10643
- return { isValid: false, error };
11051
+ logDebug("GraphQL: Calling Me query for connection verification");
11052
+ const result = await this.clientSdk.Me();
11053
+ logInfo("GraphQL: Me query successful", { result });
11054
+ return true;
11055
+ } catch (e) {
11056
+ if (e?.toString().includes("FetchError")) {
11057
+ logError("verify connection failed %o", e);
11058
+ return false;
10644
11059
  }
10645
- logDebug("Git repository validation successful");
10646
- return { isValid: true };
10647
- } catch (error) {
10648
- const errorMessage = `Failed to verify git repository: ${error.message}`;
10649
- logError(errorMessage, { error });
10650
- return { isValid: false, error: errorMessage };
10651
11060
  }
11061
+ return true;
10652
11062
  }
10653
- /**
10654
- * Gets the current git status and returns changed files
10655
- */
10656
- async getChangedFiles() {
10657
- logDebug("Getting git status");
11063
+ async uploadS3BucketInfo() {
10658
11064
  try {
10659
- const status = await this.git.status();
10660
- const gitRoot = await this.git.revparse(["--show-toplevel"]);
10661
- const relativePathFromGitRoot = path9.relative(
10662
- gitRoot,
10663
- this.repositoryPath
10664
- );
10665
- const files = status.files.map((file) => {
10666
- const gitRelativePath = file.path;
10667
- if (relativePathFromGitRoot === "") {
10668
- return gitRelativePath;
10669
- }
10670
- if (gitRelativePath.startsWith(relativePathFromGitRoot + "/")) {
10671
- return gitRelativePath.substring(relativePathFromGitRoot.length + 1);
10672
- }
10673
- return path9.relative(
10674
- this.repositoryPath,
10675
- path9.join(gitRoot, gitRelativePath)
10676
- );
11065
+ logDebug("GraphQL: Calling uploadS3BucketInfo mutation");
11066
+ const result = await this.clientSdk.uploadS3BucketInfo({
11067
+ fileName: "report.json"
10677
11068
  });
10678
- logInfo("Git status retrieved", {
10679
- fileCount: files.length,
10680
- files: files.slice(0, 10),
10681
- // Log first 10 files to avoid spam
10682
- gitRoot,
10683
- workingDir: this.repositoryPath,
10684
- relativePathFromGitRoot
11069
+ logInfo("GraphQL: uploadS3BucketInfo successful", { result });
11070
+ return result;
11071
+ } catch (e) {
11072
+ logError("GraphQL: uploadS3BucketInfo failed", {
11073
+ error: e,
11074
+ ...this.getErrorContext()
10685
11075
  });
10686
- return { files, status };
10687
- } catch (error) {
10688
- const errorMessage = `Failed to get git status: ${error.message}`;
10689
- logError(errorMessage, { error });
10690
- throw new Error(errorMessage);
10691
- }
10692
- }
10693
- };
10694
-
10695
- // src/mcp/services/PathValidation.ts
10696
- import fs8 from "fs";
10697
- import path10 from "path";
10698
- var PathValidation = class {
10699
- /**
10700
- * Validates a path for MCP usage - combines security and existence checks
10701
- */
10702
- async validatePath(inputPath) {
10703
- logDebug("Validating MCP path", { inputPath });
10704
- if (inputPath.includes("..")) {
10705
- const error = `Path contains path traversal patterns: ${inputPath}`;
10706
- logError(error);
10707
- return { isValid: false, error };
10708
- }
10709
- const normalizedPath = path10.normalize(inputPath);
10710
- if (normalizedPath.includes("..")) {
10711
- const error = `Normalized path contains path traversal patterns: ${inputPath}`;
10712
- logError(error);
10713
- return { isValid: false, error };
10714
- }
10715
- const decodedPath = decodeURIComponent(inputPath);
10716
- if (decodedPath.includes("..") || decodedPath !== inputPath) {
10717
- const error = `Path contains encoded traversal attempts: ${inputPath}`;
10718
- logError(error);
10719
- return { isValid: false, error };
10720
- }
10721
- if (inputPath.includes("\0") || inputPath.includes("\0")) {
10722
- const error = `Path contains dangerous characters: ${inputPath}`;
10723
- logError(error);
10724
- return { isValid: false, error };
10725
- }
10726
- logDebug("Path validation successful", { inputPath });
10727
- logDebug("Checking path existence", { inputPath });
10728
- try {
10729
- await fs8.promises.access(inputPath);
10730
- logDebug("Path exists and is accessible", { inputPath });
10731
- return { isValid: true };
10732
- } catch (error) {
10733
- const errorMessage = `Path does not exist or is not accessible: ${inputPath}`;
10734
- logError(errorMessage, { error });
10735
- return { isValid: false, error: errorMessage };
10736
- }
10737
- }
10738
- };
10739
-
10740
- // src/mcp/services/FilePacking.ts
10741
- import fs9 from "fs";
10742
- import path11 from "path";
10743
- import AdmZip2 from "adm-zip";
10744
- import { isBinary as isBinary2 } from "istextorbinary";
10745
- var MAX_FILE_SIZE2 = 1024 * 1024 * 5;
10746
- var EXCLUDED_FILE_PATTERNS = [
10747
- // Configuration files
10748
- ".json",
10749
- ".yaml",
10750
- ".yml",
10751
- ".toml",
10752
- ".ini",
10753
- ".conf",
10754
- ".config",
10755
- ".xml",
10756
- ".env",
10757
- // Documentation
10758
- ".md",
10759
- ".txt",
10760
- ".rst",
10761
- ".adoc",
10762
- // Lock/dependency files
10763
- ".lock",
10764
- // Images and media
10765
- ".png",
10766
- ".jpg",
10767
- ".jpeg",
10768
- ".gif",
10769
- ".svg",
10770
- ".ico",
10771
- ".webp",
10772
- ".bmp",
10773
- ".tiff",
10774
- // Fonts
10775
- ".ttf",
10776
- ".otf",
10777
- ".woff",
10778
- ".woff2",
10779
- ".eot",
10780
- // Archives
10781
- ".zip",
10782
- ".tar",
10783
- ".gz",
10784
- ".rar",
10785
- ".7z",
10786
- // Logs and databases
10787
- ".log",
10788
- ".db",
10789
- ".sqlite",
10790
- ".sql",
10791
- // Certificates and keys
10792
- ".pem",
10793
- ".crt",
10794
- ".key",
10795
- ".p12",
10796
- ".pfx",
10797
- // IDE/Editor files
10798
- ".editorconfig",
10799
- ".sublime-project",
10800
- ".sublime-workspace",
10801
- // System files
10802
- ".DS_Store",
10803
- "Thumbs.db",
10804
- // Coverage reports
10805
- ".lcov",
10806
- // Compiled/binary files
10807
- ".exe",
10808
- ".dll",
10809
- ".so",
10810
- ".dylib",
10811
- ".class",
10812
- ".pyc",
10813
- ".pyo",
10814
- ".o",
10815
- ".obj",
10816
- // Minified files
10817
- ".min.js",
10818
- ".min.css",
10819
- ".min.html",
10820
- // Test files
10821
- ".test.js",
10822
- ".test.ts",
10823
- ".test.jsx",
10824
- ".test.tsx",
10825
- ".spec.js",
10826
- ".spec.ts",
10827
- ".spec.jsx",
10828
- ".spec.tsx",
10829
- // TypeScript declaration files
10830
- ".d.ts",
10831
- // Build/generated files
10832
- ".bundle.js",
10833
- ".chunk.js",
10834
- // Build/CI files (exact filenames)
10835
- "dockerfile",
10836
- "jenkinsfile",
10837
- // Lock files (ones without standard extensions)
10838
- "go.sum",
10839
- // Version control
10840
- ".gitignore",
10841
- ".gitattributes",
10842
- ".gitmodules",
10843
- ".gitkeep",
10844
- ".keep",
10845
- ".hgignore",
10846
- // Node.js specific
10847
- ".nvmrc",
10848
- ".node-version",
10849
- ".npmrc",
10850
- ".yarnrc",
10851
- ".pnpmfile.cjs",
10852
- // Language version files
10853
- ".ruby-version",
10854
- ".python-version",
10855
- ".rvmrc",
10856
- ".rbenv-version",
10857
- ".gvmrc",
10858
- // Build tools and task runners
10859
- "makefile",
10860
- "rakefile",
10861
- "gulpfile.js",
10862
- "gruntfile.js",
10863
- "webpack.config.js",
10864
- "webpack.config.ts",
10865
- "rollup.config.js",
10866
- "vite.config.js",
10867
- "vite.config.ts",
10868
- "next.config.js",
10869
- "nuxt.config.js",
10870
- "tailwind.config.js",
10871
- "postcss.config.js",
10872
- // JavaScript/TypeScript config
10873
- ".babelrc",
10874
- ".babelrc.js",
10875
- ".swcrc",
10876
- ".browserslistrc",
10877
- // Testing frameworks
10878
- "jest.config.js",
10879
- "jest.config.ts",
10880
- "vitest.config.js",
10881
- "karma.conf.js",
10882
- "protractor.conf.js",
10883
- "cypress.config.js",
10884
- "playwright.config.js",
10885
- ".nycrc",
10886
- ".c8rc",
10887
- // Linting/formatting configs
10888
- ".eslintrc",
10889
- ".eslintrc.js",
10890
- ".prettierrc",
10891
- ".prettierrc.js",
10892
- ".stylelintrc",
10893
- ".stylelintrc.js",
10894
- // Package manager configs (ones without standard extensions)
10895
- "pipfile",
10896
- "gemfile",
10897
- "go.mod",
10898
- "project.clj",
10899
- // Python specific
10900
- "setup.py",
10901
- "setup.cfg",
10902
- "manifest.in",
10903
- ".pythonrc",
10904
- // Documentation files (ones without standard extensions)
10905
- "readme",
10906
- "changelog",
10907
- "authors",
10908
- "contributors",
10909
- // License and legal (ones without standard extensions)
10910
- "license",
10911
- "notice",
10912
- "copyright",
10913
- // Web specific
10914
- ".htaccess"
10915
- ];
10916
- var FilePacking = class {
10917
- isExcludedFileType(filepath) {
10918
- const basename = path11.basename(filepath).toLowerCase();
10919
- if (basename === ".env" || basename.startsWith(".env.")) {
10920
- return true;
10921
- }
10922
- if (EXCLUDED_FILE_PATTERNS.some((pattern) => basename.endsWith(pattern))) {
10923
- return true;
10924
- }
10925
- return false;
10926
- }
10927
- async packFiles(sourceDirectoryPath, filesToPack) {
10928
- logInfo(`FilePacking: packing files from ${sourceDirectoryPath}`);
10929
- const zip = new AdmZip2();
10930
- let packedFilesCount = 0;
10931
- logInfo("FilePacking: compressing files");
10932
- for (const filepath of filesToPack) {
10933
- const absoluteFilepath = path11.join(sourceDirectoryPath, filepath);
10934
- if (this.isExcludedFileType(filepath)) {
10935
- logInfo(
10936
- `FilePacking: ignoring ${filepath} because it is an excluded file type`
10937
- );
10938
- continue;
10939
- }
10940
- if (!fs9.existsSync(absoluteFilepath)) {
10941
- logInfo(`FilePacking: ignoring ${filepath} because it does not exist`);
10942
- continue;
10943
- }
10944
- if (fs9.lstatSync(absoluteFilepath).size > MAX_FILE_SIZE2) {
10945
- logInfo(
10946
- `FilePacking: ignoring ${filepath} because the size is > ${MAX_FILE_SIZE2 / (1024 * 1024)}MB`
10947
- );
10948
- continue;
10949
- }
10950
- let data;
10951
- try {
10952
- data = fs9.readFileSync(absoluteFilepath);
10953
- } catch (fsError) {
10954
- logInfo(
10955
- `FilePacking: failed to read ${filepath} from filesystem: ${fsError}`
10956
- );
10957
- continue;
10958
- }
10959
- if (isBinary2(null, data)) {
10960
- logInfo(
10961
- `FilePacking: ignoring ${filepath} because it seems to be a binary file`
10962
- );
10963
- continue;
10964
- }
10965
- zip.addFile(filepath, data);
10966
- packedFilesCount++;
10967
- }
10968
- const zipBuffer = zip.toBuffer();
10969
- logInfo(
10970
- `FilePacking: read ${packedFilesCount} source files. total size: ${zipBuffer.length} bytes`
10971
- );
10972
- logInfo("FilePacking: Files packed successfully");
10973
- return zipBuffer;
10974
- }
10975
- };
10976
-
10977
- // src/mcp/services/FileUpload.ts
10978
- import { HttpProxyAgent as HttpProxyAgent2 } from "http-proxy-agent";
10979
- import { HttpsProxyAgent as HttpsProxyAgent3 } from "https-proxy-agent";
10980
- var FileUpload = class {
10981
- getProxyAgent(url) {
10982
- const HTTPS_PROXY2 = process.env["HTTPS_PROXY"];
10983
- const HTTP_PROXY2 = process.env["HTTP_PROXY"];
10984
- try {
10985
- const parsedUrl = new URL(url);
10986
- const isHttp = parsedUrl.protocol === "http:";
10987
- const isHttps = parsedUrl.protocol === "https:";
10988
- const proxy = isHttps ? HTTPS_PROXY2 : isHttp ? HTTP_PROXY2 : null;
10989
- if (proxy) {
10990
- logInfo(`FileUpload: Using proxy ${proxy}`);
10991
- return isHttps ? new HttpsProxyAgent3(proxy) : new HttpProxyAgent2(proxy);
10992
- }
10993
- } catch (err) {
10994
- logInfo(
10995
- `FileUpload: Skipping proxy for ${url}. Reason: ${err.message}`
10996
- );
10997
- }
10998
- return void 0;
10999
- }
11000
- async uploadFile(options) {
11001
- const { file, url, uploadKey, uploadFields } = options;
11002
- logInfo(`FileUpload: upload file start ${url}`);
11003
- logInfo(`FileUpload: upload fields`, uploadFields);
11004
- logInfo(`FileUpload: upload key ${uploadKey}`);
11005
- const {
11006
- default: fetch5,
11007
- File: File2,
11008
- fileFrom: fileFrom2,
11009
- FormData: FormData2
11010
- } = await import("node-fetch");
11011
- const form = new FormData2();
11012
- Object.entries(uploadFields).forEach(([key, value]) => {
11013
- form.append(key, value);
11014
- });
11015
- if (!form.has("key")) {
11016
- form.append("key", uploadKey);
11017
- }
11018
- if (typeof file === "string") {
11019
- logInfo(`FileUpload: upload file from path ${file}`);
11020
- form.append("file", await fileFrom2(file));
11021
- } else {
11022
- logInfo(`FileUpload: upload file from buffer`);
11023
- form.append("file", new File2([file], "file"));
11024
- }
11025
- const agent = this.getProxyAgent(url);
11026
- const response = await fetch5(url, {
11027
- method: "POST",
11028
- body: form,
11029
- agent
11030
- });
11031
- if (!response.ok) {
11032
- logInfo(`FileUpload: error from S3 ${response.body} ${response.status}`);
11033
- throw new Error(`Failed to upload the file: ${response.status}`);
11034
- }
11035
- logInfo(`FileUpload: upload file done`);
11036
- }
11037
- };
11038
-
11039
- // src/mcp/services/McpGQLClient.ts
11040
- import { GraphQLClient as GraphQLClient2 } from "graphql-request";
11041
- import { v4 as uuidv42 } from "uuid";
11042
-
11043
- // src/mcp/constants.ts
11044
- var DEFAULT_API_URL = "https://api.mobb.ai/v1/graphql";
11045
- var API_KEY_HEADER_NAME2 = "x-mobb-key";
11046
-
11047
- // src/mcp/services/Subscribe.ts
11048
- import Debug20 from "debug";
11049
- import { createClient as createClient2 } from "graphql-ws";
11050
- import { HttpsProxyAgent as HttpsProxyAgent4 } from "https-proxy-agent";
11051
- import WebSocket2 from "ws";
11052
- var debug19 = Debug20("mobbdev:subscribe");
11053
- var SUBSCRIPTION_TIMEOUT_MS2 = 30 * 60 * 1e3;
11054
- function createWSClient2(options) {
11055
- const proxy = options.url.startsWith("wss://") && process.env["HTTPS_PROXY"] ? new HttpsProxyAgent4(process.env["HTTPS_PROXY"]) : options.url.startsWith("ws://") && process.env["HTTP_PROXY"] ? new HttpsProxyAgent4(process.env["HTTP_PROXY"]) : null;
11056
- debug19(
11057
- `Using proxy: ${proxy ? "yes" : "no"} with url: ${options.url} and with proxy: ${process.env["HTTP_PROXY"]} for the websocket connection`
11058
- );
11059
- const CustomWebSocket = class extends WebSocket2 {
11060
- constructor(address, protocols) {
11061
- super(address, protocols, proxy ? { agent: proxy } : void 0);
11062
- }
11063
- };
11064
- return createClient2({
11065
- //this is needed to prevent AWS from killing the connection
11066
- //currently our load balancer has a 29s idle timeout
11067
- keepAlive: 1e4,
11068
- url: options.url,
11069
- webSocketImpl: proxy ? CustomWebSocket : options.websocket || WebSocket2,
11070
- connectionParams: () => {
11071
- return {
11072
- headers: options.type === "apiKey" ? {
11073
- [API_KEY_HEADER_NAME2]: options.apiKey
11074
- } : { authorization: `Bearer ${options.token}` }
11075
- };
11076
- }
11077
- });
11078
- }
11079
- var Subscribe = class {
11080
- static subscribe(query, variables, callback, wsClientOptions) {
11081
- return new Promise((resolve, reject) => {
11082
- let timer = null;
11083
- const { timeoutInMs = SUBSCRIPTION_TIMEOUT_MS2 } = wsClientOptions;
11084
- const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
11085
- const client = createWSClient2({
11086
- ...wsClientOptions,
11087
- websocket: WebSocket2,
11088
- url: API_URL2.replace("http", "ws")
11089
- });
11090
- const unsubscribe = client.subscribe(
11091
- { query, variables },
11092
- {
11093
- next: (data) => {
11094
- function callbackResolve(data2) {
11095
- unsubscribe();
11096
- if (timer) {
11097
- clearTimeout(timer);
11098
- }
11099
- resolve(data2);
11100
- }
11101
- function callbackReject(data2) {
11102
- unsubscribe();
11103
- if (timer) {
11104
- clearTimeout(timer);
11105
- }
11106
- reject(data2);
11107
- }
11108
- if (!data.data) {
11109
- reject(
11110
- new Error(
11111
- `Broken data object from graphQL subscribe: ${JSON.stringify(
11112
- data
11113
- )} for query: ${query}`
11114
- )
11115
- );
11116
- } else {
11117
- callback(callbackResolve, callbackReject, data.data);
11118
- }
11119
- },
11120
- error: (error) => {
11121
- if (timer) {
11122
- clearTimeout(timer);
11123
- }
11124
- reject(error);
11125
- },
11126
- complete: () => {
11127
- return;
11128
- }
11129
- }
11130
- );
11131
- if (typeof timeoutInMs === "number") {
11132
- timer = setTimeout(() => {
11133
- unsubscribe();
11134
- reject(
11135
- new Error(
11136
- `Timeout expired for graphQL subscribe query: ${query} with timeout: ${timeoutInMs}`
11137
- )
11138
- );
11139
- }, timeoutInMs);
11140
- }
11141
- });
11142
- }
11143
- };
11144
- var subscribe2 = Subscribe.subscribe;
11145
-
11146
- // src/mcp/services/McpGQLClient.ts
11147
- var McpGQLClient = class {
11148
- constructor(args) {
11149
- __publicField(this, "client");
11150
- __publicField(this, "clientSdk");
11151
- __publicField(this, "apiKey");
11152
- __publicField(this, "apiUrl");
11153
- const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
11154
- this.apiKey = args.apiKey;
11155
- this.apiUrl = API_URL2;
11156
- this.client = new GraphQLClient2(API_URL2, {
11157
- headers: { [API_KEY_HEADER_NAME2]: args.apiKey || "" },
11158
- requestMiddleware: (request) => {
11159
- const requestId = uuidv42();
11160
- return {
11161
- ...request,
11162
- headers: {
11163
- ...request.headers,
11164
- "x-hasura-request-id": requestId
11165
- }
11166
- };
11167
- }
11168
- });
11169
- this.clientSdk = getSdk(this.client);
11170
- }
11171
- getErrorContext() {
11172
- return {
11173
- endpoint: this.apiUrl,
11174
- headers: {
11175
- [API_KEY_HEADER_NAME2]: this.apiKey ? "[REDACTED]" : "undefined",
11176
- "x-hasura-request-id": "[DYNAMIC]"
11177
- }
11178
- };
11179
- }
11180
- async verifyConnection() {
11181
- try {
11182
- logDebug("GraphQL: Calling Me query for connection verification");
11183
- const result = await this.clientSdk.Me();
11184
- logInfo("GraphQL: Me query successful", { result });
11185
- return true;
11186
- } catch (e) {
11187
- logError("GraphQL: Me query failed", {
11188
- error: e,
11189
- ...this.getErrorContext()
11190
- });
11191
- if (e?.toString().startsWith("FetchError")) {
11192
- console.error("Connection verification failed:", e);
11193
- }
11194
- return false;
11195
- }
11196
- }
11197
- async uploadS3BucketInfo() {
11198
- try {
11199
- logDebug("GraphQL: Calling uploadS3BucketInfo mutation");
11200
- const result = await this.clientSdk.uploadS3BucketInfo({
11201
- fileName: "report.json"
11202
- });
11203
- logInfo("GraphQL: uploadS3BucketInfo successful", { result });
11204
- return result;
11205
- } catch (e) {
11206
- logError("GraphQL: uploadS3BucketInfo failed", {
11207
- error: e,
11208
- ...this.getErrorContext()
11209
- });
11210
- throw e;
11076
+ throw e;
11211
11077
  }
11212
11078
  }
11213
11079
  async getAnalysis(analysisId) {
@@ -11253,7 +11119,7 @@ var McpGQLClient = class {
11253
11119
  params: params.subscribeToAnalysisParams
11254
11120
  });
11255
11121
  const { callbackStates } = params;
11256
- const result = await subscribe2(
11122
+ const result = await subscribe(
11257
11123
  GetAnalysisSubscriptionDocument,
11258
11124
  params.subscribeToAnalysisParams,
11259
11125
  async (resolve, reject, data) => {
@@ -11267,92 +11133,489 @@ var McpGQLClient = class {
11267
11133
  reject(new Error(`Analysis failed with id: ${data.analysis?.id}`));
11268
11134
  return;
11269
11135
  }
11270
- if (callbackStates.includes(data.analysis?.state)) {
11271
- logInfo("GraphQL: Analysis state matches callback states", {
11272
- analysisId: data.analysis.id,
11273
- state: data.analysis.state,
11274
- callbackStates
11275
- });
11276
- await params.callback(data.analysis.id);
11277
- resolve(data);
11136
+ if (callbackStates.includes(data.analysis?.state)) {
11137
+ logInfo("GraphQL: Analysis state matches callback states", {
11138
+ analysisId: data.analysis.id,
11139
+ state: data.analysis.state,
11140
+ callbackStates
11141
+ });
11142
+ await params.callback(data.analysis.id);
11143
+ resolve(data);
11144
+ }
11145
+ },
11146
+ this._auth.type === "apiKey" ? {
11147
+ apiKey: this._auth.apiKey,
11148
+ type: "apiKey",
11149
+ timeoutInMs: params.timeoutInMs
11150
+ } : {
11151
+ token: this._auth.token,
11152
+ type: "token",
11153
+ timeoutInMs: params.timeoutInMs
11154
+ }
11155
+ );
11156
+ logInfo("GraphQL: GetAnalysis subscription completed", { result });
11157
+ return result;
11158
+ } catch (e) {
11159
+ logError("GraphQL: GetAnalysis subscription failed", {
11160
+ error: e,
11161
+ params: params.subscribeToAnalysisParams,
11162
+ ...this.getErrorContext()
11163
+ });
11164
+ throw e;
11165
+ }
11166
+ }
11167
+ async getProjectId() {
11168
+ try {
11169
+ const projectName = "MCP Scans";
11170
+ logDebug("GraphQL: Calling getOrgAndProjectId query", { projectName });
11171
+ const getOrgAndProjectIdResult = await this.clientSdk.getOrgAndProjectId({
11172
+ filters: {},
11173
+ limit: 1
11174
+ });
11175
+ logInfo("GraphQL: getOrgAndProjectId successful", {
11176
+ result: getOrgAndProjectIdResult
11177
+ });
11178
+ const [organizationToOrganizationRole] = getOrgAndProjectIdResult.organization_to_organization_role;
11179
+ if (!organizationToOrganizationRole) {
11180
+ throw new Error("Organization not found");
11181
+ }
11182
+ const { organization: org } = organizationToOrganizationRole;
11183
+ const project = projectName ? org?.projects.find((project2) => project2.name === projectName) ?? null : org?.projects[0];
11184
+ if (project?.id) {
11185
+ logInfo("GraphQL: Found existing project", {
11186
+ projectId: project.id,
11187
+ projectName
11188
+ });
11189
+ return project.id;
11190
+ }
11191
+ logDebug("GraphQL: Project not found, creating new project", {
11192
+ organizationId: org.id,
11193
+ projectName
11194
+ });
11195
+ const createdProject = await this.clientSdk.CreateProject({
11196
+ organizationId: org.id,
11197
+ projectName
11198
+ });
11199
+ logInfo("GraphQL: CreateProject successful", { result: createdProject });
11200
+ return createdProject.createProject.projectId;
11201
+ } catch (e) {
11202
+ logError("GraphQL: getProjectId failed", {
11203
+ error: e,
11204
+ ...this.getErrorContext()
11205
+ });
11206
+ throw e;
11207
+ }
11208
+ }
11209
+ async getReportFixes(fixReportId) {
11210
+ try {
11211
+ logDebug("GraphQL: Calling GetMCPFixes query", { fixReportId });
11212
+ const res = await this.clientSdk.GetMCPFixes({ fixReportId });
11213
+ logInfo("GraphQL: GetMCPFixes successful", {
11214
+ result: res,
11215
+ fixCount: res.fix?.length || 0
11216
+ });
11217
+ return res.fix;
11218
+ } catch (e) {
11219
+ logError("GraphQL: GetMCPFixes failed", {
11220
+ error: e,
11221
+ fixReportId,
11222
+ ...this.getErrorContext()
11223
+ });
11224
+ throw e;
11225
+ }
11226
+ }
11227
+ async getUserInfo() {
11228
+ const { me } = await this.clientSdk.Me();
11229
+ return me;
11230
+ }
11231
+ async verifyToken() {
11232
+ logDebug("verifying token");
11233
+ try {
11234
+ await this.clientSdk.CreateCommunityUser();
11235
+ const info = await this.getUserInfo();
11236
+ logDebug("token verified");
11237
+ return info?.email || true;
11238
+ } catch (e) {
11239
+ logError("verify token failed");
11240
+ return false;
11241
+ }
11242
+ }
11243
+ async createCliLogin(variables) {
11244
+ try {
11245
+ const res = await this.clientSdk.CreateCliLogin(variables, {
11246
+ // We may have outdated API key in the config storage. Avoid using it for the login request.
11247
+ [API_KEY_HEADER_NAME2]: ""
11248
+ });
11249
+ const loginId = res.insert_cli_login_one?.id || "";
11250
+ if (!loginId) {
11251
+ logError("create cli login failed - no login ID returned");
11252
+ return "";
11253
+ }
11254
+ return loginId;
11255
+ } catch (e) {
11256
+ logError("create cli login failed", { error: e });
11257
+ return "";
11258
+ }
11259
+ }
11260
+ async getEncryptedApiToken(variables) {
11261
+ try {
11262
+ const res = await this.clientSdk.GetEncryptedApiToken(variables, {
11263
+ // We may have outdated API key in the config storage. Avoid using it for the login request.
11264
+ [API_KEY_HEADER_NAME2]: ""
11265
+ });
11266
+ return res?.cli_login_by_pk?.encryptedApiToken || null;
11267
+ } catch (e) {
11268
+ logError("get encrypted api token failed", { error: e });
11269
+ return null;
11270
+ }
11271
+ }
11272
+ };
11273
+ async function openBrowser(url) {
11274
+ const now = Date.now();
11275
+ if (!process.env["TEST"] && now - lastBrowserOpenTime < BROWSER_COOLDOWN_MS) {
11276
+ logDebug(`browser cooldown active, skipping open for ${url}`);
11277
+ return;
11278
+ }
11279
+ logDebug(`opening browser url ${url}`);
11280
+ await open4(url);
11281
+ lastBrowserOpenTime = now;
11282
+ }
11283
+ async function getMcpGQLClient() {
11284
+ logDebug("getting config", { apiToken: config4.get("apiToken") });
11285
+ const inGqlClient = new McpGQLClient({
11286
+ apiKey: config4.get("apiToken") || process.env["API_KEY"] || "",
11287
+ type: "apiKey"
11288
+ });
11289
+ const isConnected = await inGqlClient.verifyConnection();
11290
+ if (!isConnected) {
11291
+ throw new ApiConnectionError("Error: failed to connect to the API");
11292
+ }
11293
+ const userVerify = await inGqlClient.verifyToken();
11294
+ if (userVerify) {
11295
+ return inGqlClient;
11296
+ }
11297
+ const { publicKey, privateKey } = crypto2.generateKeyPairSync("rsa", {
11298
+ modulusLength: 2048
11299
+ });
11300
+ logDebug("creating cli login");
11301
+ const loginId = await inGqlClient.createCliLogin({
11302
+ publicKey: publicKey.export({ format: "pem", type: "pkcs1" }).toString()
11303
+ });
11304
+ if (!loginId) {
11305
+ throw new CliLoginError("Error: createCliLogin failed");
11306
+ }
11307
+ logDebug(`cli login created ${loginId}`);
11308
+ const webLoginUrl2 = `${WEB_APP_URL}/cli-login`;
11309
+ const browserUrl = `${webLoginUrl2}/${loginId}?hostname=${os2.hostname()}`;
11310
+ logDebug(`opening browser url ${browserUrl}`);
11311
+ await openBrowser(browserUrl);
11312
+ logDebug(`waiting for login to complete`);
11313
+ let newApiToken = null;
11314
+ for (let i = 0; i < LOGIN_MAX_WAIT2 / LOGIN_CHECK_DELAY2; i++) {
11315
+ const encryptedApiToken = await inGqlClient.getEncryptedApiToken({
11316
+ loginId
11317
+ });
11318
+ if (encryptedApiToken) {
11319
+ logDebug("encrypted API token received");
11320
+ newApiToken = crypto2.privateDecrypt(privateKey, Buffer.from(encryptedApiToken, "base64")).toString("utf-8");
11321
+ logDebug("API token decrypted");
11322
+ break;
11323
+ }
11324
+ await sleep(LOGIN_CHECK_DELAY2);
11325
+ }
11326
+ if (!newApiToken) {
11327
+ throw new FailedToGetApiTokenError(
11328
+ "Error: failed to get encrypted api token"
11329
+ );
11330
+ }
11331
+ const newGqlClient = new McpGQLClient({ apiKey: newApiToken, type: "apiKey" });
11332
+ const loginSuccess = await newGqlClient.verifyToken();
11333
+ if (loginSuccess) {
11334
+ logDebug("set api token %s", newApiToken);
11335
+ config4.set("apiToken", newApiToken);
11336
+ } else {
11337
+ throw new AuthenticationError("Something went wrong, API token is invalid.");
11338
+ }
11339
+ return newGqlClient;
11340
+ }
11341
+
11342
+ // src/mcp/core/ToolRegistry.ts
11343
+ var ToolRegistry = class {
11344
+ constructor() {
11345
+ __publicField(this, "tools", /* @__PURE__ */ new Map());
11346
+ }
11347
+ registerTool(tool) {
11348
+ if (this.tools.has(tool.name)) {
11349
+ logWarn(`Tool ${tool.name} is already registered, overwriting`, {
11350
+ toolName: tool.name
11351
+ });
11352
+ }
11353
+ this.tools.set(tool.name, tool);
11354
+ logDebug(`Tool registered: ${tool.name}`, {
11355
+ toolName: tool.name,
11356
+ description: tool.definition.description
11357
+ });
11358
+ }
11359
+ getTool(name) {
11360
+ return this.tools.get(name);
11361
+ }
11362
+ getAllTools() {
11363
+ return Array.from(this.tools.values()).map((tool) => tool.definition);
11364
+ }
11365
+ getToolNames() {
11366
+ return Array.from(this.tools.keys());
11367
+ }
11368
+ hasTool(name) {
11369
+ return this.tools.has(name);
11370
+ }
11371
+ getToolCount() {
11372
+ return this.tools.size;
11373
+ }
11374
+ };
11375
+
11376
+ // src/mcp/core/McpServer.ts
11377
+ var McpServer = class {
11378
+ constructor(config5) {
11379
+ __publicField(this, "server");
11380
+ __publicField(this, "toolRegistry");
11381
+ __publicField(this, "isEventHandlersSetup", false);
11382
+ this.server = new Server(
11383
+ {
11384
+ name: config5.name,
11385
+ version: config5.version
11386
+ },
11387
+ {
11388
+ capabilities: {
11389
+ tools: {}
11390
+ }
11391
+ }
11392
+ );
11393
+ this.toolRegistry = new ToolRegistry();
11394
+ this.setupHandlers();
11395
+ this.setupProcessEventHandlers();
11396
+ logInfo("MCP server instance created", config5);
11397
+ }
11398
+ setupProcessEventHandlers() {
11399
+ if (this.isEventHandlersSetup) {
11400
+ logDebug("Process event handlers already setup, skipping");
11401
+ return;
11402
+ }
11403
+ const signals = {
11404
+ SIGINT: "MCP server interrupted",
11405
+ SIGTERM: "MCP server terminated",
11406
+ exit: "MCP server exiting",
11407
+ uncaughtException: "Uncaught exception in MCP server",
11408
+ unhandledRejection: "Unhandled promise rejection in MCP server",
11409
+ warning: "Warning in MCP server"
11410
+ };
11411
+ Object.entries(signals).forEach(([signal, message]) => {
11412
+ process.on(
11413
+ signal,
11414
+ (error) => {
11415
+ if (error && signal !== "exit") {
11416
+ logError(`${message}`, { error, signal });
11417
+ } else {
11418
+ logInfo(message, { signal });
11419
+ }
11420
+ if (signal === "SIGINT" || signal === "SIGTERM") {
11421
+ process.exit(0);
11422
+ }
11423
+ if (signal === "uncaughtException") {
11424
+ process.exit(1);
11278
11425
  }
11279
- },
11280
- {
11281
- apiKey: this.apiKey,
11282
- type: "apiKey",
11283
- timeoutInMs: params.timeoutInMs
11284
11426
  }
11285
11427
  );
11286
- logInfo("GraphQL: GetAnalysis subscription completed", { result });
11287
- return result;
11288
- } catch (e) {
11289
- logError("GraphQL: GetAnalysis subscription failed", {
11290
- error: e,
11291
- params: params.subscribeToAnalysisParams,
11292
- ...this.getErrorContext()
11293
- });
11294
- throw e;
11428
+ });
11429
+ this.isEventHandlersSetup = true;
11430
+ logDebug("Process event handlers registered");
11431
+ }
11432
+ createShutdownPromise() {
11433
+ return new Promise((resolve) => {
11434
+ const cleanup = () => {
11435
+ logInfo("Process shutdown initiated");
11436
+ resolve();
11437
+ };
11438
+ process.once("SIGINT", cleanup);
11439
+ process.once("SIGTERM", cleanup);
11440
+ });
11441
+ }
11442
+ async handleListToolsRequest(request) {
11443
+ logInfo("Received list_tools request", { params: request.params });
11444
+ try {
11445
+ await getMcpGQLClient();
11446
+ } catch (error) {
11447
+ logError("Failed to get MCPGQLClient", { error });
11448
+ const authError = new Error(
11449
+ "Please authorize this client by visiting: https://mobb.ai"
11450
+ );
11451
+ authError.name = "AuthorizationRequired";
11452
+ throw authError;
11295
11453
  }
11454
+ const tools = this.toolRegistry.getAllTools();
11455
+ return {
11456
+ tools: tools.map((tool) => ({
11457
+ name: tool.name,
11458
+ display_name: tool.name,
11459
+ description: tool.description || "",
11460
+ inputSchema: {
11461
+ type: "object",
11462
+ properties: tool.inputSchema.properties || {},
11463
+ required: tool.inputSchema.required || []
11464
+ }
11465
+ }))
11466
+ };
11296
11467
  }
11297
- async getProjectId() {
11468
+ async handleCallToolRequest(request) {
11469
+ const { name, arguments: args } = request.params;
11470
+ logInfo(`Received call tool request for ${name}`, { name, args });
11298
11471
  try {
11299
- const projectName = "MCP Scans";
11300
- logDebug("GraphQL: Calling getOrgAndProjectId query", { projectName });
11301
- const getOrgAndProjectIdResult = await this.clientSdk.getOrgAndProjectId({
11302
- filters: {},
11303
- limit: 1
11304
- });
11305
- logInfo("GraphQL: getOrgAndProjectId successful", {
11306
- result: getOrgAndProjectIdResult
11307
- });
11308
- const [organizationToOrganizationRole] = getOrgAndProjectIdResult.organization_to_organization_role;
11309
- if (!organizationToOrganizationRole) {
11310
- throw new Error("Organization not found");
11311
- }
11312
- const { organization: org } = organizationToOrganizationRole;
11313
- const project = projectName ? org?.projects.find((project2) => project2.name === projectName) ?? null : org?.projects[0];
11314
- if (project?.id) {
11315
- logInfo("GraphQL: Found existing project", {
11316
- projectId: project.id,
11317
- projectName
11472
+ const tool = this.toolRegistry.getTool(name);
11473
+ if (!tool) {
11474
+ const errorMsg = `Unknown tool: ${name}`;
11475
+ logWarn(errorMsg, {
11476
+ name,
11477
+ availableTools: this.toolRegistry.getToolNames()
11318
11478
  });
11319
- return project.id;
11479
+ throw new Error(errorMsg);
11320
11480
  }
11321
- logDebug("GraphQL: Project not found, creating new project", {
11322
- organizationId: org.id,
11323
- projectName
11324
- });
11325
- const createdProject = await this.clientSdk.CreateProject({
11326
- organizationId: org.id,
11327
- projectName
11481
+ logDebug(`Executing tool: ${name}`, { args });
11482
+ const response = await tool.execute(args);
11483
+ const serializedResponse = JSON.parse(JSON.stringify(response));
11484
+ logInfo(`Tool ${name} executed successfully`, {
11485
+ responseType: typeof response,
11486
+ hasContent: !!serializedResponse.content
11328
11487
  });
11329
- logInfo("GraphQL: CreateProject successful", { result: createdProject });
11330
- return createdProject.createProject.projectId;
11331
- } catch (e) {
11332
- logError("GraphQL: getProjectId failed", {
11333
- error: e,
11334
- ...this.getErrorContext()
11488
+ return serializedResponse;
11489
+ } catch (error) {
11490
+ const errorMessage = error instanceof Error ? error.message : String(error);
11491
+ logError(`Error executing tool ${name}: ${errorMessage}`, {
11492
+ error,
11493
+ toolName: name,
11494
+ args
11335
11495
  });
11336
- throw e;
11496
+ throw error;
11337
11497
  }
11338
11498
  }
11339
- async getReportFixes(fixReportId) {
11499
+ setupHandlers() {
11500
+ this.server.setRequestHandler(
11501
+ ListToolsRequestSchema,
11502
+ (request) => this.handleListToolsRequest(request)
11503
+ );
11504
+ this.server.setRequestHandler(
11505
+ CallToolRequestSchema,
11506
+ (request) => this.handleCallToolRequest(request)
11507
+ );
11508
+ logDebug("MCP server handlers registered");
11509
+ }
11510
+ registerTool(tool) {
11511
+ this.toolRegistry.registerTool({
11512
+ name: tool.name,
11513
+ definition: tool.definition,
11514
+ execute: tool.execute
11515
+ });
11516
+ logDebug(`Tool registered: ${tool.name}`);
11517
+ }
11518
+ async start() {
11340
11519
  try {
11341
- logDebug("GraphQL: Calling GetMCPFixes query", { fixReportId });
11342
- const res = await this.clientSdk.GetMCPFixes({ fixReportId });
11343
- logInfo("GraphQL: GetMCPFixes successful", {
11344
- result: res,
11345
- fixCount: res.fix?.length || 0
11346
- });
11347
- return res.fix;
11348
- } catch (e) {
11349
- logError("GraphQL: GetMCPFixes failed", {
11350
- error: e,
11351
- fixReportId,
11352
- ...this.getErrorContext()
11353
- });
11354
- throw e;
11520
+ logDebug("Starting MCP server");
11521
+ const transport = new StdioServerTransport();
11522
+ await this.server.connect(transport);
11523
+ logInfo("MCP server is running on stdin/stdout");
11524
+ process.stdin.resume();
11525
+ await this.createShutdownPromise();
11526
+ await this.stop();
11527
+ } catch (error) {
11528
+ logError("Failed to start MCP server", { error });
11529
+ throw error;
11530
+ }
11531
+ }
11532
+ async stop() {
11533
+ logInfo("MCP server shutting down");
11534
+ }
11535
+ };
11536
+
11537
+ // src/mcp/services/PathValidation.ts
11538
+ import fs9 from "fs";
11539
+ import path11 from "path";
11540
+ var PathValidation = class {
11541
+ /**
11542
+ * Validates a path for MCP usage - combines security and existence checks
11543
+ */
11544
+ async validatePath(inputPath) {
11545
+ logDebug("Validating MCP path", { inputPath });
11546
+ if (inputPath.includes("..")) {
11547
+ const error = `Path contains path traversal patterns: ${inputPath}`;
11548
+ logError(error);
11549
+ return { isValid: false, error };
11550
+ }
11551
+ const normalizedPath = path11.normalize(inputPath);
11552
+ if (normalizedPath.includes("..")) {
11553
+ const error = `Normalized path contains path traversal patterns: ${inputPath}`;
11554
+ logError(error);
11555
+ return { isValid: false, error };
11556
+ }
11557
+ const decodedPath = decodeURIComponent(inputPath);
11558
+ if (decodedPath.includes("..") || decodedPath !== inputPath) {
11559
+ const error = `Path contains encoded traversal attempts: ${inputPath}`;
11560
+ logError(error);
11561
+ return { isValid: false, error };
11562
+ }
11563
+ if (inputPath.includes("\0") || inputPath.includes("\0")) {
11564
+ const error = `Path contains dangerous characters: ${inputPath}`;
11565
+ logError(error);
11566
+ return { isValid: false, error };
11567
+ }
11568
+ logDebug("Path validation successful", { inputPath });
11569
+ logDebug("Checking path existence", { inputPath });
11570
+ try {
11571
+ await fs9.promises.access(inputPath);
11572
+ logDebug("Path exists and is accessible", { inputPath });
11573
+ return { isValid: true };
11574
+ } catch (error) {
11575
+ const errorMessage = `Path does not exist or is not accessible: ${inputPath}`;
11576
+ logError(errorMessage, { error });
11577
+ return { isValid: false, error: errorMessage };
11578
+ }
11579
+ }
11580
+ };
11581
+
11582
+ // src/mcp/services/FilePacking.ts
11583
+ import fs10 from "fs";
11584
+ import path12 from "path";
11585
+ import AdmZip2 from "adm-zip";
11586
+ var MAX_FILE_SIZE2 = 1024 * 1024 * 5;
11587
+ var FilePacking = class {
11588
+ async packFiles(sourceDirectoryPath, filesToPack) {
11589
+ logInfo(`FilePacking: packing files from ${sourceDirectoryPath}`);
11590
+ const zip = new AdmZip2();
11591
+ let packedFilesCount = 0;
11592
+ logInfo("FilePacking: compressing files");
11593
+ for (const filepath of filesToPack) {
11594
+ const absoluteFilepath = path12.join(sourceDirectoryPath, filepath);
11595
+ if (!FileUtils.shouldPackFile(absoluteFilepath, MAX_FILE_SIZE2)) {
11596
+ logInfo(
11597
+ `FilePacking: ignoring ${filepath} because it is excluded or invalid`
11598
+ );
11599
+ continue;
11600
+ }
11601
+ let data;
11602
+ try {
11603
+ data = fs10.readFileSync(absoluteFilepath);
11604
+ } catch (fsError) {
11605
+ logInfo(
11606
+ `FilePacking: failed to read ${filepath} from filesystem: ${fsError}`
11607
+ );
11608
+ continue;
11609
+ }
11610
+ zip.addFile(filepath, data);
11611
+ packedFilesCount++;
11355
11612
  }
11613
+ const zipBuffer = zip.toBuffer();
11614
+ logInfo(
11615
+ `FilePacking: read ${packedFilesCount} source files. total size: ${zipBuffer.length} bytes`
11616
+ );
11617
+ logInfo("FilePacking: Files packed successfully");
11618
+ return zipBuffer;
11356
11619
  }
11357
11620
  };
11358
11621
 
@@ -11480,6 +11743,85 @@ ${fix2.patch || "No patch available"}
11480
11743
  - If any patch fails, continue with the others and report issues at the end
11481
11744
  `;
11482
11745
  };
11746
+ var failedToConnectToApiPrompt = `# CONNECTION ERROR: FAILED TO REACH MOBB API
11747
+
11748
+ ## ANALYSIS SUMMARY
11749
+ - **Status:** \u274C Failed
11750
+ - **Issue Type:** Connection Error
11751
+ - **Error Details:** Unable to establish connection to the Mobb API service
11752
+
11753
+ ## TROUBLESHOOTING STEPS FOR THE USER
11754
+
11755
+ The Mobb security scanning service is currently not reachable. This may be due to:
11756
+
11757
+ 1. **Missing or invalid authentication credentials**
11758
+ - Ensure the \`API_KEY\` environment variable is properly set with your valid Mobb authentication token
11759
+ - Example: \`export API_KEY=your_mobb_api_key_here\`
11760
+
11761
+ 2. **Incorrect API endpoint configuration**
11762
+ - Check if the \`API_URL\` environment variable needs to be set to the correct Mobb service endpoint
11763
+ - Example: \`export API_URL=https://api.mobb.ai/graphql\`
11764
+
11765
+ 3. **Network connectivity issues**
11766
+ - Verify your internet connection is working properly
11767
+ - Check if any firewall or proxy settings might be blocking the connection
11768
+
11769
+ 4. **Service outage**
11770
+ - The Mobb service might be temporarily unavailable
11771
+ - Please try again later or check the Mobb status page
11772
+
11773
+ ## NEXT STEPS
11774
+
11775
+ Please resolve the connection issue using the steps above and try running the security scan again.
11776
+
11777
+ For additional assistance, please:
11778
+ - Visit the Mobb documentation at https://docs.mobb.ai
11779
+ - Contact Mobb support at support@mobb.ai
11780
+
11781
+ `;
11782
+ var failedToAuthenticatePrompt = `# AUTHENTICATION ERROR: MOBB LOGIN REQUIRED
11783
+
11784
+ ## ANALYSIS SUMMARY
11785
+ - **Status:** \u274C Failed
11786
+ - **Issue Type:** Authentication Error
11787
+ - **Error Details:** Unable to authenticate with the Mobb service
11788
+
11789
+ ## AUTHENTICATION REQUIRED
11790
+
11791
+ The Mobb security scanning service requires authentication before it can analyze your code for vulnerabilities. You need to:
11792
+
11793
+ 1. **Login and authorize access to Mobb**
11794
+ - A browser window should have opened to complete the authentication process
11795
+ - If no browser window opened, please run the command again
11796
+
11797
+ 2. **Create a Mobb account if you don't have one**
11798
+ - If you don't already have a Mobb account, you'll need to sign up
11799
+ - Visit https://app.mobb.ai/auth/signup to create your free account
11800
+ - Use your work email for easier team collaboration
11801
+
11802
+ 3. **Authorization flow**
11803
+ - After logging in, you'll be asked to authorize the CLI tool
11804
+ - This creates a secure token that allows the CLI to access Mobb services
11805
+ - You only need to do this once per device
11806
+
11807
+ ## TROUBLESHOOTING
11808
+
11809
+ If you're experiencing issues with authentication:
11810
+
11811
+ - Ensure you have an active internet connection
11812
+ - Check that you can access https://app.mobb.ai in your browser
11813
+ - Try running the command again with the \`--debug\` flag for more detailed output
11814
+ - Make sure your browser isn't blocking pop-ups from the authentication window
11815
+
11816
+ ## NEXT STEPS
11817
+
11818
+ Please complete the authentication process and try running the security scan again.
11819
+
11820
+ For additional assistance, please:
11821
+ - Visit the Mobb documentation at https://docs.mobb.ai/cli/authentication
11822
+ - Contact Mobb support at support@mobb.ai
11823
+
11824
+ `;
11483
11825
 
11484
11826
  // src/mcp/tools/fixVulnerabilities/VulnerabilityFixService.ts
11485
11827
  var VUL_REPORT_DIGEST_TIMEOUT_MS2 = 1e3 * 60 * 5;
@@ -11487,15 +11829,12 @@ var VulnerabilityFixService = class {
11487
11829
  constructor() {
11488
11830
  __publicField(this, "gqlClient");
11489
11831
  __publicField(this, "filePacking");
11490
- __publicField(this, "fileUpload");
11491
11832
  this.filePacking = new FilePacking();
11492
- this.fileUpload = new FileUpload();
11493
11833
  }
11494
11834
  async processVulnerabilities(fileList, repositoryPath) {
11495
11835
  try {
11496
11836
  this.validateFiles(fileList);
11497
- const apiKey = this.validateApiKey();
11498
- this.gqlClient = await this.initializeGqlClient(apiKey);
11837
+ this.gqlClient = await this.initializeGqlClient();
11499
11838
  const repoUploadInfo = await this.initializeReport();
11500
11839
  const zipBuffer = await this.packFiles(fileList, repositoryPath);
11501
11840
  await this.uploadFiles(zipBuffer, repoUploadInfo);
@@ -11507,6 +11846,12 @@ var VulnerabilityFixService = class {
11507
11846
  const fixes = await this.getReportFixes(repoUploadInfo.fixReportId);
11508
11847
  return fixesPrompt(fixes);
11509
11848
  } catch (error) {
11849
+ if (error instanceof ApiConnectionError || error instanceof CliLoginError) {
11850
+ return failedToConnectToApiPrompt;
11851
+ }
11852
+ if (error instanceof AuthenticationError || error instanceof FailedToGetApiTokenError) {
11853
+ return failedToAuthenticatePrompt;
11854
+ }
11510
11855
  const message = error.message;
11511
11856
  logError("Vulnerability processing failed", { error: message });
11512
11857
  throw error;
@@ -11514,30 +11859,22 @@ var VulnerabilityFixService = class {
11514
11859
  }
11515
11860
  validateFiles(fileList) {
11516
11861
  if (fileList.length === 0) {
11517
- throw new Error("No files to fix");
11518
- }
11519
- }
11520
- validateApiKey() {
11521
- const apiKey = process.env["API_KEY"];
11522
- if (!apiKey) {
11523
- throw new Error("API_KEY environment variable is not set");
11862
+ throw new NoFilesError();
11524
11863
  }
11525
- return apiKey;
11526
11864
  }
11527
- async initializeGqlClient(apiKey) {
11528
- const gqlClient = new McpGQLClient({
11529
- apiKey,
11530
- type: "apiKey"
11531
- });
11865
+ async initializeGqlClient() {
11866
+ const gqlClient = await getMcpGQLClient();
11532
11867
  const isConnected = await gqlClient.verifyConnection();
11533
11868
  if (!isConnected) {
11534
- throw new Error("Failed to connect to the API. Please check your API_KEY");
11869
+ throw new ApiConnectionError(
11870
+ "Failed to connect to the API. Please check your API_KEY"
11871
+ );
11535
11872
  }
11536
11873
  return gqlClient;
11537
11874
  }
11538
11875
  async initializeReport() {
11539
11876
  if (!this.gqlClient) {
11540
- throw new Error("GraphQL client not initialized");
11877
+ throw new GqlClientError();
11541
11878
  }
11542
11879
  try {
11543
11880
  const {
@@ -11547,7 +11884,9 @@ var VulnerabilityFixService = class {
11547
11884
  return repoUploadInfo;
11548
11885
  } catch (error) {
11549
11886
  const message = error.message;
11550
- throw new Error(`Error initializing report: ${message}`);
11887
+ throw new ReportInitializationError(
11888
+ `Error initializing report: ${message}`
11889
+ );
11551
11890
  }
11552
11891
  }
11553
11892
  async packFiles(fileList, repositoryPath) {
@@ -11560,15 +11899,15 @@ var VulnerabilityFixService = class {
11560
11899
  return zipBuffer;
11561
11900
  } catch (error) {
11562
11901
  const message = error.message;
11563
- throw new Error(`Error packing files: ${message}`);
11902
+ throw new FileProcessingError(`Error packing files: ${message}`);
11564
11903
  }
11565
11904
  }
11566
11905
  async uploadFiles(zipBuffer, repoUploadInfo) {
11567
11906
  if (!repoUploadInfo) {
11568
- throw new Error("Upload info is required");
11907
+ throw new FileUploadError("Upload info is required");
11569
11908
  }
11570
11909
  try {
11571
- await this.fileUpload.uploadFile({
11910
+ await uploadFile({
11572
11911
  file: zipBuffer,
11573
11912
  url: repoUploadInfo.url,
11574
11913
  uploadFields: JSON.parse(repoUploadInfo.uploadFieldsJSON),
@@ -11577,12 +11916,14 @@ var VulnerabilityFixService = class {
11577
11916
  logInfo("File uploaded successfully");
11578
11917
  } catch (error) {
11579
11918
  logError("File upload failed", { error: error.message });
11580
- throw new Error(`Failed to upload the file: ${error.message}`);
11919
+ throw new FileUploadError(
11920
+ `Failed to upload the file: ${error.message}`
11921
+ );
11581
11922
  }
11582
11923
  }
11583
11924
  async getProjectId() {
11584
11925
  if (!this.gqlClient) {
11585
- throw new Error("GraphQL client not initialized");
11926
+ throw new GqlClientError();
11586
11927
  }
11587
11928
  const projectId = await this.gqlClient.getProjectId();
11588
11929
  logInfo("Project ID retrieved", { projectId });
@@ -11590,7 +11931,7 @@ var VulnerabilityFixService = class {
11590
11931
  }
11591
11932
  async runScan(params) {
11592
11933
  if (!this.gqlClient) {
11593
- throw new Error("GraphQL client not initialized");
11934
+ throw new GqlClientError();
11594
11935
  }
11595
11936
  const { fixReportId, projectId } = params;
11596
11937
  logInfo("Starting scan", { fixReportId, projectId });
@@ -11609,7 +11950,7 @@ var VulnerabilityFixService = class {
11609
11950
  logError("Vulnerability report submission failed", {
11610
11951
  response: submitRes
11611
11952
  });
11612
- throw new Error("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
11953
+ throw new ScanError("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
11613
11954
  }
11614
11955
  logInfo("Vulnerability report submitted successfully", {
11615
11956
  analysisId: submitRes.submitVulnerabilityReport.fixReportId
@@ -11628,7 +11969,7 @@ var VulnerabilityFixService = class {
11628
11969
  }
11629
11970
  async getReportFixes(fixReportId) {
11630
11971
  if (!this.gqlClient) {
11631
- throw new Error("GraphQL client not initialized");
11972
+ throw new GqlClientError();
11632
11973
  }
11633
11974
  const fixes = await this.gqlClient.getReportFixes(fixReportId);
11634
11975
  logInfo("Fixes retrieved", { fixCount: fixes.length });
@@ -11665,18 +12006,48 @@ var FixVulnerabilitiesTool = class {
11665
12006
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
11666
12007
  );
11667
12008
  }
11668
- const gitService = new GitService(args.path);
12009
+ const gitService = new GitService(args.path, log);
11669
12010
  const gitValidation = await gitService.validateRepository();
12011
+ let files = [];
11670
12012
  if (!gitValidation.isValid) {
11671
- throw new Error(gitValidation.error || "Git repository validation failed");
12013
+ logDebug(
12014
+ "Git repository validation failed, using all files in the repository",
12015
+ {
12016
+ path: args.path
12017
+ }
12018
+ );
12019
+ files = FileUtils.getLastChangedFiles(args.path);
12020
+ logDebug("Found files in the repository", {
12021
+ files,
12022
+ fileCount: files.length
12023
+ });
12024
+ } else {
12025
+ const gitResult = await gitService.getChangedFiles();
12026
+ files = gitResult.files;
12027
+ if (files.length === 0) {
12028
+ const recentResult = await gitService.getRecentlyChangedFiles();
12029
+ files = recentResult.files;
12030
+ logDebug(
12031
+ "No changes found, using recently changed files from git history",
12032
+ {
12033
+ files,
12034
+ fileCount: files.length,
12035
+ commitsChecked: recentResult.commitCount
12036
+ }
12037
+ );
12038
+ } else {
12039
+ logDebug("Found changed files in the git repository", {
12040
+ files,
12041
+ fileCount: files.length
12042
+ });
12043
+ }
11672
12044
  }
11673
- const gitResult = await gitService.getChangedFiles();
11674
- if (gitResult.files.length === 0) {
12045
+ if (files.length === 0) {
11675
12046
  return {
11676
12047
  content: [
11677
12048
  {
11678
12049
  type: "text",
11679
- text: "No changed files found in the git repository. The vulnerability scanner analyzes modified, added, or staged files. Make some changes to your code and try again."
12050
+ 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."
11680
12051
  }
11681
12052
  ]
11682
12053
  };
@@ -11684,7 +12055,7 @@ var FixVulnerabilitiesTool = class {
11684
12055
  try {
11685
12056
  const vulnerabilityFixService = new VulnerabilityFixService();
11686
12057
  const fixResult = await vulnerabilityFixService.processVulnerabilities(
11687
- gitResult.files,
12058
+ files,
11688
12059
  args.path
11689
12060
  );
11690
12061
  const result = {
@@ -11697,7 +12068,7 @@ var FixVulnerabilitiesTool = class {
11697
12068
  };
11698
12069
  logInfo("Tool execution completed successfully", {
11699
12070
  resultLength: fixResult.length,
11700
- fileCount: gitResult.files.length,
12071
+ fileCount: files.length,
11701
12072
  result
11702
12073
  });
11703
12074
  return result;
@@ -11770,7 +12141,7 @@ var mcpHandler = async (_args) => {
11770
12141
  };
11771
12142
 
11772
12143
  // src/args/commands/review.ts
11773
- import fs10 from "fs";
12144
+ import fs11 from "fs";
11774
12145
  import chalk9 from "chalk";
11775
12146
  function reviewBuilder(yargs2) {
11776
12147
  return yargs2.option("f", {
@@ -11807,7 +12178,7 @@ function reviewBuilder(yargs2) {
11807
12178
  ).help();
11808
12179
  }
11809
12180
  function validateReviewOptions(argv) {
11810
- if (!fs10.existsSync(argv.f)) {
12181
+ if (!fs11.existsSync(argv.f)) {
11811
12182
  throw new CliError(`
11812
12183
  Can't access ${chalk9.bold(argv.f)}`);
11813
12184
  }
@@ -11945,13 +12316,13 @@ var parseArgs = async (args) => {
11945
12316
  };
11946
12317
 
11947
12318
  // src/index.ts
11948
- var debug20 = Debug21("mobbdev:index");
12319
+ var debug19 = Debug20("mobbdev:index");
11949
12320
  async function run() {
11950
12321
  return parseArgs(hideBin(process.argv));
11951
12322
  }
11952
12323
  (async () => {
11953
12324
  try {
11954
- debug20("Bugsy CLI v%s running...", packageJson.version);
12325
+ debug19("Bugsy CLI v%s running...", packageJson.version);
11955
12326
  await run();
11956
12327
  process.exit(0);
11957
12328
  } catch (err) {