@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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  import { Run } from 'sarif'
2
- import { ColorOptions } from './model/Color'
2
+ import { ColorOptions } from './model/color/ColorOptions'
3
3
  import FindingArray from './model/FindingArray'
4
4
  import { SendIf } from './model/SendIf'
5
5
 
@@ -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: new Color(process.env.SARIF_TO_SLACK_COLOR),
111
- empty: new Color(process.env.SARIF_TO_SLACK_COLOR_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: new Color(process.env.SARIF_TO_SLACK_COLOR_ERROR),
114
- warning: new Color(process.env.SARIF_TO_SLACK_COLOR_WARNING),
115
- note: new Color(process.env.SARIF_TO_SLACK_COLOR_NOTE),
116
- none: new Color(process.env.SARIF_TO_SLACK_COLOR_NONE),
117
- unknown: new Color(process.env.SARIF_TO_SLACK_COLOR_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: new Color(process.env.SARIF_TO_SLACK_COLOR_CRITICAL),
121
- high: new Color(process.env.SARIF_TO_SLACK_COLOR_HIGH),
122
- medium: new Color(process.env.SARIF_TO_SLACK_COLOR_MEDIUM),
123
- low: new Color(process.env.SARIF_TO_SLACK_COLOR_LOW),
124
- none: new Color(process.env.SARIF_TO_SLACK_COLOR_NONE),
125
- unknown: new Color(process.env.SARIF_TO_SLACK_COLOR_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"}
@@ -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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
@@ -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
- }