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.
- package/dist/index.mjs +1440 -1056
- 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
|
|
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
|
|
14
|
+
import fs5 from "fs";
|
|
15
15
|
|
|
16
16
|
// src/commands/convert_to_sarif.ts
|
|
17
|
-
import
|
|
18
|
-
import
|
|
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
|
|
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(`${
|
|
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/
|
|
4908
|
+
// src/features/analysis/scm/git/GitService.ts
|
|
4909
|
+
import * as path2 from "path";
|
|
4896
4910
|
import { simpleGit } from "simple-git";
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
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
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
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
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
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 (
|
|
4931
|
-
|
|
4932
|
-
return `${protocol}//${accessToken}@${host}${pathname}`;
|
|
5089
|
+
if (results.length > 1e5) {
|
|
5090
|
+
return [];
|
|
4933
5091
|
}
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
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
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
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
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
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/
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
}
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
async
|
|
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:
|
|
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:
|
|
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,
|
|
6949
|
+
async getRepoBlameRanges(ref, path13) {
|
|
6444
6950
|
this._validateUrl();
|
|
6445
6951
|
return await this.githubSdk.getGithubBlameRanges({
|
|
6446
6952
|
ref,
|
|
6447
|
-
path:
|
|
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:
|
|
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,
|
|
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,
|
|
7537
|
+
async getRepoBlameRanges(ref, path13) {
|
|
7032
7538
|
this._validateUrl();
|
|
7033
7539
|
return await getGitlabBlameRanges(
|
|
7034
|
-
{ ref, path:
|
|
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
|
|
7743
|
+
import path3 from "path";
|
|
7238
7744
|
import { fileURLToPath } from "url";
|
|
7239
7745
|
function getDirName() {
|
|
7240
|
-
return
|
|
7746
|
+
return path3.dirname(fileURLToPath(import.meta.url));
|
|
7241
7747
|
}
|
|
7242
7748
|
function getTopLevelDirName(fullPath) {
|
|
7243
|
-
return
|
|
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
|
|
7307
|
-
import
|
|
7812
|
+
import fs3 from "fs";
|
|
7813
|
+
import path4 from "path";
|
|
7308
7814
|
import semver from "semver";
|
|
7309
7815
|
function getPackageJson() {
|
|
7310
|
-
let manifestPath =
|
|
7311
|
-
if (!
|
|
7312
|
-
manifestPath =
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
7493
|
-
dotenv.config({ path:
|
|
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 (!
|
|
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
|
|
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
|
|
8253
|
+
import fs7 from "fs";
|
|
7748
8254
|
import fsPromises from "fs/promises";
|
|
7749
|
-
import
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
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:
|
|
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
|
|
8940
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 (
|
|
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}`]) :
|
|
9022
|
-
if (
|
|
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
|
|
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
|
-
|
|
9638
|
+
debug20(`chunk received from ${name} std ${chunk}`);
|
|
9148
9639
|
out += chunk;
|
|
9149
9640
|
};
|
|
9150
9641
|
if (!childProcess?.stdout || !childProcess?.stderr) {
|
|
9151
|
-
|
|
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
|
-
|
|
9652
|
+
debug20(`${name} exit code ${code}`);
|
|
9162
9653
|
resolve({ message: out, code });
|
|
9163
9654
|
});
|
|
9164
9655
|
childProcess.on("error", (err) => {
|
|
9165
|
-
|
|
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
|
|
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
|
|
9182
|
-
const cxFileName =
|
|
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 =
|
|
9238
|
-
const filePath =
|
|
9239
|
-
const fileName =
|
|
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
|
|
9309
|
-
const { message: configMessage } =
|
|
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",
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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() &&
|
|
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
|
|
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(
|
|
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 && !
|
|
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
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
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
|
|
10946
|
+
var log = logger.log;
|
|
10443
10947
|
|
|
10444
|
-
// src/mcp/
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
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
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
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
|
-
|
|
10462
|
-
|
|
10972
|
+
};
|
|
10973
|
+
var AuthenticationError = class extends Error {
|
|
10974
|
+
constructor(message = "Authentication failed") {
|
|
10975
|
+
super(message);
|
|
10976
|
+
this.name = "AuthenticationError";
|
|
10463
10977
|
}
|
|
10464
|
-
|
|
10465
|
-
|
|
10978
|
+
};
|
|
10979
|
+
var NoFilesError = class extends Error {
|
|
10980
|
+
constructor(message = "No files to fix") {
|
|
10981
|
+
super(message);
|
|
10982
|
+
this.name = "NoFilesError";
|
|
10466
10983
|
}
|
|
10467
|
-
|
|
10468
|
-
|
|
10984
|
+
};
|
|
10985
|
+
var GqlClientError = class extends Error {
|
|
10986
|
+
constructor(message = "GraphQL client not initialized") {
|
|
10987
|
+
super(message);
|
|
10988
|
+
this.name = "GqlClientError";
|
|
10469
10989
|
}
|
|
10470
|
-
|
|
10471
|
-
|
|
10990
|
+
};
|
|
10991
|
+
var FileProcessingError = class extends Error {
|
|
10992
|
+
constructor(message) {
|
|
10993
|
+
super(message);
|
|
10994
|
+
this.name = "FileProcessingError";
|
|
10472
10995
|
}
|
|
10473
|
-
|
|
10474
|
-
|
|
10996
|
+
};
|
|
10997
|
+
var ReportInitializationError = class extends Error {
|
|
10998
|
+
constructor(message) {
|
|
10999
|
+
super(message);
|
|
11000
|
+
this.name = "ReportInitializationError";
|
|
10475
11001
|
}
|
|
10476
11002
|
};
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
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
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
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
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
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
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
this.
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
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
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
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
|
-
|
|
10660
|
-
const
|
|
10661
|
-
|
|
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("
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
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
|
|
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
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
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
|
-
|
|
11492
|
+
throw new Error(errorMsg);
|
|
11320
11493
|
}
|
|
11321
|
-
logDebug(
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
}
|
|
11325
|
-
|
|
11326
|
-
|
|
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
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
logError(
|
|
11333
|
-
error
|
|
11334
|
-
|
|
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
|
|
11509
|
+
throw error;
|
|
11337
11510
|
}
|
|
11338
11511
|
}
|
|
11339
|
-
|
|
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("
|
|
11342
|
-
const
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
} catch (
|
|
11349
|
-
logError("
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11354
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
11528
|
-
const gqlClient =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11920
|
+
throw new FileUploadError("Upload info is required");
|
|
11569
11921
|
}
|
|
11570
11922
|
try {
|
|
11571
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
12338
|
+
debug19("Bugsy CLI v%s running...", packageJson.version);
|
|
11955
12339
|
await run();
|
|
11956
12340
|
process.exit(0);
|
|
11957
12341
|
} catch (err) {
|