@fabasoad/sarif-to-slack 0.2.5 → 1.0.0

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 (192) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/.github/workflows/security.yml +0 -1
  3. package/.github/workflows/send-sarif-to-slack.yml +145 -73
  4. package/.gitleaksignore +8 -0
  5. package/.pre-commit-config.yaml +3 -3
  6. package/.tool-versions +1 -1
  7. package/dist/Logger.js +4 -1
  8. package/dist/SarifToSlackClient.d.ts +33 -0
  9. package/dist/SarifToSlackClient.d.ts.map +1 -0
  10. package/dist/SarifToSlackClient.js +178 -0
  11. package/dist/SlackMessageBuilder.js +34 -82
  12. package/dist/System.d.ts +1 -3
  13. package/dist/System.d.ts.map +1 -1
  14. package/dist/System.js +10 -3
  15. package/dist/index.cjs +840 -472
  16. package/dist/index.d.ts +35 -12
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +36 -12
  19. package/dist/model/Color.d.ts +70 -0
  20. package/dist/model/Color.d.ts.map +1 -0
  21. package/dist/model/Color.js +119 -0
  22. package/dist/model/Finding.d.ts +2 -0
  23. package/dist/model/Finding.d.ts.map +1 -0
  24. package/dist/model/Finding.js +93 -0
  25. package/dist/model/FindingsArray.d.ts +2 -0
  26. package/dist/model/FindingsArray.d.ts.map +1 -0
  27. package/dist/model/FindingsArray.js +24 -0
  28. package/dist/processors/CodeQLProcessor.d.ts +2 -0
  29. package/dist/processors/CodeQLProcessor.d.ts.map +1 -0
  30. package/dist/processors/CodeQLProcessor.js +17 -0
  31. package/dist/processors/CommonProcessor.d.ts +2 -0
  32. package/dist/processors/CommonProcessor.d.ts.map +1 -0
  33. package/dist/processors/CommonProcessor.js +84 -0
  34. package/dist/processors/ProcessorFactory.d.ts +2 -0
  35. package/dist/processors/ProcessorFactory.d.ts.map +1 -0
  36. package/dist/processors/ProcessorFactory.js +22 -0
  37. package/dist/processors/SnykProcessor.d.ts +2 -0
  38. package/dist/processors/SnykProcessor.d.ts.map +1 -0
  39. package/dist/processors/SnykProcessor.js +18 -0
  40. package/dist/representations/CompactGroupByRepresentation.d.ts +2 -0
  41. package/dist/representations/CompactGroupByRepresentation.d.ts.map +1 -0
  42. package/dist/representations/CompactGroupByRepresentation.js +58 -0
  43. package/dist/representations/CompactGroupByRunPerLevelRepresentation.d.ts +2 -0
  44. package/dist/representations/CompactGroupByRunPerLevelRepresentation.d.ts.map +1 -0
  45. package/dist/representations/CompactGroupByRunPerLevelRepresentation.js +13 -0
  46. package/dist/representations/CompactGroupByRunPerSeverityRepresentation.d.ts +2 -0
  47. package/dist/representations/CompactGroupByRunPerSeverityRepresentation.d.ts.map +1 -0
  48. package/dist/representations/CompactGroupByRunPerSeverityRepresentation.js +13 -0
  49. package/dist/representations/CompactGroupByRunRepresentation.d.ts +2 -0
  50. package/dist/representations/CompactGroupByRunRepresentation.d.ts.map +1 -0
  51. package/dist/representations/CompactGroupByRunRepresentation.js +39 -0
  52. package/dist/representations/CompactGroupBySarifPerLevelRepresentation.d.ts +2 -0
  53. package/dist/representations/CompactGroupBySarifPerLevelRepresentation.d.ts.map +1 -0
  54. package/dist/representations/CompactGroupBySarifPerLevelRepresentation.js +13 -0
  55. package/dist/representations/CompactGroupBySarifPerSeverityRepresentation.d.ts +2 -0
  56. package/dist/representations/CompactGroupBySarifPerSeverityRepresentation.d.ts.map +1 -0
  57. package/dist/representations/CompactGroupBySarifPerSeverityRepresentation.js +13 -0
  58. package/dist/representations/CompactGroupBySarifRepresentation.d.ts +2 -0
  59. package/dist/representations/CompactGroupBySarifRepresentation.d.ts.map +1 -0
  60. package/dist/representations/CompactGroupBySarifRepresentation.js +40 -0
  61. package/dist/representations/CompactGroupByToolNamePerLevelRepresentation.d.ts +2 -0
  62. package/dist/representations/CompactGroupByToolNamePerLevelRepresentation.d.ts.map +1 -0
  63. package/dist/representations/CompactGroupByToolNamePerLevelRepresentation.js +13 -0
  64. package/dist/representations/CompactGroupByToolNamePerSeverityRepresentation.d.ts +2 -0
  65. package/dist/representations/CompactGroupByToolNamePerSeverityRepresentation.d.ts.map +1 -0
  66. package/dist/representations/CompactGroupByToolNamePerSeverityRepresentation.js +13 -0
  67. package/dist/representations/CompactGroupByToolNameRepresentation.d.ts +2 -0
  68. package/dist/representations/CompactGroupByToolNameRepresentation.d.ts.map +1 -0
  69. package/dist/representations/CompactGroupByToolNameRepresentation.js +39 -0
  70. package/dist/representations/CompactTotalPerLevelRepresentation.d.ts +2 -0
  71. package/dist/representations/CompactTotalPerLevelRepresentation.d.ts.map +1 -0
  72. package/dist/representations/CompactTotalPerLevelRepresentation.js +13 -0
  73. package/dist/representations/CompactTotalPerSeverityRepresentation.d.ts +2 -0
  74. package/dist/representations/CompactTotalPerSeverityRepresentation.d.ts.map +1 -0
  75. package/dist/representations/CompactTotalPerSeverityRepresentation.js +13 -0
  76. package/dist/representations/CompactTotalRepresentation.d.ts +2 -0
  77. package/dist/representations/CompactTotalRepresentation.d.ts.map +1 -0
  78. package/dist/representations/CompactTotalRepresentation.js +25 -0
  79. package/dist/representations/Representation.d.ts +2 -0
  80. package/dist/representations/Representation.d.ts.map +1 -0
  81. package/dist/representations/Representation.js +28 -0
  82. package/dist/representations/RepresentationFactory.d.ts +2 -0
  83. package/dist/representations/RepresentationFactory.d.ts.map +1 -0
  84. package/dist/representations/RepresentationFactory.js +37 -0
  85. package/dist/sarif-to-slack.d.ts +337 -85
  86. package/dist/tsdoc-metadata.json +1 -1
  87. package/dist/types.d.ts +215 -51
  88. package/dist/types.d.ts.map +1 -1
  89. package/dist/types.js +225 -33
  90. package/dist/utils/Comparators.d.ts +2 -0
  91. package/dist/utils/Comparators.d.ts.map +1 -0
  92. package/dist/utils/Comparators.js +18 -0
  93. package/dist/utils/ExtendedArray.d.ts +2 -0
  94. package/dist/utils/ExtendedArray.d.ts.map +1 -0
  95. package/dist/utils/ExtendedArray.js +11 -0
  96. package/dist/utils/FileUtils.d.ts +2 -0
  97. package/dist/utils/FileUtils.d.ts.map +1 -0
  98. package/dist/utils/FileUtils.js +51 -0
  99. package/dist/utils/SarifUtils.js +20 -54
  100. package/etc/sarif-to-slack.api.md +161 -99
  101. package/jest.config.json +2 -2
  102. package/package.json +7 -7
  103. package/scripts/save-metadata.sh +12 -10
  104. package/src/Logger.ts +4 -0
  105. package/src/SarifToSlackClient.ts +202 -0
  106. package/src/SlackMessageBuilder.ts +35 -115
  107. package/src/System.ts +9 -2
  108. package/src/index.ts +47 -20
  109. package/src/model/Color.ts +201 -0
  110. package/src/model/Finding.ts +137 -0
  111. package/src/model/FindingsArray.ts +27 -0
  112. package/src/processors/CodeQLProcessor.ts +19 -0
  113. package/src/processors/CommonProcessor.ts +103 -0
  114. package/src/processors/ProcessorFactory.ts +23 -0
  115. package/src/processors/SnykProcessor.ts +19 -0
  116. package/src/representations/CompactGroupByRepresentation.ts +67 -0
  117. package/src/representations/CompactGroupByRunPerLevelRepresentation.ts +14 -0
  118. package/src/representations/CompactGroupByRunPerSeverityRepresentation.ts +14 -0
  119. package/src/representations/CompactGroupByRunRepresentation.ts +44 -0
  120. package/src/representations/CompactGroupBySarifPerLevelRepresentation.ts +15 -0
  121. package/src/representations/CompactGroupBySarifPerSeverityRepresentation.ts +15 -0
  122. package/src/representations/CompactGroupBySarifRepresentation.ts +45 -0
  123. package/src/representations/CompactGroupByToolNamePerLevelRepresentation.ts +15 -0
  124. package/src/representations/CompactGroupByToolNamePerSeverityRepresentation.ts +15 -0
  125. package/src/representations/CompactGroupByToolNameRepresentation.ts +44 -0
  126. package/src/representations/CompactTotalPerLevelRepresentation.ts +14 -0
  127. package/src/representations/CompactTotalPerSeverityRepresentation.ts +14 -0
  128. package/src/representations/CompactTotalRepresentation.ts +27 -0
  129. package/src/representations/Representation.ts +35 -0
  130. package/src/representations/RepresentationFactory.ts +49 -0
  131. package/src/types.ts +270 -53
  132. package/src/utils/Comparators.ts +19 -0
  133. package/src/utils/ExtendedArray.ts +11 -0
  134. package/src/utils/FileUtils.ts +60 -0
  135. package/src/utils/SarifUtils.ts +20 -72
  136. package/test-data/sarif/codeql-python.sarif +1448 -1
  137. package/test-data/sarif/codeql-typescript.sarif +3474 -1
  138. package/test-data/sarif/grype-github-actions.sarif +65 -0
  139. package/test-data/sarif/osv-scanner-composer.sarif +972 -0
  140. package/test-data/sarif/osv-scanner-container.sarif +2278 -0
  141. package/test-data/sarif/osv-scanner-gomodules.sarif +813 -0
  142. package/test-data/sarif/osv-scanner-hex.sarif +147 -0
  143. package/test-data/sarif/osv-scanner-maven.sarif +171 -0
  144. package/test-data/sarif/osv-scanner-npm.sarif +627 -0
  145. package/test-data/sarif/osv-scanner-pip.sarif +206 -0
  146. package/test-data/sarif/osv-scanner-pipenv.sarif +243 -0
  147. package/test-data/sarif/osv-scanner-pnpm.sarif +174 -0
  148. package/test-data/sarif/osv-scanner-poetry.sarif +1893 -0
  149. package/test-data/sarif/osv-scanner-rubygems.sarif +402 -0
  150. package/test-data/sarif/osv-scanner-uv.sarif +206 -0
  151. package/test-data/sarif/osv-scanner-yarn.sarif +5207 -0
  152. package/test-data/sarif/runs-0.sarif +5 -0
  153. package/test-data/sarif/runs-2-tools-2-results-0.sarif +1 -1
  154. package/test-data/sarif/runs-2-tools-2.sarif +1 -1
  155. package/test-data/sarif/runs-3-tools-2-results-0.sarif +1 -1
  156. package/test-data/sarif/runs-3-tools-2.sarif +1 -1
  157. package/test-data/sarif/tmp/codeql-csharp.sarif +1 -0
  158. package/test-data/sarif/tmp/grype-container.sarif +1774 -0
  159. package/test-data/sarif/tmp/runs-1-tools-1-results-0.sarif +18 -0
  160. package/test-data/sarif/tmp/runs-2-tools-2.sarif +686 -0
  161. package/test-data/sarif/trivy-iac.sarif +1 -1
  162. package/tests/integration/SendSarifToSlack.spec.ts +95 -27
  163. package/tsconfig.json +2 -0
  164. package/dist/Processors.d.ts +0 -2
  165. package/dist/Processors.d.ts.map +0 -1
  166. package/dist/Processors.js +0 -61
  167. package/dist/SarifToSlackService.d.ts +0 -39
  168. package/dist/SarifToSlackService.d.ts.map +0 -1
  169. package/dist/SarifToSlackService.js +0 -104
  170. package/dist/metadata.d.ts +0 -2
  171. package/dist/metadata.d.ts.map +0 -1
  172. package/dist/metadata.js +0 -11
  173. package/dist/model/SarifModelPerRun.d.ts +0 -2
  174. package/dist/model/SarifModelPerRun.d.ts.map +0 -1
  175. package/dist/model/SarifModelPerRun.js +0 -90
  176. package/dist/model/SarifModelPerSarif.d.ts +0 -2
  177. package/dist/model/SarifModelPerSarif.d.ts.map +0 -1
  178. package/dist/model/SarifModelPerSarif.js +0 -102
  179. package/dist/model/types.d.ts +0 -2
  180. package/dist/model/types.d.ts.map +0 -1
  181. package/dist/model/types.js +0 -49
  182. package/dist/utils/SortUtils.d.ts +0 -2
  183. package/dist/utils/SortUtils.d.ts.map +0 -1
  184. package/dist/utils/SortUtils.js +0 -20
  185. package/src/Processors.ts +0 -68
  186. package/src/SarifToSlackService.ts +0 -117
  187. package/src/metadata.ts +0 -10
  188. package/src/model/SarifModelPerRun.ts +0 -120
  189. package/src/model/SarifModelPerSarif.ts +0 -126
  190. package/src/model/types.ts +0 -50
  191. package/src/utils/SortUtils.ts +0 -33
  192. package/tests/Processors.spec.ts +0 -76
@@ -1,49 +0,0 @@
1
- /**
2
- * Enum of security severity.
3
- * @internal
4
- */
5
- export var SecuritySeverity;
6
- (function (SecuritySeverity) {
7
- SecuritySeverity["Unknown"] = "Unknown";
8
- SecuritySeverity["None"] = "None";
9
- SecuritySeverity["Low"] = "Low";
10
- SecuritySeverity["Medium"] = "Medium";
11
- SecuritySeverity["High"] = "High";
12
- SecuritySeverity["Critical"] = "Critical";
13
- })(SecuritySeverity || (SecuritySeverity = {}));
14
- /**
15
- * Ordering of security severity values. It is used for sorting purposes, so that
16
- * Slack message shows issues in the correct order.
17
- * @internal
18
- */
19
- export const SecuritySeverityOrder = [
20
- SecuritySeverity.Critical,
21
- SecuritySeverity.High,
22
- SecuritySeverity.Medium,
23
- SecuritySeverity.Low,
24
- SecuritySeverity.None,
25
- SecuritySeverity.Unknown
26
- ];
27
- /**
28
- * Enum of security level.
29
- * @internal
30
- */
31
- export var SecurityLevel;
32
- (function (SecurityLevel) {
33
- SecurityLevel["Unknown"] = "Unknown";
34
- SecurityLevel["Note"] = "Note";
35
- SecurityLevel["Warning"] = "Warning";
36
- SecurityLevel["Error"] = "Error";
37
- })(SecurityLevel || (SecurityLevel = {}));
38
- /**
39
- * Ordering of security level values. It is used for sorting purposes, so that
40
- * Slack message shows issues in the correct order.
41
- * @internal
42
- */
43
- export const SecurityLevelOrder = [
44
- SecurityLevel.Error,
45
- SecurityLevel.Warning,
46
- SecurityLevel.Note,
47
- SecurityLevel.Unknown
48
- ];
49
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBQ0gsTUFBTSxDQUFOLElBQVksZ0JBT1g7QUFQRCxXQUFZLGdCQUFnQjtJQUMxQix1Q0FBbUIsQ0FBQTtJQUNuQixpQ0FBYSxDQUFBO0lBQ2IsK0JBQVcsQ0FBQTtJQUNYLHFDQUFpQixDQUFBO0lBQ2pCLGlDQUFhLENBQUE7SUFDYix5Q0FBcUIsQ0FBQTtBQUN2QixDQUFDLEVBUFcsZ0JBQWdCLEtBQWhCLGdCQUFnQixRQU8zQjtBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FBdUI7SUFDdkQsZ0JBQWdCLENBQUMsUUFBUTtJQUN6QixnQkFBZ0IsQ0FBQyxJQUFJO0lBQ3JCLGdCQUFnQixDQUFDLE1BQU07SUFDdkIsZ0JBQWdCLENBQUMsR0FBRztJQUNwQixnQkFBZ0IsQ0FBQyxJQUFJO0lBQ3JCLGdCQUFnQixDQUFDLE9BQU87Q0FDekIsQ0FBQTtBQUVEOzs7R0FHRztBQUNILE1BQU0sQ0FBTixJQUFZLGFBS1g7QUFMRCxXQUFZLGFBQWE7SUFDdkIsb0NBQW1CLENBQUE7SUFDbkIsOEJBQWEsQ0FBQTtJQUNiLG9DQUFtQixDQUFBO0lBQ25CLGdDQUFlLENBQUE7QUFDakIsQ0FBQyxFQUxXLGFBQWEsS0FBYixhQUFhLFFBS3hCO0FBR0Q7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFvQjtJQUNqRCxhQUFhLENBQUMsS0FBSztJQUNuQixhQUFhLENBQUMsT0FBTztJQUNyQixhQUFhLENBQUMsSUFBSTtJQUNsQixhQUFhLENBQUMsT0FBTztDQUN0QixDQUFBIn0=
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=SortUtils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SortUtils.d.ts","sourceRoot":"","sources":["../../src/utils/SortUtils.ts"],"names":[],"mappings":""}
@@ -1,20 +0,0 @@
1
- import { SecurityLevelOrder, SecuritySeverityOrder } from '../model/types';
2
- /**
3
- * This function sorts the given map by security level.
4
- * See {@link SecurityLevelOrder}.
5
- * @param map A map that you need to sort.
6
- * @internal
7
- */
8
- export function sortSecurityLevelMap(map) {
9
- return map.sortBy((_, level) => level, (a, b) => SecurityLevelOrder.indexOf(a) - SecurityLevelOrder.indexOf(b)).asImmutable();
10
- }
11
- /**
12
- * This function sorts the given map by security severity.
13
- * See {@link SecuritySeverityOrder}.
14
- * @param map A map that you need to sort.
15
- * @internal
16
- */
17
- export function sortSecuritySeverityMap(map) {
18
- return map.sortBy((_, severity) => severity, (a, b) => SecuritySeverityOrder.indexOf(a) - SecuritySeverityOrder.indexOf(b)).asImmutable();
19
- }
20
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU29ydFV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL1NvcnRVdGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBRUwsa0JBQWtCLEVBRWxCLHFCQUFxQixFQUN0QixNQUFNLGdCQUFnQixDQUFBO0FBRXZCOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLEdBQXdDO0lBQzNFLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FDZixDQUFDLENBQVMsRUFBRSxLQUFvQixFQUFpQixFQUFFLENBQUMsS0FBSyxFQUN6RCxDQUFDLENBQWdCLEVBQUUsQ0FBZ0IsRUFBVSxFQUFFLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FDOUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQTtBQUNqQixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsdUJBQXVCLENBQUMsR0FBMkM7SUFDakYsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUNmLENBQUMsQ0FBUyxFQUFFLFFBQTBCLEVBQW9CLEVBQUUsQ0FBQyxRQUFRLEVBQ3JFLENBQUMsQ0FBbUIsRUFBRSxDQUFtQixFQUFVLEVBQUUsQ0FBQyxxQkFBcUIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcscUJBQXFCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUMxSCxDQUFDLFdBQVcsRUFBRSxDQUFBO0FBQ2pCLENBQUMifQ==
package/src/Processors.ts DELETED
@@ -1,68 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
- import Logger from './Logger'
4
-
5
- /**
6
- * Processes a color string and converts it to a specific hex code if it matches
7
- * a CI status identifier.
8
- * @param color - The color string to process, which can be a CI status identifier
9
- * or a custom color.
10
- * @returns The processed color as a hex string or undefined if the input is not
11
- * a recognized CI status identifier.
12
- * @internal
13
- */
14
- export function processColor(color?: string): string | undefined {
15
- switch (color) {
16
- case 'success':
17
- Logger.info(`Converting "${color}" to #008000`)
18
- return '#008000'
19
- case 'failure':
20
- Logger.info(`Converting "${color}" to #ff0000`)
21
- return '#ff0000'
22
- case 'cancelled':
23
- Logger.info(`Converting "${color}" to #0047ab`)
24
- return '#0047ab'
25
- case 'skipped':
26
- Logger.info(`Converting "${color}" to #808080`)
27
- return '#808080'
28
- default:
29
- Logger.debug(`"${color}" color is not a CI status identifier. Returning as is.`)
30
- return color
31
- }
32
- }
33
-
34
- /**
35
- * Processes the SARIF path, which can be a file or a directory. If it's a
36
- * directory, it returns an array of paths to all `.sarif` files, otherwise it
37
- * returns an array with a single path to the file.
38
- * @param sarifPath - The path to the SARIF file or directory.
39
- * @returns An array of strings representing the paths to the SARIF files.
40
- * @throws Error If the path does not exist, or if it is neither a file nor a
41
- * directory.
42
- * @internal
43
- */
44
- export function processSarifPath(sarifPath: string): string[] {
45
- if (!fs.existsSync(sarifPath)) {
46
- throw new Error(`"sarif-path" does not exist: ${sarifPath}`)
47
- }
48
-
49
- const sarifStats: fs.Stats = fs.statSync(sarifPath)
50
-
51
- if (sarifStats.isDirectory()) {
52
- Logger.info(`"sarif-path" is a directory: ${sarifPath}`)
53
- const files: string[] = fs.readdirSync(sarifPath)
54
- const filteredFiles: string[] = files.filter((file: string) =>
55
- path.extname(file).toLowerCase() === '.sarif'
56
- )
57
- Logger.info(`Found ${filteredFiles.length} SARIF files in ${sarifPath} directory`)
58
- Logger.debug(`Filtered SARIF files: ${filteredFiles.join(', ')}`)
59
- return filteredFiles.map((file: string) => path.join(sarifPath, file))
60
- }
61
-
62
- if (sarifStats.isFile()) {
63
- Logger.info(`"sarif-path" is a file: ${sarifPath}`)
64
- return [sarifPath]
65
- }
66
-
67
- throw new Error(`"sarif-path" is neither a file nor a directory: ${sarifPath}`)
68
- }
@@ -1,117 +0,0 @@
1
- import { promises as fs } from 'fs'
2
- import Logger from './Logger'
3
- import { processColor, processSarifPath } from './Processors'
4
- import { SlackMessageBuilder } from './SlackMessageBuilder'
5
- import {
6
- SarifLog,
7
- SarifToSlackServiceOptions,
8
- SlackMessage
9
- } from './types'
10
- import System from './System'
11
-
12
- /**
13
- * The main function to initialize a list of {@link SlackMessage} objects based
14
- * on the given SARIF file(s).
15
- * @param opts An instance of {@link SarifToSlackServiceOptions} object.
16
- * @returns A map where key is the SARIF file and value is an instance of
17
- * {@link SlackMessage} object
18
- * @private
19
- */
20
- async function initialize(opts: SarifToSlackServiceOptions): Promise<Map<string, SlackMessage>> {
21
- const slackMessages = new Map<string, SlackMessage>();
22
- const sarifFiles: string[] = processSarifPath(opts.sarifPath)
23
- if (sarifFiles.length === 0) {
24
- throw new Error(`No SARIF files found at the provided path: ${opts.sarifPath}`)
25
- }
26
-
27
- for (const sarifFile of sarifFiles) {
28
- const jsonString: string = await fs.readFile(sarifFile, 'utf8')
29
-
30
- const messageBuilder = new SlackMessageBuilder(opts.webhookUrl, {
31
- username: opts.username,
32
- iconUrl: opts.iconUrl,
33
- color: processColor(opts.color),
34
- sarif: JSON.parse(jsonString) as SarifLog,
35
- output: opts.output,
36
- })
37
- if (opts.header?.include) {
38
- messageBuilder.withHeader(opts.header?.value)
39
- }
40
- if (opts.footer?.include) {
41
- messageBuilder.withFooter(opts.footer?.value, opts.footer?.type)
42
- }
43
- if (opts.actor?.include) {
44
- messageBuilder.withActor(opts.actor?.value)
45
- }
46
- if (opts.run?.include) {
47
- messageBuilder.withRun()
48
- }
49
- slackMessages.set(sarifFile, messageBuilder)
50
- }
51
- return slackMessages;
52
- }
53
-
54
- /**
55
- * Service to convert SARIF files to Slack messages and send them.
56
- * @public
57
- */
58
- export class SarifToSlackService {
59
- private readonly _slackMessages: Map<string, SlackMessage>;
60
-
61
- private constructor() {
62
- this._slackMessages = new Map<string, SlackMessage>();
63
- }
64
-
65
- /**
66
- * Gets the Slack messages prepared for each SARIF file.
67
- * @returns A read-only map where keys are SARIF file paths and values are SlackMessage instances.
68
- * @public
69
- */
70
- public get slackMessages(): ReadonlyMap<string, SlackMessage> {
71
- return this._slackMessages;
72
- }
73
-
74
- /**
75
- * Creates an instance of SarifToSlackService.
76
- * @param opts - Options for the service, including webhook URL, SARIF path, and other configurations.
77
- * @returns A promise that resolves to an instance of SarifToSlackService.
78
- * @throws Error if no SARIF files are found at the provided path.
79
- * @public
80
- */
81
- public static async create(opts: SarifToSlackServiceOptions): Promise<SarifToSlackService> {
82
- Logger.initialize(opts.log)
83
- System.initialize()
84
- const instance: SarifToSlackService = new SarifToSlackService()
85
- const map: Map<string, SlackMessage> = await initialize(opts)
86
- map.forEach((val: SlackMessage, key: string) => instance._slackMessages.set(key, val))
87
- return instance
88
- }
89
-
90
- /**
91
- * Sends all prepared Slack messages.
92
- * @returns A promise that resolves when all messages have been sent.
93
- * @throws Error if a Slack message was not prepared for a SARIF path.
94
- * @public
95
- */
96
- public async sendAll(): Promise<void> {
97
- for (const sarifPath of this._slackMessages.keys()) {
98
- await this.send(sarifPath);
99
- }
100
- }
101
-
102
- /**
103
- * Sends a Slack message for a specific SARIF path.
104
- * @param sarifPath - The path of the SARIF file for which the message should be sent.
105
- * @returns A promise that resolves when the message has been sent.
106
- * @throws Error if a Slack message was not prepared for the given SARIF path.
107
- * @public
108
- */
109
- public async send(sarifPath: string): Promise<void> {
110
- const message: SlackMessage | undefined = this._slackMessages.get(sarifPath)
111
- if (!message) {
112
- throw new Error(`Slack message was not prepared for SARIF path: ${sarifPath}.`)
113
- }
114
- const text: string = await message.send()
115
- Logger.info(`Message sent for ${sarifPath} file. Status:`, text)
116
- }
117
- }
package/src/metadata.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * These values are used for the default footer in Slack message and in logging.
3
- *
4
- * @privateRemarks
5
- * This file is autogenerated by scripts/save-metadata.sh
6
- * Do not edit it manually!
7
- *
8
- * @internal
9
- */
10
- export const LIB_VERSION = '0.2.5'
@@ -1,120 +0,0 @@
1
- import type { Result, Run } from 'sarif';
2
- import { tryGetRulePropertyByResult } from '../utils/SarifUtils'
3
- import { SecurityLevel, SecuritySeverity } from './types'
4
- import Logger from '../Logger'
5
- import { Map as ImmutableMap } from 'immutable'
6
- import {
7
- sortSecurityLevelMap,
8
- sortSecuritySeverityMap
9
- } from '../utils/SortUtils';
10
-
11
- /**
12
- * This class keeps information about results per run. It has 2 hash maps:
13
- * - severity to number: the amount of results for each severity
14
- * - level to number: the amount of results for each level
15
- * @internal
16
- */
17
- export class SarifModelPerRun {
18
- public readonly toolName: string
19
-
20
- private readonly _securitySeverityMap: ImmutableMap<SecuritySeverity, number>
21
- private readonly _securityLevelMap: ImmutableMap<SecurityLevel, number>
22
-
23
- constructor(run: Run) {
24
- this.toolName = run.tool.driver.name
25
-
26
- this._securitySeverityMap = ImmutableMap<SecuritySeverity, number>().asMutable()
27
- this._securityLevelMap = ImmutableMap<SecurityLevel, number>().asMutable()
28
-
29
- this.buildSecuritySeverityMap(run)
30
- this.buildSecurityLevelMap(run)
31
- }
32
-
33
- private identifySecuritySeverity(score?: number): SecuritySeverity {
34
- if (score === undefined) {
35
- return SecuritySeverity.Unknown
36
- }
37
-
38
- if (score >= 9 && score <= 10) {
39
- return SecuritySeverity.Critical
40
- }
41
-
42
- if (score >= 7) {
43
- return SecuritySeverity.High
44
- }
45
-
46
- if (score >= 4) {
47
- return SecuritySeverity.Medium
48
- }
49
-
50
- if (score >= 0.1) {
51
- return SecuritySeverity.Low
52
- }
53
-
54
- if (score == 0) {
55
- return SecuritySeverity.None
56
- }
57
-
58
- Logger.warn(`Unsupported "${score}" security severity. Saving as "Unknown".`)
59
- return SecuritySeverity.Unknown
60
- }
61
-
62
- private identifySecurityLevel(level?: string): SecurityLevel {
63
- if (level === undefined) {
64
- return SecurityLevel.Unknown
65
- }
66
-
67
- if (level.toLowerCase() === 'error') {
68
- return SecurityLevel.Error
69
- }
70
-
71
- if (level.toLowerCase() === 'warning') {
72
- return SecurityLevel.Warning
73
- }
74
-
75
- if (level.toLowerCase() === 'note') {
76
- return SecurityLevel.Note
77
- }
78
-
79
- Logger.warn(`Unsupported ${level} security level. Saving as "Unknown".`)
80
- return SecurityLevel.Unknown
81
- }
82
-
83
- private buildSecuritySeverityMap(run: Run): void {
84
- const results: Result[] = run.results ?? []
85
- for (const result of results) {
86
- const severity: SecuritySeverity = this.identifySecuritySeverity(
87
- tryGetRulePropertyByResult(run, result, 'security-severity')
88
- )
89
- const count: number = this._securitySeverityMap.get(severity) || 0
90
- this._securitySeverityMap.set(severity, count + 1)
91
- }
92
- }
93
-
94
- private tryGetSecurityLevel(run: Run, result: Result): string | undefined {
95
- if (result.level) {
96
- return result.level
97
- }
98
-
99
- return tryGetRulePropertyByResult(run, result, 'problem.severity')
100
- }
101
-
102
- private buildSecurityLevelMap(run: Run): void {
103
- const results: Result[] = run.results ?? []
104
- for (const result of results) {
105
- const level: SecurityLevel = this.identifySecurityLevel(
106
- this.tryGetSecurityLevel(run, result)
107
- )
108
- const count: number = this._securityLevelMap.get(level) || 0
109
- this._securityLevelMap.set(level, count + 1)
110
- }
111
- }
112
-
113
- public get securitySeverityMap(): ImmutableMap<SecuritySeverity, number> {
114
- return sortSecuritySeverityMap(this._securitySeverityMap)
115
- }
116
-
117
- public get securityLevelMap(): ImmutableMap<SecurityLevel, number> {
118
- return sortSecurityLevelMap(this._securityLevelMap)
119
- }
120
- }
@@ -1,126 +0,0 @@
1
- import type { SarifLog } from '../types'
2
- import { Map as ImmutableMap } from 'immutable'
3
- import { SarifModelPerRun } from './SarifModelPerRun'
4
- import { SecurityLevel, SecuritySeverity } from './types'
5
- import {
6
- sortSecurityLevelMap,
7
- sortSecuritySeverityMap
8
- } from '../utils/SortUtils';
9
-
10
- /**
11
- * A data type that has a hash map with the amount of <severity|level> results
12
- * and tool name.
13
- * @internal
14
- */
15
- export type DataGroupedByRun<T> = {
16
- toolName: string,
17
- data: ImmutableMap<T, number>
18
- }
19
-
20
- /**
21
- * This class is used to group results by different fields, such as grouping by
22
- * tool name, runs, etc.
23
- * @internal
24
- */
25
- export class SarifModelPerSarif {
26
- private readonly sarifModelPerRunList: Array<SarifModelPerRun>;
27
-
28
- constructor(sarif: SarifLog) {
29
- this.sarifModelPerRunList = new Array<SarifModelPerRun>()
30
- this.buildRunsList(sarif)
31
- }
32
-
33
- private buildRunsList(sarif: SarifLog): void {
34
- for (const run of sarif.runs) {
35
- this.sarifModelPerRunList.push(new SarifModelPerRun(run))
36
- }
37
- }
38
-
39
- public groupByToolNameWithSecurityLevel(): Map<string, ImmutableMap<SecurityLevel, number>> {
40
- const result = new Map<string, ImmutableMap<SecurityLevel, number>>()
41
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
42
- if (!result.has(sarifModelPerRun.toolName)) {
43
- result.set(sarifModelPerRun.toolName, ImmutableMap<SecurityLevel, number>().asMutable())
44
- }
45
- for (const [k, v] of sarifModelPerRun.securityLevelMap.entries()) {
46
- const count: number = result.get(sarifModelPerRun.toolName)?.get(k) || 0
47
- result.get(sarifModelPerRun.toolName)?.set(k, count + v)
48
- }
49
- }
50
- // Sort
51
- for (const [k, v] of result) {
52
- result.set(k, sortSecurityLevelMap(v))
53
- }
54
- return result
55
- }
56
-
57
- public groupByRunWithSecurityLevel(): DataGroupedByRun<SecurityLevel>[] {
58
- const result = new Array<DataGroupedByRun<SecurityLevel>>()
59
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
60
- result.push({
61
- toolName: sarifModelPerRun.toolName,
62
- data: sarifModelPerRun.securityLevelMap,
63
- })
64
- }
65
- return result
66
- }
67
-
68
- public groupByTotalWithSecurityLevel(): ImmutableMap<SecurityLevel, number> {
69
- const result = ImmutableMap<SecurityLevel, number>().asMutable()
70
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
71
- for (const [k, v] of sarifModelPerRun.securityLevelMap.entries()) {
72
- const count: number = result.get(k) || 0
73
- result.set(k, count + v)
74
- }
75
- }
76
- return sortSecurityLevelMap(result)
77
- }
78
-
79
- public groupByToolNameWithSecuritySeverity(): Map<string, ImmutableMap<SecuritySeverity, number>> {
80
- const result = new Map<string, ImmutableMap<SecuritySeverity, number>>()
81
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
82
- if (!result.has(sarifModelPerRun.toolName)) {
83
- result.set(sarifModelPerRun.toolName, ImmutableMap<SecuritySeverity, number>().asMutable())
84
- }
85
- for (const [k, v] of sarifModelPerRun.securitySeverityMap.entries()) {
86
- const count: number = result.get(sarifModelPerRun.toolName)?.get(k) || 0
87
- result.get(sarifModelPerRun.toolName)?.set(k, count + v)
88
- }
89
- }
90
- // Sort
91
- for (const [k, v] of result.entries()) {
92
- result.set(k, sortSecuritySeverityMap(v))
93
- }
94
- return result
95
- }
96
-
97
- public groupByRunWithSecuritySeverity(): DataGroupedByRun<SecuritySeverity>[] {
98
- const result = new Array<DataGroupedByRun<SecuritySeverity>>()
99
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
100
- result.push({
101
- toolName: sarifModelPerRun.toolName,
102
- data: sarifModelPerRun.securitySeverityMap,
103
- })
104
- }
105
- return result
106
- }
107
-
108
- public groupByTotalWithSecuritySeverity(): ImmutableMap<SecuritySeverity, number> {
109
- const result = ImmutableMap<SecuritySeverity, number>().asMutable()
110
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
111
- for (const [k, v] of sarifModelPerRun.securitySeverityMap.entries()) {
112
- const count: number = result.get(k) || 0
113
- result.set(k, count + v)
114
- }
115
- }
116
- return sortSecuritySeverityMap(result)
117
- }
118
-
119
- public listToolNames(): Set<string> {
120
- const toolNames = new Set<string>()
121
- for (const sarifModelPerRun of this.sarifModelPerRunList) {
122
- toolNames.add(sarifModelPerRun.toolName)
123
- }
124
- return toolNames
125
- }
126
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * Enum of security severity.
3
- * @internal
4
- */
5
- export enum SecuritySeverity {
6
- Unknown = 'Unknown',
7
- None = 'None',
8
- Low = 'Low',
9
- Medium = 'Medium',
10
- High = 'High',
11
- Critical = 'Critical'
12
- }
13
-
14
- /**
15
- * Ordering of security severity values. It is used for sorting purposes, so that
16
- * Slack message shows issues in the correct order.
17
- * @internal
18
- */
19
- export const SecuritySeverityOrder: SecuritySeverity[] = [
20
- SecuritySeverity.Critical,
21
- SecuritySeverity.High,
22
- SecuritySeverity.Medium,
23
- SecuritySeverity.Low,
24
- SecuritySeverity.None,
25
- SecuritySeverity.Unknown
26
- ]
27
-
28
- /**
29
- * Enum of security level.
30
- * @internal
31
- */
32
- export enum SecurityLevel {
33
- Unknown = 'Unknown',
34
- Note = 'Note',
35
- Warning = 'Warning',
36
- Error = 'Error'
37
- }
38
-
39
-
40
- /**
41
- * Ordering of security level values. It is used for sorting purposes, so that
42
- * Slack message shows issues in the correct order.
43
- * @internal
44
- */
45
- export const SecurityLevelOrder: SecurityLevel[] = [
46
- SecurityLevel.Error,
47
- SecurityLevel.Warning,
48
- SecurityLevel.Note,
49
- SecurityLevel.Unknown
50
- ]
@@ -1,33 +0,0 @@
1
- import { Map as ImmutableMap } from 'immutable'
2
- import {
3
- SecurityLevel,
4
- SecurityLevelOrder,
5
- SecuritySeverity,
6
- SecuritySeverityOrder
7
- } from '../model/types'
8
-
9
- /**
10
- * This function sorts the given map by security level.
11
- * See {@link SecurityLevelOrder}.
12
- * @param map A map that you need to sort.
13
- * @internal
14
- */
15
- export function sortSecurityLevelMap(map: ImmutableMap<SecurityLevel, number>): ImmutableMap<SecurityLevel, number> {
16
- return map.sortBy(
17
- (_: number, level: SecurityLevel): SecurityLevel => level,
18
- (a: SecurityLevel, b: SecurityLevel): number => SecurityLevelOrder.indexOf(a) - SecurityLevelOrder.indexOf(b),
19
- ).asImmutable()
20
- }
21
-
22
- /**
23
- * This function sorts the given map by security severity.
24
- * See {@link SecuritySeverityOrder}.
25
- * @param map A map that you need to sort.
26
- * @internal
27
- */
28
- export function sortSecuritySeverityMap(map: ImmutableMap<SecuritySeverity, number>): ImmutableMap<SecuritySeverity, number> {
29
- return map.sortBy(
30
- (_: number, severity: SecuritySeverity): SecuritySeverity => severity,
31
- (a: SecuritySeverity, b: SecuritySeverity): number => SecuritySeverityOrder.indexOf(a) - SecuritySeverityOrder.indexOf(b),
32
- ).asImmutable()
33
- }
@@ -1,76 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
- import { processColor, processSarifPath } from '../src/Processors'
4
-
5
- jest.mock('fs')
6
- const mockedFs = fs as jest.Mocked<typeof fs>
7
-
8
- jest.mock('../src/Logger', () => ({
9
- __esModule: true,
10
- default: { info: jest.fn(), debug: jest.fn() }
11
- }))
12
-
13
- describe('(unit): processColor', () => {
14
- test('returns correct hex for success', () => {
15
- expect(processColor('success')).toBe('#008000')
16
- })
17
-
18
- test('returns correct hex for failure', () => {
19
- expect(processColor('failure')).toBe('#ff0000')
20
- })
21
-
22
- test('returns correct hex for cancelled', () => {
23
- expect(processColor('cancelled')).toBe('#0047ab')
24
- })
25
-
26
- test('returns correct hex for skipped', () => {
27
- expect(processColor('skipped')).toBe('#808080')
28
- })
29
-
30
- test('returns input for unknown color', () => {
31
- expect(processColor('other')).toBe('other')
32
- })
33
-
34
- test('returns undefined for undefined input', () => {
35
- expect(processColor(undefined)).toBeUndefined()
36
- })
37
- })
38
-
39
- describe('(unit): processSarifPath', () => {
40
- const fakeDir = '/fake/dir'
41
- const fakeFile = '/fake/file.sarif'
42
-
43
- afterEach(() => {
44
- jest.resetAllMocks()
45
- })
46
-
47
- test('throws if path does not exist', () => {
48
- mockedFs.existsSync.mockReturnValue(false)
49
- expect(() => processSarifPath(fakeFile)).toThrow(/does not exist/)
50
- })
51
-
52
- test('returns .sarif files in directory', () => {
53
- mockedFs.existsSync.mockReturnValue(true)
54
- mockedFs.statSync.mockReturnValue({ isDirectory: () => true, isFile: () => false } as any)
55
- // @ts-ignore: mocking readdirSync with a specific return value
56
- mockedFs.readdirSync.mockReturnValue(['a.sarif', 'b.SARIF', 'c.txt'])
57
- const result: string[] = processSarifPath(fakeDir)
58
- expect(result).toEqual(
59
- ['a.sarif', 'b.SARIF'].map((file: string) => path.join(fakeDir, file))
60
- )
61
- })
62
-
63
- test('returns file path if it is a file', () => {
64
- mockedFs.existsSync.mockReturnValue(true)
65
- mockedFs.statSync.mockReturnValue({ isDirectory: () => false, isFile: () => true } as any)
66
- const result: string[] = processSarifPath(fakeFile)
67
- expect(result).toHaveLength(1)
68
- expect(result[0]).toEqual(fakeFile)
69
- })
70
-
71
- test('throws if path is neither file nor directory', () => {
72
- mockedFs.existsSync.mockReturnValue(true)
73
- mockedFs.statSync.mockReturnValue({ isDirectory: () => false, isFile: () => false } as any)
74
- expect(() => processSarifPath('/weird/path')).toThrow(/neither a file nor a directory/)
75
- })
76
- })