mobbdev 1.0.127 → 1.0.131
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 +413 -289
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -41,7 +41,7 @@ var init_configs = __esm({
|
|
|
41
41
|
MCP_API_KEY_HEADER_NAME = "x-mobb-key";
|
|
42
42
|
MCP_LOGIN_MAX_WAIT = 2 * 60 * 1e3;
|
|
43
43
|
MCP_LOGIN_CHECK_DELAY = 2 * 1e3;
|
|
44
|
-
MCP_VUL_REPORT_DIGEST_TIMEOUT_MS =
|
|
44
|
+
MCP_VUL_REPORT_DIGEST_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
45
45
|
MCP_MAX_FILE_SIZE = MAX_UPLOAD_FILE_SIZE_MB * 1024 * 1024;
|
|
46
46
|
MCP_PERIODIC_CHECK_INTERVAL = 15 * 60 * 1e3;
|
|
47
47
|
MCP_DEFAULT_MAX_FILES_TO_SCAN = 10;
|
|
@@ -564,46 +564,29 @@ var init_FileUtils = __esm({
|
|
|
564
564
|
}
|
|
565
565
|
// Process directory at repository root level with special handling for excluded root directories
|
|
566
566
|
static async processRootDirectory(dir, excludedRootDirectories) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
567
|
+
const visitedDirs = /* @__PURE__ */ new Set();
|
|
568
|
+
return this.processDirectory(
|
|
569
|
+
dir,
|
|
570
|
+
dir,
|
|
571
|
+
excludedRootDirectories,
|
|
572
|
+
0,
|
|
573
|
+
visitedDirs,
|
|
574
|
+
true
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
// Process directories with tracking to prevent circular symlink recursion
|
|
578
|
+
static async processDirectory(dir, rootDir, excludedRootDirectories = [], depth = 0, visitedDirs = /* @__PURE__ */ new Set(), isRootLevel = false) {
|
|
579
|
+
if (depth > 20) {
|
|
570
580
|
return [];
|
|
571
581
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
try {
|
|
578
|
-
await fsPromises.access(fullPath, fs2.constants.R_OK);
|
|
579
|
-
const stat = await fsPromises.stat(fullPath);
|
|
580
|
-
if (stat.isDirectory()) {
|
|
581
|
-
if (excludedRootDirectories.includes(item)) {
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
filePromises.push(this.processSubdirectory(fullPath, dir, 1));
|
|
585
|
-
} else {
|
|
586
|
-
results.push({
|
|
587
|
-
name: item,
|
|
588
|
-
fullPath,
|
|
589
|
-
relativePath: item,
|
|
590
|
-
time: stat.mtime.getTime(),
|
|
591
|
-
isFile: true
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
} catch {
|
|
595
|
-
continue;
|
|
582
|
+
let canonicalPath;
|
|
583
|
+
try {
|
|
584
|
+
canonicalPath = await fsPromises.realpath(dir);
|
|
585
|
+
if (visitedDirs.has(canonicalPath)) {
|
|
586
|
+
return [];
|
|
596
587
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
for (const subdirResult of subdirResults) {
|
|
600
|
-
results.push(...subdirResult);
|
|
601
|
-
}
|
|
602
|
-
return results;
|
|
603
|
-
}
|
|
604
|
-
// Process subdirectories without applying root exclusions
|
|
605
|
-
static async processSubdirectory(dir, rootDir, depth) {
|
|
606
|
-
if (depth > 20) {
|
|
588
|
+
visitedDirs.add(canonicalPath);
|
|
589
|
+
} catch {
|
|
607
590
|
return [];
|
|
608
591
|
}
|
|
609
592
|
try {
|
|
@@ -620,8 +603,11 @@ var init_FileUtils = __esm({
|
|
|
620
603
|
await fsPromises.access(fullPath, fs2.constants.R_OK);
|
|
621
604
|
const stat = await fsPromises.stat(fullPath);
|
|
622
605
|
if (stat.isDirectory()) {
|
|
606
|
+
if (isRootLevel && excludedRootDirectories.includes(item)) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
623
609
|
filePromises.push(
|
|
624
|
-
this.
|
|
610
|
+
this.processDirectory(fullPath, rootDir, [], depth + 1, visitedDirs)
|
|
625
611
|
);
|
|
626
612
|
} else {
|
|
627
613
|
results.push({
|
|
@@ -700,25 +686,27 @@ var init_GitService = __esm({
|
|
|
700
686
|
this.log = log2 || noopLog;
|
|
701
687
|
this.git = simpleGit(repositoryPath, { binary: "git" });
|
|
702
688
|
this.repositoryPath = repositoryPath;
|
|
703
|
-
this.log("Git service initialized", "debug", {
|
|
689
|
+
this.log("[GitService] Git service initialized", "debug", {
|
|
690
|
+
repositoryPath
|
|
691
|
+
});
|
|
704
692
|
}
|
|
705
693
|
/**
|
|
706
694
|
* Validates that the path is a valid git repository
|
|
707
695
|
*/
|
|
708
696
|
async validateRepository() {
|
|
709
|
-
this.log("Validating git repository", "debug");
|
|
697
|
+
this.log("[GitService] Validating git repository", "debug");
|
|
710
698
|
try {
|
|
711
699
|
const isRepo = await this.git.checkIsRepo();
|
|
712
700
|
if (!isRepo) {
|
|
713
|
-
const error = "Path is not a valid git repository";
|
|
701
|
+
const error = "[GitService] Path is not a valid git repository";
|
|
714
702
|
this.log(error, "error");
|
|
715
703
|
return { isValid: false, error };
|
|
716
704
|
}
|
|
717
|
-
this.log("Git repository validation successful", "debug");
|
|
705
|
+
this.log("[GitService] Git repository validation successful", "debug");
|
|
718
706
|
return { isValid: true };
|
|
719
707
|
} catch (error) {
|
|
720
708
|
const errorMessage = `Failed to verify git repository: ${error.message}`;
|
|
721
|
-
this.log(errorMessage
|
|
709
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
722
710
|
return { isValid: false, error: errorMessage };
|
|
723
711
|
}
|
|
724
712
|
}
|
|
@@ -726,7 +714,7 @@ var init_GitService = __esm({
|
|
|
726
714
|
* Gets the current git status and returns changed files
|
|
727
715
|
*/
|
|
728
716
|
async getChangedFiles() {
|
|
729
|
-
this.log("Getting git status", "debug");
|
|
717
|
+
this.log("[GitService] Getting git status", "debug");
|
|
730
718
|
try {
|
|
731
719
|
const status = await this.git.status();
|
|
732
720
|
const gitRoot = await this.git.revparse(["--show-toplevel"]);
|
|
@@ -750,7 +738,7 @@ var init_GitService = __esm({
|
|
|
750
738
|
path2.join(gitRoot, gitRelativePath)
|
|
751
739
|
);
|
|
752
740
|
});
|
|
753
|
-
this.log("Git status retrieved", "info", {
|
|
741
|
+
this.log("[GitService] Git status retrieved", "info", {
|
|
754
742
|
fileCount: files.length,
|
|
755
743
|
files: files.slice(0, 10),
|
|
756
744
|
// Log first 10 files to avoid spam
|
|
@@ -763,7 +751,7 @@ var init_GitService = __esm({
|
|
|
763
751
|
return { files, deletedFiles, status };
|
|
764
752
|
} catch (error) {
|
|
765
753
|
const errorMessage = `Failed to get git status: ${error.message}`;
|
|
766
|
-
this.log(errorMessage
|
|
754
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
767
755
|
throw new Error(errorMessage);
|
|
768
756
|
}
|
|
769
757
|
}
|
|
@@ -771,7 +759,7 @@ var init_GitService = __esm({
|
|
|
771
759
|
* Gets git repository information including remote URL, current commit hash, and branch name
|
|
772
760
|
*/
|
|
773
761
|
async getGitInfo() {
|
|
774
|
-
this.log("Getting git repository information", "debug");
|
|
762
|
+
this.log("[GitService] Getting git repository information", "debug");
|
|
775
763
|
try {
|
|
776
764
|
const [repoUrl, hash, reference] = await Promise.all([
|
|
777
765
|
this.git.getConfig("remote.origin.url"),
|
|
@@ -788,7 +776,7 @@ var init_GitService = __esm({
|
|
|
788
776
|
"https://github.com/"
|
|
789
777
|
);
|
|
790
778
|
}
|
|
791
|
-
this.log("Git repository information retrieved", "debug", {
|
|
779
|
+
this.log("[GitService] Git repository information retrieved", "debug", {
|
|
792
780
|
repoUrl: normalizedRepoUrl,
|
|
793
781
|
hash,
|
|
794
782
|
reference
|
|
@@ -800,7 +788,7 @@ var init_GitService = __esm({
|
|
|
800
788
|
};
|
|
801
789
|
} catch (error) {
|
|
802
790
|
const errorMessage = `Failed to get git repository information: ${error.message}`;
|
|
803
|
-
this.log(errorMessage
|
|
791
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
804
792
|
throw new Error(errorMessage);
|
|
805
793
|
}
|
|
806
794
|
}
|
|
@@ -808,7 +796,7 @@ var init_GitService = __esm({
|
|
|
808
796
|
* Validates if a branch name is valid according to git's rules
|
|
809
797
|
*/
|
|
810
798
|
async isValidBranchName(branchName) {
|
|
811
|
-
this.log("Validating branch name", "debug", { branchName });
|
|
799
|
+
this.log("[GitService] Validating branch name", "debug", { branchName });
|
|
812
800
|
try {
|
|
813
801
|
const result = await this.git.raw([
|
|
814
802
|
"check-ref-format",
|
|
@@ -816,13 +804,16 @@ var init_GitService = __esm({
|
|
|
816
804
|
branchName
|
|
817
805
|
]);
|
|
818
806
|
const isValid = Boolean(result);
|
|
819
|
-
this.log("Branch name validation result", "debug", {
|
|
807
|
+
this.log("[GitService] Branch name validation result", "debug", {
|
|
820
808
|
branchName,
|
|
821
809
|
isValid
|
|
822
810
|
});
|
|
823
811
|
return isValid;
|
|
824
812
|
} catch (error) {
|
|
825
|
-
this.log("Branch name validation failed", "debug", {
|
|
813
|
+
this.log("[GitService] Branch name validation failed", "debug", {
|
|
814
|
+
branchName,
|
|
815
|
+
error
|
|
816
|
+
});
|
|
826
817
|
return false;
|
|
827
818
|
}
|
|
828
819
|
}
|
|
@@ -830,14 +821,14 @@ var init_GitService = __esm({
|
|
|
830
821
|
* Gets the current branch name
|
|
831
822
|
*/
|
|
832
823
|
async getCurrentBranch() {
|
|
833
|
-
this.log("Getting current branch name", "debug");
|
|
824
|
+
this.log("[GitService] Getting current branch name", "debug");
|
|
834
825
|
try {
|
|
835
826
|
const branch = await this.git.revparse(["--abbrev-ref", "HEAD"]);
|
|
836
|
-
this.log("Current branch retrieved", "debug", { branch });
|
|
827
|
+
this.log("[GitService] Current branch retrieved", "debug", { branch });
|
|
837
828
|
return branch;
|
|
838
829
|
} catch (error) {
|
|
839
830
|
const errorMessage = `Failed to get current branch: ${error.message}`;
|
|
840
|
-
this.log(errorMessage
|
|
831
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
841
832
|
throw new Error(errorMessage);
|
|
842
833
|
}
|
|
843
834
|
}
|
|
@@ -845,14 +836,14 @@ var init_GitService = __esm({
|
|
|
845
836
|
* Gets the current commit hash
|
|
846
837
|
*/
|
|
847
838
|
async getCurrentCommitHash() {
|
|
848
|
-
this.log("Getting current commit hash", "debug");
|
|
839
|
+
this.log("[GitService] Getting current commit hash", "debug");
|
|
849
840
|
try {
|
|
850
841
|
const hash = await this.git.revparse(["HEAD"]);
|
|
851
|
-
this.log("Current commit hash retrieved", "debug", { hash });
|
|
842
|
+
this.log("[GitService] Current commit hash retrieved", "debug", { hash });
|
|
852
843
|
return hash;
|
|
853
844
|
} catch (error) {
|
|
854
845
|
const errorMessage = `Failed to get current commit hash: ${error.message}`;
|
|
855
|
-
this.log(errorMessage
|
|
846
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
856
847
|
throw new Error(errorMessage);
|
|
857
848
|
}
|
|
858
849
|
}
|
|
@@ -860,20 +851,24 @@ var init_GitService = __esm({
|
|
|
860
851
|
* Gets both the current commit hash and current branch name
|
|
861
852
|
*/
|
|
862
853
|
async getCurrentCommitAndBranch() {
|
|
863
|
-
this.log("Getting current commit hash and branch", "debug");
|
|
854
|
+
this.log("[GitService] Getting current commit hash and branch", "debug");
|
|
864
855
|
try {
|
|
865
856
|
const [hash, branch] = await Promise.all([
|
|
866
857
|
this.git.revparse(["HEAD"]),
|
|
867
858
|
this.git.revparse(["--abbrev-ref", "HEAD"])
|
|
868
859
|
]);
|
|
869
|
-
this.log(
|
|
870
|
-
hash,
|
|
871
|
-
|
|
872
|
-
|
|
860
|
+
this.log(
|
|
861
|
+
"[GitService] Current commit hash and branch retrieved",
|
|
862
|
+
"debug",
|
|
863
|
+
{
|
|
864
|
+
hash,
|
|
865
|
+
branch
|
|
866
|
+
}
|
|
867
|
+
);
|
|
873
868
|
return { hash, branch };
|
|
874
869
|
} catch (error) {
|
|
875
870
|
const errorMessage = `Failed to get current commit hash and branch: ${error.message}`;
|
|
876
|
-
this.log(errorMessage
|
|
871
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
877
872
|
return { hash: "", branch: "" };
|
|
878
873
|
}
|
|
879
874
|
}
|
|
@@ -881,7 +876,7 @@ var init_GitService = __esm({
|
|
|
881
876
|
* Gets the remote repository URL
|
|
882
877
|
*/
|
|
883
878
|
async getRemoteUrl() {
|
|
884
|
-
this.log("Getting remote repository URL", "debug");
|
|
879
|
+
this.log("[GitService] Getting remote repository URL", "debug");
|
|
885
880
|
try {
|
|
886
881
|
const remoteUrl = await this.git.getConfig("remote.origin.url");
|
|
887
882
|
const url = remoteUrl.value || "";
|
|
@@ -895,13 +890,13 @@ var init_GitService = __esm({
|
|
|
895
890
|
"https://github.com/"
|
|
896
891
|
);
|
|
897
892
|
}
|
|
898
|
-
this.log("Remote repository URL retrieved", "debug", {
|
|
893
|
+
this.log("[GitService] Remote repository URL retrieved", "debug", {
|
|
899
894
|
url: normalizedUrl
|
|
900
895
|
});
|
|
901
896
|
return normalizedUrl;
|
|
902
897
|
} catch (error) {
|
|
903
898
|
const errorMessage = `Failed to get remote repository URL: ${error.message}`;
|
|
904
|
-
this.log(errorMessage
|
|
899
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
905
900
|
throw new Error(errorMessage);
|
|
906
901
|
}
|
|
907
902
|
}
|
|
@@ -912,7 +907,7 @@ var init_GitService = __esm({
|
|
|
912
907
|
maxFiles = MCP_DEFAULT_MAX_FILES_TO_SCAN
|
|
913
908
|
}) {
|
|
914
909
|
this.log(
|
|
915
|
-
`Getting the ${maxFiles} most recently changed files, starting with current changes`,
|
|
910
|
+
`[GitService] Getting the ${maxFiles} most recently changed files, starting with current changes`,
|
|
916
911
|
"debug"
|
|
917
912
|
);
|
|
918
913
|
try {
|
|
@@ -933,10 +928,14 @@ var init_GitService = __esm({
|
|
|
933
928
|
fileSet.add(file);
|
|
934
929
|
}
|
|
935
930
|
}
|
|
936
|
-
this.log(
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
931
|
+
this.log(
|
|
932
|
+
`[GitService] Added ${fileSet.size} files from current changes`,
|
|
933
|
+
"debug",
|
|
934
|
+
{
|
|
935
|
+
filesFromCurrentChanges: fileSet.size,
|
|
936
|
+
currentChangesTotal: currentChanges.files.length
|
|
937
|
+
}
|
|
938
|
+
);
|
|
940
939
|
const logResult = await this.git.log({
|
|
941
940
|
maxCount: maxFiles * 5,
|
|
942
941
|
// 5 times the max files to scan to ensure we find enough files
|
|
@@ -978,7 +977,7 @@ var init_GitService = __esm({
|
|
|
978
977
|
path2.join(gitRoot, gitRelativePath)
|
|
979
978
|
);
|
|
980
979
|
}
|
|
981
|
-
this.log(`Considering file: ${adjustedPath}`, "debug");
|
|
980
|
+
this.log(`[GitService] Considering file: ${adjustedPath}`, "debug");
|
|
982
981
|
if (!fileSet.has(adjustedPath) && await FileUtils.shouldPackFile(
|
|
983
982
|
path2.join(gitRoot, gitRelativePath)
|
|
984
983
|
) && !adjustedPath.startsWith("..")) {
|
|
@@ -986,13 +985,17 @@ var init_GitService = __esm({
|
|
|
986
985
|
}
|
|
987
986
|
}
|
|
988
987
|
} catch (showError) {
|
|
989
|
-
this.log(
|
|
990
|
-
|
|
991
|
-
|
|
988
|
+
this.log(
|
|
989
|
+
`[GitService] Could not get files for commit ${commit.hash}`,
|
|
990
|
+
"debug",
|
|
991
|
+
{
|
|
992
|
+
error: showError
|
|
993
|
+
}
|
|
994
|
+
);
|
|
992
995
|
}
|
|
993
996
|
}
|
|
994
997
|
const files = Array.from(fileSet);
|
|
995
|
-
this.log("Recently changed files retrieved", "info", {
|
|
998
|
+
this.log("[GitService] Recently changed files retrieved", "info", {
|
|
996
999
|
fileCount: files.length,
|
|
997
1000
|
commitsProcessed,
|
|
998
1001
|
totalCommitsAvailable: logResult.all.length,
|
|
@@ -1008,7 +1011,7 @@ var init_GitService = __esm({
|
|
|
1008
1011
|
};
|
|
1009
1012
|
} catch (error) {
|
|
1010
1013
|
const errorMessage = `Failed to get recently changed files: ${error.message}`;
|
|
1011
|
-
this.log(errorMessage
|
|
1014
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
1012
1015
|
throw new Error(errorMessage);
|
|
1013
1016
|
}
|
|
1014
1017
|
}
|
|
@@ -1066,7 +1069,7 @@ var init_GitService = __esm({
|
|
|
1066
1069
|
* Gets all remote repository URLs (equivalent to 'git remote -v')
|
|
1067
1070
|
*/
|
|
1068
1071
|
async getRepoUrls() {
|
|
1069
|
-
this.log("Getting all remote repository URLs", "debug");
|
|
1072
|
+
this.log("[GitService] Getting all remote repository URLs", "debug");
|
|
1070
1073
|
try {
|
|
1071
1074
|
const remotes = await this.git.remote(["-v"]);
|
|
1072
1075
|
if (!remotes) {
|
|
@@ -1088,13 +1091,13 @@ var init_GitService = __esm({
|
|
|
1088
1091
|
remote.push = normalizedUrl;
|
|
1089
1092
|
}
|
|
1090
1093
|
});
|
|
1091
|
-
this.log("Remote repository URLs retrieved", "debug", {
|
|
1094
|
+
this.log("[GitService] Remote repository URLs retrieved", "debug", {
|
|
1092
1095
|
remotes: remoteMap
|
|
1093
1096
|
});
|
|
1094
1097
|
return remoteMap;
|
|
1095
1098
|
} catch (error) {
|
|
1096
1099
|
const errorMessage = `Failed to get remote repository URLs: ${error.message}`;
|
|
1097
|
-
this.log(errorMessage
|
|
1100
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
1098
1101
|
throw new Error(errorMessage);
|
|
1099
1102
|
}
|
|
1100
1103
|
}
|
|
@@ -1103,7 +1106,7 @@ var init_GitService = __esm({
|
|
|
1103
1106
|
* @returns The contents of the .gitignore file as a string, or null if the file doesn't exist
|
|
1104
1107
|
*/
|
|
1105
1108
|
async getGitignoreContent() {
|
|
1106
|
-
this.log("Getting .gitignore contents", "debug");
|
|
1109
|
+
this.log("[GitService] Getting .gitignore contents", "debug");
|
|
1107
1110
|
try {
|
|
1108
1111
|
let combinedContent = "";
|
|
1109
1112
|
const localGitignorePath = path2.join(this.repositoryPath, ".gitignore");
|
|
@@ -1124,20 +1127,23 @@ ${rootContent}`;
|
|
|
1124
1127
|
}
|
|
1125
1128
|
} catch (rootErr) {
|
|
1126
1129
|
this.log(
|
|
1127
|
-
"Unable to resolve git root while reading .gitignore",
|
|
1130
|
+
"[GitService] Unable to resolve git root while reading .gitignore",
|
|
1128
1131
|
"debug",
|
|
1129
1132
|
{ error: rootErr }
|
|
1130
1133
|
);
|
|
1131
1134
|
}
|
|
1132
1135
|
if (combinedContent.trim() === "") {
|
|
1133
|
-
this.log(".gitignore file not found", "debug");
|
|
1136
|
+
this.log("[GitService] .gitignore file not found", "debug");
|
|
1134
1137
|
return null;
|
|
1135
1138
|
}
|
|
1136
|
-
this.log(
|
|
1139
|
+
this.log(
|
|
1140
|
+
"[GitService] .gitignore contents retrieved successfully",
|
|
1141
|
+
"debug"
|
|
1142
|
+
);
|
|
1137
1143
|
return combinedContent.trimEnd();
|
|
1138
1144
|
} catch (error) {
|
|
1139
1145
|
const errorMessage = `Failed to get .gitignore contents: ${error.message}`;
|
|
1140
|
-
this.log(errorMessage
|
|
1146
|
+
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
1141
1147
|
return null;
|
|
1142
1148
|
}
|
|
1143
1149
|
}
|
|
@@ -2106,7 +2112,7 @@ var DigestVulnerabilityReportDocument = `
|
|
|
2106
2112
|
}
|
|
2107
2113
|
`;
|
|
2108
2114
|
var SubmitVulnerabilityReportDocument = `
|
|
2109
|
-
mutation SubmitVulnerabilityReport($fixReportId: String!, $repoUrl: String!, $reference: String!, $projectId: String!, $scanSource: String!, $sha: String, $experimentalEnabled: Boolean, $vulnerabilityReportFileName: String, $pullRequest: Int, $isFullScan: Boolean) {
|
|
2115
|
+
mutation SubmitVulnerabilityReport($fixReportId: String!, $repoUrl: String!, $reference: String!, $projectId: String!, $scanSource: String!, $sha: String, $experimentalEnabled: Boolean, $vulnerabilityReportFileName: String, $pullRequest: Int, $isFullScan: Boolean, $scanContext: String!, $fileCount: Int) {
|
|
2110
2116
|
submitVulnerabilityReport(
|
|
2111
2117
|
fixReportId: $fixReportId
|
|
2112
2118
|
repoUrl: $repoUrl
|
|
@@ -2118,6 +2124,8 @@ var SubmitVulnerabilityReportDocument = `
|
|
|
2118
2124
|
projectId: $projectId
|
|
2119
2125
|
vulnerabilityReportFileName: $vulnerabilityReportFileName
|
|
2120
2126
|
scanSource: $scanSource
|
|
2127
|
+
scanContext: $scanContext
|
|
2128
|
+
fileCount: $fileCount
|
|
2121
2129
|
) {
|
|
2122
2130
|
__typename
|
|
2123
2131
|
... on VulnerabilityReport {
|
|
@@ -2260,14 +2268,14 @@ var GetReportFixesDocument = `
|
|
|
2260
2268
|
var GetLatestReportByRepoUrlDocument = `
|
|
2261
2269
|
query GetLatestReportByRepoUrl($repoUrl: String!, $filters: fix_bool_exp = {}, $limit: Int!, $offset: Int!, $currentUserEmail: String!) {
|
|
2262
2270
|
fixReport(
|
|
2263
|
-
where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Finished}}, {vulnerabilityReport: {
|
|
2271
|
+
where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Finished}}, {vulnerabilityReport: {scanSource: {_neq: MCP}}}]}
|
|
2264
2272
|
order_by: {createdOn: desc}
|
|
2265
2273
|
limit: 1
|
|
2266
2274
|
) {
|
|
2267
2275
|
...FixReportSummaryFields
|
|
2268
2276
|
}
|
|
2269
2277
|
expiredReport: fixReport(
|
|
2270
|
-
where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Expired}}, {
|
|
2278
|
+
where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Expired}}, {vulnerabilityReport: {scanSource: {_neq: MCP}}}]}
|
|
2271
2279
|
order_by: {createdOn: desc}
|
|
2272
2280
|
limit: 1
|
|
2273
2281
|
) {
|
|
@@ -4564,9 +4572,9 @@ var FixRatingZ = z7.object({
|
|
|
4564
4572
|
})
|
|
4565
4573
|
});
|
|
4566
4574
|
var IssueSharedStateZ = z7.object({
|
|
4567
|
-
id: z7.string()
|
|
4575
|
+
id: z7.string(),
|
|
4568
4576
|
isArchived: z7.boolean(),
|
|
4569
|
-
ticketIntegrationId: z7.string().
|
|
4577
|
+
ticketIntegrationId: z7.string().nullable(),
|
|
4570
4578
|
ticketIntegrations: z7.array(
|
|
4571
4579
|
z7.object({
|
|
4572
4580
|
url: z7.string()
|
|
@@ -4625,7 +4633,6 @@ var FixPartsForFixScreenZ = FixQueryZ.merge(
|
|
|
4625
4633
|
z7.object({
|
|
4626
4634
|
vendorIssueId: z7.string(),
|
|
4627
4635
|
issueType: z7.string(),
|
|
4628
|
-
issueLanguage: z7.string(),
|
|
4629
4636
|
sharedState: IssueSharedStateZ
|
|
4630
4637
|
})
|
|
4631
4638
|
)
|
|
@@ -4640,7 +4647,6 @@ import { z as z8 } from "zod";
|
|
|
4640
4647
|
var FixPageFixReportZ = z8.object({
|
|
4641
4648
|
id: z8.string().uuid(),
|
|
4642
4649
|
analysisUrl: z8.string(),
|
|
4643
|
-
expirationOn: z8.string(),
|
|
4644
4650
|
createdOn: z8.string(),
|
|
4645
4651
|
state: z8.nativeEnum(Fix_Report_State_Enum),
|
|
4646
4652
|
repo: z8.object({
|
|
@@ -4651,48 +4657,12 @@ var FixPageFixReportZ = z8.object({
|
|
|
4651
4657
|
isKnownBranch: z8.boolean().nullable()
|
|
4652
4658
|
}),
|
|
4653
4659
|
vulnerabilityReport: z8.object({
|
|
4660
|
+
id: z8.string().uuid(),
|
|
4654
4661
|
vendor: z8.nativeEnum(Vulnerability_Report_Vendor_Enum),
|
|
4655
|
-
vendorReportId: z8.string().uuid().nullable(),
|
|
4656
4662
|
projectId: z8.string().uuid(),
|
|
4657
4663
|
project: z8.object({
|
|
4658
4664
|
organizationId: z8.string().uuid()
|
|
4659
|
-
})
|
|
4660
|
-
file: z8.object({
|
|
4661
|
-
id: z8.string().uuid(),
|
|
4662
|
-
path: z8.string()
|
|
4663
|
-
}),
|
|
4664
|
-
pending: z8.object({
|
|
4665
|
-
aggregate: z8.object({
|
|
4666
|
-
count: z8.number()
|
|
4667
|
-
})
|
|
4668
|
-
}),
|
|
4669
|
-
supported: z8.object({
|
|
4670
|
-
aggregate: z8.object({
|
|
4671
|
-
count: z8.number()
|
|
4672
|
-
})
|
|
4673
|
-
}),
|
|
4674
|
-
all: z8.object({
|
|
4675
|
-
aggregate: z8.object({
|
|
4676
|
-
count: z8.number()
|
|
4677
|
-
})
|
|
4678
|
-
}),
|
|
4679
|
-
fixable: z8.object({
|
|
4680
|
-
aggregate: z8.object({
|
|
4681
|
-
count: z8.number()
|
|
4682
|
-
})
|
|
4683
|
-
}),
|
|
4684
|
-
errors: z8.object({
|
|
4685
|
-
aggregate: z8.object({
|
|
4686
|
-
count: z8.number()
|
|
4687
|
-
})
|
|
4688
|
-
}),
|
|
4689
|
-
vulnerabilityReportIssues: z8.object({
|
|
4690
|
-
extraData: z8.object({
|
|
4691
|
-
missing_files: z8.string().array().nullish(),
|
|
4692
|
-
large_files: z8.string().array().nullish(),
|
|
4693
|
-
error_files: z8.string().array().nullish()
|
|
4694
|
-
})
|
|
4695
|
-
}).array()
|
|
4665
|
+
})
|
|
4696
4666
|
})
|
|
4697
4667
|
});
|
|
4698
4668
|
|
|
@@ -4704,7 +4674,8 @@ var CATEGORY = {
|
|
|
4704
4674
|
Irrelevant: "Irrelevant",
|
|
4705
4675
|
FalsePositive: "FalsePositive",
|
|
4706
4676
|
Fixable: "Fixable",
|
|
4707
|
-
Filtered: "Filtered"
|
|
4677
|
+
Filtered: "Filtered",
|
|
4678
|
+
Pending: "Pending"
|
|
4708
4679
|
};
|
|
4709
4680
|
var ValidCategoriesZ = z9.union([
|
|
4710
4681
|
z9.literal(CATEGORY.NoFix),
|
|
@@ -4712,7 +4683,8 @@ var ValidCategoriesZ = z9.union([
|
|
|
4712
4683
|
z9.literal(CATEGORY.Irrelevant),
|
|
4713
4684
|
z9.literal(CATEGORY.FalsePositive),
|
|
4714
4685
|
z9.literal(CATEGORY.Fixable),
|
|
4715
|
-
z9.literal(CATEGORY.Filtered)
|
|
4686
|
+
z9.literal(CATEGORY.Filtered),
|
|
4687
|
+
z9.literal(CATEGORY.Pending)
|
|
4716
4688
|
]);
|
|
4717
4689
|
var VulnerabilityReportIssueSharedStateZ = z9.object({
|
|
4718
4690
|
id: z9.string().uuid(),
|
|
@@ -4801,7 +4773,8 @@ var GeneralIssueZ = BaseIssuePartsZ.merge(
|
|
|
4801
4773
|
z9.literal(CATEGORY.NoFix),
|
|
4802
4774
|
z9.literal(CATEGORY.Unsupported),
|
|
4803
4775
|
z9.literal(CATEGORY.Fixable),
|
|
4804
|
-
z9.literal(CATEGORY.Filtered)
|
|
4776
|
+
z9.literal(CATEGORY.Filtered),
|
|
4777
|
+
z9.literal(CATEGORY.Pending)
|
|
4805
4778
|
])
|
|
4806
4779
|
})
|
|
4807
4780
|
);
|
|
@@ -4832,7 +4805,8 @@ var mapCategoryToBucket = {
|
|
|
4832
4805
|
NoFix: "remaining",
|
|
4833
4806
|
Unsupported: "remaining",
|
|
4834
4807
|
Fixable: "fixable",
|
|
4835
|
-
Filtered: "
|
|
4808
|
+
Filtered: "irrelevant",
|
|
4809
|
+
Pending: "remaining"
|
|
4836
4810
|
};
|
|
4837
4811
|
|
|
4838
4812
|
// src/features/analysis/scm/shared/src/types/types.ts
|
|
@@ -9176,6 +9150,13 @@ var mobbCliCommand = {
|
|
|
9176
9150
|
convertToSarif: "convert-to-sarif",
|
|
9177
9151
|
mcp: "mcp"
|
|
9178
9152
|
};
|
|
9153
|
+
var ScanContext = {
|
|
9154
|
+
FULL_SCAN: "FULL_SCAN",
|
|
9155
|
+
BACKGROUND_PERIODIC: "BACKGROUND_PERIODIC",
|
|
9156
|
+
BACKGROUND_INITIAL: "BACKGROUND_INITIAL",
|
|
9157
|
+
USER_REQUEST: "USER_REQUEST",
|
|
9158
|
+
BUGSY: "BUGSY"
|
|
9159
|
+
};
|
|
9179
9160
|
|
|
9180
9161
|
// src/args/yargs.ts
|
|
9181
9162
|
import chalk10 from "chalk";
|
|
@@ -10358,7 +10339,8 @@ var GQLClient = class {
|
|
|
10358
10339
|
pullRequest,
|
|
10359
10340
|
sha: sha || "",
|
|
10360
10341
|
experimentalEnabled: !!experimentalEnabled,
|
|
10361
|
-
scanSource: params.scanSource
|
|
10342
|
+
scanSource: params.scanSource,
|
|
10343
|
+
scanContext: ScanContext.BUGSY
|
|
10362
10344
|
});
|
|
10363
10345
|
}
|
|
10364
10346
|
async getFixReportState(fixReportId) {
|
|
@@ -11268,7 +11250,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
11268
11250
|
sha,
|
|
11269
11251
|
experimentalEnabled: !!experimentalEnabled,
|
|
11270
11252
|
pullRequest: params.pullRequest,
|
|
11271
|
-
scanSource: _getScanSource(command, ci)
|
|
11253
|
+
scanSource: _getScanSource(command, ci),
|
|
11254
|
+
scanContext: ScanContext.BUGSY
|
|
11272
11255
|
}
|
|
11273
11256
|
});
|
|
11274
11257
|
if (sendReportRes.submitVulnerabilityReport.__typename !== "VulnerabilityReport") {
|
|
@@ -11429,7 +11412,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
11429
11412
|
sha: commitHash || gitInfo.hash || "0123456789abcdef",
|
|
11430
11413
|
scanSource: _getScanSource(command, ci),
|
|
11431
11414
|
pullRequest: params.pullRequest,
|
|
11432
|
-
experimentalEnabled: !!experimentalEnabled
|
|
11415
|
+
experimentalEnabled: !!experimentalEnabled,
|
|
11416
|
+
scanContext: ScanContext.BUGSY
|
|
11433
11417
|
}
|
|
11434
11418
|
});
|
|
11435
11419
|
if (command === "review") {
|
|
@@ -12111,7 +12095,9 @@ var McpGQLClient = class {
|
|
|
12111
12095
|
__publicField(this, "apiUrl");
|
|
12112
12096
|
this._auth = args;
|
|
12113
12097
|
this.apiUrl = process.env["API_URL"] || MCP_DEFAULT_API_URL;
|
|
12114
|
-
logDebug(`
|
|
12098
|
+
logDebug(`[GraphQL] Creating graphql client with api url ${this.apiUrl}`, {
|
|
12099
|
+
args
|
|
12100
|
+
});
|
|
12115
12101
|
this.client = new GraphQLClient2(this.apiUrl, {
|
|
12116
12102
|
headers: args.type === "apiKey" ? { [MCP_API_KEY_HEADER_NAME]: args.apiKey || "" } : {
|
|
12117
12103
|
Authorization: `Bearer ${args.token}`
|
|
@@ -12141,15 +12127,15 @@ var McpGQLClient = class {
|
|
|
12141
12127
|
}
|
|
12142
12128
|
async isApiEndpointReachable() {
|
|
12143
12129
|
try {
|
|
12144
|
-
logDebug("GraphQL
|
|
12130
|
+
logDebug("[GraphQL] Calling Me query for API connection verification");
|
|
12145
12131
|
const result = await this.getUserInfo();
|
|
12146
|
-
logDebug("GraphQL
|
|
12132
|
+
logDebug("[GraphQL] Me query successful", { result });
|
|
12147
12133
|
return true;
|
|
12148
12134
|
} catch (e) {
|
|
12149
12135
|
const error = e;
|
|
12150
|
-
logDebug(`API connection verification failed`, { error });
|
|
12136
|
+
logDebug(`[GraphQL] API connection verification failed`, { error });
|
|
12151
12137
|
if (error?.toString().includes("FetchError")) {
|
|
12152
|
-
logError("API connection verification failed", { error });
|
|
12138
|
+
logError("[GraphQL] API connection verification failed", { error });
|
|
12153
12139
|
return false;
|
|
12154
12140
|
}
|
|
12155
12141
|
}
|
|
@@ -12174,14 +12160,14 @@ var McpGQLClient = class {
|
|
|
12174
12160
|
}
|
|
12175
12161
|
async uploadS3BucketInfo() {
|
|
12176
12162
|
try {
|
|
12177
|
-
logDebug("GraphQL
|
|
12163
|
+
logDebug("[GraphQL] Calling uploadS3BucketInfo mutation");
|
|
12178
12164
|
const result = await this.clientSdk.uploadS3BucketInfo({
|
|
12179
12165
|
fileName: "report.json"
|
|
12180
12166
|
});
|
|
12181
|
-
logDebug("GraphQL
|
|
12167
|
+
logDebug("[GraphQL] uploadS3BucketInfo successful", { result });
|
|
12182
12168
|
return result;
|
|
12183
12169
|
} catch (e) {
|
|
12184
|
-
logError("GraphQL
|
|
12170
|
+
logError("[GraphQL] uploadS3BucketInfo failed", {
|
|
12185
12171
|
error: e,
|
|
12186
12172
|
...this.getErrorContext()
|
|
12187
12173
|
});
|
|
@@ -12190,17 +12176,17 @@ var McpGQLClient = class {
|
|
|
12190
12176
|
}
|
|
12191
12177
|
async getAnalysis(analysisId) {
|
|
12192
12178
|
try {
|
|
12193
|
-
logDebug("GraphQL
|
|
12179
|
+
logDebug("[GraphQL] Calling getAnalysis query", { analysisId });
|
|
12194
12180
|
const res = await this.clientSdk.getAnalysis({
|
|
12195
12181
|
analysisId
|
|
12196
12182
|
});
|
|
12197
|
-
logDebug("GraphQL
|
|
12183
|
+
logDebug("[GraphQL] getAnalysis successful", { result: res });
|
|
12198
12184
|
if (!res.analysis) {
|
|
12199
12185
|
throw new Error(`Analysis not found: ${analysisId}`);
|
|
12200
12186
|
}
|
|
12201
12187
|
return res.analysis;
|
|
12202
12188
|
} catch (e) {
|
|
12203
|
-
logError("GraphQL
|
|
12189
|
+
logError("[GraphQL] getAnalysis failed", {
|
|
12204
12190
|
error: e,
|
|
12205
12191
|
analysisId,
|
|
12206
12192
|
...this.getErrorContext()
|
|
@@ -12210,14 +12196,14 @@ var McpGQLClient = class {
|
|
|
12210
12196
|
}
|
|
12211
12197
|
async submitVulnerabilityReport(variables) {
|
|
12212
12198
|
try {
|
|
12213
|
-
logDebug("GraphQL
|
|
12199
|
+
logDebug("[GraphQL] Calling SubmitVulnerabilityReport mutation", {
|
|
12214
12200
|
variables
|
|
12215
12201
|
});
|
|
12216
12202
|
const result = await this.clientSdk.SubmitVulnerabilityReport(variables);
|
|
12217
|
-
logDebug("GraphQL
|
|
12203
|
+
logDebug("[GraphQL] SubmitVulnerabilityReport successful", { result });
|
|
12218
12204
|
return result;
|
|
12219
12205
|
} catch (e) {
|
|
12220
|
-
logError("GraphQL
|
|
12206
|
+
logError("[GraphQL] SubmitVulnerabilityReport failed", {
|
|
12221
12207
|
error: e,
|
|
12222
12208
|
variables,
|
|
12223
12209
|
...this.getErrorContext()
|
|
@@ -12299,14 +12285,14 @@ var McpGQLClient = class {
|
|
|
12299
12285
|
}
|
|
12300
12286
|
const shortEmailHash = crypto3.createHash("sha256").update(userEmail).digest("hex").slice(0, 8).toUpperCase();
|
|
12301
12287
|
const projectName = `MCP Scans ${shortEmailHash}`;
|
|
12302
|
-
logDebug("GraphQL
|
|
12288
|
+
logDebug("[GraphQL] Calling getLastOrgAndNamedProject query", {
|
|
12303
12289
|
projectName
|
|
12304
12290
|
});
|
|
12305
12291
|
const orgAndProjectRes = await this.clientSdk.getLastOrgAndNamedProject({
|
|
12306
12292
|
email: userEmail,
|
|
12307
12293
|
projectName
|
|
12308
12294
|
});
|
|
12309
|
-
logDebug("GraphQL
|
|
12295
|
+
logDebug("[GraphQL] getLastOrgAndNamedProject successful", {
|
|
12310
12296
|
result: orgAndProjectRes
|
|
12311
12297
|
});
|
|
12312
12298
|
if (!orgAndProjectRes.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization?.id) {
|
|
@@ -12317,13 +12303,13 @@ var McpGQLClient = class {
|
|
|
12317
12303
|
const organization = orgAndProjectRes.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization;
|
|
12318
12304
|
const projectId = organization?.projects?.[0]?.id;
|
|
12319
12305
|
if (projectId) {
|
|
12320
|
-
logDebug("GraphQL
|
|
12306
|
+
logDebug("[GraphQL] Found existing project", {
|
|
12321
12307
|
projectId,
|
|
12322
12308
|
projectName
|
|
12323
12309
|
});
|
|
12324
12310
|
return projectId;
|
|
12325
12311
|
}
|
|
12326
|
-
logDebug("GraphQL
|
|
12312
|
+
logDebug("[GraphQL] Project not found, creating new project", {
|
|
12327
12313
|
organizationId: organization.id,
|
|
12328
12314
|
projectName
|
|
12329
12315
|
});
|
|
@@ -12331,10 +12317,10 @@ var McpGQLClient = class {
|
|
|
12331
12317
|
organizationId: organization.id,
|
|
12332
12318
|
projectName
|
|
12333
12319
|
});
|
|
12334
|
-
logDebug("GraphQL
|
|
12320
|
+
logDebug("[GraphQL] CreateProject successful", { result: createdProject });
|
|
12335
12321
|
return createdProject.createProject.projectId;
|
|
12336
12322
|
} catch (e) {
|
|
12337
|
-
logError("GraphQL
|
|
12323
|
+
logError("[GraphQL] getProjectId failed", {
|
|
12338
12324
|
error: e,
|
|
12339
12325
|
...this.getErrorContext()
|
|
12340
12326
|
});
|
|
@@ -12350,14 +12336,14 @@ var McpGQLClient = class {
|
|
|
12350
12336
|
return this.currentUser;
|
|
12351
12337
|
}
|
|
12352
12338
|
async validateUserToken() {
|
|
12353
|
-
logDebug("
|
|
12339
|
+
logDebug("[GraphQL] Validating user token");
|
|
12354
12340
|
try {
|
|
12355
12341
|
await this.clientSdk.CreateCommunityUser();
|
|
12356
12342
|
const info = await this.getUserInfo();
|
|
12357
|
-
logDebug("
|
|
12343
|
+
logDebug("[GraphQL] User token validated successfully");
|
|
12358
12344
|
return info?.email || true;
|
|
12359
12345
|
} catch (e) {
|
|
12360
|
-
logError("
|
|
12346
|
+
logError("[GraphQL] User token validation failed");
|
|
12361
12347
|
return false;
|
|
12362
12348
|
}
|
|
12363
12349
|
}
|
|
@@ -12369,12 +12355,12 @@ var McpGQLClient = class {
|
|
|
12369
12355
|
});
|
|
12370
12356
|
const loginId = res.insert_cli_login_one?.id || "";
|
|
12371
12357
|
if (!loginId) {
|
|
12372
|
-
logError("
|
|
12358
|
+
logError("[GraphQL] Create cli login failed - no login ID returned");
|
|
12373
12359
|
return "";
|
|
12374
12360
|
}
|
|
12375
12361
|
return loginId;
|
|
12376
12362
|
} catch (e) {
|
|
12377
|
-
logError("
|
|
12363
|
+
logError("[GraphQL] Create cli login failed", { error: e });
|
|
12378
12364
|
return "";
|
|
12379
12365
|
}
|
|
12380
12366
|
}
|
|
@@ -12386,7 +12372,7 @@ var McpGQLClient = class {
|
|
|
12386
12372
|
});
|
|
12387
12373
|
return res?.cli_login_by_pk?.encryptedApiToken || null;
|
|
12388
12374
|
} catch (e) {
|
|
12389
|
-
logError("
|
|
12375
|
+
logError("[GraphQL] Get encrypted api token failed", { error: e });
|
|
12390
12376
|
return null;
|
|
12391
12377
|
}
|
|
12392
12378
|
}
|
|
@@ -12412,12 +12398,12 @@ var McpGQLClient = class {
|
|
|
12412
12398
|
fixIds,
|
|
12413
12399
|
source: "MCP" /* Mcp */
|
|
12414
12400
|
});
|
|
12415
|
-
logDebug("GraphQL
|
|
12401
|
+
logDebug("[GraphQL] updateFixesDownloadStatus successful", {
|
|
12416
12402
|
result: resUpdate,
|
|
12417
12403
|
fixIds
|
|
12418
12404
|
});
|
|
12419
12405
|
} else {
|
|
12420
|
-
logDebug("GraphQL
|
|
12406
|
+
logDebug("[GraphQL] No fixes found to update download status");
|
|
12421
12407
|
}
|
|
12422
12408
|
}
|
|
12423
12409
|
async getLatestReportByRepoUrl({
|
|
@@ -12426,7 +12412,7 @@ var McpGQLClient = class {
|
|
|
12426
12412
|
offset = 0
|
|
12427
12413
|
}) {
|
|
12428
12414
|
try {
|
|
12429
|
-
logDebug("GraphQL
|
|
12415
|
+
logDebug("[GraphQL] Calling GetLatestReportByRepoUrl query", {
|
|
12430
12416
|
repoUrl,
|
|
12431
12417
|
limit,
|
|
12432
12418
|
offset
|
|
@@ -12438,7 +12424,7 @@ var McpGQLClient = class {
|
|
|
12438
12424
|
currentUserEmail = `%${userInfo.email}%`;
|
|
12439
12425
|
}
|
|
12440
12426
|
} catch (err) {
|
|
12441
|
-
logDebug("Failed to get user email, using default pattern", {
|
|
12427
|
+
logDebug("[GraphQL] Failed to get user email, using default pattern", {
|
|
12442
12428
|
error: err
|
|
12443
12429
|
});
|
|
12444
12430
|
}
|
|
@@ -12448,7 +12434,7 @@ var McpGQLClient = class {
|
|
|
12448
12434
|
offset,
|
|
12449
12435
|
currentUserEmail
|
|
12450
12436
|
});
|
|
12451
|
-
logDebug("GraphQL
|
|
12437
|
+
logDebug("[GraphQL] GetLatestReportByRepoUrl successful", {
|
|
12452
12438
|
result: res,
|
|
12453
12439
|
reportCount: res.fixReport?.length || 0
|
|
12454
12440
|
});
|
|
@@ -12463,7 +12449,7 @@ var McpGQLClient = class {
|
|
|
12463
12449
|
expiredReport: res.expiredReport?.[0] || null
|
|
12464
12450
|
};
|
|
12465
12451
|
} catch (e) {
|
|
12466
|
-
logError("GraphQL
|
|
12452
|
+
logError("[GraphQL] GetLatestReportByRepoUrl failed", {
|
|
12467
12453
|
error: e,
|
|
12468
12454
|
repoUrl,
|
|
12469
12455
|
...this.getErrorContext()
|
|
@@ -12486,7 +12472,7 @@ var McpGQLClient = class {
|
|
|
12486
12472
|
filters["severityText"] = { _in: severity };
|
|
12487
12473
|
}
|
|
12488
12474
|
try {
|
|
12489
|
-
logDebug("GraphQL
|
|
12475
|
+
logDebug("[GraphQL] Calling GetReportFixes query", {
|
|
12490
12476
|
reportId,
|
|
12491
12477
|
limit,
|
|
12492
12478
|
offset,
|
|
@@ -12501,7 +12487,7 @@ var McpGQLClient = class {
|
|
|
12501
12487
|
currentUserEmail = `%${userInfo.email}%`;
|
|
12502
12488
|
}
|
|
12503
12489
|
} catch (err) {
|
|
12504
|
-
logDebug("Failed to get user email, using default pattern", {
|
|
12490
|
+
logDebug("[GraphQL] Failed to get user email, using default pattern", {
|
|
12505
12491
|
error: err
|
|
12506
12492
|
});
|
|
12507
12493
|
}
|
|
@@ -12512,7 +12498,7 @@ var McpGQLClient = class {
|
|
|
12512
12498
|
filters,
|
|
12513
12499
|
currentUserEmail
|
|
12514
12500
|
});
|
|
12515
|
-
logDebug("GraphQL
|
|
12501
|
+
logDebug("[GraphQL] GetReportFixes successful", {
|
|
12516
12502
|
result: res,
|
|
12517
12503
|
fixCount: res.fixReport?.[0]?.fixes?.length || 0,
|
|
12518
12504
|
totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0
|
|
@@ -12529,7 +12515,7 @@ var McpGQLClient = class {
|
|
|
12529
12515
|
expiredReport: res.expiredReport?.[0] || null
|
|
12530
12516
|
};
|
|
12531
12517
|
} catch (e) {
|
|
12532
|
-
logError("GraphQL
|
|
12518
|
+
logError("[GraphQL] GetReportFixes failed", {
|
|
12533
12519
|
error: e,
|
|
12534
12520
|
reportId,
|
|
12535
12521
|
...this.getErrorContext()
|
|
@@ -12541,18 +12527,20 @@ var McpGQLClient = class {
|
|
|
12541
12527
|
async function createAuthenticatedMcpGQLClient({
|
|
12542
12528
|
isBackgoundCall = false
|
|
12543
12529
|
} = {}) {
|
|
12544
|
-
logDebug("
|
|
12530
|
+
logDebug("[GraphQL] Getting config", {
|
|
12531
|
+
apiToken: configStore.get("apiToken")
|
|
12532
|
+
});
|
|
12545
12533
|
const initialClient = new McpGQLClient({
|
|
12546
12534
|
apiKey: process.env["MOBB_API_KEY"] || process.env["API_KEY"] || // fallback for backward compatibility
|
|
12547
12535
|
configStore.get("apiToken") || "",
|
|
12548
12536
|
type: "apiKey"
|
|
12549
12537
|
});
|
|
12550
12538
|
const isApiEndpointReachable = await initialClient.isApiEndpointReachable();
|
|
12551
|
-
logDebug("API connection status", { isApiEndpointReachable });
|
|
12539
|
+
logDebug("[GraphQL] API connection status", { isApiEndpointReachable });
|
|
12552
12540
|
if (!isApiEndpointReachable) {
|
|
12553
12541
|
throw new ApiConnectionError("Error: failed to reach Mobb GraphQL endpoint");
|
|
12554
12542
|
}
|
|
12555
|
-
logDebug("
|
|
12543
|
+
logDebug("[GraphQL] Validating user token");
|
|
12556
12544
|
const userVerify = await initialClient.validateUserToken();
|
|
12557
12545
|
if (userVerify) {
|
|
12558
12546
|
return initialClient;
|
|
@@ -13333,82 +13321,128 @@ var getLocalFiles = async ({
|
|
|
13333
13321
|
path: path13,
|
|
13334
13322
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
13335
13323
|
maxFiles,
|
|
13336
|
-
isAllFilesScan
|
|
13324
|
+
isAllFilesScan,
|
|
13325
|
+
scanContext
|
|
13337
13326
|
}) => {
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
files = await FileUtils.getLastChangedFiles({
|
|
13350
|
-
dir: path13,
|
|
13351
|
-
maxFileSize,
|
|
13352
|
-
maxFiles,
|
|
13353
|
-
isAllFilesScan
|
|
13327
|
+
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
13328
|
+
path: path13,
|
|
13329
|
+
maxFileSize,
|
|
13330
|
+
maxFiles,
|
|
13331
|
+
isAllFilesScan
|
|
13332
|
+
});
|
|
13333
|
+
try {
|
|
13334
|
+
const resolvedRepoPath = await fs11.realpath(path13);
|
|
13335
|
+
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
13336
|
+
resolvedRepoPath,
|
|
13337
|
+
originalPath: path13
|
|
13354
13338
|
});
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13339
|
+
const gitService = new GitService(resolvedRepoPath, log);
|
|
13340
|
+
const gitValidation = await gitService.validateRepository();
|
|
13341
|
+
logDebug(`[${scanContext}] Git repository validation result`, {
|
|
13342
|
+
isValid: gitValidation.isValid,
|
|
13343
|
+
error: gitValidation.error,
|
|
13344
|
+
isAllFilesScan
|
|
13358
13345
|
});
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
{
|
|
13373
|
-
|
|
13374
|
-
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13346
|
+
let files = [];
|
|
13347
|
+
if (!gitValidation.isValid || isAllFilesScan) {
|
|
13348
|
+
try {
|
|
13349
|
+
files = await FileUtils.getLastChangedFiles({
|
|
13350
|
+
dir: path13,
|
|
13351
|
+
maxFileSize,
|
|
13352
|
+
maxFiles,
|
|
13353
|
+
isAllFilesScan
|
|
13354
|
+
});
|
|
13355
|
+
logDebug(`[${scanContext}] Found files in the repository`, {
|
|
13356
|
+
fileCount: files.length
|
|
13357
|
+
});
|
|
13358
|
+
} catch (error) {
|
|
13359
|
+
logError(`${scanContext}Error getting last changed files`, {
|
|
13360
|
+
error: error instanceof Error ? error.message : String(error),
|
|
13361
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
13362
|
+
});
|
|
13363
|
+
throw error;
|
|
13364
|
+
}
|
|
13378
13365
|
} else {
|
|
13379
|
-
logDebug("Found changed files in the git repository", {
|
|
13380
|
-
files,
|
|
13381
|
-
fileCount: files.length
|
|
13382
|
-
});
|
|
13383
|
-
}
|
|
13384
|
-
}
|
|
13385
|
-
files = files.filter(
|
|
13386
|
-
(file) => FileUtils.shouldPackFile(
|
|
13387
|
-
nodePath.resolve(resolvedRepoPath, file),
|
|
13388
|
-
maxFileSize
|
|
13389
|
-
)
|
|
13390
|
-
);
|
|
13391
|
-
const filesWithStats = await Promise.all(
|
|
13392
|
-
files.map(async (file) => {
|
|
13393
|
-
const absoluteFilePath = nodePath.resolve(resolvedRepoPath, file);
|
|
13394
|
-
const relativePath = nodePath.relative(resolvedRepoPath, absoluteFilePath);
|
|
13395
|
-
let fileStat;
|
|
13396
13366
|
try {
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13367
|
+
const gitResult = await gitService.getChangedFiles();
|
|
13368
|
+
files = gitResult.files;
|
|
13369
|
+
if (files.length === 0 || maxFiles) {
|
|
13370
|
+
logDebug(
|
|
13371
|
+
`[${scanContext}] No changes found or maxFiles specified, getting recently changed files`,
|
|
13372
|
+
{ maxFiles }
|
|
13373
|
+
);
|
|
13374
|
+
const recentResult = await gitService.getRecentlyChangedFiles({
|
|
13375
|
+
maxFiles
|
|
13376
|
+
});
|
|
13377
|
+
files = recentResult.files;
|
|
13378
|
+
logDebug(
|
|
13379
|
+
`[${scanContext}] Using recently changed files from git history`,
|
|
13380
|
+
{
|
|
13381
|
+
fileCount: files.length,
|
|
13382
|
+
commitsChecked: recentResult.commitCount
|
|
13383
|
+
}
|
|
13384
|
+
);
|
|
13385
|
+
} else {
|
|
13386
|
+
logDebug(
|
|
13387
|
+
`[${scanContext}] Found changed files in the git repository`,
|
|
13388
|
+
{
|
|
13389
|
+
fileCount: files.length
|
|
13390
|
+
}
|
|
13391
|
+
);
|
|
13392
|
+
}
|
|
13393
|
+
} catch (error) {
|
|
13394
|
+
logError(`${scanContext}Error getting files from git`, {
|
|
13395
|
+
error: error instanceof Error ? error.message : String(error),
|
|
13396
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
13401
13397
|
});
|
|
13398
|
+
throw error;
|
|
13402
13399
|
}
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13400
|
+
}
|
|
13401
|
+
files = files.filter((file) => {
|
|
13402
|
+
const fullPath = nodePath.resolve(resolvedRepoPath, file);
|
|
13403
|
+
const isPackable = FileUtils.shouldPackFile(fullPath, maxFileSize);
|
|
13404
|
+
return isPackable;
|
|
13405
|
+
});
|
|
13406
|
+
const filesWithStats = await Promise.all(
|
|
13407
|
+
files.map(async (file) => {
|
|
13408
|
+
const absoluteFilePath = nodePath.resolve(resolvedRepoPath, file);
|
|
13409
|
+
const relativePath = nodePath.relative(
|
|
13410
|
+
resolvedRepoPath,
|
|
13411
|
+
absoluteFilePath
|
|
13412
|
+
);
|
|
13413
|
+
try {
|
|
13414
|
+
const fileStat = await fs11.stat(absoluteFilePath);
|
|
13415
|
+
return {
|
|
13416
|
+
filename: nodePath.basename(absoluteFilePath),
|
|
13417
|
+
relativePath,
|
|
13418
|
+
fullPath: absoluteFilePath,
|
|
13419
|
+
lastEdited: fileStat.mtime.getTime()
|
|
13420
|
+
};
|
|
13421
|
+
} catch (e) {
|
|
13422
|
+
logError(`[${scanContext}] Error getting file stats`, {
|
|
13423
|
+
file,
|
|
13424
|
+
absoluteFilePath,
|
|
13425
|
+
error: e instanceof Error ? e.message : String(e)
|
|
13426
|
+
});
|
|
13427
|
+
return {
|
|
13428
|
+
filename: nodePath.basename(absoluteFilePath),
|
|
13429
|
+
relativePath,
|
|
13430
|
+
fullPath: absoluteFilePath,
|
|
13431
|
+
lastEdited: 0
|
|
13432
|
+
};
|
|
13433
|
+
}
|
|
13434
|
+
})
|
|
13435
|
+
);
|
|
13436
|
+
const result = filesWithStats.filter((file) => file.lastEdited > 0);
|
|
13437
|
+
return result;
|
|
13438
|
+
} catch (error) {
|
|
13439
|
+
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
13440
|
+
error: error instanceof Error ? error.message : String(error),
|
|
13441
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
13442
|
+
path: path13
|
|
13443
|
+
});
|
|
13444
|
+
throw error;
|
|
13445
|
+
}
|
|
13412
13446
|
};
|
|
13413
13447
|
|
|
13414
13448
|
// src/mcp/services/ScanFiles.ts
|
|
@@ -13429,7 +13463,7 @@ var FileOperations = class {
|
|
|
13429
13463
|
* @returns ZIP archive as a Buffer with metadata
|
|
13430
13464
|
*/
|
|
13431
13465
|
async createSourceCodeArchive(fileList, repositoryPath, maxFileSize) {
|
|
13432
|
-
logDebug("
|
|
13466
|
+
logDebug("[FileOperations] Packing files");
|
|
13433
13467
|
const zip = new AdmZip2();
|
|
13434
13468
|
let packedFilesCount = 0;
|
|
13435
13469
|
const resolvedRepoPath = path12.resolve(repositoryPath);
|
|
@@ -13438,13 +13472,13 @@ var FileOperations = class {
|
|
|
13438
13472
|
const resolvedFilePath = path12.resolve(absoluteFilepath);
|
|
13439
13473
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
13440
13474
|
logDebug(
|
|
13441
|
-
`Skipping ${filepath} due to potential path traversal security risk`
|
|
13475
|
+
`[FileOperations] Skipping ${filepath} due to potential path traversal security risk`
|
|
13442
13476
|
);
|
|
13443
13477
|
continue;
|
|
13444
13478
|
}
|
|
13445
13479
|
if (!FileUtils.shouldPackFile(absoluteFilepath, maxFileSize)) {
|
|
13446
13480
|
logDebug(
|
|
13447
|
-
`Excluding ${filepath} - file is too large, binary, or matches exclusion rules`
|
|
13481
|
+
`[FileOperations] Excluding ${filepath} - file is too large, binary, or matches exclusion rules`
|
|
13448
13482
|
);
|
|
13449
13483
|
continue;
|
|
13450
13484
|
}
|
|
@@ -13461,7 +13495,7 @@ var FileOperations = class {
|
|
|
13461
13495
|
totalSize: archiveBuffer.length
|
|
13462
13496
|
};
|
|
13463
13497
|
logInfo(
|
|
13464
|
-
`Files packed successfully ${packedFilesCount} files, ${result.totalSize} bytes`
|
|
13498
|
+
`[FileOperations] Files packed successfully ${packedFilesCount} files, ${result.totalSize} bytes`
|
|
13465
13499
|
);
|
|
13466
13500
|
return result;
|
|
13467
13501
|
}
|
|
@@ -13478,14 +13512,18 @@ var FileOperations = class {
|
|
|
13478
13512
|
const absoluteFilepath = path12.join(repositoryPath, filepath);
|
|
13479
13513
|
const resolvedFilePath = path12.resolve(absoluteFilepath);
|
|
13480
13514
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
13481
|
-
logDebug(
|
|
13515
|
+
logDebug(
|
|
13516
|
+
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
13517
|
+
);
|
|
13482
13518
|
continue;
|
|
13483
13519
|
}
|
|
13484
13520
|
try {
|
|
13485
13521
|
await fs12.promises.access(absoluteFilepath, fs12.constants.R_OK);
|
|
13486
13522
|
validatedPaths.push(filepath);
|
|
13487
13523
|
} catch (error) {
|
|
13488
|
-
logDebug(
|
|
13524
|
+
logDebug(
|
|
13525
|
+
`[FileOperations] Skipping ${filepath} - file is not accessible: ${error}`
|
|
13526
|
+
);
|
|
13489
13527
|
}
|
|
13490
13528
|
}
|
|
13491
13529
|
return validatedPaths;
|
|
@@ -13507,7 +13545,9 @@ var FileOperations = class {
|
|
|
13507
13545
|
content
|
|
13508
13546
|
});
|
|
13509
13547
|
} catch (error) {
|
|
13510
|
-
logError(
|
|
13548
|
+
logError(
|
|
13549
|
+
`[FileOperations] Failed to read file ${absolutePath}: ${error}`
|
|
13550
|
+
);
|
|
13511
13551
|
}
|
|
13512
13552
|
}
|
|
13513
13553
|
return fileDataArray;
|
|
@@ -13522,7 +13562,9 @@ var FileOperations = class {
|
|
|
13522
13562
|
try {
|
|
13523
13563
|
return await fs12.promises.readFile(absoluteFilepath);
|
|
13524
13564
|
} catch (fsError) {
|
|
13525
|
-
logError(
|
|
13565
|
+
logError(
|
|
13566
|
+
`[FileOperations] Failed to read ${relativeFilepath} from filesystem: ${fsError}`
|
|
13567
|
+
);
|
|
13526
13568
|
return null;
|
|
13527
13569
|
}
|
|
13528
13570
|
}
|
|
@@ -13564,7 +13606,8 @@ var scanFiles = async ({
|
|
|
13564
13606
|
repoUrl: repoUrl || "",
|
|
13565
13607
|
branchName: branch || "no-branch",
|
|
13566
13608
|
sha: "0123456789abcdef",
|
|
13567
|
-
scanContext
|
|
13609
|
+
scanContext,
|
|
13610
|
+
fileCount: packingResult.packedFilesCount
|
|
13568
13611
|
});
|
|
13569
13612
|
return {
|
|
13570
13613
|
fixReportId,
|
|
@@ -13637,7 +13680,8 @@ var executeSecurityScan = async ({
|
|
|
13637
13680
|
repoUrl,
|
|
13638
13681
|
branchName,
|
|
13639
13682
|
sha,
|
|
13640
|
-
scanContext
|
|
13683
|
+
scanContext,
|
|
13684
|
+
fileCount
|
|
13641
13685
|
}) => {
|
|
13642
13686
|
if (!gqlClient) {
|
|
13643
13687
|
throw new GqlClientError();
|
|
@@ -13650,7 +13694,9 @@ var executeSecurityScan = async ({
|
|
|
13650
13694
|
reference: branchName,
|
|
13651
13695
|
scanSource: "MCP" /* Mcp */,
|
|
13652
13696
|
isFullScan: !!isAllDetectionRulesScan,
|
|
13653
|
-
sha
|
|
13697
|
+
sha,
|
|
13698
|
+
scanContext,
|
|
13699
|
+
fileCount
|
|
13654
13700
|
};
|
|
13655
13701
|
logInfo(`[${scanContext}] Submitting vulnerability report`);
|
|
13656
13702
|
logDebug(`[${scanContext}] Submit vulnerability report variables`, {
|
|
@@ -13679,11 +13725,64 @@ var executeSecurityScan = async ({
|
|
|
13679
13725
|
scanContext
|
|
13680
13726
|
});
|
|
13681
13727
|
} catch (error) {
|
|
13682
|
-
|
|
13683
|
-
|
|
13684
|
-
|
|
13728
|
+
const errorObj = error;
|
|
13729
|
+
const errorDetails = {
|
|
13730
|
+
message: errorObj.message || "No error message",
|
|
13731
|
+
name: errorObj.name || "Unknown error type",
|
|
13732
|
+
stack: errorObj.stack,
|
|
13733
|
+
analysisId,
|
|
13734
|
+
timeoutMs: MCP_VUL_REPORT_DIGEST_TIMEOUT_MS,
|
|
13735
|
+
isTimeoutError: errorObj.message?.includes("Timeout expired"),
|
|
13736
|
+
// Safely extract additional properties from the error object
|
|
13737
|
+
...Object.getOwnPropertyNames(errorObj).filter(
|
|
13738
|
+
(prop) => prop !== "message" && prop !== "name" && prop !== "stack"
|
|
13739
|
+
).reduce(
|
|
13740
|
+
(acc, prop) => ({
|
|
13741
|
+
...acc,
|
|
13742
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13743
|
+
[prop]: errorObj[prop]
|
|
13744
|
+
}),
|
|
13745
|
+
{}
|
|
13746
|
+
)
|
|
13747
|
+
};
|
|
13748
|
+
logError(
|
|
13749
|
+
`[${scanContext}] Security analysis failed or timed out`,
|
|
13750
|
+
errorDetails
|
|
13751
|
+
);
|
|
13752
|
+
logDebug(`[${scanContext}] Security scan failure context`, {
|
|
13753
|
+
fixReportId,
|
|
13754
|
+
projectId,
|
|
13755
|
+
repoUrl,
|
|
13756
|
+
branchName,
|
|
13757
|
+
isAllDetectionRulesScan,
|
|
13758
|
+
fileCount,
|
|
13759
|
+
scanSource: "MCP" /* Mcp */,
|
|
13760
|
+
subscriptionParams: { analysisId },
|
|
13761
|
+
expectedCallbackState: "Finished" /* Finished */,
|
|
13762
|
+
subscriptionTimeout: {
|
|
13763
|
+
configuredTimeoutMs: MCP_VUL_REPORT_DIGEST_TIMEOUT_MS,
|
|
13764
|
+
isTimeoutError: errorObj.message?.includes("Timeout expired")
|
|
13765
|
+
}
|
|
13685
13766
|
});
|
|
13686
|
-
|
|
13767
|
+
try {
|
|
13768
|
+
const analysis = await gqlClient.getAnalysis(analysisId);
|
|
13769
|
+
if (analysis) {
|
|
13770
|
+
logDebug(`[${scanContext}] Current analysis state at error time`, {
|
|
13771
|
+
analysisId,
|
|
13772
|
+
state: analysis.state,
|
|
13773
|
+
failReason: analysis.failReason || "No failure reason provided",
|
|
13774
|
+
// The createdAt field doesn't exist in the analysis type, include other useful properties
|
|
13775
|
+
analysisObjectId: analysis.id
|
|
13776
|
+
});
|
|
13777
|
+
}
|
|
13778
|
+
} catch (analysisError) {
|
|
13779
|
+
logDebug(`[${scanContext}] Failed to get analysis state`, {
|
|
13780
|
+
analysisError: analysisError.message
|
|
13781
|
+
});
|
|
13782
|
+
}
|
|
13783
|
+
throw new ScanError(
|
|
13784
|
+
`Security analysis failed: ${error.message || "Unknown error"}`
|
|
13785
|
+
);
|
|
13687
13786
|
}
|
|
13688
13787
|
logDebug(`[${scanContext}] Security scan completed successfully`, {
|
|
13689
13788
|
fixReportId,
|
|
@@ -13762,7 +13861,8 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13762
13861
|
);
|
|
13763
13862
|
const files = await getLocalFiles({
|
|
13764
13863
|
path: path13,
|
|
13765
|
-
isAllFilesScan
|
|
13864
|
+
isAllFilesScan,
|
|
13865
|
+
scanContext
|
|
13766
13866
|
});
|
|
13767
13867
|
logDebug(`[${scanContext}] Active files`, { files });
|
|
13768
13868
|
const filesToScan = files.filter((file) => {
|
|
@@ -13803,14 +13903,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13803
13903
|
logInfo(
|
|
13804
13904
|
`[${scanContext}] Security fixes retrieved, total: ${fixes?.fixes?.length || 0}, new: ${newFixes?.length || 0}`
|
|
13805
13905
|
);
|
|
13806
|
-
this.updateFreshFixesCache(newFixes || [], filesToScan);
|
|
13906
|
+
this.updateFreshFixesCache(newFixes || [], filesToScan, scanContext);
|
|
13807
13907
|
this.updateFilesScanTimestamps(filesToScan);
|
|
13808
13908
|
this.isInitialScanComplete = true;
|
|
13809
13909
|
} catch (error) {
|
|
13810
13910
|
const errorMessage = error.message;
|
|
13811
13911
|
if (errorMessage.includes("Authentication failed") || errorMessage.includes("access-denied") || errorMessage.includes("Authentication hook unauthorized")) {
|
|
13812
13912
|
logError(
|
|
13813
|
-
|
|
13913
|
+
`[${scanContext}] Periodic scan skipped due to authentication failure. Please re-authenticate by running a manual scan.`,
|
|
13814
13914
|
{
|
|
13815
13915
|
error: errorMessage
|
|
13816
13916
|
}
|
|
@@ -13818,20 +13918,28 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13818
13918
|
return;
|
|
13819
13919
|
}
|
|
13820
13920
|
if (errorMessage.includes("ReportInitializationError")) {
|
|
13821
|
-
logError(
|
|
13822
|
-
|
|
13823
|
-
|
|
13921
|
+
logError(
|
|
13922
|
+
`[${scanContext}] Periodic scan failed during report initialization`,
|
|
13923
|
+
{
|
|
13924
|
+
error: errorMessage
|
|
13925
|
+
}
|
|
13926
|
+
);
|
|
13824
13927
|
return;
|
|
13825
13928
|
}
|
|
13826
|
-
logError(
|
|
13929
|
+
logError(
|
|
13930
|
+
`[${scanContext}] Unexpected error during periodic security scan`,
|
|
13931
|
+
{ error }
|
|
13932
|
+
);
|
|
13827
13933
|
throw error;
|
|
13828
13934
|
}
|
|
13829
13935
|
}
|
|
13830
|
-
updateFreshFixesCache(newFixes, filesToScan) {
|
|
13831
|
-
this.freshFixes = this.freshFixes.filter((fix) => !this.isFixFromOldScan(fix, filesToScan)).concat(newFixes).sort((a, b) => {
|
|
13936
|
+
updateFreshFixesCache(newFixes, filesToScan, scanContext) {
|
|
13937
|
+
this.freshFixes = this.freshFixes.filter((fix) => !this.isFixFromOldScan(fix, filesToScan, scanContext)).concat(newFixes).sort((a, b) => {
|
|
13832
13938
|
return (b.severityValue ?? 0) - (a.severityValue ?? 0);
|
|
13833
13939
|
});
|
|
13834
|
-
logInfo(
|
|
13940
|
+
logInfo(
|
|
13941
|
+
`[${scanContext}] Fresh fixes cache updated, total: ${this.freshFixes.length}`
|
|
13942
|
+
);
|
|
13835
13943
|
}
|
|
13836
13944
|
updateFilesScanTimestamps(filesToScan) {
|
|
13837
13945
|
filesToScan.forEach((file) => {
|
|
@@ -13843,13 +13951,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13843
13951
|
(reportedFix) => reportedFix.sharedState?.id === fix.sharedState?.id
|
|
13844
13952
|
);
|
|
13845
13953
|
}
|
|
13846
|
-
isFixFromOldScan(fix, filesToScan) {
|
|
13954
|
+
isFixFromOldScan(fix, filesToScan, scanContext) {
|
|
13847
13955
|
const patch = fix.patchAndQuestions?.__typename === "FixData" ? fix.patchAndQuestions.patch : void 0;
|
|
13848
13956
|
const fixFile = extractPathFromPatch(patch);
|
|
13849
13957
|
if (!fixFile) {
|
|
13850
13958
|
return false;
|
|
13851
13959
|
}
|
|
13852
|
-
logDebug(
|
|
13960
|
+
logDebug(`[${scanContext}] Checking if fix is from old scan`, {
|
|
13853
13961
|
fixFile,
|
|
13854
13962
|
filesToScan,
|
|
13855
13963
|
isFromOldScan: filesToScan.some((file) => file.relativePath === fixFile)
|
|
@@ -13857,14 +13965,17 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13857
13965
|
return filesToScan.some((file) => file.relativePath === fixFile);
|
|
13858
13966
|
}
|
|
13859
13967
|
async getFreshFixes({ path: path13 }) {
|
|
13968
|
+
const scanContext = ScanContext.USER_REQUEST;
|
|
13969
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path13 });
|
|
13860
13970
|
if (this.path !== path13) {
|
|
13861
13971
|
this.path = path13;
|
|
13862
13972
|
this.reset();
|
|
13973
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path13 });
|
|
13863
13974
|
}
|
|
13864
13975
|
this.gqlClient = await createAuthenticatedMcpGQLClient();
|
|
13865
13976
|
this.triggerScan({ path: path13, gqlClient: this.gqlClient });
|
|
13866
13977
|
if (this.freshFixes.length > 0) {
|
|
13867
|
-
return this.generateFreshFixesResponse();
|
|
13978
|
+
return this.generateFreshFixesResponse(scanContext);
|
|
13868
13979
|
}
|
|
13869
13980
|
if (!this.isInitialScanComplete) {
|
|
13870
13981
|
return initialScanInProgressPrompt;
|
|
@@ -13883,21 +13994,27 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13883
13994
|
}
|
|
13884
13995
|
}
|
|
13885
13996
|
startPeriodicScanning(path13) {
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
13997
|
+
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
13998
|
+
logDebug(
|
|
13999
|
+
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
14000
|
+
{
|
|
14001
|
+
path: path13
|
|
14002
|
+
}
|
|
14003
|
+
);
|
|
13889
14004
|
this.intervalId = setInterval(() => {
|
|
13890
|
-
logDebug(
|
|
14005
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path13 });
|
|
13891
14006
|
this.scanForSecurityVulnerabilities({
|
|
13892
14007
|
path: path13,
|
|
13893
|
-
scanContext
|
|
14008
|
+
scanContext
|
|
13894
14009
|
}).catch((error) => {
|
|
13895
|
-
logError(
|
|
14010
|
+
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
14011
|
+
error
|
|
14012
|
+
});
|
|
13896
14013
|
});
|
|
13897
14014
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
13898
14015
|
}
|
|
13899
14016
|
async executeInitialFullScan(path13) {
|
|
13900
|
-
const scanContext =
|
|
14017
|
+
const scanContext = ScanContext.FULL_SCAN;
|
|
13901
14018
|
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path13 });
|
|
13902
14019
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
13903
14020
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
@@ -13917,7 +14034,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13917
14034
|
path: path13,
|
|
13918
14035
|
isAllFilesScan: true,
|
|
13919
14036
|
isAllDetectionRulesScan: true,
|
|
13920
|
-
scanContext:
|
|
14037
|
+
scanContext: ScanContext.FULL_SCAN
|
|
13921
14038
|
});
|
|
13922
14039
|
if (!this.fullScanPathsScanned.includes(path13)) {
|
|
13923
14040
|
this.fullScanPathsScanned.push(path13);
|
|
@@ -13925,25 +14042,31 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
13925
14042
|
}
|
|
13926
14043
|
logInfo(`[${scanContext}] Full scan completed`, { path: path13 });
|
|
13927
14044
|
} catch (error) {
|
|
13928
|
-
logError(
|
|
14045
|
+
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
14046
|
+
error
|
|
14047
|
+
});
|
|
13929
14048
|
}
|
|
13930
14049
|
}
|
|
13931
14050
|
executeInitialScan(path13) {
|
|
13932
|
-
const scanContext =
|
|
14051
|
+
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
13933
14052
|
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path13 });
|
|
13934
14053
|
this.scanForSecurityVulnerabilities({
|
|
13935
14054
|
path: path13,
|
|
13936
|
-
scanContext:
|
|
14055
|
+
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
13937
14056
|
}).catch((error) => {
|
|
13938
14057
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
13939
14058
|
});
|
|
13940
14059
|
}
|
|
13941
|
-
generateFreshFixesResponse() {
|
|
14060
|
+
generateFreshFixesResponse(scanContext = ScanContext.USER_REQUEST) {
|
|
13942
14061
|
const freshFixes = this.freshFixes.splice(0, MCP_DEFAULT_LIMIT);
|
|
13943
14062
|
if (freshFixes.length > 0) {
|
|
14063
|
+
logInfo(
|
|
14064
|
+
`[${scanContext}] Reporting ${freshFixes.length} fresh fixes to user`
|
|
14065
|
+
);
|
|
13944
14066
|
this.reportedFixes.push(...freshFixes);
|
|
13945
14067
|
return freshFixesPrompt({ fixes: freshFixes, limit: MCP_DEFAULT_LIMIT });
|
|
13946
14068
|
}
|
|
14069
|
+
logInfo(`[${scanContext}] No fresh fixes to report`);
|
|
13947
14070
|
return noFreshFixesPrompt;
|
|
13948
14071
|
}
|
|
13949
14072
|
};
|
|
@@ -14247,7 +14370,7 @@ var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService
|
|
|
14247
14370
|
fileList,
|
|
14248
14371
|
repositoryPath,
|
|
14249
14372
|
gqlClient: this.gqlClient,
|
|
14250
|
-
scanContext:
|
|
14373
|
+
scanContext: ScanContext.USER_REQUEST
|
|
14251
14374
|
});
|
|
14252
14375
|
fixReportId = scanResult.fixReportId;
|
|
14253
14376
|
} else {
|
|
@@ -14419,7 +14542,8 @@ Example payload:
|
|
|
14419
14542
|
const files = await getLocalFiles({
|
|
14420
14543
|
path: path13,
|
|
14421
14544
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
14422
|
-
maxFiles: args.maxFiles
|
|
14545
|
+
maxFiles: args.maxFiles,
|
|
14546
|
+
scanContext: ScanContext.USER_REQUEST
|
|
14423
14547
|
});
|
|
14424
14548
|
logDebug("Files", { files });
|
|
14425
14549
|
if (files.length === 0) {
|