mobbdev 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +266 -189
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -21,12 +21,16 @@ var mobbCliCommand = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/args/yargs.ts
|
|
24
|
-
import
|
|
24
|
+
import chalk10 from "chalk";
|
|
25
25
|
import yargs from "yargs/yargs";
|
|
26
26
|
|
|
27
27
|
// src/args/commands/analyze.ts
|
|
28
28
|
import fs5 from "node:fs";
|
|
29
29
|
|
|
30
|
+
// src/commands/index.ts
|
|
31
|
+
import crypto from "node:crypto";
|
|
32
|
+
import os from "node:os";
|
|
33
|
+
|
|
30
34
|
// src/constants.ts
|
|
31
35
|
import path from "node:path";
|
|
32
36
|
import { fileURLToPath } from "node:url";
|
|
@@ -144,6 +148,7 @@ var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
|
|
|
144
148
|
IssueType_Enum2["MissingEqualsOrHashcode"] = "MISSING_EQUALS_OR_HASHCODE";
|
|
145
149
|
IssueType_Enum2["MissingHstsHeader"] = "MISSING_HSTS_HEADER";
|
|
146
150
|
IssueType_Enum2["NonFinalPublicStaticField"] = "NON_FINAL_PUBLIC_STATIC_FIELD";
|
|
151
|
+
IssueType_Enum2["NonReadonlyField"] = "NON_READONLY_FIELD";
|
|
147
152
|
IssueType_Enum2["NoEquivalenceMethod"] = "NO_EQUIVALENCE_METHOD";
|
|
148
153
|
IssueType_Enum2["NoLimitsOrThrottling"] = "NO_LIMITS_OR_THROTTLING";
|
|
149
154
|
IssueType_Enum2["NullDereference"] = "NULL_DEREFERENCE";
|
|
@@ -159,6 +164,7 @@ var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
|
|
|
159
164
|
IssueType_Enum2["RegexInjection"] = "REGEX_INJECTION";
|
|
160
165
|
IssueType_Enum2["SqlInjection"] = "SQL_Injection";
|
|
161
166
|
IssueType_Enum2["Ssrf"] = "SSRF";
|
|
167
|
+
IssueType_Enum2["StringFormatMisuse"] = "STRING_FORMAT_MISUSE";
|
|
162
168
|
IssueType_Enum2["SystemInformationLeak"] = "SYSTEM_INFORMATION_LEAK";
|
|
163
169
|
IssueType_Enum2["SystemInformationLeakExternal"] = "SYSTEM_INFORMATION_LEAK_EXTERNAL";
|
|
164
170
|
IssueType_Enum2["TrustBoundaryViolation"] = "TRUST_BOUNDARY_VIOLATION";
|
|
@@ -414,6 +420,9 @@ var UpdateScmTokenDocument = `
|
|
|
414
420
|
status
|
|
415
421
|
error
|
|
416
422
|
}
|
|
423
|
+
... on RepoUnreachableError {
|
|
424
|
+
status
|
|
425
|
+
}
|
|
417
426
|
}
|
|
418
427
|
}
|
|
419
428
|
`;
|
|
@@ -731,7 +740,9 @@ var issueTypeMap = {
|
|
|
731
740
|
["MISSING_CSP_HEADER" /* MissingCspHeader */]: "Missing CSP Header",
|
|
732
741
|
["HARDCODED_DOMAIN_IN_HTML" /* HardcodedDomainInHtml */]: "Hardcoded Domain in HTML",
|
|
733
742
|
["HEAP_INSPECTION" /* HeapInspection */]: "Heap Inspection",
|
|
734
|
-
["CLIENT_DOM_STORED_CODE_INJECTION" /* ClientDomStoredCodeInjection */]: "Client Code Injection"
|
|
743
|
+
["CLIENT_DOM_STORED_CODE_INJECTION" /* ClientDomStoredCodeInjection */]: "Client Code Injection",
|
|
744
|
+
["STRING_FORMAT_MISUSE" /* StringFormatMisuse */]: "String Format Misuse",
|
|
745
|
+
["NON_READONLY_FIELD" /* NonReadonlyField */]: "Non Readonly Field"
|
|
735
746
|
};
|
|
736
747
|
var issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
737
748
|
var getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -1337,9 +1348,7 @@ var progressMassages = {
|
|
|
1337
1348
|
var VUL_REPORT_DIGEST_TIMEOUT_MS = 1e3 * 60 * 30;
|
|
1338
1349
|
|
|
1339
1350
|
// src/features/analysis/index.ts
|
|
1340
|
-
import crypto from "node:crypto";
|
|
1341
1351
|
import fs4 from "node:fs";
|
|
1342
|
-
import os from "node:os";
|
|
1343
1352
|
import path7 from "node:path";
|
|
1344
1353
|
import { pipeline } from "node:stream/promises";
|
|
1345
1354
|
|
|
@@ -1428,6 +1437,7 @@ import chalk4 from "chalk";
|
|
|
1428
1437
|
import Configstore from "configstore";
|
|
1429
1438
|
import Debug16 from "debug";
|
|
1430
1439
|
import extract from "extract-zip";
|
|
1440
|
+
import { createSpinner as createSpinner4 } from "nanospinner";
|
|
1431
1441
|
import fetch4 from "node-fetch";
|
|
1432
1442
|
import open2 from "open";
|
|
1433
1443
|
import tmp2 from "tmp";
|
|
@@ -1460,8 +1470,8 @@ import { z as z16 } from "zod";
|
|
|
1460
1470
|
|
|
1461
1471
|
// src/features/analysis/scm/bitbucket/bitbucket.ts
|
|
1462
1472
|
import querystring from "node:querystring";
|
|
1463
|
-
import bitbucketPkg from "bitbucket";
|
|
1464
1473
|
import * as bitbucketPkgNode from "bitbucket";
|
|
1474
|
+
import bitbucketPkg from "bitbucket";
|
|
1465
1475
|
import Debug2 from "debug";
|
|
1466
1476
|
import { z as z12 } from "zod";
|
|
1467
1477
|
|
|
@@ -1698,7 +1708,9 @@ var fixDetailsData = {
|
|
|
1698
1708
|
["CLIENT_DOM_STORED_CODE_INJECTION" /* ClientDomStoredCodeInjection */]: {
|
|
1699
1709
|
issueDescription: "Client DOM Stored Code Injection is a client-side security vulnerability where malicious JavaScript code gets stored in the DOM and later executed when retrieved by legitimate scripts.",
|
|
1700
1710
|
fixInstructions: "Update the code to avoid the possibility for malicious JavaScript code to get stored in the DOM."
|
|
1701
|
-
}
|
|
1711
|
+
},
|
|
1712
|
+
["STRING_FORMAT_MISUSE" /* StringFormatMisuse */]: void 0,
|
|
1713
|
+
["NON_READONLY_FIELD" /* NonReadonlyField */]: void 0
|
|
1702
1714
|
};
|
|
1703
1715
|
|
|
1704
1716
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -3643,7 +3655,10 @@ async function validateBitbucketParams(params) {
|
|
|
3643
3655
|
throw new InvalidRepoUrlError(safeParseError.data.error.error.message);
|
|
3644
3656
|
}
|
|
3645
3657
|
}
|
|
3646
|
-
|
|
3658
|
+
console.log("validateBitbucketParams error", e);
|
|
3659
|
+
throw new InvalidRepoUrlError(
|
|
3660
|
+
`cannot access BB repo URL: ${params.url} with the provided access token`
|
|
3661
|
+
);
|
|
3647
3662
|
}
|
|
3648
3663
|
}
|
|
3649
3664
|
async function getUsersworkspacesSlugs(bitbucketClient) {
|
|
@@ -3816,7 +3831,10 @@ async function githubValidateParams(url, accessToken) {
|
|
|
3816
3831
|
if (code === 404) {
|
|
3817
3832
|
throw new InvalidRepoUrlError(`invalid github repo Url ${url}`);
|
|
3818
3833
|
}
|
|
3819
|
-
|
|
3834
|
+
console.log("githubValidateParams error", e);
|
|
3835
|
+
throw new InvalidRepoUrlError(
|
|
3836
|
+
`cannot access GH repo URL: ${url} with the provided access token`
|
|
3837
|
+
);
|
|
3820
3838
|
}
|
|
3821
3839
|
}
|
|
3822
3840
|
|
|
@@ -4252,7 +4270,10 @@ async function gitlabValidateParams({
|
|
|
4252
4270
|
if (code === 404 || description.includes("404") || description.includes("Not Found")) {
|
|
4253
4271
|
throw new InvalidRepoUrlError(`invalid gitlab repo URL: ${url}`);
|
|
4254
4272
|
}
|
|
4255
|
-
|
|
4273
|
+
console.log("gitlabValidateParams error", e);
|
|
4274
|
+
throw new InvalidRepoUrlError(
|
|
4275
|
+
`cannot access gitlab repo URL: ${url} with the provided access token`
|
|
4276
|
+
);
|
|
4256
4277
|
}
|
|
4257
4278
|
}
|
|
4258
4279
|
async function getGitlabUsername(url, accessToken) {
|
|
@@ -5989,7 +6010,10 @@ async function adoValidateParams({
|
|
|
5989
6010
|
if (code === 404 || description.includes("404") || description.includes("Not Found")) {
|
|
5990
6011
|
throw new InvalidRepoUrlError(`invalid ADO repo URL ${url}`);
|
|
5991
6012
|
}
|
|
5992
|
-
|
|
6013
|
+
console.log("adoValidateParams error", e);
|
|
6014
|
+
throw new InvalidRepoUrlError(
|
|
6015
|
+
`cannot access ADO repo URL: ${url} with the provided access token`
|
|
6016
|
+
);
|
|
5993
6017
|
}
|
|
5994
6018
|
}
|
|
5995
6019
|
async function getOrgsForOauthToken({
|
|
@@ -6794,8 +6818,8 @@ async function addFixCommentsForPr({
|
|
|
6794
6818
|
import Debug8 from "debug";
|
|
6795
6819
|
var debug8 = Debug8("mobbdev:handleAutoPr");
|
|
6796
6820
|
async function handleAutoPr(params) {
|
|
6797
|
-
const { gqlClient, analysisId, createSpinner:
|
|
6798
|
-
const createAutoPrSpinner =
|
|
6821
|
+
const { gqlClient, analysisId, createSpinner: createSpinner5 } = params;
|
|
6822
|
+
const createAutoPrSpinner = createSpinner5(
|
|
6799
6823
|
"\u{1F504} Waiting for the analysis to finish before initiating automatic pull request creation"
|
|
6800
6824
|
).start();
|
|
6801
6825
|
return await gqlClient.subscribeToAnalysis({
|
|
@@ -7444,16 +7468,16 @@ function createSpwan({ args, processPath, name }, options) {
|
|
|
7444
7468
|
return createChildProcess({ childProcess: child, name }, options);
|
|
7445
7469
|
}
|
|
7446
7470
|
function createChildProcess({ childProcess, name }, options) {
|
|
7447
|
-
const
|
|
7471
|
+
const debug17 = Debug12(`mobbdev:${name}`);
|
|
7448
7472
|
const { display } = options;
|
|
7449
7473
|
return new Promise((resolve, reject) => {
|
|
7450
7474
|
let out = "";
|
|
7451
7475
|
const onData = (chunk) => {
|
|
7452
|
-
|
|
7476
|
+
debug17(`chunk received from ${name} std ${chunk}`);
|
|
7453
7477
|
out += chunk;
|
|
7454
7478
|
};
|
|
7455
7479
|
if (!childProcess || !childProcess?.stdout || !childProcess?.stderr) {
|
|
7456
|
-
|
|
7480
|
+
debug17(`unable to fork ${name}`);
|
|
7457
7481
|
reject(new Error(`unable to fork ${name}`));
|
|
7458
7482
|
}
|
|
7459
7483
|
childProcess.stdout?.on("data", onData);
|
|
@@ -7463,11 +7487,11 @@ function createChildProcess({ childProcess, name }, options) {
|
|
|
7463
7487
|
childProcess.stderr?.pipe(process2.stderr);
|
|
7464
7488
|
}
|
|
7465
7489
|
childProcess.on("exit", (code) => {
|
|
7466
|
-
|
|
7490
|
+
debug17(`${name} exit code ${code}`);
|
|
7467
7491
|
resolve({ message: out, code });
|
|
7468
7492
|
});
|
|
7469
7493
|
childProcess.on("error", (err) => {
|
|
7470
|
-
|
|
7494
|
+
debug17(`${name} error %o`, err);
|
|
7471
7495
|
reject(err);
|
|
7472
7496
|
});
|
|
7473
7497
|
});
|
|
@@ -7691,8 +7715,7 @@ async function uploadFile({
|
|
|
7691
7715
|
}
|
|
7692
7716
|
|
|
7693
7717
|
// src/features/analysis/index.ts
|
|
7694
|
-
var { CliError: CliError2, Spinner: Spinner2
|
|
7695
|
-
var webLoginUrl = `${WEB_APP_URL}/cli-login`;
|
|
7718
|
+
var { CliError: CliError2, Spinner: Spinner2 } = utils_exports;
|
|
7696
7719
|
function _getScanSource(command) {
|
|
7697
7720
|
if (command === "review")
|
|
7698
7721
|
return "AUTO_FIXER" /* AutoFixer */;
|
|
@@ -7705,8 +7728,8 @@ async function downloadRepo({
|
|
|
7705
7728
|
dirname,
|
|
7706
7729
|
ci
|
|
7707
7730
|
}) {
|
|
7708
|
-
const { createSpinner:
|
|
7709
|
-
const repoSpinner =
|
|
7731
|
+
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
7732
|
+
const repoSpinner = createSpinner5("\u{1F4BE} Downloading Repo").start();
|
|
7710
7733
|
debug15("download repo %s %s %s", repoUrl, dirname);
|
|
7711
7734
|
const zipFilePath = path7.join(dirname, "repo.zip");
|
|
7712
7735
|
debug15("download URL: %s auth headers: %o", downloadUrl, authHeaders);
|
|
@@ -7735,11 +7758,6 @@ async function downloadRepo({
|
|
|
7735
7758
|
repoSpinner.success({ text: "\u{1F4BE} Repo downloaded successfully" });
|
|
7736
7759
|
return path7.join(dirname, repoRoot);
|
|
7737
7760
|
}
|
|
7738
|
-
var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
|
|
7739
|
-
var LOGIN_CHECK_DELAY = 5 * 1e3;
|
|
7740
|
-
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(
|
|
7741
|
-
"press any key to continue"
|
|
7742
|
-
)};`;
|
|
7743
7761
|
var getReportUrl = ({
|
|
7744
7762
|
organizationId,
|
|
7745
7763
|
projectId,
|
|
@@ -7890,13 +7908,17 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
7890
7908
|
autoPr
|
|
7891
7909
|
} = params;
|
|
7892
7910
|
debug15("start %s %s", dirname, repo);
|
|
7893
|
-
const { createSpinner:
|
|
7911
|
+
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
7894
7912
|
skipPrompts = skipPrompts || ci;
|
|
7895
7913
|
let gqlClient = new GQLClient({
|
|
7896
7914
|
apiKey: apiKey || config2.get("apiToken"),
|
|
7897
7915
|
type: "apiKey"
|
|
7898
7916
|
});
|
|
7899
|
-
await handleMobbLogin(
|
|
7917
|
+
gqlClient = await handleMobbLogin({
|
|
7918
|
+
inGqlClient: gqlClient,
|
|
7919
|
+
skipPrompts,
|
|
7920
|
+
apiKey
|
|
7921
|
+
});
|
|
7900
7922
|
const { projectId, organizationId } = await gqlClient.getOrgAndProjectId({
|
|
7901
7923
|
projectName: mobbProjectName,
|
|
7902
7924
|
userDefinedOrganizationId: userOrganizationId
|
|
@@ -7923,7 +7945,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
7923
7945
|
});
|
|
7924
7946
|
if (!isRepoAvailable) {
|
|
7925
7947
|
if (ci || !cloudScmLibType || !scmAuthUrl) {
|
|
7926
|
-
const errorMessage = scmAuthUrl ? `Cannot access repo ${repo}
|
|
7948
|
+
const errorMessage = scmAuthUrl ? `Cannot access repo ${repo}. Make sure that the repo is accessible and the SCM token configured on Mobb is correct.` : `Cannot access repo ${repo} with the provided token, please visit ${scmAuthUrl} to refresh your source control management system token`;
|
|
7927
7949
|
throw new Error(errorMessage);
|
|
7928
7950
|
}
|
|
7929
7951
|
if (cloudScmLibType && scmAuthUrl) {
|
|
@@ -7976,7 +7998,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
7976
7998
|
if (!reportPath) {
|
|
7977
7999
|
throw new Error("reportPath is null");
|
|
7978
8000
|
}
|
|
7979
|
-
const uploadReportSpinner =
|
|
8001
|
+
const uploadReportSpinner = createSpinner5("\u{1F4C1} Uploading Report").start();
|
|
7980
8002
|
try {
|
|
7981
8003
|
await uploadFile({
|
|
7982
8004
|
file: reportPath,
|
|
@@ -7988,8 +8010,14 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
7988
8010
|
uploadReportSpinner.error({ text: "\u{1F4C1} Report upload failed" });
|
|
7989
8011
|
throw e;
|
|
7990
8012
|
}
|
|
8013
|
+
await _digestReport({
|
|
8014
|
+
gqlClient,
|
|
8015
|
+
fixReportId: reportUploadInfo.fixReportId,
|
|
8016
|
+
projectId,
|
|
8017
|
+
command
|
|
8018
|
+
});
|
|
7991
8019
|
uploadReportSpinner.success({ text: "\u{1F4C1} Report uploaded successfully" });
|
|
7992
|
-
const mobbSpinner =
|
|
8020
|
+
const mobbSpinner = createSpinner5("\u{1F575}\uFE0F\u200D\u2642\uFE0F Initiating Mobb analysis").start();
|
|
7993
8021
|
const sendReportRes = await sendReport({
|
|
7994
8022
|
gqlClient,
|
|
7995
8023
|
spinner: mobbSpinner,
|
|
@@ -8016,7 +8044,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
8016
8044
|
await handleAutoPr({
|
|
8017
8045
|
gqlClient,
|
|
8018
8046
|
analysisId: reportUploadInfo.fixReportId,
|
|
8019
|
-
createSpinner:
|
|
8047
|
+
createSpinner: createSpinner5
|
|
8020
8048
|
});
|
|
8021
8049
|
}
|
|
8022
8050
|
await askToOpenAnalysis();
|
|
@@ -8038,74 +8066,11 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
8038
8066
|
chalk4.bgBlue("\n\n My work here is done for now, see you soon! \u{1F575}\uFE0F\u200D\u2642\uFE0F ")
|
|
8039
8067
|
);
|
|
8040
8068
|
}
|
|
8041
|
-
async function handleMobbLogin() {
|
|
8042
|
-
if (await gqlClient.verifyToken()) {
|
|
8043
|
-
createSpinner4().start().success({
|
|
8044
|
-
text: "\u{1F513} Logged in to Mobb successfully"
|
|
8045
|
-
});
|
|
8046
|
-
return;
|
|
8047
|
-
} else if (apiKey) {
|
|
8048
|
-
createSpinner4().start().error({
|
|
8049
|
-
text: "\u{1F513} Logged in to Mobb failed - check your api-key"
|
|
8050
|
-
});
|
|
8051
|
-
throw new CliError2();
|
|
8052
|
-
}
|
|
8053
|
-
const loginSpinner = createSpinner4().start();
|
|
8054
|
-
if (!skipPrompts) {
|
|
8055
|
-
loginSpinner.update({ text: MOBB_LOGIN_REQUIRED_MSG });
|
|
8056
|
-
await keypress2();
|
|
8057
|
-
}
|
|
8058
|
-
loginSpinner.update({
|
|
8059
|
-
text: "\u{1F513} Waiting for Mobb login..."
|
|
8060
|
-
});
|
|
8061
|
-
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
|
|
8062
|
-
modulusLength: 2048
|
|
8063
|
-
});
|
|
8064
|
-
const loginId = await gqlClient.createCliLogin({
|
|
8065
|
-
publicKey: publicKey.export({ format: "pem", type: "pkcs1" }).toString()
|
|
8066
|
-
});
|
|
8067
|
-
const browserUrl = `${webLoginUrl}/${loginId}?hostname=${os.hostname()}`;
|
|
8068
|
-
!ci && console.log(
|
|
8069
|
-
`If the page does not open automatically, kindly access it through ${browserUrl}.`
|
|
8070
|
-
);
|
|
8071
|
-
await open2(browserUrl);
|
|
8072
|
-
let newApiToken = null;
|
|
8073
|
-
for (let i = 0; i < LOGIN_MAX_WAIT / LOGIN_CHECK_DELAY; i++) {
|
|
8074
|
-
const encryptedApiToken = await gqlClient.getEncryptedApiToken({
|
|
8075
|
-
loginId
|
|
8076
|
-
});
|
|
8077
|
-
loginSpinner.spin();
|
|
8078
|
-
if (encryptedApiToken) {
|
|
8079
|
-
debug15("encrypted API token received %s", encryptedApiToken);
|
|
8080
|
-
newApiToken = crypto.privateDecrypt(privateKey, Buffer.from(encryptedApiToken, "base64")).toString("utf-8");
|
|
8081
|
-
debug15("API token decrypted");
|
|
8082
|
-
break;
|
|
8083
|
-
}
|
|
8084
|
-
await sleep(LOGIN_CHECK_DELAY);
|
|
8085
|
-
}
|
|
8086
|
-
if (!newApiToken) {
|
|
8087
|
-
loginSpinner.error({
|
|
8088
|
-
text: "Login timeout error"
|
|
8089
|
-
});
|
|
8090
|
-
throw new CliError2();
|
|
8091
|
-
}
|
|
8092
|
-
gqlClient = new GQLClient({ apiKey: newApiToken, type: "apiKey" });
|
|
8093
|
-
if (await gqlClient.verifyToken()) {
|
|
8094
|
-
debug15("set api token %s", newApiToken);
|
|
8095
|
-
config2.set("apiToken", newApiToken);
|
|
8096
|
-
loginSpinner.success({ text: "\u{1F513} Login to Mobb successful!" });
|
|
8097
|
-
} else {
|
|
8098
|
-
loginSpinner.error({
|
|
8099
|
-
text: "Something went wrong, API token is invalid."
|
|
8100
|
-
});
|
|
8101
|
-
throw new CliError2();
|
|
8102
|
-
}
|
|
8103
|
-
}
|
|
8104
8069
|
async function handleScmIntegration(oldToken, scmAuthUrl2, repoUrl) {
|
|
8105
8070
|
const scmLibType = getCloudScmLibTypeFromUrl(repoUrl);
|
|
8106
8071
|
const scmName = scmLibType === "GITHUB" /* GITHUB */ ? "Github" : scmLibType === "GITLAB" /* GITLAB */ ? "Gitlab" : scmLibType === "ADO" /* ADO */ ? "Azure DevOps" : "";
|
|
8107
8072
|
const addScmIntegration = skipPrompts ? true : await scmIntegrationPrompt(scmName);
|
|
8108
|
-
const scmSpinner =
|
|
8073
|
+
const scmSpinner = createSpinner5(
|
|
8109
8074
|
`\u{1F517} Waiting for ${scmName} integration...`
|
|
8110
8075
|
).start();
|
|
8111
8076
|
if (!addScmIntegration) {
|
|
@@ -8149,7 +8114,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
8149
8114
|
if (!srcPath || !reportPath) {
|
|
8150
8115
|
throw new Error("src path and reportPath is required");
|
|
8151
8116
|
}
|
|
8152
|
-
const uploadReportSpinner2 =
|
|
8117
|
+
const uploadReportSpinner2 = createSpinner5("\u{1F4C1} Uploading Report").start();
|
|
8153
8118
|
try {
|
|
8154
8119
|
await uploadFile({
|
|
8155
8120
|
file: reportPath,
|
|
@@ -8164,48 +8129,17 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
8164
8129
|
uploadReportSpinner2.success({
|
|
8165
8130
|
text: "\u{1F4C1} Uploading Report successful!"
|
|
8166
8131
|
});
|
|
8167
|
-
const
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
try {
|
|
8173
|
-
const { vulnerabilityReportId } = await gqlClient.digestVulnerabilityReport({
|
|
8174
|
-
fixReportId: reportUploadInfo.fixReportId,
|
|
8175
|
-
projectId,
|
|
8176
|
-
scanSource: _getScanSource(command)
|
|
8177
|
-
});
|
|
8178
|
-
try {
|
|
8179
|
-
await gqlClient.subscribeToAnalysis({
|
|
8180
|
-
subscribeToAnalysisParams: {
|
|
8181
|
-
analysisId: reportUploadInfo.fixReportId
|
|
8182
|
-
},
|
|
8183
|
-
callback: () => digestSpinner.update({
|
|
8184
|
-
text: progressMassages.processingVulnerabilityReportSuccess
|
|
8185
|
-
}),
|
|
8186
|
-
callbackStates: [
|
|
8187
|
-
"Digested" /* Digested */,
|
|
8188
|
-
"Finished" /* Finished */
|
|
8189
|
-
],
|
|
8190
|
-
timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
|
|
8191
|
-
});
|
|
8192
|
-
} catch (e) {
|
|
8193
|
-
throw new Error(progressMassages.processingVulnerabilityReportFailed);
|
|
8194
|
-
}
|
|
8195
|
-
vulnFiles = await gqlClient.getVulnerabilityReportPaths(
|
|
8196
|
-
vulnerabilityReportId
|
|
8197
|
-
);
|
|
8198
|
-
} catch (e) {
|
|
8199
|
-
digestSpinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Digesting report failed" });
|
|
8200
|
-
throw e;
|
|
8201
|
-
}
|
|
8202
|
-
digestSpinner.success({
|
|
8203
|
-
text: progressMassages.processingVulnerabilityReportSuccess
|
|
8132
|
+
const vulnFiles = await _digestReport({
|
|
8133
|
+
gqlClient,
|
|
8134
|
+
fixReportId: reportUploadInfo.fixReportId,
|
|
8135
|
+
projectId,
|
|
8136
|
+
command
|
|
8204
8137
|
});
|
|
8205
|
-
const
|
|
8138
|
+
const gitInfo = await getGitInfo(srcPath);
|
|
8139
|
+
const zippingSpinner = createSpinner5("\u{1F4E6} Zipping repo").start();
|
|
8206
8140
|
const zipBuffer = await pack(srcPath, vulnFiles);
|
|
8207
8141
|
zippingSpinner.success({ text: "\u{1F4E6} Zipping repo successful!" });
|
|
8208
|
-
const uploadRepoSpinner =
|
|
8142
|
+
const uploadRepoSpinner = createSpinner5("\u{1F4C1} Uploading Repo").start();
|
|
8209
8143
|
try {
|
|
8210
8144
|
await uploadFile({
|
|
8211
8145
|
file: zipBuffer,
|
|
@@ -8218,7 +8152,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
8218
8152
|
throw e;
|
|
8219
8153
|
}
|
|
8220
8154
|
uploadRepoSpinner.success({ text: "\u{1F4C1} Uploading Repo successful!" });
|
|
8221
|
-
const mobbSpinner2 =
|
|
8155
|
+
const mobbSpinner2 = createSpinner5("\u{1F575}\uFE0F\u200D\u2642\uFE0F Initiating Mobb analysis").start();
|
|
8222
8156
|
try {
|
|
8223
8157
|
await sendReport({
|
|
8224
8158
|
gqlClient,
|
|
@@ -8275,17 +8209,69 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
8275
8209
|
await handleAutoPr({
|
|
8276
8210
|
gqlClient,
|
|
8277
8211
|
analysisId: reportUploadInfo.fixReportId,
|
|
8278
|
-
createSpinner:
|
|
8212
|
+
createSpinner: createSpinner5
|
|
8279
8213
|
});
|
|
8280
8214
|
}
|
|
8281
8215
|
await askToOpenAnalysis();
|
|
8282
8216
|
return reportUploadInfo.fixReportId;
|
|
8283
8217
|
}
|
|
8284
8218
|
}
|
|
8219
|
+
async function _digestReport({
|
|
8220
|
+
gqlClient,
|
|
8221
|
+
fixReportId,
|
|
8222
|
+
projectId,
|
|
8223
|
+
command
|
|
8224
|
+
}) {
|
|
8225
|
+
const digestSpinner = createSpinner4(
|
|
8226
|
+
progressMassages.processingVulnerabilityReport
|
|
8227
|
+
).start();
|
|
8228
|
+
try {
|
|
8229
|
+
const { vulnerabilityReportId } = await gqlClient.digestVulnerabilityReport(
|
|
8230
|
+
{
|
|
8231
|
+
fixReportId,
|
|
8232
|
+
projectId,
|
|
8233
|
+
scanSource: _getScanSource(command)
|
|
8234
|
+
}
|
|
8235
|
+
);
|
|
8236
|
+
try {
|
|
8237
|
+
await gqlClient.subscribeToAnalysis({
|
|
8238
|
+
subscribeToAnalysisParams: {
|
|
8239
|
+
analysisId: fixReportId
|
|
8240
|
+
},
|
|
8241
|
+
callback: () => digestSpinner.update({
|
|
8242
|
+
text: progressMassages.processingVulnerabilityReportSuccess
|
|
8243
|
+
}),
|
|
8244
|
+
callbackStates: [
|
|
8245
|
+
"Digested" /* Digested */,
|
|
8246
|
+
"Finished" /* Finished */
|
|
8247
|
+
],
|
|
8248
|
+
timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
|
|
8249
|
+
});
|
|
8250
|
+
} catch (e) {
|
|
8251
|
+
throw new Error(progressMassages.processingVulnerabilityReportFailed);
|
|
8252
|
+
}
|
|
8253
|
+
const vulnFiles = await gqlClient.getVulnerabilityReportPaths(
|
|
8254
|
+
vulnerabilityReportId
|
|
8255
|
+
);
|
|
8256
|
+
digestSpinner.success({
|
|
8257
|
+
text: progressMassages.processingVulnerabilityReportSuccess
|
|
8258
|
+
});
|
|
8259
|
+
return vulnFiles;
|
|
8260
|
+
} catch (e) {
|
|
8261
|
+
digestSpinner.error({
|
|
8262
|
+
text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Digesting report failed. Please verify that the file provided is of a valid supported report format."
|
|
8263
|
+
});
|
|
8264
|
+
throw e;
|
|
8265
|
+
}
|
|
8266
|
+
}
|
|
8285
8267
|
|
|
8286
8268
|
// src/commands/index.ts
|
|
8269
|
+
import chalk5 from "chalk";
|
|
8287
8270
|
import chalkAnimation from "chalk-animation";
|
|
8288
8271
|
import Configstore2 from "configstore";
|
|
8272
|
+
import Debug17 from "debug";
|
|
8273
|
+
import open3 from "open";
|
|
8274
|
+
var debug16 = Debug17("mobbdev:commands");
|
|
8289
8275
|
async function review(params, { skipPrompts = true } = {}) {
|
|
8290
8276
|
const {
|
|
8291
8277
|
repo,
|
|
@@ -8350,21 +8336,37 @@ async function analyze({
|
|
|
8350
8336
|
}
|
|
8351
8337
|
var config3 = new Configstore2(packageJson.name, { apiToken: "" });
|
|
8352
8338
|
async function addScmToken(addScmTokenOptions) {
|
|
8353
|
-
const { apiKey, token, organization, scmType, url, refreshToken } = addScmTokenOptions;
|
|
8354
|
-
|
|
8339
|
+
const { apiKey, token, organization, scmType, url, refreshToken, ci } = addScmTokenOptions;
|
|
8340
|
+
let gqlClient = new GQLClient({
|
|
8355
8341
|
apiKey: apiKey || config3.get("apiToken"),
|
|
8356
8342
|
type: "apiKey"
|
|
8357
8343
|
});
|
|
8344
|
+
gqlClient = await handleMobbLogin({
|
|
8345
|
+
inGqlClient: gqlClient,
|
|
8346
|
+
skipPrompts: ci,
|
|
8347
|
+
apiKey
|
|
8348
|
+
});
|
|
8358
8349
|
if (!scmType) {
|
|
8359
8350
|
throw new CliError(errorMessages.invalidScmType);
|
|
8360
8351
|
}
|
|
8361
|
-
await gqlClient.updateScmToken({
|
|
8352
|
+
const resp = await gqlClient.updateScmToken({
|
|
8362
8353
|
scmType,
|
|
8363
8354
|
url,
|
|
8364
8355
|
token,
|
|
8365
8356
|
org: organization,
|
|
8366
8357
|
refreshToken
|
|
8367
8358
|
});
|
|
8359
|
+
if (resp.updateScmToken?.__typename === "RepoUnreachableError") {
|
|
8360
|
+
throw new CliError(
|
|
8361
|
+
"Mobb could not reach the repository. Please try again. If Mobb is connected through a broker, please make sure the broker is connected."
|
|
8362
|
+
);
|
|
8363
|
+
} else if (resp.updateScmToken?.__typename === "BadScmCredentials") {
|
|
8364
|
+
throw new CliError("Invalid SCM credentials. Please try again.");
|
|
8365
|
+
} else if (resp.updateScmToken?.__typename === "ScmAccessTokenUpdateSuccess") {
|
|
8366
|
+
console.log("Token added successfully");
|
|
8367
|
+
} else {
|
|
8368
|
+
throw new CliError("Unexpected error, failed to add token");
|
|
8369
|
+
}
|
|
8368
8370
|
}
|
|
8369
8371
|
async function scan(scanOptions, { skipPrompts = false } = {}) {
|
|
8370
8372
|
const { scanner, ci } = scanOptions;
|
|
@@ -8390,34 +8392,109 @@ async function showWelcomeMessage(skipPrompts = false) {
|
|
|
8390
8392
|
skipPrompts ? await sleep(100) : await sleep(2e3);
|
|
8391
8393
|
welcome.stop();
|
|
8392
8394
|
}
|
|
8395
|
+
var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
|
|
8396
|
+
var LOGIN_CHECK_DELAY = 5 * 1e3;
|
|
8397
|
+
var webLoginUrl = `${WEB_APP_URL}/cli-login`;
|
|
8398
|
+
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, ${chalk5.bgBlue(
|
|
8399
|
+
"press any key to continue"
|
|
8400
|
+
)};`;
|
|
8401
|
+
async function handleMobbLogin({
|
|
8402
|
+
inGqlClient,
|
|
8403
|
+
apiKey,
|
|
8404
|
+
skipPrompts
|
|
8405
|
+
}) {
|
|
8406
|
+
const { createSpinner: createSpinner5 } = Spinner({ ci: skipPrompts });
|
|
8407
|
+
if (await inGqlClient.verifyToken()) {
|
|
8408
|
+
createSpinner5().start().success({
|
|
8409
|
+
text: "\u{1F513} Logged in to Mobb successfully"
|
|
8410
|
+
});
|
|
8411
|
+
return inGqlClient;
|
|
8412
|
+
} else if (apiKey) {
|
|
8413
|
+
createSpinner5().start().error({
|
|
8414
|
+
text: "\u{1F513} Logged in to Mobb failed - check your api-key"
|
|
8415
|
+
});
|
|
8416
|
+
throw new CliError();
|
|
8417
|
+
}
|
|
8418
|
+
const loginSpinner = createSpinner5().start();
|
|
8419
|
+
if (!skipPrompts) {
|
|
8420
|
+
loginSpinner.update({ text: MOBB_LOGIN_REQUIRED_MSG });
|
|
8421
|
+
await keypress();
|
|
8422
|
+
}
|
|
8423
|
+
loginSpinner.update({
|
|
8424
|
+
text: "\u{1F513} Waiting for Mobb login..."
|
|
8425
|
+
});
|
|
8426
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
|
|
8427
|
+
modulusLength: 2048
|
|
8428
|
+
});
|
|
8429
|
+
const loginId = await inGqlClient.createCliLogin({
|
|
8430
|
+
publicKey: publicKey.export({ format: "pem", type: "pkcs1" }).toString()
|
|
8431
|
+
});
|
|
8432
|
+
const browserUrl = `${webLoginUrl}/${loginId}?hostname=${os.hostname()}`;
|
|
8433
|
+
!skipPrompts && console.log(
|
|
8434
|
+
`If the page does not open automatically, kindly access it through ${browserUrl}.`
|
|
8435
|
+
);
|
|
8436
|
+
await open3(browserUrl);
|
|
8437
|
+
let newApiToken = null;
|
|
8438
|
+
for (let i = 0; i < LOGIN_MAX_WAIT / LOGIN_CHECK_DELAY; i++) {
|
|
8439
|
+
const encryptedApiToken = await inGqlClient.getEncryptedApiToken({
|
|
8440
|
+
loginId
|
|
8441
|
+
});
|
|
8442
|
+
loginSpinner.spin();
|
|
8443
|
+
if (encryptedApiToken) {
|
|
8444
|
+
debug16("encrypted API token received %s", encryptedApiToken);
|
|
8445
|
+
newApiToken = crypto.privateDecrypt(privateKey, Buffer.from(encryptedApiToken, "base64")).toString("utf-8");
|
|
8446
|
+
debug16("API token decrypted");
|
|
8447
|
+
break;
|
|
8448
|
+
}
|
|
8449
|
+
await sleep(LOGIN_CHECK_DELAY);
|
|
8450
|
+
}
|
|
8451
|
+
if (!newApiToken) {
|
|
8452
|
+
loginSpinner.error({
|
|
8453
|
+
text: "Login timeout error"
|
|
8454
|
+
});
|
|
8455
|
+
throw new CliError();
|
|
8456
|
+
}
|
|
8457
|
+
const newGqlClient = new GQLClient({ apiKey: newApiToken, type: "apiKey" });
|
|
8458
|
+
if (await newGqlClient.verifyToken()) {
|
|
8459
|
+
debug16("set api token %s", newApiToken);
|
|
8460
|
+
config3.set("apiToken", newApiToken);
|
|
8461
|
+
loginSpinner.success({ text: "\u{1F513} Login to Mobb successful!" });
|
|
8462
|
+
} else {
|
|
8463
|
+
loginSpinner.error({
|
|
8464
|
+
text: "Something went wrong, API token is invalid."
|
|
8465
|
+
});
|
|
8466
|
+
throw new CliError();
|
|
8467
|
+
}
|
|
8468
|
+
return newGqlClient;
|
|
8469
|
+
}
|
|
8393
8470
|
|
|
8394
8471
|
// src/args/commands/analyze.ts
|
|
8395
|
-
import
|
|
8472
|
+
import chalk8 from "chalk";
|
|
8396
8473
|
|
|
8397
8474
|
// src/args/options.ts
|
|
8398
|
-
import
|
|
8475
|
+
import chalk6 from "chalk";
|
|
8399
8476
|
var repoOption = {
|
|
8400
8477
|
alias: "r",
|
|
8401
8478
|
demandOption: true,
|
|
8402
8479
|
type: "string",
|
|
8403
|
-
describe:
|
|
8480
|
+
describe: chalk6.bold("Github / GitLab / Azure DevOps repository URL")
|
|
8404
8481
|
};
|
|
8405
8482
|
var projectNameOption = {
|
|
8406
8483
|
type: "string",
|
|
8407
|
-
describe:
|
|
8484
|
+
describe: chalk6.bold("Checkmarx project name (when scanning with Checkmarx)")
|
|
8408
8485
|
};
|
|
8409
8486
|
var yesOption = {
|
|
8410
8487
|
alias: "yes",
|
|
8411
8488
|
type: "boolean",
|
|
8412
|
-
describe:
|
|
8489
|
+
describe: chalk6.bold("Skip prompts and use default values")
|
|
8413
8490
|
};
|
|
8414
8491
|
var refOption = {
|
|
8415
|
-
describe:
|
|
8492
|
+
describe: chalk6.bold("reference of the repository (branch, tag, commit)"),
|
|
8416
8493
|
type: "string",
|
|
8417
8494
|
demandOption: false
|
|
8418
8495
|
};
|
|
8419
8496
|
var organizationIdOptions = {
|
|
8420
|
-
describe:
|
|
8497
|
+
describe: chalk6.bold("Organization id"),
|
|
8421
8498
|
alias: "organization-id",
|
|
8422
8499
|
type: "string",
|
|
8423
8500
|
demandOption: false
|
|
@@ -8425,15 +8502,15 @@ var organizationIdOptions = {
|
|
|
8425
8502
|
var scannerOptions = {
|
|
8426
8503
|
alias: "s",
|
|
8427
8504
|
choices: Object.values(SCANNERS),
|
|
8428
|
-
describe:
|
|
8505
|
+
describe: chalk6.bold("Select the scanner to use")
|
|
8429
8506
|
};
|
|
8430
8507
|
var mobbProjectNameOption = {
|
|
8431
8508
|
type: "string",
|
|
8432
|
-
describe:
|
|
8509
|
+
describe: chalk6.bold("Mobb project name"),
|
|
8433
8510
|
default: PROJECT_DEFAULT_NAME
|
|
8434
8511
|
};
|
|
8435
8512
|
var ciOption = {
|
|
8436
|
-
describe:
|
|
8513
|
+
describe: chalk6.bold(
|
|
8437
8514
|
"Run in CI mode, prompts and browser will not be opened"
|
|
8438
8515
|
),
|
|
8439
8516
|
type: "boolean",
|
|
@@ -8441,46 +8518,46 @@ var ciOption = {
|
|
|
8441
8518
|
};
|
|
8442
8519
|
var apiKeyOption = {
|
|
8443
8520
|
type: "string",
|
|
8444
|
-
describe:
|
|
8521
|
+
describe: chalk6.bold("Mobb authentication api-key")
|
|
8445
8522
|
};
|
|
8446
8523
|
var commitHashOption = {
|
|
8447
8524
|
alias: "ch",
|
|
8448
|
-
describe:
|
|
8525
|
+
describe: chalk6.bold("Hash of the commit"),
|
|
8449
8526
|
type: "string"
|
|
8450
8527
|
};
|
|
8451
8528
|
var autoPrOption = {
|
|
8452
|
-
describe:
|
|
8529
|
+
describe: chalk6.bold("Enable automatic pull requests for new fixes"),
|
|
8453
8530
|
type: "boolean",
|
|
8454
8531
|
default: false
|
|
8455
8532
|
};
|
|
8456
8533
|
var scmTypeOption = {
|
|
8457
8534
|
demandOption: true,
|
|
8458
|
-
describe:
|
|
8535
|
+
describe: chalk6.bold("SCM type"),
|
|
8459
8536
|
choices: Object.values(ScmType)
|
|
8460
8537
|
};
|
|
8461
8538
|
var urlOption = {
|
|
8462
|
-
describe:
|
|
8539
|
+
describe: chalk6.bold(
|
|
8463
8540
|
`URL of the repository (used in ${Object.values(ScmType).join(", ")})`
|
|
8464
8541
|
),
|
|
8465
8542
|
type: "string",
|
|
8466
8543
|
demandOption: true
|
|
8467
8544
|
};
|
|
8468
8545
|
var scmOrgOption = {
|
|
8469
|
-
describe:
|
|
8546
|
+
describe: chalk6.bold("Organization name in SCM (used in Azure DevOps)"),
|
|
8470
8547
|
type: "string"
|
|
8471
8548
|
};
|
|
8472
8549
|
var scmRefreshTokenOption = {
|
|
8473
|
-
describe:
|
|
8550
|
+
describe: chalk6.bold("SCM refresh token (used in GitLab)"),
|
|
8474
8551
|
type: "string"
|
|
8475
8552
|
};
|
|
8476
8553
|
var scmTokenOption = {
|
|
8477
|
-
describe:
|
|
8554
|
+
describe: chalk6.bold("SCM API token"),
|
|
8478
8555
|
type: "string",
|
|
8479
8556
|
demandOption: true
|
|
8480
8557
|
};
|
|
8481
8558
|
|
|
8482
8559
|
// src/args/validation.ts
|
|
8483
|
-
import
|
|
8560
|
+
import chalk7 from "chalk";
|
|
8484
8561
|
import path8 from "path";
|
|
8485
8562
|
import { z as z23 } from "zod";
|
|
8486
8563
|
function throwRepoUrlErrorMessage({
|
|
@@ -8490,11 +8567,11 @@ function throwRepoUrlErrorMessage({
|
|
|
8490
8567
|
}) {
|
|
8491
8568
|
const errorMessage = error.issues[error.issues.length - 1]?.message;
|
|
8492
8569
|
const formattedErrorMessage = `
|
|
8493
|
-
Error: ${
|
|
8570
|
+
Error: ${chalk7.bold(
|
|
8494
8571
|
repoUrl
|
|
8495
8572
|
)} is ${errorMessage}
|
|
8496
8573
|
Example:
|
|
8497
|
-
mobbdev ${command} -r ${
|
|
8574
|
+
mobbdev ${command} -r ${chalk7.bold(
|
|
8498
8575
|
"https://github.com/WebGoat/WebGoat"
|
|
8499
8576
|
)}`;
|
|
8500
8577
|
throw new CliError(formattedErrorMessage);
|
|
@@ -8528,9 +8605,9 @@ function validateReportFileFormat(reportFile) {
|
|
|
8528
8605
|
if (!supportExtensions.includes(path8.extname(reportFile))) {
|
|
8529
8606
|
throw new CliError(
|
|
8530
8607
|
`
|
|
8531
|
-
${
|
|
8608
|
+
${chalk7.bold(
|
|
8532
8609
|
reportFile
|
|
8533
|
-
)} is not a supported file extension. Supported extensions are: ${
|
|
8610
|
+
)} is not a supported file extension. Supported extensions are: ${chalk7.bold(
|
|
8534
8611
|
supportExtensions.join(", ")
|
|
8535
8612
|
)}
|
|
8536
8613
|
`
|
|
@@ -8544,18 +8621,18 @@ function analyzeBuilder(yargs2) {
|
|
|
8544
8621
|
alias: "scan-file",
|
|
8545
8622
|
demandOption: true,
|
|
8546
8623
|
type: "string",
|
|
8547
|
-
describe:
|
|
8624
|
+
describe: chalk8.bold(
|
|
8548
8625
|
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube)"
|
|
8549
8626
|
)
|
|
8550
8627
|
}).option("repo", repoOption).option("p", {
|
|
8551
8628
|
alias: "src-path",
|
|
8552
|
-
describe:
|
|
8629
|
+
describe: chalk8.bold(
|
|
8553
8630
|
"Path to the repository folder with the source code"
|
|
8554
8631
|
),
|
|
8555
8632
|
type: "string"
|
|
8556
8633
|
}).option("ref", refOption).option("ch", {
|
|
8557
8634
|
alias: "commit-hash",
|
|
8558
|
-
describe:
|
|
8635
|
+
describe: chalk8.bold("Hash of the commit"),
|
|
8559
8636
|
type: "string"
|
|
8560
8637
|
}).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).example(
|
|
8561
8638
|
"$0 analyze -r https://github.com/WebGoat/WebGoat -f <your_vulirabitliy_report_path>",
|
|
@@ -8565,7 +8642,7 @@ function analyzeBuilder(yargs2) {
|
|
|
8565
8642
|
function validateAnalyzeOptions(argv) {
|
|
8566
8643
|
if (!fs5.existsSync(argv.f)) {
|
|
8567
8644
|
throw new CliError(`
|
|
8568
|
-
Can't access ${
|
|
8645
|
+
Can't access ${chalk8.bold(argv.f)}`);
|
|
8569
8646
|
}
|
|
8570
8647
|
validateOrganizationId(argv.organizationId);
|
|
8571
8648
|
if (!argv.srcPath && !argv.repo) {
|
|
@@ -8586,32 +8663,32 @@ async function analyzeHandler(args) {
|
|
|
8586
8663
|
|
|
8587
8664
|
// src/args/commands/review.ts
|
|
8588
8665
|
import fs6 from "node:fs";
|
|
8589
|
-
import
|
|
8666
|
+
import chalk9 from "chalk";
|
|
8590
8667
|
function reviewBuilder(yargs2) {
|
|
8591
8668
|
return yargs2.option("f", {
|
|
8592
8669
|
alias: "scan-file",
|
|
8593
8670
|
demandOption: true,
|
|
8594
8671
|
type: "string",
|
|
8595
|
-
describe:
|
|
8672
|
+
describe: chalk9.bold(
|
|
8596
8673
|
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube)"
|
|
8597
8674
|
)
|
|
8598
8675
|
}).option("repo", { ...repoOption, demandOption: true }).option("scanner", { ...scannerOptions, demandOption: true }).option("ref", { ...refOption, demandOption: true }).option("ch", {
|
|
8599
8676
|
alias: "commit-hash",
|
|
8600
|
-
describe:
|
|
8677
|
+
describe: chalk9.bold("Hash of the commit"),
|
|
8601
8678
|
type: "string",
|
|
8602
8679
|
demandOption: true
|
|
8603
8680
|
}).option("mobb-project-name", mobbProjectNameOption).option("api-key", { ...apiKeyOption, demandOption: true }).option("commit-hash", { ...commitHashOption, demandOption: true }).option("github-token", {
|
|
8604
|
-
describe:
|
|
8681
|
+
describe: chalk9.bold("Github action token"),
|
|
8605
8682
|
type: "string",
|
|
8606
8683
|
demandOption: true
|
|
8607
8684
|
}).option("pull-request", {
|
|
8608
8685
|
alias: "pr",
|
|
8609
|
-
describe:
|
|
8686
|
+
describe: chalk9.bold("Number of the pull request"),
|
|
8610
8687
|
type: "number",
|
|
8611
8688
|
demandOption: true
|
|
8612
8689
|
}).option("p", {
|
|
8613
8690
|
alias: "src-path",
|
|
8614
|
-
describe:
|
|
8691
|
+
describe: chalk9.bold(
|
|
8615
8692
|
"Path to the repository folder with the source code"
|
|
8616
8693
|
),
|
|
8617
8694
|
type: "string",
|
|
@@ -8624,7 +8701,7 @@ function reviewBuilder(yargs2) {
|
|
|
8624
8701
|
function validateReviewOptions(argv) {
|
|
8625
8702
|
if (!fs6.existsSync(argv.f)) {
|
|
8626
8703
|
throw new CliError(`
|
|
8627
|
-
Can't access ${
|
|
8704
|
+
Can't access ${chalk9.bold(argv.f)}`);
|
|
8628
8705
|
}
|
|
8629
8706
|
validateRepoUrl(argv);
|
|
8630
8707
|
validateReportFileFormat(argv.f);
|
|
@@ -8661,7 +8738,7 @@ async function scanHandler(args) {
|
|
|
8661
8738
|
|
|
8662
8739
|
// src/args/commands/token.ts
|
|
8663
8740
|
function addScmTokenBuilder(args) {
|
|
8664
|
-
return args.option("scm-type", scmTypeOption).option("url", urlOption).option("token", scmTokenOption).option("organization", scmOrgOption).option("refresh-token", scmRefreshTokenOption).option("api-key", apiKeyOption).example(
|
|
8741
|
+
return args.option("scm-type", scmTypeOption).option("url", urlOption).option("token", scmTokenOption).option("organization", scmOrgOption).option("refresh-token", scmRefreshTokenOption).option("api-key", apiKeyOption).option("ci", ciOption).example(
|
|
8665
8742
|
"$0 add-scm-token --scm-type Ado --url https://dev.azure.com/adoorg/test/_git/repo --token abcdef0123456 --organization myOrg",
|
|
8666
8743
|
`Add your SCM (${Object.values(scmFriendlyText).join(", ")}) token to Mobb to enable automated fixes.`
|
|
8667
8744
|
).help().demandOption(["url", "token"]);
|
|
@@ -8698,42 +8775,42 @@ async function addScmTokenHandler(args) {
|
|
|
8698
8775
|
var parseArgs = async (args) => {
|
|
8699
8776
|
const yargsInstance = yargs(args);
|
|
8700
8777
|
return yargsInstance.updateStrings({
|
|
8701
|
-
"Commands:":
|
|
8702
|
-
"Options:":
|
|
8703
|
-
"Examples:":
|
|
8704
|
-
"Show help":
|
|
8778
|
+
"Commands:": chalk10.yellow.underline.bold("Commands:"),
|
|
8779
|
+
"Options:": chalk10.yellow.underline.bold("Options:"),
|
|
8780
|
+
"Examples:": chalk10.yellow.underline.bold("Examples:"),
|
|
8781
|
+
"Show help": chalk10.bold("Show help")
|
|
8705
8782
|
}).usage(
|
|
8706
|
-
`${
|
|
8783
|
+
`${chalk10.bold(
|
|
8707
8784
|
"\n Bugsy - Trusted, Automatic Vulnerability Fixer \u{1F575}\uFE0F\u200D\u2642\uFE0F\n\n"
|
|
8708
|
-
)} ${
|
|
8709
|
-
$0 ${
|
|
8785
|
+
)} ${chalk10.yellow.underline.bold("Usage:")}
|
|
8786
|
+
$0 ${chalk10.green(
|
|
8710
8787
|
"<command>"
|
|
8711
|
-
)} ${
|
|
8788
|
+
)} ${chalk10.dim("[options]")}
|
|
8712
8789
|
`
|
|
8713
8790
|
).version(false).command(
|
|
8714
8791
|
mobbCliCommand.scan,
|
|
8715
|
-
|
|
8792
|
+
chalk10.bold(
|
|
8716
8793
|
"Scan your code for vulnerabilities, get automated fixes right away."
|
|
8717
8794
|
),
|
|
8718
8795
|
scanBuilder,
|
|
8719
8796
|
scanHandler
|
|
8720
8797
|
).command(
|
|
8721
8798
|
mobbCliCommand.analyze,
|
|
8722
|
-
|
|
8799
|
+
chalk10.bold(
|
|
8723
8800
|
"Provide a vulnerability report and relevant code repository, get automated fixes right away."
|
|
8724
8801
|
),
|
|
8725
8802
|
analyzeBuilder,
|
|
8726
8803
|
analyzeHandler
|
|
8727
8804
|
).command(
|
|
8728
8805
|
mobbCliCommand.review,
|
|
8729
|
-
|
|
8806
|
+
chalk10.bold(
|
|
8730
8807
|
"Mobb will review your github pull requests and provide comments with fixes "
|
|
8731
8808
|
),
|
|
8732
8809
|
reviewBuilder,
|
|
8733
8810
|
reviewHandler
|
|
8734
8811
|
).command(
|
|
8735
8812
|
mobbCliCommand.addScmToken,
|
|
8736
|
-
|
|
8813
|
+
chalk10.bold(
|
|
8737
8814
|
"Add your SCM (Github, Gitlab, Azure DevOps) token to Mobb to enable automated fixes."
|
|
8738
8815
|
),
|
|
8739
8816
|
addScmTokenBuilder,
|
|
@@ -8746,7 +8823,7 @@ var parseArgs = async (args) => {
|
|
|
8746
8823
|
handler() {
|
|
8747
8824
|
yargsInstance.showHelp();
|
|
8748
8825
|
}
|
|
8749
|
-
}).strictOptions().help("h").alias("h", "help").epilog(
|
|
8826
|
+
}).strictOptions().help("h").alias("h", "help").epilog(chalk10.bgBlue("Made with \u2764\uFE0F by Mobb")).showHelpOnFail(true).wrap(Math.min(120, yargsInstance.terminalWidth())).parse();
|
|
8750
8827
|
};
|
|
8751
8828
|
|
|
8752
8829
|
// src/index.ts
|