@fabasoad/sarif-to-slack 1.3.1 → 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 +603 -237
  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 +8 -7
  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 +10 -10
  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 +20 -19
  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,151 +291,19 @@ var SecurityLevel = /* @__PURE__ */ ((SecurityLevel2) => {
212
291
  SecurityLevel2[SecurityLevel2["Error"] = 4] = "Error";
213
292
  return SecurityLevel2;
214
293
  })(SecurityLevel || {});
215
-
216
- // src/metadata.json
217
- var version = "1.3.1";
218
- var sha = "fac0524704ae1c62880212a04af4e48ac31297ed";
219
- var buildAt = "2025-08-25T09:16:54Z";
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()
276
- }
277
- });
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
- }
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
- };
294
+ var SecurityLevelValues = Object.values(SecurityLevel).filter(
295
+ (v) => typeof v === "string"
296
+ );
350
297
 
351
298
  // src/utils/FileUtils.ts
352
- var import_fs = __toESM(require("fs"));
299
+ var import_node_fs = __toESM(require("fs"));
353
300
  var path = __toESM(require("path"));
354
301
  function listFiles(dir, recursive, extension, fileList = []) {
355
- if (import_fs.default.statSync(dir).isDirectory()) {
356
- const entries = import_fs.default.readdirSync(dir);
302
+ if (import_node_fs.default.statSync(dir).isDirectory()) {
303
+ const entries = import_node_fs.default.readdirSync(dir);
357
304
  entries.forEach((entry) => {
358
305
  const fullPath = path.join(dir, entry);
359
- if (recursive && import_fs.default.statSync(fullPath).isDirectory()) {
306
+ if (recursive && import_node_fs.default.statSync(fullPath).isDirectory()) {
360
307
  listFiles(fullPath, recursive, extension, fileList);
361
308
  } else if (path.extname(fullPath).toLowerCase() === `.${extension}`) {
362
309
  fileList.push(fullPath);
@@ -366,24 +313,42 @@ function listFiles(dir, recursive, extension, fileList = []) {
366
313
  return fileList;
367
314
  }
368
315
  function extractListOfFiles(opts) {
369
- if (!import_fs.default.existsSync(opts.path)) {
316
+ const logger = new Logger(extractListOfFiles.name);
317
+ if (!import_node_fs.default.existsSync(opts.path)) {
370
318
  throw new Error(`Provided path does not exist: ${opts.path}`);
371
319
  }
372
- const stats = import_fs.default.statSync(opts.path);
320
+ const stats = import_node_fs.default.statSync(opts.path);
373
321
  if (stats.isDirectory()) {
374
- Logger.info(`Provided path is a directory: ${opts.path}`);
322
+ logger.info(`Provided path is a directory: ${opts.path}`);
375
323
  const files = listFiles(opts.path, !!opts.recursive, opts.extension ?? "sarif");
376
- Logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`);
377
- Logger.debug(`Found files: ${files.join(", ")}`);
324
+ logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`);
325
+ logger.debug(`Found files: ${files.join(", ")}`);
378
326
  return files;
379
327
  }
380
328
  if (stats.isFile()) {
381
- Logger.info(`Provided path is a file: ${opts.path}`);
329
+ logger.info(`Provided path is a file: ${opts.path}`);
382
330
  return [opts.path];
383
331
  }
384
332
  throw new Error(`Provided path is neither a file nor a directory: ${opts.path}`);
385
333
  }
386
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
+
387
352
  // src/utils/Comparators.ts
388
353
  function findingsComparatorByKey(key) {
389
354
  return (a, b) => {
@@ -402,23 +367,6 @@ function findingsComparatorByKey(key) {
402
367
  };
403
368
  }
404
369
 
405
- // src/utils/ExtendedArray.ts
406
- var ExtendedArray = class extends Array {
407
- findByProperty(prop, value) {
408
- return this.find((v) => v[prop] === value);
409
- }
410
- };
411
-
412
- // src/model/FindingArray.ts
413
- var FindingArray = class extends ExtendedArray {
414
- hasSeverityOrHigher(severity) {
415
- return Object.values(SecuritySeverity).filter((v) => typeof v === "number").filter((v) => v >= severity).some((v) => this.findByProperty("severity", v) != null);
416
- }
417
- hasLevelOrHigher(level) {
418
- return Object.values(SecurityLevel).filter((v) => typeof v === "number").filter((v) => v >= level).some((v) => this.findByProperty("level", v) != null);
419
- }
420
- };
421
-
422
370
  // src/representations/Representation.ts
423
371
  var Representation = class {
424
372
  _model;
@@ -435,6 +383,9 @@ var Representation = class {
435
383
  italic(text) {
436
384
  return `_${text}_`;
437
385
  }
386
+ codeBlock(text) {
387
+ return "```\n" + text + "\n```";
388
+ }
438
389
  };
439
390
 
440
391
  // src/representations/CompactGroupByRepresentation.ts
@@ -456,7 +407,7 @@ ${summary}`;
456
407
  const result = [];
457
408
  findings.reduce((grouped, f) => {
458
409
  if (!grouped.get(f[key])) {
459
- grouped.set(f[key], new Array());
410
+ grouped.set(f[key], []);
460
411
  }
461
412
  grouped.get(f[key])?.push(f);
462
413
  return grouped;
@@ -608,6 +559,306 @@ var CompactTotalPerLevelRepresentation = class extends CompactTotalRepresentatio
608
559
  }
609
560
  };
610
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
+
611
862
  // src/representations/RepresentationFactory.ts
612
863
  function createRepresentation(model, type = 3 /* CompactGroupByToolNamePerSeverity */) {
613
864
  switch (type) {
@@ -627,6 +878,18 @@ function createRepresentation(model, type = 3 /* CompactGroupByToolNamePerSeveri
627
878
  return new CompactTotalPerLevelRepresentation(model);
628
879
  case 7 /* CompactTotalPerSeverity */:
629
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);
630
893
  default:
631
894
  throw new Error(`Unknown representation type: ${type}`);
632
895
  }
@@ -765,6 +1028,7 @@ function createFinding(opts) {
765
1028
  return new FindingImpl(opts);
766
1029
  }
767
1030
  var FindingImpl = class {
1031
+ _logger = new Logger("FindingImpl");
768
1032
  _runMetadata;
769
1033
  _result;
770
1034
  _sarifPath;
@@ -810,7 +1074,7 @@ var FindingImpl = class {
810
1074
  this._levelCache = this._processor.tryFindLevel();
811
1075
  }
812
1076
  if (this._levelCache === void 0) {
813
- Logger.debug(`Unknown level of ${this._rule?.id} rule`);
1077
+ this._logger.debug(`Unknown level of ${this._rule?.id} rule`);
814
1078
  return 0 /* Unknown */;
815
1079
  }
816
1080
  switch (this._levelCache) {
@@ -826,7 +1090,7 @@ var FindingImpl = class {
826
1090
  }
827
1091
  get severity() {
828
1092
  if (this.cvssScore == null || this.cvssScore < 0 || this.cvssScore > 10) {
829
- 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`);
830
1094
  return 0 /* Unknown */;
831
1095
  }
832
1096
  if (this.cvssScore >= 9) {
@@ -846,167 +1110,257 @@ var FindingImpl = class {
846
1110
  };
847
1111
 
848
1112
  // src/model/color/ColorIdentification.ts
849
- function logColorTaken(color, prop) {
850
- 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.`);
851
1115
  }
852
- function logPropDefinedButNoFindings(key, val) {
1116
+ function logPropDefinedButNoFindings(logger, key, val) {
853
1117
  const prop = key === "level" ? "byLevel" : "bySeverity";
854
- 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...`);
855
1119
  }
856
- function logPropIsNotDefined(key, val) {
1120
+ function logPropIsNotDefined(logger, key, val) {
857
1121
  const prop = key === "level" ? "byLevel" : "bySeverity";
858
- Logger.trace(`'${prop}.${val}' property is not defined. Continue color identification...`);
1122
+ logger.trace(`'${prop}.${val}' property is not defined. Continue color identification...`);
859
1123
  }
860
1124
  function identifyColorCommon(findings, prop, none, unknown, color) {
1125
+ const logger = new Logger(identifyColorCommon.name);
861
1126
  if (color.none) {
862
1127
  if (findings.findByProperty(prop, none) != null) {
863
- logColorTaken(color.none, `${prop === "severity" ? "bySeverity" : "byLevel"}.none`);
1128
+ logColorTaken(logger, color.none, `${prop === "severity" ? "bySeverity" : "byLevel"}.none`);
864
1129
  return color.none.color;
865
1130
  } else {
866
- logPropDefinedButNoFindings(prop, "none");
1131
+ logPropDefinedButNoFindings(logger, prop, "none");
867
1132
  }
868
1133
  } else {
869
- logPropIsNotDefined(prop, "none");
1134
+ logPropIsNotDefined(logger, prop, "none");
870
1135
  }
871
1136
  if (color.unknown) {
872
1137
  if (findings.findByProperty(prop, unknown) != null) {
873
- logColorTaken(color.unknown, `${prop === "severity" ? "bySeverity" : "byLevel"}.unknown`);
1138
+ logColorTaken(logger, color.unknown, `${prop === "severity" ? "bySeverity" : "byLevel"}.unknown`);
874
1139
  return color.unknown.color;
875
1140
  } else {
876
- logPropDefinedButNoFindings(prop, "unknown");
1141
+ logPropDefinedButNoFindings(logger, prop, "unknown");
877
1142
  }
878
1143
  } else {
879
- logPropIsNotDefined(prop, "unknown");
1144
+ logPropIsNotDefined(logger, prop, "unknown");
880
1145
  }
881
1146
  return void 0;
882
1147
  }
883
1148
  function identifyColorBySeverity(findings, color) {
1149
+ const logger = new Logger(identifyColorBySeverity.name);
884
1150
  if (color.critical) {
885
1151
  if (findings.findByProperty("severity", 5 /* Critical */) != null) {
886
- logColorTaken(color.critical, "bySeverity.critical");
1152
+ logColorTaken(logger, color.critical, "bySeverity.critical");
887
1153
  return color.critical.color;
888
1154
  } else {
889
- logPropDefinedButNoFindings("severity", "critical");
1155
+ logPropDefinedButNoFindings(logger, "severity", "critical");
890
1156
  }
891
1157
  } else {
892
- logPropIsNotDefined("severity", "critical");
1158
+ logPropIsNotDefined(logger, "severity", "critical");
893
1159
  }
894
1160
  if (color.high) {
895
1161
  if (findings.findByProperty("severity", 4 /* High */) != null) {
896
- logColorTaken(color.high, "bySeverity.high");
1162
+ logColorTaken(logger, color.high, "bySeverity.high");
897
1163
  return color.high.color;
898
1164
  } else {
899
- logPropDefinedButNoFindings("severity", "high");
1165
+ logPropDefinedButNoFindings(logger, "severity", "high");
900
1166
  }
901
1167
  } else {
902
- logPropIsNotDefined("severity", "high");
1168
+ logPropIsNotDefined(logger, "severity", "high");
903
1169
  }
904
1170
  if (color.medium) {
905
1171
  if (findings.findByProperty("severity", 3 /* Medium */) != null) {
906
- logColorTaken(color.medium, "bySeverity.medium");
1172
+ logColorTaken(logger, color.medium, "bySeverity.medium");
907
1173
  return color.medium.color;
908
1174
  } else {
909
- logPropDefinedButNoFindings("severity", "medium");
1175
+ logPropDefinedButNoFindings(logger, "severity", "medium");
910
1176
  }
911
1177
  } else {
912
- logPropIsNotDefined("severity", "medium");
1178
+ logPropIsNotDefined(logger, "severity", "medium");
913
1179
  }
914
1180
  if (color.low) {
915
1181
  if (findings.findByProperty("severity", 2 /* Low */) != null) {
916
- logColorTaken(color.low, "bySeverity.low");
1182
+ logColorTaken(logger, color.low, "bySeverity.low");
917
1183
  return color.low.color;
918
1184
  } else {
919
- logPropDefinedButNoFindings("severity", "low");
1185
+ logPropDefinedButNoFindings(logger, "severity", "low");
920
1186
  }
921
1187
  } else {
922
- logPropIsNotDefined("severity", "low");
1188
+ logPropIsNotDefined(logger, "severity", "low");
923
1189
  }
924
1190
  return identifyColorCommon(findings, "severity", 1 /* None */, 0 /* Unknown */, color);
925
1191
  }
926
1192
  function identifyColorByLevel(findings, color) {
1193
+ const logger = new Logger(identifyColorByLevel.name);
927
1194
  if (color.error) {
928
1195
  if (findings.findByProperty("level", 4 /* Error */) != null) {
929
- logColorTaken(color.error, "byLevel.error");
1196
+ logColorTaken(logger, color.error, "byLevel.error");
930
1197
  return color.error.color;
931
1198
  } else {
932
- logPropDefinedButNoFindings("level", "error");
1199
+ logPropDefinedButNoFindings(logger, "level", "error");
933
1200
  }
934
1201
  } else {
935
- logPropIsNotDefined("level", "error");
1202
+ logPropIsNotDefined(logger, "level", "error");
936
1203
  }
937
1204
  if (color.warning) {
938
1205
  if (findings.findByProperty("level", 3 /* Warning */) != null) {
939
- logColorTaken(color.warning, "byLevel.warning");
1206
+ logColorTaken(logger, color.warning, "byLevel.warning");
940
1207
  return color.warning.color;
941
1208
  } else {
942
- logPropDefinedButNoFindings("level", "warning");
1209
+ logPropDefinedButNoFindings(logger, "level", "warning");
943
1210
  }
944
1211
  } else {
945
- logPropIsNotDefined("level", "warning");
1212
+ logPropIsNotDefined(logger, "level", "warning");
946
1213
  }
947
1214
  if (color.note != null) {
948
1215
  if (findings.findByProperty("level", 2 /* Note */) != null) {
949
- logColorTaken(color.note, "byLevel.note");
1216
+ logColorTaken(logger, color.note, "byLevel.note");
950
1217
  return color.note.color;
951
1218
  } else {
952
- logPropDefinedButNoFindings("level", "note");
1219
+ logPropDefinedButNoFindings(logger, "level", "note");
953
1220
  }
954
1221
  } else {
955
- logPropIsNotDefined("level", "note");
1222
+ logPropIsNotDefined(logger, "level", "note");
956
1223
  }
957
1224
  return identifyColorCommon(findings, "level", 1 /* None */, 0 /* Unknown */, color);
958
1225
  }
959
1226
  function identifyColor(findings, colorOpts) {
1227
+ const logger = new Logger(identifyColor.name);
960
1228
  if (!colorOpts) {
961
- 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.");
962
1230
  return void 0;
963
1231
  }
964
- 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));
965
1233
  if (colorOpts.bySeverity) {
966
1234
  const color = identifyColorBySeverity(findings, colorOpts.bySeverity);
967
1235
  if (color) {
968
1236
  return color;
969
1237
  }
970
- 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...");
971
1239
  } else {
972
- Logger.trace("'bySeverity' group is not defined. Continue color identification...");
1240
+ logger.trace("'bySeverity' group is not defined. Continue color identification...");
973
1241
  }
974
1242
  if (colorOpts.byLevel) {
975
1243
  const color = identifyColorByLevel(findings, colorOpts.byLevel);
976
1244
  if (color) {
977
1245
  return color;
978
1246
  }
979
- 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...");
980
1248
  } else {
981
- Logger.trace("'byLevel' group is not defined. Continue color identification...");
1249
+ logger.trace("'byLevel' group is not defined. Continue color identification...");
982
1250
  }
983
1251
  if (findings.length === 0) {
984
- 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...');
985
1253
  if (colorOpts.empty?.color) {
986
- logColorTaken(colorOpts.empty, "empty");
1254
+ logColorTaken(logger, colorOpts.empty, "empty");
987
1255
  return colorOpts.empty.color;
988
1256
  } else {
989
- Logger.trace('"empty" color is not defined. Continue color identification...');
1257
+ logger.trace('"empty" color is not defined. Continue color identification...');
990
1258
  }
991
1259
  } else {
992
- 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...`);
993
1261
  }
994
1262
  if (colorOpts.default?.color) {
995
- logColorTaken(colorOpts.default, "default");
1263
+ logColorTaken(logger, colorOpts.default, "default");
996
1264
  } else {
997
- 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.");
998
1266
  }
999
1267
  return colorOpts?.default?.color;
1000
1268
  }
1001
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
+
1002
1356
  // src/SarifToSlackClient.ts
1003
1357
  var SarifToSlackClient = class _SarifToSlackClient {
1358
+ _logger = new Logger("SarifToSlackClient");
1004
1359
  _message;
1005
1360
  _sarifModel;
1006
1361
  _sendIf = 20 /* Always */;
1007
- constructor(log) {
1008
- Logger.initialize(log);
1009
- System.initialize();
1362
+ constructor() {
1363
+ logMetadata();
1010
1364
  }
1011
1365
  static *createRunIdGenerator() {
1012
1366
  let runId = 1;
@@ -1014,11 +1368,22 @@ var SarifToSlackClient = class _SarifToSlackClient {
1014
1368
  yield runId++;
1015
1369
  }
1016
1370
  }
1017
- static async create(opts) {
1018
- 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);
1019
1384
  instance._sendIf = opts.sendIf ?? instance._sendIf;
1020
1385
  instance._sarifModel = await _SarifToSlackClient.buildModel(opts.sarif);
1021
- instance._message = await _SarifToSlackClient.initialize(instance._sarifModel, opts);
1386
+ instance._message = await _SarifToSlackClient.initialize(webhookUrl, opts, instance._sarifModel);
1022
1387
  return instance;
1023
1388
  }
1024
1389
  static async buildModel(sarifOpts) {
@@ -1029,11 +1394,11 @@ var SarifToSlackClient = class _SarifToSlackClient {
1029
1394
  const model = { sarifFiles, runs: [], findings: new FindingArray() };
1030
1395
  const runIdGenerator = _SarifToSlackClient.createRunIdGenerator();
1031
1396
  for (const sarifPath of sarifFiles) {
1032
- const sarifJson = await import_fs2.promises.readFile(sarifPath, "utf8");
1397
+ const sarifJson = await import_node_fs2.promises.readFile(sarifPath, "utf8");
1033
1398
  const sarifLog = JSON.parse(sarifJson);
1034
1399
  for (const run of sarifLog.runs) {
1035
1400
  const runId = runIdGenerator.next();
1036
- let runMetadata = void 0;
1401
+ let runMetadata;
1037
1402
  for (const result of run.results ?? []) {
1038
1403
  runMetadata = {
1039
1404
  id: runId.value,
@@ -1055,13 +1420,15 @@ var SarifToSlackClient = class _SarifToSlackClient {
1055
1420
  /**
1056
1421
  * The main function to initialize a list of {@link SlackMessage} objects based
1057
1422
  * on the given SARIF file(s).
1058
- * @param sarifModel - An instance of SarifModel object.
1423
+ * @param webhookUrl - Slack webhook URL.
1059
1424
  * @param opts - An instance of {@link SarifToSlackClientOptions} object.
1425
+ * @param sarifModel - An instance of SarifModel object.
1060
1426
  * @returns A map where key is the SARIF file and value is an instance of
1061
1427
  * {@link SlackMessage} object.
1428
+ * @internal
1062
1429
  */
1063
- static async initialize(sarifModel, opts) {
1064
- const message = createSlackMessage(opts.webhookUrl, {
1430
+ static async initialize(webhookUrl, opts, sarifModel) {
1431
+ const message = createSlackMessage(webhookUrl, {
1065
1432
  username: opts.username,
1066
1433
  iconUrl: opts.iconUrl,
1067
1434
  color: identifyColor(sarifModel.findings, opts.color),
@@ -1096,9 +1463,9 @@ var SarifToSlackClient = class _SarifToSlackClient {
1096
1463
  throw new Error("Slack message was not prepared.");
1097
1464
  }
1098
1465
  const text = await this._message.send();
1099
- Logger.info("Message sent. Status:", text);
1466
+ this._logger.info("Message sent. Status:", text);
1100
1467
  } else {
1101
- Logger.info(sendIfLogMessage(this._sendIf));
1468
+ this._logger.info(sendIfLogMessage(this._sendIf));
1102
1469
  }
1103
1470
  }
1104
1471
  get shouldSendMessage() {
@@ -1163,7 +1530,6 @@ var SarifToSlackClient = class _SarifToSlackClient {
1163
1530
  0 && (module.exports = {
1164
1531
  Color,
1165
1532
  FooterType,
1166
- LogLevel,
1167
1533
  RepresentationType,
1168
1534
  SarifToSlackClient,
1169
1535
  SendIf