mobbdev 1.0.61 → 1.0.63

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.mjs +90 -76
  2. package/package.json +5 -1
package/dist/index.mjs CHANGED
@@ -7,6 +7,7 @@ var __export = (target, all) => {
7
7
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
8
 
9
9
  // src/index.ts
10
+ import Debug19 from "debug";
10
11
  import { hideBin } from "yargs/helpers";
11
12
 
12
13
  // src/types.ts
@@ -232,6 +233,7 @@ var Vulnerability_Report_Vendor_Enum = /* @__PURE__ */ ((Vulnerability_Report_Ve
232
233
  Vulnerability_Report_Vendor_Enum3["Checkmarx"] = "checkmarx";
233
234
  Vulnerability_Report_Vendor_Enum3["CheckmarxXml"] = "checkmarxXml";
234
235
  Vulnerability_Report_Vendor_Enum3["Codeql"] = "codeql";
236
+ Vulnerability_Report_Vendor_Enum3["Datadog"] = "datadog";
235
237
  Vulnerability_Report_Vendor_Enum3["Fortify"] = "fortify";
236
238
  Vulnerability_Report_Vendor_Enum3["Opengrep"] = "opengrep";
237
239
  Vulnerability_Report_Vendor_Enum3["Semgrep"] = "semgrep";
@@ -1590,7 +1592,8 @@ var SCANNERS = {
1590
1592
  Fortify: "fortify",
1591
1593
  Snyk: "snyk",
1592
1594
  Sonarqube: "sonarqube",
1593
- Semgrep: "semgrep"
1595
+ Semgrep: "semgrep",
1596
+ Datadog: "datadog"
1594
1597
  };
1595
1598
  var scannerToVulnerability_Report_Vendor_Enum = {
1596
1599
  [SCANNERS.Checkmarx]: "checkmarx" /* Checkmarx */,
@@ -1598,14 +1601,17 @@ var scannerToVulnerability_Report_Vendor_Enum = {
1598
1601
  [SCANNERS.Sonarqube]: "sonarqube" /* Sonarqube */,
1599
1602
  [SCANNERS.Codeql]: "codeql" /* Codeql */,
1600
1603
  [SCANNERS.Fortify]: "fortify" /* Fortify */,
1601
- [SCANNERS.Semgrep]: "semgrep" /* Semgrep */
1604
+ [SCANNERS.Semgrep]: "semgrep" /* Semgrep */,
1605
+ [SCANNERS.Datadog]: "datadog" /* Datadog */
1602
1606
  };
1603
1607
  var SupportedScannersZ = z8.enum([SCANNERS.Checkmarx, SCANNERS.Snyk]);
1604
1608
  var envVariablesSchema = z8.object({
1605
1609
  WEB_APP_URL: z8.string(),
1606
1610
  API_URL: z8.string(),
1607
1611
  HASURA_ACCESS_KEY: z8.string(),
1608
- LOCAL_GRAPHQL_ENDPOINT: z8.string()
1612
+ LOCAL_GRAPHQL_ENDPOINT: z8.string(),
1613
+ HTTP_PROXY: z8.string().optional().default(""),
1614
+ HTTPS_PROXY: z8.string().optional().default("")
1609
1615
  }).required();
1610
1616
  var envVariables = envVariablesSchema.parse(process.env);
1611
1617
  debug("config %o", envVariables);
@@ -1642,6 +1648,8 @@ var WEB_APP_URL = envVariables.WEB_APP_URL;
1642
1648
  var API_URL = envVariables.API_URL;
1643
1649
  var HASURA_ACCESS_KEY = envVariables.HASURA_ACCESS_KEY;
1644
1650
  var LOCAL_GRAPHQL_ENDPOINT = envVariables.LOCAL_GRAPHQL_ENDPOINT;
1651
+ var HTTPS_PROXY = envVariables.HTTPS_PROXY;
1652
+ var HTTP_PROXY = envVariables.HTTP_PROXY;
1645
1653
  var errorMessages = {
1646
1654
  missingCxProjectName: `project name ${chalk.bold(
1647
1655
  "(--cx-project-name)"
@@ -5745,13 +5753,16 @@ async function getGitlabIsUserCollaborator({
5745
5753
  try {
5746
5754
  const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
5747
5755
  const api2 = getGitBeaker({ url: repoUrl, gitlabAuthToken: accessToken });
5748
- const res = await api2.Projects.show(projectPath);
5749
- const groupAccess = res.permissions?.group_access?.access_level || 0;
5750
- const projectAccess = res.permissions?.project_access?.access_level || 0;
5751
- if (groupAccess >= AccessLevel.DEVELOPER || projectAccess >= AccessLevel.DEVELOPER) {
5752
- return true;
5753
- }
5754
- return false;
5756
+ const proj = await api2.Projects.show(projectPath);
5757
+ const groupAccess = proj.permissions?.group_access?.access_level || 0;
5758
+ const projectAccess = proj.permissions?.project_access?.access_level || 0;
5759
+ const accessLevelWithWriteAccess = [
5760
+ AccessLevel.DEVELOPER,
5761
+ AccessLevel.MAINTAINER,
5762
+ AccessLevel.OWNER,
5763
+ AccessLevel.ADMIN
5764
+ ];
5765
+ return accessLevelWithWriteAccess.includes(groupAccess) || accessLevelWithWriteAccess.includes(projectAccess);
5755
5766
  } catch (e) {
5756
5767
  return false;
5757
5768
  }
@@ -6108,16 +6119,7 @@ var GitlabSCMLib = class extends SCMLib {
6108
6119
  }
6109
6120
  async getUserHasAccessToRepo() {
6110
6121
  this._validateAccessTokenAndUrl();
6111
- let username = void 0;
6112
- try {
6113
- username = await this.getUsername();
6114
- } catch (e) {
6115
- console.warn(
6116
- "could not get username. this is okay if a project token is used"
6117
- );
6118
- }
6119
6122
  return getGitlabIsUserCollaborator({
6120
- username,
6121
6123
  accessToken: this.accessToken,
6122
6124
  repoUrl: this.url
6123
6125
  });
@@ -7124,7 +7126,8 @@ var scannerToFriendlyString = {
7124
7126
  fortify: "Fortify",
7125
7127
  snyk: "Snyk",
7126
7128
  sonarqube: "Sonarqube",
7127
- semgrep: "Semgrep"
7129
+ semgrep: "Semgrep",
7130
+ datadog: "Datadog"
7128
7131
  };
7129
7132
 
7130
7133
  // src/features/analysis/add_fix_comments_for_pr/utils/buildCommentBody.ts
@@ -7406,32 +7409,6 @@ Refresh the page in order to see the changes.`,
7406
7409
  comment_id: commentId
7407
7410
  });
7408
7411
  }
7409
- function buildAnalysisSummaryComment(params) {
7410
- const { prVulenrabilities: fixesFromDiff, fixesById } = params;
7411
- const { vulnerabilityReportIssueCodeNodes, fixablePrVuls } = fixesFromDiff;
7412
- const title = `# ${MobbIconMarkdown} ${fixablePrVuls} ${fixablePrVuls === 1 ? "fix is" : "fixes are"} ready to be committed`;
7413
- const summary = Object.entries(
7414
- // count every issue type
7415
- vulnerabilityReportIssueCodeNodes.reduce(
7416
- (result, vulnerabilityReportIssueCodeNode) => {
7417
- const { vulnerabilityReportIssue } = vulnerabilityReportIssueCodeNode;
7418
- const fix = fixesById[vulnerabilityReportIssue.fixId];
7419
- if (!fix) {
7420
- throw new Error(`fix ${vulnerabilityReportIssue.fixId} not found`);
7421
- }
7422
- const issueType = getIssueTypeFriendlyString(fix.safeIssueType);
7423
- const vulnerabilityReportIssueCount = (result[issueType] || 0) + 1;
7424
- return {
7425
- ...result,
7426
- [issueType]: vulnerabilityReportIssueCount
7427
- };
7428
- },
7429
- {}
7430
- )
7431
- ).map(([issueType, issueTypeCount]) => `**${issueType}** - ${issueTypeCount}`);
7432
- return `${title}
7433
- ${summary.join("\n")}`;
7434
- }
7435
7412
  async function getRelevantVulenrabilitiesFromDiff(params) {
7436
7413
  const { gqlClient, diff, vulnerabilityReportId } = params;
7437
7414
  const parsedDiff = parseDiff(diff);
@@ -7460,20 +7437,6 @@ async function getFixesData(params) {
7460
7437
  const { fixes } = await gqlClient.getFixes(fixesId);
7461
7438
  return keyBy(fixes, "id");
7462
7439
  }
7463
- async function postAnalysisSummary(params) {
7464
- const { prVulenrabilities, fixesById, pullRequest, scm } = params;
7465
- if (Object.values(fixesById).length === 0) {
7466
- return;
7467
- }
7468
- const analysisSummaryComment = buildAnalysisSummaryComment({
7469
- fixesById,
7470
- prVulenrabilities
7471
- });
7472
- await scm.postGeneralPrComment({
7473
- body: analysisSummaryComment,
7474
- prNumber: pullRequest
7475
- });
7476
- }
7477
7440
  async function postAnalysisInsightComment(params) {
7478
7441
  const { prVulenrabilities, pullRequest, scanner, scm } = params;
7479
7442
  const scanerString = scannerToFriendlyString[scanner];
@@ -7631,12 +7594,6 @@ async function addFixCommentsForPr({
7631
7594
  pullRequest,
7632
7595
  scanner,
7633
7596
  scm
7634
- }),
7635
- postAnalysisSummary({
7636
- fixesById,
7637
- prVulenrabilities,
7638
- pullRequest,
7639
- scm
7640
7597
  })
7641
7598
  ]);
7642
7599
  }
@@ -7736,21 +7693,32 @@ async function getGitInfo(srcDirPath) {
7736
7693
  }
7737
7694
 
7738
7695
  // src/features/analysis/graphql/gql.ts
7696
+ import fetchOrig from "cross-fetch";
7739
7697
  import Debug11 from "debug";
7740
7698
  import { GraphQLClient } from "graphql-request";
7699
+ import { HttpProxyAgent as HttpProxyAgent2 } from "http-proxy-agent";
7700
+ import { HttpsProxyAgent as HttpsProxyAgent2 } from "https-proxy-agent";
7741
7701
  import { v4 as uuidv4 } from "uuid";
7742
7702
 
7743
7703
  // src/features/analysis/graphql/subscribe.ts
7744
7704
  import { createClient } from "graphql-ws";
7705
+ import { HttpProxyAgent } from "http-proxy-agent";
7706
+ import { HttpsProxyAgent } from "https-proxy-agent";
7745
7707
  import WebSocket from "ws";
7746
7708
  var SUBSCRIPTION_TIMEOUT_MS = 30 * 60 * 1e3;
7747
7709
  function createWSClient(options) {
7710
+ const proxy = options.url.startsWith("https://") && process.env["HTTPS_PROXY"] ? new HttpsProxyAgent(process.env["HTTPS_PROXY"]) : options.url.startsWith("http://") && process.env["HTTP_PROXY"] ? new HttpProxyAgent(process.env["HTTP_PROXY"]) : null;
7711
+ const CustomWebSocket = class extends WebSocket {
7712
+ constructor(address, protocols) {
7713
+ super(address, protocols, proxy ? { agent: proxy } : void 0);
7714
+ }
7715
+ };
7748
7716
  return createClient({
7749
7717
  //this is needed to prevent AWS from killing the connection
7750
7718
  //currently our load balancer has a 29s idle timeout
7751
7719
  keepAlive: 1e4,
7752
7720
  url: options.url,
7753
- webSocketImpl: options.websocket || WebSocket,
7721
+ webSocketImpl: proxy ? CustomWebSocket : options.websocket || WebSocket,
7754
7722
  connectionParams: () => {
7755
7723
  return {
7756
7724
  headers: options.type === "apiKey" ? {
@@ -7887,6 +7855,25 @@ var GetVulByNodesMetadataZ = z27.object({
7887
7855
  var debug11 = Debug11("mobbdev:gql");
7888
7856
  var API_KEY_HEADER_NAME = "x-mobb-key";
7889
7857
  var REPORT_STATE_CHECK_DELAY = 5 * 1e3;
7858
+ var fetchWithProxy = (url, options = {}) => {
7859
+ try {
7860
+ const parsedUrl = new URL(url.toString());
7861
+ const isHttp = parsedUrl.protocol === "http:";
7862
+ const isHttps = parsedUrl.protocol === "https:";
7863
+ const proxy = isHttps ? HTTPS_PROXY : isHttp ? HTTP_PROXY : null;
7864
+ if (proxy) {
7865
+ const agent = isHttps ? new HttpsProxyAgent2(proxy) : new HttpProxyAgent2(proxy);
7866
+ return fetchOrig(url, {
7867
+ ...options,
7868
+ // @ts-expect-error Node-fetch doesn't type 'agent', but it's valid
7869
+ agent
7870
+ });
7871
+ }
7872
+ } catch (err) {
7873
+ debug11(`Skipping proxy for ${url}. Reason: ${err.message}`);
7874
+ }
7875
+ return fetchOrig(url, options);
7876
+ };
7890
7877
  var GQLClient = class {
7891
7878
  constructor(args) {
7892
7879
  __publicField(this, "_client");
@@ -7898,6 +7885,7 @@ var GQLClient = class {
7898
7885
  headers: args.type === "apiKey" ? { [API_KEY_HEADER_NAME]: args.apiKey || "" } : {
7899
7886
  Authorization: `Bearer ${args.token}`
7900
7887
  },
7888
+ fetch: fetchWithProxy,
7901
7889
  requestMiddleware: (request) => {
7902
7890
  const requestId = uuidv4();
7903
7891
  debug11(
@@ -7925,6 +7913,17 @@ var GQLClient = class {
7925
7913
  });
7926
7914
  return res.insert_cli_login_one?.id || "";
7927
7915
  }
7916
+ async verifyConnection() {
7917
+ try {
7918
+ await this.getUserInfo();
7919
+ } catch (e) {
7920
+ if (e?.toString().startsWith("FetchError")) {
7921
+ debug11("verify connection failed %o", e);
7922
+ return false;
7923
+ }
7924
+ }
7925
+ return true;
7926
+ }
7928
7927
  async verifyToken() {
7929
7928
  await this.createCommunityUser();
7930
7929
  let info;
@@ -8080,7 +8079,7 @@ var GQLClient = class {
8080
8079
  projectId,
8081
8080
  pullRequest,
8082
8081
  sha: sha || "",
8083
- experimentalEnabled,
8082
+ experimentalEnabled: !!experimentalEnabled,
8084
8083
  scanSource: params.scanSource
8085
8084
  });
8086
8085
  }
@@ -8372,16 +8371,16 @@ function createSpwan({ args, processPath, name }, options) {
8372
8371
  return createChildProcess({ childProcess: child, name }, options);
8373
8372
  }
8374
8373
  function createChildProcess({ childProcess, name }, options) {
8375
- const debug18 = Debug13(`mobbdev:${name}`);
8374
+ const debug19 = Debug13(`mobbdev:${name}`);
8376
8375
  const { display } = options;
8377
8376
  return new Promise((resolve, reject) => {
8378
8377
  let out = "";
8379
8378
  const onData = (chunk) => {
8380
- debug18(`chunk received from ${name} std ${chunk}`);
8379
+ debug19(`chunk received from ${name} std ${chunk}`);
8381
8380
  out += chunk;
8382
8381
  };
8383
8382
  if (!childProcess || !childProcess?.stdout || !childProcess?.stderr) {
8384
- debug18(`unable to fork ${name}`);
8383
+ debug19(`unable to fork ${name}`);
8385
8384
  reject(new Error(`unable to fork ${name}`));
8386
8385
  }
8387
8386
  childProcess.stdout?.on("data", onData);
@@ -8391,11 +8390,11 @@ function createChildProcess({ childProcess, name }, options) {
8391
8390
  childProcess.stderr?.pipe(process2.stderr);
8392
8391
  }
8393
8392
  childProcess.on("exit", (code) => {
8394
- debug18(`${name} exit code ${code}`);
8393
+ debug19(`${name} exit code ${code}`);
8395
8394
  resolve({ message: out, code });
8396
8395
  });
8397
8396
  childProcess.on("error", (err) => {
8398
- debug18(`${name} error %o`, err);
8397
+ debug19(`${name} error %o`, err);
8399
8398
  reject(err);
8400
8399
  });
8401
8400
  });
@@ -8950,7 +8949,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
8950
8949
  projectId,
8951
8950
  vulnerabilityReportFileName: "report.json",
8952
8951
  sha,
8953
- experimentalEnabled,
8952
+ experimentalEnabled: !!experimentalEnabled,
8954
8953
  pullRequest: params.pullRequest,
8955
8954
  scanSource: _getScanSource(command, ci)
8956
8955
  }
@@ -9105,7 +9104,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
9105
9104
  reference: ref || gitInfo.reference || "no-branch",
9106
9105
  sha: commitHash || gitInfo.hash || "0123456789abcdef",
9107
9106
  scanSource: _getScanSource(command, ci),
9108
- pullRequest: params.pullRequest
9107
+ pullRequest: params.pullRequest,
9108
+ experimentalEnabled: !!experimentalEnabled
9109
9109
  }
9110
9110
  });
9111
9111
  if (command === "review") {
@@ -9367,6 +9367,18 @@ async function handleMobbLogin({
9367
9367
  skipPrompts
9368
9368
  }) {
9369
9369
  const { createSpinner: createSpinner5 } = Spinner({ ci: skipPrompts });
9370
+ const isConnected = await inGqlClient.verifyConnection();
9371
+ if (!isConnected) {
9372
+ createSpinner5().start().error({
9373
+ text: "\u{1F513} Connection to Mobb: failed to connect to the Mobb server"
9374
+ });
9375
+ throw new CliError(
9376
+ "Connection to Mobb: failed to connect to the Mobb server"
9377
+ );
9378
+ }
9379
+ createSpinner5().start().success({
9380
+ text: `\u{1F513} Connection to Mobb: succeeded`
9381
+ });
9370
9382
  const userVerify = await inGqlClient.verifyToken();
9371
9383
  if (userVerify) {
9372
9384
  createSpinner5().start().success({
@@ -9598,7 +9610,7 @@ function analyzeBuilder(yargs2) {
9598
9610
  demandOption: true,
9599
9611
  type: "string",
9600
9612
  describe: chalk8.bold(
9601
- "Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep)"
9613
+ "Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog)"
9602
9614
  )
9603
9615
  }).option("repo", repoOption).option("p", {
9604
9616
  alias: "src-path",
@@ -9818,11 +9830,13 @@ var parseArgs = async (args) => {
9818
9830
  };
9819
9831
 
9820
9832
  // src/index.ts
9833
+ var debug18 = Debug19("mobbdev:index");
9821
9834
  async function run() {
9822
9835
  return parseArgs(hideBin(process.argv));
9823
9836
  }
9824
9837
  (async () => {
9825
9838
  try {
9839
+ debug18("Bugsy CLI v%s running...", packageJson.version);
9826
9840
  await run();
9827
9841
  process.exit(0);
9828
9842
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "1.0.61",
3
+ "version": "1.0.63",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "git+https://github.com/mobb-dev/bugsy.git",
6
6
  "main": "dist/index.js",
@@ -17,6 +17,7 @@
17
17
  "test:coverage": "GIT_PROXY_HOST=http://tinyproxy:8888 TOKEN=$(../../scripts/login_auth0.sh) vitest run --coverage",
18
18
  "test:watch": "TOKEN=$(../../scripts/login_auth0.sh) vitest",
19
19
  "test:integration": "GIT_PROXY_HOST=http://tinyproxy:8888 TOKEN=$(../../scripts/login_auth0.sh) vitest watch integration.test",
20
+ "test:integration:proxy": "GIT_PROXY_HOST=http://tinyproxy:8888 HTTP_PROXY=http://localhost:8888 API_URL=http://app-api:8080/v1/graphql TOKEN=$(../../scripts/login_auth0.sh) vitest run integration.test.ts",
20
21
  "lint": "eslint --cache --max-warnings 0 --ignore-path .eslintignore --ext .ts,.tsx,.jsx .",
21
22
  "lint:fix": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx .",
22
23
  "lint:fix:files": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx",
@@ -45,6 +46,7 @@
45
46
  "chalk": "5.4.1",
46
47
  "chalk-animation": "2.0.3",
47
48
  "configstore": "6.0.0",
49
+ "cross-fetch": "4.1.0",
48
50
  "debug": "4.4.0",
49
51
  "dotenv": "16.4.7",
50
52
  "extract-zip": "2.0.1",
@@ -53,6 +55,8 @@
53
55
  "graphql-request": "6.1.0",
54
56
  "graphql-tag": "2.12.6",
55
57
  "graphql-ws": "5.16.2",
58
+ "http-proxy-agent": "7.0.2",
59
+ "https-proxy-agent": "7.0.6",
56
60
  "inquirer": "9.2.23",
57
61
  "isomorphic-ws": "5.0.0",
58
62
  "istextorbinary": "6.0.0",