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