@fabasoad/sarif-to-slack 1.2.3 → 1.3.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/dist/SarifToSlackClient.js +2 -2
- package/dist/index.cjs +227 -133
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/model/color/Color.d.ts +26 -0
- package/dist/model/color/Color.d.ts.map +1 -0
- package/dist/model/color/Color.js +46 -0
- package/dist/model/color/ColorIdentification.d.ts +2 -0
- package/dist/model/color/ColorIdentification.d.ts.map +1 -0
- package/dist/model/color/ColorIdentification.js +186 -0
- package/dist/model/{Color.d.ts → color/ColorOptions.d.ts} +10 -32
- package/dist/model/color/ColorOptions.d.ts.map +1 -0
- package/dist/model/color/ColorOptions.js +2 -0
- package/dist/sarif-to-slack.d.ts +17 -14
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/etc/sarif-to-slack.api.md +8 -4
- package/package.json +1 -1
- package/src/SarifToSlackClient.ts +1 -1
- package/src/index.ts +3 -2
- package/src/model/color/Color.ts +50 -0
- package/src/model/color/ColorIdentification.ts +198 -0
- package/src/model/color/ColorOptions.ts +63 -0
- package/src/types.ts +1 -1
- package/tests/integration/SendSarifToSlack.spec.ts +13 -13
- package/dist/model/Color.d.ts.map +0 -1
- package/dist/model/Color.js +0 -115
- package/src/model/Color.ts +0 -206
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import Finding from '../Finding'
|
|
2
|
+
import FindingArray from '../FindingArray'
|
|
3
|
+
import { SecurityLevel, SecuritySeverity } from '../../types'
|
|
4
|
+
import Logger from '../../Logger'
|
|
5
|
+
import {
|
|
6
|
+
ColorGroupByLevel,
|
|
7
|
+
ColorGroupBySeverity,
|
|
8
|
+
ColorGroupCommon,
|
|
9
|
+
ColorOptions
|
|
10
|
+
} from './ColorOptions'
|
|
11
|
+
import { Color } from './Color';
|
|
12
|
+
|
|
13
|
+
function logColorTaken(color: Color | undefined, prop: string): void {
|
|
14
|
+
Logger.debug(`Message has ${color?.color} color taken from '${prop}' property.`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function logPropDefinedButNoFindings<K extends keyof Pick<Finding, 'level' | 'severity'>>(key: K, val: string): void {
|
|
18
|
+
const prop: string = key === 'level' ? 'byLevel' : 'bySeverity'
|
|
19
|
+
Logger.trace(`'${prop}.${val}' property is defined but no findings with "${val}" ${key} is found. Continue color identification...`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function logPropIsNotDefined<K extends keyof Pick<Finding, 'level' | 'severity'>>(key: K, val: string): void {
|
|
23
|
+
const prop: string = key === 'level' ? 'byLevel' : 'bySeverity'
|
|
24
|
+
Logger.trace(`'${prop}.${val}' property is not defined. Continue color identification...`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function identifyColorCommon<K extends keyof Pick<Finding, 'level' | 'severity'>>(
|
|
28
|
+
findings: FindingArray,
|
|
29
|
+
prop: K,
|
|
30
|
+
none: Finding[K],
|
|
31
|
+
unknown: Finding[K],
|
|
32
|
+
color: ColorGroupCommon
|
|
33
|
+
): string | undefined {
|
|
34
|
+
if (color.none) {
|
|
35
|
+
if (findings.findByProperty(prop, none) != null) {
|
|
36
|
+
logColorTaken(color.none, `${prop === 'severity' ? 'bySeverity' : 'byLevel'}.none`)
|
|
37
|
+
return color.none.color
|
|
38
|
+
} else {
|
|
39
|
+
logPropDefinedButNoFindings(prop, 'none')
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
logPropIsNotDefined(prop, 'none')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (color.unknown) {
|
|
46
|
+
if (findings.findByProperty(prop, unknown) != null) {
|
|
47
|
+
logColorTaken(color.unknown, `${prop === 'severity' ? 'bySeverity' : 'byLevel'}.unknown`)
|
|
48
|
+
return color.unknown.color
|
|
49
|
+
} else {
|
|
50
|
+
logPropDefinedButNoFindings(prop, 'unknown')
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
logPropIsNotDefined(prop, 'unknown')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function identifyColorBySeverity(findings: FindingArray, color: ColorGroupBySeverity): string | undefined {
|
|
60
|
+
if (color.critical) {
|
|
61
|
+
if (findings.findByProperty('severity', SecuritySeverity.Critical) != null) {
|
|
62
|
+
logColorTaken(color.critical, 'bySeverity.critical')
|
|
63
|
+
return color.critical.color
|
|
64
|
+
} else {
|
|
65
|
+
logPropDefinedButNoFindings('severity', 'critical')
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
logPropIsNotDefined('severity', 'critical')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (color.high) {
|
|
72
|
+
if (findings.findByProperty('severity', SecuritySeverity.High) != null) {
|
|
73
|
+
logColorTaken(color.high, 'bySeverity.high')
|
|
74
|
+
return color.high.color
|
|
75
|
+
} else {
|
|
76
|
+
logPropDefinedButNoFindings('severity', 'high')
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
logPropIsNotDefined('severity', 'high')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (color.medium) {
|
|
83
|
+
if (findings.findByProperty('severity', SecuritySeverity.Medium) != null) {
|
|
84
|
+
logColorTaken(color.medium, 'bySeverity.medium')
|
|
85
|
+
return color.medium.color
|
|
86
|
+
} else {
|
|
87
|
+
logPropDefinedButNoFindings('severity', 'medium')
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
logPropIsNotDefined('severity', 'medium')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (color.low) {
|
|
94
|
+
if (findings.findByProperty('severity', SecuritySeverity.Low) != null) {
|
|
95
|
+
logColorTaken(color.low, 'bySeverity.low')
|
|
96
|
+
return color.low.color
|
|
97
|
+
} else {
|
|
98
|
+
logPropDefinedButNoFindings('severity', 'low')
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
logPropIsNotDefined('severity', 'low')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return identifyColorCommon(findings, 'severity', SecuritySeverity.None, SecuritySeverity.Unknown, color)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function identifyColorByLevel(findings: FindingArray, color: ColorGroupByLevel): string | undefined {
|
|
108
|
+
if (color.error) {
|
|
109
|
+
if (findings.findByProperty('level', SecurityLevel.Error) != null) {
|
|
110
|
+
logColorTaken(color.error, 'byLevel.error')
|
|
111
|
+
return color.error.color
|
|
112
|
+
} else {
|
|
113
|
+
logPropDefinedButNoFindings('level', 'error')
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
logPropIsNotDefined('level', 'error')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (color.warning) {
|
|
120
|
+
if (findings.findByProperty('level', SecurityLevel.Warning) != null) {
|
|
121
|
+
logColorTaken(color.warning, 'byLevel.warning')
|
|
122
|
+
return color.warning.color
|
|
123
|
+
} else {
|
|
124
|
+
logPropDefinedButNoFindings('level', 'warning')
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
logPropIsNotDefined('level', 'warning')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (color.note != null) {
|
|
131
|
+
if (findings.findByProperty('level', SecurityLevel.Note) != null) {
|
|
132
|
+
logColorTaken(color.note, 'byLevel.note')
|
|
133
|
+
return color.note.color
|
|
134
|
+
} else {
|
|
135
|
+
logPropDefinedButNoFindings('level', 'note')
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
logPropIsNotDefined('level', 'note')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return identifyColorCommon(findings, 'level', SecurityLevel.None, SecurityLevel.Unknown, color)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Makes an ultimate decision on what color should be Slack message. The decision
|
|
146
|
+
* is based on the provided {@param colorOpts} parameter and {@param findings}
|
|
147
|
+
* list.
|
|
148
|
+
* @param findings An instance of {@link FindingArray} object.
|
|
149
|
+
* @param colorOpts An instance of {@link ColorOptions} type.
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
export function identifyColor(findings: FindingArray, colorOpts?: ColorOptions): string | undefined {
|
|
153
|
+
if (!colorOpts) {
|
|
154
|
+
Logger.debug('Message has no color as color options are not defined.')
|
|
155
|
+
return undefined
|
|
156
|
+
}
|
|
157
|
+
Logger.trace(`Identifying color for ${findings.length} findings and the following color options:`, JSON.stringify(colorOpts, null, 2))
|
|
158
|
+
|
|
159
|
+
if (colorOpts.bySeverity) {
|
|
160
|
+
const color: string | undefined = identifyColorBySeverity(findings, colorOpts.bySeverity)
|
|
161
|
+
if (color) {
|
|
162
|
+
return color
|
|
163
|
+
}
|
|
164
|
+
Logger.trace('None of the properties in \'bySeverity\' group is applicable. Continue color identification...')
|
|
165
|
+
} else {
|
|
166
|
+
Logger.trace('\'bySeverity\' group is not defined. Continue color identification...')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (colorOpts.byLevel) {
|
|
170
|
+
const color: string | undefined = identifyColorByLevel(findings, colorOpts.byLevel)
|
|
171
|
+
if (color) {
|
|
172
|
+
return color
|
|
173
|
+
}
|
|
174
|
+
Logger.trace('None of the properties in \'byLevel\' group is applicable. Continue color identification...')
|
|
175
|
+
} else {
|
|
176
|
+
Logger.trace('\'byLevel\' group is not defined. Continue color identification...')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (findings.length === 0) {
|
|
180
|
+
Logger.trace('There are no findings in the provided SARIF file(s). Checking if color is defined in "empty" property...')
|
|
181
|
+
if (colorOpts.empty?.color) {
|
|
182
|
+
logColorTaken(colorOpts.empty, 'empty')
|
|
183
|
+
return colorOpts.empty.color
|
|
184
|
+
} else {
|
|
185
|
+
Logger.trace('"empty" color is not defined. Continue color identification...')
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
Logger.trace(`"empty" color is not taken into account because there are ${findings.length} findings in the provided SARIF file(s). Continue color identification...`)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (colorOpts.default?.color) {
|
|
192
|
+
logColorTaken(colorOpts.default, 'default')
|
|
193
|
+
} else {
|
|
194
|
+
Logger.debug('Message has no color as none of the defined color options is applicable.')
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return colorOpts?.default?.color
|
|
198
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Color } from './Color'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base type that has common fields for both {@link ColorGroupByLevel} and
|
|
5
|
+
* {@link ColorGroupBySeverity}.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export type ColorGroupCommon = {
|
|
9
|
+
none?: Color,
|
|
10
|
+
unknown?: Color,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Color schema for the findings with the certain level. Color is used by the
|
|
15
|
+
* level importance, i.e. if at least 1 error finding exists then
|
|
16
|
+
* {@link ColorGroupByLevel#error} color is used, then if at least 1 warning
|
|
17
|
+
* finding exists then {@link ColorGroupByLevel#warning} color is used, etc.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type ColorGroupByLevel = ColorGroupCommon & {
|
|
21
|
+
error?: Color,
|
|
22
|
+
warning?: Color,
|
|
23
|
+
note?: Color,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Color schema for the findings with the certain severity. Color is used by the
|
|
28
|
+
* severity importance, i.e. if at least 1 critical finding exists then
|
|
29
|
+
* {@link ColorGroupBySeverity#critical} color is used, then if at least 1 high
|
|
30
|
+
* finding exists then {@link ColorGroupBySeverity#high} color is used, etc.
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export type ColorGroupBySeverity = ColorGroupCommon & {
|
|
34
|
+
critical?: Color,
|
|
35
|
+
high?: Color,
|
|
36
|
+
medium?: Color,
|
|
37
|
+
low?: Color,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Represents configuration of the color scheme. If both {@link ColorOptions#byLevel}
|
|
42
|
+
* and {@link ColorOptions#bySeverity} are defined, then {@link ColorOptions#bySeverity}
|
|
43
|
+
* takes precedence.
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export type ColorOptions = {
|
|
47
|
+
/**
|
|
48
|
+
* Default color if specific color was not found. It is a fallback option.
|
|
49
|
+
*/
|
|
50
|
+
default?: Color,
|
|
51
|
+
/**
|
|
52
|
+
* Color scheme for the findings where certain level is presented.
|
|
53
|
+
*/
|
|
54
|
+
byLevel?: ColorGroupByLevel,
|
|
55
|
+
/**
|
|
56
|
+
* Color scheme for the findings where certain severity is presented.
|
|
57
|
+
*/
|
|
58
|
+
bySeverity?: ColorGroupBySeverity,
|
|
59
|
+
/**
|
|
60
|
+
* Color when no findings are found.
|
|
61
|
+
*/
|
|
62
|
+
empty?: Color,
|
|
63
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -107,22 +107,22 @@ describe('(integration): SendSarifToSlack', (): void => {
|
|
|
107
107
|
username: process.env.SARIF_TO_SLACK_USERNAME,
|
|
108
108
|
iconUrl: process.env.SARIF_TO_SLACK_ICON_URL,
|
|
109
109
|
color: {
|
|
110
|
-
default:
|
|
111
|
-
empty:
|
|
110
|
+
default: Color.from(process.env.SARIF_TO_SLACK_COLOR),
|
|
111
|
+
empty: Color.from(process.env.SARIF_TO_SLACK_COLOR_EMPTY),
|
|
112
112
|
byLevel: {
|
|
113
|
-
error:
|
|
114
|
-
warning:
|
|
115
|
-
note:
|
|
116
|
-
none:
|
|
117
|
-
unknown:
|
|
113
|
+
error: Color.from(process.env.SARIF_TO_SLACK_COLOR_ERROR),
|
|
114
|
+
warning: Color.from(process.env.SARIF_TO_SLACK_COLOR_WARNING),
|
|
115
|
+
note: Color.from(process.env.SARIF_TO_SLACK_COLOR_NOTE),
|
|
116
|
+
none: Color.from(process.env.SARIF_TO_SLACK_COLOR_NONE),
|
|
117
|
+
unknown: Color.from(process.env.SARIF_TO_SLACK_COLOR_UNKNOWN),
|
|
118
118
|
},
|
|
119
119
|
bySeverity: {
|
|
120
|
-
critical:
|
|
121
|
-
high:
|
|
122
|
-
medium:
|
|
123
|
-
low:
|
|
124
|
-
none:
|
|
125
|
-
unknown:
|
|
120
|
+
critical: Color.from(process.env.SARIF_TO_SLACK_COLOR_CRITICAL),
|
|
121
|
+
high: Color.from(process.env.SARIF_TO_SLACK_COLOR_HIGH),
|
|
122
|
+
medium: Color.from(process.env.SARIF_TO_SLACK_COLOR_MEDIUM),
|
|
123
|
+
low: Color.from(process.env.SARIF_TO_SLACK_COLOR_LOW),
|
|
124
|
+
none: Color.from(process.env.SARIF_TO_SLACK_COLOR_NONE),
|
|
125
|
+
unknown: Color.from(process.env.SARIF_TO_SLACK_COLOR_UNKNOWN),
|
|
126
126
|
},
|
|
127
127
|
},
|
|
128
128
|
sarif: {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Color.d.ts","sourceRoot":"","sources":["../../src/model/Color.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAQ;IAEhC;;;;;;;;OAQG;gBACgB,KAAK,CAAC,EAAE,MAAM;IAKjC;;OAEG;IACH,IAAW,KAAK,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,QAAQ;CAcjB;AAED;;;GAGG;AACH,KAAK,gBAAgB,GAAG;IACtB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG;IACjD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC;CACd,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG;IACpD,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;OAEG;IACH,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAA"}
|
package/dist/model/Color.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { SecurityLevel, SecuritySeverity } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* This class represents a color in hex format.
|
|
4
|
-
* @public
|
|
5
|
-
*/
|
|
6
|
-
export class Color {
|
|
7
|
-
_color;
|
|
8
|
-
/**
|
|
9
|
-
* Creates an instance of {@link Color} class. Before creating an instance of
|
|
10
|
-
* {@link Color} class, it (if applicable) maps CI status into the hex color,
|
|
11
|
-
* and also validates color parameter to be a valid string that represents a
|
|
12
|
-
* color in hex format.
|
|
13
|
-
* @param color - Can be either undefined, valid color in hex format or GitHub
|
|
14
|
-
* CI status (one of: success, failure, cancelled, skipped)
|
|
15
|
-
* @public
|
|
16
|
-
*/
|
|
17
|
-
constructor(color) {
|
|
18
|
-
this._color = this.mapColor(color);
|
|
19
|
-
this.assertHexColor();
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Returns a valid string that represents a color in hex format, or undefined.
|
|
23
|
-
*/
|
|
24
|
-
get value() {
|
|
25
|
-
return this._color;
|
|
26
|
-
}
|
|
27
|
-
assertHexColor() {
|
|
28
|
-
if (this._color) {
|
|
29
|
-
const hexColorRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
|
|
30
|
-
if (!hexColorRegex.test(this._color)) {
|
|
31
|
-
throw new Error(`Invalid hex color: "${this._color}"`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
mapColor(from) {
|
|
36
|
-
switch (from) {
|
|
37
|
-
case 'success':
|
|
38
|
-
return '#008000';
|
|
39
|
-
case 'failure':
|
|
40
|
-
return '#ff0000';
|
|
41
|
-
case 'cancelled':
|
|
42
|
-
return '#0047ab';
|
|
43
|
-
case 'skipped':
|
|
44
|
-
return '#808080';
|
|
45
|
-
default:
|
|
46
|
-
return from;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function identifyColorCommon(findings, prop, none, unknown, color) {
|
|
51
|
-
if (color.none != null && findings.findByProperty(prop, none) != null) {
|
|
52
|
-
return color.none.value;
|
|
53
|
-
}
|
|
54
|
-
if (color.unknown != null && findings.findByProperty(prop, unknown) != null) {
|
|
55
|
-
return color.unknown.value;
|
|
56
|
-
}
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
function identifyColorBySeverity(findings, color) {
|
|
60
|
-
if (color.critical != null && findings.findByProperty('severity', SecuritySeverity.Critical) != null) {
|
|
61
|
-
return color.critical.value;
|
|
62
|
-
}
|
|
63
|
-
if (color.high != null && findings.findByProperty('severity', SecuritySeverity.High) != null) {
|
|
64
|
-
return color.high.value;
|
|
65
|
-
}
|
|
66
|
-
if (color.medium != null && findings.findByProperty('severity', SecuritySeverity.Medium) != null) {
|
|
67
|
-
return color.medium.value;
|
|
68
|
-
}
|
|
69
|
-
if (color.low != null && findings.findByProperty('severity', SecuritySeverity.Low) != null) {
|
|
70
|
-
return color.low.value;
|
|
71
|
-
}
|
|
72
|
-
return identifyColorCommon(findings, 'severity', SecuritySeverity.None, SecuritySeverity.Unknown, color);
|
|
73
|
-
}
|
|
74
|
-
function identifyColorByLevel(findings, color) {
|
|
75
|
-
if (color.error != null && findings.findByProperty('level', SecurityLevel.Error) != null) {
|
|
76
|
-
return color.error.value;
|
|
77
|
-
}
|
|
78
|
-
if (color.warning != null && findings.findByProperty('level', SecurityLevel.Warning) != null) {
|
|
79
|
-
return color.warning.value;
|
|
80
|
-
}
|
|
81
|
-
if (color.note != null && findings.findByProperty('level', SecurityLevel.Note) != null) {
|
|
82
|
-
return color.note.value;
|
|
83
|
-
}
|
|
84
|
-
return identifyColorCommon(findings, 'level', SecurityLevel.None, SecurityLevel.Unknown, color);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Makes an ultimate decision on what color should be Slack message. The decision
|
|
88
|
-
* is based on the provided {@param colorOpts} parameter and {@param findings}
|
|
89
|
-
* list.
|
|
90
|
-
* @param findings An instance of {@link FindingArray} object.
|
|
91
|
-
* @param colorOpts An instance of {@link ColorOptions} type.
|
|
92
|
-
* @internal
|
|
93
|
-
*/
|
|
94
|
-
export function identifyColor(findings, colorOpts) {
|
|
95
|
-
if (!colorOpts) {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
if (colorOpts.bySeverity) {
|
|
99
|
-
const color = identifyColorBySeverity(findings, colorOpts.bySeverity);
|
|
100
|
-
if (color !== undefined) {
|
|
101
|
-
return color;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (colorOpts.byLevel) {
|
|
105
|
-
const color = identifyColorByLevel(findings, colorOpts.byLevel);
|
|
106
|
-
if (color !== undefined) {
|
|
107
|
-
return color;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (findings.length === 0 && colorOpts.empty?.value !== undefined) {
|
|
111
|
-
return colorOpts.empty.value;
|
|
112
|
-
}
|
|
113
|
-
return colorOpts?.default?.value;
|
|
114
|
-
}
|
|
115
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29sb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwvQ29sb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGFBQWEsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUkxRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sS0FBSztJQUNDLE1BQU0sQ0FBUztJQUVoQzs7Ozs7Ozs7T0FRRztJQUNILFlBQW1CLEtBQWM7UUFDL0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBQ2xDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQTtJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLEtBQUs7UUFDZCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUE7SUFDcEIsQ0FBQztJQUVPLGNBQWM7UUFDcEIsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDaEIsTUFBTSxhQUFhLEdBQUcsb0VBQW9FLENBQUE7WUFFMUYsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7Z0JBQ3JDLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFBO1lBQ3hELENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVPLFFBQVEsQ0FBQyxJQUFhO1FBQzVCLFFBQVEsSUFBSSxFQUFFLENBQUM7WUFDYixLQUFLLFNBQVM7Z0JBQ1osT0FBTyxTQUFTLENBQUE7WUFDbEIsS0FBSyxTQUFTO2dCQUNaLE9BQU8sU0FBUyxDQUFBO1lBQ2xCLEtBQUssV0FBVztnQkFDZCxPQUFPLFNBQVMsQ0FBQTtZQUNsQixLQUFLLFNBQVM7Z0JBQ1osT0FBTyxTQUFTLENBQUE7WUFDbEI7Z0JBQ0UsT0FBTyxJQUFJLENBQUE7UUFDZixDQUFDO0lBQ0gsQ0FBQztDQUNGO0FB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
|
package/src/model/Color.ts
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { SecurityLevel, SecuritySeverity } from '../types'
|
|
2
|
-
import Finding from './Finding'
|
|
3
|
-
import FindingArray from './FindingArray'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* This class represents a color in hex format.
|
|
7
|
-
* @public
|
|
8
|
-
*/
|
|
9
|
-
export class Color {
|
|
10
|
-
private readonly _color?: string
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Creates an instance of {@link Color} class. Before creating an instance of
|
|
14
|
-
* {@link Color} class, it (if applicable) maps CI status into the hex color,
|
|
15
|
-
* and also validates color parameter to be a valid string that represents a
|
|
16
|
-
* color in hex format.
|
|
17
|
-
* @param color - Can be either undefined, valid color in hex format or GitHub
|
|
18
|
-
* CI status (one of: success, failure, cancelled, skipped)
|
|
19
|
-
* @public
|
|
20
|
-
*/
|
|
21
|
-
public constructor(color?: string) {
|
|
22
|
-
this._color = this.mapColor(color)
|
|
23
|
-
this.assertHexColor()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Returns a valid string that represents a color in hex format, or undefined.
|
|
28
|
-
*/
|
|
29
|
-
public get value(): string | undefined {
|
|
30
|
-
return this._color
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private assertHexColor(): void {
|
|
34
|
-
if (this._color) {
|
|
35
|
-
const hexColorRegex = /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/
|
|
36
|
-
|
|
37
|
-
if (!hexColorRegex.test(this._color)) {
|
|
38
|
-
throw new Error(`Invalid hex color: "${this._color}"`)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private mapColor(from?: string): string | undefined {
|
|
44
|
-
switch (from) {
|
|
45
|
-
case 'success':
|
|
46
|
-
return '#008000'
|
|
47
|
-
case 'failure':
|
|
48
|
-
return '#ff0000'
|
|
49
|
-
case 'cancelled':
|
|
50
|
-
return '#0047ab'
|
|
51
|
-
case 'skipped':
|
|
52
|
-
return '#808080'
|
|
53
|
-
default:
|
|
54
|
-
return from
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Base type that has common fields for both {@link ColorGroupByLevel} and
|
|
61
|
-
* {@link ColorGroupBySeverity}.
|
|
62
|
-
*/
|
|
63
|
-
type ColorGroupCommon = {
|
|
64
|
-
none?: Color,
|
|
65
|
-
unknown?: Color,
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Color schema for the findings with the certain level. Color is used by the
|
|
70
|
-
* level importance, i.e. if at least 1 error finding exists then
|
|
71
|
-
* {@link ColorGroupByLevel.error} color is used, then if at least 1 warning
|
|
72
|
-
* finding exists then {@link ColorGroupByLevel.warning} color is used, etc.
|
|
73
|
-
* @public
|
|
74
|
-
*/
|
|
75
|
-
export type ColorGroupByLevel = ColorGroupCommon & {
|
|
76
|
-
error?: Color,
|
|
77
|
-
warning?: Color,
|
|
78
|
-
note?: Color,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Color schema for the findings with the certain severity. Color is used by the
|
|
83
|
-
* severity importance, i.e. if at least 1 critical finding exists then
|
|
84
|
-
* {@link ColorGroupBySeverity.critical} color is used, then if at least 1 high
|
|
85
|
-
* finding exists then {@link ColorGroupBySeverity.high} color is used, etc.
|
|
86
|
-
* @public
|
|
87
|
-
*/
|
|
88
|
-
export type ColorGroupBySeverity = ColorGroupCommon & {
|
|
89
|
-
critical?: Color,
|
|
90
|
-
high?: Color,
|
|
91
|
-
medium?: Color,
|
|
92
|
-
low?: Color,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Represents configuration of the color scheme. If both {@link ColorOptions.byLevel}
|
|
97
|
-
* and {@link ColorOptions.bySeverity} are defined, then {@link ColorOptions.bySeverity}
|
|
98
|
-
* takes precedence.
|
|
99
|
-
* @public
|
|
100
|
-
*/
|
|
101
|
-
export type ColorOptions = {
|
|
102
|
-
/**
|
|
103
|
-
* Default color if specific color was not found. It is a fallback option.
|
|
104
|
-
*/
|
|
105
|
-
default?: Color,
|
|
106
|
-
/**
|
|
107
|
-
* Color scheme for the findings where certain level is presented.
|
|
108
|
-
*/
|
|
109
|
-
byLevel?: ColorGroupByLevel,
|
|
110
|
-
/**
|
|
111
|
-
* Color scheme for the findings where certain severity is presented.
|
|
112
|
-
*/
|
|
113
|
-
bySeverity?: ColorGroupBySeverity,
|
|
114
|
-
/**
|
|
115
|
-
* Color when no findings are found.
|
|
116
|
-
*/
|
|
117
|
-
empty?: Color,
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function identifyColorCommon<K extends keyof Finding>(
|
|
121
|
-
findings: FindingArray,
|
|
122
|
-
prop: K,
|
|
123
|
-
none: Finding[K],
|
|
124
|
-
unknown: Finding[K],
|
|
125
|
-
color: ColorGroupCommon
|
|
126
|
-
): string | undefined {
|
|
127
|
-
if (color.none != null && findings.findByProperty(prop, none) != null) {
|
|
128
|
-
return color.none.value
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (color.unknown != null && findings.findByProperty(prop, unknown) != null) {
|
|
132
|
-
return color.unknown.value
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return undefined
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function identifyColorBySeverity(findings: FindingArray, color: ColorGroupBySeverity): string | undefined {
|
|
139
|
-
if (color.critical != null && findings.findByProperty('severity', SecuritySeverity.Critical) != null) {
|
|
140
|
-
return color.critical.value
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (color.high != null && findings.findByProperty('severity', SecuritySeverity.High) != null) {
|
|
144
|
-
return color.high.value
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (color.medium != null && findings.findByProperty('severity', SecuritySeverity.Medium) != null) {
|
|
148
|
-
return color.medium.value
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (color.low != null && findings.findByProperty('severity', SecuritySeverity.Low) != null) {
|
|
152
|
-
return color.low.value
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return identifyColorCommon(findings, 'severity', SecuritySeverity.None, SecuritySeverity.Unknown, color)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function identifyColorByLevel(findings: FindingArray, color: ColorGroupByLevel): string | undefined {
|
|
159
|
-
if (color.error != null && findings.findByProperty('level', SecurityLevel.Error) != null) {
|
|
160
|
-
return color.error.value
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (color.warning != null && findings.findByProperty('level', SecurityLevel.Warning) != null) {
|
|
164
|
-
return color.warning.value
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (color.note != null && findings.findByProperty('level', SecurityLevel.Note) != null) {
|
|
168
|
-
return color.note.value
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return identifyColorCommon(findings, 'level', SecurityLevel.None, SecurityLevel.Unknown, color)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Makes an ultimate decision on what color should be Slack message. The decision
|
|
176
|
-
* is based on the provided {@param colorOpts} parameter and {@param findings}
|
|
177
|
-
* list.
|
|
178
|
-
* @param findings An instance of {@link FindingArray} object.
|
|
179
|
-
* @param colorOpts An instance of {@link ColorOptions} type.
|
|
180
|
-
* @internal
|
|
181
|
-
*/
|
|
182
|
-
export function identifyColor(findings: FindingArray, colorOpts?: ColorOptions): string | undefined {
|
|
183
|
-
if (!colorOpts) {
|
|
184
|
-
return undefined
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (colorOpts.bySeverity) {
|
|
188
|
-
const color: string | undefined = identifyColorBySeverity(findings, colorOpts.bySeverity)
|
|
189
|
-
if (color !== undefined) {
|
|
190
|
-
return color
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (colorOpts.byLevel) {
|
|
195
|
-
const color: string | undefined = identifyColorByLevel(findings, colorOpts.byLevel)
|
|
196
|
-
if (color !== undefined) {
|
|
197
|
-
return color
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (findings.length === 0 && colorOpts.empty?.value !== undefined) {
|
|
202
|
-
return colorOpts.empty.value
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return colorOpts?.default?.value
|
|
206
|
-
}
|