@fabasoad/sarif-to-slack 0.2.4 → 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 +148 -76
- 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 +2 -0
- package/dist/System.d.ts.map +1 -0
- package/dist/System.js +15 -0
- package/dist/index.cjs +843 -467
- 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 +19 -53
- package/etc/sarif-to-slack.api.md +161 -99
- package/jest.config.json +2 -2
- package/package.json +9 -9
- package/scripts/save-metadata.sh +15 -0
- package/src/Logger.ts +4 -0
- package/src/SarifToSlackClient.ts +202 -0
- package/src/SlackMessageBuilder.ts +35 -115
- package/src/System.ts +16 -0
- 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 +19 -71
- 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 -102
- 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/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -11
- package/scripts/save-version.sh +0 -13
- package/src/Processors.ts +0 -68
- package/src/SarifToSlackService.ts +0 -115
- package/src/model/SarifModelPerRun.ts +0 -123
- package/src/model/SarifModelPerSarif.ts +0 -126
- package/src/model/types.ts +0 -50
- package/src/utils/SortUtils.ts +0 -33
- package/src/version.ts +0 -10
- package/tests/Processors.spec.ts +0 -76
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import CompactGroupBySarifRepresentation
|
|
2
|
+
from './CompactGroupBySarifRepresentation'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Since {@link CompactGroupBySarifRepresentation} is an abstract class, the only
|
|
6
|
+
* question that this class should "answer" is what property should be used in
|
|
7
|
+
* the compact representation. In this case it is "severity".
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export default class CompactGroupBySarifPerSeverityRepresentation extends CompactGroupBySarifRepresentation {
|
|
11
|
+
|
|
12
|
+
public override compose(): string {
|
|
13
|
+
return this.composeByProperty('severity')
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { Finding } from '../model/Finding'
|
|
3
|
+
import CompactGroupByRepresentation from './CompactGroupByRepresentation'
|
|
4
|
+
import { SarifModel } from '../types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Since {@link CompactGroupByRepresentation} already prepares compact representation
|
|
8
|
+
* of findings, this class defines a grouping rule. In this case it groups
|
|
9
|
+
* findings by SARIF file. Every SARIF file will be grouped separately, such as:
|
|
10
|
+
* @example
|
|
11
|
+
* ```text
|
|
12
|
+
* grype-results-01.sarif
|
|
13
|
+
* Error: 1, Warning: 4
|
|
14
|
+
* grype-results-02.sarif
|
|
15
|
+
* Warning: 1, Note: 20
|
|
16
|
+
* ```
|
|
17
|
+
* @internal
|
|
18
|
+
* It is an abstract class, so the only question that derived classes should
|
|
19
|
+
* "answer" is what property should be used in the compact representation, such
|
|
20
|
+
* as "level" and "severity".
|
|
21
|
+
*/
|
|
22
|
+
export default abstract class CompactGroupBySarifRepresentation extends CompactGroupByRepresentation {
|
|
23
|
+
|
|
24
|
+
public constructor(model: SarifModel) {
|
|
25
|
+
super(model, 'sarifPath')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected override groupFindings(): Map<string, Finding[]> {
|
|
29
|
+
const result = new Map<string, Finding[]>()
|
|
30
|
+
for (let index = 0; index < this._model.sarifFiles.length; index++) {
|
|
31
|
+
const key: string = this.composeGroupTitle(this._model.sarifFiles[index], index)
|
|
32
|
+
if (result.get(key) == null) {
|
|
33
|
+
result.set(key, [])
|
|
34
|
+
}
|
|
35
|
+
this._model.findings
|
|
36
|
+
.filter((f: Finding): boolean => f.sarifPath === this._model.sarifFiles[index])
|
|
37
|
+
.forEach((f: Finding) => result.get(key)?.push(f))
|
|
38
|
+
}
|
|
39
|
+
return result
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private composeGroupTitle(sarifPath: string, index: number): string {
|
|
43
|
+
return `${this.italic(`[File ${index + 1}]`)} ${this.bold(path.basename(sarifPath))}`
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import CompactGroupByToolNameRepresentation
|
|
2
|
+
from './CompactGroupByToolNameRepresentation'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Since {@link CompactGroupByToolNameRepresentation} is an abstract class, the
|
|
6
|
+
* only question that this class should "answer" is what property should be used
|
|
7
|
+
* in the compact representation. In this case it is "level".
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export default class CompactGroupByToolNamePerLevelRepresentation extends CompactGroupByToolNameRepresentation {
|
|
11
|
+
|
|
12
|
+
public override compose(): string {
|
|
13
|
+
return this.composeByProperty('level')
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import CompactGroupByToolNameRepresentation
|
|
2
|
+
from './CompactGroupByToolNameRepresentation'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Since {@link CompactGroupByToolNameRepresentation} is an abstract class, the
|
|
6
|
+
* only question that this class should "answer" is what property should be used
|
|
7
|
+
* in the compact representation. In this case it is "severity".
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export default class CompactGroupByToolNamePerSeverityRepresentation extends CompactGroupByToolNameRepresentation {
|
|
11
|
+
|
|
12
|
+
public override compose(): string {
|
|
13
|
+
return this.composeByProperty('severity')
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Finding } from '../model/Finding'
|
|
2
|
+
import CompactGroupByRepresentation from './CompactGroupByRepresentation'
|
|
3
|
+
import { SarifModel } from '../types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Since {@link CompactGroupByRepresentation} already prepares compact representation
|
|
7
|
+
* of findings, this class defines a grouping rule. In this case it groups
|
|
8
|
+
* findings by tool name. Every tool name will be grouped separately, such as:
|
|
9
|
+
* @example
|
|
10
|
+
* ```text
|
|
11
|
+
* Grype
|
|
12
|
+
* Error: 1, Warning: 4
|
|
13
|
+
* Trivy
|
|
14
|
+
* Warning: 1, Note: 20
|
|
15
|
+
* ```
|
|
16
|
+
* @internal
|
|
17
|
+
* It is an abstract class, so the only question that derived classes should
|
|
18
|
+
* "answer" is what property should be used in the compact representation, such
|
|
19
|
+
* as "level" and "severity".
|
|
20
|
+
*/
|
|
21
|
+
export default abstract class CompactGroupByToolNameRepresentation extends CompactGroupByRepresentation {
|
|
22
|
+
|
|
23
|
+
public constructor(model: SarifModel) {
|
|
24
|
+
super(model, 'toolName')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected override groupFindings(): Map<string, Finding[]> {
|
|
28
|
+
const result = new Map<string, Finding[]>()
|
|
29
|
+
for (const run of this._model.runs) {
|
|
30
|
+
const key: string = this.composeGroupTitle(run.toolName)
|
|
31
|
+
if (result.get(key) == null) {
|
|
32
|
+
result.set(key, [])
|
|
33
|
+
}
|
|
34
|
+
this._model.findings
|
|
35
|
+
.filter((f: Finding): boolean => f.runId === run.id)
|
|
36
|
+
.forEach((f: Finding) => result.get(key)?.push(f))
|
|
37
|
+
}
|
|
38
|
+
return result
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private composeGroupTitle(toolName: string): string {
|
|
42
|
+
return this.bold(toolName)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import CompactTotalRepresentation from './CompactTotalRepresentation'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Since {@link CompactTotalRepresentation} is an abstract class, the only
|
|
5
|
+
* question that this class should "answer" is what property should be used in
|
|
6
|
+
* the compact representation. In this case it is "level".
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export default class CompactTotalPerLevelRepresentation extends CompactTotalRepresentation {
|
|
10
|
+
|
|
11
|
+
public override compose(): string {
|
|
12
|
+
return this.composeByProperty('level')
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import CompactTotalRepresentation from './CompactTotalRepresentation'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Since {@link CompactTotalRepresentation} is an abstract class, the only
|
|
5
|
+
* question that this class should "answer" is what property should be used in
|
|
6
|
+
* the compact representation. In this case it is "severity".
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export default class CompactTotalPerSeverityRepresentation extends CompactTotalRepresentation {
|
|
10
|
+
|
|
11
|
+
public override compose(): string {
|
|
12
|
+
return this.composeByProperty('severity')
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import CompactGroupByRepresentation from './CompactGroupByRepresentation'
|
|
2
|
+
import { Finding } from '../model/Finding'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Since {@link CompactGroupByRepresentation} already prepares compact representation
|
|
6
|
+
* of findings, this class defines a grouping rule. In this case it does not really
|
|
7
|
+
* group, but just add everything under the same "Total" group, such as:
|
|
8
|
+
* @example
|
|
9
|
+
* ```text
|
|
10
|
+
* Total
|
|
11
|
+
* Warning: 1, Note: 20
|
|
12
|
+
* ```
|
|
13
|
+
* @internal
|
|
14
|
+
* It is an abstract class, so the only question that derived classes should
|
|
15
|
+
* "answer" is what property should be used in the compact representation, such
|
|
16
|
+
* as "level" and "severity".
|
|
17
|
+
*/
|
|
18
|
+
export default abstract class CompactTotalRepresentation extends CompactGroupByRepresentation {
|
|
19
|
+
|
|
20
|
+
protected override groupFindings(): Map<string, Finding[]> {
|
|
21
|
+
const result = new Map<string, Finding[]>()
|
|
22
|
+
if (this._model.findings.length > 0) {
|
|
23
|
+
result.set('Total', this._model.findings)
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SarifModel } from '../types'
|
|
2
|
+
import { Finding } from '../model/Finding'
|
|
3
|
+
import { findingsComparatorByKey } from '../utils/Comparators'
|
|
4
|
+
import FindingsArray from '../model/FindingsArray'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The most base abstract class for the representation. Every representation class
|
|
8
|
+
* must be derived from this class implicitly or explicitly.
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export default abstract class Representation {
|
|
12
|
+
protected readonly _model: SarifModel
|
|
13
|
+
|
|
14
|
+
public constructor(model: SarifModel, findingSortKey: keyof Finding = 'level') {
|
|
15
|
+
this._model = model
|
|
16
|
+
this._model.findings = model
|
|
17
|
+
.findings
|
|
18
|
+
.map((f: Finding): Finding => f.clone())
|
|
19
|
+
.sort(findingsComparatorByKey(findingSortKey))
|
|
20
|
+
.reduce((arr: FindingsArray, f: Finding): FindingsArray => {
|
|
21
|
+
arr.push(f)
|
|
22
|
+
return arr
|
|
23
|
+
}, new FindingsArray())
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protected bold(text: string): string {
|
|
27
|
+
return `*${text}*`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected italic(text: string): string {
|
|
31
|
+
return `_${text}_`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
abstract compose(): string
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Representation from './Representation'
|
|
2
|
+
import { RepresentationType, SarifModel } from '../types'
|
|
3
|
+
import CompactGroupByRunPerLevelRepresentation
|
|
4
|
+
from './CompactGroupByRunPerLevelRepresentation'
|
|
5
|
+
import CompactGroupByRunPerSeverityRepresentation
|
|
6
|
+
from './CompactGroupByRunPerSeverityRepresentation'
|
|
7
|
+
import CompactGroupByToolNamePerLevelRepresentation
|
|
8
|
+
from './CompactGroupByToolNamePerLevelRepresentation'
|
|
9
|
+
import CompactGroupByToolNamePerSeverityRepresentation
|
|
10
|
+
from './CompactGroupByToolNamePerSeverityRepresentation'
|
|
11
|
+
import CompactGroupBySarifPerLevelRepresentation
|
|
12
|
+
from './CompactGroupBySarifPerLevelRepresentation'
|
|
13
|
+
import CompactGroupBySarifPerSeverityRepresentation
|
|
14
|
+
from './CompactGroupBySarifPerSeverityRepresentation'
|
|
15
|
+
import CompactTotalPerSeverityRepresentation
|
|
16
|
+
from './CompactTotalPerSeverityRepresentation'
|
|
17
|
+
import CompactTotalPerLevelRepresentation
|
|
18
|
+
from './CompactTotalPerLevelRepresentation'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Factory class that creates a {@link Representation} class based on the provided
|
|
22
|
+
* {@link RepresentationType}.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export function createRepresentation(
|
|
26
|
+
model: SarifModel,
|
|
27
|
+
type: RepresentationType = RepresentationType.CompactGroupByToolNamePerSeverity
|
|
28
|
+
): Representation {
|
|
29
|
+
switch (type) {
|
|
30
|
+
case RepresentationType.CompactGroupByRunPerLevel:
|
|
31
|
+
return new CompactGroupByRunPerLevelRepresentation(model)
|
|
32
|
+
case RepresentationType.CompactGroupByRunPerSeverity:
|
|
33
|
+
return new CompactGroupByRunPerSeverityRepresentation(model)
|
|
34
|
+
case RepresentationType.CompactGroupByToolNamePerLevel:
|
|
35
|
+
return new CompactGroupByToolNamePerLevelRepresentation(model)
|
|
36
|
+
case RepresentationType.CompactGroupByToolNamePerSeverity:
|
|
37
|
+
return new CompactGroupByToolNamePerSeverityRepresentation(model)
|
|
38
|
+
case RepresentationType.CompactGroupBySarifPerLevel:
|
|
39
|
+
return new CompactGroupBySarifPerLevelRepresentation(model)
|
|
40
|
+
case RepresentationType.CompactGroupBySarifPerSeverity:
|
|
41
|
+
return new CompactGroupBySarifPerSeverityRepresentation(model)
|
|
42
|
+
case RepresentationType.CompactTotalPerLevel:
|
|
43
|
+
return new CompactTotalPerLevelRepresentation(model)
|
|
44
|
+
case RepresentationType.CompactTotalPerSeverity:
|
|
45
|
+
return new CompactTotalPerSeverityRepresentation(model)
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`Unknown representation type: ${type}`)
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Type representing a SARIF log.
|
|
5
|
-
* @public
|
|
6
|
-
*/
|
|
7
|
-
export type SarifLog = Log
|
|
1
|
+
import { Run } from 'sarif'
|
|
2
|
+
import { Color, ColorOptions } from './model/Color'
|
|
3
|
+
import FindingsArray from './model/FindingsArray'
|
|
8
4
|
|
|
9
5
|
/**
|
|
10
6
|
* Interface for a Slack message that can be sent.
|
|
@@ -16,10 +12,10 @@ export interface SlackMessage {
|
|
|
16
12
|
* @returns A promise that resolves to the response from the Slack webhook.
|
|
17
13
|
*/
|
|
18
14
|
send: () => Promise<string>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
withActor(actor?: string): void
|
|
16
|
+
withFooter(text?: string, type?: FooterType): void
|
|
17
|
+
withHeader(header?: string): void
|
|
18
|
+
withRun(): void
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
/**
|
|
@@ -107,86 +103,307 @@ export type FooterOptions = IncludeAwareWithValueOptions & {
|
|
|
107
103
|
}
|
|
108
104
|
|
|
109
105
|
/**
|
|
110
|
-
*
|
|
106
|
+
* This represents what type of message should be sent. There are various options
|
|
107
|
+
* to show information from SARIF in Slack message.
|
|
111
108
|
* @public
|
|
112
109
|
*/
|
|
113
|
-
export enum
|
|
110
|
+
export enum RepresentationType {
|
|
111
|
+
/**
|
|
112
|
+
* Compact information about findings grouped by Run with the level representation.
|
|
113
|
+
* @example
|
|
114
|
+
* ```text
|
|
115
|
+
* [Run 1] Grype
|
|
116
|
+
* Error: 1, Warning: 4
|
|
117
|
+
* [Run 2] Grype
|
|
118
|
+
* Warning: 1, Note: 20
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
CompactGroupByRunPerLevel = 0,
|
|
122
|
+
/**
|
|
123
|
+
* Compact information about findings grouped by Run with the severity representation.
|
|
124
|
+
* @example
|
|
125
|
+
* ```text
|
|
126
|
+
* [Run 1] Grype
|
|
127
|
+
* Critical: 1, High: 3, Medium: 1
|
|
128
|
+
* [Run 2] Grype
|
|
129
|
+
* Medium: 1, Low: 20
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
CompactGroupByRunPerSeverity = 1,
|
|
133
|
+
/**
|
|
134
|
+
* Compact information about findings grouped by tool name with the level representation.
|
|
135
|
+
* @example
|
|
136
|
+
* ```text
|
|
137
|
+
* Grype
|
|
138
|
+
* Error: 1, Warning: 5, Note: 20
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
CompactGroupByToolNamePerLevel = 2,
|
|
114
142
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
143
|
+
* Compact information about findings grouped by tool name with the severity representation.
|
|
144
|
+
* @example
|
|
145
|
+
* ```text
|
|
146
|
+
* Grype
|
|
147
|
+
* Critical: 1, High: 3, Medium: 2, Low: 20
|
|
148
|
+
* ```
|
|
117
149
|
*/
|
|
118
|
-
|
|
150
|
+
CompactGroupByToolNamePerSeverity = 3,
|
|
119
151
|
/**
|
|
120
|
-
*
|
|
152
|
+
* Compact information about findings grouped by SARIF file with the level representation.
|
|
153
|
+
* @example
|
|
154
|
+
* ```text
|
|
155
|
+
* grype-results-01.sarif
|
|
156
|
+
* Error: 1, Warning: 2, Note: 1
|
|
157
|
+
* grype-results-02.sarif
|
|
158
|
+
* Warning: 3, Note: 19
|
|
159
|
+
* ```
|
|
121
160
|
*/
|
|
122
|
-
|
|
161
|
+
CompactGroupBySarifPerLevel = 4,
|
|
123
162
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
163
|
+
* Compact information about findings grouped by SARIF file with the severity
|
|
164
|
+
* representation.
|
|
165
|
+
* @example
|
|
166
|
+
* ```text
|
|
167
|
+
* grype-results-01.sarif
|
|
168
|
+
* High: 3, Medium: 1, Low: 11
|
|
169
|
+
* grype-results-02.sarif
|
|
170
|
+
* Critical: 1, Medium: 1, Low: 9
|
|
171
|
+
* ```
|
|
126
172
|
*/
|
|
127
|
-
|
|
173
|
+
CompactGroupBySarifPerSeverity = 5,
|
|
174
|
+
/**
|
|
175
|
+
* Compact information about findings with the level representation.
|
|
176
|
+
* @example
|
|
177
|
+
* ```text
|
|
178
|
+
* Total
|
|
179
|
+
* Error: 1, Warning: 5, Note: 20
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
CompactTotalPerLevel = 6,
|
|
183
|
+
/**
|
|
184
|
+
* Compact information about findings with the severity representation.
|
|
185
|
+
* @example
|
|
186
|
+
* ```text
|
|
187
|
+
* Total
|
|
188
|
+
* Critical: 1, High: 3, Medium: 2, Low: 20
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
CompactTotalPerSeverity = 7,
|
|
128
192
|
}
|
|
129
193
|
|
|
130
194
|
/**
|
|
131
|
-
*
|
|
195
|
+
* Options for logging.
|
|
132
196
|
* @public
|
|
133
197
|
*/
|
|
134
|
-
export
|
|
135
|
-
|
|
136
|
-
* Calculates results by the security level of the findings: Error, Warning,
|
|
137
|
-
* Note and Unknown. At first, it tries to get the security level from runs[].results[].level
|
|
138
|
-
* property. If it is not defined, it tries to get the security level from the
|
|
139
|
-
* respective rule of each result, using the rules[].properties['problem.severity']
|
|
140
|
-
* property.
|
|
141
|
-
*/
|
|
142
|
-
Level = 0,
|
|
198
|
+
export type LogOptions = {
|
|
199
|
+
level?: LogLevel,
|
|
143
200
|
/**
|
|
144
|
-
*
|
|
145
|
-
* Medium, Low, None and Unknown. it tries to get the security severity from the
|
|
146
|
-
* respective rule of each result, using the rules[].properties['security-severity']
|
|
147
|
-
* property. This property contains CVSS score, which is then mapped to the
|
|
148
|
-
* security severity value.
|
|
201
|
+
* More details here: https://github.com/fullstack-build/tslog?tab=readme-ov-file#pretty-templates-and-styles-color-settings
|
|
149
202
|
*/
|
|
150
|
-
|
|
203
|
+
template?: string,
|
|
204
|
+
colored?: boolean,
|
|
151
205
|
}
|
|
152
206
|
|
|
153
207
|
/**
|
|
154
|
-
*
|
|
208
|
+
* SARIF file extension.
|
|
155
209
|
* @public
|
|
156
210
|
*/
|
|
157
|
-
export type
|
|
158
|
-
|
|
159
|
-
|
|
211
|
+
export type SarifFileExtension = 'sarif' | 'json'
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Represents options for the provided SARIF file(s), such as path, should files
|
|
215
|
+
* from this path be retrieved recursively or not, and file extension.
|
|
216
|
+
* @public
|
|
217
|
+
*/
|
|
218
|
+
export type SarifOptions = {
|
|
219
|
+
path: string,
|
|
220
|
+
recursive?: boolean,
|
|
221
|
+
extension?: SarifFileExtension,
|
|
160
222
|
}
|
|
161
223
|
|
|
162
224
|
/**
|
|
163
|
-
*
|
|
225
|
+
* This enum represents the condition on when message should be sent. If this
|
|
226
|
+
* condition is satisfied then message is sent, otherwise - message is not sent.
|
|
164
227
|
* @public
|
|
165
228
|
*/
|
|
166
|
-
export
|
|
167
|
-
level?: LogLevel,
|
|
229
|
+
export enum SendIf {
|
|
168
230
|
/**
|
|
169
|
-
*
|
|
231
|
+
* Send message only if there is at least one finding with "Critical" severity.
|
|
232
|
+
* Since it is the higher possible severity, it is the same as "Critical" or
|
|
233
|
+
* higher.
|
|
170
234
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
235
|
+
SeverityCritical,
|
|
236
|
+
/**
|
|
237
|
+
* Send message only if there is at least one finding with "High" severity.
|
|
238
|
+
*/
|
|
239
|
+
SeverityHigh,
|
|
240
|
+
/**
|
|
241
|
+
* Send message only if there is at least one finding with "High" severity or
|
|
242
|
+
* higher, that includes "High" and "Critical".
|
|
243
|
+
*/
|
|
244
|
+
SeverityHighOrHigher,
|
|
245
|
+
/**
|
|
246
|
+
* Send message only if there is at least one finding with "Medium" severity.
|
|
247
|
+
*/
|
|
248
|
+
SeverityMedium,
|
|
249
|
+
/**
|
|
250
|
+
* Send message only if there is at least one finding with "Medium" severity
|
|
251
|
+
* or higher, that includes "Medium", "High" and "Critical".
|
|
252
|
+
*/
|
|
253
|
+
SeverityMediumOrHigher,
|
|
254
|
+
/**
|
|
255
|
+
* Send message only if there is at least one finding with "Low" severity.
|
|
256
|
+
*/
|
|
257
|
+
SeverityLow,
|
|
258
|
+
/**
|
|
259
|
+
* Send message only if there is at least one finding with "Low" severity or
|
|
260
|
+
* higher, that includes "Low", "Medium", "High" and "Critical".
|
|
261
|
+
*/
|
|
262
|
+
SeverityLowOrHigher,
|
|
263
|
+
/**
|
|
264
|
+
* Send message only if there is at least one finding with "None" severity.
|
|
265
|
+
*/
|
|
266
|
+
SeverityNone,
|
|
267
|
+
/**
|
|
268
|
+
* Send message only if there is at least one finding with "None" severity or
|
|
269
|
+
* higher, that includes "None", "Low", "Medium", "High" and "Critical".
|
|
270
|
+
*/
|
|
271
|
+
SeverityNoneOrHigher,
|
|
272
|
+
/**
|
|
273
|
+
* Send message only if there is at least one finding with "Unknown" severity.
|
|
274
|
+
*/
|
|
275
|
+
SeverityUnknown,
|
|
276
|
+
/**
|
|
277
|
+
* Send message only if there is at least one finding with "Unknown" severity
|
|
278
|
+
* or higher, that includes "Unknown", "None", "Low", "Medium", "High" and "Critical".
|
|
279
|
+
*/
|
|
280
|
+
SeverityUnknownOrHigher,
|
|
281
|
+
/**
|
|
282
|
+
* Send message only if there is at least one finding with "Error" level.
|
|
283
|
+
* Since it is the higher possible level, it is the same as "Error" or higher.
|
|
284
|
+
*/
|
|
285
|
+
LevelError,
|
|
286
|
+
/**
|
|
287
|
+
* Send message only if there is at least one finding with "Warning" level.
|
|
288
|
+
*/
|
|
289
|
+
LevelWarning,
|
|
290
|
+
/**
|
|
291
|
+
* Send message only if there is at least one finding with "Warning" level or
|
|
292
|
+
* higher, that includes "Warning" and "Error".
|
|
293
|
+
*/
|
|
294
|
+
LevelWarningOrHigher,
|
|
295
|
+
/**
|
|
296
|
+
* Send message only if there is at least one finding with "Note" level.
|
|
297
|
+
*/
|
|
298
|
+
LevelNote,
|
|
299
|
+
/**
|
|
300
|
+
* Send message only if there is at least one finding with "Note" level or
|
|
301
|
+
* higher, that includes "Note", "Warning" and "Error.
|
|
302
|
+
*/
|
|
303
|
+
LevelNoteOrHigher,
|
|
304
|
+
/**
|
|
305
|
+
* Send message only if there is at least one finding with "None" level.
|
|
306
|
+
*/
|
|
307
|
+
LevelNone,
|
|
308
|
+
/**
|
|
309
|
+
* Send message only if there is at least one finding with "None" level or
|
|
310
|
+
* higher, that includes "None", "Note", "Warning" and "Error.
|
|
311
|
+
*/
|
|
312
|
+
LevelNoneOrHigher,
|
|
313
|
+
/**
|
|
314
|
+
* Send message only if there is at least one finding with "Unknown" level.
|
|
315
|
+
*/
|
|
316
|
+
LevelUnknown,
|
|
317
|
+
/**
|
|
318
|
+
* Send message only if there is at least one finding with "Unknown" level or
|
|
319
|
+
* higher, that includes "Unknown", "None", "Note", "Warning" and "Error.
|
|
320
|
+
*/
|
|
321
|
+
LevelUnknownOrHigher,
|
|
322
|
+
/**
|
|
323
|
+
* Always send a message.
|
|
324
|
+
*/
|
|
325
|
+
Always,
|
|
326
|
+
/**
|
|
327
|
+
* Send a message if at least 1 vulnerability is found.
|
|
328
|
+
*/
|
|
329
|
+
Some,
|
|
330
|
+
/**
|
|
331
|
+
* Send a message only if no vulnerabilities are found.
|
|
332
|
+
*/
|
|
333
|
+
Empty,
|
|
334
|
+
/**
|
|
335
|
+
* Never send a message.
|
|
336
|
+
*/
|
|
337
|
+
Never,
|
|
173
338
|
}
|
|
174
339
|
|
|
175
340
|
/**
|
|
176
|
-
* Options for the
|
|
341
|
+
* Options for the SarifToSlackClient.
|
|
177
342
|
* @public
|
|
178
343
|
*/
|
|
179
|
-
export type
|
|
180
|
-
// The Slack webhook URL to send messages to.
|
|
344
|
+
export type SarifToSlackClientOptions = {
|
|
181
345
|
webhookUrl: string,
|
|
182
|
-
|
|
346
|
+
sarif: SarifOptions,
|
|
183
347
|
username?: string,
|
|
184
348
|
iconUrl?: string,
|
|
185
|
-
color?:
|
|
349
|
+
color?: Color | ColorOptions,
|
|
186
350
|
log?: LogOptions,
|
|
187
351
|
header?: IncludeAwareWithValueOptions,
|
|
188
352
|
footer?: FooterOptions,
|
|
189
353
|
actor?: IncludeAwareWithValueOptions,
|
|
190
354
|
run?: IncludeAwareOptions,
|
|
191
|
-
|
|
355
|
+
representation?: RepresentationType,
|
|
356
|
+
sendIf?: SendIf,
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Enum of security severity.
|
|
361
|
+
* @privateRemarks Order should remain unchanged. It is used in multiple places,
|
|
362
|
+
* such as sorting in Slack message (more important come first) and to identify
|
|
363
|
+
* provided severity if it is requested severity or higher.
|
|
364
|
+
* @internal
|
|
365
|
+
*/
|
|
366
|
+
export enum SecuritySeverity {
|
|
367
|
+
Unknown = 0,
|
|
368
|
+
None = 1,
|
|
369
|
+
Low = 2,
|
|
370
|
+
Medium = 3,
|
|
371
|
+
High = 4,
|
|
372
|
+
Critical = 5,
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Enum of security level.
|
|
377
|
+
* @privateRemarks Order should remain unchanged. It is used in multiple places,
|
|
378
|
+
* such as sorting in Slack message (more important come first) and to identify
|
|
379
|
+
* provided level if it is requested level or higher.
|
|
380
|
+
* @internal
|
|
381
|
+
*/
|
|
382
|
+
export enum SecurityLevel {
|
|
383
|
+
Unknown = 0,
|
|
384
|
+
None = 1,
|
|
385
|
+
Note = 2,
|
|
386
|
+
Warning = 3,
|
|
387
|
+
Error = 4,
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* The data about run, such as {@link Run} itself, tool name of the run and ID
|
|
392
|
+
* which is manually generated and unique within a single execution.
|
|
393
|
+
* @internal
|
|
394
|
+
*/
|
|
395
|
+
export type RunData = {
|
|
396
|
+
id: number,
|
|
397
|
+
run: Run,
|
|
398
|
+
toolName: string,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Model that is used by {@link Representation}
|
|
403
|
+
* @internal
|
|
404
|
+
*/
|
|
405
|
+
export type SarifModel = {
|
|
406
|
+
sarifFiles: string[],
|
|
407
|
+
runs: RunData[],
|
|
408
|
+
findings: FindingsArray,
|
|
192
409
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Finding } from '../model/Finding'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This function returns a comparator function based on the property of the
|
|
5
|
+
* {@link Finding} object.
|
|
6
|
+
* @param key Property name of the {@link Finding} object.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export function findingsComparatorByKey<K extends keyof Finding>(key: K): (a: Finding, b: Finding) => number {
|
|
10
|
+
return (a: Finding, b: Finding): number => {
|
|
11
|
+
switch (key) {
|
|
12
|
+
case 'severity': return b.severity - a.severity
|
|
13
|
+
case 'level': return b.level - a.level
|
|
14
|
+
case 'runId': return a.runId - b.runId
|
|
15
|
+
case 'toolName': return a.toolName.toLowerCase().localeCompare(b.toolName.toLowerCase())
|
|
16
|
+
default: return 1
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|