mobbdev 0.0.65 → 0.0.67

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 +237 -137
  2. package/package.json +2 -1
package/dist/index.mjs CHANGED
@@ -163,7 +163,7 @@ import fetch3 from "node-fetch";
163
163
  import open2 from "open";
164
164
  import semver from "semver";
165
165
  import tmp2 from "tmp";
166
- import { z as z8 } from "zod";
166
+ import { z as z7 } from "zod";
167
167
 
168
168
  // src/features/analysis/git.ts
169
169
  import Debug2 from "debug";
@@ -385,16 +385,7 @@ var GET_ANALYSIS = gql2`
385
385
  commitSha
386
386
  pullRequest
387
387
  }
388
- fixes {
389
- id
390
- issueType
391
- vulnerabilityReportIssues {
392
- issueLanguage
393
- state
394
- issueType
395
- vendorIssueId
396
- }
397
- }
388
+ vulnerabilityReportId
398
389
  vulnerabilityReport {
399
390
  projectId
400
391
  project {
@@ -412,12 +403,38 @@ var GET_ANALYSIS = gql2`
412
403
  var GET_FIX = gql2`
413
404
  query getFix($fixId: uuid!) {
414
405
  fix_by_pk(id: $fixId) {
406
+ issueType
407
+ id
415
408
  patchAndQuestions {
416
409
  patch
417
410
  }
418
411
  }
419
412
  }
420
413
  `;
414
+ var GET_VUL_BY_NODES_METADATA = gql2`
415
+ query getVulByNodesMetadata(
416
+ $filters: [vulnerability_report_issue_code_node_bool_exp!]
417
+ $vulnerabilityReportId: uuid!
418
+ ) {
419
+ vulnerabilityReportIssueCodeNodes: vulnerability_report_issue_code_node(
420
+ order_by: { index: desc }
421
+ where: {
422
+ _or: $filters
423
+ vulnerabilityReportIssue: {
424
+ vulnerabilityReportId: { _eq: $vulnerabilityReportId }
425
+ }
426
+ }
427
+ ) {
428
+ vulnerabilityReportIssueId
429
+ path
430
+ startLine
431
+ vulnerabilityReportIssue {
432
+ issueType
433
+ fixId
434
+ }
435
+ }
436
+ }
437
+ `;
421
438
 
422
439
  // src/features/analysis/graphql/subscirbe.ts
423
440
  import { createClient } from "graphql-ws";
@@ -610,20 +627,7 @@ var GetAnalysisQueryZ = z2.object({
610
627
  commitSha: z2.string(),
611
628
  pullRequest: z2.number()
612
629
  }),
613
- fixes: z2.array(
614
- z2.object({
615
- id: z2.string(),
616
- issueType: z2.string(),
617
- vulnerabilityReportIssues: z2.array(
618
- z2.object({
619
- issueLanguage: z2.string(),
620
- state: z2.string(),
621
- issueType: z2.string(),
622
- vendorIssueId: z2.string()
623
- })
624
- )
625
- })
626
- ),
630
+ vulnerabilityReportId: z2.string(),
627
631
  vulnerabilityReport: z2.object({
628
632
  projectId: z2.string(),
629
633
  project: z2.object({
@@ -639,11 +643,24 @@ var GetAnalysisQueryZ = z2.object({
639
643
  });
640
644
  var GetFixQueryZ = z2.object({
641
645
  fix_by_pk: z2.object({
646
+ issueType: z2.string(),
647
+ id: z2.string(),
642
648
  patchAndQuestions: z2.object({
643
649
  patch: z2.string()
644
650
  })
645
651
  })
646
652
  });
653
+ var VulnerabilityReportIssueCodeNodeZ = z2.object({
654
+ vulnerabilityReportIssueId: z2.string(),
655
+ path: z2.string(),
656
+ startLine: z2.number(),
657
+ vulnerabilityReportIssue: z2.object({
658
+ fixId: z2.string()
659
+ })
660
+ });
661
+ var GetVulByNodesMetadataZ = z2.object({
662
+ vulnerabilityReportIssueCodeNodes: z2.array(VulnerabilityReportIssueCodeNodeZ)
663
+ });
647
664
 
648
665
  // src/features/analysis/graphql/gql.ts
649
666
  var debug3 = Debug3("mobbdev:gql");
@@ -738,6 +755,42 @@ var GQLClient = class {
738
755
  });
739
756
  return UploadS3BucketInfoZ.parse(uploadS3BucketInfoResult);
740
757
  }
758
+ async getVulByNodesMetadata({
759
+ hunks,
760
+ vulnerabilityReportId
761
+ }) {
762
+ const filters = hunks.map((hunk) => {
763
+ const filter = {
764
+ path: { _eq: hunk.path },
765
+ _or: hunk.ranges.map(({ endLine, startLine }) => ({
766
+ startLine: { _gte: startLine, _lte: endLine },
767
+ endLine: { _gte: startLine, _lte: endLine }
768
+ }))
769
+ };
770
+ return filter;
771
+ });
772
+ const getVulByNodesMetadataRes = await this._client.request(GET_VUL_BY_NODES_METADATA, {
773
+ filters: { _or: filters },
774
+ vulnerabilityReportId
775
+ });
776
+ const parsedGetVulByNodesMetadataRes = GetVulByNodesMetadataZ.parse(
777
+ getVulByNodesMetadataRes
778
+ );
779
+ const uniqueVulByNodesMetadata = parsedGetVulByNodesMetadataRes.vulnerabilityReportIssueCodeNodes.reduce((acc, vulnerabilityReportIssueCodeNode) => {
780
+ if (acc[vulnerabilityReportIssueCodeNode.vulnerabilityReportIssueId]) {
781
+ return acc;
782
+ }
783
+ return {
784
+ ...acc,
785
+ [vulnerabilityReportIssueCodeNode.vulnerabilityReportIssueId]: vulnerabilityReportIssueCodeNode
786
+ };
787
+ }, {});
788
+ return {
789
+ vulnerabilityReportIssueCodeNodes: Object.values(
790
+ uniqueVulByNodesMetadata
791
+ )
792
+ };
793
+ }
741
794
  async digestVulnerabilityReport({
742
795
  fixReportId,
743
796
  projectId
@@ -831,16 +884,20 @@ var GQLClient = class {
831
884
  return GetAnalysisQueryZ.parse(res);
832
885
  }
833
886
  async getFix(fixId) {
834
- const res = await this._client.request(GET_FIX, {
835
- fixId
836
- });
887
+ const res = await this._client.request(
888
+ GET_FIX,
889
+ {
890
+ fixId
891
+ }
892
+ );
837
893
  return GetFixQueryZ.parse(res);
838
894
  }
839
895
  };
840
896
 
841
897
  // src/features/analysis/handle_finished_analysis.ts
842
898
  import Debug4 from "debug";
843
- import { z as z7 } from "zod";
899
+ import parseDiff from "parse-diff";
900
+ import { z as z6 } from "zod";
844
901
 
845
902
  // src/features/analysis/scm/gitlab.ts
846
903
  import querystring from "node:querystring";
@@ -1234,6 +1291,7 @@ var POST_COMMENT_PATH = "POST /repos/{owner}/{repo}/pulls/{pull_number}/comments
1234
1291
  var DELETE_COMMENT_PATH = "DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}";
1235
1292
  var UPDATE_COMMENT_PATH = "PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}";
1236
1293
  var GET_PR_COMMENTS_PATH = "GET /repos/{owner}/{repo}/pulls/comments";
1294
+ var GET_PR = "GET /repos/{owner}/{repo}/pulls/{pull_number}";
1237
1295
 
1238
1296
  // src/features/analysis/scm/github/github-v2.ts
1239
1297
  function postPrComment(client, params) {
@@ -1248,6 +1306,9 @@ function getPrComments(client, params) {
1248
1306
  function deleteComment(client, params) {
1249
1307
  return client.request(DELETE_COMMENT_PATH, params);
1250
1308
  }
1309
+ function getPr(client, params) {
1310
+ return client.request(GET_PR, params);
1311
+ }
1251
1312
 
1252
1313
  // src/features/analysis/scm/scmSubmit.ts
1253
1314
  import fs from "node:fs/promises";
@@ -1286,7 +1347,9 @@ var submitToScmMessageType = {
1286
1347
  var CommitToSameBranchParamsZ = BaseSubmitToScmMessageZ.merge(
1287
1348
  z4.object({
1288
1349
  type: z4.literal(submitToScmMessageType.commitToSameBranch),
1289
- branch: z4.string()
1350
+ branch: z4.string(),
1351
+ commitMessage: z4.string(),
1352
+ commitDescription: z4.string().nullish()
1290
1353
  })
1291
1354
  );
1292
1355
  var SubmitFixesToDifferentBranchParamsZ = z4.object({
@@ -1671,6 +1734,19 @@ var GithubSCMLib = class extends SCMLib {
1671
1734
  repo
1672
1735
  });
1673
1736
  }
1737
+ async getPrDiff(params) {
1738
+ if (!this.accessToken || !this.url) {
1739
+ throw new Error("cannot get Pr Comments without access token or url");
1740
+ }
1741
+ const { owner, repo } = parseOwnerAndRepo(this.url);
1742
+ const prRes = await getPr(this.oktokit, {
1743
+ ...params,
1744
+ owner,
1745
+ repo
1746
+ });
1747
+ const diffUrl = prRes.data.diff_url;
1748
+ return this.oktokit.request("GET " + diffUrl);
1749
+ }
1674
1750
  async getRepoList() {
1675
1751
  if (!this.accessToken) {
1676
1752
  console.error("no access token");
@@ -2076,22 +2152,30 @@ var GitlabAuthResultZ = z5.object({
2076
2152
  refresh_token: z5.string()
2077
2153
  });
2078
2154
 
2079
- // src/features/analysis/types.ts
2080
- import { z as z6 } from "zod";
2081
- var VulReportLocationZ = z6.object({
2082
- physicalLocation: z6.object({
2083
- artifactLocation: z6.object({
2084
- uri: z6.string(),
2085
- uriBaseId: z6.string(),
2086
- index: z6.number()
2087
- }),
2088
- region: z6.object({
2089
- startLine: z6.number(),
2090
- startColumn: z6.number(),
2091
- endColumn: z6.number()
2092
- })
2093
- })
2094
- });
2155
+ // src/features/analysis/utils/calculate_ranges.ts
2156
+ function calculateRanges(integers) {
2157
+ if (integers.length === 0) {
2158
+ return [];
2159
+ }
2160
+ integers.sort((a, b) => a - b);
2161
+ const ranges = integers.reduce(
2162
+ (result, current, index) => {
2163
+ if (index === 0) {
2164
+ return [...result, [current, current]];
2165
+ }
2166
+ const currentRange = result[result.length - 1];
2167
+ const [_start, end] = currentRange;
2168
+ if (current === end + 1) {
2169
+ currentRange[1] = current;
2170
+ } else {
2171
+ result.push([current, current]);
2172
+ }
2173
+ return result;
2174
+ },
2175
+ []
2176
+ );
2177
+ return ranges;
2178
+ }
2095
2179
 
2096
2180
  // src/features/analysis/utils/get_issue_type.ts
2097
2181
  var getIssueType = (issueType) => {
@@ -2144,6 +2228,8 @@ var getIssueType = (issueType) => {
2144
2228
  return "Cookie is not HttpOnly";
2145
2229
  case "INSECURE_COOKIE" /* InsecureCookie */:
2146
2230
  return "Insecure Cookie";
2231
+ case "TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */:
2232
+ return "Trust Boundary Violation";
2147
2233
  default: {
2148
2234
  return issueType ? issueType.replaceAll("_", " ") : "Other";
2149
2235
  }
@@ -2151,6 +2237,20 @@ var getIssueType = (issueType) => {
2151
2237
  };
2152
2238
 
2153
2239
  // src/features/analysis/utils/index.ts
2240
+ function getFixUrlWithRedirect({
2241
+ fixId,
2242
+ projectId,
2243
+ organizationId,
2244
+ analysisId,
2245
+ redirectUrl
2246
+ }) {
2247
+ return `${getFixUrl({
2248
+ fixId,
2249
+ projectId,
2250
+ organizationId,
2251
+ analysisId
2252
+ })}?commit_redirect_url=${encodeURIComponent(redirectUrl)}`;
2253
+ }
2154
2254
  function getFixUrl({
2155
2255
  fixId,
2156
2256
  projectId,
@@ -2191,6 +2291,29 @@ function scannerToFriendlyString(scanner) {
2191
2291
  return "Snyk";
2192
2292
  }
2193
2293
  }
2294
+ async function getFixesFromDiff(params) {
2295
+ const { gqlClient, diff, vulnerabilityReportId } = params;
2296
+ const parsedDiff = parseDiff(diff);
2297
+ const fileHunks = parsedDiff.map((file) => {
2298
+ const fileNumbers = file.chunks.flatMap((chunk) => chunk.changes).filter((change) => change.type === "add").map((_change) => {
2299
+ const change = _change;
2300
+ return change.ln;
2301
+ });
2302
+ const lineAddedRanges = calculateRanges(fileNumbers);
2303
+ const fileFilter = {
2304
+ path: z6.string().parse(file.to),
2305
+ ranges: lineAddedRanges.map(([startLine, endLine]) => ({
2306
+ endLine,
2307
+ startLine
2308
+ }))
2309
+ };
2310
+ return fileFilter;
2311
+ });
2312
+ return gqlClient.getVulByNodesMetadata({
2313
+ hunks: fileHunks,
2314
+ vulnerabilityReportId
2315
+ });
2316
+ }
2194
2317
  async function handleFinishedAnalysis({
2195
2318
  analysisId,
2196
2319
  scm: _scm,
@@ -2202,7 +2325,20 @@ async function handleFinishedAnalysis({
2202
2325
  return;
2203
2326
  }
2204
2327
  const scm = _scm;
2205
- const res = await gqlClient.getAnalysis(analysisId);
2328
+ const getAnalysis = await gqlClient.getAnalysis(analysisId);
2329
+ const {
2330
+ vulnerabilityReport: {
2331
+ projectId,
2332
+ project: { organizationId }
2333
+ }
2334
+ } = getAnalysis.analysis;
2335
+ const { commitSha, pullRequest } = getAnalysis.analysis.repo;
2336
+ const getPrDiff = await scm.getPrDiff({ pull_number: pullRequest });
2337
+ const { vulnerabilityReportIssueCodeNodes } = await getFixesFromDiff({
2338
+ diff: getPrDiff.data,
2339
+ gqlClient,
2340
+ vulnerabilityReportId: getAnalysis.analysis.vulnerabilityReportId
2341
+ });
2206
2342
  const comments = await scm.getPrComments({}, githubActionOctokit);
2207
2343
  await Promise.all(
2208
2344
  comments.data.filter((comment) => {
@@ -2219,97 +2355,62 @@ async function handleFinishedAnalysis({
2219
2355
  }
2220
2356
  })
2221
2357
  );
2222
- const {
2223
- vulnerabilityReport: {
2224
- file: {
2225
- signedFile: { url: vulReportUrl }
2226
- }
2227
- },
2228
- repo: { commitSha, pullRequest }
2229
- } = res.analysis;
2230
- const {
2231
- projectId,
2232
- project: { organizationId }
2233
- } = res.analysis.vulnerabilityReport;
2234
- const vulReportRes = await fetch(vulReportUrl);
2235
- const vulReport = await vulReportRes.json();
2236
2358
  return Promise.all(
2237
- res.analysis.fixes.map((fix) => {
2238
- const [vulnerabilityReportIssue] = fix.vulnerabilityReportIssues;
2239
- const issueIndex = parseInt(
2240
- z7.string().parse(vulnerabilityReportIssue?.vendorIssueId)
2241
- );
2242
- const results = vulReport.runs[0]?.results || [];
2243
- const ruleId = results[issueIndex]?.ruleId;
2244
- const location = VulReportLocationZ.parse(
2245
- results[issueIndex]?.locations[0]
2246
- );
2247
- const { uri: filePath } = location.physicalLocation.artifactLocation;
2248
- const { startLine, startColumn, endColumn } = location.physicalLocation.region;
2249
- const fixLocation = {
2250
- filePath,
2251
- startLine,
2252
- startColumn,
2253
- endColumn,
2254
- ruleId
2255
- };
2256
- return {
2257
- fix,
2258
- fixLocation
2259
- };
2260
- }).map(async ({ fix, fixLocation }) => {
2261
- const { filePath, startLine } = fixLocation;
2262
- const getFixContent = await gqlClient.getFix(fix.id);
2263
- const {
2264
- fix_by_pk: {
2359
+ vulnerabilityReportIssueCodeNodes.map(
2360
+ async (vulnerabilityReportIssueCodeNodes2) => {
2361
+ const { path: path8, startLine, vulnerabilityReportIssue } = vulnerabilityReportIssueCodeNodes2;
2362
+ const { fixId } = vulnerabilityReportIssue;
2363
+ const { fix_by_pk } = await gqlClient.getFix(fixId);
2364
+ const {
2265
2365
  patchAndQuestions: { patch }
2266
- }
2267
- } = getFixContent;
2268
- const commentRes = await scm.postPrComment(
2269
- {
2270
- body: "empty",
2271
- pull_number: pullRequest,
2272
- commit_id: commitSha,
2273
- path: filePath,
2274
- line: startLine
2275
- },
2276
- githubActionOctokit
2277
- );
2278
- const commitUrl = getCommitUrl({
2279
- fixId: fix.id,
2280
- projectId,
2281
- analysisId,
2282
- organizationId,
2283
- redirectUrl: commentRes.data.html_url
2284
- });
2285
- const fixUrl = getFixUrl({
2286
- fixId: fix.id,
2287
- projectId,
2288
- analysisId,
2289
- organizationId
2290
- });
2291
- const scanerString = scannerToFriendlyString(scanner);
2292
- const issueType = getIssueType(fix.issueType);
2293
- const title = `# ${MOBB_ICON_IMG} ${issueType} fix by Mobb is ready`;
2294
- const subTitle = `### Apply the following code change to fix ${issueType} issue detected by ${scanerString}:`;
2295
- const diff = `\`\`\`diff
2366
+ } = fix_by_pk;
2367
+ const commentRes = await scm.postPrComment(
2368
+ {
2369
+ body: "empty",
2370
+ pull_number: pullRequest,
2371
+ commit_id: commitSha,
2372
+ path: path8,
2373
+ line: startLine
2374
+ },
2375
+ githubActionOctokit
2376
+ );
2377
+ const commitUrl = getCommitUrl({
2378
+ fixId: fix_by_pk.id,
2379
+ projectId,
2380
+ analysisId,
2381
+ organizationId,
2382
+ redirectUrl: commentRes.data.html_url
2383
+ });
2384
+ const fixUrl = getFixUrlWithRedirect({
2385
+ fixId: fix_by_pk.id,
2386
+ projectId,
2387
+ analysisId,
2388
+ organizationId,
2389
+ redirectUrl: commentRes.data.html_url
2390
+ });
2391
+ const scanerString = scannerToFriendlyString(scanner);
2392
+ const issueType = getIssueType(fix_by_pk.issueType);
2393
+ const title = `# ${MOBB_ICON_IMG} ${issueType} fix by Mobb is ready`;
2394
+ const subTitle = `### Apply the following code change to fix ${issueType} issue detected by ${scanerString}:`;
2395
+ const diff = `\`\`\`diff
2296
2396
  ${patch}
2297
2397
  \`\`\``;
2298
- const fixPageLink = `[Learn more and fine tune the fix](${fixUrl})`;
2299
- await scm.updatePrComment(
2300
- {
2301
- body: `${title}
2398
+ const fixPageLink = `[Learn more and fine tune the fix](${fixUrl})`;
2399
+ await scm.updatePrComment(
2400
+ {
2401
+ body: `${title}
2302
2402
  ${subTitle}
2303
2403
  ${diff}
2304
2404
  ${commitFixButton(
2305
- commitUrl
2306
- )}
2405
+ commitUrl
2406
+ )}
2307
2407
  ${fixPageLink}`,
2308
- comment_id: commentRes.data.id
2309
- },
2310
- githubActionOctokit
2311
- );
2312
- })
2408
+ comment_id: commentRes.data.id
2409
+ },
2410
+ githubActionOctokit
2411
+ );
2412
+ }
2413
+ )
2313
2414
  );
2314
2415
  }
2315
2416
 
@@ -2595,9 +2696,8 @@ async function validateCheckamxCredentials() {
2595
2696
  if (!tryAgain) {
2596
2697
  await throwCheckmarxConfigError();
2597
2698
  }
2598
- if (await tryCheckmarxConfiguarationAgain()) {
2599
- validateCheckamxCredentials();
2600
- }
2699
+ await validateCheckamxCredentials();
2700
+ return;
2601
2701
  }
2602
2702
  await createSpinner2("\u{1F513} Checkmarx configured successfully!").start().success();
2603
2703
  }
@@ -2893,7 +2993,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
2893
2993
  gqlClient,
2894
2994
  scm,
2895
2995
  githubActionOctokit: new Octokit3({ auth: githubActionToken }),
2896
- scanner: z8.nativeEnum(SCANNERS).parse(scanner)
2996
+ scanner: z7.nativeEnum(SCANNERS).parse(scanner)
2897
2997
  })
2898
2998
  );
2899
2999
  }
@@ -2905,7 +3005,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
2905
3005
  try {
2906
3006
  const sumbitRes = await gqlClient.submitVulnerabilityReport({
2907
3007
  fixReportId: reportUploadInfo.fixReportId,
2908
- repoUrl: z8.string().parse(repo),
3008
+ repoUrl: z7.string().parse(repo),
2909
3009
  reference,
2910
3010
  projectId,
2911
3011
  vulnerabilityReportFileName: "report.json",
@@ -3270,7 +3370,7 @@ var commitHashOption = {
3270
3370
  // src/args/validation.ts
3271
3371
  import chalk6 from "chalk";
3272
3372
  import path7 from "path";
3273
- import { z as z9 } from "zod";
3373
+ import { z as z8 } from "zod";
3274
3374
  function throwRepoUrlErrorMessage({
3275
3375
  error,
3276
3376
  repoUrl,
@@ -3287,7 +3387,7 @@ Example:
3287
3387
  )}`;
3288
3388
  throw new CliError(formattedErrorMessage);
3289
3389
  }
3290
- var UrlZ = z9.string({
3390
+ var UrlZ = z8.string({
3291
3391
  invalid_type_error: "is not a valid GitHub / GitLab URL"
3292
3392
  }).refine((data) => !!parseScmURL(data), {
3293
3393
  message: "is not a valid GitHub / GitLab URL"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "0.0.65",
3
+ "version": "0.0.67",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "https://github.com/mobb-dev/bugsy",
6
6
  "main": "dist/index.js",
@@ -46,6 +46,7 @@
46
46
  "node-fetch": "3.3.1",
47
47
  "octokit": "2.0.14",
48
48
  "open": "8.4.2",
49
+ "parse-diff": "0.11.1",
49
50
  "semver": "7.5.0",
50
51
  "simple-git": "3.19.1",
51
52
  "snyk": "1.1118.0",