@fabasoad/sarif-to-slack 0.2.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/security.yml +0 -1
- package/.github/workflows/send-sarif-to-slack.yml +145 -73
- package/.gitleaksignore +8 -0
- package/.pre-commit-config.yaml +3 -3
- package/.tool-versions +1 -1
- package/dist/Logger.js +4 -1
- package/dist/SarifToSlackClient.d.ts +33 -0
- package/dist/SarifToSlackClient.d.ts.map +1 -0
- package/dist/SarifToSlackClient.js +178 -0
- package/dist/SlackMessageBuilder.js +34 -82
- package/dist/System.d.ts +1 -3
- package/dist/System.d.ts.map +1 -1
- package/dist/System.js +10 -3
- package/dist/index.cjs +826 -472
- package/dist/index.d.ts +35 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -12
- package/dist/model/Color.d.ts +80 -0
- package/dist/model/Color.d.ts.map +1 -0
- package/dist/model/Color.js +106 -0
- package/dist/model/Finding.d.ts +2 -0
- package/dist/model/Finding.d.ts.map +1 -0
- package/dist/model/Finding.js +93 -0
- package/dist/model/FindingsArray.d.ts +2 -0
- package/dist/model/FindingsArray.d.ts.map +1 -0
- package/dist/model/FindingsArray.js +24 -0
- package/dist/processors/CodeQLProcessor.d.ts +2 -0
- package/dist/processors/CodeQLProcessor.d.ts.map +1 -0
- package/dist/processors/CodeQLProcessor.js +17 -0
- package/dist/processors/CommonProcessor.d.ts +2 -0
- package/dist/processors/CommonProcessor.d.ts.map +1 -0
- package/dist/processors/CommonProcessor.js +84 -0
- package/dist/processors/ProcessorFactory.d.ts +2 -0
- package/dist/processors/ProcessorFactory.d.ts.map +1 -0
- package/dist/processors/ProcessorFactory.js +22 -0
- package/dist/processors/SnykProcessor.d.ts +2 -0
- package/dist/processors/SnykProcessor.d.ts.map +1 -0
- package/dist/processors/SnykProcessor.js +18 -0
- package/dist/representations/CompactGroupByRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByRepresentation.js +58 -0
- package/dist/representations/CompactGroupByRunPerLevelRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByRunPerLevelRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByRunPerLevelRepresentation.js +13 -0
- package/dist/representations/CompactGroupByRunPerSeverityRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByRunPerSeverityRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByRunPerSeverityRepresentation.js +13 -0
- package/dist/representations/CompactGroupByRunRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByRunRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByRunRepresentation.js +39 -0
- package/dist/representations/CompactGroupBySarifPerLevelRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupBySarifPerLevelRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupBySarifPerLevelRepresentation.js +13 -0
- package/dist/representations/CompactGroupBySarifPerSeverityRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupBySarifPerSeverityRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupBySarifPerSeverityRepresentation.js +13 -0
- package/dist/representations/CompactGroupBySarifRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupBySarifRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupBySarifRepresentation.js +40 -0
- package/dist/representations/CompactGroupByToolNamePerLevelRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByToolNamePerLevelRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByToolNamePerLevelRepresentation.js +13 -0
- package/dist/representations/CompactGroupByToolNamePerSeverityRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByToolNamePerSeverityRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByToolNamePerSeverityRepresentation.js +13 -0
- package/dist/representations/CompactGroupByToolNameRepresentation.d.ts +2 -0
- package/dist/representations/CompactGroupByToolNameRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactGroupByToolNameRepresentation.js +39 -0
- package/dist/representations/CompactTotalPerLevelRepresentation.d.ts +2 -0
- package/dist/representations/CompactTotalPerLevelRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactTotalPerLevelRepresentation.js +13 -0
- package/dist/representations/CompactTotalPerSeverityRepresentation.d.ts +2 -0
- package/dist/representations/CompactTotalPerSeverityRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactTotalPerSeverityRepresentation.js +13 -0
- package/dist/representations/CompactTotalRepresentation.d.ts +2 -0
- package/dist/representations/CompactTotalRepresentation.d.ts.map +1 -0
- package/dist/representations/CompactTotalRepresentation.js +25 -0
- package/dist/representations/Representation.d.ts +2 -0
- package/dist/representations/Representation.d.ts.map +1 -0
- package/dist/representations/Representation.js +28 -0
- package/dist/representations/RepresentationFactory.d.ts +2 -0
- package/dist/representations/RepresentationFactory.d.ts.map +1 -0
- package/dist/representations/RepresentationFactory.js +37 -0
- package/dist/sarif-to-slack.d.ts +347 -85
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/types.d.ts +215 -51
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +225 -33
- package/dist/utils/Comparators.d.ts +2 -0
- package/dist/utils/Comparators.d.ts.map +1 -0
- package/dist/utils/Comparators.js +18 -0
- package/dist/utils/ExtendedArray.d.ts +2 -0
- package/dist/utils/ExtendedArray.d.ts.map +1 -0
- package/dist/utils/ExtendedArray.js +11 -0
- package/dist/utils/FileUtils.d.ts +2 -0
- package/dist/utils/FileUtils.d.ts.map +1 -0
- package/dist/utils/FileUtils.js +51 -0
- package/dist/utils/SarifUtils.js +20 -54
- package/etc/sarif-to-slack.api.md +162 -99
- package/jest.config.json +2 -2
- package/package.json +7 -7
- package/scripts/save-metadata.sh +12 -10
- package/src/Logger.ts +4 -0
- package/src/SarifToSlackClient.ts +202 -0
- package/src/SlackMessageBuilder.ts +35 -115
- package/src/System.ts +9 -2
- package/src/index.ts +47 -20
- package/src/model/Color.ts +195 -0
- package/src/model/Finding.ts +137 -0
- package/src/model/FindingsArray.ts +27 -0
- package/src/processors/CodeQLProcessor.ts +19 -0
- package/src/processors/CommonProcessor.ts +103 -0
- package/src/processors/ProcessorFactory.ts +23 -0
- package/src/processors/SnykProcessor.ts +19 -0
- package/src/representations/CompactGroupByRepresentation.ts +67 -0
- package/src/representations/CompactGroupByRunPerLevelRepresentation.ts +14 -0
- package/src/representations/CompactGroupByRunPerSeverityRepresentation.ts +14 -0
- package/src/representations/CompactGroupByRunRepresentation.ts +44 -0
- package/src/representations/CompactGroupBySarifPerLevelRepresentation.ts +15 -0
- package/src/representations/CompactGroupBySarifPerSeverityRepresentation.ts +15 -0
- package/src/representations/CompactGroupBySarifRepresentation.ts +45 -0
- package/src/representations/CompactGroupByToolNamePerLevelRepresentation.ts +15 -0
- package/src/representations/CompactGroupByToolNamePerSeverityRepresentation.ts +15 -0
- package/src/representations/CompactGroupByToolNameRepresentation.ts +44 -0
- package/src/representations/CompactTotalPerLevelRepresentation.ts +14 -0
- package/src/representations/CompactTotalPerSeverityRepresentation.ts +14 -0
- package/src/representations/CompactTotalRepresentation.ts +27 -0
- package/src/representations/Representation.ts +35 -0
- package/src/representations/RepresentationFactory.ts +49 -0
- package/src/types.ts +270 -53
- package/src/utils/Comparators.ts +19 -0
- package/src/utils/ExtendedArray.ts +11 -0
- package/src/utils/FileUtils.ts +60 -0
- package/src/utils/SarifUtils.ts +20 -72
- package/test-data/sarif/codeql-python.sarif +1448 -1
- package/test-data/sarif/codeql-typescript.sarif +3474 -1
- package/test-data/sarif/grype-github-actions.sarif +65 -0
- package/test-data/sarif/osv-scanner-composer.sarif +972 -0
- package/test-data/sarif/osv-scanner-container.sarif +2278 -0
- package/test-data/sarif/osv-scanner-gomodules.sarif +813 -0
- package/test-data/sarif/osv-scanner-hex.sarif +147 -0
- package/test-data/sarif/osv-scanner-maven.sarif +171 -0
- package/test-data/sarif/osv-scanner-npm.sarif +627 -0
- package/test-data/sarif/osv-scanner-pip.sarif +206 -0
- package/test-data/sarif/osv-scanner-pipenv.sarif +243 -0
- package/test-data/sarif/osv-scanner-pnpm.sarif +174 -0
- package/test-data/sarif/osv-scanner-poetry.sarif +1893 -0
- package/test-data/sarif/osv-scanner-rubygems.sarif +402 -0
- package/test-data/sarif/osv-scanner-uv.sarif +206 -0
- package/test-data/sarif/osv-scanner-yarn.sarif +5207 -0
- package/test-data/sarif/runs-0.sarif +5 -0
- package/test-data/sarif/runs-2-tools-2-results-0.sarif +1 -1
- package/test-data/sarif/runs-2-tools-2.sarif +1 -1
- package/test-data/sarif/runs-3-tools-2-results-0.sarif +1 -1
- package/test-data/sarif/runs-3-tools-2.sarif +1 -1
- package/test-data/sarif/tmp/codeql-csharp.sarif +1 -0
- package/test-data/sarif/tmp/grype-container.sarif +1774 -0
- package/test-data/sarif/tmp/runs-1-tools-1-results-0.sarif +18 -0
- package/test-data/sarif/tmp/runs-2-tools-2.sarif +686 -0
- package/test-data/sarif/trivy-iac.sarif +1 -1
- package/tests/integration/SendSarifToSlack.spec.ts +95 -27
- package/tsconfig.json +2 -0
- package/dist/Processors.d.ts +0 -2
- package/dist/Processors.d.ts.map +0 -1
- package/dist/Processors.js +0 -61
- package/dist/SarifToSlackService.d.ts +0 -39
- package/dist/SarifToSlackService.d.ts.map +0 -1
- package/dist/SarifToSlackService.js +0 -104
- package/dist/metadata.d.ts +0 -2
- package/dist/metadata.d.ts.map +0 -1
- package/dist/metadata.js +0 -11
- package/dist/model/SarifModelPerRun.d.ts +0 -2
- package/dist/model/SarifModelPerRun.d.ts.map +0 -1
- package/dist/model/SarifModelPerRun.js +0 -90
- package/dist/model/SarifModelPerSarif.d.ts +0 -2
- package/dist/model/SarifModelPerSarif.d.ts.map +0 -1
- package/dist/model/SarifModelPerSarif.js +0 -102
- package/dist/model/types.d.ts +0 -2
- package/dist/model/types.d.ts.map +0 -1
- package/dist/model/types.js +0 -49
- package/dist/utils/SortUtils.d.ts +0 -2
- package/dist/utils/SortUtils.d.ts.map +0 -1
- package/dist/utils/SortUtils.js +0 -20
- package/src/Processors.ts +0 -68
- package/src/SarifToSlackService.ts +0 -117
- package/src/metadata.ts +0 -10
- package/src/model/SarifModelPerRun.ts +0 -120
- package/src/model/SarifModelPerSarif.ts +0 -126
- package/src/model/types.ts +0 -50
- package/src/utils/SortUtils.ts +0 -33
- package/tests/Processors.spec.ts +0 -76
package/dist/index.cjs
CHANGED
|
@@ -30,20 +30,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
33
|
+
Color: () => Color,
|
|
34
34
|
FooterType: () => FooterType,
|
|
35
|
-
GroupResultsBy: () => GroupResultsBy,
|
|
36
35
|
LogLevel: () => LogLevel,
|
|
37
|
-
|
|
36
|
+
RepresentationType: () => RepresentationType,
|
|
37
|
+
SarifToSlackClient: () => SarifToSlackClient,
|
|
38
|
+
SendIf: () => SendIf
|
|
38
39
|
});
|
|
39
40
|
module.exports = __toCommonJS(index_exports);
|
|
40
41
|
|
|
41
|
-
// src/SarifToSlackService.ts
|
|
42
|
-
var import_fs = require("fs");
|
|
43
|
-
|
|
44
|
-
// src/Logger.ts
|
|
45
|
-
var import_tslog = require("tslog");
|
|
46
|
-
|
|
47
42
|
// src/types.ts
|
|
48
43
|
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
49
44
|
LogLevel2[LogLevel2["Silly"] = 0] = "Silly";
|
|
@@ -60,19 +55,161 @@ var FooterType = /* @__PURE__ */ ((FooterType2) => {
|
|
|
60
55
|
FooterType2["Markdown"] = "mrkdwn";
|
|
61
56
|
return FooterType2;
|
|
62
57
|
})(FooterType || {});
|
|
63
|
-
var
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
73
|
-
})(
|
|
58
|
+
var RepresentationType = /* @__PURE__ */ ((RepresentationType2) => {
|
|
59
|
+
RepresentationType2[RepresentationType2["CompactGroupByRunPerLevel"] = 0] = "CompactGroupByRunPerLevel";
|
|
60
|
+
RepresentationType2[RepresentationType2["CompactGroupByRunPerSeverity"] = 1] = "CompactGroupByRunPerSeverity";
|
|
61
|
+
RepresentationType2[RepresentationType2["CompactGroupByToolNamePerLevel"] = 2] = "CompactGroupByToolNamePerLevel";
|
|
62
|
+
RepresentationType2[RepresentationType2["CompactGroupByToolNamePerSeverity"] = 3] = "CompactGroupByToolNamePerSeverity";
|
|
63
|
+
RepresentationType2[RepresentationType2["CompactGroupBySarifPerLevel"] = 4] = "CompactGroupBySarifPerLevel";
|
|
64
|
+
RepresentationType2[RepresentationType2["CompactGroupBySarifPerSeverity"] = 5] = "CompactGroupBySarifPerSeverity";
|
|
65
|
+
RepresentationType2[RepresentationType2["CompactTotalPerLevel"] = 6] = "CompactTotalPerLevel";
|
|
66
|
+
RepresentationType2[RepresentationType2["CompactTotalPerSeverity"] = 7] = "CompactTotalPerSeverity";
|
|
67
|
+
return RepresentationType2;
|
|
68
|
+
})(RepresentationType || {});
|
|
69
|
+
var SendIf = /* @__PURE__ */ ((SendIf2) => {
|
|
70
|
+
SendIf2[SendIf2["SeverityCritical"] = 0] = "SeverityCritical";
|
|
71
|
+
SendIf2[SendIf2["SeverityHigh"] = 1] = "SeverityHigh";
|
|
72
|
+
SendIf2[SendIf2["SeverityHighOrHigher"] = 2] = "SeverityHighOrHigher";
|
|
73
|
+
SendIf2[SendIf2["SeverityMedium"] = 3] = "SeverityMedium";
|
|
74
|
+
SendIf2[SendIf2["SeverityMediumOrHigher"] = 4] = "SeverityMediumOrHigher";
|
|
75
|
+
SendIf2[SendIf2["SeverityLow"] = 5] = "SeverityLow";
|
|
76
|
+
SendIf2[SendIf2["SeverityLowOrHigher"] = 6] = "SeverityLowOrHigher";
|
|
77
|
+
SendIf2[SendIf2["SeverityNone"] = 7] = "SeverityNone";
|
|
78
|
+
SendIf2[SendIf2["SeverityNoneOrHigher"] = 8] = "SeverityNoneOrHigher";
|
|
79
|
+
SendIf2[SendIf2["SeverityUnknown"] = 9] = "SeverityUnknown";
|
|
80
|
+
SendIf2[SendIf2["SeverityUnknownOrHigher"] = 10] = "SeverityUnknownOrHigher";
|
|
81
|
+
SendIf2[SendIf2["LevelError"] = 11] = "LevelError";
|
|
82
|
+
SendIf2[SendIf2["LevelWarning"] = 12] = "LevelWarning";
|
|
83
|
+
SendIf2[SendIf2["LevelWarningOrHigher"] = 13] = "LevelWarningOrHigher";
|
|
84
|
+
SendIf2[SendIf2["LevelNote"] = 14] = "LevelNote";
|
|
85
|
+
SendIf2[SendIf2["LevelNoteOrHigher"] = 15] = "LevelNoteOrHigher";
|
|
86
|
+
SendIf2[SendIf2["LevelNone"] = 16] = "LevelNone";
|
|
87
|
+
SendIf2[SendIf2["LevelNoneOrHigher"] = 17] = "LevelNoneOrHigher";
|
|
88
|
+
SendIf2[SendIf2["LevelUnknown"] = 18] = "LevelUnknown";
|
|
89
|
+
SendIf2[SendIf2["LevelUnknownOrHigher"] = 19] = "LevelUnknownOrHigher";
|
|
90
|
+
SendIf2[SendIf2["Always"] = 20] = "Always";
|
|
91
|
+
SendIf2[SendIf2["Some"] = 21] = "Some";
|
|
92
|
+
SendIf2[SendIf2["Empty"] = 22] = "Empty";
|
|
93
|
+
SendIf2[SendIf2["Never"] = 23] = "Never";
|
|
94
|
+
return SendIf2;
|
|
95
|
+
})(SendIf || {});
|
|
96
|
+
var SecuritySeverity = /* @__PURE__ */ ((SecuritySeverity2) => {
|
|
97
|
+
SecuritySeverity2[SecuritySeverity2["Unknown"] = 0] = "Unknown";
|
|
98
|
+
SecuritySeverity2[SecuritySeverity2["None"] = 1] = "None";
|
|
99
|
+
SecuritySeverity2[SecuritySeverity2["Low"] = 2] = "Low";
|
|
100
|
+
SecuritySeverity2[SecuritySeverity2["Medium"] = 3] = "Medium";
|
|
101
|
+
SecuritySeverity2[SecuritySeverity2["High"] = 4] = "High";
|
|
102
|
+
SecuritySeverity2[SecuritySeverity2["Critical"] = 5] = "Critical";
|
|
103
|
+
return SecuritySeverity2;
|
|
104
|
+
})(SecuritySeverity || {});
|
|
105
|
+
var SecurityLevel = /* @__PURE__ */ ((SecurityLevel2) => {
|
|
106
|
+
SecurityLevel2[SecurityLevel2["Unknown"] = 0] = "Unknown";
|
|
107
|
+
SecurityLevel2[SecurityLevel2["None"] = 1] = "None";
|
|
108
|
+
SecurityLevel2[SecurityLevel2["Note"] = 2] = "Note";
|
|
109
|
+
SecurityLevel2[SecurityLevel2["Warning"] = 3] = "Warning";
|
|
110
|
+
SecurityLevel2[SecurityLevel2["Error"] = 4] = "Error";
|
|
111
|
+
return SecurityLevel2;
|
|
112
|
+
})(SecurityLevel || {});
|
|
113
|
+
|
|
114
|
+
// src/model/Color.ts
|
|
115
|
+
var Color = class {
|
|
116
|
+
_color;
|
|
117
|
+
/**
|
|
118
|
+
* Creates an instance of {@link Color} class. Before creating an instance of
|
|
119
|
+
* {@link Color} class, it (if applicable) maps CI status into the hex color,
|
|
120
|
+
* and also validates {@param color} to be a valid string that represents a
|
|
121
|
+
* color in hex format.
|
|
122
|
+
* @param color Can be either undefined, valid color in hex format or GitHub
|
|
123
|
+
* CI status (one of: success, failure, cancelled, skipped)
|
|
124
|
+
* @public
|
|
125
|
+
*/
|
|
126
|
+
constructor(color) {
|
|
127
|
+
this._color = this.mapColor(color);
|
|
128
|
+
this.validateHexColor();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Returns a valid string that represents a color in hex format, or undefined.
|
|
132
|
+
*/
|
|
133
|
+
get value() {
|
|
134
|
+
return this._color;
|
|
135
|
+
}
|
|
136
|
+
validateHexColor() {
|
|
137
|
+
if (this._color != null) {
|
|
138
|
+
const hexColorRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
139
|
+
if (!hexColorRegex.test(this._color)) {
|
|
140
|
+
throw new Error(`Invalid hex color: "${this._color}"`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
mapColor(from) {
|
|
145
|
+
switch (from) {
|
|
146
|
+
case "success":
|
|
147
|
+
return "#008000";
|
|
148
|
+
case "failure":
|
|
149
|
+
return "#ff0000";
|
|
150
|
+
case "cancelled":
|
|
151
|
+
return "#0047ab";
|
|
152
|
+
case "skipped":
|
|
153
|
+
return "#808080";
|
|
154
|
+
default:
|
|
155
|
+
return from;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
function identifyColorCommon(findings, prop, none, unknown, color, defaultColor) {
|
|
160
|
+
if (color.none != null && findings.findByProperty(prop, none) != null) {
|
|
161
|
+
return color.none.value;
|
|
162
|
+
}
|
|
163
|
+
if (color.unknown != null && findings.findByProperty(prop, unknown) != null) {
|
|
164
|
+
return color.unknown.value;
|
|
165
|
+
}
|
|
166
|
+
if (color.empty != null && findings.length === 0) {
|
|
167
|
+
return color.empty.value;
|
|
168
|
+
}
|
|
169
|
+
return defaultColor?.value;
|
|
170
|
+
}
|
|
171
|
+
function identifyColorBySeverity(findings, color, defaultColor) {
|
|
172
|
+
if (color.critical != null && findings.findByProperty("severity", 5 /* Critical */) != null) {
|
|
173
|
+
return color.critical.value;
|
|
174
|
+
}
|
|
175
|
+
if (color.high != null && findings.findByProperty("severity", 4 /* High */) != null) {
|
|
176
|
+
return color.high.value;
|
|
177
|
+
}
|
|
178
|
+
if (color.medium != null && findings.findByProperty("severity", 3 /* Medium */) != null) {
|
|
179
|
+
return color.medium.value;
|
|
180
|
+
}
|
|
181
|
+
if (color.low != null && findings.findByProperty("severity", 2 /* Low */) != null) {
|
|
182
|
+
return color.low.value;
|
|
183
|
+
}
|
|
184
|
+
return identifyColorCommon(findings, "severity", 1 /* None */, 0 /* Unknown */, color, defaultColor);
|
|
185
|
+
}
|
|
186
|
+
function identifyColorByLevel(findings, color, defaultColor) {
|
|
187
|
+
if (color.error != null && findings.findByProperty("level", 4 /* Error */) != null) {
|
|
188
|
+
return color.error.value;
|
|
189
|
+
}
|
|
190
|
+
if (color.warning != null && findings.findByProperty("level", 3 /* Warning */) != null) {
|
|
191
|
+
return color.warning.value;
|
|
192
|
+
}
|
|
193
|
+
if (color.note != null && findings.findByProperty("level", 2 /* Note */) != null) {
|
|
194
|
+
return color.note.value;
|
|
195
|
+
}
|
|
196
|
+
return identifyColorCommon(findings, "level", 1 /* None */, 0 /* Unknown */, color, defaultColor);
|
|
197
|
+
}
|
|
198
|
+
function identifyColor(findings, colorOpts) {
|
|
199
|
+
if (colorOpts?.bySeverity != null) {
|
|
200
|
+
return identifyColorBySeverity(findings, colorOpts.bySeverity, colorOpts.default);
|
|
201
|
+
}
|
|
202
|
+
if (colorOpts?.byLevel != null) {
|
|
203
|
+
return identifyColorByLevel(findings, colorOpts.byLevel, colorOpts.default);
|
|
204
|
+
}
|
|
205
|
+
return colorOpts?.default?.value;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/SarifToSlackClient.ts
|
|
209
|
+
var import_fs2 = require("fs");
|
|
74
210
|
|
|
75
211
|
// src/Logger.ts
|
|
212
|
+
var import_tslog = require("tslog");
|
|
76
213
|
var Logger = class _Logger {
|
|
77
214
|
static DEFAULT_LOG_LEVEL = 3 /* Info */;
|
|
78
215
|
static DEFAULT_LOG_TEMPLATE = "[{{logLevelName}}] [{{name}}] {{dateIsoStr}} ";
|
|
@@ -99,345 +236,40 @@ var Logger = class _Logger {
|
|
|
99
236
|
static debug(...args) {
|
|
100
237
|
_Logger.instance.debug(...args);
|
|
101
238
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// src/Processors.ts
|
|
105
|
-
var fs = __toESM(require("fs"));
|
|
106
|
-
var path = __toESM(require("path"));
|
|
107
|
-
function processColor(color) {
|
|
108
|
-
switch (color) {
|
|
109
|
-
case "success":
|
|
110
|
-
Logger.info(`Converting "${color}" to #008000`);
|
|
111
|
-
return "#008000";
|
|
112
|
-
case "failure":
|
|
113
|
-
Logger.info(`Converting "${color}" to #ff0000`);
|
|
114
|
-
return "#ff0000";
|
|
115
|
-
case "cancelled":
|
|
116
|
-
Logger.info(`Converting "${color}" to #0047ab`);
|
|
117
|
-
return "#0047ab";
|
|
118
|
-
case "skipped":
|
|
119
|
-
Logger.info(`Converting "${color}" to #808080`);
|
|
120
|
-
return "#808080";
|
|
121
|
-
default:
|
|
122
|
-
Logger.debug(`"${color}" color is not a CI status identifier. Returning as is.`);
|
|
123
|
-
return color;
|
|
239
|
+
static trace(...args) {
|
|
240
|
+
_Logger.instance.trace(...args);
|
|
124
241
|
}
|
|
125
|
-
}
|
|
126
|
-
function processSarifPath(sarifPath) {
|
|
127
|
-
if (!fs.existsSync(sarifPath)) {
|
|
128
|
-
throw new Error(`"sarif-path" does not exist: ${sarifPath}`);
|
|
129
|
-
}
|
|
130
|
-
const sarifStats = fs.statSync(sarifPath);
|
|
131
|
-
if (sarifStats.isDirectory()) {
|
|
132
|
-
Logger.info(`"sarif-path" is a directory: ${sarifPath}`);
|
|
133
|
-
const files = fs.readdirSync(sarifPath);
|
|
134
|
-
const filteredFiles = files.filter(
|
|
135
|
-
(file) => path.extname(file).toLowerCase() === ".sarif"
|
|
136
|
-
);
|
|
137
|
-
Logger.info(`Found ${filteredFiles.length} SARIF files in ${sarifPath} directory`);
|
|
138
|
-
Logger.debug(`Filtered SARIF files: ${filteredFiles.join(", ")}`);
|
|
139
|
-
return filteredFiles.map((file) => path.join(sarifPath, file));
|
|
140
|
-
}
|
|
141
|
-
if (sarifStats.isFile()) {
|
|
142
|
-
Logger.info(`"sarif-path" is a file: ${sarifPath}`);
|
|
143
|
-
return [sarifPath];
|
|
144
|
-
}
|
|
145
|
-
throw new Error(`"sarif-path" is neither a file nor a directory: ${sarifPath}`);
|
|
146
|
-
}
|
|
242
|
+
};
|
|
147
243
|
|
|
148
244
|
// src/SlackMessageBuilder.ts
|
|
149
245
|
var import_webhook = require("@slack/webhook");
|
|
150
246
|
|
|
151
|
-
// src/metadata.
|
|
152
|
-
var
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
var import_immutable2 = require("immutable");
|
|
156
|
-
|
|
157
|
-
// src/utils/SarifUtils.ts
|
|
158
|
-
function findToolComponentByResult(run, result) {
|
|
159
|
-
let tool;
|
|
160
|
-
if (result.rule?.toolComponent?.index != null) {
|
|
161
|
-
tool = run.tool.extensions?.[result.rule.toolComponent.index];
|
|
162
|
-
}
|
|
163
|
-
if (!tool) {
|
|
164
|
-
tool = run.tool.driver;
|
|
165
|
-
}
|
|
166
|
-
return tool;
|
|
167
|
-
}
|
|
168
|
-
function findRuleByResult(run, result) {
|
|
169
|
-
const ruleData = {};
|
|
170
|
-
if (result.rule) {
|
|
171
|
-
if (result.rule?.index != null) {
|
|
172
|
-
ruleData.index = result.rule.index;
|
|
173
|
-
}
|
|
174
|
-
if (result.rule?.id) {
|
|
175
|
-
ruleData.id = result.rule.id;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (ruleData.index == null && result.ruleIndex != null) {
|
|
179
|
-
ruleData.index = result.ruleIndex;
|
|
180
|
-
}
|
|
181
|
-
if (!ruleData.id && result.ruleId) {
|
|
182
|
-
ruleData.id = result.ruleId;
|
|
183
|
-
}
|
|
184
|
-
const tool = findToolComponentByResult(run, result);
|
|
185
|
-
if (ruleData.index != null && tool?.rules && ruleData.index < tool.rules.length) {
|
|
186
|
-
return tool.rules[ruleData.index];
|
|
187
|
-
}
|
|
188
|
-
if (ruleData.id && tool?.rules) {
|
|
189
|
-
return tool.rules.find(
|
|
190
|
-
(r) => r.id === ruleData.id
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
return void 0;
|
|
194
|
-
}
|
|
195
|
-
function tryGetRulePropertyByResult(run, result, propertyName) {
|
|
196
|
-
const rule = findRuleByResult(run, result);
|
|
197
|
-
if (rule && rule.properties && propertyName in rule.properties) {
|
|
198
|
-
return rule.properties[propertyName];
|
|
199
|
-
}
|
|
200
|
-
return void 0;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// src/model/types.ts
|
|
204
|
-
var SecuritySeverityOrder = [
|
|
205
|
-
"Critical" /* Critical */,
|
|
206
|
-
"High" /* High */,
|
|
207
|
-
"Medium" /* Medium */,
|
|
208
|
-
"Low" /* Low */,
|
|
209
|
-
"None" /* None */,
|
|
210
|
-
"Unknown" /* Unknown */
|
|
211
|
-
];
|
|
212
|
-
var SecurityLevelOrder = [
|
|
213
|
-
"Error" /* Error */,
|
|
214
|
-
"Warning" /* Warning */,
|
|
215
|
-
"Note" /* Note */,
|
|
216
|
-
"Unknown" /* Unknown */
|
|
217
|
-
];
|
|
218
|
-
|
|
219
|
-
// src/model/SarifModelPerRun.ts
|
|
220
|
-
var import_immutable = require("immutable");
|
|
221
|
-
|
|
222
|
-
// src/utils/SortUtils.ts
|
|
223
|
-
function sortSecurityLevelMap(map) {
|
|
224
|
-
return map.sortBy(
|
|
225
|
-
(_, level) => level,
|
|
226
|
-
(a, b) => SecurityLevelOrder.indexOf(a) - SecurityLevelOrder.indexOf(b)
|
|
227
|
-
).asImmutable();
|
|
228
|
-
}
|
|
229
|
-
function sortSecuritySeverityMap(map) {
|
|
230
|
-
return map.sortBy(
|
|
231
|
-
(_, severity) => severity,
|
|
232
|
-
(a, b) => SecuritySeverityOrder.indexOf(a) - SecuritySeverityOrder.indexOf(b)
|
|
233
|
-
).asImmutable();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// src/model/SarifModelPerRun.ts
|
|
237
|
-
var SarifModelPerRun = class {
|
|
238
|
-
toolName;
|
|
239
|
-
_securitySeverityMap;
|
|
240
|
-
_securityLevelMap;
|
|
241
|
-
constructor(run) {
|
|
242
|
-
this.toolName = run.tool.driver.name;
|
|
243
|
-
this._securitySeverityMap = (0, import_immutable.Map)().asMutable();
|
|
244
|
-
this._securityLevelMap = (0, import_immutable.Map)().asMutable();
|
|
245
|
-
this.buildSecuritySeverityMap(run);
|
|
246
|
-
this.buildSecurityLevelMap(run);
|
|
247
|
-
}
|
|
248
|
-
identifySecuritySeverity(score) {
|
|
249
|
-
if (score === void 0) {
|
|
250
|
-
return "Unknown" /* Unknown */;
|
|
251
|
-
}
|
|
252
|
-
if (score >= 9 && score <= 10) {
|
|
253
|
-
return "Critical" /* Critical */;
|
|
254
|
-
}
|
|
255
|
-
if (score >= 7) {
|
|
256
|
-
return "High" /* High */;
|
|
257
|
-
}
|
|
258
|
-
if (score >= 4) {
|
|
259
|
-
return "Medium" /* Medium */;
|
|
260
|
-
}
|
|
261
|
-
if (score >= 0.1) {
|
|
262
|
-
return "Low" /* Low */;
|
|
263
|
-
}
|
|
264
|
-
if (score == 0) {
|
|
265
|
-
return "None" /* None */;
|
|
266
|
-
}
|
|
267
|
-
Logger.warn(`Unsupported "${score}" security severity. Saving as "Unknown".`);
|
|
268
|
-
return "Unknown" /* Unknown */;
|
|
269
|
-
}
|
|
270
|
-
identifySecurityLevel(level) {
|
|
271
|
-
if (level === void 0) {
|
|
272
|
-
return "Unknown" /* Unknown */;
|
|
273
|
-
}
|
|
274
|
-
if (level.toLowerCase() === "error") {
|
|
275
|
-
return "Error" /* Error */;
|
|
276
|
-
}
|
|
277
|
-
if (level.toLowerCase() === "warning") {
|
|
278
|
-
return "Warning" /* Warning */;
|
|
279
|
-
}
|
|
280
|
-
if (level.toLowerCase() === "note") {
|
|
281
|
-
return "Note" /* Note */;
|
|
282
|
-
}
|
|
283
|
-
Logger.warn(`Unsupported ${level} security level. Saving as "Unknown".`);
|
|
284
|
-
return "Unknown" /* Unknown */;
|
|
285
|
-
}
|
|
286
|
-
buildSecuritySeverityMap(run) {
|
|
287
|
-
const results = run.results ?? [];
|
|
288
|
-
for (const result of results) {
|
|
289
|
-
const severity = this.identifySecuritySeverity(
|
|
290
|
-
tryGetRulePropertyByResult(run, result, "security-severity")
|
|
291
|
-
);
|
|
292
|
-
const count = this._securitySeverityMap.get(severity) || 0;
|
|
293
|
-
this._securitySeverityMap.set(severity, count + 1);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
tryGetSecurityLevel(run, result) {
|
|
297
|
-
if (result.level) {
|
|
298
|
-
return result.level;
|
|
299
|
-
}
|
|
300
|
-
return tryGetRulePropertyByResult(run, result, "problem.severity");
|
|
301
|
-
}
|
|
302
|
-
buildSecurityLevelMap(run) {
|
|
303
|
-
const results = run.results ?? [];
|
|
304
|
-
for (const result of results) {
|
|
305
|
-
const level = this.identifySecurityLevel(
|
|
306
|
-
this.tryGetSecurityLevel(run, result)
|
|
307
|
-
);
|
|
308
|
-
const count = this._securityLevelMap.get(level) || 0;
|
|
309
|
-
this._securityLevelMap.set(level, count + 1);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
get securitySeverityMap() {
|
|
313
|
-
return sortSecuritySeverityMap(this._securitySeverityMap);
|
|
314
|
-
}
|
|
315
|
-
get securityLevelMap() {
|
|
316
|
-
return sortSecurityLevelMap(this._securityLevelMap);
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
// src/model/SarifModelPerSarif.ts
|
|
321
|
-
var SarifModelPerSarif = class {
|
|
322
|
-
sarifModelPerRunList;
|
|
323
|
-
constructor(sarif) {
|
|
324
|
-
this.sarifModelPerRunList = new Array();
|
|
325
|
-
this.buildRunsList(sarif);
|
|
326
|
-
}
|
|
327
|
-
buildRunsList(sarif) {
|
|
328
|
-
for (const run of sarif.runs) {
|
|
329
|
-
this.sarifModelPerRunList.push(new SarifModelPerRun(run));
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
groupByToolNameWithSecurityLevel() {
|
|
333
|
-
const result = /* @__PURE__ */ new Map();
|
|
334
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
335
|
-
if (!result.has(sarifModelPerRun.toolName)) {
|
|
336
|
-
result.set(sarifModelPerRun.toolName, (0, import_immutable2.Map)().asMutable());
|
|
337
|
-
}
|
|
338
|
-
for (const [k, v] of sarifModelPerRun.securityLevelMap.entries()) {
|
|
339
|
-
const count = result.get(sarifModelPerRun.toolName)?.get(k) || 0;
|
|
340
|
-
result.get(sarifModelPerRun.toolName)?.set(k, count + v);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
for (const [k, v] of result) {
|
|
344
|
-
result.set(k, sortSecurityLevelMap(v));
|
|
345
|
-
}
|
|
346
|
-
return result;
|
|
347
|
-
}
|
|
348
|
-
groupByRunWithSecurityLevel() {
|
|
349
|
-
const result = new Array();
|
|
350
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
351
|
-
result.push({
|
|
352
|
-
toolName: sarifModelPerRun.toolName,
|
|
353
|
-
data: sarifModelPerRun.securityLevelMap
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
return result;
|
|
357
|
-
}
|
|
358
|
-
groupByTotalWithSecurityLevel() {
|
|
359
|
-
const result = (0, import_immutable2.Map)().asMutable();
|
|
360
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
361
|
-
for (const [k, v] of sarifModelPerRun.securityLevelMap.entries()) {
|
|
362
|
-
const count = result.get(k) || 0;
|
|
363
|
-
result.set(k, count + v);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return sortSecurityLevelMap(result);
|
|
367
|
-
}
|
|
368
|
-
groupByToolNameWithSecuritySeverity() {
|
|
369
|
-
const result = /* @__PURE__ */ new Map();
|
|
370
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
371
|
-
if (!result.has(sarifModelPerRun.toolName)) {
|
|
372
|
-
result.set(sarifModelPerRun.toolName, (0, import_immutable2.Map)().asMutable());
|
|
373
|
-
}
|
|
374
|
-
for (const [k, v] of sarifModelPerRun.securitySeverityMap.entries()) {
|
|
375
|
-
const count = result.get(sarifModelPerRun.toolName)?.get(k) || 0;
|
|
376
|
-
result.get(sarifModelPerRun.toolName)?.set(k, count + v);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
for (const [k, v] of result.entries()) {
|
|
380
|
-
result.set(k, sortSecuritySeverityMap(v));
|
|
381
|
-
}
|
|
382
|
-
return result;
|
|
383
|
-
}
|
|
384
|
-
groupByRunWithSecuritySeverity() {
|
|
385
|
-
const result = new Array();
|
|
386
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
387
|
-
result.push({
|
|
388
|
-
toolName: sarifModelPerRun.toolName,
|
|
389
|
-
data: sarifModelPerRun.securitySeverityMap
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
return result;
|
|
393
|
-
}
|
|
394
|
-
groupByTotalWithSecuritySeverity() {
|
|
395
|
-
const result = (0, import_immutable2.Map)().asMutable();
|
|
396
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
397
|
-
for (const [k, v] of sarifModelPerRun.securitySeverityMap.entries()) {
|
|
398
|
-
const count = result.get(k) || 0;
|
|
399
|
-
result.set(k, count + v);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return sortSecuritySeverityMap(result);
|
|
403
|
-
}
|
|
404
|
-
listToolNames() {
|
|
405
|
-
const toolNames = /* @__PURE__ */ new Set();
|
|
406
|
-
for (const sarifModelPerRun of this.sarifModelPerRunList) {
|
|
407
|
-
toolNames.add(sarifModelPerRun.toolName);
|
|
408
|
-
}
|
|
409
|
-
return toolNames;
|
|
410
|
-
}
|
|
411
|
-
};
|
|
247
|
+
// src/metadata.json
|
|
248
|
+
var version = "1.1.0";
|
|
249
|
+
var sha = "cf8ff099965ca704d7e58bd11f6d4481f69e3125";
|
|
250
|
+
var buildAt = "2025-08-15T05:22:37Z";
|
|
412
251
|
|
|
413
252
|
// src/SlackMessageBuilder.ts
|
|
414
253
|
var SlackMessageBuilder = class {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
runId;
|
|
424
|
-
sarif;
|
|
254
|
+
_webhook;
|
|
255
|
+
_gitHubServerUrl;
|
|
256
|
+
_color;
|
|
257
|
+
_representation;
|
|
258
|
+
_header;
|
|
259
|
+
_footer;
|
|
260
|
+
_actor;
|
|
261
|
+
_runId;
|
|
425
262
|
constructor(url, opts) {
|
|
426
|
-
this.
|
|
263
|
+
this._webhook = new import_webhook.IncomingWebhook(url, {
|
|
427
264
|
username: opts.username || "SARIF results",
|
|
428
265
|
icon_url: opts.iconUrl
|
|
429
266
|
});
|
|
430
|
-
this.
|
|
431
|
-
this.
|
|
432
|
-
this.
|
|
433
|
-
this.sarifModelPerSarif = new SarifModelPerSarif(opts.sarif);
|
|
434
|
-
this.output = opts.output || {
|
|
435
|
-
groupBy: 0 /* ToolName */,
|
|
436
|
-
calculateBy: 0 /* Level */
|
|
437
|
-
};
|
|
267
|
+
this._gitHubServerUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
|
|
268
|
+
this._color = opts.color;
|
|
269
|
+
this._representation = opts.representation;
|
|
438
270
|
}
|
|
439
271
|
withHeader(header) {
|
|
440
|
-
this.
|
|
272
|
+
this._header = {
|
|
441
273
|
type: "header",
|
|
442
274
|
text: {
|
|
443
275
|
type: "plain_text",
|
|
@@ -446,23 +278,23 @@ var SlackMessageBuilder = class {
|
|
|
446
278
|
};
|
|
447
279
|
}
|
|
448
280
|
withActor(actor) {
|
|
449
|
-
this.
|
|
281
|
+
this._actor = actor || process.env.GITHUB_ACTOR;
|
|
450
282
|
}
|
|
451
283
|
withRun() {
|
|
452
|
-
this.
|
|
284
|
+
this._runId = process.env.GITHUB_RUN_ID;
|
|
453
285
|
}
|
|
454
286
|
withFooter(text, type) {
|
|
455
287
|
const repoName = "fabasoad/sarif-to-slack";
|
|
456
|
-
const element = text ? { type: type || "plain_text" /* PlainText */, text } : { type: "mrkdwn" /* Markdown */, text: `Generated by <${this.
|
|
457
|
-
this.
|
|
288
|
+
const element = text ? { type: type || "plain_text" /* PlainText */, text } : { type: "mrkdwn" /* Markdown */, text: `Generated by <${this._gitHubServerUrl}/${repoName}|@${repoName}@${version}>` };
|
|
289
|
+
this._footer = {
|
|
458
290
|
type: "context",
|
|
459
291
|
elements: [element]
|
|
460
292
|
};
|
|
461
293
|
}
|
|
462
294
|
async send() {
|
|
463
295
|
const blocks = [];
|
|
464
|
-
if (this.
|
|
465
|
-
blocks.push(this.
|
|
296
|
+
if (this._header) {
|
|
297
|
+
blocks.push(this._header);
|
|
466
298
|
}
|
|
467
299
|
blocks.push({
|
|
468
300
|
type: "section",
|
|
@@ -471,181 +303,703 @@ var SlackMessageBuilder = class {
|
|
|
471
303
|
text: this.buildText()
|
|
472
304
|
}
|
|
473
305
|
});
|
|
474
|
-
if (this.
|
|
475
|
-
blocks.push(this.
|
|
306
|
+
if (this._footer) {
|
|
307
|
+
blocks.push(this._footer);
|
|
476
308
|
}
|
|
477
|
-
const { text } = await this.
|
|
478
|
-
attachments: [{ color: this.
|
|
309
|
+
const { text } = await this._webhook.send({
|
|
310
|
+
attachments: [{ color: this._color, blocks }]
|
|
479
311
|
});
|
|
480
312
|
return text;
|
|
481
313
|
}
|
|
482
314
|
buildText() {
|
|
483
315
|
const text = [];
|
|
484
|
-
if (this.
|
|
485
|
-
const actorUrl = `${this.
|
|
486
|
-
text.push(`_Triggered by <${actorUrl}|${this.
|
|
316
|
+
if (this._actor) {
|
|
317
|
+
const actorUrl = `${this._gitHubServerUrl}/${this._actor}`;
|
|
318
|
+
text.push(`_Triggered by <${actorUrl}|${this._actor}>_`);
|
|
487
319
|
}
|
|
488
|
-
text.push(this.
|
|
489
|
-
if (this.
|
|
320
|
+
text.push(this._representation.compose());
|
|
321
|
+
if (this._runId) {
|
|
490
322
|
let runText = "Job ";
|
|
491
323
|
if (process.env.GITHUB_REPOSITORY) {
|
|
492
|
-
runText += `<${this.
|
|
324
|
+
runText += `<${this._gitHubServerUrl}/${process.env.GITHUB_REPOSITORY}/actions/runs/${this._runId}|#${this._runId}>`;
|
|
493
325
|
} else {
|
|
494
|
-
runText += `#${this.
|
|
326
|
+
runText += `#${this._runId}`;
|
|
495
327
|
}
|
|
496
328
|
text.push(runText);
|
|
497
329
|
}
|
|
498
330
|
return text.join("\n\n");
|
|
499
331
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// src/System.ts
|
|
335
|
+
var System = class {
|
|
336
|
+
static initialize() {
|
|
337
|
+
Logger.info(`@fabasoad/sarif-to-slack version: ${version}`);
|
|
338
|
+
Logger.info(`@fabasoad/sarif-to-slack sha: ${sha}`);
|
|
339
|
+
Logger.info(`@fabasoad/sarif-to-slack built at: ${buildAt}`);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// src/utils/FileUtils.ts
|
|
344
|
+
var import_fs = __toESM(require("fs"));
|
|
345
|
+
var path = __toESM(require("path"));
|
|
346
|
+
function listFilesRecursively(dir, extension, fileList = []) {
|
|
347
|
+
const entries = import_fs.default.readdirSync(dir);
|
|
348
|
+
entries.forEach((entry) => {
|
|
349
|
+
const fullPath = path.join(dir, entry);
|
|
350
|
+
if (import_fs.default.statSync(fullPath).isDirectory()) {
|
|
351
|
+
listFilesRecursively(fullPath, extension, fileList);
|
|
352
|
+
} else if (path.extname(fullPath).toLowerCase() === `.${extension}`) {
|
|
353
|
+
fileList.push(fullPath);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
return fileList;
|
|
357
|
+
}
|
|
358
|
+
function extractListOfFiles(opts) {
|
|
359
|
+
if (!import_fs.default.existsSync(opts.path)) {
|
|
360
|
+
throw new Error(`Provided path does not exist: ${opts.path}`);
|
|
361
|
+
}
|
|
362
|
+
const stats = import_fs.default.statSync(opts.path);
|
|
363
|
+
if (stats.isDirectory()) {
|
|
364
|
+
Logger.info(`Provided path is a directory: ${opts.path}`);
|
|
365
|
+
const files = opts.recursive && listFilesRecursively(opts.path, opts.extension ?? "sarif") || import_fs.default.readdirSync(opts.path);
|
|
366
|
+
Logger.info(`Found ${files.length} files in ${opts.path} directory with ${opts.extension} extension`);
|
|
367
|
+
Logger.debug(`Found files: ${files.join(", ")}`);
|
|
368
|
+
return files;
|
|
369
|
+
}
|
|
370
|
+
if (stats.isFile()) {
|
|
371
|
+
Logger.info(`Provided path is a file: ${opts.path}`);
|
|
372
|
+
return [opts.path];
|
|
373
|
+
}
|
|
374
|
+
throw new Error(`Provided path is neither a file nor a directory: ${opts.path}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/utils/Comparators.ts
|
|
378
|
+
function findingsComparatorByKey(key) {
|
|
379
|
+
return (a, b) => {
|
|
380
|
+
switch (key) {
|
|
381
|
+
case "severity":
|
|
382
|
+
return b.severity - a.severity;
|
|
383
|
+
case "level":
|
|
384
|
+
return b.level - a.level;
|
|
385
|
+
case "runId":
|
|
386
|
+
return a.runId - b.runId;
|
|
387
|
+
case "toolName":
|
|
388
|
+
return a.toolName.toLowerCase().localeCompare(b.toolName.toLowerCase());
|
|
389
|
+
default:
|
|
390
|
+
return 1;
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/utils/ExtendedArray.ts
|
|
396
|
+
var ExtendedArray = class extends Array {
|
|
397
|
+
findByProperty(prop, value) {
|
|
398
|
+
return this.find((v) => v[prop] === value);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/model/FindingsArray.ts
|
|
403
|
+
var FindingsArray = class extends ExtendedArray {
|
|
404
|
+
hasSeverityOrHigher(severity) {
|
|
405
|
+
return Object.values(SecuritySeverity).filter((v) => typeof v === "number").filter((v) => v >= severity).some((v) => this.findByProperty("severity", v) != null);
|
|
406
|
+
}
|
|
407
|
+
hasLevelOrHigher(level) {
|
|
408
|
+
return Object.values(SecurityLevel).filter((v) => typeof v === "number").filter((v) => v >= level).some((v) => this.findByProperty("level", v) != null);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// src/representations/Representation.ts
|
|
413
|
+
var Representation = class {
|
|
414
|
+
_model;
|
|
415
|
+
constructor(model, findingSortKey = "level") {
|
|
416
|
+
this._model = model;
|
|
417
|
+
this._model.findings = model.findings.map((f) => f.clone()).sort(findingsComparatorByKey(findingSortKey)).reduce((arr, f) => {
|
|
418
|
+
arr.push(f);
|
|
419
|
+
return arr;
|
|
420
|
+
}, new FindingsArray());
|
|
421
|
+
}
|
|
422
|
+
bold(text) {
|
|
423
|
+
return `*${text}*`;
|
|
424
|
+
}
|
|
425
|
+
italic(text) {
|
|
426
|
+
return `_${text}_`;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/representations/CompactGroupByRepresentation.ts
|
|
431
|
+
var NO_VULNS_FOUND_TEXT = "No vulnerabilities found";
|
|
432
|
+
var CompactGroupByRepresentation = class extends Representation {
|
|
433
|
+
composeByProperty(key) {
|
|
434
|
+
const grouped = this.groupFindings();
|
|
435
|
+
if (grouped.size === 0) {
|
|
436
|
+
return NO_VULNS_FOUND_TEXT;
|
|
437
|
+
}
|
|
438
|
+
return Array.from(grouped).map(([title, findings]) => {
|
|
439
|
+
findings.sort(findingsComparatorByKey(key));
|
|
440
|
+
const summary = findings.length === 0 ? NO_VULNS_FOUND_TEXT : this.composeCompactReport(findings, key);
|
|
441
|
+
return `${title}
|
|
442
|
+
${summary}`;
|
|
443
|
+
}).join("\n\n");
|
|
444
|
+
}
|
|
445
|
+
composeCompactReport(findings, key) {
|
|
446
|
+
return Object.entries(Object.groupBy(findings, (f) => f[key])).map(([prop, findings2]) => {
|
|
447
|
+
if (findings2 == null) {
|
|
448
|
+
return void 0;
|
|
522
449
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
450
|
+
return `${this.bold(this.extractEnumValue(key, prop))}: ${findings2.length}`;
|
|
451
|
+
}).filter((v) => v != null).join(", ");
|
|
452
|
+
}
|
|
453
|
+
extractEnumValue(key, prop) {
|
|
454
|
+
switch (key) {
|
|
455
|
+
case "level":
|
|
456
|
+
return SecurityLevel[Number(prop)];
|
|
457
|
+
case "severity":
|
|
458
|
+
return SecuritySeverity[Number(prop)];
|
|
459
|
+
default:
|
|
460
|
+
throw new Error("Unknown property:", key);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// src/representations/CompactGroupByRunRepresentation.ts
|
|
466
|
+
var CompactGroupByRunRepresentation = class extends CompactGroupByRepresentation {
|
|
467
|
+
constructor(model) {
|
|
468
|
+
super(model, "runId");
|
|
469
|
+
}
|
|
470
|
+
groupFindings() {
|
|
471
|
+
const result = /* @__PURE__ */ new Map();
|
|
472
|
+
for (const run of this._model.runs) {
|
|
473
|
+
const key = this.composeGroupTitle(run.id, run.toolName);
|
|
474
|
+
if (result.get(key) == null) {
|
|
475
|
+
result.set(key, []);
|
|
534
476
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
477
|
+
this._model.findings.filter((f) => f.runId === run.id).forEach((f) => result.get(key)?.push(f));
|
|
478
|
+
}
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
composeGroupTitle(runId, toolName) {
|
|
482
|
+
return `${this.italic(`[Run ${runId}]`)} ${this.bold(toolName)}`;
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// src/representations/CompactGroupByRunPerLevelRepresentation.ts
|
|
487
|
+
var CompactGroupByRunPerLevelRepresentation = class extends CompactGroupByRunRepresentation {
|
|
488
|
+
compose() {
|
|
489
|
+
return this.composeByProperty("level");
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// src/representations/CompactGroupByRunPerSeverityRepresentation.ts
|
|
494
|
+
var CompactGroupByRunPerSeverityRepresentation = class extends CompactGroupByRunRepresentation {
|
|
495
|
+
compose() {
|
|
496
|
+
return this.composeByProperty("severity");
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/representations/CompactGroupByToolNameRepresentation.ts
|
|
501
|
+
var CompactGroupByToolNameRepresentation = class extends CompactGroupByRepresentation {
|
|
502
|
+
constructor(model) {
|
|
503
|
+
super(model, "toolName");
|
|
504
|
+
}
|
|
505
|
+
groupFindings() {
|
|
506
|
+
const result = /* @__PURE__ */ new Map();
|
|
507
|
+
for (const run of this._model.runs) {
|
|
508
|
+
const key = this.composeGroupTitle(run.toolName);
|
|
509
|
+
if (result.get(key) == null) {
|
|
510
|
+
result.set(key, []);
|
|
544
511
|
}
|
|
512
|
+
this._model.findings.filter((f) => f.runId === run.id).forEach((f) => result.get(key)?.push(f));
|
|
545
513
|
}
|
|
546
|
-
return
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
composeGroupTitle(toolName) {
|
|
517
|
+
return this.bold(toolName);
|
|
547
518
|
}
|
|
548
519
|
};
|
|
549
520
|
|
|
550
|
-
// src/
|
|
551
|
-
var
|
|
552
|
-
|
|
553
|
-
|
|
521
|
+
// src/representations/CompactGroupByToolNamePerLevelRepresentation.ts
|
|
522
|
+
var CompactGroupByToolNamePerLevelRepresentation = class extends CompactGroupByToolNameRepresentation {
|
|
523
|
+
compose() {
|
|
524
|
+
return this.composeByProperty("level");
|
|
554
525
|
}
|
|
555
526
|
};
|
|
556
527
|
|
|
557
|
-
// src/
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (sarifFiles.length === 0) {
|
|
562
|
-
throw new Error(`No SARIF files found at the provided path: ${opts.sarifPath}`);
|
|
528
|
+
// src/representations/CompactGroupByToolNamePerSeverityRepresentation.ts
|
|
529
|
+
var CompactGroupByToolNamePerSeverityRepresentation = class extends CompactGroupByToolNameRepresentation {
|
|
530
|
+
compose() {
|
|
531
|
+
return this.composeByProperty("severity");
|
|
563
532
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// src/representations/CompactGroupBySarifRepresentation.ts
|
|
536
|
+
var import_node_path = __toESM(require("path"));
|
|
537
|
+
var CompactGroupBySarifRepresentation = class extends CompactGroupByRepresentation {
|
|
538
|
+
constructor(model) {
|
|
539
|
+
super(model, "sarifPath");
|
|
540
|
+
}
|
|
541
|
+
groupFindings() {
|
|
542
|
+
const result = /* @__PURE__ */ new Map();
|
|
543
|
+
for (let index = 0; index < this._model.sarifFiles.length; index++) {
|
|
544
|
+
const key = this.composeGroupTitle(this._model.sarifFiles[index], index);
|
|
545
|
+
if (result.get(key) == null) {
|
|
546
|
+
result.set(key, []);
|
|
547
|
+
}
|
|
548
|
+
this._model.findings.filter((f) => f.sarifPath === this._model.sarifFiles[index]).forEach((f) => result.get(key)?.push(f));
|
|
575
549
|
}
|
|
576
|
-
|
|
577
|
-
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
composeGroupTitle(sarifPath, index) {
|
|
553
|
+
return `${this.italic(`[File ${index + 1}]`)} ${this.bold(import_node_path.default.basename(sarifPath))}`;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// src/representations/CompactGroupBySarifPerLevelRepresentation.ts
|
|
558
|
+
var CompactGroupBySarifPerLevelRepresentation = class extends CompactGroupBySarifRepresentation {
|
|
559
|
+
compose() {
|
|
560
|
+
return this.composeByProperty("level");
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/representations/CompactGroupBySarifPerSeverityRepresentation.ts
|
|
565
|
+
var CompactGroupBySarifPerSeverityRepresentation = class extends CompactGroupBySarifRepresentation {
|
|
566
|
+
compose() {
|
|
567
|
+
return this.composeByProperty("severity");
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// src/representations/CompactTotalRepresentation.ts
|
|
572
|
+
var CompactTotalRepresentation = class extends CompactGroupByRepresentation {
|
|
573
|
+
groupFindings() {
|
|
574
|
+
const result = /* @__PURE__ */ new Map();
|
|
575
|
+
if (this._model.findings.length > 0) {
|
|
576
|
+
result.set("Total", this._model.findings);
|
|
578
577
|
}
|
|
579
|
-
|
|
580
|
-
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
// src/representations/CompactTotalPerSeverityRepresentation.ts
|
|
583
|
+
var CompactTotalPerSeverityRepresentation = class extends CompactTotalRepresentation {
|
|
584
|
+
compose() {
|
|
585
|
+
return this.composeByProperty("severity");
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
// src/representations/CompactTotalPerLevelRepresentation.ts
|
|
590
|
+
var CompactTotalPerLevelRepresentation = class extends CompactTotalRepresentation {
|
|
591
|
+
compose() {
|
|
592
|
+
return this.composeByProperty("level");
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
// src/representations/RepresentationFactory.ts
|
|
597
|
+
function createRepresentation(model, type = 3 /* CompactGroupByToolNamePerSeverity */) {
|
|
598
|
+
switch (type) {
|
|
599
|
+
case 0 /* CompactGroupByRunPerLevel */:
|
|
600
|
+
return new CompactGroupByRunPerLevelRepresentation(model);
|
|
601
|
+
case 1 /* CompactGroupByRunPerSeverity */:
|
|
602
|
+
return new CompactGroupByRunPerSeverityRepresentation(model);
|
|
603
|
+
case 2 /* CompactGroupByToolNamePerLevel */:
|
|
604
|
+
return new CompactGroupByToolNamePerLevelRepresentation(model);
|
|
605
|
+
case 3 /* CompactGroupByToolNamePerSeverity */:
|
|
606
|
+
return new CompactGroupByToolNamePerSeverityRepresentation(model);
|
|
607
|
+
case 4 /* CompactGroupBySarifPerLevel */:
|
|
608
|
+
return new CompactGroupBySarifPerLevelRepresentation(model);
|
|
609
|
+
case 5 /* CompactGroupBySarifPerSeverity */:
|
|
610
|
+
return new CompactGroupBySarifPerSeverityRepresentation(model);
|
|
611
|
+
case 6 /* CompactTotalPerLevel */:
|
|
612
|
+
return new CompactTotalPerLevelRepresentation(model);
|
|
613
|
+
case 7 /* CompactTotalPerSeverity */:
|
|
614
|
+
return new CompactTotalPerSeverityRepresentation(model);
|
|
615
|
+
default:
|
|
616
|
+
throw new Error(`Unknown representation type: ${type}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/utils/SarifUtils.ts
|
|
621
|
+
function findToolComponentDriver(run) {
|
|
622
|
+
return run.tool.driver;
|
|
623
|
+
}
|
|
624
|
+
function tryFindToolComponentExtension(run, result) {
|
|
625
|
+
let tool;
|
|
626
|
+
if (result.rule?.toolComponent?.index != null) {
|
|
627
|
+
tool = run.tool.extensions?.[result.rule.toolComponent.index];
|
|
628
|
+
}
|
|
629
|
+
return tool;
|
|
630
|
+
}
|
|
631
|
+
function findToolComponent(run, result) {
|
|
632
|
+
return tryFindToolComponentExtension(run, result) ?? findToolComponentDriver(run);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/processors/CommonProcessor.ts
|
|
636
|
+
var CommonProcessor = class {
|
|
637
|
+
_run;
|
|
638
|
+
_result;
|
|
639
|
+
/**
|
|
640
|
+
* Creates an instance of {@link CommonProcessor} class.
|
|
641
|
+
* @param run An instance of {@link Run} object.
|
|
642
|
+
* @param result An instance of {@link Result} object.
|
|
643
|
+
*/
|
|
644
|
+
constructor(run, result) {
|
|
645
|
+
this._run = run;
|
|
646
|
+
this._result = result;
|
|
647
|
+
}
|
|
648
|
+
tryFindCvssScore() {
|
|
649
|
+
return this.tryFindRuleProperty("security-severity");
|
|
650
|
+
}
|
|
651
|
+
tryFindLevel() {
|
|
652
|
+
return this._result.level ?? this.tryFindRule()?.defaultConfiguration?.level;
|
|
653
|
+
}
|
|
654
|
+
findToolComponentDriver() {
|
|
655
|
+
return findToolComponentDriver(this._run);
|
|
656
|
+
}
|
|
657
|
+
tryFindToolComponentExtension() {
|
|
658
|
+
return tryFindToolComponentExtension(this._run, this._result);
|
|
659
|
+
}
|
|
660
|
+
findToolComponent() {
|
|
661
|
+
return findToolComponent(this._run, this._result);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* This function tries to find the respective rule for the given result.
|
|
665
|
+
* @internal
|
|
666
|
+
*/
|
|
667
|
+
tryFindRule() {
|
|
668
|
+
const ruleData = {};
|
|
669
|
+
if (this._result.rule) {
|
|
670
|
+
if (this._result.rule?.index != null) {
|
|
671
|
+
ruleData.index = this._result.rule.index;
|
|
672
|
+
}
|
|
673
|
+
if (this._result.rule?.id) {
|
|
674
|
+
ruleData.id = this._result.rule.id;
|
|
675
|
+
}
|
|
581
676
|
}
|
|
582
|
-
if (
|
|
583
|
-
|
|
677
|
+
if (ruleData.index == null && this._result.ruleIndex != null) {
|
|
678
|
+
ruleData.index = this._result.ruleIndex;
|
|
679
|
+
}
|
|
680
|
+
if (!ruleData.id && this._result.ruleId) {
|
|
681
|
+
ruleData.id = this._result.ruleId;
|
|
682
|
+
}
|
|
683
|
+
const tool = this.findToolComponent();
|
|
684
|
+
if (ruleData.index != null && tool?.rules && ruleData.index < tool.rules.length) {
|
|
685
|
+
return tool.rules[ruleData.index];
|
|
686
|
+
}
|
|
687
|
+
if (ruleData.id && tool?.rules) {
|
|
688
|
+
return tool.rules.find(
|
|
689
|
+
(r) => r.id === ruleData.id
|
|
690
|
+
);
|
|
584
691
|
}
|
|
585
|
-
|
|
692
|
+
return void 0;
|
|
586
693
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
694
|
+
/**
|
|
695
|
+
* This function searches respective rule for the given result, and then gets
|
|
696
|
+
* the property of interest from it.
|
|
697
|
+
* @param propertyName The property name that you want to get the value from.
|
|
698
|
+
* @protected
|
|
699
|
+
*/
|
|
700
|
+
tryFindRuleProperty(propertyName) {
|
|
701
|
+
const rule = this.tryFindRule();
|
|
702
|
+
if (rule?.properties && propertyName in rule.properties) {
|
|
703
|
+
return rule.properties[propertyName];
|
|
704
|
+
}
|
|
705
|
+
return void 0;
|
|
593
706
|
}
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// src/processors/SnykProcessor.ts
|
|
710
|
+
var SnykProcessor = class extends CommonProcessor {
|
|
594
711
|
/**
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
712
|
+
* Rules in SARIF files produced by Snyk Open Source has additional "cvssv3_baseScore"
|
|
713
|
+
* property where CVSS score is also defined. This method tries to get level
|
|
714
|
+
* from this "cvssv3_baseScore" property and if it fails to do so, then it tries
|
|
715
|
+
* to get CVSS score in a common way.
|
|
598
716
|
*/
|
|
599
|
-
|
|
600
|
-
return this.
|
|
717
|
+
tryFindCvssScore() {
|
|
718
|
+
return this.tryFindRuleProperty("cvssv3_baseScore") ?? super.tryFindCvssScore();
|
|
601
719
|
}
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
// src/processors/CodeQLProcessor.ts
|
|
723
|
+
var CodeQLProcessor = class extends CommonProcessor {
|
|
602
724
|
/**
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
* @public
|
|
725
|
+
* Rules in SARIF files produced by CodeQL has additional "problem.severity"
|
|
726
|
+
* property where level is also defined. This method tries to get level in a
|
|
727
|
+
* common way but if it fails to do so, then it tries to get level from
|
|
728
|
+
* "problem.severity" property.
|
|
608
729
|
*/
|
|
609
|
-
|
|
610
|
-
|
|
730
|
+
tryFindLevel() {
|
|
731
|
+
return super.tryFindLevel() ?? this.tryFindRuleProperty("problem.severity");
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/processors/ProcessorFactory.ts
|
|
736
|
+
function createProcessor(run, result) {
|
|
737
|
+
const toolComponent = findToolComponent(run, result);
|
|
738
|
+
switch (toolComponent.name) {
|
|
739
|
+
case "CodeQL":
|
|
740
|
+
return new CodeQLProcessor(run, result);
|
|
741
|
+
case "Snyk Open Source":
|
|
742
|
+
return new SnykProcessor(run, result);
|
|
743
|
+
default:
|
|
744
|
+
return new CommonProcessor(run, result);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/model/Finding.ts
|
|
749
|
+
function createFinding(opts) {
|
|
750
|
+
return new SarifFinding(opts);
|
|
751
|
+
}
|
|
752
|
+
var SarifFinding = class {
|
|
753
|
+
_runMetadata;
|
|
754
|
+
_result;
|
|
755
|
+
_sarifPath;
|
|
756
|
+
_rule;
|
|
757
|
+
_processor;
|
|
758
|
+
_cvssScoreCacheProcessed = false;
|
|
759
|
+
_cvssScoreCache = void 0;
|
|
760
|
+
_levelCacheProcessed = false;
|
|
761
|
+
_levelCache = void 0;
|
|
762
|
+
constructor(opts) {
|
|
763
|
+
this._processor = createProcessor(opts.runMetadata.run, opts.result);
|
|
764
|
+
this._sarifPath = opts.sarifPath;
|
|
765
|
+
this._runMetadata = opts.runMetadata;
|
|
766
|
+
this._result = opts.result;
|
|
767
|
+
this._rule = this._processor.tryFindRule();
|
|
768
|
+
}
|
|
769
|
+
clone() {
|
|
770
|
+
return createFinding({
|
|
771
|
+
sarifPath: this._sarifPath,
|
|
772
|
+
runMetadata: this._runMetadata,
|
|
773
|
+
result: this._result
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
get sarifPath() {
|
|
777
|
+
return this._sarifPath;
|
|
778
|
+
}
|
|
779
|
+
get runId() {
|
|
780
|
+
return this._runMetadata.id;
|
|
781
|
+
}
|
|
782
|
+
get toolName() {
|
|
783
|
+
return this._processor.findToolComponent().name;
|
|
784
|
+
}
|
|
785
|
+
get cvssScore() {
|
|
786
|
+
if (!this._cvssScoreCacheProcessed) {
|
|
787
|
+
this._cvssScoreCacheProcessed = true;
|
|
788
|
+
this._cvssScoreCache = this._processor.tryFindCvssScore();
|
|
789
|
+
}
|
|
790
|
+
return this._cvssScoreCache;
|
|
791
|
+
}
|
|
792
|
+
get level() {
|
|
793
|
+
if (!this._levelCacheProcessed) {
|
|
794
|
+
this._levelCacheProcessed = true;
|
|
795
|
+
this._levelCache = this._processor.tryFindLevel();
|
|
796
|
+
}
|
|
797
|
+
if (this._levelCache === void 0) {
|
|
798
|
+
Logger.debug(`Unknown level of ${this._rule?.id} rule`);
|
|
799
|
+
return 0 /* Unknown */;
|
|
800
|
+
}
|
|
801
|
+
switch (this._levelCache) {
|
|
802
|
+
case "error":
|
|
803
|
+
return 4 /* Error */;
|
|
804
|
+
case "warning":
|
|
805
|
+
return 3 /* Warning */;
|
|
806
|
+
case "note":
|
|
807
|
+
return 2 /* Note */;
|
|
808
|
+
default:
|
|
809
|
+
return 1 /* None */;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
get severity() {
|
|
813
|
+
if (this.cvssScore == null || this.cvssScore < 0 || this.cvssScore > 10) {
|
|
814
|
+
Logger.debug(`Unsupported CVSS score ${this.cvssScore} in ${this._rule?.id} rule`);
|
|
815
|
+
return 0 /* Unknown */;
|
|
816
|
+
}
|
|
817
|
+
if (this.cvssScore >= 9) {
|
|
818
|
+
return 5 /* Critical */;
|
|
819
|
+
}
|
|
820
|
+
if (this.cvssScore >= 7) {
|
|
821
|
+
return 4 /* High */;
|
|
822
|
+
}
|
|
823
|
+
if (this.cvssScore >= 4) {
|
|
824
|
+
return 3 /* Medium */;
|
|
825
|
+
}
|
|
826
|
+
if (this.cvssScore >= 0.1) {
|
|
827
|
+
return 2 /* Low */;
|
|
828
|
+
}
|
|
829
|
+
return 1 /* None */;
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
// src/SarifToSlackClient.ts
|
|
834
|
+
var SarifToSlackClient = class _SarifToSlackClient {
|
|
835
|
+
_message;
|
|
836
|
+
_sarifModel;
|
|
837
|
+
_sendIf = 20 /* Always */;
|
|
838
|
+
constructor(log) {
|
|
839
|
+
Logger.initialize(log);
|
|
611
840
|
System.initialize();
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
841
|
+
}
|
|
842
|
+
static *createRunIdGenerator() {
|
|
843
|
+
let runId = 1;
|
|
844
|
+
while (true) {
|
|
845
|
+
yield runId++;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
static async create(opts) {
|
|
849
|
+
const instance = new _SarifToSlackClient(opts.log);
|
|
850
|
+
Logger.trace("opts", opts);
|
|
851
|
+
instance._sendIf = opts.sendIf ?? instance._sendIf;
|
|
852
|
+
instance._sarifModel = await _SarifToSlackClient.buildModel(opts.sarif);
|
|
853
|
+
Logger.trace("instance._sarifModel", instance._sarifModel);
|
|
854
|
+
instance._message = await _SarifToSlackClient.initialize(instance._sarifModel, opts);
|
|
855
|
+
Logger.trace("instance._message", instance._message);
|
|
615
856
|
return instance;
|
|
616
857
|
}
|
|
858
|
+
static async buildModel(sarifOpts) {
|
|
859
|
+
const sarifFiles = extractListOfFiles(sarifOpts);
|
|
860
|
+
if (sarifFiles.length === 0) {
|
|
861
|
+
throw new Error(`No SARIF files found at the provided path: ${sarifOpts.path}`);
|
|
862
|
+
}
|
|
863
|
+
const model = { sarifFiles, runs: [], findings: new FindingsArray() };
|
|
864
|
+
const runIdGenerator = _SarifToSlackClient.createRunIdGenerator();
|
|
865
|
+
for (const sarifPath of sarifFiles) {
|
|
866
|
+
const sarifJson = await import_fs2.promises.readFile(sarifPath, "utf8");
|
|
867
|
+
const sarifLog = JSON.parse(sarifJson);
|
|
868
|
+
for (const run of sarifLog.runs) {
|
|
869
|
+
const runId = runIdGenerator.next();
|
|
870
|
+
let runMetadata = void 0;
|
|
871
|
+
for (const result of run.results ?? []) {
|
|
872
|
+
runMetadata = {
|
|
873
|
+
id: runId.value,
|
|
874
|
+
run,
|
|
875
|
+
toolName: findToolComponent(run, result).name
|
|
876
|
+
};
|
|
877
|
+
model.findings.push(createFinding({ sarifPath, result, runMetadata }));
|
|
878
|
+
}
|
|
879
|
+
runMetadata ??= {
|
|
880
|
+
id: runId.value,
|
|
881
|
+
run,
|
|
882
|
+
toolName: findToolComponentDriver(run).name
|
|
883
|
+
};
|
|
884
|
+
model.runs.push(runMetadata);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return model;
|
|
888
|
+
}
|
|
617
889
|
/**
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
* @
|
|
621
|
-
* @
|
|
890
|
+
* The main function to initialize a list of {@link SlackMessage} objects based
|
|
891
|
+
* on the given SARIF file(s).
|
|
892
|
+
* @param sarifModel An instance of {@link SarifModel} object.
|
|
893
|
+
* @param opts An instance of {@link SarifToSlackClientOptions} object.
|
|
894
|
+
* @returns A map where key is the SARIF file and value is an instance of
|
|
895
|
+
* {@link SlackMessage} object
|
|
896
|
+
* @private
|
|
622
897
|
*/
|
|
623
|
-
async
|
|
624
|
-
|
|
625
|
-
|
|
898
|
+
static async initialize(sarifModel, opts) {
|
|
899
|
+
const message = new SlackMessageBuilder(opts.webhookUrl, {
|
|
900
|
+
username: opts.username,
|
|
901
|
+
iconUrl: opts.iconUrl,
|
|
902
|
+
color: identifyColor(sarifModel.findings, opts.color),
|
|
903
|
+
representation: createRepresentation(sarifModel, opts.representation)
|
|
904
|
+
});
|
|
905
|
+
if (opts.header?.include) {
|
|
906
|
+
message.withHeader(opts.header?.value);
|
|
626
907
|
}
|
|
908
|
+
if (opts.footer?.include) {
|
|
909
|
+
message.withFooter(opts.footer?.value, opts.footer?.type);
|
|
910
|
+
}
|
|
911
|
+
if (opts.actor?.include) {
|
|
912
|
+
message.withActor(opts.actor?.value);
|
|
913
|
+
}
|
|
914
|
+
if (opts.run?.include) {
|
|
915
|
+
message.withRun();
|
|
916
|
+
}
|
|
917
|
+
return message;
|
|
627
918
|
}
|
|
628
919
|
/**
|
|
629
|
-
* Sends a Slack message
|
|
630
|
-
* @param sarifPath - The path of the SARIF file for which the message should be sent.
|
|
920
|
+
* Sends a Slack message.
|
|
631
921
|
* @returns A promise that resolves when the message has been sent.
|
|
632
922
|
* @throws Error if a Slack message was not prepared for the given SARIF path.
|
|
633
923
|
* @public
|
|
634
924
|
*/
|
|
635
|
-
async send(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
925
|
+
async send() {
|
|
926
|
+
if (this._sarifModel == null) {
|
|
927
|
+
throw new Error("Could not parse SARIF file(s).");
|
|
928
|
+
}
|
|
929
|
+
if (this.shouldSendMessage) {
|
|
930
|
+
if (this._message == null) {
|
|
931
|
+
throw new Error("Slack message was not prepared.");
|
|
932
|
+
}
|
|
933
|
+
const text = await this._message.send();
|
|
934
|
+
Logger.info("Message sent. Status:", text);
|
|
935
|
+
} else {
|
|
936
|
+
Logger.info("Message was not sent based on the sendIf parameter:", SendIf[this._sendIf]);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
get shouldSendMessage() {
|
|
940
|
+
if (this._sendIf == null) {
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
switch (this._sendIf) {
|
|
944
|
+
case 0 /* SeverityCritical */:
|
|
945
|
+
return this._sarifModel?.findings.findByProperty("severity", 5 /* Critical */) != null;
|
|
946
|
+
case 1 /* SeverityHigh */:
|
|
947
|
+
return this._sarifModel?.findings.findByProperty("severity", 4 /* High */) != null;
|
|
948
|
+
case 2 /* SeverityHighOrHigher */:
|
|
949
|
+
return !!this._sarifModel?.findings.hasSeverityOrHigher(4 /* High */);
|
|
950
|
+
case 3 /* SeverityMedium */:
|
|
951
|
+
return this._sarifModel?.findings.findByProperty("severity", 3 /* Medium */) != null;
|
|
952
|
+
case 4 /* SeverityMediumOrHigher */:
|
|
953
|
+
return !!this._sarifModel?.findings.hasSeverityOrHigher(3 /* Medium */);
|
|
954
|
+
case 5 /* SeverityLow */:
|
|
955
|
+
return this._sarifModel?.findings.findByProperty("severity", 2 /* Low */) != null;
|
|
956
|
+
case 6 /* SeverityLowOrHigher */:
|
|
957
|
+
return !!this._sarifModel?.findings.hasSeverityOrHigher(2 /* Low */);
|
|
958
|
+
case 7 /* SeverityNone */:
|
|
959
|
+
return this._sarifModel?.findings.findByProperty("severity", 1 /* None */) != null;
|
|
960
|
+
case 8 /* SeverityNoneOrHigher */:
|
|
961
|
+
return !!this._sarifModel?.findings.hasSeverityOrHigher(1 /* None */);
|
|
962
|
+
case 9 /* SeverityUnknown */:
|
|
963
|
+
return this._sarifModel?.findings.findByProperty("severity", 0 /* Unknown */) != null;
|
|
964
|
+
case 10 /* SeverityUnknownOrHigher */:
|
|
965
|
+
return !!this._sarifModel?.findings.hasSeverityOrHigher(0 /* Unknown */);
|
|
966
|
+
case 11 /* LevelError */:
|
|
967
|
+
return this._sarifModel?.findings.findByProperty("level", 4 /* Error */) != null;
|
|
968
|
+
case 12 /* LevelWarning */:
|
|
969
|
+
return this._sarifModel?.findings.findByProperty("level", 3 /* Warning */) != null;
|
|
970
|
+
case 13 /* LevelWarningOrHigher */:
|
|
971
|
+
return !!this._sarifModel?.findings.hasLevelOrHigher(3 /* Warning */);
|
|
972
|
+
case 14 /* LevelNote */:
|
|
973
|
+
return this._sarifModel?.findings.findByProperty("level", 2 /* Note */) != null;
|
|
974
|
+
case 15 /* LevelNoteOrHigher */:
|
|
975
|
+
return !!this._sarifModel?.findings.hasLevelOrHigher(2 /* Note */);
|
|
976
|
+
case 16 /* LevelNone */:
|
|
977
|
+
return this._sarifModel?.findings.findByProperty("level", 1 /* None */) != null;
|
|
978
|
+
case 17 /* LevelNoneOrHigher */:
|
|
979
|
+
return !!this._sarifModel?.findings.hasLevelOrHigher(1 /* None */);
|
|
980
|
+
case 18 /* LevelUnknown */:
|
|
981
|
+
return this._sarifModel?.findings.findByProperty("level", 0 /* Unknown */) != null;
|
|
982
|
+
case 19 /* LevelUnknownOrHigher */:
|
|
983
|
+
return !!this._sarifModel?.findings.hasLevelOrHigher(0 /* Unknown */);
|
|
984
|
+
case 20 /* Always */:
|
|
985
|
+
return true;
|
|
986
|
+
case 21 /* Some */:
|
|
987
|
+
return (this._sarifModel?.findings.length ?? 0) > 0;
|
|
988
|
+
case 22 /* Empty */:
|
|
989
|
+
return (this._sarifModel?.findings.length ?? 0) === 0;
|
|
990
|
+
case 23 /* Never */:
|
|
991
|
+
return false;
|
|
992
|
+
default:
|
|
993
|
+
throw new Error(`Unknown sendIf parameter: ${this._sendIf}`);
|
|
639
994
|
}
|
|
640
|
-
const text = await message.send();
|
|
641
|
-
Logger.info(`Message sent for ${sarifPath} file. Status:`, text);
|
|
642
995
|
}
|
|
643
996
|
};
|
|
644
997
|
// Annotate the CommonJS export names for ESM import in node:
|
|
645
998
|
0 && (module.exports = {
|
|
646
|
-
|
|
999
|
+
Color,
|
|
647
1000
|
FooterType,
|
|
648
|
-
GroupResultsBy,
|
|
649
1001
|
LogLevel,
|
|
650
|
-
|
|
1002
|
+
RepresentationType,
|
|
1003
|
+
SarifToSlackClient,
|
|
1004
|
+
SendIf
|
|
651
1005
|
});
|