mobbdev 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +116 -33
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -69,6 +69,7 @@ var WEB_APP_URL = envVariables.WEB_APP_URL;
69
69
  var API_URL = envVariables.API_URL;
70
70
 
71
71
  // src/features/analysis/index.ts
72
+ import crypto from "node:crypto";
72
73
  import fs3 from "node:fs";
73
74
  import path5 from "node:path";
74
75
 
@@ -408,6 +409,20 @@ var CREATE_COMMUNITY_USER = gql`
408
409
  }
409
410
  }
410
411
  `;
412
+ var CREATE_CLI_LOGIN = gql`
413
+ mutation CreateCliLogin($publicKey: String!) {
414
+ insert_cli_login_one(object: { publicKey: $publicKey }) {
415
+ id
416
+ }
417
+ }
418
+ `;
419
+ var PERFORM_CLI_LOGIN = gql`
420
+ mutation performCliLogin($loginId: String!) {
421
+ performCliLogin(loginId: $loginId) {
422
+ status
423
+ }
424
+ }
425
+ `;
411
426
 
412
427
  // src/features/analysis/graphql/queries.ts
413
428
  import { gql as gql2 } from "graphql-request";
@@ -434,6 +449,13 @@ var GET_ORG_AND_PROJECT_ID = gql2`
434
449
  }
435
450
  }
436
451
  `;
452
+ var GET_ENCRYPTED_API_TOKEN = gql2`
453
+ query GetEncryptedApiToken($loginId: uuid!) {
454
+ cli_login_by_pk(id: $loginId) {
455
+ encryptedApiToken
456
+ }
457
+ }
458
+ `;
437
459
 
438
460
  // src/features/analysis/graphql/types.ts
439
461
  import { z as z3 } from "zod";
@@ -487,22 +509,40 @@ var GetOrgAndProjectIdQueryZ = z3.object({
487
509
  })
488
510
  ).nonempty()
489
511
  });
512
+ var CreateCliLoginZ = z3.object({
513
+ insert_cli_login_one: z3.object({
514
+ id: z3.string()
515
+ })
516
+ });
517
+ var GetEncryptedApiTokenZ = z3.object({
518
+ cli_login_by_pk: z3.object({
519
+ encryptedApiToken: z3.string().nullable()
520
+ })
521
+ });
490
522
 
491
523
  // src/features/analysis/graphql/gql.ts
492
524
  var debug5 = Debug5("mobbdev:gql");
493
525
  var GQLClient = class {
494
526
  constructor(args) {
495
- const { token, apiKey } = args;
496
- debug5(`init with apiKey ${apiKey} token ${token}`);
497
- const headers = apiKey ? { "x-mobb-key": apiKey } : {
498
- authorization: `Bearer ${token}`
499
- };
500
- this._client = new GraphQLClient(API_URL, { headers });
527
+ const { apiKey } = args;
528
+ debug5(`init with apiKey ${apiKey}`);
529
+ this._client = new GraphQLClient(API_URL, {
530
+ headers: { "x-mobb-key": apiKey || "" }
531
+ });
501
532
  }
502
533
  async getUserInfo() {
503
534
  const { me } = await this._client.request(ME);
504
535
  return me;
505
536
  }
537
+ async createCliLogin(variables) {
538
+ const res = CreateCliLoginZ.parse(
539
+ await this._client.request(
540
+ CREATE_CLI_LOGIN,
541
+ variables
542
+ )
543
+ );
544
+ return res.insert_cli_login_one.id;
545
+ }
506
546
  async verifyToken() {
507
547
  await this.createCommunityUser();
508
548
  try {
@@ -526,6 +566,13 @@ var GQLClient = class {
526
566
  projectId: org.projects[0].id
527
567
  };
528
568
  }
569
+ async getEncryptedApiToken(variables) {
570
+ const res = await this._client.request(
571
+ GET_ENCRYPTED_API_TOKEN,
572
+ variables
573
+ );
574
+ return GetEncryptedApiTokenZ.parse(res).cli_login_by_pk.encryptedApiToken;
575
+ }
529
576
  async createCommunityUser() {
530
577
  try {
531
578
  await this._client.request(CREATE_COMMUNITY_USER);
@@ -761,6 +808,8 @@ var { CliError: CliError2, Spinner: Spinner2, keypress: keypress2, getDirName: g
761
808
  var webLoginUrl = `${WEB_APP_URL}/cli-login`;
762
809
  var githubSubmitUrl = `${WEB_APP_URL}/gh-callback`;
763
810
  var githubAuthUrl = `${WEB_APP_URL}/github-auth`;
811
+ var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
812
+ var LOGIN_CHECK_DELAY = 5 * 1e3;
764
813
  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(
765
814
  "press any key to continue"
766
815
  )};`;
@@ -781,7 +830,7 @@ if (!semver.satisfies(process.version, packageJson.engines.node)) {
781
830
  `${packageJson.name} requires node version ${packageJson.engines.node}, but running ${process.version}.`
782
831
  );
783
832
  }
784
- var config2 = new Configstore(packageJson.name, { token: "" });
833
+ var config2 = new Configstore(packageJson.name, { apiToken: "" });
785
834
  debug9("config %o", config2);
786
835
  async function runAnalysis(params, options) {
787
836
  try {
@@ -809,10 +858,9 @@ async function _scan({
809
858
  debug9("start %s %s", dirname, repo);
810
859
  const { createSpinner: createSpinner3 } = Spinner2({ ci });
811
860
  skipPrompts = skipPrompts || ci;
812
- let token = config2.get("token");
813
- debug9("token %s", token);
814
- apiKey ?? debug9("token %s", apiKey);
815
- let gqlClient = new GQLClient(apiKey ? { apiKey } : { token });
861
+ let gqlClient = new GQLClient({
862
+ apiKey: apiKey || config2.get("apiToken")
863
+ });
816
864
  await handleMobbLogin();
817
865
  const { projectId, organizationId } = await gqlClient.getOrgAndProjectId();
818
866
  const {
@@ -836,8 +884,8 @@ async function _scan({
836
884
  `Cannot access repo ${repo} with the provided token, please visit ${githubAuthUrl} to refresh your Github token`
837
885
  );
838
886
  }
839
- const { token: token2 } = await handleGithubIntegration();
840
- githubToken = token2;
887
+ const { token } = await handleGithubIntegration();
888
+ githubToken = token;
841
889
  }
842
890
  const reference = ref ?? (await getRepo(repo, { token: githubToken })).data.default_branch;
843
891
  debug9("org id %s", organizationId);
@@ -907,41 +955,67 @@ async function _scan({
907
955
  );
908
956
  }
909
957
  async function handleMobbLogin() {
910
- if (token && await gqlClient.verifyToken() || apiKey && await gqlClient.verifyToken()) {
958
+ if (await gqlClient.verifyToken()) {
911
959
  createSpinner3().start().success({
912
960
  text: "\u{1F513} Logged in to Mobb successfully"
913
961
  });
914
962
  return;
915
- }
916
- if (apiKey && !await gqlClient.verifyToken()) {
963
+ } else if (apiKey) {
917
964
  createSpinner3().start().error({
918
965
  text: "\u{1F513} Logged in to Mobb failed - check your api-key"
919
966
  });
920
967
  throw new CliError2();
921
968
  }
922
- const mobbLoginSpinner = createSpinner3().start();
969
+ const loginSpinner = createSpinner3().start();
923
970
  if (!skipPrompts) {
924
- mobbLoginSpinner.update({ text: MOBB_LOGIN_REQUIRED_MSG });
971
+ loginSpinner.update({ text: MOBB_LOGIN_REQUIRED_MSG });
925
972
  await keypress2();
926
973
  }
927
- mobbLoginSpinner.update({
974
+ loginSpinner.update({
928
975
  text: "\u{1F513} Waiting for Mobb login..."
929
976
  });
930
- const loginResponse = await callbackServer({
931
- url: webLoginUrl,
932
- redirectUrl: `${webLoginUrl}?done=true`
977
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
978
+ modulusLength: 2048
979
+ });
980
+ const loginId = await gqlClient.createCliLogin({
981
+ publicKey: publicKey.export({ format: "pem", type: "pkcs1" }).toString()
933
982
  });
934
- token = loginResponse.token;
935
- gqlClient = new GQLClient({ token });
936
- if (!await gqlClient.verifyToken()) {
937
- mobbLoginSpinner.error({
983
+ const browserUrl = `${webLoginUrl}/${loginId}`;
984
+ !ci && console.log(
985
+ `If the page does not open automatically, kindly access it through ${browserUrl}.`
986
+ );
987
+ await open3(browserUrl);
988
+ let newApiToken = null;
989
+ for (let i = 0; i < LOGIN_MAX_WAIT / LOGIN_CHECK_DELAY; i++) {
990
+ const encryptedApiToken = await gqlClient.getEncryptedApiToken({
991
+ loginId
992
+ });
993
+ loginSpinner.spin();
994
+ if (encryptedApiToken) {
995
+ debug9("encrypted API token received %s", encryptedApiToken);
996
+ newApiToken = crypto.privateDecrypt(privateKey, Buffer.from(encryptedApiToken, "base64")).toString("utf-8");
997
+ debug9("API token decrypted");
998
+ break;
999
+ }
1000
+ await sleep(LOGIN_CHECK_DELAY);
1001
+ }
1002
+ if (!newApiToken) {
1003
+ loginSpinner.error({
1004
+ text: "Login timeout error"
1005
+ });
1006
+ throw new CliError2();
1007
+ }
1008
+ gqlClient = new GQLClient({ apiKey: newApiToken });
1009
+ if (await gqlClient.verifyToken()) {
1010
+ debug9("set api token %s", newApiToken);
1011
+ config2.set("apiToken", newApiToken);
1012
+ loginSpinner.success({ text: "\u{1F513} Login to Mobb successful!" });
1013
+ } else {
1014
+ loginSpinner.error({
938
1015
  text: "Something went wrong, API token is invalid."
939
1016
  });
940
1017
  throw new CliError2();
941
1018
  }
942
- debug9("set token %s", token);
943
- config2.set("token", token);
944
- mobbLoginSpinner.success({ text: "\u{1F513} Login to Mobb successful!" });
945
1019
  }
946
1020
  async function handleGithubIntegration() {
947
1021
  const addGithubIntegration = skipPrompts ? true : await githubIntegrationPrompt();
@@ -976,7 +1050,7 @@ async function _scan({
976
1050
  uploadKey: reportUploadInfo.uploadKey
977
1051
  });
978
1052
  } catch (e) {
979
- uploadReportSpinner2.error({ text: "\u{1F4C1} Repo upload failed" });
1053
+ uploadReportSpinner2.error({ text: "\u{1F4C1} Report upload failed" });
980
1054
  throw e;
981
1055
  }
982
1056
  uploadReportSpinner2.success({
@@ -991,7 +1065,7 @@ async function _scan({
991
1065
  uploadKey: repoUploadInfo.uploadKey
992
1066
  });
993
1067
  } catch (e) {
994
- uploadRepoSpinner.error({ text: "\u{1F4C1} Report upload failed" });
1068
+ uploadRepoSpinner.error({ text: "\u{1F4C1} Repo upload failed" });
995
1069
  throw e;
996
1070
  }
997
1071
  uploadRepoSpinner.success({ text: "\u{1F4C1} Uploading Repo successful!" });
@@ -1081,6 +1155,15 @@ var ciOption = {
1081
1155
  type: "boolean",
1082
1156
  default: false
1083
1157
  };
1158
+ var apiKeyOption = {
1159
+ type: "string",
1160
+ describe: chalk4.bold("Mobb authentication api-key")
1161
+ };
1162
+ var commitHashOption = {
1163
+ alias: "ch",
1164
+ describe: chalk4.bold("Hash of the commit"),
1165
+ type: "string"
1166
+ };
1084
1167
 
1085
1168
  // src/args/validation.ts
1086
1169
  import chalk5 from "chalk";
@@ -1157,7 +1240,7 @@ function analyzeBuilder(yargs2) {
1157
1240
  alias: "commit-hash",
1158
1241
  describe: chalk6.bold("Hash of the commit"),
1159
1242
  type: "string"
1160
- }).option("y", yesOption).option("ci", ciOption).example(
1243
+ }).option("y", yesOption).option("ci", ciOption).option("api-key", apiKeyOption).option("commit-hash", commitHashOption).example(
1161
1244
  "$0 analyze -r https://github.com/WebGoat/WebGoat -f <your_vulirabitliy_report_path>",
1162
1245
  "analyze an existing repository"
1163
1246
  ).help();
@@ -1190,7 +1273,7 @@ function scanBuilder(args) {
1190
1273
  alias: "scanner",
1191
1274
  choices: Object.values(SCANNERS),
1192
1275
  describe: chalk7.bold("Select the scanner to use")
1193
- }).option("y", yesOption).option("ci", ciOption).example(
1276
+ }).option("y", yesOption).option("ci", ciOption).option("api-key", apiKeyOption).example(
1194
1277
  "$0 scan -r https://github.com/WebGoat/WebGoat",
1195
1278
  "Scan an existing repository"
1196
1279
  ).help();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "https://github.com/mobb-dev/bugsy",
6
6
  "main": "dist/index.js",
@@ -13,7 +13,7 @@
13
13
  "lint": "eslint --cache --max-warnings 0 --ignore-path .eslintignore --ext .ts,.tsx,.jsx .",
14
14
  "lint:fix": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx .",
15
15
  "lint:fix:files": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx",
16
- "prepack": "pnpm build && dotenv-vault pull production .env"
16
+ "prepack": "dotenv-vault pull production .env && pnpm build"
17
17
  },
18
18
  "bin": {
19
19
  "mobbdev": "bin/cli.mjs"