mobbdev 1.0.90 → 1.0.92

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 +1440 -1056
  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";
@@ -408,6 +408,7 @@ var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
408
408
  IssueType_Enum2["MissingEqualsOrHashcode"] = "MISSING_EQUALS_OR_HASHCODE";
409
409
  IssueType_Enum2["MissingHstsHeader"] = "MISSING_HSTS_HEADER";
410
410
  IssueType_Enum2["MissingSslMinversion"] = "MISSING_SSL_MINVERSION";
411
+ IssueType_Enum2["MissingWhitespace"] = "MISSING_WHITESPACE";
411
412
  IssueType_Enum2["ModifiedDefaultParam"] = "MODIFIED_DEFAULT_PARAM";
412
413
  IssueType_Enum2["NonFinalPublicStaticField"] = "NON_FINAL_PUBLIC_STATIC_FIELD";
413
414
  IssueType_Enum2["NonReadonlyField"] = "NON_READONLY_FIELD";
@@ -1472,7 +1473,8 @@ var issueTypeMap = {
1472
1473
  ["AVOID_IDENTITY_COMPARISON_CACHED_TYPES" /* AvoidIdentityComparisonCachedTypes */]: "Avoid Identity Comparison of Cached Types",
1473
1474
  ["AVOID_BUILTIN_SHADOWING" /* AvoidBuiltinShadowing */]: "Avoid Builtin Shadowing",
1474
1475
  ["IMPROPER_STRING_FORMATTING" /* ImproperStringFormatting */]: "Improper String Formatting",
1475
- ["TAR_SLIP" /* TarSlip */]: "Tar Slip"
1476
+ ["TAR_SLIP" /* TarSlip */]: "Tar Slip",
1477
+ ["MISSING_WHITESPACE" /* MissingWhitespace */]: "Missing Whitespace"
1476
1478
  };
1477
1479
  var issueTypeZ = z5.nativeEnum(IssueType_Enum);
1478
1480
  var getIssueTypeFriendlyString = (issueType) => {
@@ -2206,7 +2208,8 @@ var fixDetailsData = {
2206
2208
  ["AVOID_BUILTIN_SHADOWING" /* AvoidBuiltinShadowing */]: void 0,
2207
2209
  ["IMPROPER_STRING_FORMATTING" /* ImproperStringFormatting */]: void 0,
2208
2210
  ["WILDCARD_IMPORTS" /* WildcardImports */]: void 0,
2209
- ["TAR_SLIP" /* TarSlip */]: void 0
2211
+ ["TAR_SLIP" /* TarSlip */]: void 0,
2212
+ ["MISSING_WHITESPACE" /* MissingWhitespace */]: void 0
2210
2213
  };
2211
2214
 
2212
2215
  // src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
@@ -3632,6 +3635,15 @@ var vulnerabilities12 = {
3632
3635
  };
3633
3636
  var js_default = vulnerabilities12;
3634
3637
 
3638
+ // src/features/analysis/scm/shared/src/storedQuestionData/python/duplicatedStrings.ts
3639
+ var duplicatedStrings2 = {
3640
+ constantName: {
3641
+ content: () => "New constant name",
3642
+ description: () => "",
3643
+ guidance: () => ""
3644
+ }
3645
+ };
3646
+
3635
3647
  // src/features/analysis/scm/shared/src/storedQuestionData/python/logForging.ts
3636
3648
  var logForging5 = {
3637
3649
  isHtmlDisplay: {
@@ -3676,7 +3688,8 @@ var vulnerabilities13 = {
3676
3688
  ["CSRF" /* Csrf */]: csrf2,
3677
3689
  ["LOG_FORGING" /* LogForging */]: logForging5,
3678
3690
  ["OPEN_REDIRECT" /* OpenRedirect */]: openRedirect2,
3679
- ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition3
3691
+ ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition3,
3692
+ ["DUPLICATED_STRINGS" /* DuplicatedStrings */]: duplicatedStrings2
3680
3693
  };
3681
3694
  var python_default2 = vulnerabilities13;
3682
3695
 
@@ -4658,7 +4671,7 @@ async function getAdoSdk(params) {
4658
4671
  const url = new URL(repoUrl);
4659
4672
  const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
4660
4673
  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 = [
4674
+ const path13 = [
4662
4675
  prefixPath,
4663
4676
  owner,
4664
4677
  projectName,
@@ -4669,7 +4682,7 @@ async function getAdoSdk(params) {
4669
4682
  "items",
4670
4683
  "items"
4671
4684
  ].filter(Boolean).join("/");
4672
- return new URL(`${path12}?${params2}`, origin2).toString();
4685
+ return new URL(`${path13}?${params2}`, origin2).toString();
4673
4686
  },
4674
4687
  async getAdoBranchList({ repoUrl }) {
4675
4688
  try {
@@ -4892,103 +4905,596 @@ async function getAdoRepoList({
4892
4905
  // src/features/analysis/scm/ado/AdoSCMLib.ts
4893
4906
  import { setTimeout as setTimeout2 } from "timers/promises";
4894
4907
 
4895
- // src/features/analysis/scm/scmSubmit/index.ts
4908
+ // src/features/analysis/scm/git/GitService.ts
4909
+ import * as path2 from "path";
4896
4910
  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) {
4911
+
4912
+ // src/features/analysis/scm/FileUtils.ts
4913
+ import fs2 from "fs";
4914
+ import { isBinary } from "istextorbinary";
4915
+ import path from "path";
4916
+ var EXCLUDED_FILE_PATTERNS = [
4917
+ // ... (copy the full array from FilePacking.ts)
4918
+ ".json",
4919
+ ".yaml",
4920
+ ".yml",
4921
+ ".toml",
4922
+ ".ini",
4923
+ ".conf",
4924
+ ".config",
4925
+ ".xml",
4926
+ ".env",
4927
+ ".md",
4928
+ ".txt",
4929
+ ".rst",
4930
+ ".adoc",
4931
+ ".lock",
4932
+ ".png",
4933
+ ".jpg",
4934
+ ".jpeg",
4935
+ ".gif",
4936
+ ".svg",
4937
+ ".ico",
4938
+ ".webp",
4939
+ ".bmp",
4940
+ ".tiff",
4941
+ ".ttf",
4942
+ ".otf",
4943
+ ".woff",
4944
+ ".woff2",
4945
+ ".eot",
4946
+ ".zip",
4947
+ ".tar",
4948
+ ".gz",
4949
+ ".rar",
4950
+ ".7z",
4951
+ ".log",
4952
+ ".db",
4953
+ ".sqlite",
4954
+ ".sql",
4955
+ ".pem",
4956
+ ".crt",
4957
+ ".key",
4958
+ ".p12",
4959
+ ".pfx",
4960
+ ".editorconfig",
4961
+ ".sublime-project",
4962
+ ".sublime-workspace",
4963
+ ".DS_Store",
4964
+ "Thumbs.db",
4965
+ ".lcov",
4966
+ ".exe",
4967
+ ".dll",
4968
+ ".so",
4969
+ ".dylib",
4970
+ ".class",
4971
+ ".pyc",
4972
+ ".pyo",
4973
+ ".o",
4974
+ ".obj",
4975
+ ".min.js",
4976
+ ".min.css",
4977
+ ".min.html",
4978
+ ".test.js",
4979
+ ".test.ts",
4980
+ ".test.jsx",
4981
+ ".test.tsx",
4982
+ ".spec.js",
4983
+ ".spec.ts",
4984
+ ".spec.jsx",
4985
+ ".spec.tsx",
4986
+ ".d.ts",
4987
+ ".bundle.js",
4988
+ ".chunk.js",
4989
+ "dockerfile",
4990
+ "jenkinsfile",
4991
+ "go.sum",
4992
+ ".gitignore",
4993
+ ".gitattributes",
4994
+ ".gitmodules",
4995
+ ".gitkeep",
4996
+ ".keep",
4997
+ ".hgignore",
4998
+ ".nvmrc",
4999
+ ".node-version",
5000
+ ".npmrc",
5001
+ ".yarnrc",
5002
+ ".pnpmfile.cjs",
5003
+ ".ruby-version",
5004
+ ".python-version",
5005
+ ".rvmrc",
5006
+ ".rbenv-version",
5007
+ ".gvmrc",
5008
+ "makefile",
5009
+ "rakefile",
5010
+ "gulpfile.js",
5011
+ "gruntfile.js",
5012
+ "webpack.config.js",
5013
+ "webpack.config.ts",
5014
+ "rollup.config.js",
5015
+ "vite.config.js",
5016
+ "vite.config.ts",
5017
+ "next.config.js",
5018
+ "nuxt.config.js",
5019
+ "tailwind.config.js",
5020
+ "postcss.config.js",
5021
+ ".babelrc",
5022
+ ".babelrc.js",
5023
+ ".swcrc",
5024
+ ".browserslistrc",
5025
+ "jest.config.js",
5026
+ "jest.config.ts",
5027
+ "vitest.config.js",
5028
+ "karma.conf.js",
5029
+ "protractor.conf.js",
5030
+ "cypress.config.js",
5031
+ "playwright.config.js",
5032
+ ".nycrc",
5033
+ ".c8rc",
5034
+ ".eslintrc",
5035
+ ".eslintrc.js",
5036
+ ".prettierrc",
5037
+ ".prettierrc.js",
5038
+ ".stylelintrc",
5039
+ ".stylelintrc.js",
5040
+ "pipfile",
5041
+ "gemfile",
5042
+ "go.mod",
5043
+ "project.clj",
5044
+ "setup.py",
5045
+ "setup.cfg",
5046
+ "manifest.in",
5047
+ ".pythonrc",
5048
+ "readme",
5049
+ "changelog",
5050
+ "authors",
5051
+ "contributors",
5052
+ "license",
5053
+ "notice",
5054
+ "copyright",
5055
+ ".htaccess"
5056
+ ];
5057
+ var FileUtils = class {
5058
+ static isExcludedFileType(filepath) {
5059
+ const basename = path.basename(filepath).toLowerCase();
5060
+ if (basename === ".env" || basename.startsWith(".env.")) {
5061
+ return true;
5062
+ }
5063
+ if (EXCLUDED_FILE_PATTERNS.some((pattern) => basename.endsWith(pattern))) {
4902
5064
  return true;
4903
5065
  }
4904
5066
  return false;
4905
- } catch (e) {
4906
- return false;
4907
- }
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
5067
  }
4920
- async getUrlWithCredentials() {
4921
- if (!this.url) {
4922
- console.error("no url for getUrlWithCredentials()");
4923
- throw new Error("no url");
5068
+ static shouldPackFile(filepath, maxFileSize = 1024 * 1024 * 5) {
5069
+ const absoluteFilepath = path.resolve(filepath);
5070
+ if (this.isExcludedFileType(filepath)) return false;
5071
+ if (!fs2.existsSync(absoluteFilepath)) return false;
5072
+ if (fs2.lstatSync(absoluteFilepath).size > maxFileSize) return false;
5073
+ let data;
5074
+ try {
5075
+ data = fs2.readFileSync(absoluteFilepath);
5076
+ } catch {
5077
+ return false;
4924
5078
  }
4925
- const trimmedUrl = this.url.trim().replace(/\/$/, "");
4926
- const accessToken = this.getAccessToken();
4927
- if (!accessToken) {
4928
- return trimmedUrl;
5079
+ if (isBinary(null, data)) return false;
5080
+ return true;
5081
+ }
5082
+ static getAllFiles(dir, rootDir) {
5083
+ const root = rootDir || dir;
5084
+ const results = [];
5085
+ const relativeDepth = path.relative(root, dir).split(path.sep).length;
5086
+ if (relativeDepth > 20) {
5087
+ return [];
4929
5088
  }
4930
- if (this.scmLibType === "ADO" /* ADO */) {
4931
- const { host, protocol, pathname } = new URL(trimmedUrl);
4932
- return `${protocol}//${accessToken}@${host}${pathname}`;
5089
+ if (results.length > 1e5) {
5090
+ return [];
4933
5091
  }
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 "";
5092
+ try {
5093
+ fs2.accessSync(dir, fs2.constants.R_OK);
5094
+ } catch {
5095
+ return [];
4951
5096
  }
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");
5097
+ const items = fs2.readdirSync(dir);
5098
+ for (const item of items) {
5099
+ const fullPath = path.join(dir, item);
5100
+ try {
5101
+ fs2.accessSync(fullPath, fs2.constants.R_OK);
5102
+ } catch {
5103
+ continue;
5104
+ }
5105
+ const stat = fs2.statSync(fullPath);
5106
+ if (stat.isDirectory()) {
5107
+ results.push(...this.getAllFiles(fullPath, root));
5108
+ } else {
5109
+ results.push({
5110
+ name: item,
5111
+ fullPath,
5112
+ relativePath: path.relative(root, fullPath),
5113
+ time: stat.mtime.getTime(),
5114
+ isFile: true
5115
+ });
5116
+ }
4958
5117
  }
5118
+ return results;
4959
5119
  }
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
- }
5120
+ static getLastChangedFiles(dir, maxFileSize = 1024 * 1024 * 5, count = 10) {
5121
+ if (!fs2.existsSync(dir) || !fs2.lstatSync(dir).isDirectory()) return [];
5122
+ const files = this.getAllFiles(dir);
5123
+ return files.filter((file) => this.shouldPackFile(file.fullPath, maxFileSize)).sort((a, b) => b.time - a.time).slice(0, count).map((file) => file.relativePath);
4972
5124
  }
4973
5125
  };
4974
5126
 
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 });
4990
- }
4991
- async getAdoSdk() {
5127
+ // src/features/analysis/scm/git/GitService.ts
5128
+ var GitService = class {
5129
+ constructor(repositoryPath, log2) {
5130
+ __publicField(this, "git");
5131
+ __publicField(this, "repositoryPath");
5132
+ __publicField(this, "log");
5133
+ const noopLog = (_message, _level, _data) => {
5134
+ };
5135
+ this.log = log2 || noopLog;
5136
+ this.git = simpleGit(repositoryPath, { binary: "git" });
5137
+ this.repositoryPath = repositoryPath;
5138
+ this.log("Git service initialized", "debug", { repositoryPath });
5139
+ }
5140
+ /**
5141
+ * Validates that the path is a valid git repository
5142
+ */
5143
+ async validateRepository() {
5144
+ this.log("Validating git repository", "debug");
5145
+ try {
5146
+ const isRepo = await this.git.checkIsRepo();
5147
+ if (!isRepo) {
5148
+ const error = "Path is not a valid git repository";
5149
+ this.log(error, "error");
5150
+ return { isValid: false, error };
5151
+ }
5152
+ this.log("Git repository validation successful", "debug");
5153
+ return { isValid: true };
5154
+ } catch (error) {
5155
+ const errorMessage = `Failed to verify git repository: ${error.message}`;
5156
+ this.log(errorMessage, "error", { error });
5157
+ return { isValid: false, error: errorMessage };
5158
+ }
5159
+ }
5160
+ /**
5161
+ * Gets the current git status and returns changed files
5162
+ */
5163
+ async getChangedFiles() {
5164
+ this.log("Getting git status", "debug");
5165
+ try {
5166
+ const status = await this.git.status();
5167
+ const gitRoot = await this.git.revparse(["--show-toplevel"]);
5168
+ const relativePathFromGitRoot = path2.relative(
5169
+ gitRoot,
5170
+ this.repositoryPath
5171
+ );
5172
+ const files = status.files.map((file) => {
5173
+ const gitRelativePath = file.path;
5174
+ if (relativePathFromGitRoot === "") {
5175
+ return gitRelativePath;
5176
+ }
5177
+ if (gitRelativePath.startsWith(relativePathFromGitRoot + "/")) {
5178
+ return gitRelativePath.substring(relativePathFromGitRoot.length + 1);
5179
+ }
5180
+ return path2.relative(
5181
+ this.repositoryPath,
5182
+ path2.join(gitRoot, gitRelativePath)
5183
+ );
5184
+ });
5185
+ this.log("Git status retrieved", "info", {
5186
+ fileCount: files.length,
5187
+ files: files.slice(0, 10),
5188
+ // Log first 10 files to avoid spam
5189
+ gitRoot,
5190
+ workingDir: this.repositoryPath,
5191
+ relativePathFromGitRoot
5192
+ });
5193
+ return { files, status };
5194
+ } catch (error) {
5195
+ const errorMessage = `Failed to get git status: ${error.message}`;
5196
+ this.log(errorMessage, "error", { error });
5197
+ throw new Error(errorMessage);
5198
+ }
5199
+ }
5200
+ /**
5201
+ * Gets git repository information including remote URL, current commit hash, and branch name
5202
+ */
5203
+ async getGitInfo() {
5204
+ this.log("Getting git repository information", "debug");
5205
+ try {
5206
+ const [repoUrl, hash, reference] = await Promise.all([
5207
+ this.git.getConfig("remote.origin.url"),
5208
+ this.git.revparse(["HEAD"]),
5209
+ this.git.revparse(["--abbrev-ref", "HEAD"])
5210
+ ]);
5211
+ let normalizedRepoUrl = repoUrl.value || "";
5212
+ if (normalizedRepoUrl.endsWith(".git")) {
5213
+ normalizedRepoUrl = normalizedRepoUrl.slice(0, -".git".length);
5214
+ }
5215
+ if (normalizedRepoUrl.startsWith("git@github.com:")) {
5216
+ normalizedRepoUrl = normalizedRepoUrl.replace(
5217
+ "git@github.com:",
5218
+ "https://github.com/"
5219
+ );
5220
+ }
5221
+ this.log("Git repository information retrieved", "debug", {
5222
+ repoUrl: normalizedRepoUrl,
5223
+ hash,
5224
+ reference
5225
+ });
5226
+ return {
5227
+ repoUrl: normalizedRepoUrl,
5228
+ hash,
5229
+ reference
5230
+ };
5231
+ } catch (error) {
5232
+ const errorMessage = `Failed to get git repository information: ${error.message}`;
5233
+ this.log(errorMessage, "error", { error });
5234
+ throw new Error(errorMessage);
5235
+ }
5236
+ }
5237
+ /**
5238
+ * Validates if a branch name is valid according to git's rules
5239
+ */
5240
+ async isValidBranchName(branchName) {
5241
+ this.log("Validating branch name", "debug", { branchName });
5242
+ try {
5243
+ const result = await this.git.raw([
5244
+ "check-ref-format",
5245
+ "--branch",
5246
+ branchName
5247
+ ]);
5248
+ const isValid = Boolean(result);
5249
+ this.log("Branch name validation result", "debug", {
5250
+ branchName,
5251
+ isValid
5252
+ });
5253
+ return isValid;
5254
+ } catch (error) {
5255
+ this.log("Branch name validation failed", "debug", { branchName, error });
5256
+ return false;
5257
+ }
5258
+ }
5259
+ /**
5260
+ * Gets the current branch name
5261
+ */
5262
+ async getCurrentBranch() {
5263
+ this.log("Getting current branch name", "debug");
5264
+ try {
5265
+ const branch = await this.git.revparse(["--abbrev-ref", "HEAD"]);
5266
+ this.log("Current branch retrieved", "debug", { branch });
5267
+ return branch;
5268
+ } catch (error) {
5269
+ const errorMessage = `Failed to get current branch: ${error.message}`;
5270
+ this.log(errorMessage, "error", { error });
5271
+ throw new Error(errorMessage);
5272
+ }
5273
+ }
5274
+ /**
5275
+ * Gets the current commit hash
5276
+ */
5277
+ async getCurrentCommitHash() {
5278
+ this.log("Getting current commit hash", "debug");
5279
+ try {
5280
+ const hash = await this.git.revparse(["HEAD"]);
5281
+ this.log("Current commit hash retrieved", "debug", { hash });
5282
+ return hash;
5283
+ } catch (error) {
5284
+ const errorMessage = `Failed to get current commit hash: ${error.message}`;
5285
+ this.log(errorMessage, "error", { error });
5286
+ throw new Error(errorMessage);
5287
+ }
5288
+ }
5289
+ /**
5290
+ * Gets the remote repository URL
5291
+ */
5292
+ async getRemoteUrl() {
5293
+ this.log("Getting remote repository URL", "debug");
5294
+ try {
5295
+ const remoteUrl = await this.git.getConfig("remote.origin.url");
5296
+ const url = remoteUrl.value || "";
5297
+ let normalizedUrl = url;
5298
+ if (normalizedUrl.endsWith(".git")) {
5299
+ normalizedUrl = normalizedUrl.slice(0, -".git".length);
5300
+ }
5301
+ if (normalizedUrl.startsWith("git@github.com:")) {
5302
+ normalizedUrl = normalizedUrl.replace(
5303
+ "git@github.com:",
5304
+ "https://github.com/"
5305
+ );
5306
+ }
5307
+ this.log("Remote repository URL retrieved", "debug", {
5308
+ url: normalizedUrl
5309
+ });
5310
+ return normalizedUrl;
5311
+ } catch (error) {
5312
+ const errorMessage = `Failed to get remote repository URL: ${error.message}`;
5313
+ this.log(errorMessage, "error", { error });
5314
+ throw new Error(errorMessage);
5315
+ }
5316
+ }
5317
+ /**
5318
+ * Gets the 10 most recently changed files based on commit history
5319
+ */
5320
+ async getRecentlyChangedFiles() {
5321
+ this.log(
5322
+ "Getting the 10 most recently changed files from commit history",
5323
+ "debug"
5324
+ );
5325
+ try {
5326
+ const gitRoot = await this.git.revparse(["--show-toplevel"]);
5327
+ const relativePathFromGitRoot = path2.relative(
5328
+ gitRoot,
5329
+ this.repositoryPath
5330
+ );
5331
+ const fileSet = /* @__PURE__ */ new Set();
5332
+ const files = [];
5333
+ let commitsProcessed = 0;
5334
+ const logResult = await this.git.log({
5335
+ maxCount: 100,
5336
+ // Get last 100 commits - should be enough to find 10 unique files
5337
+ format: {
5338
+ hash: "%H",
5339
+ date: "%ai",
5340
+ message: "%s",
5341
+ //the field name author_name can't follow the naming convention as we are using the git log command
5342
+ // eslint-disable-next-line @typescript-eslint/naming-convention
5343
+ author_name: "%an"
5344
+ }
5345
+ });
5346
+ for (const commit of logResult.all) {
5347
+ if (files.length >= 10) {
5348
+ break;
5349
+ }
5350
+ commitsProcessed++;
5351
+ try {
5352
+ const filesOutput = await this.git.show([
5353
+ "--name-only",
5354
+ "--pretty=format:",
5355
+ commit.hash
5356
+ ]);
5357
+ const commitFiles = filesOutput.split("\n").filter((file) => file.trim() !== "");
5358
+ for (const file of commitFiles) {
5359
+ if (files.length >= 10) {
5360
+ break;
5361
+ }
5362
+ const gitRelativePath = file.trim();
5363
+ let adjustedPath;
5364
+ if (relativePathFromGitRoot === "") {
5365
+ adjustedPath = gitRelativePath;
5366
+ } else if (gitRelativePath.startsWith(relativePathFromGitRoot + "/")) {
5367
+ adjustedPath = gitRelativePath.substring(
5368
+ relativePathFromGitRoot.length + 1
5369
+ );
5370
+ } else {
5371
+ adjustedPath = path2.relative(
5372
+ this.repositoryPath,
5373
+ path2.join(gitRoot, gitRelativePath)
5374
+ );
5375
+ }
5376
+ this.log(`Considering file: ${adjustedPath}`, "debug");
5377
+ if (!fileSet.has(adjustedPath) && FileUtils.shouldPackFile(path2.join(gitRoot, gitRelativePath))) {
5378
+ fileSet.add(adjustedPath);
5379
+ files.push(adjustedPath);
5380
+ }
5381
+ }
5382
+ } catch (showError) {
5383
+ this.log(`Could not get files for commit ${commit.hash}`, "debug", {
5384
+ error: showError
5385
+ });
5386
+ }
5387
+ }
5388
+ this.log("Recently changed files retrieved", "info", {
5389
+ fileCount: files.length,
5390
+ commitsProcessed,
5391
+ totalCommitsAvailable: logResult.all.length,
5392
+ files: files.slice(0, 10),
5393
+ // Log the files (should be all of them since we limit to 10)
5394
+ gitRoot,
5395
+ workingDir: this.repositoryPath,
5396
+ relativePathFromGitRoot
5397
+ });
5398
+ return {
5399
+ files,
5400
+ commitCount: commitsProcessed
5401
+ };
5402
+ } catch (error) {
5403
+ const errorMessage = `Failed to get recently changed files: ${error.message}`;
5404
+ this.log(errorMessage, "error", { error });
5405
+ throw new Error(errorMessage);
5406
+ }
5407
+ }
5408
+ };
5409
+
5410
+ // src/features/analysis/scm/scmSubmit/index.ts
5411
+ var isValidBranchName = async (branchName) => {
5412
+ const gitService = new GitService(process.cwd());
5413
+ return gitService.isValidBranchName(branchName);
5414
+ };
5415
+
5416
+ // src/features/analysis/scm/scm.ts
5417
+ var SCMLib = class {
5418
+ constructor(url, accessToken, scmOrg) {
5419
+ __publicField(this, "url");
5420
+ __publicField(this, "accessToken");
5421
+ __publicField(this, "scmOrg");
5422
+ this.accessToken = accessToken;
5423
+ this.url = url;
5424
+ this.scmOrg = scmOrg;
5425
+ }
5426
+ async getUrlWithCredentials() {
5427
+ if (!this.url) {
5428
+ console.error("no url for getUrlWithCredentials()");
5429
+ throw new Error("no url");
5430
+ }
5431
+ const trimmedUrl = this.url.trim().replace(/\/$/, "");
5432
+ const accessToken = this.getAccessToken();
5433
+ if (!accessToken) {
5434
+ return trimmedUrl;
5435
+ }
5436
+ if (this.scmLibType === "ADO" /* ADO */) {
5437
+ const { host, protocol, pathname } = new URL(trimmedUrl);
5438
+ return `${protocol}//${accessToken}@${host}${pathname}`;
5439
+ }
5440
+ const finalUrl = this.scmLibType === "GITLAB" /* GITLAB */ ? `${trimmedUrl}.git` : trimmedUrl;
5441
+ const username = await this._getUsernameForAuthUrl();
5442
+ return buildAuthorizedRepoUrl({
5443
+ url: finalUrl,
5444
+ username,
5445
+ password: accessToken
5446
+ });
5447
+ }
5448
+ getAccessToken() {
5449
+ return this.accessToken || "";
5450
+ }
5451
+ getUrl() {
5452
+ return this.url;
5453
+ }
5454
+ getName() {
5455
+ if (!this.url) {
5456
+ return "";
5457
+ }
5458
+ return this.url.split("/").at(-1) || "";
5459
+ }
5460
+ _validateAccessToken() {
5461
+ if (!this.accessToken) {
5462
+ console.error("no access token");
5463
+ throw new Error("no access token");
5464
+ }
5465
+ }
5466
+ static async getIsValidBranchName(branchName) {
5467
+ return isValidBranchName(branchName);
5468
+ }
5469
+ _validateAccessTokenAndUrl() {
5470
+ this._validateAccessToken();
5471
+ this._validateUrl();
5472
+ }
5473
+ _validateUrl() {
5474
+ if (!this.url) {
5475
+ console.error("no url");
5476
+ throw new InvalidRepoUrlError("no url");
5477
+ }
5478
+ }
5479
+ };
5480
+
5481
+ // src/features/analysis/scm/ado/AdoSCMLib.ts
5482
+ async function initAdoSdk(params) {
5483
+ const { url, accessToken, scmOrg } = params;
5484
+ const adoClientParams = await getAdoClientParams({
5485
+ tokenOrg: scmOrg,
5486
+ accessToken,
5487
+ url
5488
+ });
5489
+ return getAdoSdk(adoClientParams);
5490
+ }
5491
+ var AdoSCMLib = class extends SCMLib {
5492
+ constructor(url, accessToken, scmOrg) {
5493
+ super(url, accessToken, scmOrg);
5494
+ __publicField(this, "_adoSdkPromise");
5495
+ this._adoSdkPromise = initAdoSdk({ accessToken, url, scmOrg });
5496
+ }
5497
+ async getAdoSdk() {
4992
5498
  if (!this._adoSdkPromise) {
4993
5499
  console.error("ado sdk was not initialized");
4994
5500
  throw new InvalidAccessTokenError("ado sdk was not initialized");
@@ -6127,14 +6633,14 @@ function getGithubSdk(params = {}) {
6127
6633
  };
6128
6634
  },
6129
6635
  async getGithubBlameRanges(params2) {
6130
- const { ref, gitHubUrl, path: path12 } = params2;
6636
+ const { ref, gitHubUrl, path: path13 } = params2;
6131
6637
  const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
6132
6638
  const res = await octokit.graphql(
6133
6639
  GET_BLAME_DOCUMENT,
6134
6640
  {
6135
6641
  owner,
6136
6642
  repo,
6137
- path: path12,
6643
+ path: path13,
6138
6644
  ref
6139
6645
  }
6140
6646
  );
@@ -6440,11 +6946,11 @@ var GithubSCMLib = class extends SCMLib {
6440
6946
  markdownComment: comment
6441
6947
  });
6442
6948
  }
6443
- async getRepoBlameRanges(ref, path12) {
6949
+ async getRepoBlameRanges(ref, path13) {
6444
6950
  this._validateUrl();
6445
6951
  return await this.githubSdk.getGithubBlameRanges({
6446
6952
  ref,
6447
- path: path12,
6953
+ path: path13,
6448
6954
  gitHubUrl: this.url
6449
6955
  });
6450
6956
  }
@@ -6846,13 +7352,13 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
6846
7352
  const { organization, repoName, projectPath } = parsingResult;
6847
7353
  return { owner: organization, repo: repoName, projectPath };
6848
7354
  }
6849
- async function getGitlabBlameRanges({ ref, gitlabUrl, path: path12 }, options) {
7355
+ async function getGitlabBlameRanges({ ref, gitlabUrl, path: path13 }, options) {
6850
7356
  const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
6851
7357
  const api2 = getGitBeaker({
6852
7358
  url: gitlabUrl,
6853
7359
  gitlabAuthToken: options?.gitlabAuthToken
6854
7360
  });
6855
- const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path12, ref);
7361
+ const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path13, ref);
6856
7362
  let lineNumber = 1;
6857
7363
  return resp.filter((range) => range.lines).map((range) => {
6858
7364
  const oldLineNumber = lineNumber;
@@ -7028,10 +7534,10 @@ var GitlabSCMLib = class extends SCMLib {
7028
7534
  markdownComment: comment
7029
7535
  });
7030
7536
  }
7031
- async getRepoBlameRanges(ref, path12) {
7537
+ async getRepoBlameRanges(ref, path13) {
7032
7538
  this._validateUrl();
7033
7539
  return await getGitlabBlameRanges(
7034
- { ref, path: path12, gitlabUrl: this.url },
7540
+ { ref, path: path13, gitlabUrl: this.url },
7035
7541
  {
7036
7542
  url: this.url,
7037
7543
  gitlabAuthToken: this.accessToken
@@ -7234,13 +7740,13 @@ __export(utils_exports, {
7234
7740
  });
7235
7741
 
7236
7742
  // src/utils/dirname.ts
7237
- import path from "path";
7743
+ import path3 from "path";
7238
7744
  import { fileURLToPath } from "url";
7239
7745
  function getDirName() {
7240
- return path.dirname(fileURLToPath(import.meta.url));
7746
+ return path3.dirname(fileURLToPath(import.meta.url));
7241
7747
  }
7242
7748
  function getTopLevelDirName(fullPath) {
7243
- return path.parse(fullPath).name;
7749
+ return path3.parse(fullPath).name;
7244
7750
  }
7245
7751
 
7246
7752
  // src/utils/keypress.ts
@@ -7303,15 +7809,15 @@ function Spinner({ ci = false } = {}) {
7303
7809
  }
7304
7810
 
7305
7811
  // src/utils/check_node_version.ts
7306
- import fs2 from "fs";
7307
- import path2 from "path";
7812
+ import fs3 from "fs";
7813
+ import path4 from "path";
7308
7814
  import semver from "semver";
7309
7815
  function getPackageJson() {
7310
- let manifestPath = path2.join(getDirName(), "../package.json");
7311
- if (!fs2.existsSync(manifestPath)) {
7312
- manifestPath = path2.join(getDirName(), "../../package.json");
7816
+ let manifestPath = path4.join(getDirName(), "../package.json");
7817
+ if (!fs3.existsSync(manifestPath)) {
7818
+ manifestPath = path4.join(getDirName(), "../../package.json");
7313
7819
  }
7314
- return JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
7820
+ return JSON.parse(fs3.readFileSync(manifestPath, "utf8"));
7315
7821
  }
7316
7822
  var packageJson = getPackageJson();
7317
7823
  if (!semver.satisfies(process.version, packageJson.engines.node)) {
@@ -7354,12 +7860,12 @@ async function convertFprToSarif(inputFilePath, outputFilePath, codePathPatterns
7354
7860
  unsafeCleanup: true
7355
7861
  });
7356
7862
  try {
7357
- const auditFvdlPath = path3.join(tmpObj.name, "audit.fvdl");
7863
+ const auditFvdlPath = path5.join(tmpObj.name, "audit.fvdl");
7358
7864
  await zipIn.extract("audit.fvdl", auditFvdlPath);
7359
7865
  const auditFvdlSaxParser = initSaxParser(auditFvdlPath);
7360
7866
  const vulnerabilityParser = new VulnerabilityParser(
7361
7867
  auditFvdlSaxParser.parser,
7362
- path3.join(tmpObj.name, "vulns.json")
7868
+ path5.join(tmpObj.name, "vulns.json")
7363
7869
  );
7364
7870
  const unifiedNodePoolParser = new UnifiedNodePoolParser(
7365
7871
  auditFvdlSaxParser.parser
@@ -7370,14 +7876,14 @@ async function convertFprToSarif(inputFilePath, outputFilePath, codePathPatterns
7370
7876
  let auditMetadataParser = null;
7371
7877
  await auditFvdlSaxParser.parse();
7372
7878
  if ("audit.xml" in zipInEntries) {
7373
- const auditXmlPath = path3.join(tmpObj.name, "audit.xml");
7879
+ const auditXmlPath = path5.join(tmpObj.name, "audit.xml");
7374
7880
  await zipIn.extract("audit.xml", auditXmlPath);
7375
7881
  const auditXmlSaxParser = initSaxParser(auditXmlPath);
7376
7882
  auditMetadataParser = new AuditMetadataParser(auditXmlSaxParser.parser);
7377
7883
  await auditXmlSaxParser.parse();
7378
7884
  }
7379
7885
  await zipIn.close();
7380
- const writer = fs3.createWriteStream(outputFilePath);
7886
+ const writer = fs4.createWriteStream(outputFilePath);
7381
7887
  writer.write(`{
7382
7888
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
7383
7889
  "version": "2.1.0",
@@ -7482,15 +7988,15 @@ function fortifyNodesToSarifLocations(nodes, unifiedNodePoolParser) {
7482
7988
  import chalk2 from "chalk";
7483
7989
 
7484
7990
  // src/constants.ts
7485
- import path4 from "path";
7991
+ import path6 from "path";
7486
7992
  import { fileURLToPath as fileURLToPath2 } from "url";
7487
7993
  import chalk from "chalk";
7488
7994
  import Debug4 from "debug";
7489
7995
  import * as dotenv from "dotenv";
7490
7996
  import { z as z24 } from "zod";
7491
7997
  var debug4 = Debug4("mobbdev:constants");
7492
- var __dirname = path4.dirname(fileURLToPath2(import.meta.url));
7493
- dotenv.config({ path: path4.join(__dirname, "../.env") });
7998
+ var __dirname = path6.dirname(fileURLToPath2(import.meta.url));
7999
+ dotenv.config({ path: path6.join(__dirname, "../.env") });
7494
8000
  var scmFriendlyText = {
7495
8001
  ["Ado" /* Ado */]: "Azure DevOps",
7496
8002
  ["Bitbucket" /* Bitbucket */]: "Bitbucket",
@@ -7711,7 +8217,7 @@ function convertToSarifBuilder(args) {
7711
8217
  ).help().demandOption(["input-file-path", "input-file-format", "output-file-path"]);
7712
8218
  }
7713
8219
  async function validateConvertToSarifOptions(args) {
7714
- if (!fs4.existsSync(args.inputFilePath)) {
8220
+ if (!fs5.existsSync(args.inputFilePath)) {
7715
8221
  throw new CliError(
7716
8222
  "\nError: --input-file-path flag should point to an existing file"
7717
8223
  );
@@ -7737,16 +8243,16 @@ import chalk10 from "chalk";
7737
8243
  import yargs from "yargs/yargs";
7738
8244
 
7739
8245
  // src/args/commands/analyze.ts
7740
- import fs7 from "fs";
8246
+ import fs8 from "fs";
7741
8247
 
7742
8248
  // src/commands/index.ts
7743
8249
  import crypto from "crypto";
7744
8250
  import os from "os";
7745
8251
 
7746
8252
  // src/features/analysis/index.ts
7747
- import fs6 from "fs";
8253
+ import fs7 from "fs";
7748
8254
  import fsPromises from "fs/promises";
7749
- import path7 from "path";
8255
+ import path9 from "path";
7750
8256
  import { env as env2 } from "process";
7751
8257
  import { pipeline } from "stream/promises";
7752
8258
  import chalk5 from "chalk";
@@ -8030,7 +8536,7 @@ async function postIssueComment(params) {
8030
8536
  fpDescription
8031
8537
  } = params;
8032
8538
  const {
8033
- path: path12,
8539
+ path: path13,
8034
8540
  startLine,
8035
8541
  vulnerabilityReportIssue: {
8036
8542
  vulnerabilityReportIssueTags,
@@ -8045,7 +8551,7 @@ async function postIssueComment(params) {
8045
8551
  Refresh the page in order to see the changes.`,
8046
8552
  pull_number: pullRequest,
8047
8553
  commit_id: commitSha,
8048
- path: path12,
8554
+ path: path13,
8049
8555
  line: startLine
8050
8556
  });
8051
8557
  const commentId = commentRes.data.id;
@@ -8079,7 +8585,7 @@ async function postFixComment(params) {
8079
8585
  scanner
8080
8586
  } = params;
8081
8587
  const {
8082
- path: path12,
8588
+ path: path13,
8083
8589
  startLine,
8084
8590
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
8085
8591
  vulnerabilityReportIssueId
@@ -8097,7 +8603,7 @@ async function postFixComment(params) {
8097
8603
  Refresh the page in order to see the changes.`,
8098
8604
  pull_number: pullRequest,
8099
8605
  commit_id: commitSha,
8100
- path: path12,
8606
+ path: path13,
8101
8607
  line: startLine
8102
8608
  });
8103
8609
  const commentId = commentRes.data.id;
@@ -8376,54 +8882,37 @@ async function handleAutoPr(params) {
8376
8882
 
8377
8883
  // src/features/analysis/git.ts
8378
8884
  import Debug10 from "debug";
8379
- import { simpleGit as simpleGit2 } from "simple-git";
8380
8885
  var debug10 = Debug10("mobbdev:git");
8381
- var GIT_NOT_INITIALIZED_ERROR_MESSAGE = "not a git repository";
8382
8886
  async function getGitInfo(srcDirPath) {
8383
8887
  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 = "";
8888
+ const gitService = new GitService(srcDirPath);
8392
8889
  try {
8393
- repoUrl = (await git.getConfig("remote.origin.url")).value || "";
8394
- hash = await git.revparse(["HEAD"]) || "";
8395
- reference = await git.revparse(["--abbrev-ref", "HEAD"]) || "";
8890
+ const validationResult = await gitService.validateRepository();
8891
+ if (!validationResult.isValid) {
8892
+ debug10("folder is not a git repo");
8893
+ return {
8894
+ success: false,
8895
+ hash: void 0,
8896
+ reference: void 0,
8897
+ repoUrl: void 0
8898
+ };
8899
+ }
8900
+ const gitInfo = await gitService.getGitInfo();
8901
+ return {
8902
+ success: true,
8903
+ ...gitInfo
8904
+ };
8396
8905
  } catch (e) {
8397
8906
  if (e instanceof Error) {
8398
8907
  debug10("failed to run git %o", e);
8399
8908
  if (e.message.includes(" spawn ")) {
8400
8909
  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
8910
  } else {
8410
8911
  throw e;
8411
8912
  }
8412
8913
  }
8413
8914
  throw e;
8414
8915
  }
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
8916
  }
8428
8917
 
8429
8918
  // src/features/analysis/graphql/gql.ts
@@ -8439,6 +8928,7 @@ import Debug11 from "debug";
8439
8928
  import { createClient } from "graphql-ws";
8440
8929
  import { HttpsProxyAgent } from "https-proxy-agent";
8441
8930
  import WebSocket from "ws";
8931
+ var DEFAULT_API_URL = "https://api.mobb.ai/v1/graphql";
8442
8932
  var debug11 = Debug11("mobbdev:subscribe");
8443
8933
  var SUBSCRIPTION_TIMEOUT_MS = 30 * 60 * 1e3;
8444
8934
  function createWSClient(options) {
@@ -8470,10 +8960,11 @@ function subscribe(query, variables, callback, wsClientOptions) {
8470
8960
  return new Promise((resolve, reject) => {
8471
8961
  let timer = null;
8472
8962
  const { timeoutInMs = SUBSCRIPTION_TIMEOUT_MS } = wsClientOptions;
8963
+ const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
8473
8964
  const client = createWSClient({
8474
8965
  ...wsClientOptions,
8475
8966
  websocket: WebSocket,
8476
- url: API_URL.replace("http", "ws")
8967
+ url: API_URL2.replace("http", "ws")
8477
8968
  });
8478
8969
  const unsubscribe = client.subscribe(
8479
8970
  { query, variables },
@@ -8936,13 +9427,13 @@ var GQLClient = class {
8936
9427
  };
8937
9428
 
8938
9429
  // src/features/analysis/pack.ts
8939
- import fs5 from "fs";
8940
- import path5 from "path";
9430
+ import fs6 from "fs";
9431
+ import path7 from "path";
8941
9432
  import AdmZip from "adm-zip";
8942
9433
  import Debug13 from "debug";
8943
9434
  import { globby } from "globby";
8944
- import { isBinary } from "istextorbinary";
8945
- import { simpleGit as simpleGit3 } from "simple-git";
9435
+ import { isBinary as isBinary2 } from "istextorbinary";
9436
+ import { simpleGit as simpleGit2 } from "simple-git";
8946
9437
  import { parseStringPromise } from "xml2js";
8947
9438
  import { z as z28 } from "zod";
8948
9439
  var debug13 = Debug13("mobbdev:pack");
@@ -8971,7 +9462,7 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
8971
9462
  debug13("pack folder %s", srcDirPath);
8972
9463
  let git = void 0;
8973
9464
  try {
8974
- git = simpleGit3({
9465
+ git = simpleGit2({
8975
9466
  baseDir: srcDirPath,
8976
9467
  maxConcurrentProcesses: 1,
8977
9468
  trimmed: true
@@ -9003,23 +9494,23 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
9003
9494
  const zip = new AdmZip();
9004
9495
  debug13("compressing files");
9005
9496
  for (const filepath of filepaths) {
9006
- const absFilepath = path5.join(srcDirPath, filepath.toString());
9497
+ const absFilepath = path7.join(srcDirPath, filepath.toString());
9007
9498
  if (!isIncludeAllFiles) {
9008
9499
  vulnFiles = vulnFiles.concat(getManifestFilesSuffixes());
9009
9500
  if (!endsWithAny(
9010
- absFilepath.toString().replaceAll(path5.win32.sep, path5.posix.sep),
9501
+ absFilepath.toString().replaceAll(path7.win32.sep, path7.posix.sep),
9011
9502
  vulnFiles
9012
9503
  )) {
9013
9504
  debug13("ignoring %s because it is not a vulnerability file", filepath);
9014
9505
  continue;
9015
9506
  }
9016
9507
  }
9017
- if (fs5.lstatSync(absFilepath).size > MAX_FILE_SIZE) {
9508
+ if (fs6.lstatSync(absFilepath).size > MAX_FILE_SIZE) {
9018
9509
  debug13("ignoring %s because the size is > 5MB", filepath);
9019
9510
  continue;
9020
9511
  }
9021
- const data = git ? await git.showBuffer([`HEAD:./${filepath}`]) : fs5.readFileSync(absFilepath);
9022
- if (isBinary(null, data)) {
9512
+ const data = git ? await git.showBuffer([`HEAD:./${filepath}`]) : fs6.readFileSync(absFilepath);
9513
+ if (isBinary2(null, data)) {
9023
9514
  debug13("ignoring %s because is seems to be a binary file", filepath);
9024
9515
  continue;
9025
9516
  }
@@ -9139,16 +9630,16 @@ function createSpawn({ args, processPath, name, cwd }, options) {
9139
9630
  return createChildProcess({ childProcess: child, name }, options);
9140
9631
  }
9141
9632
  function createChildProcess({ childProcess, name }, options) {
9142
- const debug21 = Debug14(`mobbdev:${name}`);
9633
+ const debug20 = Debug14(`mobbdev:${name}`);
9143
9634
  const { display } = options;
9144
9635
  return new Promise((resolve, reject) => {
9145
9636
  let out = "";
9146
9637
  const onData = (chunk) => {
9147
- debug21(`chunk received from ${name} std ${chunk}`);
9638
+ debug20(`chunk received from ${name} std ${chunk}`);
9148
9639
  out += chunk;
9149
9640
  };
9150
9641
  if (!childProcess?.stdout || !childProcess?.stderr) {
9151
- debug21(`unable to fork ${name}`);
9642
+ debug20(`unable to fork ${name}`);
9152
9643
  reject(new Error(`unable to fork ${name}`));
9153
9644
  }
9154
9645
  childProcess.stdout?.on("data", onData);
@@ -9158,11 +9649,11 @@ function createChildProcess({ childProcess, name }, options) {
9158
9649
  childProcess.stderr?.pipe(process2.stderr);
9159
9650
  }
9160
9651
  childProcess.on("exit", (code) => {
9161
- debug21(`${name} exit code ${code}`);
9652
+ debug20(`${name} exit code ${code}`);
9162
9653
  resolve({ message: out, code });
9163
9654
  });
9164
9655
  childProcess.on("error", (err) => {
9165
- debug21(`${name} error %o`, err);
9656
+ debug20(`${name} error %o`, err);
9166
9657
  reject(err);
9167
9658
  });
9168
9659
  });
@@ -9174,12 +9665,12 @@ import Debug15 from "debug";
9174
9665
  import { existsSync } from "fs";
9175
9666
  import { createSpinner as createSpinner2 } from "nanospinner";
9176
9667
  import { type } from "os";
9177
- import path6 from "path";
9668
+ import path8 from "path";
9178
9669
  var debug14 = Debug15("mobbdev:checkmarx");
9179
9670
  var require2 = createRequire(import.meta.url);
9180
9671
  var getCheckmarxPath = () => {
9181
- const os2 = type();
9182
- const cxFileName = os2 === "Windows_NT" ? "cx.exe" : "cx";
9672
+ const os3 = type();
9673
+ const cxFileName = os3 === "Windows_NT" ? "cx.exe" : "cx";
9183
9674
  try {
9184
9675
  return require2.resolve(`.bin/${cxFileName}`);
9185
9676
  } catch (e) {
@@ -9234,9 +9725,9 @@ async function getCheckmarxReport({ reportPath, repositoryRoot, branch, projectN
9234
9725
  await startCheckmarxConfigationPrompt();
9235
9726
  await validateCheckamxCredentials();
9236
9727
  }
9237
- const extension = path6.extname(reportPath);
9238
- const filePath = path6.dirname(reportPath);
9239
- const fileName = path6.basename(reportPath, extension);
9728
+ const extension = path8.extname(reportPath);
9729
+ const filePath = path8.dirname(reportPath);
9730
+ const fileName = path8.basename(reportPath, extension);
9240
9731
  const checkmarxCommandArgs = getCheckmarxCommandArgs({
9241
9732
  repoPath: repositoryRoot,
9242
9733
  branch,
@@ -9305,8 +9796,8 @@ async function forkSnyk(args, { display }) {
9305
9796
  }
9306
9797
  async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
9307
9798
  debug15("get snyk report start %s %s", reportPath, repoRoot);
9308
- const config4 = await forkSnyk(["config"], { display: false });
9309
- const { message: configMessage } = config4;
9799
+ const config5 = await forkSnyk(["config"], { display: false });
9800
+ const { message: configMessage } = config5;
9310
9801
  if (!configMessage.includes("api: ")) {
9311
9802
  const snykLoginSpinner = createSpinner3().start();
9312
9803
  if (!skipPrompts) {
@@ -9318,7 +9809,7 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
9318
9809
  snykLoginSpinner.update({
9319
9810
  text: "\u{1F513} Waiting for Snyk login to complete"
9320
9811
  });
9321
- debug15("no token in the config %s", config4);
9812
+ debug15("no token in the config %s", config5);
9322
9813
  await forkSnyk(["auth"], { display: true });
9323
9814
  snykLoginSpinner.success({ text: "\u{1F513} Login to Snyk Successful" });
9324
9815
  }
@@ -9355,8 +9846,14 @@ async function uploadFile({
9355
9846
  file,
9356
9847
  url,
9357
9848
  uploadKey,
9358
- uploadFields
9849
+ uploadFields,
9850
+ logger: logger2
9359
9851
  }) {
9852
+ const logInfo2 = logger2 || ((_message, _data) => {
9853
+ });
9854
+ logInfo2(`FileUpload: upload file start ${url}`);
9855
+ logInfo2(`FileUpload: upload fields`, uploadFields);
9856
+ logInfo2(`FileUpload: upload key ${uploadKey}`);
9360
9857
  debug16("upload file start %s", url);
9361
9858
  debug16("upload fields %o", uploadFields);
9362
9859
  debug16("upload key %s", uploadKey);
@@ -9369,9 +9866,11 @@ async function uploadFile({
9369
9866
  }
9370
9867
  if (typeof file === "string") {
9371
9868
  debug16("upload file from path %s", file);
9869
+ logInfo2(`FileUpload: upload file from path ${file}`);
9372
9870
  form.append("file", await fileFrom(file));
9373
9871
  } else {
9374
9872
  debug16("upload file from buffer");
9873
+ logInfo2(`FileUpload: upload file from buffer`);
9375
9874
  form.append("file", new File([file], "file"));
9376
9875
  }
9377
9876
  const agent = getProxyAgent(url);
@@ -9382,9 +9881,11 @@ async function uploadFile({
9382
9881
  });
9383
9882
  if (!response.ok) {
9384
9883
  debug16("error from S3 %s %s", response.body, response.status);
9884
+ logInfo2(`FileUpload: error from S3 ${response.body} ${response.status}`);
9385
9885
  throw new Error(`Failed to upload the file: ${response.status}`);
9386
9886
  }
9387
9887
  debug16("upload file done");
9888
+ logInfo2(`FileUpload: upload file done`);
9388
9889
  }
9389
9890
 
9390
9891
  // src/features/analysis/index.ts
@@ -9419,7 +9920,7 @@ async function downloadRepo({
9419
9920
  const { createSpinner: createSpinner5 } = Spinner2({ ci });
9420
9921
  const repoSpinner = createSpinner5("\u{1F4BE} Downloading Repo").start();
9421
9922
  debug17("download repo %s %s %s", repoUrl, dirname);
9422
- const zipFilePath = path7.join(dirname, "repo.zip");
9923
+ const zipFilePath = path9.join(dirname, "repo.zip");
9423
9924
  debug17("download URL: %s auth headers: %o", downloadUrl, authHeaders);
9424
9925
  const response = await fetch4(downloadUrl, {
9425
9926
  method: "GET",
@@ -9432,19 +9933,19 @@ async function downloadRepo({
9432
9933
  repoSpinner.error({ text: "\u{1F4BE} Repo download failed" });
9433
9934
  throw new Error(`Can't access ${chalk5.bold(repoUrl)}`);
9434
9935
  }
9435
- const fileWriterStream = fs6.createWriteStream(zipFilePath);
9936
+ const fileWriterStream = fs7.createWriteStream(zipFilePath);
9436
9937
  if (!response.body) {
9437
9938
  throw new Error("Response body is empty");
9438
9939
  }
9439
9940
  await pipeline(response.body, fileWriterStream);
9440
9941
  await extract(zipFilePath, { dir: dirname });
9441
- const repoRoot = fs6.readdirSync(dirname, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name)[0];
9942
+ const repoRoot = fs7.readdirSync(dirname, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name)[0];
9442
9943
  if (!repoRoot) {
9443
9944
  throw new Error("Repo root not found");
9444
9945
  }
9445
9946
  debug17("repo root %s", repoRoot);
9446
9947
  repoSpinner.success({ text: "\u{1F4BE} Repo downloaded successfully" });
9447
- return path7.join(dirname, repoRoot);
9948
+ return path9.join(dirname, repoRoot);
9448
9949
  }
9449
9950
  var getReportUrl = ({
9450
9951
  organizationId,
@@ -9554,7 +10055,7 @@ async function getReport(params, { skipPrompts }) {
9554
10055
  authHeaders: scm.getAuthHeaders(),
9555
10056
  downloadUrl
9556
10057
  });
9557
- const reportPath = path7.join(dirname, REPORT_DEFAULT_FILE_NAME);
10058
+ const reportPath = path9.join(dirname, REPORT_DEFAULT_FILE_NAME);
9558
10059
  switch (scanner) {
9559
10060
  case "snyk":
9560
10061
  await getSnykReport(reportPath, repositoryRoot, { skipPrompts });
@@ -9930,7 +10431,7 @@ async function _zipAndUploadRepo({
9930
10431
  const zippingSpinner = createSpinner4("\u{1F4E6} Zipping repo").start();
9931
10432
  let zipBuffer;
9932
10433
  let gitInfo = { success: false };
9933
- if (srcFileStatus.isFile() && path7.extname(srcPath).toLowerCase() === ".fpr") {
10434
+ if (srcFileStatus.isFile() && path9.extname(srcPath).toLowerCase() === ".fpr") {
9934
10435
  zipBuffer = await repackFpr(srcPath);
9935
10436
  } else {
9936
10437
  gitInfo = await getGitInfo(srcPath);
@@ -10277,7 +10778,7 @@ import chalk8 from "chalk";
10277
10778
 
10278
10779
  // src/args/validation.ts
10279
10780
  import chalk7 from "chalk";
10280
- import path8 from "path";
10781
+ import path10 from "path";
10281
10782
  import { z as z30 } from "zod";
10282
10783
  function throwRepoUrlErrorMessage({
10283
10784
  error,
@@ -10321,7 +10822,7 @@ function validateRepoUrl(args) {
10321
10822
  }
10322
10823
  var supportExtensions = [".json", ".xml", ".fpr", ".sarif"];
10323
10824
  function validateReportFileFormat(reportFile) {
10324
- if (!supportExtensions.includes(path8.extname(reportFile))) {
10825
+ if (!supportExtensions.includes(path10.extname(reportFile))) {
10325
10826
  throw new CliError(
10326
10827
  `
10327
10828
  ${chalk7.bold(
@@ -10363,7 +10864,7 @@ function analyzeBuilder(yargs2) {
10363
10864
  ).help();
10364
10865
  }
10365
10866
  function validateAnalyzeOptions(argv) {
10366
- if (argv.f && !fs7.existsSync(argv.f)) {
10867
+ if (argv.f && !fs8.existsSync(argv.f)) {
10367
10868
  throw new CliError(`
10368
10869
  Can't access ${chalk8.bold(argv.f)}`);
10369
10870
  }
@@ -10416,6 +10917,7 @@ import {
10416
10917
 
10417
10918
  // src/mcp/Logger.ts
10418
10919
  var logglerUrl = "http://localhost:4444/log";
10920
+ var isTestEnvironment = process.env["VITEST"] || process.env["TEST"];
10419
10921
  var Logger = class {
10420
10922
  log(message, level = "info", data) {
10421
10923
  const logMessage = {
@@ -10424,13 +10926,15 @@ var Logger = class {
10424
10926
  message,
10425
10927
  data
10426
10928
  };
10427
- try {
10428
- fetch(logglerUrl, {
10429
- method: "POST",
10430
- headers: { "Content-Type": "application/json" },
10431
- body: JSON.stringify(logMessage)
10432
- });
10433
- } catch (error) {
10929
+ if (!isTestEnvironment) {
10930
+ try {
10931
+ fetch(logglerUrl, {
10932
+ method: "POST",
10933
+ headers: { "Content-Type": "application/json" },
10934
+ body: JSON.stringify(logMessage)
10935
+ });
10936
+ } catch (error) {
10937
+ }
10434
10938
  }
10435
10939
  }
10436
10940
  };
@@ -10439,775 +10943,150 @@ var logInfo = (message, data) => logger.log(message, "info", data);
10439
10943
  var logError = (message, data) => logger.log(message, "error", data);
10440
10944
  var logWarn = (message, data) => logger.log(message, "warn", data);
10441
10945
  var logDebug = (message, data) => logger.log(message, "debug", data);
10442
- var Logger_default = logger.log;
10946
+ var log = logger.log;
10443
10947
 
10444
- // src/mcp/core/ToolRegistry.ts
10445
- var ToolRegistry = class {
10446
- constructor() {
10447
- __publicField(this, "tools", /* @__PURE__ */ new Map());
10948
+ // src/mcp/services/McpGQLClient.ts
10949
+ import crypto2 from "crypto";
10950
+ import os2 from "os";
10951
+ import Configstore3 from "configstore";
10952
+ import { GraphQLClient as GraphQLClient2 } from "graphql-request";
10953
+ import open4 from "open";
10954
+ import { v4 as uuidv42 } from "uuid";
10955
+
10956
+ // src/mcp/constants.ts
10957
+ var DEFAULT_API_URL2 = "https://api.mobb.ai/v1/graphql";
10958
+ var API_KEY_HEADER_NAME2 = "x-mobb-key";
10959
+
10960
+ // src/mcp/tools/fixVulnerabilities/errors/VulnerabilityFixErrors.ts
10961
+ var ApiConnectionError = class extends Error {
10962
+ constructor(message = "Failed to connect to the API") {
10963
+ super(message);
10964
+ this.name = "ApiConnectionError";
10448
10965
  }
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
- });
10966
+ };
10967
+ var CliLoginError = class extends Error {
10968
+ constructor(message = "CLI login failed") {
10969
+ super(message);
10970
+ this.name = "CliLoginError";
10460
10971
  }
10461
- getTool(name) {
10462
- return this.tools.get(name);
10972
+ };
10973
+ var AuthenticationError = class extends Error {
10974
+ constructor(message = "Authentication failed") {
10975
+ super(message);
10976
+ this.name = "AuthenticationError";
10463
10977
  }
10464
- getAllTools() {
10465
- return Array.from(this.tools.values()).map((tool) => tool.definition);
10978
+ };
10979
+ var NoFilesError = class extends Error {
10980
+ constructor(message = "No files to fix") {
10981
+ super(message);
10982
+ this.name = "NoFilesError";
10466
10983
  }
10467
- getToolNames() {
10468
- return Array.from(this.tools.keys());
10984
+ };
10985
+ var GqlClientError = class extends Error {
10986
+ constructor(message = "GraphQL client not initialized") {
10987
+ super(message);
10988
+ this.name = "GqlClientError";
10469
10989
  }
10470
- hasTool(name) {
10471
- return this.tools.has(name);
10990
+ };
10991
+ var FileProcessingError = class extends Error {
10992
+ constructor(message) {
10993
+ super(message);
10994
+ this.name = "FileProcessingError";
10472
10995
  }
10473
- getToolCount() {
10474
- return this.tools.size;
10996
+ };
10997
+ var ReportInitializationError = class extends Error {
10998
+ constructor(message) {
10999
+ super(message);
11000
+ this.name = "ReportInitializationError";
10475
11001
  }
10476
11002
  };
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);
11003
+ var FileUploadError = class extends Error {
11004
+ constructor(message) {
11005
+ super(message);
11006
+ this.name = "FileUploadError";
10499
11007
  }
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");
11008
+ };
11009
+ var ScanError = class extends Error {
11010
+ constructor(message) {
11011
+ super(message);
11012
+ this.name = "ScanError";
10533
11013
  }
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
- });
11014
+ };
11015
+ var FailedToGetApiTokenError = class extends Error {
11016
+ constructor(message) {
11017
+ super(message);
11018
+ this.name = "FailedToGetApiTokenError";
10543
11019
  }
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);
11020
+ };
11021
+
11022
+ // src/mcp/services/McpGQLClient.ts
11023
+ var LOGIN_MAX_WAIT2 = 10 * 1e3;
11024
+ var LOGIN_CHECK_DELAY2 = 1 * 1e3;
11025
+ var config4 = new Configstore3(packageJson.name, { apiToken: "" });
11026
+ var BROWSER_COOLDOWN_MS = 5e3;
11027
+ var lastBrowserOpenTime = 0;
11028
+ var McpGQLClient = class {
11029
+ constructor(args) {
11030
+ __publicField(this, "client");
11031
+ __publicField(this, "clientSdk");
11032
+ __publicField(this, "_auth");
11033
+ this._auth = args;
11034
+ const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL2;
11035
+ this.client = new GraphQLClient2(API_URL2, {
11036
+ headers: args.type === "apiKey" ? { [API_KEY_HEADER_NAME2]: args.apiKey || "" } : {
11037
+ Authorization: `Bearer ${args.token}`
11038
+ },
11039
+ requestMiddleware: (request) => {
11040
+ const requestId = uuidv42();
11041
+ return {
11042
+ ...request,
11043
+ headers: {
11044
+ ...request.headers,
11045
+ "x-hasura-request-id": requestId
10573
11046
  }
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
- }
11047
+ };
10591
11048
  }
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
11049
  });
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");
11050
+ this.clientSdk = getSdk(this.client);
10619
11051
  }
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 });
11052
+ getErrorContext() {
11053
+ return {
11054
+ endpoint: process.env["API_URL"] || DEFAULT_API_URL2,
11055
+ apiKey: this._auth.type === "apiKey" ? this._auth.apiKey : "",
11056
+ headers: {
11057
+ [API_KEY_HEADER_NAME2]: this._auth.type === "apiKey" ? "[REDACTED]" : "undefined",
11058
+ "x-hasura-request-id": "[DYNAMIC]"
11059
+ }
11060
+ };
10632
11061
  }
10633
- /**
10634
- * Validates that the path is a valid git repository
10635
- */
10636
- async validateRepository() {
10637
- logDebug("Validating git repository");
11062
+ async verifyConnection() {
10638
11063
  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 };
11064
+ logDebug("GraphQL: Calling Me query for connection verification");
11065
+ const result = await this.clientSdk.Me();
11066
+ logInfo("GraphQL: Me query successful", { result });
11067
+ return true;
11068
+ } catch (e) {
11069
+ if (e?.toString().includes("FetchError")) {
11070
+ logError("verify connection failed %o", e);
11071
+ return false;
10644
11072
  }
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
11073
  }
11074
+ return true;
10652
11075
  }
10653
- /**
10654
- * Gets the current git status and returns changed files
10655
- */
10656
- async getChangedFiles() {
10657
- logDebug("Getting git status");
11076
+ async uploadS3BucketInfo() {
10658
11077
  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
- );
11078
+ logDebug("GraphQL: Calling uploadS3BucketInfo mutation");
11079
+ const result = await this.clientSdk.uploadS3BucketInfo({
11080
+ fileName: "report.json"
10677
11081
  });
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
11082
+ logInfo("GraphQL: uploadS3BucketInfo successful", { result });
11083
+ return result;
11084
+ } catch (e) {
11085
+ logError("GraphQL: uploadS3BucketInfo failed", {
11086
+ error: e,
11087
+ ...this.getErrorContext()
10685
11088
  });
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;
11089
+ throw e;
11211
11090
  }
11212
11091
  }
11213
11092
  async getAnalysis(analysisId) {
@@ -11253,7 +11132,7 @@ var McpGQLClient = class {
11253
11132
  params: params.subscribeToAnalysisParams
11254
11133
  });
11255
11134
  const { callbackStates } = params;
11256
- const result = await subscribe2(
11135
+ const result = await subscribe(
11257
11136
  GetAnalysisSubscriptionDocument,
11258
11137
  params.subscribeToAnalysisParams,
11259
11138
  async (resolve, reject, data) => {
@@ -11267,92 +11146,489 @@ var McpGQLClient = class {
11267
11146
  reject(new Error(`Analysis failed with id: ${data.analysis?.id}`));
11268
11147
  return;
11269
11148
  }
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);
11149
+ if (callbackStates.includes(data.analysis?.state)) {
11150
+ logInfo("GraphQL: Analysis state matches callback states", {
11151
+ analysisId: data.analysis.id,
11152
+ state: data.analysis.state,
11153
+ callbackStates
11154
+ });
11155
+ await params.callback(data.analysis.id);
11156
+ resolve(data);
11157
+ }
11158
+ },
11159
+ this._auth.type === "apiKey" ? {
11160
+ apiKey: this._auth.apiKey,
11161
+ type: "apiKey",
11162
+ timeoutInMs: params.timeoutInMs
11163
+ } : {
11164
+ token: this._auth.token,
11165
+ type: "token",
11166
+ timeoutInMs: params.timeoutInMs
11167
+ }
11168
+ );
11169
+ logInfo("GraphQL: GetAnalysis subscription completed", { result });
11170
+ return result;
11171
+ } catch (e) {
11172
+ logError("GraphQL: GetAnalysis subscription failed", {
11173
+ error: e,
11174
+ params: params.subscribeToAnalysisParams,
11175
+ ...this.getErrorContext()
11176
+ });
11177
+ throw e;
11178
+ }
11179
+ }
11180
+ async getProjectId() {
11181
+ try {
11182
+ const projectName = "MCP Scans";
11183
+ logDebug("GraphQL: Calling getOrgAndProjectId query", { projectName });
11184
+ const getOrgAndProjectIdResult = await this.clientSdk.getOrgAndProjectId({
11185
+ filters: {},
11186
+ limit: 1
11187
+ });
11188
+ logInfo("GraphQL: getOrgAndProjectId successful", {
11189
+ result: getOrgAndProjectIdResult
11190
+ });
11191
+ const [organizationToOrganizationRole] = getOrgAndProjectIdResult.organization_to_organization_role;
11192
+ if (!organizationToOrganizationRole) {
11193
+ throw new Error("Organization not found");
11194
+ }
11195
+ const { organization: org } = organizationToOrganizationRole;
11196
+ const project = projectName ? org?.projects.find((project2) => project2.name === projectName) ?? null : org?.projects[0];
11197
+ if (project?.id) {
11198
+ logInfo("GraphQL: Found existing project", {
11199
+ projectId: project.id,
11200
+ projectName
11201
+ });
11202
+ return project.id;
11203
+ }
11204
+ logDebug("GraphQL: Project not found, creating new project", {
11205
+ organizationId: org.id,
11206
+ projectName
11207
+ });
11208
+ const createdProject = await this.clientSdk.CreateProject({
11209
+ organizationId: org.id,
11210
+ projectName
11211
+ });
11212
+ logInfo("GraphQL: CreateProject successful", { result: createdProject });
11213
+ return createdProject.createProject.projectId;
11214
+ } catch (e) {
11215
+ logError("GraphQL: getProjectId failed", {
11216
+ error: e,
11217
+ ...this.getErrorContext()
11218
+ });
11219
+ throw e;
11220
+ }
11221
+ }
11222
+ async getReportFixes(fixReportId) {
11223
+ try {
11224
+ logDebug("GraphQL: Calling GetMCPFixes query", { fixReportId });
11225
+ const res = await this.clientSdk.GetMCPFixes({ fixReportId });
11226
+ logInfo("GraphQL: GetMCPFixes successful", {
11227
+ result: res,
11228
+ fixCount: res.fix?.length || 0
11229
+ });
11230
+ return res.fix;
11231
+ } catch (e) {
11232
+ logError("GraphQL: GetMCPFixes failed", {
11233
+ error: e,
11234
+ fixReportId,
11235
+ ...this.getErrorContext()
11236
+ });
11237
+ throw e;
11238
+ }
11239
+ }
11240
+ async getUserInfo() {
11241
+ const { me } = await this.clientSdk.Me();
11242
+ return me;
11243
+ }
11244
+ async verifyToken() {
11245
+ logDebug("verifying token");
11246
+ try {
11247
+ await this.clientSdk.CreateCommunityUser();
11248
+ const info = await this.getUserInfo();
11249
+ logDebug("token verified");
11250
+ return info?.email || true;
11251
+ } catch (e) {
11252
+ logError("verify token failed");
11253
+ return false;
11254
+ }
11255
+ }
11256
+ async createCliLogin(variables) {
11257
+ try {
11258
+ const res = await this.clientSdk.CreateCliLogin(variables, {
11259
+ // We may have outdated API key in the config storage. Avoid using it for the login request.
11260
+ [API_KEY_HEADER_NAME2]: ""
11261
+ });
11262
+ const loginId = res.insert_cli_login_one?.id || "";
11263
+ if (!loginId) {
11264
+ logError("create cli login failed - no login ID returned");
11265
+ return "";
11266
+ }
11267
+ return loginId;
11268
+ } catch (e) {
11269
+ logError("create cli login failed", { error: e });
11270
+ return "";
11271
+ }
11272
+ }
11273
+ async getEncryptedApiToken(variables) {
11274
+ try {
11275
+ const res = await this.clientSdk.GetEncryptedApiToken(variables, {
11276
+ // We may have outdated API key in the config storage. Avoid using it for the login request.
11277
+ [API_KEY_HEADER_NAME2]: ""
11278
+ });
11279
+ return res?.cli_login_by_pk?.encryptedApiToken || null;
11280
+ } catch (e) {
11281
+ logError("get encrypted api token failed", { error: e });
11282
+ return null;
11283
+ }
11284
+ }
11285
+ };
11286
+ async function openBrowser(url) {
11287
+ const now = Date.now();
11288
+ if (!process.env["TEST"] && now - lastBrowserOpenTime < BROWSER_COOLDOWN_MS) {
11289
+ logDebug(`browser cooldown active, skipping open for ${url}`);
11290
+ return;
11291
+ }
11292
+ logDebug(`opening browser url ${url}`);
11293
+ await open4(url);
11294
+ lastBrowserOpenTime = now;
11295
+ }
11296
+ async function getMcpGQLClient() {
11297
+ logDebug("getting config", { apiToken: config4.get("apiToken") });
11298
+ const inGqlClient = new McpGQLClient({
11299
+ apiKey: config4.get("apiToken") || process.env["API_KEY"] || "",
11300
+ type: "apiKey"
11301
+ });
11302
+ const isConnected = await inGqlClient.verifyConnection();
11303
+ if (!isConnected) {
11304
+ throw new ApiConnectionError("Error: failed to connect to the API");
11305
+ }
11306
+ const userVerify = await inGqlClient.verifyToken();
11307
+ if (userVerify) {
11308
+ return inGqlClient;
11309
+ }
11310
+ const { publicKey, privateKey } = crypto2.generateKeyPairSync("rsa", {
11311
+ modulusLength: 2048
11312
+ });
11313
+ logDebug("creating cli login");
11314
+ const loginId = await inGqlClient.createCliLogin({
11315
+ publicKey: publicKey.export({ format: "pem", type: "pkcs1" }).toString()
11316
+ });
11317
+ if (!loginId) {
11318
+ throw new CliLoginError("Error: createCliLogin failed");
11319
+ }
11320
+ logDebug(`cli login created ${loginId}`);
11321
+ const webLoginUrl2 = `${WEB_APP_URL}/cli-login`;
11322
+ const browserUrl = `${webLoginUrl2}/${loginId}?hostname=${os2.hostname()}`;
11323
+ logDebug(`opening browser url ${browserUrl}`);
11324
+ await openBrowser(browserUrl);
11325
+ logDebug(`waiting for login to complete`);
11326
+ let newApiToken = null;
11327
+ for (let i = 0; i < LOGIN_MAX_WAIT2 / LOGIN_CHECK_DELAY2; i++) {
11328
+ const encryptedApiToken = await inGqlClient.getEncryptedApiToken({
11329
+ loginId
11330
+ });
11331
+ if (encryptedApiToken) {
11332
+ logDebug("encrypted API token received");
11333
+ newApiToken = crypto2.privateDecrypt(privateKey, Buffer.from(encryptedApiToken, "base64")).toString("utf-8");
11334
+ logDebug("API token decrypted");
11335
+ break;
11336
+ }
11337
+ await sleep(LOGIN_CHECK_DELAY2);
11338
+ }
11339
+ if (!newApiToken) {
11340
+ throw new FailedToGetApiTokenError(
11341
+ "Error: failed to get encrypted api token"
11342
+ );
11343
+ }
11344
+ const newGqlClient = new McpGQLClient({ apiKey: newApiToken, type: "apiKey" });
11345
+ const loginSuccess = await newGqlClient.verifyToken();
11346
+ if (loginSuccess) {
11347
+ logDebug("set api token %s", newApiToken);
11348
+ config4.set("apiToken", newApiToken);
11349
+ } else {
11350
+ throw new AuthenticationError("Something went wrong, API token is invalid.");
11351
+ }
11352
+ return newGqlClient;
11353
+ }
11354
+
11355
+ // src/mcp/core/ToolRegistry.ts
11356
+ var ToolRegistry = class {
11357
+ constructor() {
11358
+ __publicField(this, "tools", /* @__PURE__ */ new Map());
11359
+ }
11360
+ registerTool(tool) {
11361
+ if (this.tools.has(tool.name)) {
11362
+ logWarn(`Tool ${tool.name} is already registered, overwriting`, {
11363
+ toolName: tool.name
11364
+ });
11365
+ }
11366
+ this.tools.set(tool.name, tool);
11367
+ logDebug(`Tool registered: ${tool.name}`, {
11368
+ toolName: tool.name,
11369
+ description: tool.definition.description
11370
+ });
11371
+ }
11372
+ getTool(name) {
11373
+ return this.tools.get(name);
11374
+ }
11375
+ getAllTools() {
11376
+ return Array.from(this.tools.values()).map((tool) => tool.definition);
11377
+ }
11378
+ getToolNames() {
11379
+ return Array.from(this.tools.keys());
11380
+ }
11381
+ hasTool(name) {
11382
+ return this.tools.has(name);
11383
+ }
11384
+ getToolCount() {
11385
+ return this.tools.size;
11386
+ }
11387
+ };
11388
+
11389
+ // src/mcp/core/McpServer.ts
11390
+ var McpServer = class {
11391
+ constructor(config5) {
11392
+ __publicField(this, "server");
11393
+ __publicField(this, "toolRegistry");
11394
+ __publicField(this, "isEventHandlersSetup", false);
11395
+ this.server = new Server(
11396
+ {
11397
+ name: config5.name,
11398
+ version: config5.version
11399
+ },
11400
+ {
11401
+ capabilities: {
11402
+ tools: {}
11403
+ }
11404
+ }
11405
+ );
11406
+ this.toolRegistry = new ToolRegistry();
11407
+ this.setupHandlers();
11408
+ this.setupProcessEventHandlers();
11409
+ logInfo("MCP server instance created", config5);
11410
+ }
11411
+ setupProcessEventHandlers() {
11412
+ if (this.isEventHandlersSetup) {
11413
+ logDebug("Process event handlers already setup, skipping");
11414
+ return;
11415
+ }
11416
+ const signals = {
11417
+ SIGINT: "MCP server interrupted",
11418
+ SIGTERM: "MCP server terminated",
11419
+ exit: "MCP server exiting",
11420
+ uncaughtException: "Uncaught exception in MCP server",
11421
+ unhandledRejection: "Unhandled promise rejection in MCP server",
11422
+ warning: "Warning in MCP server"
11423
+ };
11424
+ Object.entries(signals).forEach(([signal, message]) => {
11425
+ process.on(
11426
+ signal,
11427
+ (error) => {
11428
+ if (error && signal !== "exit") {
11429
+ logError(`${message}`, { error, signal });
11430
+ } else {
11431
+ logInfo(message, { signal });
11432
+ }
11433
+ if (signal === "SIGINT" || signal === "SIGTERM") {
11434
+ process.exit(0);
11435
+ }
11436
+ if (signal === "uncaughtException") {
11437
+ process.exit(1);
11278
11438
  }
11279
- },
11280
- {
11281
- apiKey: this.apiKey,
11282
- type: "apiKey",
11283
- timeoutInMs: params.timeoutInMs
11284
11439
  }
11285
11440
  );
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;
11441
+ });
11442
+ this.isEventHandlersSetup = true;
11443
+ logDebug("Process event handlers registered");
11444
+ }
11445
+ createShutdownPromise() {
11446
+ return new Promise((resolve) => {
11447
+ const cleanup = () => {
11448
+ logInfo("Process shutdown initiated");
11449
+ resolve();
11450
+ };
11451
+ process.once("SIGINT", cleanup);
11452
+ process.once("SIGTERM", cleanup);
11453
+ });
11454
+ }
11455
+ async handleListToolsRequest(request) {
11456
+ logInfo("Received list_tools request", { params: request.params });
11457
+ try {
11458
+ await getMcpGQLClient();
11459
+ } catch (error) {
11460
+ logError("Failed to get MCPGQLClient", { error });
11461
+ const authError = new Error(
11462
+ "Please authorize this client by visiting: https://mobb.ai"
11463
+ );
11464
+ authError.name = "AuthorizationRequired";
11465
+ throw authError;
11295
11466
  }
11467
+ const tools = this.toolRegistry.getAllTools();
11468
+ return {
11469
+ tools: tools.map((tool) => ({
11470
+ name: tool.name,
11471
+ display_name: tool.name,
11472
+ description: tool.description || "",
11473
+ inputSchema: {
11474
+ type: "object",
11475
+ properties: tool.inputSchema.properties || {},
11476
+ required: tool.inputSchema.required || []
11477
+ }
11478
+ }))
11479
+ };
11296
11480
  }
11297
- async getProjectId() {
11481
+ async handleCallToolRequest(request) {
11482
+ const { name, arguments: args } = request.params;
11483
+ logInfo(`Received call tool request for ${name}`, { name, args });
11298
11484
  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
11485
+ const tool = this.toolRegistry.getTool(name);
11486
+ if (!tool) {
11487
+ const errorMsg = `Unknown tool: ${name}`;
11488
+ logWarn(errorMsg, {
11489
+ name,
11490
+ availableTools: this.toolRegistry.getToolNames()
11318
11491
  });
11319
- return project.id;
11492
+ throw new Error(errorMsg);
11320
11493
  }
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
11494
+ logDebug(`Executing tool: ${name}`, { args });
11495
+ const response = await tool.execute(args);
11496
+ const serializedResponse = JSON.parse(JSON.stringify(response));
11497
+ logInfo(`Tool ${name} executed successfully`, {
11498
+ responseType: typeof response,
11499
+ hasContent: !!serializedResponse.content
11328
11500
  });
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()
11501
+ return serializedResponse;
11502
+ } catch (error) {
11503
+ const errorMessage = error instanceof Error ? error.message : String(error);
11504
+ logError(`Error executing tool ${name}: ${errorMessage}`, {
11505
+ error,
11506
+ toolName: name,
11507
+ args
11335
11508
  });
11336
- throw e;
11509
+ throw error;
11337
11510
  }
11338
11511
  }
11339
- async getReportFixes(fixReportId) {
11512
+ setupHandlers() {
11513
+ this.server.setRequestHandler(
11514
+ ListToolsRequestSchema,
11515
+ (request) => this.handleListToolsRequest(request)
11516
+ );
11517
+ this.server.setRequestHandler(
11518
+ CallToolRequestSchema,
11519
+ (request) => this.handleCallToolRequest(request)
11520
+ );
11521
+ logDebug("MCP server handlers registered");
11522
+ }
11523
+ registerTool(tool) {
11524
+ this.toolRegistry.registerTool({
11525
+ name: tool.name,
11526
+ definition: tool.definition,
11527
+ execute: tool.execute
11528
+ });
11529
+ logDebug(`Tool registered: ${tool.name}`);
11530
+ }
11531
+ async start() {
11340
11532
  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;
11533
+ logDebug("Starting MCP server");
11534
+ const transport = new StdioServerTransport();
11535
+ await this.server.connect(transport);
11536
+ logInfo("MCP server is running on stdin/stdout");
11537
+ process.stdin.resume();
11538
+ await this.createShutdownPromise();
11539
+ await this.stop();
11540
+ } catch (error) {
11541
+ logError("Failed to start MCP server", { error });
11542
+ throw error;
11543
+ }
11544
+ }
11545
+ async stop() {
11546
+ logInfo("MCP server shutting down");
11547
+ }
11548
+ };
11549
+
11550
+ // src/mcp/services/PathValidation.ts
11551
+ import fs9 from "fs";
11552
+ import path11 from "path";
11553
+ var PathValidation = class {
11554
+ /**
11555
+ * Validates a path for MCP usage - combines security and existence checks
11556
+ */
11557
+ async validatePath(inputPath) {
11558
+ logDebug("Validating MCP path", { inputPath });
11559
+ if (inputPath.includes("..")) {
11560
+ const error = `Path contains path traversal patterns: ${inputPath}`;
11561
+ logError(error);
11562
+ return { isValid: false, error };
11563
+ }
11564
+ const normalizedPath = path11.normalize(inputPath);
11565
+ if (normalizedPath.includes("..")) {
11566
+ const error = `Normalized path contains path traversal patterns: ${inputPath}`;
11567
+ logError(error);
11568
+ return { isValid: false, error };
11569
+ }
11570
+ const decodedPath = decodeURIComponent(inputPath);
11571
+ if (decodedPath.includes("..") || decodedPath !== inputPath) {
11572
+ const error = `Path contains encoded traversal attempts: ${inputPath}`;
11573
+ logError(error);
11574
+ return { isValid: false, error };
11575
+ }
11576
+ if (inputPath.includes("\0") || inputPath.includes("\0")) {
11577
+ const error = `Path contains dangerous characters: ${inputPath}`;
11578
+ logError(error);
11579
+ return { isValid: false, error };
11580
+ }
11581
+ logDebug("Path validation successful", { inputPath });
11582
+ logDebug("Checking path existence", { inputPath });
11583
+ try {
11584
+ await fs9.promises.access(inputPath);
11585
+ logDebug("Path exists and is accessible", { inputPath });
11586
+ return { isValid: true };
11587
+ } catch (error) {
11588
+ const errorMessage = `Path does not exist or is not accessible: ${inputPath}`;
11589
+ logError(errorMessage, { error });
11590
+ return { isValid: false, error: errorMessage };
11591
+ }
11592
+ }
11593
+ };
11594
+
11595
+ // src/mcp/services/FilePacking.ts
11596
+ import fs10 from "fs";
11597
+ import path12 from "path";
11598
+ import AdmZip2 from "adm-zip";
11599
+ var MAX_FILE_SIZE2 = 1024 * 1024 * 5;
11600
+ var FilePacking = class {
11601
+ async packFiles(sourceDirectoryPath, filesToPack) {
11602
+ logInfo(`FilePacking: packing files from ${sourceDirectoryPath}`);
11603
+ const zip = new AdmZip2();
11604
+ let packedFilesCount = 0;
11605
+ logInfo("FilePacking: compressing files");
11606
+ for (const filepath of filesToPack) {
11607
+ const absoluteFilepath = path12.join(sourceDirectoryPath, filepath);
11608
+ if (!FileUtils.shouldPackFile(absoluteFilepath, MAX_FILE_SIZE2)) {
11609
+ logInfo(
11610
+ `FilePacking: ignoring ${filepath} because it is excluded or invalid`
11611
+ );
11612
+ continue;
11613
+ }
11614
+ let data;
11615
+ try {
11616
+ data = fs10.readFileSync(absoluteFilepath);
11617
+ } catch (fsError) {
11618
+ logInfo(
11619
+ `FilePacking: failed to read ${filepath} from filesystem: ${fsError}`
11620
+ );
11621
+ continue;
11622
+ }
11623
+ zip.addFile(filepath, data);
11624
+ packedFilesCount++;
11355
11625
  }
11626
+ const zipBuffer = zip.toBuffer();
11627
+ logInfo(
11628
+ `FilePacking: read ${packedFilesCount} source files. total size: ${zipBuffer.length} bytes`
11629
+ );
11630
+ logInfo("FilePacking: Files packed successfully");
11631
+ return zipBuffer;
11356
11632
  }
11357
11633
  };
11358
11634
 
@@ -11480,6 +11756,85 @@ ${fix2.patch || "No patch available"}
11480
11756
  - If any patch fails, continue with the others and report issues at the end
11481
11757
  `;
11482
11758
  };
11759
+ var failedToConnectToApiPrompt = `# CONNECTION ERROR: FAILED TO REACH MOBB API
11760
+
11761
+ ## ANALYSIS SUMMARY
11762
+ - **Status:** \u274C Failed
11763
+ - **Issue Type:** Connection Error
11764
+ - **Error Details:** Unable to establish connection to the Mobb API service
11765
+
11766
+ ## TROUBLESHOOTING STEPS FOR THE USER
11767
+
11768
+ The Mobb security scanning service is currently not reachable. This may be due to:
11769
+
11770
+ 1. **Missing or invalid authentication credentials**
11771
+ - Ensure the \`API_KEY\` environment variable is properly set with your valid Mobb authentication token
11772
+ - Example: \`export API_KEY=your_mobb_api_key_here\`
11773
+
11774
+ 2. **Incorrect API endpoint configuration**
11775
+ - Check if the \`API_URL\` environment variable needs to be set to the correct Mobb service endpoint
11776
+ - Example: \`export API_URL=https://api.mobb.ai/graphql\`
11777
+
11778
+ 3. **Network connectivity issues**
11779
+ - Verify your internet connection is working properly
11780
+ - Check if any firewall or proxy settings might be blocking the connection
11781
+
11782
+ 4. **Service outage**
11783
+ - The Mobb service might be temporarily unavailable
11784
+ - Please try again later or check the Mobb status page
11785
+
11786
+ ## NEXT STEPS
11787
+
11788
+ Please resolve the connection issue using the steps above and try running the security scan again.
11789
+
11790
+ For additional assistance, please:
11791
+ - Visit the Mobb documentation at https://docs.mobb.ai
11792
+ - Contact Mobb support at support@mobb.ai
11793
+
11794
+ `;
11795
+ var failedToAuthenticatePrompt = `# AUTHENTICATION ERROR: MOBB LOGIN REQUIRED
11796
+
11797
+ ## ANALYSIS SUMMARY
11798
+ - **Status:** \u274C Failed
11799
+ - **Issue Type:** Authentication Error
11800
+ - **Error Details:** Unable to authenticate with the Mobb service
11801
+
11802
+ ## AUTHENTICATION REQUIRED
11803
+
11804
+ The Mobb security scanning service requires authentication before it can analyze your code for vulnerabilities. You need to:
11805
+
11806
+ 1. **Login and authorize access to Mobb**
11807
+ - A browser window should have opened to complete the authentication process
11808
+ - If no browser window opened, please run the command again
11809
+
11810
+ 2. **Create a Mobb account if you don't have one**
11811
+ - If you don't already have a Mobb account, you'll need to sign up
11812
+ - Visit https://app.mobb.ai/auth/signup to create your free account
11813
+ - Use your work email for easier team collaboration
11814
+
11815
+ 3. **Authorization flow**
11816
+ - After logging in, you'll be asked to authorize the CLI tool
11817
+ - This creates a secure token that allows the CLI to access Mobb services
11818
+ - You only need to do this once per device
11819
+
11820
+ ## TROUBLESHOOTING
11821
+
11822
+ If you're experiencing issues with authentication:
11823
+
11824
+ - Ensure you have an active internet connection
11825
+ - Check that you can access https://app.mobb.ai in your browser
11826
+ - Try running the command again with the \`--debug\` flag for more detailed output
11827
+ - Make sure your browser isn't blocking pop-ups from the authentication window
11828
+
11829
+ ## NEXT STEPS
11830
+
11831
+ Please complete the authentication process and try running the security scan again.
11832
+
11833
+ For additional assistance, please:
11834
+ - Visit the Mobb documentation at https://docs.mobb.ai/cli/authentication
11835
+ - Contact Mobb support at support@mobb.ai
11836
+
11837
+ `;
11483
11838
 
11484
11839
  // src/mcp/tools/fixVulnerabilities/VulnerabilityFixService.ts
11485
11840
  var VUL_REPORT_DIGEST_TIMEOUT_MS2 = 1e3 * 60 * 5;
@@ -11487,15 +11842,12 @@ var VulnerabilityFixService = class {
11487
11842
  constructor() {
11488
11843
  __publicField(this, "gqlClient");
11489
11844
  __publicField(this, "filePacking");
11490
- __publicField(this, "fileUpload");
11491
11845
  this.filePacking = new FilePacking();
11492
- this.fileUpload = new FileUpload();
11493
11846
  }
11494
11847
  async processVulnerabilities(fileList, repositoryPath) {
11495
11848
  try {
11496
11849
  this.validateFiles(fileList);
11497
- const apiKey = this.validateApiKey();
11498
- this.gqlClient = await this.initializeGqlClient(apiKey);
11850
+ this.gqlClient = await this.initializeGqlClient();
11499
11851
  const repoUploadInfo = await this.initializeReport();
11500
11852
  const zipBuffer = await this.packFiles(fileList, repositoryPath);
11501
11853
  await this.uploadFiles(zipBuffer, repoUploadInfo);
@@ -11507,6 +11859,12 @@ var VulnerabilityFixService = class {
11507
11859
  const fixes = await this.getReportFixes(repoUploadInfo.fixReportId);
11508
11860
  return fixesPrompt(fixes);
11509
11861
  } catch (error) {
11862
+ if (error instanceof ApiConnectionError || error instanceof CliLoginError) {
11863
+ return failedToConnectToApiPrompt;
11864
+ }
11865
+ if (error instanceof AuthenticationError || error instanceof FailedToGetApiTokenError) {
11866
+ return failedToAuthenticatePrompt;
11867
+ }
11510
11868
  const message = error.message;
11511
11869
  logError("Vulnerability processing failed", { error: message });
11512
11870
  throw error;
@@ -11514,30 +11872,22 @@ var VulnerabilityFixService = class {
11514
11872
  }
11515
11873
  validateFiles(fileList) {
11516
11874
  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");
11875
+ throw new NoFilesError();
11524
11876
  }
11525
- return apiKey;
11526
11877
  }
11527
- async initializeGqlClient(apiKey) {
11528
- const gqlClient = new McpGQLClient({
11529
- apiKey,
11530
- type: "apiKey"
11531
- });
11878
+ async initializeGqlClient() {
11879
+ const gqlClient = await getMcpGQLClient();
11532
11880
  const isConnected = await gqlClient.verifyConnection();
11533
11881
  if (!isConnected) {
11534
- throw new Error("Failed to connect to the API. Please check your API_KEY");
11882
+ throw new ApiConnectionError(
11883
+ "Failed to connect to the API. Please check your API_KEY"
11884
+ );
11535
11885
  }
11536
11886
  return gqlClient;
11537
11887
  }
11538
11888
  async initializeReport() {
11539
11889
  if (!this.gqlClient) {
11540
- throw new Error("GraphQL client not initialized");
11890
+ throw new GqlClientError();
11541
11891
  }
11542
11892
  try {
11543
11893
  const {
@@ -11547,7 +11897,9 @@ var VulnerabilityFixService = class {
11547
11897
  return repoUploadInfo;
11548
11898
  } catch (error) {
11549
11899
  const message = error.message;
11550
- throw new Error(`Error initializing report: ${message}`);
11900
+ throw new ReportInitializationError(
11901
+ `Error initializing report: ${message}`
11902
+ );
11551
11903
  }
11552
11904
  }
11553
11905
  async packFiles(fileList, repositoryPath) {
@@ -11560,15 +11912,15 @@ var VulnerabilityFixService = class {
11560
11912
  return zipBuffer;
11561
11913
  } catch (error) {
11562
11914
  const message = error.message;
11563
- throw new Error(`Error packing files: ${message}`);
11915
+ throw new FileProcessingError(`Error packing files: ${message}`);
11564
11916
  }
11565
11917
  }
11566
11918
  async uploadFiles(zipBuffer, repoUploadInfo) {
11567
11919
  if (!repoUploadInfo) {
11568
- throw new Error("Upload info is required");
11920
+ throw new FileUploadError("Upload info is required");
11569
11921
  }
11570
11922
  try {
11571
- await this.fileUpload.uploadFile({
11923
+ await uploadFile({
11572
11924
  file: zipBuffer,
11573
11925
  url: repoUploadInfo.url,
11574
11926
  uploadFields: JSON.parse(repoUploadInfo.uploadFieldsJSON),
@@ -11577,12 +11929,14 @@ var VulnerabilityFixService = class {
11577
11929
  logInfo("File uploaded successfully");
11578
11930
  } catch (error) {
11579
11931
  logError("File upload failed", { error: error.message });
11580
- throw new Error(`Failed to upload the file: ${error.message}`);
11932
+ throw new FileUploadError(
11933
+ `Failed to upload the file: ${error.message}`
11934
+ );
11581
11935
  }
11582
11936
  }
11583
11937
  async getProjectId() {
11584
11938
  if (!this.gqlClient) {
11585
- throw new Error("GraphQL client not initialized");
11939
+ throw new GqlClientError();
11586
11940
  }
11587
11941
  const projectId = await this.gqlClient.getProjectId();
11588
11942
  logInfo("Project ID retrieved", { projectId });
@@ -11590,7 +11944,7 @@ var VulnerabilityFixService = class {
11590
11944
  }
11591
11945
  async runScan(params) {
11592
11946
  if (!this.gqlClient) {
11593
- throw new Error("GraphQL client not initialized");
11947
+ throw new GqlClientError();
11594
11948
  }
11595
11949
  const { fixReportId, projectId } = params;
11596
11950
  logInfo("Starting scan", { fixReportId, projectId });
@@ -11609,7 +11963,7 @@ var VulnerabilityFixService = class {
11609
11963
  logError("Vulnerability report submission failed", {
11610
11964
  response: submitRes
11611
11965
  });
11612
- throw new Error("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
11966
+ throw new ScanError("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
11613
11967
  }
11614
11968
  logInfo("Vulnerability report submitted successfully", {
11615
11969
  analysisId: submitRes.submitVulnerabilityReport.fixReportId
@@ -11628,7 +11982,7 @@ var VulnerabilityFixService = class {
11628
11982
  }
11629
11983
  async getReportFixes(fixReportId) {
11630
11984
  if (!this.gqlClient) {
11631
- throw new Error("GraphQL client not initialized");
11985
+ throw new GqlClientError();
11632
11986
  }
11633
11987
  const fixes = await this.gqlClient.getReportFixes(fixReportId);
11634
11988
  logInfo("Fixes retrieved", { fixCount: fixes.length });
@@ -11665,18 +12019,48 @@ var FixVulnerabilitiesTool = class {
11665
12019
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
11666
12020
  );
11667
12021
  }
11668
- const gitService = new GitService(args.path);
12022
+ const gitService = new GitService(args.path, log);
11669
12023
  const gitValidation = await gitService.validateRepository();
12024
+ let files = [];
11670
12025
  if (!gitValidation.isValid) {
11671
- throw new Error(gitValidation.error || "Git repository validation failed");
12026
+ logDebug(
12027
+ "Git repository validation failed, using all files in the repository",
12028
+ {
12029
+ path: args.path
12030
+ }
12031
+ );
12032
+ files = FileUtils.getLastChangedFiles(args.path);
12033
+ logDebug("Found files in the repository", {
12034
+ files,
12035
+ fileCount: files.length
12036
+ });
12037
+ } else {
12038
+ const gitResult = await gitService.getChangedFiles();
12039
+ files = gitResult.files;
12040
+ if (files.length === 0) {
12041
+ const recentResult = await gitService.getRecentlyChangedFiles();
12042
+ files = recentResult.files;
12043
+ logDebug(
12044
+ "No changes found, using recently changed files from git history",
12045
+ {
12046
+ files,
12047
+ fileCount: files.length,
12048
+ commitsChecked: recentResult.commitCount
12049
+ }
12050
+ );
12051
+ } else {
12052
+ logDebug("Found changed files in the git repository", {
12053
+ files,
12054
+ fileCount: files.length
12055
+ });
12056
+ }
11672
12057
  }
11673
- const gitResult = await gitService.getChangedFiles();
11674
- if (gitResult.files.length === 0) {
12058
+ if (files.length === 0) {
11675
12059
  return {
11676
12060
  content: [
11677
12061
  {
11678
12062
  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."
12063
+ 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
12064
  }
11681
12065
  ]
11682
12066
  };
@@ -11684,7 +12068,7 @@ var FixVulnerabilitiesTool = class {
11684
12068
  try {
11685
12069
  const vulnerabilityFixService = new VulnerabilityFixService();
11686
12070
  const fixResult = await vulnerabilityFixService.processVulnerabilities(
11687
- gitResult.files,
12071
+ files,
11688
12072
  args.path
11689
12073
  );
11690
12074
  const result = {
@@ -11697,7 +12081,7 @@ var FixVulnerabilitiesTool = class {
11697
12081
  };
11698
12082
  logInfo("Tool execution completed successfully", {
11699
12083
  resultLength: fixResult.length,
11700
- fileCount: gitResult.files.length,
12084
+ fileCount: files.length,
11701
12085
  result
11702
12086
  });
11703
12087
  return result;
@@ -11770,7 +12154,7 @@ var mcpHandler = async (_args) => {
11770
12154
  };
11771
12155
 
11772
12156
  // src/args/commands/review.ts
11773
- import fs10 from "fs";
12157
+ import fs11 from "fs";
11774
12158
  import chalk9 from "chalk";
11775
12159
  function reviewBuilder(yargs2) {
11776
12160
  return yargs2.option("f", {
@@ -11807,7 +12191,7 @@ function reviewBuilder(yargs2) {
11807
12191
  ).help();
11808
12192
  }
11809
12193
  function validateReviewOptions(argv) {
11810
- if (!fs10.existsSync(argv.f)) {
12194
+ if (!fs11.existsSync(argv.f)) {
11811
12195
  throw new CliError(`
11812
12196
  Can't access ${chalk9.bold(argv.f)}`);
11813
12197
  }
@@ -11945,13 +12329,13 @@ var parseArgs = async (args) => {
11945
12329
  };
11946
12330
 
11947
12331
  // src/index.ts
11948
- var debug20 = Debug21("mobbdev:index");
12332
+ var debug19 = Debug20("mobbdev:index");
11949
12333
  async function run() {
11950
12334
  return parseArgs(hideBin(process.argv));
11951
12335
  }
11952
12336
  (async () => {
11953
12337
  try {
11954
- debug20("Bugsy CLI v%s running...", packageJson.version);
12338
+ debug19("Bugsy CLI v%s running...", packageJson.version);
11955
12339
  await run();
11956
12340
  process.exit(0);
11957
12341
  } catch (err) {