@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
package/dist/index.cjs CHANGED
@@ -32,7 +32,6 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  Color: () => Color,
34
34
  FooterType: () => FooterType,
35
- LogLevel: () => LogLevel,
36
35
  RepresentationType: () => RepresentationType,
37
36
  SarifToSlackClient: () => SarifToSlackClient,
38
37
  SendIf: () => SendIf
@@ -165,20 +164,91 @@ function sendIfLogMessage(sendIf) {
165
164
  }
166
165
  }
167
166
 
168
- // src/model/SlackMessage.ts
169
- var import_webhook = require("@slack/webhook");
167
+ // src/SarifToSlackClient.ts
168
+ var import_node_fs2 = require("fs");
169
+
170
+ // src/Logger.ts
171
+ var import_tslog = require("tslog");
172
+ var import_zod2 = require("zod");
173
+
174
+ // src/system.ts
175
+ var import_zod = require("zod");
176
+
177
+ // src/metadata.json
178
+ var version = "1.3.2";
179
+ var sha = "7f304afbd27622223e810bcac958e15bfde8d56d";
180
+ var buildAt = "2025-10-01T12:12:59Z";
181
+
182
+ // src/system.ts
183
+ function logMetadata() {
184
+ const logger = new Logger(logMetadata.name);
185
+ logger.info(`version: ${version}`);
186
+ logger.info(`sha: ${sha}`);
187
+ logger.info(`built at: ${buildAt}`);
188
+ }
189
+ function isDebug() {
190
+ const parseResult = import_zod.z.stringbool().safeParse(
191
+ process.env.ACTIONS_STEP_DEBUG
192
+ );
193
+ return parseResult.success && parseResult.data;
194
+ }
195
+
196
+ // src/Logger.ts
197
+ var LogLevelItems = ["silly", "trace", "debug", "info", "warning", "error", "fatal"];
198
+ var Logger = class _Logger {
199
+ static APP_NAME = "@fabasoad/sarif-to-slack";
200
+ static DEFAULT_LOG_LEVEL = "info";
201
+ static DEFAULT_LOG_TEMPLATE = "[{{logLevelName}}] [{{name}}] {{dateIsoStr}} ";
202
+ static DEFAULT_LOG_COLORED = true;
203
+ _instance;
204
+ isLogLevel(v) {
205
+ return LogLevelItems.includes(v);
206
+ }
207
+ getMinLevel() {
208
+ let result = _Logger.DEFAULT_LOG_LEVEL;
209
+ if (isDebug()) {
210
+ result = "silly";
211
+ } else {
212
+ const parseResult = import_zod2.z.string().refine((v) => this.isLogLevel(v)).transform((v) => v).safeParse(process.env.SARIF_TO_SLACK_LOG_LEVEL);
213
+ if (parseResult.success) {
214
+ result = parseResult.data;
215
+ }
216
+ }
217
+ return LogLevelItems.findIndex((v) => v === result);
218
+ }
219
+ getLogTemplate() {
220
+ const result = import_zod2.z.string().safeParse(process.env.SARIF_TO_SLACK_LOG_TEMPLATE);
221
+ return result.success ? result.data : _Logger.DEFAULT_LOG_TEMPLATE;
222
+ }
223
+ getLogColored() {
224
+ const result = import_zod2.z.stringbool().safeParse(process.env.SARIF_TO_SLACK_LOG_COLORED);
225
+ return result.success ? result.data : _Logger.DEFAULT_LOG_COLORED;
226
+ }
227
+ constructor(memberName) {
228
+ this._instance = new import_tslog.Logger({
229
+ name: `${_Logger.APP_NAME}${memberName === void 0 ? "" : `::${memberName}`}`,
230
+ minLevel: this.getMinLevel(),
231
+ type: "pretty",
232
+ prettyLogTimeZone: "UTC",
233
+ prettyLogTemplate: this.getLogTemplate(),
234
+ stylePrettyLogs: this.getLogColored()
235
+ });
236
+ }
237
+ info(...args) {
238
+ this._instance.info(...args);
239
+ }
240
+ warn(...args) {
241
+ this._instance.warn(...args);
242
+ }
243
+ trace(...args) {
244
+ this._instance.trace(...args);
245
+ }
246
+ debug(...args) {
247
+ this._instance.debug(...args);
248
+ }
249
+ };
170
250
 
171
251
  // src/types.ts
172
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
173
- LogLevel2[LogLevel2["Silly"] = 0] = "Silly";
174
- LogLevel2[LogLevel2["Trace"] = 1] = "Trace";
175
- LogLevel2[LogLevel2["Debug"] = 2] = "Debug";
176
- LogLevel2[LogLevel2["Info"] = 3] = "Info";
177
- LogLevel2[LogLevel2["Warning"] = 4] = "Warning";
178
- LogLevel2[LogLevel2["Error"] = 5] = "Error";
179
- LogLevel2[LogLevel2["Fatal"] = 6] = "Fatal";
180
- return LogLevel2;
181
- })(LogLevel || {});
182
252
  var FooterType = /* @__PURE__ */ ((FooterType2) => {
183
253
  FooterType2["PlainText"] = "plain_text";
184
254
  FooterType2["Markdown"] = "mrkdwn";
@@ -193,6 +263,12 @@ var RepresentationType = /* @__PURE__ */ ((RepresentationType2) => {
193
263
  RepresentationType2[RepresentationType2["CompactGroupBySarifPerSeverity"] = 5] = "CompactGroupBySarifPerSeverity";
194
264
  RepresentationType2[RepresentationType2["CompactTotalPerLevel"] = 6] = "CompactTotalPerLevel";
195
265
  RepresentationType2[RepresentationType2["CompactTotalPerSeverity"] = 7] = "CompactTotalPerSeverity";
266
+ RepresentationType2[RepresentationType2["TableGroupByRunPerLevel"] = 8] = "TableGroupByRunPerLevel";
267
+ RepresentationType2[RepresentationType2["TableGroupByRunPerSeverity"] = 9] = "TableGroupByRunPerSeverity";
268
+ RepresentationType2[RepresentationType2["TableGroupByToolNamePerLevel"] = 10] = "TableGroupByToolNamePerLevel";
269
+ RepresentationType2[RepresentationType2["TableGroupByToolNamePerSeverity"] = 11] = "TableGroupByToolNamePerSeverity";
270
+ RepresentationType2[RepresentationType2["TableGroupBySarifPerLevel"] = 12] = "TableGroupBySarifPerLevel";
271
+ RepresentationType2[RepresentationType2["TableGroupBySarifPerSeverity"] = 13] = "TableGroupBySarifPerSeverity";
196
272
  return RepresentationType2;
197
273
  })(RepresentationType || {});
198
274
  var SecuritySeverity = /* @__PURE__ */ ((SecuritySeverity2) => {
@@ -204,6 +280,9 @@ var SecuritySeverity = /* @__PURE__ */ ((SecuritySeverity2) => {
204
280
  SecuritySeverity2[SecuritySeverity2["Critical"] = 5] = "Critical";
205
281
  return SecuritySeverity2;
206
282
  })(SecuritySeverity || {});
283
+ var SecuritySeverityValues = Object.values(SecuritySeverity).filter(
284
+ (v) => typeof v === "string"
285
+ );
207
286
  var SecurityLevel = /* @__PURE__ */ ((SecurityLevel2) => {
208
287
  SecurityLevel2[SecurityLevel2["Unknown"] = 0] = "Unknown";
209
288
  SecurityLevel2[SecurityLevel2["None"] = 1] = "None";
@@ -212,176 +291,64 @@ var SecurityLevel = /* @__PURE__ */ ((SecurityLevel2) => {
212
291
  SecurityLevel2[SecurityLevel2["Error"] = 4] = "Error";
213
292
  return SecurityLevel2;
214
293
  })(SecurityLevel || {});
294
+ var SecurityLevelValues = Object.values(SecurityLevel).filter(
295
+ (v) => typeof v === "string"
296
+ );
215
297
 
216
- // src/metadata.json
217
- var version = "1.3.0";
218
- var sha = "5c2d7ac234dc99d16b3d8ec9c05475ce26a6a573";
219
- var buildAt = "2025-08-21T13:21:58Z";
220
-
221
- // src/model/SlackMessage.ts
222
- function createSlackMessage(url, opts) {
223
- return new SlackMessageImpl(url, opts);
224
- }
225
- var SlackMessageImpl = class {
226
- _webhook;
227
- _gitHubServerUrl;
228
- _color;
229
- _representation;
230
- _header;
231
- _footer;
232
- _actor;
233
- _runId;
234
- constructor(url, opts) {
235
- this._webhook = new import_webhook.IncomingWebhook(url, {
236
- username: opts.username || "SARIF results",
237
- icon_url: opts.iconUrl
238
- });
239
- this._gitHubServerUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
240
- this._color = opts.color;
241
- this._representation = opts.representation;
242
- }
243
- withHeader(header) {
244
- this._header = {
245
- type: "header",
246
- text: {
247
- type: "plain_text",
248
- text: header || process.env.GITHUB_REPOSITORY || "SARIF results"
249
- }
250
- };
251
- }
252
- withActor(actor) {
253
- this._actor = actor || process.env.GITHUB_ACTOR;
254
- }
255
- withRun() {
256
- this._runId = process.env.GITHUB_RUN_ID;
257
- }
258
- withFooter(text, type) {
259
- const repoName = "fabasoad/sarif-to-slack";
260
- const element = text ? { type: type || "plain_text" /* PlainText */, text } : { type: "mrkdwn" /* Markdown */, text: `Generated by <${this._gitHubServerUrl}/${repoName}|@${repoName}@${version}>` };
261
- this._footer = {
262
- type: "context",
263
- elements: [element]
264
- };
265
- }
266
- async send() {
267
- const blocks = [];
268
- if (this._header) {
269
- blocks.push(this._header);
270
- }
271
- blocks.push({
272
- type: "section",
273
- text: {
274
- type: "mrkdwn",
275
- text: this.buildText()
298
+ // src/utils/FileUtils.ts
299
+ var import_node_fs = __toESM(require("fs"));
300
+ var path = __toESM(require("path"));
301
+ function listFiles(dir, recursive, extension, fileList = []) {
302
+ if (import_node_fs.default.statSync(dir).isDirectory()) {
303
+ const entries = import_node_fs.default.readdirSync(dir);
304
+ entries.forEach((entry) => {
305
+ const fullPath = path.join(dir, entry);
306
+ if (recursive && import_node_fs.default.statSync(fullPath).isDirectory()) {
307
+ listFiles(fullPath, recursive, extension, fileList);
308
+ } else if (path.extname(fullPath).toLowerCase() === `.${extension}`) {
309
+ fileList.push(fullPath);
276
310
  }
277
311
  });
278
- if (this._footer) {
279
- blocks.push(this._footer);
280
- }
281
- const { text } = await this._webhook.send({
282
- attachments: [{ color: this._color, blocks }]
283
- });
284
- return text;
285
312
  }
286
- buildText() {
287
- const text = [];
288
- if (this._actor) {
289
- const actorUrl = `${this._gitHubServerUrl}/${this._actor}`;
290
- text.push(`_Triggered by <${actorUrl}|${this._actor}>_`);
291
- }
292
- text.push(this._representation.compose());
293
- if (this._runId) {
294
- let runText = "Job ";
295
- if (process.env.GITHUB_REPOSITORY) {
296
- runText += `<${this._gitHubServerUrl}/${process.env.GITHUB_REPOSITORY}/actions/runs/${this._runId}|#${this._runId}>`;
297
- } else {
298
- runText += `#${this._runId}`;
299
- }
300
- text.push(runText);
301
- }
302
- return text.join("\n\n");
303
- }
304
- };
305
-
306
- // src/SarifToSlackClient.ts
307
- var import_fs2 = require("fs");
308
-
309
- // src/Logger.ts
310
- var import_tslog = require("tslog");
311
- var Logger = class _Logger {
312
- static DEFAULT_LOG_LEVEL = 3 /* Info */;
313
- static DEFAULT_LOG_TEMPLATE = "[{{logLevelName}}] [{{name}}] {{dateIsoStr}} ";
314
- static DEFAULT_LOG_COLORED = true;
315
- static instance;
316
- static initialize(opts) {
317
- if (!_Logger.instance) {
318
- _Logger.instance = new import_tslog.Logger({
319
- name: "@fabasoad/sarif-to-slack",
320
- minLevel: process.env.ACTIONS_STEP_DEBUG === "true" ? 0 /* Silly */ : opts?.level ?? _Logger.DEFAULT_LOG_LEVEL,
321
- type: "pretty",
322
- prettyLogTimeZone: "UTC",
323
- prettyLogTemplate: opts?.template ?? _Logger.DEFAULT_LOG_TEMPLATE,
324
- stylePrettyLogs: opts?.colored ?? _Logger.DEFAULT_LOG_COLORED
325
- });
326
- }
327
- }
328
- static warn(...args) {
329
- _Logger.instance.warn(...args);
330
- }
331
- static info(...args) {
332
- _Logger.instance.info(...args);
333
- }
334
- static debug(...args) {
335
- _Logger.instance.debug(...args);
336
- }
337
- static trace(...args) {
338
- _Logger.instance.trace(...args);
339
- }
340
- };
341
-
342
- // src/System.ts
343
- var System = class {
344
- static initialize() {
345
- Logger.info(`@fabasoad/sarif-to-slack version: ${version}`);
346
- Logger.info(`@fabasoad/sarif-to-slack sha: ${sha}`);
347
- Logger.info(`@fabasoad/sarif-to-slack built at: ${buildAt}`);
348
- }
349
- };
350
-
351
- // src/utils/FileUtils.ts
352
- var import_fs = __toESM(require("fs"));
353
- var path = __toESM(require("path"));
354
- function listFilesRecursively(dir, extension, fileList = []) {
355
- const entries = import_fs.default.readdirSync(dir);
356
- entries.forEach((entry) => {
357
- const fullPath = path.join(dir, entry);
358
- if (import_fs.default.statSync(fullPath).isDirectory()) {
359
- listFilesRecursively(fullPath, extension, fileList);
360
- } else if (path.extname(fullPath).toLowerCase() === `.${extension}`) {
361
- fileList.push(fullPath);
362
- }
363
- });
364
313
  return fileList;
365
314
  }
366
315
  function extractListOfFiles(opts) {
367
- if (!import_fs.default.existsSync(opts.path)) {
316
+ const logger = new Logger(extractListOfFiles.name);
317
+ if (!import_node_fs.default.existsSync(opts.path)) {
368
318
  throw new Error(`Provided path does not exist: ${opts.path}`);
369
319
  }
370
- const stats = import_fs.default.statSync(opts.path);
320
+ const stats = import_node_fs.default.statSync(opts.path);
371
321
  if (stats.isDirectory()) {
372
- Logger.info(`Provided path is a directory: ${opts.path}`);
373
- const files = opts.recursive && listFilesRecursively(opts.path, opts.extension ?? "sarif") || import_fs.default.readdirSync(opts.path);
374
- Logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`);
375
- Logger.debug(`Found files: ${files.join(", ")}`);
322
+ logger.info(`Provided path is a directory: ${opts.path}`);
323
+ const files = listFiles(opts.path, !!opts.recursive, opts.extension ?? "sarif");
324
+ logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`);
325
+ logger.debug(`Found files: ${files.join(", ")}`);
376
326
  return files;
377
327
  }
378
328
  if (stats.isFile()) {
379
- Logger.info(`Provided path is a file: ${opts.path}`);
329
+ logger.info(`Provided path is a file: ${opts.path}`);
380
330
  return [opts.path];
381
331
  }
382
332
  throw new Error(`Provided path is neither a file nor a directory: ${opts.path}`);
383
333
  }
384
334
 
335
+ // src/utils/ExtendedArray.ts
336
+ var ExtendedArray = class extends Array {
337
+ findByProperty(prop, value) {
338
+ return this.find((v) => v[prop] === value);
339
+ }
340
+ };
341
+
342
+ // src/model/FindingArray.ts
343
+ var FindingArray = class extends ExtendedArray {
344
+ hasSeverityOrHigher(severity) {
345
+ return Object.values(SecuritySeverity).filter((v) => typeof v === "number").filter((v) => v >= severity).some((v) => this.findByProperty("severity", v) != null);
346
+ }
347
+ hasLevelOrHigher(level) {
348
+ return Object.values(SecurityLevel).filter((v) => typeof v === "number").filter((v) => v >= level).some((v) => this.findByProperty("level", v) != null);
349
+ }
350
+ };
351
+
385
352
  // src/utils/Comparators.ts
386
353
  function findingsComparatorByKey(key) {
387
354
  return (a, b) => {
@@ -400,23 +367,6 @@ function findingsComparatorByKey(key) {
400
367
  };
401
368
  }
402
369
 
403
- // src/utils/ExtendedArray.ts
404
- var ExtendedArray = class extends Array {
405
- findByProperty(prop, value) {
406
- return this.find((v) => v[prop] === value);
407
- }
408
- };
409
-
410
- // src/model/FindingArray.ts
411
- var FindingArray = class extends ExtendedArray {
412
- hasSeverityOrHigher(severity) {
413
- return Object.values(SecuritySeverity).filter((v) => typeof v === "number").filter((v) => v >= severity).some((v) => this.findByProperty("severity", v) != null);
414
- }
415
- hasLevelOrHigher(level) {
416
- return Object.values(SecurityLevel).filter((v) => typeof v === "number").filter((v) => v >= level).some((v) => this.findByProperty("level", v) != null);
417
- }
418
- };
419
-
420
370
  // src/representations/Representation.ts
421
371
  var Representation = class {
422
372
  _model;
@@ -433,6 +383,9 @@ var Representation = class {
433
383
  italic(text) {
434
384
  return `_${text}_`;
435
385
  }
386
+ codeBlock(text) {
387
+ return "```\n" + text + "\n```";
388
+ }
436
389
  };
437
390
 
438
391
  // src/representations/CompactGroupByRepresentation.ts
@@ -454,7 +407,7 @@ ${summary}`;
454
407
  const result = [];
455
408
  findings.reduce((grouped, f) => {
456
409
  if (!grouped.get(f[key])) {
457
- grouped.set(f[key], new Array());
410
+ grouped.set(f[key], []);
458
411
  }
459
412
  grouped.get(f[key])?.push(f);
460
413
  return grouped;
@@ -606,6 +559,306 @@ var CompactTotalPerLevelRepresentation = class extends CompactTotalRepresentatio
606
559
  }
607
560
  };
608
561
 
562
+ // src/representations/table/Column.ts
563
+ var Column = class {
564
+ constructor(header, cellsCount) {
565
+ this.header = header;
566
+ this._cells = new Array(cellsCount);
567
+ }
568
+ _logger = new Logger("Column");
569
+ _cells;
570
+ get total() {
571
+ return this._cells.reduce((sum, c) => {
572
+ sum += Number(c.value);
573
+ return sum;
574
+ }, 0);
575
+ }
576
+ get width() {
577
+ return Math.max(
578
+ ...this._cells.map((c) => c.getWidth()),
579
+ this.total.toString().length
580
+ );
581
+ }
582
+ setCell(index, value) {
583
+ if (index >= 0 && index < this._cells.length) {
584
+ this._cells[index] = value;
585
+ const width = this.width;
586
+ this._cells.forEach((c) => c.setWidth(width));
587
+ } else {
588
+ this._logger.warn(`Cell index out of range. Requested index: ${index}. Cells count: ${this._cells.length}.`);
589
+ }
590
+ }
591
+ };
592
+
593
+ // src/representations/table/Cell.ts
594
+ var Cell = class {
595
+ constructor(_value = 0) {
596
+ this._value = _value;
597
+ this._width = _value.toString().length;
598
+ }
599
+ _width;
600
+ setWidth(width) {
601
+ this._width = width > this._width ? width : this._width;
602
+ }
603
+ getWidth() {
604
+ return this._width;
605
+ }
606
+ get value() {
607
+ return this._value;
608
+ }
609
+ toString() {
610
+ const str = this._value.toString();
611
+ const repeatCount = this._width - str.length;
612
+ return `${str}${repeatCount > 0 ? " ".repeat(repeatCount) : ""}`;
613
+ }
614
+ };
615
+
616
+ // src/representations/table/Row.ts
617
+ var Row = class {
618
+ constructor(_header, headerWidth, cellsCount) {
619
+ this._header = _header;
620
+ this.headerWidth = headerWidth;
621
+ this._cells = Array.from({ length: cellsCount }, () => new Cell());
622
+ this._totalWidth = 1;
623
+ }
624
+ _logger = new Logger("Row");
625
+ _cells;
626
+ _totalWidth;
627
+ get total() {
628
+ return this._cells.reduce((sum, c) => {
629
+ sum += Number(c.value);
630
+ return sum;
631
+ }, 0);
632
+ }
633
+ setCell(index, value) {
634
+ if (index >= 0 && index < this._cells.length) {
635
+ this._cells[index] = value;
636
+ } else {
637
+ this._logger.warn(`Setting cell failed. Reason: index out of range. Requested index: ${index}. Cells count: ${this._cells.length}.`);
638
+ }
639
+ }
640
+ get totalWidth() {
641
+ return this._totalWidth;
642
+ }
643
+ setTotalWidth(value) {
644
+ this._totalWidth = value;
645
+ }
646
+ toString() {
647
+ const result = [];
648
+ result.push(`${this._header}${this.headerWidth > this._header.length ? " ".repeat(this.headerWidth - this._header.length) : ""}`);
649
+ this._cells.map((c) => c.toString()).forEach((v) => result.push(v));
650
+ const totalStr = this.total.toString();
651
+ result.push(`${totalStr}${this._totalWidth > totalStr.length ? " ".repeat(this._totalWidth - totalStr.length) : ""}`);
652
+ return `|${result.join("|")}|`;
653
+ }
654
+ };
655
+
656
+ // src/representations/table/Table.ts
657
+ var HEADER_TOTAL = "Total";
658
+ var Table = class {
659
+ header;
660
+ columns;
661
+ rows;
662
+ constructor(headers) {
663
+ this.header = headers.main ?? "";
664
+ this.columns = Array.from(
665
+ { length: headers.columns.length },
666
+ (_, index) => new Column(headers.columns[index], headers.rows.length)
667
+ );
668
+ const headerWidth = Math.max(
669
+ this.header.length,
670
+ ...headers.rows.map((v) => v.length),
671
+ HEADER_TOTAL.length
672
+ );
673
+ this.rows = Array.from(
674
+ { length: headers.rows.length },
675
+ (_, index) => new Row(headers.rows[index], headerWidth, headers.columns.length)
676
+ );
677
+ }
678
+ set(rowIndex, columnIndex, v) {
679
+ if (rowIndex >= 0 && rowIndex < this.rows.length && columnIndex >= 0 && columnIndex < this.columns.length) {
680
+ const cell = new Cell(v);
681
+ cell.setWidth(this.columns[columnIndex].header.length);
682
+ this.columns[columnIndex].setCell(rowIndex, cell);
683
+ this.rows[rowIndex].setCell(columnIndex, cell);
684
+ const rowTotalWidth = Math.max(
685
+ // Based on the sum of all total values
686
+ this.rows.reduce((sum, r) => {
687
+ sum += r.total;
688
+ return sum;
689
+ }, 0).toString().length,
690
+ // Based on the width of "Total" header
691
+ HEADER_TOTAL.length
692
+ );
693
+ this.rows.forEach((r) => r.setTotalWidth(rowTotalWidth));
694
+ }
695
+ }
696
+ toString() {
697
+ const rowsStr = [];
698
+ if (this.rows.length > 0 && this.columns.length > 0) {
699
+ this.rows.forEach((row) => rowsStr.push(row.toString()));
700
+ const rowSeparator = rowsStr[0].replace(/[^|]/g, "-");
701
+ rowsStr.unshift(rowSeparator);
702
+ rowsStr.push(rowSeparator);
703
+ const rowTotal = [];
704
+ let sumTotal = 0;
705
+ for (const column of this.columns) {
706
+ const total = column.total;
707
+ rowTotal.push(`${total}${total.toString().length < column.width ? " ".repeat(column.width - total.toString().length) : ""}`);
708
+ sumTotal += total;
709
+ }
710
+ const column1 = `${HEADER_TOTAL}${this.rows[0].headerWidth > HEADER_TOTAL.length ? " ".repeat(this.rows[0].headerWidth - HEADER_TOTAL.length) : ""}`;
711
+ const columnLast = `${sumTotal}${sumTotal.toString().length < HEADER_TOTAL.length ? " ".repeat(HEADER_TOTAL.length - sumTotal.toString().length) : ""}`;
712
+ rowsStr.push(`|${column1}|${rowTotal.join("|")}|${columnLast}|`);
713
+ const rowTop = [
714
+ this.header + (this.header.length < this.rows[0].headerWidth ? " ".repeat(this.rows[0].headerWidth - this.header.length) : ""),
715
+ this.columns.map((c) => `${c.header}${c.header.length < c.width ? " ".repeat(c.width - c.header.length) : ""}`).join("|"),
716
+ HEADER_TOTAL + (HEADER_TOTAL.length < this.rows[0].totalWidth ? " ".repeat(this.rows[0].totalWidth - HEADER_TOTAL.length) : "")
717
+ ];
718
+ rowsStr.unshift(`|${rowTop.join("|")}|`);
719
+ }
720
+ return rowsStr.join("\n").replace(/[|]/g, " | ");
721
+ }
722
+ };
723
+
724
+ // src/representations/TableGroupRepresentation.ts
725
+ var TableGroupRepresentation = class extends Representation {
726
+ constructor(_keyBy, _keyPer, _values, model) {
727
+ super(model, "toolName");
728
+ this._keyBy = _keyBy;
729
+ this._keyPer = _keyPer;
730
+ this._values = _values;
731
+ }
732
+ _logger = new Logger("TableGroupRepresentation");
733
+ groupFindingsPer(findings) {
734
+ return findings.reduce(
735
+ (grouped, f) => {
736
+ grouped[f[this._keyPer]].push(f);
737
+ return grouped;
738
+ },
739
+ Array.from({ length: this._values.length }, () => [])
740
+ );
741
+ }
742
+ keyByToString(key) {
743
+ return key.toString();
744
+ }
745
+ groupFindingsBy(findings) {
746
+ return findings.reduce(
747
+ (grouped, f) => {
748
+ const key = this.keyByToString(f[this._keyBy]);
749
+ if (!grouped.has(key)) {
750
+ grouped.set(key, []);
751
+ }
752
+ grouped.get(key)?.push(f);
753
+ return grouped;
754
+ },
755
+ /* @__PURE__ */ new Map()
756
+ );
757
+ }
758
+ get title() {
759
+ switch (this._keyBy) {
760
+ case "toolName":
761
+ return "Tool";
762
+ case "runId":
763
+ return "Run #";
764
+ case "sarifPath":
765
+ return "File #";
766
+ default:
767
+ return "";
768
+ }
769
+ }
770
+ compose() {
771
+ const groupedBy = this.groupFindingsBy(this._model.findings);
772
+ const table = new Table({
773
+ main: this.title,
774
+ rows: Array.from(groupedBy.keys()),
775
+ columns: this._values
776
+ });
777
+ let i = 0;
778
+ for (const findings of groupedBy.values()) {
779
+ const grouped = this.groupFindingsPer(findings);
780
+ for (let j = 0; j < grouped.length; j++) {
781
+ table.set(i, j, grouped[j].length);
782
+ }
783
+ i++;
784
+ }
785
+ const result = this.codeBlock(table.toString());
786
+ this._logger.trace(result);
787
+ return result;
788
+ }
789
+ };
790
+
791
+ // src/representations/TableGroupByToolNameRepresentation.ts
792
+ var TableGroupByToolNameRepresentation = class extends TableGroupRepresentation {
793
+ constructor(keyPer, values, model) {
794
+ super("toolName", keyPer, values, model);
795
+ }
796
+ };
797
+
798
+ // src/representations/TableGroupByToolNamePerLevelRepresentation.ts
799
+ var TableGroupByToolNamePerLevelRepresentation = class extends TableGroupByToolNameRepresentation {
800
+ constructor(model) {
801
+ super("level", SecurityLevelValues, model);
802
+ }
803
+ };
804
+
805
+ // src/representations/TableGroupByToolNamePerSeverityRepresentation.ts
806
+ var TableGroupByToolNamePerSeverityRepresentation = class extends TableGroupByToolNameRepresentation {
807
+ constructor(model) {
808
+ super("severity", SecuritySeverityValues, model);
809
+ }
810
+ };
811
+
812
+ // src/representations/TableGroupByRunRepresentation.ts
813
+ var TableGroupByRunRepresentation = class extends TableGroupRepresentation {
814
+ constructor(keyPer, values, model) {
815
+ super("runId", keyPer, values, model);
816
+ }
817
+ };
818
+
819
+ // src/representations/TableGroupByRunPerLevelRepresentation.ts
820
+ var TableGroupByRunPerLevelRepresentation = class extends TableGroupByRunRepresentation {
821
+ constructor(model) {
822
+ super("level", SecurityLevelValues, model);
823
+ }
824
+ };
825
+
826
+ // src/representations/TableGroupByRunPerSeverityRepresentation.ts
827
+ var TableGroupByRunPerSeverityRepresentation = class extends TableGroupByRunRepresentation {
828
+ constructor(model) {
829
+ super("severity", SecuritySeverityValues, model);
830
+ }
831
+ };
832
+
833
+ // src/representations/TableGroupBySarifRepresentation.ts
834
+ var TableGroupBySarifRepresentation = class extends TableGroupRepresentation {
835
+ _fileToNumberMap = /* @__PURE__ */ new Map();
836
+ constructor(keyPer, values, model) {
837
+ super("sarifPath", keyPer, values, model);
838
+ }
839
+ keyByToString(key) {
840
+ const keyStr = key.toString();
841
+ if (!this._fileToNumberMap.has(keyStr)) {
842
+ this._fileToNumberMap.set(keyStr, this._fileToNumberMap.size + 1);
843
+ }
844
+ return this._fileToNumberMap.get(keyStr)?.toString();
845
+ }
846
+ };
847
+
848
+ // src/representations/TableGroupBySarifPerLevelRepresentation.ts
849
+ var TableGroupBySarifPerLevelRepresentation = class extends TableGroupBySarifRepresentation {
850
+ constructor(model) {
851
+ super("level", SecurityLevelValues, model);
852
+ }
853
+ };
854
+
855
+ // src/representations/TableGroupBySarifPerSeverityRepresentation.ts
856
+ var TableGroupBySarifPerSeverityRepresentation = class extends TableGroupBySarifRepresentation {
857
+ constructor(model) {
858
+ super("severity", SecuritySeverityValues, model);
859
+ }
860
+ };
861
+
609
862
  // src/representations/RepresentationFactory.ts
610
863
  function createRepresentation(model, type = 3 /* CompactGroupByToolNamePerSeverity */) {
611
864
  switch (type) {
@@ -625,6 +878,18 @@ function createRepresentation(model, type = 3 /* CompactGroupByToolNamePerSeveri
625
878
  return new CompactTotalPerLevelRepresentation(model);
626
879
  case 7 /* CompactTotalPerSeverity */:
627
880
  return new CompactTotalPerSeverityRepresentation(model);
881
+ case 8 /* TableGroupByRunPerLevel */:
882
+ return new TableGroupByRunPerLevelRepresentation(model);
883
+ case 9 /* TableGroupByRunPerSeverity */:
884
+ return new TableGroupByRunPerSeverityRepresentation(model);
885
+ case 10 /* TableGroupByToolNamePerLevel */:
886
+ return new TableGroupByToolNamePerLevelRepresentation(model);
887
+ case 11 /* TableGroupByToolNamePerSeverity */:
888
+ return new TableGroupByToolNamePerSeverityRepresentation(model);
889
+ case 12 /* TableGroupBySarifPerLevel */:
890
+ return new TableGroupBySarifPerLevelRepresentation(model);
891
+ case 13 /* TableGroupBySarifPerSeverity */:
892
+ return new TableGroupBySarifPerSeverityRepresentation(model);
628
893
  default:
629
894
  throw new Error(`Unknown representation type: ${type}`);
630
895
  }
@@ -763,6 +1028,7 @@ function createFinding(opts) {
763
1028
  return new FindingImpl(opts);
764
1029
  }
765
1030
  var FindingImpl = class {
1031
+ _logger = new Logger("FindingImpl");
766
1032
  _runMetadata;
767
1033
  _result;
768
1034
  _sarifPath;
@@ -808,7 +1074,7 @@ var FindingImpl = class {
808
1074
  this._levelCache = this._processor.tryFindLevel();
809
1075
  }
810
1076
  if (this._levelCache === void 0) {
811
- Logger.debug(`Unknown level of ${this._rule?.id} rule`);
1077
+ this._logger.debug(`Unknown level of ${this._rule?.id} rule`);
812
1078
  return 0 /* Unknown */;
813
1079
  }
814
1080
  switch (this._levelCache) {
@@ -824,7 +1090,7 @@ var FindingImpl = class {
824
1090
  }
825
1091
  get severity() {
826
1092
  if (this.cvssScore == null || this.cvssScore < 0 || this.cvssScore > 10) {
827
- Logger.debug(`Unsupported CVSS score ${this.cvssScore} in ${this._rule?.id} rule`);
1093
+ this._logger.debug(`Unsupported CVSS score ${this.cvssScore} in ${this._rule?.id} rule`);
828
1094
  return 0 /* Unknown */;
829
1095
  }
830
1096
  if (this.cvssScore >= 9) {
@@ -844,167 +1110,257 @@ var FindingImpl = class {
844
1110
  };
845
1111
 
846
1112
  // src/model/color/ColorIdentification.ts
847
- function logColorTaken(color, prop) {
848
- Logger.debug(`Message has ${color?.color} color taken from '${prop}' property.`);
1113
+ function logColorTaken(logger, color, prop) {
1114
+ logger.debug(`Message has ${color?.color} color taken from '${prop}' property.`);
849
1115
  }
850
- function logPropDefinedButNoFindings(key, val) {
1116
+ function logPropDefinedButNoFindings(logger, key, val) {
851
1117
  const prop = key === "level" ? "byLevel" : "bySeverity";
852
- Logger.trace(`'${prop}.${val}' property is defined but no findings with "${val}" ${key} is found. Continue color identification...`);
1118
+ logger.trace(`'${prop}.${val}' property is defined but no findings with "${val}" ${key} is found. Continue color identification...`);
853
1119
  }
854
- function logPropIsNotDefined(key, val) {
1120
+ function logPropIsNotDefined(logger, key, val) {
855
1121
  const prop = key === "level" ? "byLevel" : "bySeverity";
856
- Logger.trace(`'${prop}.${val}' property is not defined. Continue color identification...`);
1122
+ logger.trace(`'${prop}.${val}' property is not defined. Continue color identification...`);
857
1123
  }
858
1124
  function identifyColorCommon(findings, prop, none, unknown, color) {
1125
+ const logger = new Logger(identifyColorCommon.name);
859
1126
  if (color.none) {
860
1127
  if (findings.findByProperty(prop, none) != null) {
861
- logColorTaken(color.none, `${prop === "severity" ? "bySeverity" : "byLevel"}.none`);
1128
+ logColorTaken(logger, color.none, `${prop === "severity" ? "bySeverity" : "byLevel"}.none`);
862
1129
  return color.none.color;
863
1130
  } else {
864
- logPropDefinedButNoFindings(prop, "none");
1131
+ logPropDefinedButNoFindings(logger, prop, "none");
865
1132
  }
866
1133
  } else {
867
- logPropIsNotDefined(prop, "none");
1134
+ logPropIsNotDefined(logger, prop, "none");
868
1135
  }
869
1136
  if (color.unknown) {
870
1137
  if (findings.findByProperty(prop, unknown) != null) {
871
- logColorTaken(color.unknown, `${prop === "severity" ? "bySeverity" : "byLevel"}.unknown`);
1138
+ logColorTaken(logger, color.unknown, `${prop === "severity" ? "bySeverity" : "byLevel"}.unknown`);
872
1139
  return color.unknown.color;
873
1140
  } else {
874
- logPropDefinedButNoFindings(prop, "unknown");
1141
+ logPropDefinedButNoFindings(logger, prop, "unknown");
875
1142
  }
876
1143
  } else {
877
- logPropIsNotDefined(prop, "unknown");
1144
+ logPropIsNotDefined(logger, prop, "unknown");
878
1145
  }
879
1146
  return void 0;
880
1147
  }
881
1148
  function identifyColorBySeverity(findings, color) {
1149
+ const logger = new Logger(identifyColorBySeverity.name);
882
1150
  if (color.critical) {
883
1151
  if (findings.findByProperty("severity", 5 /* Critical */) != null) {
884
- logColorTaken(color.critical, "bySeverity.critical");
1152
+ logColorTaken(logger, color.critical, "bySeverity.critical");
885
1153
  return color.critical.color;
886
1154
  } else {
887
- logPropDefinedButNoFindings("severity", "critical");
1155
+ logPropDefinedButNoFindings(logger, "severity", "critical");
888
1156
  }
889
1157
  } else {
890
- logPropIsNotDefined("severity", "critical");
1158
+ logPropIsNotDefined(logger, "severity", "critical");
891
1159
  }
892
1160
  if (color.high) {
893
1161
  if (findings.findByProperty("severity", 4 /* High */) != null) {
894
- logColorTaken(color.high, "bySeverity.high");
1162
+ logColorTaken(logger, color.high, "bySeverity.high");
895
1163
  return color.high.color;
896
1164
  } else {
897
- logPropDefinedButNoFindings("severity", "high");
1165
+ logPropDefinedButNoFindings(logger, "severity", "high");
898
1166
  }
899
1167
  } else {
900
- logPropIsNotDefined("severity", "high");
1168
+ logPropIsNotDefined(logger, "severity", "high");
901
1169
  }
902
1170
  if (color.medium) {
903
1171
  if (findings.findByProperty("severity", 3 /* Medium */) != null) {
904
- logColorTaken(color.medium, "bySeverity.medium");
1172
+ logColorTaken(logger, color.medium, "bySeverity.medium");
905
1173
  return color.medium.color;
906
1174
  } else {
907
- logPropDefinedButNoFindings("severity", "medium");
1175
+ logPropDefinedButNoFindings(logger, "severity", "medium");
908
1176
  }
909
1177
  } else {
910
- logPropIsNotDefined("severity", "medium");
1178
+ logPropIsNotDefined(logger, "severity", "medium");
911
1179
  }
912
1180
  if (color.low) {
913
1181
  if (findings.findByProperty("severity", 2 /* Low */) != null) {
914
- logColorTaken(color.low, "bySeverity.low");
1182
+ logColorTaken(logger, color.low, "bySeverity.low");
915
1183
  return color.low.color;
916
1184
  } else {
917
- logPropDefinedButNoFindings("severity", "low");
1185
+ logPropDefinedButNoFindings(logger, "severity", "low");
918
1186
  }
919
1187
  } else {
920
- logPropIsNotDefined("severity", "low");
1188
+ logPropIsNotDefined(logger, "severity", "low");
921
1189
  }
922
1190
  return identifyColorCommon(findings, "severity", 1 /* None */, 0 /* Unknown */, color);
923
1191
  }
924
1192
  function identifyColorByLevel(findings, color) {
1193
+ const logger = new Logger(identifyColorByLevel.name);
925
1194
  if (color.error) {
926
1195
  if (findings.findByProperty("level", 4 /* Error */) != null) {
927
- logColorTaken(color.error, "byLevel.error");
1196
+ logColorTaken(logger, color.error, "byLevel.error");
928
1197
  return color.error.color;
929
1198
  } else {
930
- logPropDefinedButNoFindings("level", "error");
1199
+ logPropDefinedButNoFindings(logger, "level", "error");
931
1200
  }
932
1201
  } else {
933
- logPropIsNotDefined("level", "error");
1202
+ logPropIsNotDefined(logger, "level", "error");
934
1203
  }
935
1204
  if (color.warning) {
936
1205
  if (findings.findByProperty("level", 3 /* Warning */) != null) {
937
- logColorTaken(color.warning, "byLevel.warning");
1206
+ logColorTaken(logger, color.warning, "byLevel.warning");
938
1207
  return color.warning.color;
939
1208
  } else {
940
- logPropDefinedButNoFindings("level", "warning");
1209
+ logPropDefinedButNoFindings(logger, "level", "warning");
941
1210
  }
942
1211
  } else {
943
- logPropIsNotDefined("level", "warning");
1212
+ logPropIsNotDefined(logger, "level", "warning");
944
1213
  }
945
1214
  if (color.note != null) {
946
1215
  if (findings.findByProperty("level", 2 /* Note */) != null) {
947
- logColorTaken(color.note, "byLevel.note");
1216
+ logColorTaken(logger, color.note, "byLevel.note");
948
1217
  return color.note.color;
949
1218
  } else {
950
- logPropDefinedButNoFindings("level", "note");
1219
+ logPropDefinedButNoFindings(logger, "level", "note");
951
1220
  }
952
1221
  } else {
953
- logPropIsNotDefined("level", "note");
1222
+ logPropIsNotDefined(logger, "level", "note");
954
1223
  }
955
1224
  return identifyColorCommon(findings, "level", 1 /* None */, 0 /* Unknown */, color);
956
1225
  }
957
1226
  function identifyColor(findings, colorOpts) {
1227
+ const logger = new Logger(identifyColor.name);
958
1228
  if (!colorOpts) {
959
- Logger.debug("Message has no color as color options are not defined.");
1229
+ logger.debug("Message has no color as color options are not defined.");
960
1230
  return void 0;
961
1231
  }
962
- Logger.trace(`Identifying color for ${findings.length} findings and the following color options:`, JSON.stringify(colorOpts, null, 2));
1232
+ logger.trace(`Identifying color for ${findings.length} findings and the following color options:`, JSON.stringify(colorOpts, null, 2));
963
1233
  if (colorOpts.bySeverity) {
964
1234
  const color = identifyColorBySeverity(findings, colorOpts.bySeverity);
965
1235
  if (color) {
966
1236
  return color;
967
1237
  }
968
- Logger.trace("None of the properties in 'bySeverity' group is applicable. Continue color identification...");
1238
+ logger.trace("None of the properties in 'bySeverity' group is applicable. Continue color identification...");
969
1239
  } else {
970
- Logger.trace("'bySeverity' group is not defined. Continue color identification...");
1240
+ logger.trace("'bySeverity' group is not defined. Continue color identification...");
971
1241
  }
972
1242
  if (colorOpts.byLevel) {
973
1243
  const color = identifyColorByLevel(findings, colorOpts.byLevel);
974
1244
  if (color) {
975
1245
  return color;
976
1246
  }
977
- Logger.trace("None of the properties in 'byLevel' group is applicable. Continue color identification...");
1247
+ logger.trace("None of the properties in 'byLevel' group is applicable. Continue color identification...");
978
1248
  } else {
979
- Logger.trace("'byLevel' group is not defined. Continue color identification...");
1249
+ logger.trace("'byLevel' group is not defined. Continue color identification...");
980
1250
  }
981
1251
  if (findings.length === 0) {
982
- Logger.trace('There are no findings in the provided SARIF file(s). Checking if color is defined in "empty" property...');
1252
+ logger.trace('There are no findings in the provided SARIF file(s). Checking if color is defined in "empty" property...');
983
1253
  if (colorOpts.empty?.color) {
984
- logColorTaken(colorOpts.empty, "empty");
1254
+ logColorTaken(logger, colorOpts.empty, "empty");
985
1255
  return colorOpts.empty.color;
986
1256
  } else {
987
- Logger.trace('"empty" color is not defined. Continue color identification...');
1257
+ logger.trace('"empty" color is not defined. Continue color identification...');
988
1258
  }
989
1259
  } else {
990
- Logger.trace(`"empty" color is not taken into account because there are ${findings.length} findings in the provided SARIF file(s). Continue color identification...`);
1260
+ logger.trace(`"empty" color is not taken into account because there are ${findings.length} findings in the provided SARIF file(s). Continue color identification...`);
991
1261
  }
992
1262
  if (colorOpts.default?.color) {
993
- logColorTaken(colorOpts.default, "default");
1263
+ logColorTaken(logger, colorOpts.default, "default");
994
1264
  } else {
995
- Logger.debug("Message has no color as none of the defined color options is applicable.");
1265
+ logger.debug("Message has no color as none of the defined color options is applicable.");
996
1266
  }
997
1267
  return colorOpts?.default?.color;
998
1268
  }
999
1269
 
1270
+ // src/model/SlackMessage.ts
1271
+ var import_webhook = require("@slack/webhook");
1272
+ function createSlackMessage(url, opts) {
1273
+ return new SlackMessageImpl(url, opts);
1274
+ }
1275
+ var SlackMessageImpl = class {
1276
+ _webhook;
1277
+ _gitHubServerUrl;
1278
+ _color;
1279
+ _representation;
1280
+ _header;
1281
+ _footer;
1282
+ _actor;
1283
+ _runId;
1284
+ constructor(url, opts) {
1285
+ this._webhook = new import_webhook.IncomingWebhook(url, {
1286
+ username: opts.username || "SARIF results",
1287
+ icon_url: opts.iconUrl
1288
+ });
1289
+ this._gitHubServerUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
1290
+ this._color = opts.color;
1291
+ this._representation = opts.representation;
1292
+ }
1293
+ withHeader(header) {
1294
+ this._header = {
1295
+ type: "header",
1296
+ text: {
1297
+ type: "plain_text",
1298
+ text: header || process.env.GITHUB_REPOSITORY || "SARIF results"
1299
+ }
1300
+ };
1301
+ }
1302
+ withActor(actor) {
1303
+ this._actor = actor || process.env.GITHUB_ACTOR;
1304
+ }
1305
+ withRun() {
1306
+ this._runId = process.env.GITHUB_RUN_ID;
1307
+ }
1308
+ withFooter(text, type) {
1309
+ const repoName = "fabasoad/sarif-to-slack";
1310
+ const element = text ? { type: type || "plain_text" /* PlainText */, text } : { type: "mrkdwn" /* Markdown */, text: `Generated by <${this._gitHubServerUrl}/${repoName}|@${repoName}@${version}>` };
1311
+ this._footer = {
1312
+ type: "context",
1313
+ elements: [element]
1314
+ };
1315
+ }
1316
+ async send() {
1317
+ const blocks = [];
1318
+ if (this._header) {
1319
+ blocks.push(this._header);
1320
+ }
1321
+ blocks.push({
1322
+ type: "section",
1323
+ text: {
1324
+ type: "mrkdwn",
1325
+ text: this.buildText()
1326
+ }
1327
+ });
1328
+ if (this._footer) {
1329
+ blocks.push(this._footer);
1330
+ }
1331
+ const { text } = await this._webhook.send({
1332
+ attachments: [{ color: this._color, blocks }]
1333
+ });
1334
+ return text;
1335
+ }
1336
+ buildText() {
1337
+ const text = [];
1338
+ if (this._actor) {
1339
+ const actorUrl = `${this._gitHubServerUrl}/${this._actor}`;
1340
+ text.push(`_Triggered by <${actorUrl}|${this._actor}>_`);
1341
+ }
1342
+ text.push(this._representation.compose());
1343
+ if (this._runId) {
1344
+ let runText = "Job ";
1345
+ if (process.env.GITHUB_REPOSITORY) {
1346
+ runText += `<${this._gitHubServerUrl}/${process.env.GITHUB_REPOSITORY}/actions/runs/${this._runId}|#${this._runId}>`;
1347
+ } else {
1348
+ runText += `#${this._runId}`;
1349
+ }
1350
+ text.push(runText);
1351
+ }
1352
+ return text.join("\n\n");
1353
+ }
1354
+ };
1355
+
1000
1356
  // src/SarifToSlackClient.ts
1001
1357
  var SarifToSlackClient = class _SarifToSlackClient {
1358
+ _logger = new Logger("SarifToSlackClient");
1002
1359
  _message;
1003
1360
  _sarifModel;
1004
1361
  _sendIf = 20 /* Always */;
1005
- constructor(log) {
1006
- Logger.initialize(log);
1007
- System.initialize();
1362
+ constructor() {
1363
+ logMetadata();
1008
1364
  }
1009
1365
  static *createRunIdGenerator() {
1010
1366
  let runId = 1;
@@ -1012,11 +1368,22 @@ var SarifToSlackClient = class _SarifToSlackClient {
1012
1368
  yield runId++;
1013
1369
  }
1014
1370
  }
1015
- static async create(opts) {
1016
- const instance = new _SarifToSlackClient(opts.log);
1371
+ /**
1372
+ * Creates an instance of {@link SarifToSlackClient} class. It already has all
1373
+ * properties and fields initialized.
1374
+ * @param webhookUrl - Slack webhook URL.
1375
+ * @param opts - An instance of {@link SarifToSlackClientOptions} type.
1376
+ *
1377
+ * @see SarifToSlackClientOptions
1378
+ *
1379
+ * @public
1380
+ */
1381
+ static async create(webhookUrl, opts) {
1382
+ const instance = new _SarifToSlackClient();
1383
+ instance._logger.trace(opts);
1017
1384
  instance._sendIf = opts.sendIf ?? instance._sendIf;
1018
1385
  instance._sarifModel = await _SarifToSlackClient.buildModel(opts.sarif);
1019
- instance._message = await _SarifToSlackClient.initialize(instance._sarifModel, opts);
1386
+ instance._message = await _SarifToSlackClient.initialize(webhookUrl, opts, instance._sarifModel);
1020
1387
  return instance;
1021
1388
  }
1022
1389
  static async buildModel(sarifOpts) {
@@ -1027,11 +1394,11 @@ var SarifToSlackClient = class _SarifToSlackClient {
1027
1394
  const model = { sarifFiles, runs: [], findings: new FindingArray() };
1028
1395
  const runIdGenerator = _SarifToSlackClient.createRunIdGenerator();
1029
1396
  for (const sarifPath of sarifFiles) {
1030
- const sarifJson = await import_fs2.promises.readFile(sarifPath, "utf8");
1397
+ const sarifJson = await import_node_fs2.promises.readFile(sarifPath, "utf8");
1031
1398
  const sarifLog = JSON.parse(sarifJson);
1032
1399
  for (const run of sarifLog.runs) {
1033
1400
  const runId = runIdGenerator.next();
1034
- let runMetadata = void 0;
1401
+ let runMetadata;
1035
1402
  for (const result of run.results ?? []) {
1036
1403
  runMetadata = {
1037
1404
  id: runId.value,
@@ -1053,13 +1420,15 @@ var SarifToSlackClient = class _SarifToSlackClient {
1053
1420
  /**
1054
1421
  * The main function to initialize a list of {@link SlackMessage} objects based
1055
1422
  * on the given SARIF file(s).
1056
- * @param sarifModel - An instance of SarifModel object.
1423
+ * @param webhookUrl - Slack webhook URL.
1057
1424
  * @param opts - An instance of {@link SarifToSlackClientOptions} object.
1425
+ * @param sarifModel - An instance of SarifModel object.
1058
1426
  * @returns A map where key is the SARIF file and value is an instance of
1059
1427
  * {@link SlackMessage} object.
1428
+ * @internal
1060
1429
  */
1061
- static async initialize(sarifModel, opts) {
1062
- const message = createSlackMessage(opts.webhookUrl, {
1430
+ static async initialize(webhookUrl, opts, sarifModel) {
1431
+ const message = createSlackMessage(webhookUrl, {
1063
1432
  username: opts.username,
1064
1433
  iconUrl: opts.iconUrl,
1065
1434
  color: identifyColor(sarifModel.findings, opts.color),
@@ -1094,9 +1463,9 @@ var SarifToSlackClient = class _SarifToSlackClient {
1094
1463
  throw new Error("Slack message was not prepared.");
1095
1464
  }
1096
1465
  const text = await this._message.send();
1097
- Logger.info("Message sent. Status:", text);
1466
+ this._logger.info("Message sent. Status:", text);
1098
1467
  } else {
1099
- Logger.info(sendIfLogMessage(this._sendIf));
1468
+ this._logger.info(sendIfLogMessage(this._sendIf));
1100
1469
  }
1101
1470
  }
1102
1471
  get shouldSendMessage() {
@@ -1161,7 +1530,6 @@ var SarifToSlackClient = class _SarifToSlackClient {
1161
1530
  0 && (module.exports = {
1162
1531
  Color,
1163
1532
  FooterType,
1164
- LogLevel,
1165
1533
  RepresentationType,
1166
1534
  SarifToSlackClient,
1167
1535
  SendIf