mobbdev 1.2.33 → 1.2.35
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/args/commands/upload_ai_blame.d.mts +47 -33
- package/dist/args/commands/upload_ai_blame.mjs +304 -218
- package/dist/index.mjs +1479 -1352
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -73,6 +73,12 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
73
73
|
FinalizeAIBlameInferencesUpload(variables, requestHeaders, signal) {
|
|
74
74
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: FinalizeAiBlameInferencesUploadDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "FinalizeAIBlameInferencesUpload", "mutation", variables);
|
|
75
75
|
},
|
|
76
|
+
UploadTracyRecords(variables, requestHeaders, signal) {
|
|
77
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: UploadTracyRecordsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "UploadTracyRecords", "mutation", variables);
|
|
78
|
+
},
|
|
79
|
+
GetTracyRawDataUploadUrls(variables, requestHeaders, signal) {
|
|
80
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetTracyRawDataUploadUrlsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetTracyRawDataUploadUrls", "mutation", variables);
|
|
81
|
+
},
|
|
76
82
|
DigestVulnerabilityReport(variables, requestHeaders, signal) {
|
|
77
83
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: DigestVulnerabilityReportDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "DigestVulnerabilityReport", "mutation", variables);
|
|
78
84
|
},
|
|
@@ -126,7 +132,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
126
132
|
}
|
|
127
133
|
};
|
|
128
134
|
}
|
|
129
|
-
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, defaultWrapper;
|
|
135
|
+
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlsDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, defaultWrapper;
|
|
130
136
|
var init_client_generates = __esm({
|
|
131
137
|
"src/features/analysis/scm/generates/client_generates.ts"() {
|
|
132
138
|
"use strict";
|
|
@@ -962,6 +968,28 @@ var init_client_generates = __esm({
|
|
|
962
968
|
status
|
|
963
969
|
error
|
|
964
970
|
}
|
|
971
|
+
}
|
|
972
|
+
`;
|
|
973
|
+
UploadTracyRecordsDocument = `
|
|
974
|
+
mutation UploadTracyRecords($records: [TracyRecordInput!]!) {
|
|
975
|
+
uploadTracyRecords(records: $records) {
|
|
976
|
+
status
|
|
977
|
+
error
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
`;
|
|
981
|
+
GetTracyRawDataUploadUrlsDocument = `
|
|
982
|
+
mutation GetTracyRawDataUploadUrls($recordIds: [String!]!) {
|
|
983
|
+
getTracyRawDataUploadUrls(recordIds: $recordIds) {
|
|
984
|
+
status
|
|
985
|
+
error
|
|
986
|
+
uploads {
|
|
987
|
+
recordId
|
|
988
|
+
url
|
|
989
|
+
uploadFieldsJSON
|
|
990
|
+
uploadKey
|
|
991
|
+
}
|
|
992
|
+
}
|
|
965
993
|
}
|
|
966
994
|
`;
|
|
967
995
|
DigestVulnerabilityReportDocument = `
|
|
@@ -2276,20 +2304,20 @@ function computeCanonicalUrl(data) {
|
|
|
2276
2304
|
} = data;
|
|
2277
2305
|
switch (scmType) {
|
|
2278
2306
|
case "GitHub" /* GitHub */:
|
|
2279
|
-
return `https://${hostname}/${organization}/${repoName}`;
|
|
2307
|
+
return `https://${hostname}/${organization.toLowerCase()}/${repoName.toLowerCase()}`;
|
|
2280
2308
|
case "GitLab" /* GitLab */:
|
|
2281
|
-
return `https://${hostname}/${projectPath}`;
|
|
2309
|
+
return `https://${hostname}/${projectPath.toLowerCase()}`;
|
|
2282
2310
|
case "Bitbucket" /* Bitbucket */:
|
|
2283
|
-
return `https://${hostname}/${organization}/${repoName}`;
|
|
2311
|
+
return `https://${hostname}/${organization.toLowerCase()}/${repoName.toLowerCase()}`;
|
|
2284
2312
|
case "Ado" /* Ado */: {
|
|
2285
2313
|
const adoHostname = hostname === "ssh.dev.azure.com" ? "dev.azure.com" : hostname;
|
|
2286
2314
|
if (projectName) {
|
|
2287
|
-
return `https://${adoHostname}/${organization}/${projectName}/_git/${repoName}`;
|
|
2315
|
+
return `https://${adoHostname}/${organization.toLowerCase()}/${projectName.toLowerCase()}/_git/${repoName.toLowerCase()}`;
|
|
2288
2316
|
}
|
|
2289
|
-
return `https://${adoHostname}/${organization}/_git/${repoName}`;
|
|
2317
|
+
return `https://${adoHostname}/${organization.toLowerCase()}/_git/${repoName.toLowerCase()}`;
|
|
2290
2318
|
}
|
|
2291
2319
|
default:
|
|
2292
|
-
return `https://${hostname}/${projectPath}`;
|
|
2320
|
+
return `https://${hostname}/${projectPath.toLowerCase()}`;
|
|
2293
2321
|
}
|
|
2294
2322
|
}
|
|
2295
2323
|
function detectAdoUrl(args) {
|
|
@@ -4126,7 +4154,7 @@ ${rootContent}`;
|
|
|
4126
4154
|
});
|
|
4127
4155
|
|
|
4128
4156
|
// src/index.ts
|
|
4129
|
-
import
|
|
4157
|
+
import Debug22 from "debug";
|
|
4130
4158
|
import { hideBin } from "yargs/helpers";
|
|
4131
4159
|
|
|
4132
4160
|
// src/args/yargs.ts
|
|
@@ -11700,39 +11728,40 @@ var ScanContext = {
|
|
|
11700
11728
|
|
|
11701
11729
|
// src/args/commands/analyze.ts
|
|
11702
11730
|
import fs12 from "fs";
|
|
11703
|
-
import
|
|
11731
|
+
import chalk10 from "chalk";
|
|
11704
11732
|
|
|
11705
11733
|
// src/commands/index.ts
|
|
11706
|
-
import
|
|
11734
|
+
import chalk8 from "chalk";
|
|
11707
11735
|
import chalkAnimation from "chalk-animation";
|
|
11708
11736
|
|
|
11709
11737
|
// src/features/analysis/index.ts
|
|
11710
11738
|
import fs10 from "fs";
|
|
11711
|
-
import
|
|
11712
|
-
import
|
|
11739
|
+
import fsPromises3 from "fs/promises";
|
|
11740
|
+
import path10 from "path";
|
|
11713
11741
|
import { env as env2 } from "process";
|
|
11714
11742
|
import { pipeline } from "stream/promises";
|
|
11715
|
-
import
|
|
11716
|
-
import
|
|
11743
|
+
import chalk7 from "chalk";
|
|
11744
|
+
import Debug21 from "debug";
|
|
11717
11745
|
import extract from "extract-zip";
|
|
11718
11746
|
import { createSpinner as createSpinner4 } from "nanospinner";
|
|
11719
11747
|
import fetch4 from "node-fetch";
|
|
11720
11748
|
import open3 from "open";
|
|
11721
11749
|
import tmp2 from "tmp";
|
|
11722
|
-
import { z as
|
|
11750
|
+
import { z as z31 } from "zod";
|
|
11723
11751
|
|
|
11724
11752
|
// src/commands/handleMobbLogin.ts
|
|
11725
|
-
import
|
|
11726
|
-
import
|
|
11753
|
+
import chalk4 from "chalk";
|
|
11754
|
+
import Debug10 from "debug";
|
|
11727
11755
|
|
|
11728
11756
|
// src/commands/AuthManager.ts
|
|
11729
11757
|
import crypto from "crypto";
|
|
11730
|
-
import
|
|
11758
|
+
import os3 from "os";
|
|
11759
|
+
import Debug9 from "debug";
|
|
11731
11760
|
import open from "open";
|
|
11732
11761
|
|
|
11733
11762
|
// src/features/analysis/graphql/gql.ts
|
|
11734
11763
|
import Debug6 from "debug";
|
|
11735
|
-
import { GraphQLClient } from "graphql-request";
|
|
11764
|
+
import { ClientError, GraphQLClient } from "graphql-request";
|
|
11736
11765
|
import { v4 as uuidv4 } from "uuid";
|
|
11737
11766
|
|
|
11738
11767
|
// src/mcp/core/Errors.ts
|
|
@@ -11847,8 +11876,7 @@ function getProxyAgent(url) {
|
|
|
11847
11876
|
const isHttps = parsedUrl.protocol === "https:";
|
|
11848
11877
|
const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : null;
|
|
11849
11878
|
if (proxy) {
|
|
11850
|
-
debug6("Using proxy %s", proxy);
|
|
11851
|
-
debug6("Proxy agent %o", proxy);
|
|
11879
|
+
debug6("Using proxy %s for %s", proxy, url);
|
|
11852
11880
|
return new HttpsProxyAgent(proxy);
|
|
11853
11881
|
}
|
|
11854
11882
|
} catch (err) {
|
|
@@ -12069,6 +12097,19 @@ var GetVulByNodesMetadataZ = z26.object({
|
|
|
12069
12097
|
|
|
12070
12098
|
// src/features/analysis/graphql/gql.ts
|
|
12071
12099
|
var debug7 = Debug6("mobbdev:gql");
|
|
12100
|
+
function isAuthError(error) {
|
|
12101
|
+
if (error instanceof ClientError) {
|
|
12102
|
+
const gqlErrors = error.response?.errors;
|
|
12103
|
+
return gqlErrors?.some(
|
|
12104
|
+
(e) => e.extensions?.["code"] === "access-denied" || e.message?.includes("Authentication hook unauthorized")
|
|
12105
|
+
) ?? false;
|
|
12106
|
+
}
|
|
12107
|
+
return false;
|
|
12108
|
+
}
|
|
12109
|
+
function isNetworkError(error) {
|
|
12110
|
+
const errorString = error?.toString() ?? "";
|
|
12111
|
+
return errorString.includes("FetchError") || errorString.includes("TypeError") || errorString.includes("ECONNREFUSED") || errorString.includes("ENOTFOUND") || errorString.includes("ETIMEDOUT") || errorString.includes("UND_ERR");
|
|
12112
|
+
}
|
|
12072
12113
|
var API_KEY_HEADER_NAME = "x-mobb-key";
|
|
12073
12114
|
var REPORT_STATE_CHECK_DELAY = 5 * 1e3;
|
|
12074
12115
|
var GQLClient = class {
|
|
@@ -12128,23 +12169,35 @@ var GQLClient = class {
|
|
|
12128
12169
|
try {
|
|
12129
12170
|
await this.getUserInfo();
|
|
12130
12171
|
} catch (e) {
|
|
12131
|
-
if (e
|
|
12132
|
-
debug7("verify connection failed %o", e);
|
|
12172
|
+
if (isNetworkError(e)) {
|
|
12173
|
+
debug7("verify connection failed (network error) %o", e);
|
|
12133
12174
|
return false;
|
|
12134
12175
|
}
|
|
12176
|
+
debug7("verify connection: endpoint reachable but request failed %o", e);
|
|
12135
12177
|
}
|
|
12136
12178
|
return true;
|
|
12137
12179
|
}
|
|
12138
12180
|
async validateUserToken() {
|
|
12139
|
-
await this.createCommunityUser();
|
|
12140
|
-
let info;
|
|
12141
12181
|
try {
|
|
12142
|
-
|
|
12182
|
+
await this.createCommunityUser();
|
|
12183
|
+
const info = await this.getUserInfo();
|
|
12184
|
+
if (!info) {
|
|
12185
|
+
debug7("verify token failed - no user info returned");
|
|
12186
|
+
return false;
|
|
12187
|
+
}
|
|
12188
|
+
return info.email || true;
|
|
12143
12189
|
} catch (e) {
|
|
12144
|
-
|
|
12145
|
-
|
|
12190
|
+
if (isAuthError(e)) {
|
|
12191
|
+
debug7("verify token failed - auth error %o", e);
|
|
12192
|
+
return false;
|
|
12193
|
+
}
|
|
12194
|
+
if (isNetworkError(e)) {
|
|
12195
|
+
debug7("verify token failed - network error, rethrowing %o", e);
|
|
12196
|
+
throw e;
|
|
12197
|
+
}
|
|
12198
|
+
debug7("verify token failed - unexpected error, rethrowing %o", e);
|
|
12199
|
+
throw e;
|
|
12146
12200
|
}
|
|
12147
|
-
return info?.email || true;
|
|
12148
12201
|
}
|
|
12149
12202
|
async getLastOrgAndNamedProject(params) {
|
|
12150
12203
|
const me = await this.getUserInfo();
|
|
@@ -12500,6 +12553,12 @@ var GQLClient = class {
|
|
|
12500
12553
|
async finalizeAIBlameInferencesUploadRaw(variables) {
|
|
12501
12554
|
return await this._clientSdk.FinalizeAIBlameInferencesUpload(variables);
|
|
12502
12555
|
}
|
|
12556
|
+
async uploadTracyRecords(variables) {
|
|
12557
|
+
return await this._clientSdk.UploadTracyRecords(variables);
|
|
12558
|
+
}
|
|
12559
|
+
async getTracyRawDataUploadUrls(variables) {
|
|
12560
|
+
return await this._clientSdk.GetTracyRawDataUploadUrls(variables);
|
|
12561
|
+
}
|
|
12503
12562
|
async analyzeCommitForExtensionAIBlame(variables) {
|
|
12504
12563
|
return await this._clientSdk.AnalyzeCommitForExtensionAIBlame(variables);
|
|
12505
12564
|
}
|
|
@@ -12517,38 +12576,76 @@ var GQLClient = class {
|
|
|
12517
12576
|
}
|
|
12518
12577
|
};
|
|
12519
12578
|
|
|
12520
|
-
// src/
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12528
|
-
|
|
12529
|
-
|
|
12530
|
-
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12579
|
+
// src/features/analysis/graphql/tracy-batch-upload.ts
|
|
12580
|
+
import { promisify } from "util";
|
|
12581
|
+
import { gzip } from "zlib";
|
|
12582
|
+
import Debug8 from "debug";
|
|
12583
|
+
|
|
12584
|
+
// src/args/commands/upload_ai_blame.ts
|
|
12585
|
+
import fsPromises2 from "fs/promises";
|
|
12586
|
+
import * as os2 from "os";
|
|
12587
|
+
import path7 from "path";
|
|
12588
|
+
import chalk3 from "chalk";
|
|
12589
|
+
import { withFile } from "tmp-promise";
|
|
12590
|
+
import z27 from "zod";
|
|
12591
|
+
init_client_generates();
|
|
12592
|
+
init_GitService();
|
|
12593
|
+
init_urlParser2();
|
|
12594
|
+
|
|
12595
|
+
// src/features/analysis/upload-file.ts
|
|
12596
|
+
import Debug7 from "debug";
|
|
12597
|
+
import fetch3, { File, fileFrom, FormData } from "node-fetch";
|
|
12598
|
+
var debug8 = Debug7("mobbdev:upload-file");
|
|
12599
|
+
async function uploadFile({
|
|
12600
|
+
file,
|
|
12601
|
+
url,
|
|
12602
|
+
uploadKey,
|
|
12603
|
+
uploadFields,
|
|
12604
|
+
logger: logger2
|
|
12605
|
+
}) {
|
|
12606
|
+
const logInfo2 = logger2 || ((_message, _data) => {
|
|
12607
|
+
});
|
|
12608
|
+
logInfo2(`FileUpload: upload file start ${url}`);
|
|
12609
|
+
logInfo2(`FileUpload: upload fields`, uploadFields);
|
|
12610
|
+
logInfo2(`FileUpload: upload key ${uploadKey}`);
|
|
12611
|
+
debug8("upload file start %s", url);
|
|
12612
|
+
debug8("upload fields %o", uploadFields);
|
|
12613
|
+
debug8("upload key %s", uploadKey);
|
|
12614
|
+
const form = new FormData();
|
|
12615
|
+
Object.entries(uploadFields).forEach(([key, value]) => {
|
|
12616
|
+
form.append(key, value);
|
|
12617
|
+
});
|
|
12618
|
+
if (!form.has("key")) {
|
|
12619
|
+
form.append("key", uploadKey);
|
|
12548
12620
|
}
|
|
12549
|
-
|
|
12621
|
+
if (typeof file === "string") {
|
|
12622
|
+
debug8("upload file from path %s", file);
|
|
12623
|
+
logInfo2(`FileUpload: upload file from path ${file}`);
|
|
12624
|
+
form.append("file", await fileFrom(file));
|
|
12625
|
+
} else {
|
|
12626
|
+
debug8("upload file from buffer");
|
|
12627
|
+
logInfo2(`FileUpload: upload file from buffer`);
|
|
12628
|
+
form.append("file", new File([new Uint8Array(file)], "file"));
|
|
12629
|
+
}
|
|
12630
|
+
const agent = getProxyAgent(url);
|
|
12631
|
+
const response = await fetch3(url, {
|
|
12632
|
+
method: "POST",
|
|
12633
|
+
body: form,
|
|
12634
|
+
agent
|
|
12635
|
+
});
|
|
12636
|
+
if (!response.ok) {
|
|
12637
|
+
debug8("error from S3 %s %s", response.body, response.status);
|
|
12638
|
+
logInfo2(`FileUpload: error from S3 ${response.body} ${response.status}`);
|
|
12639
|
+
throw new Error(`Failed to upload the file: ${response.status}`);
|
|
12640
|
+
}
|
|
12641
|
+
debug8("upload file done");
|
|
12642
|
+
logInfo2(`FileUpload: upload file done`);
|
|
12550
12643
|
}
|
|
12551
12644
|
|
|
12645
|
+
// src/utils/computerName.ts
|
|
12646
|
+
import { execSync } from "child_process";
|
|
12647
|
+
import os from "os";
|
|
12648
|
+
|
|
12552
12649
|
// src/utils/ConfigStoreService.ts
|
|
12553
12650
|
import Configstore from "configstore";
|
|
12554
12651
|
function createConfigStore(defaultValues = { apiToken: "" }) {
|
|
@@ -12568,339 +12665,941 @@ function getConfigStore() {
|
|
|
12568
12665
|
}
|
|
12569
12666
|
var configStore = getConfigStore();
|
|
12570
12667
|
|
|
12571
|
-
// src/
|
|
12572
|
-
var
|
|
12573
|
-
var
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12580
|
-
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12668
|
+
// src/utils/computerName.ts
|
|
12669
|
+
var STABLE_COMPUTER_NAME_CONFIG_KEY = "stableComputerName";
|
|
12670
|
+
var HOSTNAME_SUFFIXES = [
|
|
12671
|
+
// Cloud providers (must be first - most specific)
|
|
12672
|
+
".ec2.internal",
|
|
12673
|
+
".compute.internal",
|
|
12674
|
+
".cloudapp.net",
|
|
12675
|
+
// mDNS/Bonjour
|
|
12676
|
+
".local",
|
|
12677
|
+
".localhost",
|
|
12678
|
+
".localdomain",
|
|
12679
|
+
// Home networks
|
|
12680
|
+
".lan",
|
|
12681
|
+
".home",
|
|
12682
|
+
".homelan",
|
|
12683
|
+
// Corporate networks
|
|
12684
|
+
".corp",
|
|
12685
|
+
".internal",
|
|
12686
|
+
".intranet",
|
|
12687
|
+
".domain",
|
|
12688
|
+
".work",
|
|
12689
|
+
".office",
|
|
12690
|
+
// Container environments
|
|
12691
|
+
".docker",
|
|
12692
|
+
".kubernetes",
|
|
12693
|
+
".k8s"
|
|
12694
|
+
];
|
|
12695
|
+
function getOsSpecificComputerName() {
|
|
12696
|
+
const platform2 = os.platform();
|
|
12697
|
+
try {
|
|
12698
|
+
if (platform2 === "darwin") {
|
|
12699
|
+
const result = execSync("scutil --get LocalHostName", {
|
|
12700
|
+
encoding: "utf-8",
|
|
12701
|
+
timeout: 1e3
|
|
12702
|
+
});
|
|
12703
|
+
return result.trim();
|
|
12704
|
+
} else if (platform2 === "win32") {
|
|
12705
|
+
return process.env["COMPUTERNAME"] || null;
|
|
12706
|
+
} else {
|
|
12707
|
+
try {
|
|
12708
|
+
const result2 = execSync("hostnamectl --static", {
|
|
12709
|
+
encoding: "utf-8",
|
|
12710
|
+
timeout: 1e3
|
|
12711
|
+
});
|
|
12712
|
+
const hostname = result2.trim();
|
|
12713
|
+
if (hostname) return hostname;
|
|
12714
|
+
} catch {
|
|
12600
12715
|
}
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
this.gqlClient = new GQLClient({
|
|
12607
|
-
apiKey: newApiToken,
|
|
12608
|
-
type: "apiKey",
|
|
12609
|
-
apiUrl: this.resolvedApiUrl
|
|
12610
|
-
});
|
|
12611
|
-
const loginSuccess = await this.gqlClient.validateUserToken();
|
|
12612
|
-
if (loginSuccess) {
|
|
12613
|
-
configStore.set("apiToken", newApiToken);
|
|
12614
|
-
this.authenticated = true;
|
|
12615
|
-
return true;
|
|
12716
|
+
const result = execSync("cat /etc/hostname", {
|
|
12717
|
+
encoding: "utf-8",
|
|
12718
|
+
timeout: 1e3
|
|
12719
|
+
});
|
|
12720
|
+
return result.trim();
|
|
12616
12721
|
}
|
|
12617
|
-
|
|
12722
|
+
} catch (error) {
|
|
12723
|
+
return null;
|
|
12618
12724
|
}
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12622
|
-
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12725
|
+
}
|
|
12726
|
+
function stripHostnameSuffixes(hostname) {
|
|
12727
|
+
if (!hostname) return hostname;
|
|
12728
|
+
let normalized = hostname;
|
|
12729
|
+
for (const suffix of HOSTNAME_SUFFIXES) {
|
|
12730
|
+
if (normalized.endsWith(suffix)) {
|
|
12731
|
+
normalized = normalized.slice(0, -suffix.length);
|
|
12732
|
+
break;
|
|
12626
12733
|
}
|
|
12627
|
-
return this.authenticated;
|
|
12628
12734
|
}
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12735
|
+
return normalized;
|
|
12736
|
+
}
|
|
12737
|
+
function generateStableComputerName() {
|
|
12738
|
+
const osSpecificName = getOsSpecificComputerName();
|
|
12739
|
+
if (osSpecificName) {
|
|
12740
|
+
return osSpecificName;
|
|
12741
|
+
}
|
|
12742
|
+
const currentHostname = os.hostname();
|
|
12743
|
+
return stripHostnameSuffixes(currentHostname);
|
|
12744
|
+
}
|
|
12745
|
+
function getStableComputerName() {
|
|
12746
|
+
const cached = configStore.get(STABLE_COMPUTER_NAME_CONFIG_KEY);
|
|
12747
|
+
if (cached) {
|
|
12748
|
+
return cached;
|
|
12749
|
+
}
|
|
12750
|
+
const currentName = generateStableComputerName();
|
|
12751
|
+
configStore.set(STABLE_COMPUTER_NAME_CONFIG_KEY, currentName);
|
|
12752
|
+
return currentName;
|
|
12753
|
+
}
|
|
12754
|
+
|
|
12755
|
+
// src/utils/sanitize-sensitive-data.ts
|
|
12756
|
+
import { OpenRedaction } from "@openredaction/openredaction";
|
|
12757
|
+
var openRedaction = new OpenRedaction({
|
|
12758
|
+
patterns: [
|
|
12759
|
+
// Core Personal Data
|
|
12760
|
+
// Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
|
|
12761
|
+
// Prefer false negatives over false positives for this use case.
|
|
12762
|
+
"SSN",
|
|
12763
|
+
"NATIONAL_INSURANCE_UK",
|
|
12764
|
+
"DATE_OF_BIRTH",
|
|
12765
|
+
// Identity Documents
|
|
12766
|
+
"PASSPORT_UK",
|
|
12767
|
+
"PASSPORT_US",
|
|
12768
|
+
"PASSPORT_MRZ_TD1",
|
|
12769
|
+
"PASSPORT_MRZ_TD3",
|
|
12770
|
+
"DRIVING_LICENSE_UK",
|
|
12771
|
+
"DRIVING_LICENSE_US",
|
|
12772
|
+
"VISA_NUMBER",
|
|
12773
|
+
"VISA_MRZ",
|
|
12774
|
+
"TAX_ID",
|
|
12775
|
+
// Financial Data (removed SWIFT_BIC, CARD_AUTH_CODE - too broad, causing false positives with authentication words)
|
|
12776
|
+
// Removed CREDIT_CARD - causes false positives on zero-filled UUIDs (e.g. '00000000-0000-0000-0000-000000000000')
|
|
12777
|
+
// Prefer false negatives over false positives for this use case.
|
|
12778
|
+
"IBAN",
|
|
12779
|
+
"BANK_ACCOUNT_UK",
|
|
12780
|
+
"ROUTING_NUMBER_US",
|
|
12781
|
+
"CARD_TRACK1_DATA",
|
|
12782
|
+
"CARD_TRACK2_DATA",
|
|
12783
|
+
"CARD_EXPIRY",
|
|
12784
|
+
// Cryptocurrency (removed BITCOIN_ADDRESS - too broad, matches hash-like strings)
|
|
12785
|
+
"ETHEREUM_ADDRESS",
|
|
12786
|
+
"LITECOIN_ADDRESS",
|
|
12787
|
+
"CARDANO_ADDRESS",
|
|
12788
|
+
"SOLANA_ADDRESS",
|
|
12789
|
+
"MONERO_ADDRESS",
|
|
12790
|
+
"RIPPLE_ADDRESS",
|
|
12791
|
+
// Medical Data (removed PRESCRIPTION_NUMBER - too broad, matches words containing "ription")
|
|
12792
|
+
// Removed MEDICAL_RECORD_NUMBER - too broad, "MR" prefix matches "Merge Request" in SCM contexts (e.g. "MR branches" → "MR br****es")
|
|
12793
|
+
"NHS_NUMBER",
|
|
12794
|
+
"AUSTRALIAN_MEDICARE",
|
|
12795
|
+
"HEALTH_PLAN_NUMBER",
|
|
12796
|
+
"PATIENT_ID",
|
|
12797
|
+
// Communications (removed EMERGENCY_CONTACT, ADDRESS_PO_BOX, ZIP_CODE_US, PHONE_US, PHONE_INTERNATIONAL - too broad, causing false positives)
|
|
12798
|
+
"PHONE_UK",
|
|
12799
|
+
"PHONE_UK_MOBILE",
|
|
12800
|
+
"PHONE_LINE_NUMBER",
|
|
12801
|
+
"ADDRESS_STREET",
|
|
12802
|
+
"POSTCODE_UK",
|
|
12803
|
+
// Network & Technical
|
|
12804
|
+
"IPV4",
|
|
12805
|
+
"IPV6",
|
|
12806
|
+
"MAC_ADDRESS",
|
|
12807
|
+
"URL_WITH_AUTH",
|
|
12808
|
+
// Security Keys & Tokens
|
|
12809
|
+
"PRIVATE_KEY",
|
|
12810
|
+
"SSH_PRIVATE_KEY",
|
|
12811
|
+
"AWS_SECRET_KEY",
|
|
12812
|
+
"AWS_ACCESS_KEY",
|
|
12813
|
+
"AZURE_STORAGE_KEY",
|
|
12814
|
+
"GCP_SERVICE_ACCOUNT",
|
|
12815
|
+
"JWT_TOKEN",
|
|
12816
|
+
"OAUTH_TOKEN",
|
|
12817
|
+
"OAUTH_CLIENT_SECRET",
|
|
12818
|
+
"BEARER_TOKEN",
|
|
12819
|
+
"PAYMENT_TOKEN",
|
|
12820
|
+
"GENERIC_SECRET",
|
|
12821
|
+
"GENERIC_API_KEY",
|
|
12822
|
+
// Platform-Specific API Keys
|
|
12823
|
+
"GITHUB_TOKEN",
|
|
12824
|
+
"SLACK_TOKEN",
|
|
12825
|
+
"STRIPE_API_KEY",
|
|
12826
|
+
"GOOGLE_API_KEY",
|
|
12827
|
+
"FIREBASE_API_KEY",
|
|
12828
|
+
"HEROKU_API_KEY",
|
|
12829
|
+
"MAILGUN_API_KEY",
|
|
12830
|
+
"SENDGRID_API_KEY",
|
|
12831
|
+
"TWILIO_API_KEY",
|
|
12832
|
+
"NPM_TOKEN",
|
|
12833
|
+
"PYPI_TOKEN",
|
|
12834
|
+
"DOCKER_AUTH",
|
|
12835
|
+
"KUBERNETES_SECRET",
|
|
12836
|
+
// Government & Legal
|
|
12837
|
+
// Removed CLIENT_ID - too broad, "client" is ubiquitous in code (npm packages like @scope/client-*, class names like ClientSdkOptions)
|
|
12838
|
+
"POLICE_REPORT_NUMBER",
|
|
12839
|
+
"IMMIGRATION_NUMBER",
|
|
12840
|
+
"COURT_REPORTER_LICENSE"
|
|
12841
|
+
]
|
|
12842
|
+
});
|
|
12843
|
+
function maskString(str, showStart = 2, showEnd = 2) {
|
|
12844
|
+
if (str.length <= showStart + showEnd) {
|
|
12845
|
+
return "*".repeat(str.length);
|
|
12846
|
+
}
|
|
12847
|
+
return str.slice(0, showStart) + "*".repeat(str.length - showStart - showEnd) + str.slice(-showEnd);
|
|
12848
|
+
}
|
|
12849
|
+
async function sanitizeDataWithCounts(obj) {
|
|
12850
|
+
const counts = {
|
|
12851
|
+
detections: { total: 0, high: 0, medium: 0, low: 0 }
|
|
12852
|
+
};
|
|
12853
|
+
const sanitizeString = async (str) => {
|
|
12854
|
+
let result = str;
|
|
12855
|
+
const piiDetections = openRedaction.scan(str);
|
|
12856
|
+
if (piiDetections && piiDetections.total > 0) {
|
|
12857
|
+
const allDetections = [
|
|
12858
|
+
...piiDetections.high,
|
|
12859
|
+
...piiDetections.medium,
|
|
12860
|
+
...piiDetections.low
|
|
12861
|
+
];
|
|
12862
|
+
for (const detection of allDetections) {
|
|
12863
|
+
counts.detections.total++;
|
|
12864
|
+
if (detection.severity === "high") counts.detections.high++;
|
|
12865
|
+
else if (detection.severity === "medium") counts.detections.medium++;
|
|
12866
|
+
else if (detection.severity === "low") counts.detections.low++;
|
|
12867
|
+
const masked = maskString(detection.value);
|
|
12868
|
+
result = result.replaceAll(detection.value, masked);
|
|
12650
12869
|
}
|
|
12651
|
-
} catch (error) {
|
|
12652
|
-
return {
|
|
12653
|
-
isAuthenticated: false,
|
|
12654
|
-
message: error instanceof Error ? error.message : "Unknown authentication error"
|
|
12655
|
-
};
|
|
12656
12870
|
}
|
|
12657
|
-
return
|
|
12658
|
-
}
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12871
|
+
return result;
|
|
12872
|
+
};
|
|
12873
|
+
const sanitizeRecursive = async (data) => {
|
|
12874
|
+
if (typeof data === "string") {
|
|
12875
|
+
return sanitizeString(data);
|
|
12876
|
+
} else if (Array.isArray(data)) {
|
|
12877
|
+
return Promise.all(data.map((item) => sanitizeRecursive(item)));
|
|
12878
|
+
} else if (data instanceof Error) {
|
|
12879
|
+
return data;
|
|
12880
|
+
} else if (data instanceof Date) {
|
|
12881
|
+
return data;
|
|
12882
|
+
} else if (typeof data === "object" && data !== null) {
|
|
12883
|
+
const sanitized = {};
|
|
12884
|
+
const record = data;
|
|
12885
|
+
for (const key in record) {
|
|
12886
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
12887
|
+
sanitized[key] = await sanitizeRecursive(record[key]);
|
|
12888
|
+
}
|
|
12666
12889
|
}
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12681
|
-
|
|
12890
|
+
return sanitized;
|
|
12891
|
+
}
|
|
12892
|
+
return data;
|
|
12893
|
+
};
|
|
12894
|
+
const sanitizedData = await sanitizeRecursive(obj);
|
|
12895
|
+
return { sanitizedData, counts };
|
|
12896
|
+
}
|
|
12897
|
+
|
|
12898
|
+
// src/args/commands/upload_ai_blame.ts
|
|
12899
|
+
var defaultLogger2 = {
|
|
12900
|
+
info: (msg, data) => {
|
|
12901
|
+
if (data !== void 0) {
|
|
12902
|
+
console.log(msg, data);
|
|
12903
|
+
} else {
|
|
12904
|
+
console.log(msg);
|
|
12905
|
+
}
|
|
12906
|
+
},
|
|
12907
|
+
error: (msg, data) => {
|
|
12908
|
+
if (data !== void 0) {
|
|
12909
|
+
console.error(msg, data);
|
|
12910
|
+
} else {
|
|
12911
|
+
console.error(msg);
|
|
12682
12912
|
}
|
|
12683
12913
|
}
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12914
|
+
};
|
|
12915
|
+
var PromptItemZ = z27.object({
|
|
12916
|
+
type: z27.enum([
|
|
12917
|
+
"USER_PROMPT",
|
|
12918
|
+
"AI_RESPONSE",
|
|
12919
|
+
"TOOL_EXECUTION",
|
|
12920
|
+
"AI_THINKING",
|
|
12921
|
+
"MCP_TOOL_CALL"
|
|
12922
|
+
// MCP (Model Context Protocol) tool invocation
|
|
12923
|
+
]),
|
|
12924
|
+
attachedFiles: z27.array(
|
|
12925
|
+
z27.object({
|
|
12926
|
+
relativePath: z27.string(),
|
|
12927
|
+
startLine: z27.number().optional()
|
|
12928
|
+
})
|
|
12929
|
+
).optional(),
|
|
12930
|
+
tokens: z27.object({
|
|
12931
|
+
inputCount: z27.number(),
|
|
12932
|
+
outputCount: z27.number()
|
|
12933
|
+
}).optional(),
|
|
12934
|
+
text: z27.string().optional(),
|
|
12935
|
+
date: z27.date().optional(),
|
|
12936
|
+
tool: z27.object({
|
|
12937
|
+
name: z27.string(),
|
|
12938
|
+
parameters: z27.string(),
|
|
12939
|
+
result: z27.string(),
|
|
12940
|
+
rawArguments: z27.string().optional(),
|
|
12941
|
+
accepted: z27.boolean().optional(),
|
|
12942
|
+
// MCP-specific fields (only populated for MCP_TOOL_CALL type)
|
|
12943
|
+
mcpServer: z27.string().optional(),
|
|
12944
|
+
// MCP server name (e.g., "datadog", "mobb-mcp")
|
|
12945
|
+
mcpToolName: z27.string().optional()
|
|
12946
|
+
// MCP tool name without prefix (e.g., "scan_and_fix_vulnerabilities")
|
|
12947
|
+
}).optional()
|
|
12948
|
+
});
|
|
12949
|
+
var PromptItemArrayZ = z27.array(PromptItemZ);
|
|
12950
|
+
async function getRepositoryUrl() {
|
|
12951
|
+
try {
|
|
12952
|
+
const gitService = new GitService(process.cwd());
|
|
12953
|
+
const isRepo = await gitService.isGitRepository();
|
|
12954
|
+
if (!isRepo) {
|
|
12689
12955
|
return null;
|
|
12690
12956
|
}
|
|
12691
|
-
const
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
return crypto.privateDecrypt(
|
|
12696
|
-
this.privateKey,
|
|
12697
|
-
Buffer.from(encryptedApiToken, "base64")
|
|
12698
|
-
).toString("utf-8");
|
|
12699
|
-
}
|
|
12957
|
+
const remoteUrl = await gitService.getRemoteUrl();
|
|
12958
|
+
const parsed = parseScmURL(remoteUrl);
|
|
12959
|
+
return parsed?.scmType === "GitHub" /* GitHub */ || parsed?.scmType === "GitLab" /* GitLab */ ? remoteUrl : null;
|
|
12960
|
+
} catch {
|
|
12700
12961
|
return null;
|
|
12701
12962
|
}
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12963
|
+
}
|
|
12964
|
+
function getSystemInfo() {
|
|
12965
|
+
let userName;
|
|
12966
|
+
try {
|
|
12967
|
+
userName = os2.userInfo().username;
|
|
12968
|
+
} catch {
|
|
12969
|
+
userName = void 0;
|
|
12970
|
+
}
|
|
12971
|
+
return {
|
|
12972
|
+
computerName: getStableComputerName(),
|
|
12973
|
+
userName
|
|
12974
|
+
};
|
|
12975
|
+
}
|
|
12976
|
+
function uploadAiBlameBuilder(args) {
|
|
12977
|
+
return args.option("prompt", {
|
|
12978
|
+
type: "string",
|
|
12979
|
+
array: true,
|
|
12980
|
+
demandOption: true,
|
|
12981
|
+
describe: chalk3.bold("Path(s) to prompt artifact(s) (one per session)")
|
|
12982
|
+
}).option("inference", {
|
|
12983
|
+
type: "string",
|
|
12984
|
+
array: true,
|
|
12985
|
+
demandOption: true,
|
|
12986
|
+
describe: chalk3.bold(
|
|
12987
|
+
"Path(s) to inference artifact(s) (one per session)"
|
|
12988
|
+
)
|
|
12989
|
+
}).option("ai-response-at", {
|
|
12990
|
+
type: "string",
|
|
12991
|
+
array: true,
|
|
12992
|
+
describe: chalk3.bold(
|
|
12993
|
+
"ISO timestamp(s) for AI response (one per session, defaults to now)"
|
|
12994
|
+
)
|
|
12995
|
+
}).option("model", {
|
|
12996
|
+
type: "string",
|
|
12997
|
+
array: true,
|
|
12998
|
+
describe: chalk3.bold("AI model name(s) (optional, one per session)")
|
|
12999
|
+
}).option("tool-name", {
|
|
13000
|
+
type: "string",
|
|
13001
|
+
array: true,
|
|
13002
|
+
describe: chalk3.bold("Tool/IDE name(s) (optional, one per session)")
|
|
13003
|
+
}).option("blame-type", {
|
|
13004
|
+
type: "string",
|
|
13005
|
+
array: true,
|
|
13006
|
+
choices: Object.values(AiBlameInferenceType),
|
|
13007
|
+
describe: chalk3.bold(
|
|
13008
|
+
"Blame type(s) (optional, one per session, defaults to CHAT)"
|
|
13009
|
+
)
|
|
13010
|
+
}).strict();
|
|
13011
|
+
}
|
|
13012
|
+
async function uploadAiBlameHandlerFromExtension(args) {
|
|
13013
|
+
const uploadArgs = {
|
|
13014
|
+
prompt: [],
|
|
13015
|
+
inference: [],
|
|
13016
|
+
model: [],
|
|
13017
|
+
toolName: [],
|
|
13018
|
+
aiResponseAt: [],
|
|
13019
|
+
blameType: [],
|
|
13020
|
+
sessionId: []
|
|
13021
|
+
};
|
|
13022
|
+
let promptsCounts;
|
|
13023
|
+
let inferenceCounts;
|
|
13024
|
+
let promptsUUID;
|
|
13025
|
+
let inferenceUUID;
|
|
13026
|
+
await withFile(async (promptFile) => {
|
|
13027
|
+
const promptsResult = await sanitizeDataWithCounts(args.prompts);
|
|
13028
|
+
promptsCounts = promptsResult.counts;
|
|
13029
|
+
promptsUUID = path7.basename(promptFile.path, path7.extname(promptFile.path));
|
|
13030
|
+
await fsPromises2.writeFile(
|
|
13031
|
+
promptFile.path,
|
|
13032
|
+
JSON.stringify(promptsResult.sanitizedData, null, 2),
|
|
13033
|
+
"utf-8"
|
|
13034
|
+
);
|
|
13035
|
+
uploadArgs.prompt.push(promptFile.path);
|
|
13036
|
+
await withFile(async (inferenceFile) => {
|
|
13037
|
+
const inferenceResult = await sanitizeDataWithCounts(args.inference);
|
|
13038
|
+
inferenceCounts = inferenceResult.counts;
|
|
13039
|
+
inferenceUUID = path7.basename(
|
|
13040
|
+
inferenceFile.path,
|
|
13041
|
+
path7.extname(inferenceFile.path)
|
|
13042
|
+
);
|
|
13043
|
+
await fsPromises2.writeFile(
|
|
13044
|
+
inferenceFile.path,
|
|
13045
|
+
inferenceResult.sanitizedData,
|
|
13046
|
+
"utf-8"
|
|
13047
|
+
);
|
|
13048
|
+
uploadArgs.inference.push(inferenceFile.path);
|
|
13049
|
+
uploadArgs.model.push(args.model);
|
|
13050
|
+
uploadArgs.toolName.push(args.tool);
|
|
13051
|
+
uploadArgs.aiResponseAt.push(args.responseTime);
|
|
13052
|
+
uploadArgs.blameType.push(args.blameType || "CHAT" /* Chat */);
|
|
13053
|
+
if (args.sessionId) {
|
|
13054
|
+
uploadArgs.sessionId.push(args.sessionId);
|
|
13055
|
+
}
|
|
13056
|
+
await uploadAiBlameHandler({
|
|
13057
|
+
args: uploadArgs,
|
|
13058
|
+
exitOnError: false,
|
|
13059
|
+
apiUrl: args.apiUrl,
|
|
13060
|
+
webAppUrl: args.webAppUrl,
|
|
13061
|
+
repositoryUrl: args.repositoryUrl
|
|
12711
13062
|
});
|
|
13063
|
+
});
|
|
13064
|
+
});
|
|
13065
|
+
return {
|
|
13066
|
+
promptsCounts,
|
|
13067
|
+
inferenceCounts,
|
|
13068
|
+
promptsUUID,
|
|
13069
|
+
inferenceUUID
|
|
13070
|
+
};
|
|
13071
|
+
}
|
|
13072
|
+
async function uploadAiBlameHandler(options) {
|
|
13073
|
+
const {
|
|
13074
|
+
args,
|
|
13075
|
+
exitOnError = true,
|
|
13076
|
+
apiUrl,
|
|
13077
|
+
webAppUrl,
|
|
13078
|
+
logger: logger2 = defaultLogger2
|
|
13079
|
+
} = options;
|
|
13080
|
+
const prompts = args.prompt || [];
|
|
13081
|
+
const inferences = args.inference || [];
|
|
13082
|
+
const models = args.model || [];
|
|
13083
|
+
const tools = args.toolName || args["tool-name"] || [];
|
|
13084
|
+
const responseTimes = args.aiResponseAt || args["ai-response-at"] || [];
|
|
13085
|
+
const blameTypes = args.blameType || args["blame-type"] || [];
|
|
13086
|
+
const sessionIds = args.sessionId || args["session-id"] || [];
|
|
13087
|
+
if (prompts.length !== inferences.length) {
|
|
13088
|
+
const errorMsg = "prompt and inference must have the same number of entries";
|
|
13089
|
+
logger2.error(chalk3.red(errorMsg));
|
|
13090
|
+
if (exitOnError) {
|
|
13091
|
+
process.exit(1);
|
|
12712
13092
|
}
|
|
12713
|
-
|
|
12714
|
-
}
|
|
12715
|
-
/**
|
|
12716
|
-
* Assigns a GQL client instance to the AuthManager, and resets auth state
|
|
12717
|
-
* @param gqlClient The GQL client instance to set
|
|
12718
|
-
*/
|
|
12719
|
-
setGQLClient(gqlClient) {
|
|
12720
|
-
this.gqlClient = gqlClient;
|
|
12721
|
-
this.cleanup();
|
|
13093
|
+
throw new Error(errorMsg);
|
|
12722
13094
|
}
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
13095
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
13096
|
+
const { computerName, userName } = getSystemInfo();
|
|
13097
|
+
const repositoryUrl = options.repositoryUrl !== void 0 ? options.repositoryUrl : await getRepositoryUrl();
|
|
13098
|
+
const sessions = [];
|
|
13099
|
+
for (let i = 0; i < prompts.length; i++) {
|
|
13100
|
+
const promptPath = String(prompts[i]);
|
|
13101
|
+
const inferencePath = String(inferences[i]);
|
|
13102
|
+
try {
|
|
13103
|
+
await Promise.all([
|
|
13104
|
+
fsPromises2.access(promptPath),
|
|
13105
|
+
fsPromises2.access(inferencePath)
|
|
13106
|
+
]);
|
|
13107
|
+
} catch {
|
|
13108
|
+
const errorMsg = `File not found for session ${i + 1}`;
|
|
13109
|
+
logger2.error(chalk3.red(errorMsg));
|
|
13110
|
+
if (exitOnError) {
|
|
13111
|
+
process.exit(1);
|
|
13112
|
+
}
|
|
13113
|
+
throw new Error(errorMsg);
|
|
13114
|
+
}
|
|
13115
|
+
sessions.push({
|
|
13116
|
+
promptFileName: path7.basename(promptPath),
|
|
13117
|
+
inferenceFileName: path7.basename(inferencePath),
|
|
13118
|
+
aiResponseAt: responseTimes[i] || nowIso,
|
|
13119
|
+
model: models[i],
|
|
13120
|
+
toolName: tools[i],
|
|
13121
|
+
blameType: blameTypes[i] || "CHAT" /* Chat */,
|
|
13122
|
+
computerName,
|
|
13123
|
+
userName,
|
|
13124
|
+
sessionId: sessionIds[i],
|
|
13125
|
+
repositoryUrl
|
|
13126
|
+
});
|
|
12732
13127
|
}
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
// src/commands/handleMobbLogin.ts
|
|
12736
|
-
var debug8 = Debug7("mobbdev:commands");
|
|
12737
|
-
var LOGIN_MAX_WAIT2 = 10 * 60 * 1e3;
|
|
12738
|
-
var LOGIN_CHECK_DELAY2 = 5 * 1e3;
|
|
12739
|
-
var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${chalk3.bgBlue(
|
|
12740
|
-
"press any key to continue"
|
|
12741
|
-
)};`;
|
|
12742
|
-
async function getAuthenticatedGQLClient({
|
|
12743
|
-
inputApiKey = "",
|
|
12744
|
-
isSkipPrompts = true,
|
|
12745
|
-
apiUrl,
|
|
12746
|
-
webAppUrl
|
|
12747
|
-
}) {
|
|
12748
|
-
debug8(
|
|
12749
|
-
"getAuthenticatedGQLClient called with: apiUrl=%s, webAppUrl=%s",
|
|
12750
|
-
apiUrl || "undefined",
|
|
12751
|
-
webAppUrl || "undefined"
|
|
12752
|
-
);
|
|
12753
|
-
const authManager = new AuthManager(webAppUrl, apiUrl);
|
|
12754
|
-
let gqlClient = authManager.getGQLClient(inputApiKey);
|
|
12755
|
-
gqlClient = await handleMobbLogin({
|
|
12756
|
-
inGqlClient: gqlClient,
|
|
12757
|
-
skipPrompts: isSkipPrompts,
|
|
13128
|
+
const authenticatedClient = await getAuthenticatedGQLClient({
|
|
13129
|
+
isSkipPrompts: true,
|
|
12758
13130
|
apiUrl,
|
|
12759
13131
|
webAppUrl
|
|
12760
13132
|
});
|
|
12761
|
-
|
|
12762
|
-
}
|
|
12763
|
-
async function handleMobbLogin({
|
|
12764
|
-
inGqlClient,
|
|
12765
|
-
apiKey,
|
|
12766
|
-
skipPrompts,
|
|
12767
|
-
apiUrl,
|
|
12768
|
-
webAppUrl,
|
|
12769
|
-
loginContext
|
|
12770
|
-
}) {
|
|
12771
|
-
debug8(
|
|
12772
|
-
"handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
|
|
12773
|
-
apiUrl || "fallback",
|
|
12774
|
-
apiUrl || "fallback",
|
|
12775
|
-
webAppUrl || "fallback",
|
|
12776
|
-
webAppUrl || "fallback"
|
|
13133
|
+
const initSessions = sessions.map(
|
|
13134
|
+
({ sessionId: _sessionId, ...rest }) => rest
|
|
12777
13135
|
);
|
|
12778
|
-
const
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
return authManager.getGQLClient();
|
|
13136
|
+
const initRes = await authenticatedClient.uploadAIBlameInferencesInitRaw({
|
|
13137
|
+
sessions: initSessions
|
|
13138
|
+
});
|
|
13139
|
+
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
13140
|
+
if (uploadSessions.length !== sessions.length) {
|
|
13141
|
+
const errorMsg = "Init failed to return expected number of sessions";
|
|
13142
|
+
logger2.error(chalk3.red(errorMsg));
|
|
13143
|
+
if (exitOnError) {
|
|
13144
|
+
process.exit(1);
|
|
12788
13145
|
}
|
|
12789
|
-
|
|
12790
|
-
debug8("Authentication check failed:", error);
|
|
13146
|
+
throw new Error(errorMsg);
|
|
12791
13147
|
}
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
13148
|
+
for (let i = 0; i < uploadSessions.length; i++) {
|
|
13149
|
+
const us = uploadSessions[i];
|
|
13150
|
+
const promptPath = String(prompts[i]);
|
|
13151
|
+
const inferencePath = String(inferences[i]);
|
|
13152
|
+
await Promise.all([
|
|
13153
|
+
// Prompt
|
|
13154
|
+
uploadFile({
|
|
13155
|
+
file: promptPath,
|
|
13156
|
+
url: us.prompt.url,
|
|
13157
|
+
uploadFields: JSON.parse(us.prompt.uploadFieldsJSON),
|
|
13158
|
+
uploadKey: us.prompt.uploadKey
|
|
13159
|
+
}),
|
|
13160
|
+
// Inference
|
|
13161
|
+
uploadFile({
|
|
13162
|
+
file: inferencePath,
|
|
13163
|
+
url: us.inference.url,
|
|
13164
|
+
uploadFields: JSON.parse(us.inference.uploadFieldsJSON),
|
|
13165
|
+
uploadKey: us.inference.uploadKey
|
|
13166
|
+
})
|
|
13167
|
+
]);
|
|
12799
13168
|
}
|
|
12800
|
-
const
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
13169
|
+
const finalizeSessions = uploadSessions.map((us, i) => {
|
|
13170
|
+
const s = sessions[i];
|
|
13171
|
+
return {
|
|
13172
|
+
aiBlameInferenceId: us.aiBlameInferenceId,
|
|
13173
|
+
promptKey: us.prompt.uploadKey,
|
|
13174
|
+
inferenceKey: us.inference.uploadKey,
|
|
13175
|
+
aiResponseAt: s.aiResponseAt,
|
|
13176
|
+
model: s.model,
|
|
13177
|
+
toolName: s.toolName,
|
|
13178
|
+
blameType: s.blameType,
|
|
13179
|
+
computerName: s.computerName,
|
|
13180
|
+
userName: s.userName,
|
|
13181
|
+
sessionId: s.sessionId,
|
|
13182
|
+
repositoryUrl: s.repositoryUrl
|
|
13183
|
+
};
|
|
12807
13184
|
});
|
|
12808
13185
|
try {
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
loginSpinner.error({
|
|
12812
|
-
text: "Failed to generate login URL"
|
|
12813
|
-
});
|
|
12814
|
-
throw new CliError("Failed to generate login URL");
|
|
12815
|
-
}
|
|
12816
|
-
!skipPrompts && console.log(
|
|
12817
|
-
`If the page does not open automatically, kindly access it through ${loginUrl}.`
|
|
13186
|
+
logger2.info(
|
|
13187
|
+
`[UPLOAD] Calling finalizeAIBlameInferencesUploadRaw with ${finalizeSessions.length} sessions`
|
|
12818
13188
|
);
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
|
|
12825
|
-
|
|
13189
|
+
const finRes = await authenticatedClient.finalizeAIBlameInferencesUploadRaw(
|
|
13190
|
+
{
|
|
13191
|
+
sessions: finalizeSessions
|
|
13192
|
+
}
|
|
13193
|
+
);
|
|
13194
|
+
logger2.info("[UPLOAD] Finalize response:", JSON.stringify(finRes, null, 2));
|
|
13195
|
+
const status = finRes?.finalizeAIBlameInferencesUpload?.status;
|
|
13196
|
+
if (status !== "OK") {
|
|
13197
|
+
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
13198
|
+
logger2.error(
|
|
13199
|
+
chalk3.red(
|
|
13200
|
+
`[UPLOAD] Finalize failed with status: ${status}, error: ${errorMsg}`
|
|
13201
|
+
)
|
|
13202
|
+
);
|
|
13203
|
+
if (exitOnError) {
|
|
13204
|
+
process.exit(1);
|
|
13205
|
+
}
|
|
13206
|
+
throw new Error(errorMsg);
|
|
12826
13207
|
}
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
} finally {
|
|
12832
|
-
authManager.cleanup();
|
|
13208
|
+
logger2.info(chalk3.green("[UPLOAD] AI Blame uploads finalized successfully"));
|
|
13209
|
+
} catch (error) {
|
|
13210
|
+
logger2.error("[UPLOAD] Finalize threw error:", error);
|
|
13211
|
+
throw error;
|
|
12833
13212
|
}
|
|
12834
13213
|
}
|
|
13214
|
+
async function uploadAiBlameCommandHandler(args) {
|
|
13215
|
+
await uploadAiBlameHandler({ args });
|
|
13216
|
+
}
|
|
12835
13217
|
|
|
12836
|
-
// src/features/analysis/
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
import Debug10 from "debug";
|
|
12841
|
-
import parseDiff from "parse-diff";
|
|
12842
|
-
import { z as z28 } from "zod";
|
|
13218
|
+
// src/features/analysis/graphql/tracy-batch-upload.ts
|
|
13219
|
+
var gzipAsync = promisify(gzip);
|
|
13220
|
+
var debug9 = Debug8("mobbdev:tracy-batch-upload");
|
|
13221
|
+
var MAX_BATCH_PAYLOAD_BYTES = 3 * 1024 * 1024;
|
|
12843
13222
|
|
|
12844
|
-
// src/
|
|
12845
|
-
function
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
13223
|
+
// src/mcp/services/types.ts
|
|
13224
|
+
function detectIDE() {
|
|
13225
|
+
const env3 = process.env;
|
|
13226
|
+
if (env3["CURSOR_TRACE_ID"] || env3["CURSOR_SESSION_ID"]) return "cursor";
|
|
13227
|
+
if (env3["WINDSURF_IPC_HOOK"] || env3["WINDSURF_PID"]) return "windsurf";
|
|
13228
|
+
if (env3["CLAUDE_DESKTOP"] || env3["ANTHROPIC_CLAUDE"]) return "claude";
|
|
13229
|
+
if (env3["WEBSTORM_VM_OPTIONS"] || env3["IDEA_VM_OPTIONS"] || env3["JETBRAINS_IDE"])
|
|
13230
|
+
return "webstorm";
|
|
13231
|
+
if (env3["VSCODE_IPC_HOOK"] || env3["VSCODE_PID"]) return "vscode";
|
|
13232
|
+
const termProgram = env3["TERM_PROGRAM"]?.toLowerCase();
|
|
13233
|
+
if (termProgram === "windsurf") return "windsurf";
|
|
13234
|
+
if (termProgram === "vscode") return "vscode";
|
|
13235
|
+
return void 0;
|
|
13236
|
+
}
|
|
13237
|
+
function createMcpLoginContext(trigger) {
|
|
13238
|
+
return {
|
|
13239
|
+
trigger,
|
|
13240
|
+
source: "mcp",
|
|
13241
|
+
ide: detectIDE()
|
|
13242
|
+
};
|
|
13243
|
+
}
|
|
13244
|
+
function buildLoginUrl(baseUrl, loginId, hostname, context) {
|
|
13245
|
+
const url = new URL(`${baseUrl}/${loginId}`);
|
|
13246
|
+
url.searchParams.set("hostname", hostname);
|
|
13247
|
+
url.searchParams.set("trigger", context.trigger);
|
|
13248
|
+
url.searchParams.set("source", context.source);
|
|
13249
|
+
if (context.ide) {
|
|
13250
|
+
url.searchParams.set("ide", context.ide);
|
|
13251
|
+
}
|
|
13252
|
+
return url.toString();
|
|
12849
13253
|
}
|
|
12850
13254
|
|
|
12851
|
-
// src/
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
var
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
|
|
12864
|
-
);
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
13255
|
+
// src/commands/AuthManager.ts
|
|
13256
|
+
var debug10 = Debug9("mobbdev:auth");
|
|
13257
|
+
var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
|
|
13258
|
+
var LOGIN_CHECK_DELAY = 5 * 1e3;
|
|
13259
|
+
var _AuthManager = class _AuthManager {
|
|
13260
|
+
constructor(webAppUrl, apiUrl) {
|
|
13261
|
+
__publicField(this, "publicKey");
|
|
13262
|
+
__publicField(this, "privateKey");
|
|
13263
|
+
__publicField(this, "loginId");
|
|
13264
|
+
__publicField(this, "gqlClient");
|
|
13265
|
+
__publicField(this, "currentBrowserUrl");
|
|
13266
|
+
__publicField(this, "authenticated", null);
|
|
13267
|
+
__publicField(this, "resolvedWebAppUrl");
|
|
13268
|
+
__publicField(this, "resolvedApiUrl");
|
|
13269
|
+
this.resolvedWebAppUrl = webAppUrl || WEB_APP_URL;
|
|
13270
|
+
this.resolvedApiUrl = apiUrl || API_URL;
|
|
13271
|
+
}
|
|
13272
|
+
openUrlInBrowser() {
|
|
13273
|
+
if (this.currentBrowserUrl) {
|
|
13274
|
+
open(this.currentBrowserUrl);
|
|
13275
|
+
return true;
|
|
12868
13276
|
}
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
13277
|
+
return false;
|
|
13278
|
+
}
|
|
13279
|
+
async waitForAuthentication() {
|
|
13280
|
+
let newApiToken = null;
|
|
13281
|
+
for (let i = 0; i < _AuthManager.loginMaxWait / LOGIN_CHECK_DELAY; i++) {
|
|
13282
|
+
newApiToken = await this.getApiToken();
|
|
13283
|
+
if (newApiToken) {
|
|
13284
|
+
break;
|
|
13285
|
+
}
|
|
13286
|
+
await sleep(LOGIN_CHECK_DELAY);
|
|
13287
|
+
}
|
|
13288
|
+
if (!newApiToken) {
|
|
13289
|
+
return false;
|
|
13290
|
+
}
|
|
13291
|
+
this.gqlClient = new GQLClient({
|
|
13292
|
+
apiKey: newApiToken,
|
|
13293
|
+
type: "apiKey",
|
|
13294
|
+
apiUrl: this.resolvedApiUrl
|
|
12872
13295
|
});
|
|
12873
|
-
const
|
|
12874
|
-
|
|
12875
|
-
"
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
13296
|
+
const loginSuccess = await this.gqlClient.validateUserToken();
|
|
13297
|
+
if (loginSuccess) {
|
|
13298
|
+
configStore.set("apiToken", newApiToken);
|
|
13299
|
+
this.authenticated = true;
|
|
13300
|
+
return true;
|
|
13301
|
+
}
|
|
13302
|
+
return false;
|
|
13303
|
+
}
|
|
13304
|
+
/**
|
|
13305
|
+
* Checks if the user is already authenticated
|
|
13306
|
+
*/
|
|
13307
|
+
async isAuthenticated() {
|
|
13308
|
+
if (this.authenticated === null) {
|
|
13309
|
+
const result = await this.checkAuthentication();
|
|
13310
|
+
this.authenticated = result.isAuthenticated;
|
|
13311
|
+
if (!result.isAuthenticated) {
|
|
13312
|
+
debug10("isAuthenticated: false \u2014 %s", result.message);
|
|
13313
|
+
}
|
|
13314
|
+
}
|
|
13315
|
+
return this.authenticated;
|
|
13316
|
+
}
|
|
13317
|
+
/**
|
|
13318
|
+
* Private function to check if the user is authenticated with the server
|
|
13319
|
+
*/
|
|
13320
|
+
async checkAuthentication(apiKey) {
|
|
13321
|
+
try {
|
|
13322
|
+
if (!this.gqlClient) {
|
|
13323
|
+
this.gqlClient = this.getGQLClient(apiKey);
|
|
13324
|
+
}
|
|
13325
|
+
const isConnected = await this.gqlClient.verifyApiConnection();
|
|
13326
|
+
if (!isConnected) {
|
|
13327
|
+
return {
|
|
13328
|
+
isAuthenticated: false,
|
|
13329
|
+
message: "Failed to connect to Mobb server"
|
|
13330
|
+
};
|
|
13331
|
+
}
|
|
13332
|
+
const userVerify = await this.gqlClient.validateUserToken();
|
|
13333
|
+
if (!userVerify) {
|
|
13334
|
+
return {
|
|
13335
|
+
isAuthenticated: false,
|
|
13336
|
+
message: "User token validation failed"
|
|
13337
|
+
};
|
|
13338
|
+
}
|
|
13339
|
+
} catch (error) {
|
|
13340
|
+
return {
|
|
13341
|
+
isAuthenticated: false,
|
|
13342
|
+
message: error instanceof Error ? error.message : "Unknown authentication error"
|
|
13343
|
+
};
|
|
13344
|
+
}
|
|
13345
|
+
return { isAuthenticated: true, message: "Successfully authenticated" };
|
|
13346
|
+
}
|
|
13347
|
+
/**
|
|
13348
|
+
* Generates a login URL for manual authentication
|
|
13349
|
+
*/
|
|
13350
|
+
async generateLoginUrl(loginContext) {
|
|
13351
|
+
try {
|
|
13352
|
+
if (!this.gqlClient) {
|
|
13353
|
+
this.gqlClient = this.getGQLClient();
|
|
13354
|
+
}
|
|
13355
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
|
|
13356
|
+
modulusLength: 2048
|
|
12884
13357
|
});
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
await gqlClient.
|
|
12888
|
-
|
|
12889
|
-
analysisId: submitRes.submitVulnerabilityReport.fixReportId
|
|
12890
|
-
},
|
|
12891
|
-
callback,
|
|
12892
|
-
callbackStates,
|
|
12893
|
-
timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
|
|
13358
|
+
this.publicKey = publicKey;
|
|
13359
|
+
this.privateKey = privateKey;
|
|
13360
|
+
this.loginId = await this.gqlClient.createCliLogin({
|
|
13361
|
+
publicKey: this.publicKey.export({ format: "pem", type: "pkcs1" }).toString()
|
|
12894
13362
|
});
|
|
13363
|
+
const webLoginUrl = `${this.resolvedWebAppUrl}/cli-login`;
|
|
13364
|
+
const browserUrl = loginContext ? buildLoginUrl(webLoginUrl, this.loginId, os3.hostname(), loginContext) : `${webLoginUrl}/${this.loginId}?hostname=${os3.hostname()}`;
|
|
13365
|
+
this.currentBrowserUrl = browserUrl;
|
|
13366
|
+
return browserUrl;
|
|
13367
|
+
} catch (error) {
|
|
13368
|
+
console.error("Failed to generate login URL:", error);
|
|
13369
|
+
return null;
|
|
12895
13370
|
}
|
|
12896
|
-
return submitRes;
|
|
12897
|
-
} catch (e) {
|
|
12898
|
-
spinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed" });
|
|
12899
|
-
throw e;
|
|
12900
13371
|
}
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
13372
|
+
/**
|
|
13373
|
+
* Retrieves and decrypts the API token after authentication
|
|
13374
|
+
*/
|
|
13375
|
+
async getApiToken() {
|
|
13376
|
+
if (!this.gqlClient || !this.loginId || !this.privateKey) {
|
|
13377
|
+
return null;
|
|
13378
|
+
}
|
|
13379
|
+
const encryptedApiToken = await this.gqlClient.getEncryptedApiToken({
|
|
13380
|
+
loginId: this.loginId
|
|
13381
|
+
});
|
|
13382
|
+
if (encryptedApiToken) {
|
|
13383
|
+
return crypto.privateDecrypt(
|
|
13384
|
+
this.privateKey,
|
|
13385
|
+
Buffer.from(encryptedApiToken, "base64")
|
|
13386
|
+
).toString("utf-8");
|
|
13387
|
+
}
|
|
13388
|
+
return null;
|
|
13389
|
+
}
|
|
13390
|
+
/**
|
|
13391
|
+
* Returns true if a non-empty API token is stored in the configStore.
|
|
13392
|
+
* Used for diagnostics — does NOT validate the token.
|
|
13393
|
+
*/
|
|
13394
|
+
hasStoredToken() {
|
|
13395
|
+
const token = configStore.get("apiToken");
|
|
13396
|
+
return typeof token === "string" && token.length > 0;
|
|
13397
|
+
}
|
|
13398
|
+
/**
|
|
13399
|
+
* Gets the current GQL client (if authenticated)
|
|
13400
|
+
*/
|
|
13401
|
+
getGQLClient(inputApiKey) {
|
|
13402
|
+
if (this.gqlClient === void 0) {
|
|
13403
|
+
this.gqlClient = new GQLClient({
|
|
13404
|
+
apiKey: inputApiKey || configStore.get("apiToken") || "",
|
|
13405
|
+
type: "apiKey",
|
|
13406
|
+
apiUrl: this.resolvedApiUrl
|
|
13407
|
+
});
|
|
13408
|
+
}
|
|
13409
|
+
return this.gqlClient;
|
|
13410
|
+
}
|
|
13411
|
+
/**
|
|
13412
|
+
* Assigns a GQL client instance to the AuthManager, and resets auth state
|
|
13413
|
+
* @param gqlClient The GQL client instance to set
|
|
13414
|
+
*/
|
|
13415
|
+
setGQLClient(gqlClient) {
|
|
13416
|
+
this.gqlClient = gqlClient;
|
|
13417
|
+
this.cleanup();
|
|
13418
|
+
}
|
|
13419
|
+
/**
|
|
13420
|
+
* Cleans up any active login session
|
|
13421
|
+
*/
|
|
13422
|
+
cleanup() {
|
|
13423
|
+
this.publicKey = void 0;
|
|
13424
|
+
this.privateKey = void 0;
|
|
13425
|
+
this.loginId = void 0;
|
|
13426
|
+
this.authenticated = null;
|
|
13427
|
+
this.currentBrowserUrl = null;
|
|
13428
|
+
}
|
|
13429
|
+
};
|
|
13430
|
+
/** Maximum time (ms) to wait for login authentication. Override in tests for faster failures. */
|
|
13431
|
+
__publicField(_AuthManager, "loginMaxWait", LOGIN_MAX_WAIT);
|
|
13432
|
+
var AuthManager = _AuthManager;
|
|
13433
|
+
|
|
13434
|
+
// src/commands/handleMobbLogin.ts
|
|
13435
|
+
var debug11 = Debug10("mobbdev:commands");
|
|
13436
|
+
var LOGIN_MAX_WAIT2 = 10 * 60 * 1e3;
|
|
13437
|
+
var LOGIN_CHECK_DELAY2 = 5 * 1e3;
|
|
13438
|
+
var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${chalk4.bgBlue(
|
|
13439
|
+
"press any key to continue"
|
|
13440
|
+
)};`;
|
|
13441
|
+
async function getAuthenticatedGQLClient({
|
|
13442
|
+
inputApiKey = "",
|
|
13443
|
+
isSkipPrompts = true,
|
|
13444
|
+
apiUrl,
|
|
13445
|
+
webAppUrl
|
|
13446
|
+
}) {
|
|
13447
|
+
debug11(
|
|
13448
|
+
"getAuthenticatedGQLClient called with: apiUrl=%s, webAppUrl=%s",
|
|
13449
|
+
apiUrl || "undefined",
|
|
13450
|
+
webAppUrl || "undefined"
|
|
13451
|
+
);
|
|
13452
|
+
const authManager = new AuthManager(webAppUrl, apiUrl);
|
|
13453
|
+
let gqlClient = authManager.getGQLClient(inputApiKey);
|
|
13454
|
+
gqlClient = await handleMobbLogin({
|
|
13455
|
+
inGqlClient: gqlClient,
|
|
13456
|
+
skipPrompts: isSkipPrompts,
|
|
13457
|
+
apiUrl,
|
|
13458
|
+
webAppUrl
|
|
13459
|
+
});
|
|
13460
|
+
return gqlClient;
|
|
13461
|
+
}
|
|
13462
|
+
async function handleMobbLogin({
|
|
13463
|
+
inGqlClient,
|
|
13464
|
+
apiKey,
|
|
13465
|
+
skipPrompts,
|
|
13466
|
+
apiUrl,
|
|
13467
|
+
webAppUrl,
|
|
13468
|
+
loginContext
|
|
13469
|
+
}) {
|
|
13470
|
+
debug11(
|
|
13471
|
+
"handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
|
|
13472
|
+
apiUrl || "fallback",
|
|
13473
|
+
apiUrl || "fallback",
|
|
13474
|
+
webAppUrl || "fallback",
|
|
13475
|
+
webAppUrl || "fallback"
|
|
13476
|
+
);
|
|
13477
|
+
const { createSpinner: createSpinner5 } = Spinner({ ci: skipPrompts });
|
|
13478
|
+
const authManager = new AuthManager(webAppUrl, apiUrl);
|
|
13479
|
+
authManager.setGQLClient(inGqlClient);
|
|
13480
|
+
try {
|
|
13481
|
+
const isAuthenticated = await authManager.isAuthenticated();
|
|
13482
|
+
if (isAuthenticated) {
|
|
13483
|
+
createSpinner5().start().success({
|
|
13484
|
+
text: `\u{1F513} Login to Mobb succeeded. Already authenticated`
|
|
13485
|
+
});
|
|
13486
|
+
return authManager.getGQLClient();
|
|
13487
|
+
}
|
|
13488
|
+
} catch (error) {
|
|
13489
|
+
debug11("Authentication check failed:", error);
|
|
13490
|
+
}
|
|
13491
|
+
if (apiKey) {
|
|
13492
|
+
createSpinner5().start().error({
|
|
13493
|
+
text: "\u{1F513} Login to Mobb failed: The provided API key does not match any configured API key on the system"
|
|
13494
|
+
});
|
|
13495
|
+
throw new CliError(
|
|
13496
|
+
"Login to Mobb failed: The provided API key does not match any configured API key on the system"
|
|
13497
|
+
);
|
|
13498
|
+
}
|
|
13499
|
+
const loginSpinner = createSpinner5().start();
|
|
13500
|
+
if (!skipPrompts) {
|
|
13501
|
+
loginSpinner.update({ text: MOBB_LOGIN_REQUIRED_MSG });
|
|
13502
|
+
await keypress();
|
|
13503
|
+
}
|
|
13504
|
+
loginSpinner.update({
|
|
13505
|
+
text: "\u{1F513} Waiting for Mobb login..."
|
|
13506
|
+
});
|
|
13507
|
+
try {
|
|
13508
|
+
const loginUrl = await authManager.generateLoginUrl(loginContext);
|
|
13509
|
+
if (!loginUrl) {
|
|
13510
|
+
loginSpinner.error({
|
|
13511
|
+
text: "Failed to generate login URL"
|
|
13512
|
+
});
|
|
13513
|
+
throw new CliError("Failed to generate login URL");
|
|
13514
|
+
}
|
|
13515
|
+
!skipPrompts && console.log(
|
|
13516
|
+
`If the page does not open automatically, kindly access it through ${loginUrl}.`
|
|
13517
|
+
);
|
|
13518
|
+
authManager.openUrlInBrowser();
|
|
13519
|
+
const authSuccess = await authManager.waitForAuthentication();
|
|
13520
|
+
if (!authSuccess) {
|
|
13521
|
+
loginSpinner.error({
|
|
13522
|
+
text: "Login timeout error"
|
|
13523
|
+
});
|
|
13524
|
+
throw new CliError("Login timeout error");
|
|
13525
|
+
}
|
|
13526
|
+
loginSpinner.success({
|
|
13527
|
+
text: `\u{1F513} Login to Mobb successful!`
|
|
13528
|
+
});
|
|
13529
|
+
return authManager.getGQLClient();
|
|
13530
|
+
} finally {
|
|
13531
|
+
authManager.cleanup();
|
|
13532
|
+
}
|
|
13533
|
+
}
|
|
13534
|
+
|
|
13535
|
+
// src/features/analysis/add_fix_comments_for_pr/add_fix_comments_for_pr.ts
|
|
13536
|
+
import Debug14 from "debug";
|
|
13537
|
+
|
|
13538
|
+
// src/features/analysis/add_fix_comments_for_pr/utils/utils.ts
|
|
13539
|
+
import Debug13 from "debug";
|
|
13540
|
+
import parseDiff from "parse-diff";
|
|
13541
|
+
import { z as z29 } from "zod";
|
|
13542
|
+
|
|
13543
|
+
// src/features/analysis/utils/by_key.ts
|
|
13544
|
+
function keyBy(array, keyBy2) {
|
|
13545
|
+
return array.reduce((acc, item) => {
|
|
13546
|
+
return { ...acc, [item[keyBy2]]: item };
|
|
13547
|
+
}, {});
|
|
13548
|
+
}
|
|
13549
|
+
|
|
13550
|
+
// src/features/analysis/utils/send_report.ts
|
|
13551
|
+
import Debug11 from "debug";
|
|
13552
|
+
init_client_generates();
|
|
13553
|
+
var debug12 = Debug11("mobbdev:index");
|
|
13554
|
+
async function sendReport({
|
|
13555
|
+
spinner,
|
|
13556
|
+
submitVulnerabilityReportVariables,
|
|
13557
|
+
gqlClient,
|
|
13558
|
+
polling
|
|
13559
|
+
}) {
|
|
13560
|
+
try {
|
|
13561
|
+
const submitRes = await gqlClient.submitVulnerabilityReport(
|
|
13562
|
+
submitVulnerabilityReportVariables
|
|
13563
|
+
);
|
|
13564
|
+
if (submitRes.submitVulnerabilityReport.__typename !== "VulnerabilityReport") {
|
|
13565
|
+
debug12("error submit vul report %s", submitRes);
|
|
13566
|
+
throw new Error("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
|
|
13567
|
+
}
|
|
13568
|
+
spinner.update({ text: progressMassages.processingVulnerabilityReport });
|
|
13569
|
+
const callback = (_analysisId) => spinner.update({
|
|
13570
|
+
text: "\u2699\uFE0F Vulnerability report processed successfully"
|
|
13571
|
+
});
|
|
13572
|
+
const callbackStates = [
|
|
13573
|
+
"Digested" /* Digested */,
|
|
13574
|
+
"Finished" /* Finished */
|
|
13575
|
+
];
|
|
13576
|
+
if (polling) {
|
|
13577
|
+
debug12("[sendReport] Using POLLING mode for analysis state updates");
|
|
13578
|
+
await gqlClient.pollForAnalysisState({
|
|
13579
|
+
analysisId: submitRes.submitVulnerabilityReport.fixReportId,
|
|
13580
|
+
callback,
|
|
13581
|
+
callbackStates,
|
|
13582
|
+
timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
|
|
13583
|
+
});
|
|
13584
|
+
} else {
|
|
13585
|
+
debug12("[sendReport] Using WEBSOCKET mode for analysis state updates");
|
|
13586
|
+
await gqlClient.subscribeToAnalysis({
|
|
13587
|
+
subscribeToAnalysisParams: {
|
|
13588
|
+
analysisId: submitRes.submitVulnerabilityReport.fixReportId
|
|
13589
|
+
},
|
|
13590
|
+
callback,
|
|
13591
|
+
callbackStates,
|
|
13592
|
+
timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
|
|
13593
|
+
});
|
|
13594
|
+
}
|
|
13595
|
+
return submitRes;
|
|
13596
|
+
} catch (e) {
|
|
13597
|
+
spinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed" });
|
|
13598
|
+
throw e;
|
|
13599
|
+
}
|
|
13600
|
+
}
|
|
13601
|
+
|
|
13602
|
+
// src/features/analysis/utils/index.ts
|
|
12904
13603
|
function getFromArraySafe(array) {
|
|
12905
13604
|
return array.reduce((acc, nullableItem) => {
|
|
12906
13605
|
if (nullableItem) {
|
|
@@ -12926,10 +13625,10 @@ var scannerToFriendlyString = {
|
|
|
12926
13625
|
};
|
|
12927
13626
|
|
|
12928
13627
|
// src/features/analysis/add_fix_comments_for_pr/utils/buildCommentBody.ts
|
|
12929
|
-
import
|
|
12930
|
-
import { z as
|
|
13628
|
+
import Debug12 from "debug";
|
|
13629
|
+
import { z as z28 } from "zod";
|
|
12931
13630
|
init_client_generates();
|
|
12932
|
-
var
|
|
13631
|
+
var debug13 = Debug12("mobbdev:handle-finished-analysis");
|
|
12933
13632
|
var getCommitFixButton = (commitUrl) => `<a href="${commitUrl}"><img src=${COMMIT_FIX_SVG}></a>`;
|
|
12934
13633
|
function buildFixCommentBody({
|
|
12935
13634
|
fix,
|
|
@@ -12981,14 +13680,14 @@ function buildFixCommentBody({
|
|
|
12981
13680
|
});
|
|
12982
13681
|
const issueType = getIssueTypeFriendlyString(fix.safeIssueType);
|
|
12983
13682
|
const title = `# ${MobbIconMarkdown} ${issueType} fix is ready`;
|
|
12984
|
-
const validFixParseRes =
|
|
13683
|
+
const validFixParseRes = z28.object({
|
|
12985
13684
|
patchAndQuestions: PatchAndQuestionsZ,
|
|
12986
|
-
safeIssueLanguage:
|
|
12987
|
-
severityText:
|
|
12988
|
-
safeIssueType:
|
|
13685
|
+
safeIssueLanguage: z28.nativeEnum(IssueLanguage_Enum),
|
|
13686
|
+
severityText: z28.nativeEnum(Vulnerability_Severity_Enum),
|
|
13687
|
+
safeIssueType: z28.nativeEnum(IssueType_Enum)
|
|
12989
13688
|
}).safeParse(fix);
|
|
12990
13689
|
if (!validFixParseRes.success) {
|
|
12991
|
-
|
|
13690
|
+
debug13(
|
|
12992
13691
|
`fix ${fixId} has custom issue type or language, therefore the commit description will not be added`,
|
|
12993
13692
|
validFixParseRes.error
|
|
12994
13693
|
);
|
|
@@ -13052,7 +13751,7 @@ ${issuePageLink}`;
|
|
|
13052
13751
|
}
|
|
13053
13752
|
|
|
13054
13753
|
// src/features/analysis/add_fix_comments_for_pr/utils/utils.ts
|
|
13055
|
-
var
|
|
13754
|
+
var debug14 = Debug13("mobbdev:handle-finished-analysis");
|
|
13056
13755
|
function calculateRanges(integers) {
|
|
13057
13756
|
if (integers.length === 0) {
|
|
13058
13757
|
return [];
|
|
@@ -13086,7 +13785,7 @@ function deleteAllPreviousComments({
|
|
|
13086
13785
|
try {
|
|
13087
13786
|
return scm.deleteComment({ comment_id: comment.id });
|
|
13088
13787
|
} catch (e) {
|
|
13089
|
-
|
|
13788
|
+
debug14("delete comment failed %s", e);
|
|
13090
13789
|
return Promise.resolve();
|
|
13091
13790
|
}
|
|
13092
13791
|
});
|
|
@@ -13102,7 +13801,7 @@ function deleteAllPreviousGeneralPrComments(params) {
|
|
|
13102
13801
|
try {
|
|
13103
13802
|
return scm.deleteGeneralPrComment({ commentId: comment.id });
|
|
13104
13803
|
} catch (e) {
|
|
13105
|
-
|
|
13804
|
+
debug14("delete comment failed %s", e);
|
|
13106
13805
|
return Promise.resolve();
|
|
13107
13806
|
}
|
|
13108
13807
|
});
|
|
@@ -13219,7 +13918,7 @@ async function getRelevantVulenrabilitiesFromDiff(params) {
|
|
|
13219
13918
|
});
|
|
13220
13919
|
const lineAddedRanges = calculateRanges(fileNumbers);
|
|
13221
13920
|
const fileFilter = {
|
|
13222
|
-
path:
|
|
13921
|
+
path: z29.string().parse(file.to),
|
|
13223
13922
|
ranges: lineAddedRanges.map(([startLine, endLine]) => ({
|
|
13224
13923
|
endLine,
|
|
13225
13924
|
startLine
|
|
@@ -13246,7 +13945,7 @@ async function postAnalysisInsightComment(params) {
|
|
|
13246
13945
|
fixablePrVuls,
|
|
13247
13946
|
nonFixablePrVuls
|
|
13248
13947
|
} = prVulenrabilities;
|
|
13249
|
-
|
|
13948
|
+
debug14({
|
|
13250
13949
|
fixablePrVuls,
|
|
13251
13950
|
nonFixablePrVuls,
|
|
13252
13951
|
vulnerabilitiesOutsidePr,
|
|
@@ -13301,7 +14000,7 @@ ${contactUsMarkdown}`;
|
|
|
13301
14000
|
}
|
|
13302
14001
|
|
|
13303
14002
|
// src/features/analysis/add_fix_comments_for_pr/add_fix_comments_for_pr.ts
|
|
13304
|
-
var
|
|
14003
|
+
var debug15 = Debug14("mobbdev:handle-finished-analysis");
|
|
13305
14004
|
async function addFixCommentsForPr({
|
|
13306
14005
|
analysisId,
|
|
13307
14006
|
scm: _scm,
|
|
@@ -13313,7 +14012,7 @@ async function addFixCommentsForPr({
|
|
|
13313
14012
|
}
|
|
13314
14013
|
const scm = _scm;
|
|
13315
14014
|
const getAnalysisRes = await gqlClient.getAnalysis(analysisId);
|
|
13316
|
-
|
|
14015
|
+
debug15("getAnalysis %o", getAnalysisRes);
|
|
13317
14016
|
const {
|
|
13318
14017
|
vulnerabilityReport: {
|
|
13319
14018
|
projectId,
|
|
@@ -13423,8 +14122,8 @@ ${contextString}` : description;
|
|
|
13423
14122
|
|
|
13424
14123
|
// src/features/analysis/auto_pr_handler.ts
|
|
13425
14124
|
init_client_generates();
|
|
13426
|
-
import
|
|
13427
|
-
var
|
|
14125
|
+
import Debug15 from "debug";
|
|
14126
|
+
var debug16 = Debug15("mobbdev:handleAutoPr");
|
|
13428
14127
|
async function handleAutoPr(params) {
|
|
13429
14128
|
const {
|
|
13430
14129
|
gqlClient,
|
|
@@ -13445,7 +14144,7 @@ async function handleAutoPr(params) {
|
|
|
13445
14144
|
prId,
|
|
13446
14145
|
prStrategy: createOnePr ? "CONDENSE" /* Condense */ : "SPREAD" /* Spread */
|
|
13447
14146
|
});
|
|
13448
|
-
|
|
14147
|
+
debug16("auto pr analysis res %o", autoPrAnalysisRes);
|
|
13449
14148
|
if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrError") {
|
|
13450
14149
|
createAutoPrSpinner.error({
|
|
13451
14150
|
text: `\u{1F504} Automatic pull request failed - ${autoPrAnalysisRes.autoPrAnalysis.error}`
|
|
@@ -13467,14 +14166,14 @@ async function handleAutoPr(params) {
|
|
|
13467
14166
|
};
|
|
13468
14167
|
const callbackStates = ["Finished" /* Finished */];
|
|
13469
14168
|
if (polling) {
|
|
13470
|
-
|
|
14169
|
+
debug16("[handleAutoPr] Using POLLING mode for analysis state updates");
|
|
13471
14170
|
return await gqlClient.pollForAnalysisState({
|
|
13472
14171
|
analysisId,
|
|
13473
14172
|
callback,
|
|
13474
14173
|
callbackStates
|
|
13475
14174
|
});
|
|
13476
14175
|
} else {
|
|
13477
|
-
|
|
14176
|
+
debug16("[handleAutoPr] Using WEBSOCKET mode for analysis state updates");
|
|
13478
14177
|
return await gqlClient.subscribeToAnalysis({
|
|
13479
14178
|
subscribeToAnalysisParams: {
|
|
13480
14179
|
analysisId
|
|
@@ -13487,15 +14186,15 @@ async function handleAutoPr(params) {
|
|
|
13487
14186
|
|
|
13488
14187
|
// src/features/analysis/git.ts
|
|
13489
14188
|
init_GitService();
|
|
13490
|
-
import
|
|
13491
|
-
var
|
|
14189
|
+
import Debug16 from "debug";
|
|
14190
|
+
var debug17 = Debug16("mobbdev:git");
|
|
13492
14191
|
async function getGitInfo(srcDirPath) {
|
|
13493
|
-
|
|
14192
|
+
debug17("getting git info for %s", srcDirPath);
|
|
13494
14193
|
const gitService = new GitService(srcDirPath);
|
|
13495
14194
|
try {
|
|
13496
14195
|
const validationResult = await gitService.validateRepository();
|
|
13497
14196
|
if (!validationResult.isValid) {
|
|
13498
|
-
|
|
14197
|
+
debug17("folder is not a git repo");
|
|
13499
14198
|
return {
|
|
13500
14199
|
success: false,
|
|
13501
14200
|
hash: void 0,
|
|
@@ -13510,9 +14209,9 @@ async function getGitInfo(srcDirPath) {
|
|
|
13510
14209
|
};
|
|
13511
14210
|
} catch (e) {
|
|
13512
14211
|
if (e instanceof Error) {
|
|
13513
|
-
|
|
14212
|
+
debug17("failed to run git %o", e);
|
|
13514
14213
|
if (e.message.includes(" spawn ")) {
|
|
13515
|
-
|
|
14214
|
+
debug17("git cli not installed");
|
|
13516
14215
|
} else {
|
|
13517
14216
|
throw e;
|
|
13518
14217
|
}
|
|
@@ -13524,22 +14223,22 @@ async function getGitInfo(srcDirPath) {
|
|
|
13524
14223
|
// src/features/analysis/pack.ts
|
|
13525
14224
|
init_configs();
|
|
13526
14225
|
import fs9 from "fs";
|
|
13527
|
-
import
|
|
14226
|
+
import path8 from "path";
|
|
13528
14227
|
import AdmZip from "adm-zip";
|
|
13529
|
-
import
|
|
14228
|
+
import Debug17 from "debug";
|
|
13530
14229
|
import { globby } from "globby";
|
|
13531
14230
|
import { isBinary as isBinary2 } from "istextorbinary";
|
|
13532
14231
|
import { simpleGit as simpleGit3 } from "simple-git";
|
|
13533
14232
|
import { parseStringPromise } from "xml2js";
|
|
13534
|
-
import { z as
|
|
13535
|
-
var
|
|
13536
|
-
var FPR_SOURCE_CODE_FILE_MAPPING_SCHEMA =
|
|
13537
|
-
properties:
|
|
13538
|
-
entry:
|
|
13539
|
-
|
|
13540
|
-
_:
|
|
13541
|
-
$:
|
|
13542
|
-
key:
|
|
14233
|
+
import { z as z30 } from "zod";
|
|
14234
|
+
var debug18 = Debug17("mobbdev:pack");
|
|
14235
|
+
var FPR_SOURCE_CODE_FILE_MAPPING_SCHEMA = z30.object({
|
|
14236
|
+
properties: z30.object({
|
|
14237
|
+
entry: z30.array(
|
|
14238
|
+
z30.object({
|
|
14239
|
+
_: z30.string(),
|
|
14240
|
+
$: z30.object({
|
|
14241
|
+
key: z30.string()
|
|
13543
14242
|
})
|
|
13544
14243
|
})
|
|
13545
14244
|
)
|
|
@@ -13554,7 +14253,7 @@ function getManifestFilesSuffixes() {
|
|
|
13554
14253
|
return ["package.json", "pom.xml"];
|
|
13555
14254
|
}
|
|
13556
14255
|
async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
|
|
13557
|
-
|
|
14256
|
+
debug18("pack folder %s", srcDirPath);
|
|
13558
14257
|
let git = void 0;
|
|
13559
14258
|
try {
|
|
13560
14259
|
git = simpleGit3({
|
|
@@ -13564,13 +14263,13 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
|
|
|
13564
14263
|
});
|
|
13565
14264
|
await git.status();
|
|
13566
14265
|
} catch (e) {
|
|
13567
|
-
|
|
14266
|
+
debug18("failed to run git %o", e);
|
|
13568
14267
|
git = void 0;
|
|
13569
14268
|
if (e instanceof Error) {
|
|
13570
14269
|
if (e.message.includes(" spawn ")) {
|
|
13571
|
-
|
|
14270
|
+
debug18("git cli not installed");
|
|
13572
14271
|
} else if (e.message.includes("not a git repository")) {
|
|
13573
|
-
|
|
14272
|
+
debug18("folder is not a git repo");
|
|
13574
14273
|
} else {
|
|
13575
14274
|
throw e;
|
|
13576
14275
|
}
|
|
@@ -13585,23 +14284,23 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
|
|
|
13585
14284
|
followSymbolicLinks: false,
|
|
13586
14285
|
dot: true
|
|
13587
14286
|
});
|
|
13588
|
-
|
|
14287
|
+
debug18("files found %d", filepaths.length);
|
|
13589
14288
|
const zip = new AdmZip();
|
|
13590
|
-
|
|
14289
|
+
debug18("compressing files");
|
|
13591
14290
|
for (const filepath of filepaths) {
|
|
13592
|
-
const absFilepath =
|
|
14291
|
+
const absFilepath = path8.join(srcDirPath, filepath.toString());
|
|
13593
14292
|
if (!isIncludeAllFiles) {
|
|
13594
14293
|
vulnFiles = vulnFiles.concat(getManifestFilesSuffixes());
|
|
13595
14294
|
if (!endsWithAny(
|
|
13596
|
-
absFilepath.toString().replaceAll(
|
|
14295
|
+
absFilepath.toString().replaceAll(path8.win32.sep, path8.posix.sep),
|
|
13597
14296
|
vulnFiles
|
|
13598
14297
|
)) {
|
|
13599
|
-
|
|
14298
|
+
debug18("ignoring %s because it is not a vulnerability file", filepath);
|
|
13600
14299
|
continue;
|
|
13601
14300
|
}
|
|
13602
14301
|
}
|
|
13603
14302
|
if (fs9.lstatSync(absFilepath).size > MCP_MAX_FILE_SIZE) {
|
|
13604
|
-
|
|
14303
|
+
debug18("ignoring %s because the size is > 5MB", filepath);
|
|
13605
14304
|
continue;
|
|
13606
14305
|
}
|
|
13607
14306
|
let data;
|
|
@@ -13615,16 +14314,16 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
|
|
|
13615
14314
|
data = fs9.readFileSync(absFilepath);
|
|
13616
14315
|
}
|
|
13617
14316
|
if (isBinary2(null, data)) {
|
|
13618
|
-
|
|
14317
|
+
debug18("ignoring %s because is seems to be a binary file", filepath);
|
|
13619
14318
|
continue;
|
|
13620
14319
|
}
|
|
13621
14320
|
zip.addFile(filepath.toString(), data);
|
|
13622
14321
|
}
|
|
13623
|
-
|
|
14322
|
+
debug18("get zip file buffer");
|
|
13624
14323
|
return zip.toBuffer();
|
|
13625
14324
|
}
|
|
13626
14325
|
async function repackFpr(fprPath) {
|
|
13627
|
-
|
|
14326
|
+
debug18("repack fpr file %s", fprPath);
|
|
13628
14327
|
const zipIn = new AdmZip(fprPath);
|
|
13629
14328
|
const zipOut = new AdmZip();
|
|
13630
14329
|
const mappingXML = zipIn.readAsText("src-archive/index.xml", "utf-8");
|
|
@@ -13639,7 +14338,7 @@ async function repackFpr(fprPath) {
|
|
|
13639
14338
|
zipOut.addFile(realPath, buf);
|
|
13640
14339
|
}
|
|
13641
14340
|
}
|
|
13642
|
-
|
|
14341
|
+
debug18("get repacked zip file buffer");
|
|
13643
14342
|
return zipOut.toBuffer();
|
|
13644
14343
|
}
|
|
13645
14344
|
|
|
@@ -13709,12 +14408,12 @@ async function snykArticlePrompt() {
|
|
|
13709
14408
|
|
|
13710
14409
|
// src/features/analysis/scanners/checkmarx.ts
|
|
13711
14410
|
import { createRequire } from "module";
|
|
13712
|
-
import
|
|
13713
|
-
import
|
|
14411
|
+
import chalk5 from "chalk";
|
|
14412
|
+
import Debug19 from "debug";
|
|
13714
14413
|
import { existsSync } from "fs";
|
|
13715
14414
|
import { createSpinner as createSpinner2 } from "nanospinner";
|
|
13716
14415
|
import { type } from "os";
|
|
13717
|
-
import
|
|
14416
|
+
import path9 from "path";
|
|
13718
14417
|
|
|
13719
14418
|
// src/post_install/constants.mjs
|
|
13720
14419
|
var cxOperatingSystemSupportMessage = `Your operating system does not support checkmarx.
|
|
@@ -13722,7 +14421,7 @@ var cxOperatingSystemSupportMessage = `Your operating system does not support ch
|
|
|
13722
14421
|
|
|
13723
14422
|
// src/utils/child_process.ts
|
|
13724
14423
|
import cp from "child_process";
|
|
13725
|
-
import
|
|
14424
|
+
import Debug18 from "debug";
|
|
13726
14425
|
import * as process2 from "process";
|
|
13727
14426
|
function createFork({ args, processPath, name }, options) {
|
|
13728
14427
|
const child = cp.fork(processPath, args, {
|
|
@@ -13740,16 +14439,16 @@ function createSpawn({ args, processPath, name, cwd }, options) {
|
|
|
13740
14439
|
return createChildProcess({ childProcess: child, name }, options);
|
|
13741
14440
|
}
|
|
13742
14441
|
function createChildProcess({ childProcess, name }, options) {
|
|
13743
|
-
const
|
|
14442
|
+
const debug23 = Debug18(`mobbdev:${name}`);
|
|
13744
14443
|
const { display } = options;
|
|
13745
14444
|
return new Promise((resolve, reject) => {
|
|
13746
14445
|
let out = "";
|
|
13747
14446
|
const onData = (chunk) => {
|
|
13748
|
-
|
|
14447
|
+
debug23(`chunk received from ${name} std ${chunk}`);
|
|
13749
14448
|
out += chunk;
|
|
13750
14449
|
};
|
|
13751
14450
|
if (!childProcess?.stdout || !childProcess?.stderr) {
|
|
13752
|
-
|
|
14451
|
+
debug23(`unable to fork ${name}`);
|
|
13753
14452
|
reject(new Error(`unable to fork ${name}`));
|
|
13754
14453
|
}
|
|
13755
14454
|
childProcess.stdout?.on("data", onData);
|
|
@@ -13759,18 +14458,18 @@ function createChildProcess({ childProcess, name }, options) {
|
|
|
13759
14458
|
childProcess.stderr?.pipe(process2.stderr);
|
|
13760
14459
|
}
|
|
13761
14460
|
childProcess.on("exit", (code) => {
|
|
13762
|
-
|
|
14461
|
+
debug23(`${name} exit code ${code}`);
|
|
13763
14462
|
resolve({ message: out, code });
|
|
13764
14463
|
});
|
|
13765
14464
|
childProcess.on("error", (err) => {
|
|
13766
|
-
|
|
14465
|
+
debug23(`${name} error %o`, err);
|
|
13767
14466
|
reject(err);
|
|
13768
14467
|
});
|
|
13769
14468
|
});
|
|
13770
14469
|
}
|
|
13771
14470
|
|
|
13772
14471
|
// src/features/analysis/scanners/checkmarx.ts
|
|
13773
|
-
var
|
|
14472
|
+
var debug19 = Debug19("mobbdev:checkmarx");
|
|
13774
14473
|
var moduleUrl;
|
|
13775
14474
|
if (typeof __filename !== "undefined") {
|
|
13776
14475
|
moduleUrl = __filename;
|
|
@@ -13829,14 +14528,14 @@ function validateCheckmarxInstallation() {
|
|
|
13829
14528
|
existsSync(getCheckmarxPath());
|
|
13830
14529
|
}
|
|
13831
14530
|
async function forkCheckmarx(args, { display }) {
|
|
13832
|
-
|
|
14531
|
+
debug19("fork checkmarx with args %o %s", args.join(" "), display);
|
|
13833
14532
|
return createSpawn(
|
|
13834
14533
|
{ args, processPath: getCheckmarxPath(), name: "checkmarx" },
|
|
13835
14534
|
{ display }
|
|
13836
14535
|
);
|
|
13837
14536
|
}
|
|
13838
14537
|
async function getCheckmarxReport({ reportPath, repositoryRoot, branch, projectName }, { skipPrompts = false }) {
|
|
13839
|
-
|
|
14538
|
+
debug19("get checkmarx report start %s %s", reportPath, repositoryRoot);
|
|
13840
14539
|
const { code: loginCode } = await forkCheckmarx(VALIDATE_COMMAND, {
|
|
13841
14540
|
display: false
|
|
13842
14541
|
});
|
|
@@ -13847,9 +14546,9 @@ async function getCheckmarxReport({ reportPath, repositoryRoot, branch, projectN
|
|
|
13847
14546
|
await startCheckmarxConfigationPrompt();
|
|
13848
14547
|
await validateCheckamxCredentials();
|
|
13849
14548
|
}
|
|
13850
|
-
const extension =
|
|
13851
|
-
const filePath =
|
|
13852
|
-
const fileName =
|
|
14549
|
+
const extension = path9.extname(reportPath);
|
|
14550
|
+
const filePath = path9.dirname(reportPath);
|
|
14551
|
+
const fileName = path9.basename(reportPath, extension);
|
|
13853
14552
|
const checkmarxCommandArgs = getCheckmarxCommandArgs({
|
|
13854
14553
|
repoPath: repositoryRoot,
|
|
13855
14554
|
branch,
|
|
@@ -13875,7 +14574,7 @@ async function throwCheckmarxConfigError() {
|
|
|
13875
14574
|
await createSpinner2("\u{1F513} Checkmarx is not configued correctly").start().error();
|
|
13876
14575
|
throw new CliError(
|
|
13877
14576
|
`Checkmarx is not configued correctly
|
|
13878
|
-
you can configure it by using the ${
|
|
14577
|
+
you can configure it by using the ${chalk5.bold(
|
|
13879
14578
|
"cx configure"
|
|
13880
14579
|
)} command`
|
|
13881
14580
|
);
|
|
@@ -13883,8 +14582,8 @@ async function throwCheckmarxConfigError() {
|
|
|
13883
14582
|
async function validateCheckamxCredentials() {
|
|
13884
14583
|
console.log(`
|
|
13885
14584
|
Here's a suggestion for checkmarx configuation:
|
|
13886
|
-
${
|
|
13887
|
-
${
|
|
14585
|
+
${chalk5.bold("AST Base URI:")} https://ast.checkmarx.net
|
|
14586
|
+
${chalk5.bold("AST Base Auth URI (IAM):")} https://iam.checkmarx.net
|
|
13888
14587
|
`);
|
|
13889
14588
|
await forkCheckmarx(CONFIGURE_COMMAND, { display: true });
|
|
13890
14589
|
const { code: loginCode } = await forkCheckmarx(VALIDATE_COMMAND, {
|
|
@@ -13903,11 +14602,11 @@ async function validateCheckamxCredentials() {
|
|
|
13903
14602
|
|
|
13904
14603
|
// src/features/analysis/scanners/snyk.ts
|
|
13905
14604
|
import { createRequire as createRequire2 } from "module";
|
|
13906
|
-
import
|
|
13907
|
-
import
|
|
14605
|
+
import chalk6 from "chalk";
|
|
14606
|
+
import Debug20 from "debug";
|
|
13908
14607
|
import { createSpinner as createSpinner3 } from "nanospinner";
|
|
13909
14608
|
import open2 from "open";
|
|
13910
|
-
var
|
|
14609
|
+
var debug20 = Debug20("mobbdev:snyk");
|
|
13911
14610
|
var moduleUrl2;
|
|
13912
14611
|
if (typeof __filename !== "undefined") {
|
|
13913
14612
|
moduleUrl2 = __filename;
|
|
@@ -13929,13 +14628,13 @@ if (typeof __filename !== "undefined") {
|
|
|
13929
14628
|
var costumeRequire2 = createRequire2(moduleUrl2);
|
|
13930
14629
|
var SNYK_PATH = costumeRequire2.resolve("snyk/bin/snyk");
|
|
13931
14630
|
var SNYK_ARTICLE_URL = "https://docs.snyk.io/scan-using-snyk/snyk-code/configure-snyk-code#enable-snyk-code";
|
|
13932
|
-
|
|
14631
|
+
debug20("snyk executable path %s", SNYK_PATH);
|
|
13933
14632
|
async function forkSnyk(args, { display }) {
|
|
13934
|
-
|
|
14633
|
+
debug20("fork snyk with args %o %s", args, display);
|
|
13935
14634
|
return createFork({ args, processPath: SNYK_PATH, name: "snyk" }, { display });
|
|
13936
14635
|
}
|
|
13937
14636
|
async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
13938
|
-
|
|
14637
|
+
debug20("get snyk report start %s %s", reportPath, repoRoot);
|
|
13939
14638
|
const config2 = await forkSnyk(["config"], { display: false });
|
|
13940
14639
|
const { message: configMessage } = config2;
|
|
13941
14640
|
if (!configMessage.includes("api: ")) {
|
|
@@ -13949,7 +14648,7 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
|
13949
14648
|
snykLoginSpinner.update({
|
|
13950
14649
|
text: "\u{1F513} Waiting for Snyk login to complete"
|
|
13951
14650
|
});
|
|
13952
|
-
|
|
14651
|
+
debug20("no token in the config %s", config2);
|
|
13953
14652
|
await forkSnyk(["auth"], { display: true });
|
|
13954
14653
|
snykLoginSpinner.success({ text: "\u{1F513} Login to Snyk Successful" });
|
|
13955
14654
|
}
|
|
@@ -13959,16 +14658,16 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
|
13959
14658
|
{ display: true }
|
|
13960
14659
|
);
|
|
13961
14660
|
if (scanOutput.includes("Snyk Code is not supported for org")) {
|
|
13962
|
-
|
|
14661
|
+
debug20("snyk code is not enabled %s", scanOutput);
|
|
13963
14662
|
snykSpinner.error({ text: "\u{1F50D} Snyk configuration needed" });
|
|
13964
14663
|
const answer = await snykArticlePrompt();
|
|
13965
|
-
|
|
14664
|
+
debug20("answer %s", answer);
|
|
13966
14665
|
if (answer) {
|
|
13967
|
-
|
|
14666
|
+
debug20("opening the browser");
|
|
13968
14667
|
await open2(SNYK_ARTICLE_URL);
|
|
13969
14668
|
}
|
|
13970
14669
|
console.log(
|
|
13971
|
-
|
|
14670
|
+
chalk6.bgBlue(
|
|
13972
14671
|
"\nPlease enable Snyk Code in your Snyk account and try again."
|
|
13973
14672
|
)
|
|
13974
14673
|
);
|
|
@@ -13980,58 +14679,6 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
|
13980
14679
|
|
|
13981
14680
|
// src/features/analysis/index.ts
|
|
13982
14681
|
init_client_generates();
|
|
13983
|
-
|
|
13984
|
-
// src/features/analysis/upload-file.ts
|
|
13985
|
-
import Debug18 from "debug";
|
|
13986
|
-
import fetch3, { File, fileFrom, FormData } from "node-fetch";
|
|
13987
|
-
var debug18 = Debug18("mobbdev:upload-file");
|
|
13988
|
-
async function uploadFile({
|
|
13989
|
-
file,
|
|
13990
|
-
url,
|
|
13991
|
-
uploadKey,
|
|
13992
|
-
uploadFields,
|
|
13993
|
-
logger: logger2
|
|
13994
|
-
}) {
|
|
13995
|
-
const logInfo2 = logger2 || ((_message, _data) => {
|
|
13996
|
-
});
|
|
13997
|
-
logInfo2(`FileUpload: upload file start ${url}`);
|
|
13998
|
-
logInfo2(`FileUpload: upload fields`, uploadFields);
|
|
13999
|
-
logInfo2(`FileUpload: upload key ${uploadKey}`);
|
|
14000
|
-
debug18("upload file start %s", url);
|
|
14001
|
-
debug18("upload fields %o", uploadFields);
|
|
14002
|
-
debug18("upload key %s", uploadKey);
|
|
14003
|
-
const form = new FormData();
|
|
14004
|
-
Object.entries(uploadFields).forEach(([key, value]) => {
|
|
14005
|
-
form.append(key, value);
|
|
14006
|
-
});
|
|
14007
|
-
if (!form.has("key")) {
|
|
14008
|
-
form.append("key", uploadKey);
|
|
14009
|
-
}
|
|
14010
|
-
if (typeof file === "string") {
|
|
14011
|
-
debug18("upload file from path %s", file);
|
|
14012
|
-
logInfo2(`FileUpload: upload file from path ${file}`);
|
|
14013
|
-
form.append("file", await fileFrom(file));
|
|
14014
|
-
} else {
|
|
14015
|
-
debug18("upload file from buffer");
|
|
14016
|
-
logInfo2(`FileUpload: upload file from buffer`);
|
|
14017
|
-
form.append("file", new File([new Uint8Array(file)], "file"));
|
|
14018
|
-
}
|
|
14019
|
-
const agent = getProxyAgent(url);
|
|
14020
|
-
const response = await fetch3(url, {
|
|
14021
|
-
method: "POST",
|
|
14022
|
-
body: form,
|
|
14023
|
-
agent
|
|
14024
|
-
});
|
|
14025
|
-
if (!response.ok) {
|
|
14026
|
-
debug18("error from S3 %s %s", response.body, response.status);
|
|
14027
|
-
logInfo2(`FileUpload: error from S3 ${response.body} ${response.status}`);
|
|
14028
|
-
throw new Error(`Failed to upload the file: ${response.status}`);
|
|
14029
|
-
}
|
|
14030
|
-
debug18("upload file done");
|
|
14031
|
-
logInfo2(`FileUpload: upload file done`);
|
|
14032
|
-
}
|
|
14033
|
-
|
|
14034
|
-
// src/features/analysis/index.ts
|
|
14035
14682
|
var { CliError: CliError2, Spinner: Spinner2 } = utils_exports;
|
|
14036
14683
|
function _getScanSource(command, ci) {
|
|
14037
14684
|
if (command === "review") return "AUTO_FIXER" /* AutoFixer */;
|
|
@@ -14062,9 +14709,9 @@ async function downloadRepo({
|
|
|
14062
14709
|
}) {
|
|
14063
14710
|
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
14064
14711
|
const repoSpinner = createSpinner5("\u{1F4BE} Downloading Repo").start();
|
|
14065
|
-
|
|
14066
|
-
const zipFilePath =
|
|
14067
|
-
|
|
14712
|
+
debug21("download repo %s %s %s", repoUrl, dirname);
|
|
14713
|
+
const zipFilePath = path10.join(dirname, "repo.zip");
|
|
14714
|
+
debug21("download URL: %s auth headers: %o", downloadUrl, authHeaders);
|
|
14068
14715
|
const response = await fetch4(downloadUrl, {
|
|
14069
14716
|
method: "GET",
|
|
14070
14717
|
headers: {
|
|
@@ -14072,9 +14719,9 @@ async function downloadRepo({
|
|
|
14072
14719
|
}
|
|
14073
14720
|
});
|
|
14074
14721
|
if (!response.ok) {
|
|
14075
|
-
|
|
14722
|
+
debug21("SCM zipball request failed %s %s", response.body, response.status);
|
|
14076
14723
|
repoSpinner.error({ text: "\u{1F4BE} Repo download failed" });
|
|
14077
|
-
throw new Error(`Can't access ${
|
|
14724
|
+
throw new Error(`Can't access ${chalk7.bold(repoUrl)}`);
|
|
14078
14725
|
}
|
|
14079
14726
|
const fileWriterStream = fs10.createWriteStream(zipFilePath);
|
|
14080
14727
|
if (!response.body) {
|
|
@@ -14086,16 +14733,16 @@ async function downloadRepo({
|
|
|
14086
14733
|
if (!repoRoot) {
|
|
14087
14734
|
throw new Error("Repo root not found");
|
|
14088
14735
|
}
|
|
14089
|
-
|
|
14736
|
+
debug21("repo root %s", repoRoot);
|
|
14090
14737
|
repoSpinner.success({ text: "\u{1F4BE} Repo downloaded successfully" });
|
|
14091
|
-
return
|
|
14738
|
+
return path10.join(dirname, repoRoot);
|
|
14092
14739
|
}
|
|
14093
14740
|
var getReportUrl = ({
|
|
14094
14741
|
organizationId,
|
|
14095
14742
|
projectId,
|
|
14096
14743
|
fixReportId
|
|
14097
14744
|
}) => `${WEB_APP_URL}/organization/${organizationId}/project/${projectId}/report/${fixReportId}`;
|
|
14098
|
-
var
|
|
14745
|
+
var debug21 = Debug21("mobbdev:index");
|
|
14099
14746
|
async function runAnalysis(params, options) {
|
|
14100
14747
|
const tmpObj = tmp2.dirSync({
|
|
14101
14748
|
unsafeCleanup: true
|
|
@@ -14196,7 +14843,7 @@ async function getReport(params, { skipPrompts }) {
|
|
|
14196
14843
|
authHeaders: scm.getAuthHeaders(),
|
|
14197
14844
|
downloadUrl
|
|
14198
14845
|
});
|
|
14199
|
-
const reportPath =
|
|
14846
|
+
const reportPath = path10.join(dirname, REPORT_DEFAULT_FILE_NAME);
|
|
14200
14847
|
switch (scanner) {
|
|
14201
14848
|
case "snyk":
|
|
14202
14849
|
await getSnykReport(reportPath, repositoryRoot, { skipPrompts });
|
|
@@ -14241,7 +14888,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
14241
14888
|
pullRequest,
|
|
14242
14889
|
polling
|
|
14243
14890
|
} = params;
|
|
14244
|
-
|
|
14891
|
+
debug21("start %s %s", dirname, repo);
|
|
14245
14892
|
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
14246
14893
|
skipPrompts = skipPrompts || ci;
|
|
14247
14894
|
const gqlClient = await getAuthenticatedGQLClient({
|
|
@@ -14310,8 +14957,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
14310
14957
|
);
|
|
14311
14958
|
}
|
|
14312
14959
|
const { sha } = getReferenceDataRes.gitReference;
|
|
14313
|
-
|
|
14314
|
-
|
|
14960
|
+
debug21("project id %s", projectId);
|
|
14961
|
+
debug21("default branch %s", reference);
|
|
14315
14962
|
if (command === "scan") {
|
|
14316
14963
|
reportPath = await getReport(
|
|
14317
14964
|
{
|
|
@@ -14361,7 +15008,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
14361
15008
|
spinner: mobbSpinner,
|
|
14362
15009
|
submitVulnerabilityReportVariables: {
|
|
14363
15010
|
fixReportId: reportUploadInfo.fixReportId,
|
|
14364
|
-
repoUrl:
|
|
15011
|
+
repoUrl: z31.string().parse(repo),
|
|
14365
15012
|
reference,
|
|
14366
15013
|
projectId,
|
|
14367
15014
|
vulnerabilityReportFileName: shouldScan ? void 0 : REPORT_DEFAULT_FILE_NAME,
|
|
@@ -14413,11 +15060,11 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
14413
15060
|
fixReportId: reportUploadInfo.fixReportId
|
|
14414
15061
|
});
|
|
14415
15062
|
!ci && console.log("You can access the analysis at: \n");
|
|
14416
|
-
console.log(
|
|
15063
|
+
console.log(chalk7.bold(reportUrl));
|
|
14417
15064
|
!skipPrompts && await mobbAnalysisPrompt();
|
|
14418
15065
|
!ci && open3(reportUrl);
|
|
14419
15066
|
!ci && console.log(
|
|
14420
|
-
|
|
15067
|
+
chalk7.bgBlue("\n\n My work here is done for now, see you soon! \u{1F575}\uFE0F\u200D\u2642\uFE0F ")
|
|
14421
15068
|
);
|
|
14422
15069
|
}
|
|
14423
15070
|
async function handleScmIntegration(oldToken, scmAuthUrl2, repoUrl) {
|
|
@@ -14578,11 +15225,11 @@ async function _zipAndUploadRepo({
|
|
|
14578
15225
|
repoUploadInfo,
|
|
14579
15226
|
isIncludeAllFiles
|
|
14580
15227
|
}) {
|
|
14581
|
-
const srcFileStatus = await
|
|
15228
|
+
const srcFileStatus = await fsPromises3.lstat(srcPath);
|
|
14582
15229
|
const zippingSpinner = createSpinner4("\u{1F4E6} Zipping repo").start();
|
|
14583
15230
|
let zipBuffer;
|
|
14584
15231
|
let gitInfo2 = { success: false };
|
|
14585
|
-
if (srcFileStatus.isFile() &&
|
|
15232
|
+
if (srcFileStatus.isFile() && path10.extname(srcPath).toLowerCase() === ".fpr") {
|
|
14586
15233
|
zipBuffer = await repackFpr(srcPath);
|
|
14587
15234
|
} else {
|
|
14588
15235
|
gitInfo2 = await getGitInfo(srcPath);
|
|
@@ -14639,11 +15286,11 @@ async function _digestReport({
|
|
|
14639
15286
|
text: progressMassages.processingVulnerabilityReportSuccess
|
|
14640
15287
|
});
|
|
14641
15288
|
if (polling) {
|
|
14642
|
-
|
|
15289
|
+
debug21(
|
|
14643
15290
|
"[_digestReport] Using POLLING mode for analysis state updates (--polling flag enabled)"
|
|
14644
15291
|
);
|
|
14645
15292
|
console.log(
|
|
14646
|
-
|
|
15293
|
+
chalk7.cyan(
|
|
14647
15294
|
"\u{1F504} [Polling Mode] Using HTTP polling instead of WebSocket for status updates"
|
|
14648
15295
|
)
|
|
14649
15296
|
);
|
|
@@ -14654,11 +15301,11 @@ async function _digestReport({
|
|
|
14654
15301
|
timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
|
|
14655
15302
|
});
|
|
14656
15303
|
} else {
|
|
14657
|
-
|
|
15304
|
+
debug21(
|
|
14658
15305
|
"[_digestReport] Using WEBSOCKET mode for analysis state updates (default)"
|
|
14659
15306
|
);
|
|
14660
15307
|
console.log(
|
|
14661
|
-
|
|
15308
|
+
chalk7.cyan(
|
|
14662
15309
|
"\u{1F50C} [WebSocket Mode] Using WebSocket subscription for status updates"
|
|
14663
15310
|
)
|
|
14664
15311
|
);
|
|
@@ -14694,9 +15341,9 @@ async function waitForAnaysisAndReviewPr({
|
|
|
14694
15341
|
gqlClient,
|
|
14695
15342
|
polling
|
|
14696
15343
|
}) {
|
|
14697
|
-
const params =
|
|
14698
|
-
repo:
|
|
14699
|
-
githubActionToken:
|
|
15344
|
+
const params = z31.object({
|
|
15345
|
+
repo: z31.string().url(),
|
|
15346
|
+
githubActionToken: z31.string()
|
|
14700
15347
|
}).parse({ repo, githubActionToken });
|
|
14701
15348
|
const scm = await createScmLib(
|
|
14702
15349
|
{
|
|
@@ -14714,15 +15361,15 @@ async function waitForAnaysisAndReviewPr({
|
|
|
14714
15361
|
analysisId: analysisId2,
|
|
14715
15362
|
gqlClient,
|
|
14716
15363
|
scm,
|
|
14717
|
-
scanner:
|
|
15364
|
+
scanner: z31.nativeEnum(SCANNERS).parse(scanner)
|
|
14718
15365
|
});
|
|
14719
15366
|
};
|
|
14720
15367
|
if (polling) {
|
|
14721
|
-
|
|
15368
|
+
debug21(
|
|
14722
15369
|
"[waitForAnaysisAndReviewPr] Using POLLING mode for analysis state updates"
|
|
14723
15370
|
);
|
|
14724
15371
|
console.log(
|
|
14725
|
-
|
|
15372
|
+
chalk7.cyan(
|
|
14726
15373
|
"\u{1F504} [Polling Mode] Waiting for analysis completion using HTTP polling"
|
|
14727
15374
|
)
|
|
14728
15375
|
);
|
|
@@ -14732,11 +15379,11 @@ async function waitForAnaysisAndReviewPr({
|
|
|
14732
15379
|
callbackStates: ["Finished" /* Finished */]
|
|
14733
15380
|
});
|
|
14734
15381
|
} else {
|
|
14735
|
-
|
|
15382
|
+
debug21(
|
|
14736
15383
|
"[waitForAnaysisAndReviewPr] Using WEBSOCKET mode for analysis state updates"
|
|
14737
15384
|
);
|
|
14738
15385
|
console.log(
|
|
14739
|
-
|
|
15386
|
+
chalk7.cyan(
|
|
14740
15387
|
"\u{1F50C} [WebSocket Mode] Waiting for analysis completion using WebSocket"
|
|
14741
15388
|
)
|
|
14742
15389
|
);
|
|
@@ -14752,12 +15399,12 @@ async function waitForAnaysisAndReviewPr({
|
|
|
14752
15399
|
|
|
14753
15400
|
// src/commands/scan_skill_input.ts
|
|
14754
15401
|
import fs11 from "fs";
|
|
14755
|
-
import
|
|
15402
|
+
import path11 from "path";
|
|
14756
15403
|
import AdmZip2 from "adm-zip";
|
|
14757
15404
|
var LOCAL_SKILL_ZIP_DATA_URL_PREFIX = "data:application/zip;base64,";
|
|
14758
15405
|
var MAX_LOCAL_SKILL_ARCHIVE_BYTES = 2 * 1024 * 1024;
|
|
14759
15406
|
async function resolveSkillScanInput(skillInput) {
|
|
14760
|
-
const resolvedPath =
|
|
15407
|
+
const resolvedPath = path11.resolve(skillInput);
|
|
14761
15408
|
if (!fs11.existsSync(resolvedPath)) {
|
|
14762
15409
|
return skillInput;
|
|
14763
15410
|
}
|
|
@@ -14771,18 +15418,18 @@ async function resolveSkillScanInput(skillInput) {
|
|
|
14771
15418
|
}
|
|
14772
15419
|
function packageLocalSkillDirectory(directoryPath) {
|
|
14773
15420
|
const zip = new AdmZip2();
|
|
14774
|
-
const rootPath =
|
|
15421
|
+
const rootPath = path11.resolve(directoryPath);
|
|
14775
15422
|
const filePaths = listDirectoryFiles(rootPath);
|
|
14776
15423
|
let hasSkillMd = false;
|
|
14777
15424
|
for (const relativePath of filePaths) {
|
|
14778
|
-
const fullPath =
|
|
14779
|
-
const normalizedFullPath =
|
|
14780
|
-
if (normalizedFullPath !== rootPath && !normalizedFullPath.startsWith(rootPath +
|
|
15425
|
+
const fullPath = path11.join(rootPath, relativePath);
|
|
15426
|
+
const normalizedFullPath = path11.resolve(fullPath);
|
|
15427
|
+
if (normalizedFullPath !== rootPath && !normalizedFullPath.startsWith(rootPath + path11.sep)) {
|
|
14781
15428
|
continue;
|
|
14782
15429
|
}
|
|
14783
15430
|
const fileContent = fs11.readFileSync(normalizedFullPath);
|
|
14784
15431
|
zip.addFile(relativePath, fileContent);
|
|
14785
|
-
if (
|
|
15432
|
+
if (path11.basename(relativePath).toLowerCase() === "skill.md") {
|
|
14786
15433
|
hasSkillMd = true;
|
|
14787
15434
|
}
|
|
14788
15435
|
}
|
|
@@ -14800,17 +15447,17 @@ function packageLocalSkillDirectory(directoryPath) {
|
|
|
14800
15447
|
function listDirectoryFiles(rootPath) {
|
|
14801
15448
|
const files = [];
|
|
14802
15449
|
function walk(relativeDir) {
|
|
14803
|
-
const absoluteDir =
|
|
15450
|
+
const absoluteDir = path11.join(rootPath, relativeDir);
|
|
14804
15451
|
const entries = fs11.readdirSync(absoluteDir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
14805
15452
|
for (const entry of entries) {
|
|
14806
|
-
const safeEntryName =
|
|
15453
|
+
const safeEntryName = path11.basename(entry.name).replace(/\0/g, "");
|
|
14807
15454
|
if (!safeEntryName) {
|
|
14808
15455
|
continue;
|
|
14809
15456
|
}
|
|
14810
|
-
const relativePath = relativeDir ?
|
|
14811
|
-
const absolutePath =
|
|
14812
|
-
const normalizedAbsolutePath =
|
|
14813
|
-
if (normalizedAbsolutePath !== rootPath && !normalizedAbsolutePath.startsWith(rootPath +
|
|
15457
|
+
const relativePath = relativeDir ? path11.posix.join(relativeDir, safeEntryName) : safeEntryName;
|
|
15458
|
+
const absolutePath = path11.join(rootPath, relativePath);
|
|
15459
|
+
const normalizedAbsolutePath = path11.resolve(absolutePath);
|
|
15460
|
+
if (normalizedAbsolutePath !== rootPath && !normalizedAbsolutePath.startsWith(rootPath + path11.sep)) {
|
|
14814
15461
|
continue;
|
|
14815
15462
|
}
|
|
14816
15463
|
if (entry.isSymbolicLink()) {
|
|
@@ -14898,831 +15545,266 @@ async function analyze({
|
|
|
14898
15545
|
pullRequest,
|
|
14899
15546
|
createOnePr,
|
|
14900
15547
|
polling
|
|
14901
|
-
},
|
|
14902
|
-
{ skipPrompts }
|
|
14903
|
-
);
|
|
14904
|
-
}
|
|
14905
|
-
async function addScmToken(addScmTokenOptions) {
|
|
14906
|
-
const { apiKey, token, organization, scmType, url, refreshToken, ci } = addScmTokenOptions;
|
|
14907
|
-
const gqlClient = await getAuthenticatedGQLClient({
|
|
14908
|
-
inputApiKey: apiKey,
|
|
14909
|
-
isSkipPrompts: ci
|
|
14910
|
-
});
|
|
14911
|
-
if (!scmType) {
|
|
14912
|
-
throw new CliError(errorMessages.invalidScmType);
|
|
14913
|
-
}
|
|
14914
|
-
const resp = await gqlClient.updateScmToken({
|
|
14915
|
-
scmType,
|
|
14916
|
-
url,
|
|
14917
|
-
token,
|
|
14918
|
-
org: organization,
|
|
14919
|
-
refreshToken
|
|
14920
|
-
});
|
|
14921
|
-
if (resp.updateScmToken?.__typename === "RepoUnreachableError") {
|
|
14922
|
-
throw new CliError(
|
|
14923
|
-
"Mobb could not reach the repository. Please try again. If Mobb is connected through a broker, please make sure the broker is connected."
|
|
14924
|
-
);
|
|
14925
|
-
} else if (resp.updateScmToken?.__typename === "BadScmCredentials") {
|
|
14926
|
-
throw new CliError("Invalid SCM credentials. Please try again.");
|
|
14927
|
-
} else if (resp.updateScmToken?.__typename === "ScmAccessTokenUpdateSuccess") {
|
|
14928
|
-
console.log("Token added successfully");
|
|
14929
|
-
} else {
|
|
14930
|
-
throw new CliError("Unexpected error, failed to add token");
|
|
14931
|
-
}
|
|
14932
|
-
}
|
|
14933
|
-
async function scan(scanOptions, { skipPrompts = false } = {}) {
|
|
14934
|
-
const { scanner, ci } = scanOptions;
|
|
14935
|
-
!ci && await showWelcomeMessage(skipPrompts);
|
|
14936
|
-
const selectedScanner = scanner || await choseScanner();
|
|
14937
|
-
if (selectedScanner !== SCANNERS.Checkmarx && selectedScanner !== SCANNERS.Snyk) {
|
|
14938
|
-
throw new CliError(
|
|
14939
|
-
"Vulnerability scanning via Bugsy is available only with Snyk and Checkmarx at the moment. Additional scanners will follow soon."
|
|
14940
|
-
);
|
|
14941
|
-
}
|
|
14942
|
-
selectedScanner === SCANNERS.Checkmarx && validateCheckmarxInstallation();
|
|
14943
|
-
if (selectedScanner === SCANNERS.Checkmarx && !scanOptions.cxProjectName) {
|
|
14944
|
-
throw new CliError(errorMessages.missingCxProjectName);
|
|
14945
|
-
}
|
|
14946
|
-
await runAnalysis(
|
|
14947
|
-
{ ...scanOptions, scanner: selectedScanner, command: "scan" },
|
|
14948
|
-
{ skipPrompts }
|
|
14949
|
-
);
|
|
14950
|
-
}
|
|
14951
|
-
async function showWelcomeMessage(skipPrompts = false) {
|
|
14952
|
-
console.log(mobbAscii);
|
|
14953
|
-
const welcome = chalkAnimation.rainbow("\n Welcome to Bugsy\n");
|
|
14954
|
-
skipPrompts ? await sleep(100) : await sleep(2e3);
|
|
14955
|
-
welcome.stop();
|
|
14956
|
-
}
|
|
14957
|
-
var VERDICT_COLORS = {
|
|
14958
|
-
BENIGN: "green",
|
|
14959
|
-
WARNING: "yellow",
|
|
14960
|
-
SUSPICIOUS: "magenta",
|
|
14961
|
-
MALICIOUS: "red"
|
|
14962
|
-
};
|
|
14963
|
-
var SEVERITY_COLORS = {
|
|
14964
|
-
CRITICAL: "red",
|
|
14965
|
-
HIGH: "magenta",
|
|
14966
|
-
MEDIUM: "yellow",
|
|
14967
|
-
LOW: "cyan"
|
|
14968
|
-
};
|
|
14969
|
-
async function scanSkill(options) {
|
|
14970
|
-
const { url, apiKey, ci } = options;
|
|
14971
|
-
const gqlClient = await getAuthenticatedGQLClient({
|
|
14972
|
-
inputApiKey: apiKey,
|
|
14973
|
-
isSkipPrompts: ci
|
|
14974
|
-
});
|
|
14975
|
-
console.log(chalk7.dim(`Scanning skill: ${url}`));
|
|
14976
|
-
console.log();
|
|
14977
|
-
const skillUrl = await resolveSkillScanInput(url);
|
|
14978
|
-
const result = await gqlClient.scanSkill({ skillUrl });
|
|
14979
|
-
const scan2 = result.scanSkill;
|
|
14980
|
-
const verdictColor = VERDICT_COLORS[scan2.verdict] ?? "white";
|
|
14981
|
-
console.log(
|
|
14982
|
-
chalk7.bold(`Verdict: `) + chalk7[verdictColor](
|
|
14983
|
-
scan2.verdict
|
|
14984
|
-
)
|
|
14985
|
-
);
|
|
14986
|
-
console.log(`Skill: ${scan2.skillName}`);
|
|
14987
|
-
if (scan2.skillVersion) {
|
|
14988
|
-
console.log(`Version: ${scan2.skillVersion}`);
|
|
14989
|
-
}
|
|
14990
|
-
console.log(`Hash: ${scan2.skillHash ?? "N/A"}`);
|
|
14991
|
-
console.log(`Findings: ${scan2.findingsCount}`);
|
|
14992
|
-
console.log(`Duration: ${scan2.scanDurationMs}ms`);
|
|
14993
|
-
if (scan2.cached) {
|
|
14994
|
-
console.log(chalk7.dim("(cached result)"));
|
|
14995
|
-
}
|
|
14996
|
-
console.log();
|
|
14997
|
-
if (scan2.findings.length > 0) {
|
|
14998
|
-
console.log(chalk7.bold("Findings:"));
|
|
14999
|
-
console.log();
|
|
15000
|
-
for (const f of scan2.findings) {
|
|
15001
|
-
const sevColor = SEVERITY_COLORS[f.severity] ?? "white";
|
|
15002
|
-
const location = [f.filePath, f.lineNumber].filter(Boolean).join(":");
|
|
15003
|
-
console.log(
|
|
15004
|
-
` ${chalk7[sevColor](f.severity)} [${f.layer}] ${f.category}${f.ruleId ? ` (${f.ruleId})` : ""}`
|
|
15005
|
-
);
|
|
15006
|
-
if (location) {
|
|
15007
|
-
console.log(` ${chalk7.dim(location)}`);
|
|
15008
|
-
}
|
|
15009
|
-
console.log(` ${f.explanation}`);
|
|
15010
|
-
if (f.evidence) {
|
|
15011
|
-
console.log(
|
|
15012
|
-
` ${String(chalk7.dim("Evidence: " + f.evidence.slice(0, 120))).replace(/\n|\r/g, "")}`
|
|
15013
|
-
);
|
|
15014
|
-
}
|
|
15015
|
-
console.log();
|
|
15016
|
-
}
|
|
15017
|
-
}
|
|
15018
|
-
if (scan2.summary) {
|
|
15019
|
-
console.log(chalk7.bold("Analysis:"));
|
|
15020
|
-
console.log(` ${scan2.summary}`);
|
|
15021
|
-
console.log();
|
|
15022
|
-
}
|
|
15023
|
-
if (scan2.verdict === "MALICIOUS" || scan2.verdict === "SUSPICIOUS") {
|
|
15024
|
-
process.exit(2);
|
|
15025
|
-
}
|
|
15026
|
-
}
|
|
15027
|
-
|
|
15028
|
-
// src/args/validation.ts
|
|
15029
|
-
import chalk8 from "chalk";
|
|
15030
|
-
import path11 from "path";
|
|
15031
|
-
import { z as z31 } from "zod";
|
|
15032
|
-
function throwRepoUrlErrorMessage({
|
|
15033
|
-
error,
|
|
15034
|
-
repoUrl,
|
|
15035
|
-
command
|
|
15036
|
-
}) {
|
|
15037
|
-
const errorMessage = error.issues[error.issues.length - 1]?.message;
|
|
15038
|
-
const formattedErrorMessage = `
|
|
15039
|
-
Error: ${chalk8.bold(
|
|
15040
|
-
repoUrl
|
|
15041
|
-
)} is ${errorMessage}
|
|
15042
|
-
Example:
|
|
15043
|
-
mobbdev ${command} -r ${chalk8.bold(
|
|
15044
|
-
"https://github.com/WebGoat/WebGoat"
|
|
15045
|
-
)}`;
|
|
15046
|
-
throw new CliError(formattedErrorMessage);
|
|
15047
|
-
}
|
|
15048
|
-
var UrlZ = z31.string({
|
|
15049
|
-
invalid_type_error: `is not a valid ${Object.values(ScmType).join("/ ")} URL`
|
|
15050
|
-
});
|
|
15051
|
-
function validateOrganizationId(organizationId) {
|
|
15052
|
-
const orgIdValidation = z31.string().uuid().nullish().safeParse(organizationId);
|
|
15053
|
-
if (!orgIdValidation.success) {
|
|
15054
|
-
throw new CliError(`organizationId: ${organizationId} is not a valid UUID`);
|
|
15055
|
-
}
|
|
15056
|
-
}
|
|
15057
|
-
function validateRepoUrl(args) {
|
|
15058
|
-
const repoSafeParseResult = UrlZ.safeParse(args.repo);
|
|
15059
|
-
const { success } = repoSafeParseResult;
|
|
15060
|
-
const [command] = args._;
|
|
15061
|
-
if (!command) {
|
|
15062
|
-
throw new CliError("Command not found");
|
|
15063
|
-
}
|
|
15064
|
-
if (!success) {
|
|
15065
|
-
throwRepoUrlErrorMessage({
|
|
15066
|
-
error: repoSafeParseResult.error,
|
|
15067
|
-
repoUrl: args.repo,
|
|
15068
|
-
command
|
|
15069
|
-
});
|
|
15070
|
-
}
|
|
15071
|
-
}
|
|
15072
|
-
var supportExtensions = [".json", ".xml", ".fpr", ".sarif", ".zip"];
|
|
15073
|
-
function validateReportFileFormat(reportFile) {
|
|
15074
|
-
if (!supportExtensions.includes(path11.extname(reportFile))) {
|
|
15075
|
-
throw new CliError(
|
|
15076
|
-
`
|
|
15077
|
-
${chalk8.bold(
|
|
15078
|
-
reportFile
|
|
15079
|
-
)} is not a supported file extension. Supported extensions are: ${chalk8.bold(
|
|
15080
|
-
supportExtensions.join(", ")
|
|
15081
|
-
)}
|
|
15082
|
-
`
|
|
15083
|
-
);
|
|
15084
|
-
}
|
|
15085
|
-
}
|
|
15086
|
-
|
|
15087
|
-
// src/args/commands/analyze.ts
|
|
15088
|
-
function analyzeBuilder(yargs2) {
|
|
15089
|
-
return yargs2.option("f", {
|
|
15090
|
-
alias: "scan-file",
|
|
15091
|
-
type: "string",
|
|
15092
|
-
describe: chalk9.bold(
|
|
15093
|
-
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog)"
|
|
15094
|
-
)
|
|
15095
|
-
}).option("repo", repoOption).option("p", {
|
|
15096
|
-
alias: "src-path",
|
|
15097
|
-
describe: chalk9.bold(
|
|
15098
|
-
"Path to the repository folder with the source code; alternatively, you can specify the Fortify FPR file to extract source code out of it"
|
|
15099
|
-
),
|
|
15100
|
-
type: "string"
|
|
15101
|
-
}).option("ref", refOption).option("ch", {
|
|
15102
|
-
alias: "commit-hash",
|
|
15103
|
-
describe: chalk9.bold("Hash of the commit"),
|
|
15104
|
-
type: "string"
|
|
15105
|
-
}).option("mobb-project-name", mobbProjectNameOption).option("y", yesOption).option("ci", ciOption).option("org", organizationIdOptions).option("api-key", apiKeyOption).option("commit-hash", commitHashOption).option("auto-pr", autoPrOption).option("create-one-pr", createOnePrOption).option("commit-directly", commitDirectlyOption).option("pull-request", {
|
|
15106
|
-
alias: ["pr", "pr-number", "pr-id"],
|
|
15107
|
-
describe: chalk9.bold("Number of the pull request"),
|
|
15108
|
-
type: "number",
|
|
15109
|
-
demandOption: false
|
|
15110
|
-
}).option("polling", pollingOption).example(
|
|
15111
|
-
"npx mobbdev@latest analyze -r https://github.com/WebGoat/WebGoat -f <your_vulnerability_report_path>",
|
|
15112
|
-
"analyze an existing repository"
|
|
15113
|
-
).help();
|
|
15114
|
-
}
|
|
15115
|
-
function validateAnalyzeOptions(argv) {
|
|
15116
|
-
if (argv.f && !fs12.existsSync(argv.f)) {
|
|
15117
|
-
throw new CliError(`
|
|
15118
|
-
Can't access ${chalk9.bold(argv.f)}`);
|
|
15119
|
-
}
|
|
15120
|
-
validateOrganizationId(argv.organizationId);
|
|
15121
|
-
if (!argv.srcPath && !argv.repo) {
|
|
15122
|
-
throw new CliError("You must supply either --src-path or --repo");
|
|
15123
|
-
}
|
|
15124
|
-
if (!argv.srcPath && argv.repo) {
|
|
15125
|
-
validateRepoUrl(argv);
|
|
15126
|
-
}
|
|
15127
|
-
if (argv.ci && !argv.apiKey) {
|
|
15128
|
-
throw new CliError("--ci flag requires --api-key to be provided as well");
|
|
15129
|
-
}
|
|
15130
|
-
if (argv.commitDirectly && !argv["auto-pr"]) {
|
|
15131
|
-
throw new CliError(
|
|
15132
|
-
"--commit-directly flag requires --auto-pr to be provided as well"
|
|
15133
|
-
);
|
|
15134
|
-
}
|
|
15135
|
-
if (argv["create-one-pr"] && !argv["auto-pr"]) {
|
|
15136
|
-
throw new CliError(
|
|
15137
|
-
"--create-one-pr flag requires --auto-pr to be provided as well"
|
|
15138
|
-
);
|
|
15139
|
-
}
|
|
15140
|
-
if (argv["create-one-pr"] && argv.commitDirectly) {
|
|
15141
|
-
throw new CliError(
|
|
15142
|
-
"--create-one-pr and --commit-directly cannot be provided at the same time"
|
|
15143
|
-
);
|
|
15144
|
-
}
|
|
15145
|
-
if (argv.pullRequest && !argv.commitDirectly) {
|
|
15146
|
-
throw new CliError(
|
|
15147
|
-
"--pull-request flag requires --commit-directly to be provided as well"
|
|
15148
|
-
);
|
|
15149
|
-
}
|
|
15150
|
-
if (argv.f) {
|
|
15151
|
-
validateReportFileFormat(argv.f);
|
|
15152
|
-
}
|
|
15153
|
-
}
|
|
15154
|
-
async function analyzeHandler(args) {
|
|
15155
|
-
validateAnalyzeOptions(args);
|
|
15156
|
-
await analyze(args, { skipPrompts: args.yes });
|
|
15157
|
-
}
|
|
15158
|
-
|
|
15159
|
-
// src/features/claude_code/data_collector.ts
|
|
15160
|
-
import { z as z33 } from "zod";
|
|
15161
|
-
|
|
15162
|
-
// src/args/commands/upload_ai_blame.ts
|
|
15163
|
-
import fsPromises3 from "fs/promises";
|
|
15164
|
-
import * as os3 from "os";
|
|
15165
|
-
import path12 from "path";
|
|
15166
|
-
import chalk10 from "chalk";
|
|
15167
|
-
import { withFile } from "tmp-promise";
|
|
15168
|
-
import z32 from "zod";
|
|
15169
|
-
init_client_generates();
|
|
15170
|
-
init_GitService();
|
|
15171
|
-
init_urlParser2();
|
|
15172
|
-
|
|
15173
|
-
// src/utils/computerName.ts
|
|
15174
|
-
import { execSync } from "child_process";
|
|
15175
|
-
import os2 from "os";
|
|
15176
|
-
var STABLE_COMPUTER_NAME_CONFIG_KEY = "stableComputerName";
|
|
15177
|
-
var HOSTNAME_SUFFIXES = [
|
|
15178
|
-
// Cloud providers (must be first - most specific)
|
|
15179
|
-
".ec2.internal",
|
|
15180
|
-
".compute.internal",
|
|
15181
|
-
".cloudapp.net",
|
|
15182
|
-
// mDNS/Bonjour
|
|
15183
|
-
".local",
|
|
15184
|
-
".localhost",
|
|
15185
|
-
".localdomain",
|
|
15186
|
-
// Home networks
|
|
15187
|
-
".lan",
|
|
15188
|
-
".home",
|
|
15189
|
-
".homelan",
|
|
15190
|
-
// Corporate networks
|
|
15191
|
-
".corp",
|
|
15192
|
-
".internal",
|
|
15193
|
-
".intranet",
|
|
15194
|
-
".domain",
|
|
15195
|
-
".work",
|
|
15196
|
-
".office",
|
|
15197
|
-
// Container environments
|
|
15198
|
-
".docker",
|
|
15199
|
-
".kubernetes",
|
|
15200
|
-
".k8s"
|
|
15201
|
-
];
|
|
15202
|
-
function getOsSpecificComputerName() {
|
|
15203
|
-
const platform2 = os2.platform();
|
|
15204
|
-
try {
|
|
15205
|
-
if (platform2 === "darwin") {
|
|
15206
|
-
const result = execSync("scutil --get LocalHostName", {
|
|
15207
|
-
encoding: "utf-8",
|
|
15208
|
-
timeout: 1e3
|
|
15209
|
-
});
|
|
15210
|
-
return result.trim();
|
|
15211
|
-
} else if (platform2 === "win32") {
|
|
15212
|
-
return process.env["COMPUTERNAME"] || null;
|
|
15213
|
-
} else {
|
|
15214
|
-
try {
|
|
15215
|
-
const result2 = execSync("hostnamectl --static", {
|
|
15216
|
-
encoding: "utf-8",
|
|
15217
|
-
timeout: 1e3
|
|
15218
|
-
});
|
|
15219
|
-
const hostname = result2.trim();
|
|
15220
|
-
if (hostname) return hostname;
|
|
15221
|
-
} catch {
|
|
15222
|
-
}
|
|
15223
|
-
const result = execSync("cat /etc/hostname", {
|
|
15224
|
-
encoding: "utf-8",
|
|
15225
|
-
timeout: 1e3
|
|
15226
|
-
});
|
|
15227
|
-
return result.trim();
|
|
15228
|
-
}
|
|
15229
|
-
} catch (error) {
|
|
15230
|
-
return null;
|
|
15231
|
-
}
|
|
15232
|
-
}
|
|
15233
|
-
function stripHostnameSuffixes(hostname) {
|
|
15234
|
-
if (!hostname) return hostname;
|
|
15235
|
-
let normalized = hostname;
|
|
15236
|
-
for (const suffix of HOSTNAME_SUFFIXES) {
|
|
15237
|
-
if (normalized.endsWith(suffix)) {
|
|
15238
|
-
normalized = normalized.slice(0, -suffix.length);
|
|
15239
|
-
break;
|
|
15240
|
-
}
|
|
15241
|
-
}
|
|
15242
|
-
return normalized;
|
|
15243
|
-
}
|
|
15244
|
-
function generateStableComputerName() {
|
|
15245
|
-
const osSpecificName = getOsSpecificComputerName();
|
|
15246
|
-
if (osSpecificName) {
|
|
15247
|
-
return osSpecificName;
|
|
15248
|
-
}
|
|
15249
|
-
const currentHostname = os2.hostname();
|
|
15250
|
-
return stripHostnameSuffixes(currentHostname);
|
|
15251
|
-
}
|
|
15252
|
-
function getStableComputerName() {
|
|
15253
|
-
const cached = configStore.get(STABLE_COMPUTER_NAME_CONFIG_KEY);
|
|
15254
|
-
if (cached) {
|
|
15255
|
-
return cached;
|
|
15256
|
-
}
|
|
15257
|
-
const currentName = generateStableComputerName();
|
|
15258
|
-
configStore.set(STABLE_COMPUTER_NAME_CONFIG_KEY, currentName);
|
|
15259
|
-
return currentName;
|
|
15260
|
-
}
|
|
15261
|
-
|
|
15262
|
-
// src/utils/sanitize-sensitive-data.ts
|
|
15263
|
-
import { OpenRedaction } from "@openredaction/openredaction";
|
|
15264
|
-
var openRedaction = new OpenRedaction({
|
|
15265
|
-
patterns: [
|
|
15266
|
-
// Core Personal Data
|
|
15267
|
-
// Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
|
|
15268
|
-
// Prefer false negatives over false positives for this use case.
|
|
15269
|
-
"SSN",
|
|
15270
|
-
"NATIONAL_INSURANCE_UK",
|
|
15271
|
-
"DATE_OF_BIRTH",
|
|
15272
|
-
// Identity Documents
|
|
15273
|
-
"PASSPORT_UK",
|
|
15274
|
-
"PASSPORT_US",
|
|
15275
|
-
"PASSPORT_MRZ_TD1",
|
|
15276
|
-
"PASSPORT_MRZ_TD3",
|
|
15277
|
-
"DRIVING_LICENSE_UK",
|
|
15278
|
-
"DRIVING_LICENSE_US",
|
|
15279
|
-
"VISA_NUMBER",
|
|
15280
|
-
"VISA_MRZ",
|
|
15281
|
-
"TAX_ID",
|
|
15282
|
-
// Financial Data (removed SWIFT_BIC, CARD_AUTH_CODE - too broad, causing false positives with authentication words)
|
|
15283
|
-
// Removed CREDIT_CARD - causes false positives on zero-filled UUIDs (e.g. '00000000-0000-0000-0000-000000000000')
|
|
15284
|
-
// Prefer false negatives over false positives for this use case.
|
|
15285
|
-
"IBAN",
|
|
15286
|
-
"BANK_ACCOUNT_UK",
|
|
15287
|
-
"ROUTING_NUMBER_US",
|
|
15288
|
-
"CARD_TRACK1_DATA",
|
|
15289
|
-
"CARD_TRACK2_DATA",
|
|
15290
|
-
"CARD_EXPIRY",
|
|
15291
|
-
// Cryptocurrency (removed BITCOIN_ADDRESS - too broad, matches hash-like strings)
|
|
15292
|
-
"ETHEREUM_ADDRESS",
|
|
15293
|
-
"LITECOIN_ADDRESS",
|
|
15294
|
-
"CARDANO_ADDRESS",
|
|
15295
|
-
"SOLANA_ADDRESS",
|
|
15296
|
-
"MONERO_ADDRESS",
|
|
15297
|
-
"RIPPLE_ADDRESS",
|
|
15298
|
-
// Medical Data (removed PRESCRIPTION_NUMBER - too broad, matches words containing "ription")
|
|
15299
|
-
// Removed MEDICAL_RECORD_NUMBER - too broad, "MR" prefix matches "Merge Request" in SCM contexts (e.g. "MR branches" → "MR br****es")
|
|
15300
|
-
"NHS_NUMBER",
|
|
15301
|
-
"AUSTRALIAN_MEDICARE",
|
|
15302
|
-
"HEALTH_PLAN_NUMBER",
|
|
15303
|
-
"PATIENT_ID",
|
|
15304
|
-
// Communications (removed EMERGENCY_CONTACT, ADDRESS_PO_BOX, ZIP_CODE_US, PHONE_US, PHONE_INTERNATIONAL - too broad, causing false positives)
|
|
15305
|
-
"PHONE_UK",
|
|
15306
|
-
"PHONE_UK_MOBILE",
|
|
15307
|
-
"PHONE_LINE_NUMBER",
|
|
15308
|
-
"ADDRESS_STREET",
|
|
15309
|
-
"POSTCODE_UK",
|
|
15310
|
-
// Network & Technical
|
|
15311
|
-
"IPV4",
|
|
15312
|
-
"IPV6",
|
|
15313
|
-
"MAC_ADDRESS",
|
|
15314
|
-
"URL_WITH_AUTH",
|
|
15315
|
-
// Security Keys & Tokens
|
|
15316
|
-
"PRIVATE_KEY",
|
|
15317
|
-
"SSH_PRIVATE_KEY",
|
|
15318
|
-
"AWS_SECRET_KEY",
|
|
15319
|
-
"AWS_ACCESS_KEY",
|
|
15320
|
-
"AZURE_STORAGE_KEY",
|
|
15321
|
-
"GCP_SERVICE_ACCOUNT",
|
|
15322
|
-
"JWT_TOKEN",
|
|
15323
|
-
"OAUTH_TOKEN",
|
|
15324
|
-
"OAUTH_CLIENT_SECRET",
|
|
15325
|
-
"BEARER_TOKEN",
|
|
15326
|
-
"PAYMENT_TOKEN",
|
|
15327
|
-
"GENERIC_SECRET",
|
|
15328
|
-
"GENERIC_API_KEY",
|
|
15329
|
-
// Platform-Specific API Keys
|
|
15330
|
-
"GITHUB_TOKEN",
|
|
15331
|
-
"SLACK_TOKEN",
|
|
15332
|
-
"STRIPE_API_KEY",
|
|
15333
|
-
"GOOGLE_API_KEY",
|
|
15334
|
-
"FIREBASE_API_KEY",
|
|
15335
|
-
"HEROKU_API_KEY",
|
|
15336
|
-
"MAILGUN_API_KEY",
|
|
15337
|
-
"SENDGRID_API_KEY",
|
|
15338
|
-
"TWILIO_API_KEY",
|
|
15339
|
-
"NPM_TOKEN",
|
|
15340
|
-
"PYPI_TOKEN",
|
|
15341
|
-
"DOCKER_AUTH",
|
|
15342
|
-
"KUBERNETES_SECRET",
|
|
15343
|
-
// Government & Legal
|
|
15344
|
-
// Removed CLIENT_ID - too broad, "client" is ubiquitous in code (npm packages like @scope/client-*, class names like ClientSdkOptions)
|
|
15345
|
-
"POLICE_REPORT_NUMBER",
|
|
15346
|
-
"IMMIGRATION_NUMBER",
|
|
15347
|
-
"COURT_REPORTER_LICENSE"
|
|
15348
|
-
]
|
|
15349
|
-
});
|
|
15350
|
-
function maskString(str, showStart = 2, showEnd = 2) {
|
|
15351
|
-
if (str.length <= showStart + showEnd) {
|
|
15352
|
-
return "*".repeat(str.length);
|
|
15353
|
-
}
|
|
15354
|
-
return str.slice(0, showStart) + "*".repeat(str.length - showStart - showEnd) + str.slice(-showEnd);
|
|
15355
|
-
}
|
|
15356
|
-
async function sanitizeDataWithCounts(obj) {
|
|
15357
|
-
const counts = {
|
|
15358
|
-
detections: { total: 0, high: 0, medium: 0, low: 0 }
|
|
15359
|
-
};
|
|
15360
|
-
const sanitizeString = async (str) => {
|
|
15361
|
-
let result = str;
|
|
15362
|
-
const piiDetections = openRedaction.scan(str);
|
|
15363
|
-
if (piiDetections && piiDetections.total > 0) {
|
|
15364
|
-
const allDetections = [
|
|
15365
|
-
...piiDetections.high,
|
|
15366
|
-
...piiDetections.medium,
|
|
15367
|
-
...piiDetections.low
|
|
15368
|
-
];
|
|
15369
|
-
for (const detection of allDetections) {
|
|
15370
|
-
counts.detections.total++;
|
|
15371
|
-
if (detection.severity === "high") counts.detections.high++;
|
|
15372
|
-
else if (detection.severity === "medium") counts.detections.medium++;
|
|
15373
|
-
else if (detection.severity === "low") counts.detections.low++;
|
|
15374
|
-
const masked = maskString(detection.value);
|
|
15375
|
-
result = result.replaceAll(detection.value, masked);
|
|
15376
|
-
}
|
|
15377
|
-
}
|
|
15378
|
-
return result;
|
|
15379
|
-
};
|
|
15380
|
-
const sanitizeRecursive = async (data) => {
|
|
15381
|
-
if (typeof data === "string") {
|
|
15382
|
-
return sanitizeString(data);
|
|
15383
|
-
} else if (Array.isArray(data)) {
|
|
15384
|
-
return Promise.all(data.map((item) => sanitizeRecursive(item)));
|
|
15385
|
-
} else if (data instanceof Error) {
|
|
15386
|
-
return data;
|
|
15387
|
-
} else if (data instanceof Date) {
|
|
15388
|
-
return data;
|
|
15389
|
-
} else if (typeof data === "object" && data !== null) {
|
|
15390
|
-
const sanitized = {};
|
|
15391
|
-
const record = data;
|
|
15392
|
-
for (const key in record) {
|
|
15393
|
-
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
15394
|
-
sanitized[key] = await sanitizeRecursive(record[key]);
|
|
15395
|
-
}
|
|
15396
|
-
}
|
|
15397
|
-
return sanitized;
|
|
15398
|
-
}
|
|
15399
|
-
return data;
|
|
15400
|
-
};
|
|
15401
|
-
const sanitizedData = await sanitizeRecursive(obj);
|
|
15402
|
-
return { sanitizedData, counts };
|
|
15548
|
+
},
|
|
15549
|
+
{ skipPrompts }
|
|
15550
|
+
);
|
|
15403
15551
|
}
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
}
|
|
15413
|
-
},
|
|
15414
|
-
error: (msg, data) => {
|
|
15415
|
-
if (data !== void 0) {
|
|
15416
|
-
console.error(msg, data);
|
|
15417
|
-
} else {
|
|
15418
|
-
console.error(msg);
|
|
15419
|
-
}
|
|
15552
|
+
async function addScmToken(addScmTokenOptions) {
|
|
15553
|
+
const { apiKey, token, organization, scmType, url, refreshToken, ci } = addScmTokenOptions;
|
|
15554
|
+
const gqlClient = await getAuthenticatedGQLClient({
|
|
15555
|
+
inputApiKey: apiKey,
|
|
15556
|
+
isSkipPrompts: ci
|
|
15557
|
+
});
|
|
15558
|
+
if (!scmType) {
|
|
15559
|
+
throw new CliError(errorMessages.invalidScmType);
|
|
15420
15560
|
}
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15425
|
-
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
inputCount: z32.number(),
|
|
15439
|
-
outputCount: z32.number()
|
|
15440
|
-
}).optional(),
|
|
15441
|
-
text: z32.string().optional(),
|
|
15442
|
-
date: z32.date().optional(),
|
|
15443
|
-
tool: z32.object({
|
|
15444
|
-
name: z32.string(),
|
|
15445
|
-
parameters: z32.string(),
|
|
15446
|
-
result: z32.string(),
|
|
15447
|
-
rawArguments: z32.string().optional(),
|
|
15448
|
-
accepted: z32.boolean().optional(),
|
|
15449
|
-
// MCP-specific fields (only populated for MCP_TOOL_CALL type)
|
|
15450
|
-
mcpServer: z32.string().optional(),
|
|
15451
|
-
// MCP server name (e.g., "datadog", "mobb-mcp")
|
|
15452
|
-
mcpToolName: z32.string().optional()
|
|
15453
|
-
// MCP tool name without prefix (e.g., "scan_and_fix_vulnerabilities")
|
|
15454
|
-
}).optional()
|
|
15455
|
-
});
|
|
15456
|
-
var PromptItemArrayZ = z32.array(PromptItemZ);
|
|
15457
|
-
async function getRepositoryUrl() {
|
|
15458
|
-
try {
|
|
15459
|
-
const gitService = new GitService(process.cwd());
|
|
15460
|
-
const isRepo = await gitService.isGitRepository();
|
|
15461
|
-
if (!isRepo) {
|
|
15462
|
-
return null;
|
|
15463
|
-
}
|
|
15464
|
-
const remoteUrl = await gitService.getRemoteUrl();
|
|
15465
|
-
const parsed = parseScmURL(remoteUrl);
|
|
15466
|
-
return parsed?.scmType === "GitHub" /* GitHub */ || parsed?.scmType === "GitLab" /* GitLab */ ? remoteUrl : null;
|
|
15467
|
-
} catch {
|
|
15468
|
-
return null;
|
|
15561
|
+
const resp = await gqlClient.updateScmToken({
|
|
15562
|
+
scmType,
|
|
15563
|
+
url,
|
|
15564
|
+
token,
|
|
15565
|
+
org: organization,
|
|
15566
|
+
refreshToken
|
|
15567
|
+
});
|
|
15568
|
+
if (resp.updateScmToken?.__typename === "RepoUnreachableError") {
|
|
15569
|
+
throw new CliError(
|
|
15570
|
+
"Mobb could not reach the repository. Please try again. If Mobb is connected through a broker, please make sure the broker is connected."
|
|
15571
|
+
);
|
|
15572
|
+
} else if (resp.updateScmToken?.__typename === "BadScmCredentials") {
|
|
15573
|
+
throw new CliError("Invalid SCM credentials. Please try again.");
|
|
15574
|
+
} else if (resp.updateScmToken?.__typename === "ScmAccessTokenUpdateSuccess") {
|
|
15575
|
+
console.log("Token added successfully");
|
|
15576
|
+
} else {
|
|
15577
|
+
throw new CliError("Unexpected error, failed to add token");
|
|
15469
15578
|
}
|
|
15470
15579
|
}
|
|
15471
|
-
function
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
|
|
15476
|
-
|
|
15580
|
+
async function scan(scanOptions, { skipPrompts = false } = {}) {
|
|
15581
|
+
const { scanner, ci } = scanOptions;
|
|
15582
|
+
!ci && await showWelcomeMessage(skipPrompts);
|
|
15583
|
+
const selectedScanner = scanner || await choseScanner();
|
|
15584
|
+
if (selectedScanner !== SCANNERS.Checkmarx && selectedScanner !== SCANNERS.Snyk) {
|
|
15585
|
+
throw new CliError(
|
|
15586
|
+
"Vulnerability scanning via Bugsy is available only with Snyk and Checkmarx at the moment. Additional scanners will follow soon."
|
|
15587
|
+
);
|
|
15477
15588
|
}
|
|
15478
|
-
|
|
15479
|
-
|
|
15480
|
-
|
|
15481
|
-
}
|
|
15589
|
+
selectedScanner === SCANNERS.Checkmarx && validateCheckmarxInstallation();
|
|
15590
|
+
if (selectedScanner === SCANNERS.Checkmarx && !scanOptions.cxProjectName) {
|
|
15591
|
+
throw new CliError(errorMessages.missingCxProjectName);
|
|
15592
|
+
}
|
|
15593
|
+
await runAnalysis(
|
|
15594
|
+
{ ...scanOptions, scanner: selectedScanner, command: "scan" },
|
|
15595
|
+
{ skipPrompts }
|
|
15596
|
+
);
|
|
15482
15597
|
}
|
|
15483
|
-
function
|
|
15484
|
-
|
|
15485
|
-
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
describe: chalk10.bold("Path(s) to prompt artifact(s) (one per session)")
|
|
15489
|
-
}).option("inference", {
|
|
15490
|
-
type: "string",
|
|
15491
|
-
array: true,
|
|
15492
|
-
demandOption: true,
|
|
15493
|
-
describe: chalk10.bold(
|
|
15494
|
-
"Path(s) to inference artifact(s) (one per session)"
|
|
15495
|
-
)
|
|
15496
|
-
}).option("ai-response-at", {
|
|
15497
|
-
type: "string",
|
|
15498
|
-
array: true,
|
|
15499
|
-
describe: chalk10.bold(
|
|
15500
|
-
"ISO timestamp(s) for AI response (one per session, defaults to now)"
|
|
15501
|
-
)
|
|
15502
|
-
}).option("model", {
|
|
15503
|
-
type: "string",
|
|
15504
|
-
array: true,
|
|
15505
|
-
describe: chalk10.bold("AI model name(s) (optional, one per session)")
|
|
15506
|
-
}).option("tool-name", {
|
|
15507
|
-
type: "string",
|
|
15508
|
-
array: true,
|
|
15509
|
-
describe: chalk10.bold("Tool/IDE name(s) (optional, one per session)")
|
|
15510
|
-
}).option("blame-type", {
|
|
15511
|
-
type: "string",
|
|
15512
|
-
array: true,
|
|
15513
|
-
choices: Object.values(AiBlameInferenceType),
|
|
15514
|
-
describe: chalk10.bold(
|
|
15515
|
-
"Blame type(s) (optional, one per session, defaults to CHAT)"
|
|
15516
|
-
)
|
|
15517
|
-
}).strict();
|
|
15598
|
+
async function showWelcomeMessage(skipPrompts = false) {
|
|
15599
|
+
console.log(mobbAscii);
|
|
15600
|
+
const welcome = chalkAnimation.rainbow("\n Welcome to Bugsy\n");
|
|
15601
|
+
skipPrompts ? await sleep(100) : await sleep(2e3);
|
|
15602
|
+
welcome.stop();
|
|
15518
15603
|
}
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
promptsUUID = path12.basename(promptFile.path, path12.extname(promptFile.path));
|
|
15537
|
-
await fsPromises3.writeFile(
|
|
15538
|
-
promptFile.path,
|
|
15539
|
-
JSON.stringify(promptsResult.sanitizedData, null, 2),
|
|
15540
|
-
"utf-8"
|
|
15541
|
-
);
|
|
15542
|
-
uploadArgs.prompt.push(promptFile.path);
|
|
15543
|
-
await withFile(async (inferenceFile) => {
|
|
15544
|
-
const inferenceResult = await sanitizeDataWithCounts(args.inference);
|
|
15545
|
-
inferenceCounts = inferenceResult.counts;
|
|
15546
|
-
inferenceUUID = path12.basename(
|
|
15547
|
-
inferenceFile.path,
|
|
15548
|
-
path12.extname(inferenceFile.path)
|
|
15549
|
-
);
|
|
15550
|
-
await fsPromises3.writeFile(
|
|
15551
|
-
inferenceFile.path,
|
|
15552
|
-
inferenceResult.sanitizedData,
|
|
15553
|
-
"utf-8"
|
|
15554
|
-
);
|
|
15555
|
-
uploadArgs.inference.push(inferenceFile.path);
|
|
15556
|
-
uploadArgs.model.push(args.model);
|
|
15557
|
-
uploadArgs.toolName.push(args.tool);
|
|
15558
|
-
uploadArgs.aiResponseAt.push(args.responseTime);
|
|
15559
|
-
uploadArgs.blameType.push(args.blameType || "CHAT" /* Chat */);
|
|
15560
|
-
if (args.sessionId) {
|
|
15561
|
-
uploadArgs.sessionId.push(args.sessionId);
|
|
15562
|
-
}
|
|
15563
|
-
await uploadAiBlameHandler({
|
|
15564
|
-
args: uploadArgs,
|
|
15565
|
-
exitOnError: false,
|
|
15566
|
-
apiUrl: args.apiUrl,
|
|
15567
|
-
webAppUrl: args.webAppUrl,
|
|
15568
|
-
repositoryUrl: args.repositoryUrl
|
|
15569
|
-
});
|
|
15570
|
-
});
|
|
15604
|
+
var VERDICT_COLORS = {
|
|
15605
|
+
BENIGN: "green",
|
|
15606
|
+
WARNING: "yellow",
|
|
15607
|
+
SUSPICIOUS: "magenta",
|
|
15608
|
+
MALICIOUS: "red"
|
|
15609
|
+
};
|
|
15610
|
+
var SEVERITY_COLORS = {
|
|
15611
|
+
CRITICAL: "red",
|
|
15612
|
+
HIGH: "magenta",
|
|
15613
|
+
MEDIUM: "yellow",
|
|
15614
|
+
LOW: "cyan"
|
|
15615
|
+
};
|
|
15616
|
+
async function scanSkill(options) {
|
|
15617
|
+
const { url, apiKey, ci } = options;
|
|
15618
|
+
const gqlClient = await getAuthenticatedGQLClient({
|
|
15619
|
+
inputApiKey: apiKey,
|
|
15620
|
+
isSkipPrompts: ci
|
|
15571
15621
|
});
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
} = options;
|
|
15587
|
-
const prompts = args.prompt || [];
|
|
15588
|
-
const inferences = args.inference || [];
|
|
15589
|
-
const models = args.model || [];
|
|
15590
|
-
const tools = args.toolName || args["tool-name"] || [];
|
|
15591
|
-
const responseTimes = args.aiResponseAt || args["ai-response-at"] || [];
|
|
15592
|
-
const blameTypes = args.blameType || args["blame-type"] || [];
|
|
15593
|
-
const sessionIds = args.sessionId || args["session-id"] || [];
|
|
15594
|
-
if (prompts.length !== inferences.length) {
|
|
15595
|
-
const errorMsg = "prompt and inference must have the same number of entries";
|
|
15596
|
-
logger2.error(chalk10.red(errorMsg));
|
|
15597
|
-
if (exitOnError) {
|
|
15598
|
-
process.exit(1);
|
|
15599
|
-
}
|
|
15600
|
-
throw new Error(errorMsg);
|
|
15622
|
+
console.log(chalk8.dim(`Scanning skill: ${url}`));
|
|
15623
|
+
console.log();
|
|
15624
|
+
const skillUrl = await resolveSkillScanInput(url);
|
|
15625
|
+
const result = await gqlClient.scanSkill({ skillUrl });
|
|
15626
|
+
const scan2 = result.scanSkill;
|
|
15627
|
+
const verdictColor = VERDICT_COLORS[scan2.verdict] ?? "white";
|
|
15628
|
+
console.log(
|
|
15629
|
+
chalk8.bold(`Verdict: `) + chalk8[verdictColor](
|
|
15630
|
+
scan2.verdict
|
|
15631
|
+
)
|
|
15632
|
+
);
|
|
15633
|
+
console.log(`Skill: ${scan2.skillName}`);
|
|
15634
|
+
if (scan2.skillVersion) {
|
|
15635
|
+
console.log(`Version: ${scan2.skillVersion}`);
|
|
15601
15636
|
}
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
]
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15618
|
-
|
|
15637
|
+
console.log(`Hash: ${scan2.skillHash ?? "N/A"}`);
|
|
15638
|
+
console.log(`Findings: ${scan2.findingsCount}`);
|
|
15639
|
+
console.log(`Duration: ${scan2.scanDurationMs}ms`);
|
|
15640
|
+
if (scan2.cached) {
|
|
15641
|
+
console.log(chalk8.dim("(cached result)"));
|
|
15642
|
+
}
|
|
15643
|
+
console.log();
|
|
15644
|
+
if (scan2.findings.length > 0) {
|
|
15645
|
+
console.log(chalk8.bold("Findings:"));
|
|
15646
|
+
console.log();
|
|
15647
|
+
for (const f of scan2.findings) {
|
|
15648
|
+
const sevColor = SEVERITY_COLORS[f.severity] ?? "white";
|
|
15649
|
+
const location = [f.filePath, f.lineNumber].filter(Boolean).join(":");
|
|
15650
|
+
console.log(
|
|
15651
|
+
` ${chalk8[sevColor](f.severity)} [${f.layer}] ${f.category}${f.ruleId ? ` (${f.ruleId})` : ""}`
|
|
15652
|
+
);
|
|
15653
|
+
if (location) {
|
|
15654
|
+
console.log(` ${chalk8.dim(location)}`);
|
|
15619
15655
|
}
|
|
15620
|
-
|
|
15656
|
+
console.log(` ${f.explanation}`);
|
|
15657
|
+
if (f.evidence) {
|
|
15658
|
+
console.log(
|
|
15659
|
+
` ${String(chalk8.dim("Evidence: " + f.evidence.slice(0, 120))).replace(/\n|\r/g, "")}`
|
|
15660
|
+
);
|
|
15661
|
+
}
|
|
15662
|
+
console.log();
|
|
15621
15663
|
}
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
|
|
15664
|
+
}
|
|
15665
|
+
if (scan2.summary) {
|
|
15666
|
+
console.log(chalk8.bold("Analysis:"));
|
|
15667
|
+
console.log(` ${scan2.summary}`);
|
|
15668
|
+
console.log();
|
|
15669
|
+
}
|
|
15670
|
+
if (scan2.verdict === "MALICIOUS" || scan2.verdict === "SUSPICIOUS") {
|
|
15671
|
+
process.exit(2);
|
|
15672
|
+
}
|
|
15673
|
+
}
|
|
15674
|
+
|
|
15675
|
+
// src/args/validation.ts
|
|
15676
|
+
import chalk9 from "chalk";
|
|
15677
|
+
import path12 from "path";
|
|
15678
|
+
import { z as z32 } from "zod";
|
|
15679
|
+
function throwRepoUrlErrorMessage({
|
|
15680
|
+
error,
|
|
15681
|
+
repoUrl,
|
|
15682
|
+
command
|
|
15683
|
+
}) {
|
|
15684
|
+
const errorMessage = error.issues[error.issues.length - 1]?.message;
|
|
15685
|
+
const formattedErrorMessage = `
|
|
15686
|
+
Error: ${chalk9.bold(
|
|
15687
|
+
repoUrl
|
|
15688
|
+
)} is ${errorMessage}
|
|
15689
|
+
Example:
|
|
15690
|
+
mobbdev ${command} -r ${chalk9.bold(
|
|
15691
|
+
"https://github.com/WebGoat/WebGoat"
|
|
15692
|
+
)}`;
|
|
15693
|
+
throw new CliError(formattedErrorMessage);
|
|
15694
|
+
}
|
|
15695
|
+
var UrlZ = z32.string({
|
|
15696
|
+
invalid_type_error: `is not a valid ${Object.values(ScmType).join("/ ")} URL`
|
|
15697
|
+
});
|
|
15698
|
+
function validateOrganizationId(organizationId) {
|
|
15699
|
+
const orgIdValidation = z32.string().uuid().nullish().safeParse(organizationId);
|
|
15700
|
+
if (!orgIdValidation.success) {
|
|
15701
|
+
throw new CliError(`organizationId: ${organizationId} is not a valid UUID`);
|
|
15702
|
+
}
|
|
15703
|
+
}
|
|
15704
|
+
function validateRepoUrl(args) {
|
|
15705
|
+
const repoSafeParseResult = UrlZ.safeParse(args.repo);
|
|
15706
|
+
const { success } = repoSafeParseResult;
|
|
15707
|
+
const [command] = args._;
|
|
15708
|
+
if (!command) {
|
|
15709
|
+
throw new CliError("Command not found");
|
|
15710
|
+
}
|
|
15711
|
+
if (!success) {
|
|
15712
|
+
throwRepoUrlErrorMessage({
|
|
15713
|
+
error: repoSafeParseResult.error,
|
|
15714
|
+
repoUrl: args.repo,
|
|
15715
|
+
command
|
|
15633
15716
|
});
|
|
15634
15717
|
}
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
const errorMsg = "Init failed to return expected number of sessions";
|
|
15649
|
-
logger2.error(chalk10.red(errorMsg));
|
|
15650
|
-
if (exitOnError) {
|
|
15651
|
-
process.exit(1);
|
|
15652
|
-
}
|
|
15653
|
-
throw new Error(errorMsg);
|
|
15718
|
+
}
|
|
15719
|
+
var supportExtensions = [".json", ".xml", ".fpr", ".sarif", ".zip"];
|
|
15720
|
+
function validateReportFileFormat(reportFile) {
|
|
15721
|
+
if (!supportExtensions.includes(path12.extname(reportFile))) {
|
|
15722
|
+
throw new CliError(
|
|
15723
|
+
`
|
|
15724
|
+
${chalk9.bold(
|
|
15725
|
+
reportFile
|
|
15726
|
+
)} is not a supported file extension. Supported extensions are: ${chalk9.bold(
|
|
15727
|
+
supportExtensions.join(", ")
|
|
15728
|
+
)}
|
|
15729
|
+
`
|
|
15730
|
+
);
|
|
15654
15731
|
}
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
|
|
15662
|
-
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15732
|
+
}
|
|
15733
|
+
|
|
15734
|
+
// src/args/commands/analyze.ts
|
|
15735
|
+
function analyzeBuilder(yargs2) {
|
|
15736
|
+
return yargs2.option("f", {
|
|
15737
|
+
alias: "scan-file",
|
|
15738
|
+
type: "string",
|
|
15739
|
+
describe: chalk10.bold(
|
|
15740
|
+
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog)"
|
|
15741
|
+
)
|
|
15742
|
+
}).option("repo", repoOption).option("p", {
|
|
15743
|
+
alias: "src-path",
|
|
15744
|
+
describe: chalk10.bold(
|
|
15745
|
+
"Path to the repository folder with the source code; alternatively, you can specify the Fortify FPR file to extract source code out of it"
|
|
15746
|
+
),
|
|
15747
|
+
type: "string"
|
|
15748
|
+
}).option("ref", refOption).option("ch", {
|
|
15749
|
+
alias: "commit-hash",
|
|
15750
|
+
describe: chalk10.bold("Hash of the commit"),
|
|
15751
|
+
type: "string"
|
|
15752
|
+
}).option("mobb-project-name", mobbProjectNameOption).option("y", yesOption).option("ci", ciOption).option("org", organizationIdOptions).option("api-key", apiKeyOption).option("commit-hash", commitHashOption).option("auto-pr", autoPrOption).option("create-one-pr", createOnePrOption).option("commit-directly", commitDirectlyOption).option("pull-request", {
|
|
15753
|
+
alias: ["pr", "pr-number", "pr-id"],
|
|
15754
|
+
describe: chalk10.bold("Number of the pull request"),
|
|
15755
|
+
type: "number",
|
|
15756
|
+
demandOption: false
|
|
15757
|
+
}).option("polling", pollingOption).example(
|
|
15758
|
+
"npx mobbdev@latest analyze -r https://github.com/WebGoat/WebGoat -f <your_vulnerability_report_path>",
|
|
15759
|
+
"analyze an existing repository"
|
|
15760
|
+
).help();
|
|
15761
|
+
}
|
|
15762
|
+
function validateAnalyzeOptions(argv) {
|
|
15763
|
+
if (argv.f && !fs12.existsSync(argv.f)) {
|
|
15764
|
+
throw new CliError(`
|
|
15765
|
+
Can't access ${chalk10.bold(argv.f)}`);
|
|
15675
15766
|
}
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
|
|
15680
|
-
|
|
15681
|
-
|
|
15682
|
-
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
repositoryUrl: s.repositoryUrl
|
|
15690
|
-
};
|
|
15691
|
-
});
|
|
15692
|
-
try {
|
|
15693
|
-
logger2.info(
|
|
15694
|
-
`[UPLOAD] Calling finalizeAIBlameInferencesUploadRaw with ${finalizeSessions.length} sessions`
|
|
15767
|
+
validateOrganizationId(argv.organizationId);
|
|
15768
|
+
if (!argv.srcPath && !argv.repo) {
|
|
15769
|
+
throw new CliError("You must supply either --src-path or --repo");
|
|
15770
|
+
}
|
|
15771
|
+
if (!argv.srcPath && argv.repo) {
|
|
15772
|
+
validateRepoUrl(argv);
|
|
15773
|
+
}
|
|
15774
|
+
if (argv.ci && !argv.apiKey) {
|
|
15775
|
+
throw new CliError("--ci flag requires --api-key to be provided as well");
|
|
15776
|
+
}
|
|
15777
|
+
if (argv.commitDirectly && !argv["auto-pr"]) {
|
|
15778
|
+
throw new CliError(
|
|
15779
|
+
"--commit-directly flag requires --auto-pr to be provided as well"
|
|
15695
15780
|
);
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15781
|
+
}
|
|
15782
|
+
if (argv["create-one-pr"] && !argv["auto-pr"]) {
|
|
15783
|
+
throw new CliError(
|
|
15784
|
+
"--create-one-pr flag requires --auto-pr to be provided as well"
|
|
15700
15785
|
);
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
}
|
|
15715
|
-
logger2.info(chalk10.green("[UPLOAD] AI Blame uploads finalized successfully"));
|
|
15716
|
-
} catch (error) {
|
|
15717
|
-
logger2.error("[UPLOAD] Finalize threw error:", error);
|
|
15718
|
-
throw error;
|
|
15786
|
+
}
|
|
15787
|
+
if (argv["create-one-pr"] && argv.commitDirectly) {
|
|
15788
|
+
throw new CliError(
|
|
15789
|
+
"--create-one-pr and --commit-directly cannot be provided at the same time"
|
|
15790
|
+
);
|
|
15791
|
+
}
|
|
15792
|
+
if (argv.pullRequest && !argv.commitDirectly) {
|
|
15793
|
+
throw new CliError(
|
|
15794
|
+
"--pull-request flag requires --commit-directly to be provided as well"
|
|
15795
|
+
);
|
|
15796
|
+
}
|
|
15797
|
+
if (argv.f) {
|
|
15798
|
+
validateReportFileFormat(argv.f);
|
|
15719
15799
|
}
|
|
15720
15800
|
}
|
|
15721
|
-
async function
|
|
15722
|
-
|
|
15801
|
+
async function analyzeHandler(args) {
|
|
15802
|
+
validateAnalyzeOptions(args);
|
|
15803
|
+
await analyze(args, { skipPrompts: args.yes });
|
|
15723
15804
|
}
|
|
15724
15805
|
|
|
15725
15806
|
// src/features/claude_code/data_collector.ts
|
|
15807
|
+
import { z as z33 } from "zod";
|
|
15726
15808
|
init_client_generates();
|
|
15727
15809
|
init_GitService();
|
|
15728
15810
|
init_urlParser2();
|
|
@@ -16348,7 +16430,7 @@ var log = logger.log.bind(logger);
|
|
|
16348
16430
|
|
|
16349
16431
|
// src/mcp/services/McpGQLClient.ts
|
|
16350
16432
|
import crypto3 from "crypto";
|
|
16351
|
-
import { GraphQLClient as GraphQLClient2 } from "graphql-request";
|
|
16433
|
+
import { ClientError as ClientError2, GraphQLClient as GraphQLClient2 } from "graphql-request";
|
|
16352
16434
|
import { v4 as uuidv42 } from "uuid";
|
|
16353
16435
|
init_client_generates();
|
|
16354
16436
|
init_configs();
|
|
@@ -16487,12 +16569,15 @@ import crypto2 from "crypto";
|
|
|
16487
16569
|
import os5 from "os";
|
|
16488
16570
|
import open4 from "open";
|
|
16489
16571
|
init_configs();
|
|
16490
|
-
var
|
|
16572
|
+
var _McpAuthService = class _McpAuthService {
|
|
16491
16573
|
constructor(client) {
|
|
16492
16574
|
__publicField(this, "client");
|
|
16493
|
-
__publicField(this, "lastBrowserOpenTime", 0);
|
|
16494
16575
|
this.client = client;
|
|
16495
16576
|
}
|
|
16577
|
+
/** Reset cooldown state. Used by tests to ensure isolation. */
|
|
16578
|
+
static resetCooldown() {
|
|
16579
|
+
_McpAuthService.lastBrowserOpenTime = 0;
|
|
16580
|
+
}
|
|
16496
16581
|
/**
|
|
16497
16582
|
* Opens a browser window for authentication
|
|
16498
16583
|
* @param url URL to open in browser
|
|
@@ -16501,14 +16586,15 @@ var McpAuthService = class {
|
|
|
16501
16586
|
async openBrowser(url, isBackgoundCall) {
|
|
16502
16587
|
if (isBackgoundCall) {
|
|
16503
16588
|
const now = Date.now();
|
|
16504
|
-
if (now -
|
|
16589
|
+
if (now - _McpAuthService.lastBrowserOpenTime < MCP_TOOLS_BROWSER_COOLDOWN_MS) {
|
|
16505
16590
|
logDebug(`browser cooldown active, skipping open for ${url}`);
|
|
16506
|
-
return;
|
|
16591
|
+
return false;
|
|
16507
16592
|
}
|
|
16508
16593
|
}
|
|
16509
16594
|
logDebug(`opening browser url ${url}`);
|
|
16510
16595
|
await open4(url);
|
|
16511
|
-
|
|
16596
|
+
_McpAuthService.lastBrowserOpenTime = Date.now();
|
|
16597
|
+
return true;
|
|
16512
16598
|
}
|
|
16513
16599
|
/**
|
|
16514
16600
|
* Handles the complete authentication flow
|
|
@@ -16530,7 +16616,12 @@ var McpAuthService = class {
|
|
|
16530
16616
|
logDebug(`cli login created ${loginId}`);
|
|
16531
16617
|
const webLoginUrl = `${WEB_APP_URL}/mvs-login`;
|
|
16532
16618
|
const browserUrl = loginContext ? buildLoginUrl(webLoginUrl, loginId, os5.hostname(), loginContext) : `${webLoginUrl}/${loginId}?hostname=${os5.hostname()}`;
|
|
16533
|
-
await this.openBrowser(browserUrl, isBackgoundCall);
|
|
16619
|
+
const browserOpened = await this.openBrowser(browserUrl, isBackgoundCall);
|
|
16620
|
+
if (!browserOpened) {
|
|
16621
|
+
throw new AuthenticationError(
|
|
16622
|
+
"Authentication required but browser cooldown is active"
|
|
16623
|
+
);
|
|
16624
|
+
}
|
|
16534
16625
|
logDebug(`waiting for login to complete`);
|
|
16535
16626
|
let newApiToken = null;
|
|
16536
16627
|
for (let i = 0; i < MCP_LOGIN_MAX_WAIT / MCP_LOGIN_CHECK_DELAY; i++) {
|
|
@@ -16561,8 +16652,24 @@ var McpAuthService = class {
|
|
|
16561
16652
|
return newApiToken;
|
|
16562
16653
|
}
|
|
16563
16654
|
};
|
|
16655
|
+
// Static so cooldown persists across McpAuthService instances
|
|
16656
|
+
__publicField(_McpAuthService, "lastBrowserOpenTime", 0);
|
|
16657
|
+
var McpAuthService = _McpAuthService;
|
|
16564
16658
|
|
|
16565
16659
|
// src/mcp/services/McpGQLClient.ts
|
|
16660
|
+
function isAuthError2(error) {
|
|
16661
|
+
if (error instanceof ClientError2) {
|
|
16662
|
+
const gqlErrors = error.response?.errors;
|
|
16663
|
+
return gqlErrors?.some(
|
|
16664
|
+
(e) => e.extensions?.["code"] === "access-denied" || e.message?.includes("Authentication hook unauthorized")
|
|
16665
|
+
) ?? false;
|
|
16666
|
+
}
|
|
16667
|
+
return false;
|
|
16668
|
+
}
|
|
16669
|
+
function isNetworkError2(error) {
|
|
16670
|
+
const errorString = error?.toString() ?? "";
|
|
16671
|
+
return errorString.includes("FetchError") || errorString.includes("TypeError") || errorString.includes("ECONNREFUSED") || errorString.includes("ENOTFOUND") || errorString.includes("ETIMEDOUT") || errorString.includes("UND_ERR");
|
|
16672
|
+
}
|
|
16566
16673
|
var McpGQLClient = class {
|
|
16567
16674
|
constructor(args) {
|
|
16568
16675
|
__publicField(this, "client");
|
|
@@ -16616,14 +16723,16 @@ var McpGQLClient = class {
|
|
|
16616
16723
|
logDebug("[GraphQL] Me query successful", { result });
|
|
16617
16724
|
return true;
|
|
16618
16725
|
} catch (e) {
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16726
|
+
logDebug("[GraphQL] API connection verification failed", { error: e });
|
|
16727
|
+
if (isNetworkError2(e)) {
|
|
16728
|
+
logError("[GraphQL] API endpoint unreachable (network error)", {
|
|
16729
|
+
error: e
|
|
16730
|
+
});
|
|
16623
16731
|
return false;
|
|
16624
16732
|
}
|
|
16733
|
+
logDebug("[GraphQL] API endpoint is reachable (non-network error)");
|
|
16734
|
+
return true;
|
|
16625
16735
|
}
|
|
16626
|
-
return true;
|
|
16627
16736
|
}
|
|
16628
16737
|
/**
|
|
16629
16738
|
* Verifies both API endpoint reachability and user authentication
|
|
@@ -16894,11 +17003,27 @@ var McpGQLClient = class {
|
|
|
16894
17003
|
try {
|
|
16895
17004
|
await this.clientSdk.CreateCommunityUser();
|
|
16896
17005
|
const info = await this.getUserInfo();
|
|
17006
|
+
if (!info) {
|
|
17007
|
+
logDebug("[GraphQL] User token is invalid (no user info returned)");
|
|
17008
|
+
return false;
|
|
17009
|
+
}
|
|
16897
17010
|
logDebug("[GraphQL] User token validated successfully");
|
|
16898
|
-
return info
|
|
17011
|
+
return info.email || true;
|
|
16899
17012
|
} catch (e) {
|
|
16900
|
-
|
|
16901
|
-
|
|
17013
|
+
if (isAuthError2(e)) {
|
|
17014
|
+
logDebug("[GraphQL] User token is invalid (auth error from server)");
|
|
17015
|
+
return false;
|
|
17016
|
+
}
|
|
17017
|
+
if (isNetworkError2(e)) {
|
|
17018
|
+
logError("[GraphQL] Token validation failed due to network error", {
|
|
17019
|
+
error: e
|
|
17020
|
+
});
|
|
17021
|
+
throw e;
|
|
17022
|
+
}
|
|
17023
|
+
logError("[GraphQL] Token validation failed with unexpected error", {
|
|
17024
|
+
error: e
|
|
17025
|
+
});
|
|
17026
|
+
throw e;
|
|
16902
17027
|
}
|
|
16903
17028
|
}
|
|
16904
17029
|
async createCliLogin(variables) {
|
|
@@ -17206,7 +17331,15 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
17206
17331
|
throw new ApiConnectionError("Error: failed to reach Mobb GraphQL endpoint");
|
|
17207
17332
|
}
|
|
17208
17333
|
logDebug("[GraphQL] Validating user token");
|
|
17209
|
-
|
|
17334
|
+
let userVerify;
|
|
17335
|
+
try {
|
|
17336
|
+
userVerify = await initialClient.validateUserToken();
|
|
17337
|
+
} catch (e) {
|
|
17338
|
+
logError("[GraphQL] Token validation failed due to transient error", {
|
|
17339
|
+
error: e
|
|
17340
|
+
});
|
|
17341
|
+
throw e;
|
|
17342
|
+
}
|
|
17210
17343
|
if (userVerify) {
|
|
17211
17344
|
return initialClient;
|
|
17212
17345
|
}
|
|
@@ -20157,13 +20290,7 @@ var BaseTool = class {
|
|
|
20157
20290
|
}
|
|
20158
20291
|
async execute(args) {
|
|
20159
20292
|
if (this.hasAuthentication) {
|
|
20160
|
-
logDebug(`
|
|
20161
|
-
const loginContext = createMcpLoginContext(this.name);
|
|
20162
|
-
const mcpGqlClient = await createAuthenticatedMcpGQLClient({
|
|
20163
|
-
loginContext
|
|
20164
|
-
});
|
|
20165
|
-
const userInfo2 = await mcpGqlClient.getUserInfo();
|
|
20166
|
-
logDebug("User authenticated successfully", { userInfo: userInfo2 });
|
|
20293
|
+
logDebug(`Tool ${this.name} requires authentication (handled by service)`);
|
|
20167
20294
|
}
|
|
20168
20295
|
const validatedArgs = this.validateInput(args);
|
|
20169
20296
|
logDebug(`Tool ${this.name} input validation successful`, {
|
|
@@ -24963,13 +25090,13 @@ var parseArgs = async (args) => {
|
|
|
24963
25090
|
};
|
|
24964
25091
|
|
|
24965
25092
|
// src/index.ts
|
|
24966
|
-
var
|
|
25093
|
+
var debug22 = Debug22("mobbdev:index");
|
|
24967
25094
|
async function run() {
|
|
24968
25095
|
return parseArgs(hideBin(process.argv));
|
|
24969
25096
|
}
|
|
24970
25097
|
(async () => {
|
|
24971
25098
|
try {
|
|
24972
|
-
|
|
25099
|
+
debug22("Bugsy CLI v%s running...", packageJson.version);
|
|
24973
25100
|
await run();
|
|
24974
25101
|
process.exit(0);
|
|
24975
25102
|
} catch (err) {
|