@fabasoad/sarif-to-slack 1.3.0 → 1.3.2

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 (128) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/workflows/release.yml +3 -3
  3. package/.github/workflows/send-sarif-to-slack.yml +15 -12
  4. package/.pre-commit-config.yaml +2 -1
  5. package/.tool-versions +1 -1
  6. package/README.md +47 -12
  7. package/biome.json +5 -52
  8. package/dist/Logger.js +51 -22
  9. package/dist/SarifToSlackClient.d.ts +12 -10
  10. package/dist/SarifToSlackClient.d.ts.map +1 -1
  11. package/dist/SarifToSlackClient.js +28 -15
  12. package/dist/index.cjs +612 -244
  13. package/dist/index.d.ts +1 -8
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -8
  16. package/dist/model/Finding.js +4 -3
  17. package/dist/model/SlackMessage.d.ts +0 -16
  18. package/dist/model/SlackMessage.d.ts.map +1 -1
  19. package/dist/model/color/ColorIdentification.js +50 -46
  20. package/dist/model/color/ColorOptions.d.ts +1 -1
  21. package/dist/model/color/ColorOptions.d.ts.map +1 -1
  22. package/dist/representations/CompactGroupByRepresentation.js +2 -2
  23. package/dist/representations/Representation.js +6 -2
  24. package/dist/representations/RepresentationFactory.js +19 -1
  25. package/dist/representations/TableGroupByRunPerLevelRepresentation.d.ts +6 -0
  26. package/dist/representations/TableGroupByRunPerLevelRepresentation.d.ts.map +1 -0
  27. package/dist/representations/TableGroupByRunPerLevelRepresentation.js +8 -0
  28. package/dist/representations/TableGroupByRunPerSeverityRepresentation.d.ts +6 -0
  29. package/dist/representations/TableGroupByRunPerSeverityRepresentation.d.ts.map +1 -0
  30. package/dist/representations/TableGroupByRunPerSeverityRepresentation.js +8 -0
  31. package/dist/representations/TableGroupByRunRepresentation.d.ts +7 -0
  32. package/dist/representations/TableGroupByRunRepresentation.d.ts.map +1 -0
  33. package/dist/representations/TableGroupByRunRepresentation.js +7 -0
  34. package/dist/representations/TableGroupBySarifPerLevelRepresentation.d.ts +6 -0
  35. package/dist/representations/TableGroupBySarifPerLevelRepresentation.d.ts.map +1 -0
  36. package/dist/representations/TableGroupBySarifPerLevelRepresentation.js +8 -0
  37. package/dist/representations/TableGroupBySarifPerSeverityRepresentation.d.ts +6 -0
  38. package/dist/representations/TableGroupBySarifPerSeverityRepresentation.d.ts.map +1 -0
  39. package/dist/representations/TableGroupBySarifPerSeverityRepresentation.js +8 -0
  40. package/dist/representations/TableGroupBySarifRepresentation.d.ts +9 -0
  41. package/dist/representations/TableGroupBySarifRepresentation.d.ts.map +1 -0
  42. package/dist/representations/TableGroupBySarifRepresentation.js +15 -0
  43. package/dist/representations/TableGroupByToolNamePerLevelRepresentation.d.ts +6 -0
  44. package/dist/representations/TableGroupByToolNamePerLevelRepresentation.d.ts.map +1 -0
  45. package/dist/representations/TableGroupByToolNamePerLevelRepresentation.js +8 -0
  46. package/dist/representations/TableGroupByToolNamePerSeverityRepresentation.d.ts +6 -0
  47. package/dist/representations/TableGroupByToolNamePerSeverityRepresentation.d.ts.map +1 -0
  48. package/dist/representations/TableGroupByToolNamePerSeverityRepresentation.js +8 -0
  49. package/dist/representations/TableGroupByToolNameRepresentation.d.ts +7 -0
  50. package/dist/representations/TableGroupByToolNameRepresentation.d.ts.map +1 -0
  51. package/dist/representations/TableGroupByToolNameRepresentation.js +7 -0
  52. package/dist/representations/TableGroupRepresentation.d.ts +16 -0
  53. package/dist/representations/TableGroupRepresentation.d.ts.map +1 -0
  54. package/dist/representations/TableGroupRepresentation.js +62 -0
  55. package/dist/representations/table/Cell.d.ts +10 -0
  56. package/dist/representations/table/Cell.d.ts.map +1 -0
  57. package/dist/representations/table/Cell.js +23 -0
  58. package/dist/representations/table/Column.d.ts +11 -0
  59. package/dist/representations/table/Column.d.ts.map +1 -0
  60. package/dist/representations/table/Column.js +31 -0
  61. package/dist/representations/table/Row.d.ts +15 -0
  62. package/dist/representations/table/Row.d.ts.map +1 -0
  63. package/dist/representations/table/Row.js +45 -0
  64. package/dist/representations/table/Table.d.ts +14 -0
  65. package/dist/representations/table/Table.d.ts.map +1 -0
  66. package/dist/representations/table/Table.js +66 -0
  67. package/dist/sarif-to-slack.d.ts +98 -88
  68. package/dist/system.d.ts +2 -0
  69. package/dist/system.d.ts.map +1 -0
  70. package/dist/system.js +24 -0
  71. package/dist/tsdoc-metadata.json +1 -1
  72. package/dist/types.d.ts +90 -56
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js +89 -42
  75. package/dist/utils/FileUtils.js +23 -21
  76. package/dist/utils/StringUtils.d.ts +2 -0
  77. package/dist/utils/StringUtils.d.ts.map +1 -0
  78. package/dist/utils/StringUtils.js +5 -0
  79. package/etc/sarif-to-slack.api.md +8 -36
  80. package/package.json +11 -11
  81. package/src/Logger.ts +64 -26
  82. package/src/SarifToSlackClient.ts +42 -29
  83. package/src/index.ts +0 -9
  84. package/src/model/Finding.ts +14 -13
  85. package/src/model/FindingArray.ts +1 -1
  86. package/src/model/SlackMessage.ts +5 -5
  87. package/src/model/color/ColorIdentification.ts +66 -50
  88. package/src/model/color/ColorOptions.ts +1 -1
  89. package/src/processors/CodeQLProcessor.ts +1 -1
  90. package/src/representations/CompactGroupByRepresentation.ts +2 -2
  91. package/src/representations/CompactGroupByRunRepresentation.ts +2 -2
  92. package/src/representations/CompactGroupBySarifRepresentation.ts +2 -2
  93. package/src/representations/CompactGroupByToolNameRepresentation.ts +2 -2
  94. package/src/representations/CompactTotalRepresentation.ts +1 -1
  95. package/src/representations/Representation.ts +9 -4
  96. package/src/representations/RepresentationFactory.ts +26 -2
  97. package/src/representations/TableGroupByRunPerLevelRepresentation.ts +9 -0
  98. package/src/representations/TableGroupByRunPerSeverityRepresentation.ts +9 -0
  99. package/src/representations/TableGroupByRunRepresentation.ts +15 -0
  100. package/src/representations/TableGroupBySarifPerLevelRepresentation.ts +9 -0
  101. package/src/representations/TableGroupBySarifPerSeverityRepresentation.ts +9 -0
  102. package/src/representations/TableGroupBySarifRepresentation.ts +25 -0
  103. package/src/representations/TableGroupByToolNamePerLevelRepresentation.ts +10 -0
  104. package/src/representations/TableGroupByToolNamePerSeverityRepresentation.ts +10 -0
  105. package/src/representations/TableGroupByToolNameRepresentation.ts +15 -0
  106. package/src/representations/TableGroupRepresentation.ts +78 -0
  107. package/src/representations/table/Cell.ts +25 -0
  108. package/src/representations/table/Column.ts +39 -0
  109. package/src/representations/table/Row.ts +50 -0
  110. package/src/representations/table/Table.ts +93 -0
  111. package/src/system.ts +27 -0
  112. package/src/types.ts +98 -58
  113. package/src/utils/Comparators.ts +1 -1
  114. package/src/utils/FileUtils.ts +30 -27
  115. package/src/utils/StringUtils.ts +7 -0
  116. package/test-data/sarif/codeql-go.sarif +1 -1
  117. package/test-data/sarif/runs-1-extensions-1-results-0.sarif +2 -2
  118. package/test-data/sarif/snyk-hex.sarif +1 -1
  119. package/tests/integration/SendSarifToSlack.spec.ts +73 -83
  120. package/tests/representations/table/Table.spec.ts +174 -0
  121. package/dist/System.d.ts +0 -2
  122. package/dist/System.d.ts.map +0 -1
  123. package/dist/System.js +0 -15
  124. package/src/System.ts +0 -16
  125. /package/test-data/sarif/{tmp → multiple}/codeql-csharp.sarif +0 -0
  126. /package/test-data/sarif/{tmp → multiple}/grype-container.sarif +0 -0
  127. /package/test-data/sarif/{tmp → multiple}/runs-1-tools-1-results-0.sarif +0 -0
  128. /package/test-data/sarif/{tmp → multiple}/runs-2-tools-2.sarif +0 -0
@@ -0,0 +1,39 @@
1
+ import type Cell from './Cell'
2
+ import Logger from '../../Logger'
3
+
4
+ export default class Column {
5
+ private readonly _logger: Logger = new Logger('Column');
6
+ private readonly _cells: Cell[];
7
+
8
+ public constructor(
9
+ public readonly header: string,
10
+ cellsCount: number,
11
+ ) {
12
+ this._cells = new Array<Cell>(cellsCount)
13
+ }
14
+
15
+ public get total(): number {
16
+ return this._cells
17
+ .reduce((sum: number, c: Cell): number => {
18
+ sum += Number(c.value)
19
+ return sum
20
+ }, 0)
21
+ }
22
+
23
+ public get width(): number {
24
+ return Math.max(
25
+ ...this._cells.map((c: Cell): number => c.getWidth()),
26
+ this.total.toString().length
27
+ )
28
+ }
29
+
30
+ public setCell(index: number, value: Cell): void {
31
+ if (index >= 0 && index < this._cells.length) {
32
+ this._cells[index] = value
33
+ const width: number = this.width
34
+ this._cells.forEach((c: Cell): void => c.setWidth(width))
35
+ } else {
36
+ this._logger.warn(`Cell index out of range. Requested index: ${index}. Cells count: ${this._cells.length}.`)
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,50 @@
1
+ import Cell from './Cell'
2
+ import Logger from '../../Logger'
3
+
4
+ export default class Row {
5
+ private readonly _logger = new Logger('Row');
6
+ private readonly _cells: Cell[];
7
+ private _totalWidth: number;
8
+
9
+ public constructor(
10
+ private readonly _header: string,
11
+ public readonly headerWidth: number,
12
+ cellsCount: number,
13
+ ) {
14
+ this._cells = Array.from({ length: cellsCount }, (): Cell => new Cell())
15
+ this._totalWidth = 1
16
+ }
17
+
18
+ public get total(): number {
19
+ return this._cells
20
+ .reduce((sum: number, c: Cell): number => {
21
+ sum += Number(c.value)
22
+ return sum
23
+ }, 0)
24
+ }
25
+
26
+ public setCell(index: number, value: Cell): void {
27
+ if (index >= 0 && index < this._cells.length) {
28
+ this._cells[index] = value
29
+ } else {
30
+ this._logger.warn(`Setting cell failed. Reason: index out of range. Requested index: ${index}. Cells count: ${this._cells.length}.`)
31
+ }
32
+ }
33
+
34
+ public get totalWidth(): number {
35
+ return this._totalWidth
36
+ }
37
+
38
+ public setTotalWidth(value: number): void {
39
+ this._totalWidth = value
40
+ }
41
+
42
+ public toString(): string {
43
+ const result: string[] = []
44
+ result.push(`${this._header}${this.headerWidth > this._header.length ? ' '.repeat(this.headerWidth - this._header.length) : ''}`)
45
+ this._cells.map((c: Cell): string => c.toString()).forEach((v: string): number => result.push(v))
46
+ const totalStr: string = this.total.toString();
47
+ result.push(`${totalStr}${this._totalWidth > totalStr.length ? ' '.repeat(this._totalWidth - totalStr.length) : ''}`)
48
+ return `|${result.join('|')}|`
49
+ }
50
+ }
@@ -0,0 +1,93 @@
1
+ import Column from './Column'
2
+ import Row from './Row'
3
+ import Cell from './Cell'
4
+
5
+ const HEADER_TOTAL: string = 'Total'
6
+
7
+ export type TableHeaders = {
8
+ main?: string,
9
+ rows: string[],
10
+ columns: string[],
11
+ }
12
+
13
+ export default class Table {
14
+ private readonly header: string
15
+ private readonly columns: Column[]
16
+ private readonly rows: Row[]
17
+
18
+ public constructor(
19
+ headers: TableHeaders,
20
+ ) {
21
+ this.header = headers.main ?? ''
22
+ this.columns = Array.from(
23
+ { length: headers.columns.length },
24
+ (_: unknown, index: number): Column => new Column(headers.columns[index], headers.rows.length)
25
+ )
26
+ const headerWidth: number = Math.max(
27
+ this.header.length,
28
+ ...headers.rows.map((v: string): number => v.length),
29
+ HEADER_TOTAL.length,
30
+ )
31
+ this.rows = Array.from(
32
+ { length: headers.rows.length },
33
+ (_: unknown, index: number): Row => new Row(headers.rows[index], headerWidth, headers.columns.length)
34
+ )
35
+ }
36
+
37
+ public set(rowIndex: number, columnIndex: number, v: number): void {
38
+ if (rowIndex >= 0 && rowIndex < this.rows.length && columnIndex >= 0 && columnIndex < this.columns.length) {
39
+ const cell = new Cell(v)
40
+ cell.setWidth(this.columns[columnIndex].header.length)
41
+ this.columns[columnIndex].setCell(rowIndex, cell)
42
+ this.rows[rowIndex].setCell(columnIndex, cell)
43
+ // Update width of the last cell ("Total") of every row, so that it is shown
44
+ // correctly in string representation
45
+ const rowTotalWidth: number = Math.max(
46
+ // Based on the sum of all total values
47
+ this.rows.reduce((sum: number, r: Row): number => {
48
+ sum += r.total
49
+ return sum
50
+ }, 0).toString().length,
51
+ // Based on the width of "Total" header
52
+ HEADER_TOTAL.length,
53
+ )
54
+ this.rows.forEach((r: Row): void => r.setTotalWidth(rowTotalWidth))
55
+ }
56
+ }
57
+
58
+ public toString(): string {
59
+ const rowsStr: string[] = [];
60
+ if (this.rows.length > 0 && this.columns.length > 0) {
61
+ this.rows.forEach((row: Row): number => rowsStr.push(row.toString()));
62
+
63
+ const rowSeparator: string = rowsStr[0].replace(/[^|]/g, '-');
64
+ rowsStr.unshift(rowSeparator);
65
+ rowsStr.push(rowSeparator);
66
+
67
+ const rowTotal: string[] = [];
68
+ let sumTotal: number = 0;
69
+ for (const column of this.columns) {
70
+ const total: number = column.total;
71
+ rowTotal.push(`${total}${total.toString().length < column.width ? ' '.repeat(column.width - total.toString().length) : ''}`);
72
+ sumTotal += total;
73
+ }
74
+ const column1: string = `${HEADER_TOTAL}${this.rows[0].headerWidth > HEADER_TOTAL.length ? ' '.repeat(this.rows[0].headerWidth - HEADER_TOTAL.length) : ''}`;
75
+ const columnLast: string = `${sumTotal}${sumTotal.toString().length < HEADER_TOTAL.length ? ' '.repeat(HEADER_TOTAL.length - sumTotal.toString().length) : ''}`;
76
+ rowsStr.push(`|${column1}|${rowTotal.join('|')}|${columnLast}|`);
77
+
78
+ // Insert first row with titles and second row with separator
79
+ const rowTop: string[] = [
80
+ this.header + (this.header.length < this.rows[0].headerWidth ? ' '.repeat(this.rows[0].headerWidth - this.header.length) : ''),
81
+ this.columns
82
+ .map((c: Column): string => `${c.header}${c.header.length < c.width ? ' '.repeat(c.width - c.header.length) : ''}`)
83
+ .join('|'),
84
+ HEADER_TOTAL + (HEADER_TOTAL.length < this.rows[0].totalWidth ? ' '.repeat(this.rows[0].totalWidth - HEADER_TOTAL.length) : ''),
85
+ ];
86
+ rowsStr.unshift(`|${rowTop.join('|')}|`);
87
+ }
88
+
89
+ return rowsStr
90
+ .join('\n')
91
+ .replace(/[|]/g, ' | ');
92
+ }
93
+ }
package/src/system.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { z, type ZodSafeParseResult } from 'zod';
2
+ import Logger from './Logger';
3
+ import { version, sha, buildAt } from './metadata.json';
4
+
5
+ /**
6
+ * Prints metadata information into the logs, such as library version, SHA and
7
+ * build time.
8
+ * @internal
9
+ */
10
+ export function logMetadata(): void {
11
+ const logger = new Logger(logMetadata.name);
12
+ logger.info(`version: ${version}`);
13
+ logger.info(`sha: ${sha}`);
14
+ logger.info(`built at: ${buildAt}`);
15
+ }
16
+
17
+ /**
18
+ * Checks if it is running in GitHub Actions with debug mode enabled.
19
+ * @returns false if ACTIONS_STEP_DEBUG env var is falsy, otherwise returns true.
20
+ * @internal
21
+ */
22
+ export function isDebug(): boolean {
23
+ const parseResult: ZodSafeParseResult<boolean> = z.stringbool().safeParse(
24
+ process.env.ACTIONS_STEP_DEBUG
25
+ );
26
+ return parseResult.success && parseResult.data;
27
+ }
package/src/types.ts CHANGED
@@ -1,48 +1,7 @@
1
- import { Run } from 'sarif'
2
- import { ColorOptions } from './model/color/ColorOptions'
3
- import FindingArray from './model/FindingArray'
4
- import { SendIf } from './model/SendIf'
5
-
6
- /**
7
- * Enum representing log levels for the service.
8
- * @public
9
- */
10
- export enum LogLevel {
11
- /**
12
- * Represents the most verbose logging level, typically used for detailed
13
- * debugging information.
14
- */
15
- Silly = 0,
16
- /**
17
- * Represents a logging level for tracing the flow of the application.
18
- */
19
- Trace = 1,
20
- /**
21
- * Represents a logging level for debugging information that is less verbose
22
- * than silly.
23
- */
24
- Debug = 2,
25
- /**
26
- * Represents a logging level for general informational messages that highlight
27
- * the progress of the application.
28
- */
29
- Info = 3,
30
- /**
31
- * Represents a logging level for potentially harmful situations that require
32
- * attention.
33
- */
34
- Warning = 4,
35
- /**
36
- * Represents a logging level for error conditions that do not require immediate
37
- * action but should be noted.
38
- */
39
- Error = 5,
40
- /**
41
- * Represents a logging level for critical errors that require immediate attention
42
- * and may cause the application to terminate.
43
- */
44
- Fatal = 6
45
- }
1
+ import type { Run } from 'sarif'
2
+ import type { ColorOptions } from './model/color/ColorOptions'
3
+ import type FindingArray from './model/FindingArray'
4
+ import type { SendIf } from './model/SendIf'
46
5
 
47
6
  /**
48
7
  * Type representing properties that indicate whether to include certain information
@@ -174,19 +133,92 @@ export enum RepresentationType {
174
133
  * ```
175
134
  */
176
135
  CompactTotalPerSeverity = 7,
177
- }
178
-
179
- /**
180
- * Options for logging.
181
- * @public
182
- */
183
- export type LogOptions = {
184
- level?: LogLevel,
185
136
  /**
186
- * More details here: https://github.com/fullstack-build/tslog?tab=readme-ov-file#pretty-templates-and-styles-color-settings
137
+ * Table information about findings grouped by Run with the level representation.
138
+ * @example
139
+ * ```text
140
+ * | | Unknown | None | Note | Warning | Error | Total |
141
+ * | ----- | ------- | ---- | ---- | ------- | ----- | ----- |
142
+ * | 1 | 0 | 0 | 0 | 1 | 0 | 1 |
143
+ * | 2 | 0 | 0 | 9 | 20 | 10 | 39 |
144
+ * | 3 | 0 | 0 | 1 | 0 | 1 | 2 |
145
+ * | 4 | 0 | 0 | 5 | 5 | 0 | 10 |
146
+ * | ----- | ------- | ---- | ---- | ------- | ----- | ----- |
147
+ * | Total | 0 | 0 | 15 | 26 | 11 | 52 |
148
+ * ```
149
+ */
150
+ TableGroupByRunPerLevel = 8,
151
+ /**
152
+ * Table information about findings grouped by Run with the severity representation.
153
+ * @example
154
+ * ```text
155
+ * | | Unknown | None | Low | Medium | High | Critical | Total |
156
+ * | ----- | ------- | ---- | --- | ------ | ---- | -------- | ----- |
157
+ * | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
158
+ * | 2 | 0 | 0 | 9 | 20 | 10 | 0 | 39 |
159
+ * | 3 | 0 | 0 | 1 | 0 | 1 | 0 | 2 |
160
+ * | 4 | 0 | 0 | 5 | 5 | 0 | 0 | 10 |
161
+ * | ----- | ------- | ---- | --- | ------ | ---- | -------- | ----- |
162
+ * | Total | 0 | 0 | 15 | 26 | 11 | 0 | 52 |
163
+ * ```
164
+ */
165
+ TableGroupByRunPerSeverity = 9,
166
+ /**
167
+ * Table information about findings grouped by tool name with the level representation.
168
+ * @example
169
+ * ```text
170
+ * | | Unknown | None | Note | Warning | Error | Total |
171
+ * | ------ | ------- | ---- | ---- | ------- | ----- | ----- |
172
+ * | CodeQL | 0 | 0 | 0 | 1 | 0 | 1 |
173
+ * | grype | 0 | 0 | 9 | 20 | 10 | 39 |
174
+ * | Trivy | 0 | 0 | 1 | 0 | 1 | 2 |
175
+ * | WizCLI | 0 | 0 | 5 | 5 | 0 | 10 |
176
+ * | ------ | ------- | ---- | ---- | ------- | ----- | ----- |
177
+ * | Total | 0 | 0 | 15 | 26 | 11 | 52 |
178
+ * ```
187
179
  */
188
- template?: string,
189
- colored?: boolean,
180
+ TableGroupByToolNamePerLevel = 10,
181
+ /**
182
+ * Table information about findings grouped by tool name with the severity representation.
183
+ * @example
184
+ * ```text
185
+ * | | Unknown | None | Low | Medium | High | Critical | Total |
186
+ * | ------ | ------- | ---- | --- | ------ | ---- | -------- | ----- |
187
+ * | CodeQL | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
188
+ * | grype | 0 | 0 | 9 | 20 | 10 | 0 | 39 |
189
+ * | Trivy | 0 | 0 | 1 | 0 | 1 | 0 | 2 |
190
+ * | WizCLI | 0 | 0 | 5 | 5 | 0 | 0 | 10 |
191
+ * | ------ | ------- | ---- | --- | ------ | ---- | -------- | ----- |
192
+ * | Total | 0 | 0 | 15 | 26 | 11 | 0 | 52 |
193
+ * ```
194
+ */
195
+ TableGroupByToolNamePerSeverity = 11,
196
+ /**
197
+ * Table information about findings grouped by SARIF file with the level representation.
198
+ * @example
199
+ * ```text
200
+ * | | Unknown | None | Note | Warning | Error | Total |
201
+ * | ----- | ------- | ---- | ---- | ------- | ----- | ----- |
202
+ * | 1 | 0 | 0 | 0 | 1 | 0 | 1 |
203
+ * | 2 | 0 | 0 | 9 | 20 | 10 | 39 |
204
+ * | ----- | ------- | ---- | ---- | ------- | ----- | ----- |
205
+ * | Total | 0 | 0 | 9 | 21 | 10 | 40 |
206
+ * ```
207
+ */
208
+ TableGroupBySarifPerLevel = 12,
209
+ /**
210
+ * Table information about findings grouped by SARIF file with the severity representation.
211
+ * @example
212
+ * ```text
213
+ * | | Unknown | None | Low | Medium | High | Critical | Total |
214
+ * | ----- | ------- | ---- | --- | ------ | ---- | -------- | ----- |
215
+ * | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
216
+ * | 2 | 0 | 0 | 9 | 20 | 10 | 0 | 39 |
217
+ * | ----- | ------- | ---- | --- | ------ | ---- | -------- | ----- |
218
+ * | Total | 0 | 0 | 9 | 21 | 10 | 0 | 40 |
219
+ * ```
220
+ */
221
+ TableGroupBySarifPerSeverity = 13,
190
222
  }
191
223
 
192
224
  /**
@@ -211,12 +243,10 @@ export type SarifOptions = {
211
243
  * @public
212
244
  */
213
245
  export type SarifToSlackClientOptions = {
214
- webhookUrl: string,
215
246
  sarif: SarifOptions,
216
247
  username?: string,
217
248
  iconUrl?: string,
218
249
  color?: ColorOptions,
219
- log?: LogOptions,
220
250
  header?: IncludeAwareWithValueOptions,
221
251
  footer?: FooterOptions,
222
252
  actor?: IncludeAwareWithValueOptions,
@@ -241,6 +271,11 @@ export enum SecuritySeverity {
241
271
  Critical = 5,
242
272
  }
243
273
 
274
+ export const SecuritySeverityValues: string[] =
275
+ Object.values(SecuritySeverity).filter(
276
+ (v: string | SecuritySeverity): v is string => typeof v === 'string'
277
+ )
278
+
244
279
  /**
245
280
  * Enum of security level.
246
281
  * @privateRemarks Order should remain unchanged. It is used in multiple places,
@@ -256,6 +291,11 @@ export enum SecurityLevel {
256
291
  Error = 4,
257
292
  }
258
293
 
294
+ export const SecurityLevelValues: string[] =
295
+ Object.values(SecurityLevel).filter(
296
+ (v: string | SecurityLevel): v is string => typeof v === 'string'
297
+ )
298
+
259
299
  /**
260
300
  * The data about run, such as {@link Run} itself, tool name of the run and ID
261
301
  * which is manually generated and unique within a single execution.
@@ -1,4 +1,4 @@
1
- import Finding from '../model/Finding'
1
+ import type Finding from '../model/Finding'
2
2
 
3
3
  /**
4
4
  * This function returns a comparator function based on the property of the
@@ -1,31 +1,35 @@
1
- import fs from 'fs'
2
- import Logger from '../Logger'
3
- import { SarifFileExtension, SarifOptions } from '../types'
4
- import * as path from 'path'
1
+ import fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import Logger from '../Logger';
4
+ import type { SarifFileExtension, SarifOptions } from '../types';
5
5
 
6
6
  /**
7
7
  * Traverse directory recursively and returns list of files with the requested
8
8
  * extension.
9
9
  * @param dir A root directory. Starting point.
10
+ * @param recursive Whether to list files recursively or not.
10
11
  * @param extension An instance of {@link SarifFileExtension} type.
11
12
  * @param fileList Collected list of files.
12
13
  * @private
13
14
  */
14
- function listFilesRecursively(
15
+ function listFiles(
15
16
  dir: string,
17
+ recursive: boolean,
16
18
  extension: SarifFileExtension,
17
19
  fileList: string[] = []
18
20
  ): string[] {
19
- const entries: string[] = fs.readdirSync(dir)
20
- entries.forEach((entry: string): void => {
21
- const fullPath: string = path.join(dir, entry)
22
- if (fs.statSync(fullPath).isDirectory()) {
23
- listFilesRecursively(fullPath, extension, fileList)
24
- } else if (path.extname(fullPath).toLowerCase() === `.${extension}`) {
25
- fileList.push(fullPath)
26
- }
27
- })
28
- return fileList
21
+ if (fs.statSync(dir).isDirectory()) {
22
+ const entries: string[] = fs.readdirSync(dir);
23
+ entries.forEach((entry: string): void => {
24
+ const fullPath: string = path.join(dir, entry);
25
+ if (recursive && fs.statSync(fullPath).isDirectory()) {
26
+ listFiles(fullPath, recursive, extension, fileList);
27
+ } else if (path.extname(fullPath).toLowerCase() === `.${extension}`) {
28
+ fileList.push(fullPath);
29
+ }
30
+ })
31
+ }
32
+ return fileList;
29
33
  }
30
34
 
31
35
  /**
@@ -35,26 +39,25 @@ function listFilesRecursively(
35
39
  * @internal
36
40
  */
37
41
  export function extractListOfFiles(opts: SarifOptions): string[] {
42
+ const logger = new Logger(extractListOfFiles.name);
38
43
  if (!fs.existsSync(opts.path)) {
39
- throw new Error(`Provided path does not exist: ${opts.path}`)
44
+ throw new Error(`Provided path does not exist: ${opts.path}`);
40
45
  }
41
46
 
42
- const stats: fs.Stats = fs.statSync(opts.path)
47
+ const stats: fs.Stats = fs.statSync(opts.path);
43
48
 
44
49
  if (stats.isDirectory()) {
45
- Logger.info(`Provided path is a directory: ${opts.path}`)
46
- const files: string[] = opts.recursive
47
- && listFilesRecursively(opts.path, opts.extension ?? 'sarif')
48
- || fs.readdirSync(opts.path)
49
- Logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`)
50
- Logger.debug(`Found files: ${files.join(', ')}`)
51
- return files
50
+ logger.info(`Provided path is a directory: ${opts.path}`);
51
+ const files: string[] = listFiles(opts.path, !!opts.recursive, opts.extension ?? 'sarif');
52
+ logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`);
53
+ logger.debug(`Found files: ${files.join(', ')}`);
54
+ return files;
52
55
  }
53
56
 
54
57
  if (stats.isFile()) {
55
- Logger.info(`Provided path is a file: ${opts.path}`)
56
- return [opts.path]
58
+ logger.info(`Provided path is a file: ${opts.path}`);
59
+ return [opts.path];
57
60
  }
58
61
 
59
- throw new Error(`Provided path is neither a file nor a directory: ${opts.path}`)
62
+ throw new Error(`Provided path is neither a file nor a directory: ${opts.path}`);
60
63
  }
@@ -0,0 +1,7 @@
1
+ export function randomAlphabetic(length: number): string {
2
+ const alphabet = 'abcdefghijklmnopqrstuvwxyz'
3
+ return Array.from(
4
+ { length },
5
+ (): string => alphabet[Math.floor(Math.random() * alphabet.length)]
6
+ ).join('')
7
+ }