@fabasoad/sarif-to-slack 0.1.1 → 0.2.1

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.
Files changed (99) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/pull_request_template.md +3 -3
  3. package/.github/workflows/linting.yml +14 -0
  4. package/.github/workflows/release.yml +5 -1
  5. package/.github/workflows/send-sarif-to-slack.yml +214 -0
  6. package/.github/workflows/unit-tests.yml +1 -0
  7. package/.pre-commit-config.yaml +3 -3
  8. package/.tool-versions +1 -1
  9. package/CONTRIBUTING.md +1 -1
  10. package/Makefile +10 -3
  11. package/README.md +36 -5
  12. package/biome.json +15 -12
  13. package/dist/Logger.js +17 -6
  14. package/dist/Processors.js +23 -22
  15. package/dist/SarifToSlackService.d.ts.map +1 -1
  16. package/dist/SarifToSlackService.js +6 -7
  17. package/dist/SlackMessageBuilder.js +51 -55
  18. package/dist/index.d.ts +9 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +10 -5
  21. package/dist/model/SarifModelPerRun.d.ts +17 -0
  22. package/dist/model/SarifModelPerRun.d.ts.map +1 -0
  23. package/dist/model/SarifModelPerRun.js +84 -0
  24. package/dist/model/SarifModelPerSarif.d.ts +20 -0
  25. package/dist/model/SarifModelPerSarif.d.ts.map +1 -0
  26. package/dist/model/SarifModelPerSarif.js +97 -0
  27. package/dist/model/types.d.ts +17 -0
  28. package/dist/model/types.d.ts.map +1 -0
  29. package/dist/model/types.js +31 -0
  30. package/dist/sarif-to-slack.d.ts +121 -18
  31. package/dist/tsdoc-metadata.json +1 -1
  32. package/dist/types.d.ts +107 -15
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/types.js +73 -7
  35. package/dist/utils/SarifUtils.d.ts +5 -0
  36. package/dist/utils/SarifUtils.d.ts.map +1 -0
  37. package/dist/utils/SarifUtils.js +32 -0
  38. package/dist/utils/SortUtils.d.ts +5 -0
  39. package/dist/utils/SortUtils.d.ts.map +1 -0
  40. package/dist/utils/SortUtils.js +8 -0
  41. package/dist/version.d.ts +2 -0
  42. package/dist/version.d.ts.map +1 -0
  43. package/dist/version.js +4 -0
  44. package/etc/sarif-to-slack.api.md +47 -9
  45. package/jest.config.json +4 -4
  46. package/package.json +14 -10
  47. package/scripts/save-version.sh +6 -0
  48. package/src/Logger.ts +22 -17
  49. package/src/Processors.ts +22 -22
  50. package/src/SarifToSlackService.ts +6 -7
  51. package/src/SlackMessageBuilder.ts +85 -68
  52. package/src/index.ts +17 -6
  53. package/src/model/SarifModelPerRun.ts +114 -0
  54. package/src/model/SarifModelPerSarif.ts +116 -0
  55. package/src/model/types.ts +31 -0
  56. package/src/types.ts +113 -15
  57. package/src/utils/SarifUtils.ts +44 -0
  58. package/src/utils/SortUtils.ts +21 -0
  59. package/src/version.ts +3 -0
  60. package/test-data/sarif/codeql-csharp.sarif +1 -0
  61. package/test-data/sarif/codeql-go.sarif +1 -0
  62. package/test-data/sarif/codeql-python.sarif +1 -0
  63. package/test-data/sarif/codeql-ruby.sarif +1 -0
  64. package/test-data/sarif/codeql-typescript.sarif +1 -0
  65. package/test-data/sarif/grype-container.sarif +1774 -0
  66. package/test-data/sarif/runs-1-tools-1-results-0.sarif +18 -0
  67. package/test-data/sarif/runs-2-tools-1-results-0.sarif +30 -0
  68. package/test-data/sarif/runs-2-tools-1.sarif +656 -0
  69. package/test-data/sarif/runs-2-tools-2-results-0.sarif +44 -0
  70. package/test-data/sarif/runs-2-tools-2.sarif +686 -0
  71. package/test-data/sarif/runs-3-tools-2-results-0.sarif +48 -0
  72. package/test-data/sarif/runs-3-tools-2.sarif +278 -0
  73. package/test-data/sarif/snyk-composer.sarif +934 -0
  74. package/test-data/sarif/snyk-container.sarif +313 -0
  75. package/test-data/sarif/snyk-gomodules.sarif +388 -0
  76. package/test-data/sarif/snyk-gradle.sarif +274 -0
  77. package/test-data/sarif/snyk-hex.sarif +66 -0
  78. package/test-data/sarif/snyk-maven.sarif +274 -0
  79. package/test-data/sarif/snyk-npm.sarif +896 -0
  80. package/test-data/sarif/snyk-nuget.sarif +90 -0
  81. package/test-data/sarif/snyk-pip.sarif +66 -0
  82. package/test-data/sarif/snyk-pnpm.sarif +90 -0
  83. package/test-data/sarif/snyk-poetry.sarif +1952 -0
  84. package/test-data/sarif/snyk-rubygems.sarif +440 -0
  85. package/test-data/sarif/snyk-sbt.sarif +178 -0
  86. package/test-data/sarif/snyk-swift.sarif +112 -0
  87. package/test-data/sarif/snyk-yarn.sarif +2900 -0
  88. package/test-data/sarif/trivy-iac.sarif +134 -0
  89. package/test-data/sarif/wiz-container.sarif +30916 -0
  90. package/test-data/sarif/wiz-iac.sarif +558 -0
  91. package/tests/Processors.spec.ts +3 -3
  92. package/tests/integration/SendSarifToSlack.spec.ts +56 -0
  93. package/tsconfig.json +14 -14
  94. package/dist/Logger.js.map +0 -1
  95. package/dist/Processors.js.map +0 -1
  96. package/dist/SarifToSlackService.js.map +0 -1
  97. package/dist/SlackMessageBuilder.js.map +0 -1
  98. package/dist/index.js.map +0 -1
  99. package/dist/types.js.map +0 -1
@@ -0,0 +1,114 @@
1
+ import type { Result, Run } from 'sarif';
2
+ import { tryGetRulePropertyByResult } from '../utils/SarifUtils'
3
+ import { SecurityLevel, SecuritySeverity } from './types'
4
+ import Logger from '../Logger'
5
+ import { Map as ImmutableMap } from 'immutable'
6
+ import {
7
+ sortSecurityLevelMap,
8
+ sortSecuritySeverityMap
9
+ } from '../utils/SortUtils';
10
+
11
+ export class SarifModelPerRun {
12
+ public readonly toolName: string
13
+
14
+ private readonly _securitySeverityMap: ImmutableMap<SecuritySeverity, number>
15
+ private readonly _securityLevelMap: ImmutableMap<SecurityLevel, number>
16
+
17
+ constructor(run: Run) {
18
+ this.toolName = run.tool.driver.name
19
+
20
+ this._securitySeverityMap = ImmutableMap<SecuritySeverity, number>().asMutable()
21
+ this._securityLevelMap = ImmutableMap<SecurityLevel, number>().asMutable()
22
+
23
+ this.buildSecuritySeverityMap(run)
24
+ this.buildSecurityLevelMap(run)
25
+ }
26
+
27
+ private identifySecuritySeverity(score?: number): SecuritySeverity {
28
+ if (score === undefined) {
29
+ return SecuritySeverity.Unknown
30
+ }
31
+
32
+ if (score >= 9 && score <= 10) {
33
+ return SecuritySeverity.Critical
34
+ }
35
+
36
+ if (score >= 7) {
37
+ return SecuritySeverity.High
38
+ }
39
+
40
+ if (score >= 4) {
41
+ return SecuritySeverity.Medium
42
+ }
43
+
44
+ if (score >= 0.1) {
45
+ return SecuritySeverity.Low
46
+ }
47
+
48
+ if (score == 0) {
49
+ return SecuritySeverity.None
50
+ }
51
+
52
+ Logger.warn(`Unsupported "${score}" security severity. Saving as "Unknown".`)
53
+ return SecuritySeverity.Unknown
54
+ }
55
+
56
+ private identifySecurityLevel(level?: string): SecurityLevel {
57
+ if (level === undefined) {
58
+ return SecurityLevel.Unknown
59
+ }
60
+
61
+ if (level.toLowerCase() === 'error') {
62
+ return SecurityLevel.Error
63
+ }
64
+
65
+ if (level.toLowerCase() === 'warning') {
66
+ return SecurityLevel.Warning
67
+ }
68
+
69
+ if (level.toLowerCase() === 'note') {
70
+ return SecurityLevel.Note
71
+ }
72
+
73
+ Logger.warn(`Unsupported ${level} security level. Saving as "Unknown".`)
74
+ return SecurityLevel.Unknown
75
+ }
76
+
77
+ private buildSecuritySeverityMap(run: Run): void {
78
+ const results: Result[] = run.results ?? []
79
+ for (const result of results) {
80
+ const severity: SecuritySeverity = this.identifySecuritySeverity(
81
+ tryGetRulePropertyByResult(run, result, 'security-severity')
82
+ )
83
+ const count: number = this._securitySeverityMap.get(severity) || 0
84
+ this._securitySeverityMap.set(severity, count + 1)
85
+ }
86
+ }
87
+
88
+ private tryGetSecurityLevel(run: Run, result: Result): string | undefined {
89
+ if (result.level) {
90
+ return result.level
91
+ }
92
+
93
+ return tryGetRulePropertyByResult(run, result, 'problem.severity')
94
+ }
95
+
96
+ private buildSecurityLevelMap(run: Run): void {
97
+ const results: Result[] = run.results ?? []
98
+ for (const result of results) {
99
+ const level: SecurityLevel = this.identifySecurityLevel(
100
+ this.tryGetSecurityLevel(run, result)
101
+ )
102
+ const count: number = this._securityLevelMap.get(level) || 0
103
+ this._securityLevelMap.set(level, count + 1)
104
+ }
105
+ }
106
+
107
+ public get securitySeverityMap(): ImmutableMap<SecuritySeverity, number> {
108
+ return sortSecuritySeverityMap(this._securitySeverityMap)
109
+ }
110
+
111
+ public get securityLevelMap(): ImmutableMap<SecurityLevel, number> {
112
+ return sortSecurityLevelMap(this._securityLevelMap)
113
+ }
114
+ }
@@ -0,0 +1,116 @@
1
+ import type { SarifLog } from '../types'
2
+ import { Map as ImmutableMap } from 'immutable'
3
+ import { SarifModelPerRun } from './SarifModelPerRun'
4
+ import { SecurityLevel, SecuritySeverity } from './types'
5
+ import {
6
+ sortSecurityLevelMap,
7
+ sortSecuritySeverityMap
8
+ } from '../utils/SortUtils';
9
+
10
+ export type DataGroupedByRun<T> = {
11
+ toolName: string,
12
+ data: ImmutableMap<T, number>
13
+ }
14
+
15
+ export class SarifModelPerSarif {
16
+ private readonly sarifModelPerRunList: Array<SarifModelPerRun>;
17
+
18
+ constructor(sarif: SarifLog) {
19
+ this.sarifModelPerRunList = new Array<SarifModelPerRun>()
20
+ this.buildRunsList(sarif)
21
+ }
22
+
23
+ private buildRunsList(sarif: SarifLog): void {
24
+ for (const run of sarif.runs) {
25
+ this.sarifModelPerRunList.push(new SarifModelPerRun(run))
26
+ }
27
+ }
28
+
29
+ public groupByToolNameWithSecurityLevel(): Map<string, ImmutableMap<SecurityLevel, number>> {
30
+ const result = new Map<string, ImmutableMap<SecurityLevel, number>>()
31
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
32
+ if (!result.has(sarifModelPerRun.toolName)) {
33
+ result.set(sarifModelPerRun.toolName, ImmutableMap<SecurityLevel, number>().asMutable())
34
+ }
35
+ for (const [k, v] of sarifModelPerRun.securityLevelMap.entries()) {
36
+ const count: number = result.get(sarifModelPerRun.toolName)?.get(k) || 0
37
+ result.get(sarifModelPerRun.toolName)?.set(k, count + v)
38
+ }
39
+ }
40
+ // Sort
41
+ for (const [k, v] of result) {
42
+ result.set(k, sortSecurityLevelMap(v))
43
+ }
44
+ return result
45
+ }
46
+
47
+ public groupByRunWithSecurityLevel(): DataGroupedByRun<SecurityLevel>[] {
48
+ const result = new Array<DataGroupedByRun<SecurityLevel>>()
49
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
50
+ result.push({
51
+ toolName: sarifModelPerRun.toolName,
52
+ data: sarifModelPerRun.securityLevelMap,
53
+ })
54
+ }
55
+ return result
56
+ }
57
+
58
+ public groupByTotalWithSecurityLevel(): ImmutableMap<SecurityLevel, number> {
59
+ const result = ImmutableMap<SecurityLevel, number>().asMutable()
60
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
61
+ for (const [k, v] of sarifModelPerRun.securityLevelMap.entries()) {
62
+ const count: number = result.get(k) || 0
63
+ result.set(k, count + v)
64
+ }
65
+ }
66
+ return sortSecurityLevelMap(result)
67
+ }
68
+
69
+ public groupByToolNameWithSecuritySeverity(): Map<string, ImmutableMap<SecuritySeverity, number>> {
70
+ const result = new Map<string, ImmutableMap<SecuritySeverity, number>>()
71
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
72
+ if (!result.has(sarifModelPerRun.toolName)) {
73
+ result.set(sarifModelPerRun.toolName, ImmutableMap<SecuritySeverity, number>().asMutable())
74
+ }
75
+ for (const [k, v] of sarifModelPerRun.securitySeverityMap.entries()) {
76
+ const count: number = result.get(sarifModelPerRun.toolName)?.get(k) || 0
77
+ result.get(sarifModelPerRun.toolName)?.set(k, count + v)
78
+ }
79
+ }
80
+ // Sort
81
+ for (const [k, v] of result.entries()) {
82
+ result.set(k, sortSecuritySeverityMap(v))
83
+ }
84
+ return result
85
+ }
86
+
87
+ public groupByRunWithSecuritySeverity(): DataGroupedByRun<SecuritySeverity>[] {
88
+ const result = new Array<DataGroupedByRun<SecuritySeverity>>()
89
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
90
+ result.push({
91
+ toolName: sarifModelPerRun.toolName,
92
+ data: sarifModelPerRun.securitySeverityMap,
93
+ })
94
+ }
95
+ return result
96
+ }
97
+
98
+ public groupByTotalWithSecuritySeverity(): ImmutableMap<SecuritySeverity, number> {
99
+ const result = ImmutableMap<SecuritySeverity, number>().asMutable()
100
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
101
+ for (const [k, v] of sarifModelPerRun.securitySeverityMap.entries()) {
102
+ const count: number = result.get(k) || 0
103
+ result.set(k, count + v)
104
+ }
105
+ }
106
+ return sortSecuritySeverityMap(result)
107
+ }
108
+
109
+ public listToolNames(): Set<string> {
110
+ const toolNames = new Set<string>()
111
+ for (const sarifModelPerRun of this.sarifModelPerRunList) {
112
+ toolNames.add(sarifModelPerRun.toolName)
113
+ }
114
+ return toolNames
115
+ }
116
+ }
@@ -0,0 +1,31 @@
1
+ export enum SecuritySeverity {
2
+ Unknown = 'Unknown',
3
+ None = 'None',
4
+ Low = 'Low',
5
+ Medium = 'Medium',
6
+ High = 'High',
7
+ Critical = 'Critical'
8
+ }
9
+
10
+ export const SecuritySeverityOrder: SecuritySeverity[] = [
11
+ SecuritySeverity.Critical,
12
+ SecuritySeverity.High,
13
+ SecuritySeverity.Medium,
14
+ SecuritySeverity.Low,
15
+ SecuritySeverity.None,
16
+ SecuritySeverity.Unknown
17
+ ]
18
+
19
+ export enum SecurityLevel {
20
+ Unknown = 'Unknown',
21
+ Note = 'Note',
22
+ Warning = 'Warning',
23
+ Error = 'Error'
24
+ }
25
+
26
+ export const SecurityLevelOrder: SecurityLevel[] = [
27
+ SecurityLevel.Error,
28
+ SecurityLevel.Warning,
29
+ SecurityLevel.Note,
30
+ SecurityLevel.Unknown
31
+ ]
package/src/types.ts CHANGED
@@ -4,7 +4,7 @@ import type { Log } from 'sarif'
4
4
  * Type representing a SARIF log.
5
5
  * @public
6
6
  */
7
- export type Sarif = Log
7
+ export type SarifLog = Log
8
8
 
9
9
  /**
10
10
  * Interface for a Slack message that can be sent.
@@ -19,7 +19,7 @@ export interface SlackMessage {
19
19
  /**
20
20
  * The SARIF log associated with this Slack message.
21
21
  */
22
- sarif: Sarif
22
+ sarif: SarifLog
23
23
  }
24
24
 
25
25
  /**
@@ -28,7 +28,8 @@ export interface SlackMessage {
28
28
  */
29
29
  export enum LogLevel {
30
30
  /**
31
- * Represents the most verbose logging level, typically used for detailed debugging information.
31
+ * Represents the most verbose logging level, typically used for detailed
32
+ * debugging information.
32
33
  */
33
34
  Silly = 0,
34
35
  /**
@@ -36,23 +37,28 @@ export enum LogLevel {
36
37
  */
37
38
  Trace = 1,
38
39
  /**
39
- * Represents a logging level for debugging information that is less verbose than silly.
40
+ * Represents a logging level for debugging information that is less verbose
41
+ * than silly.
40
42
  */
41
43
  Debug = 2,
42
44
  /**
43
- * Represents a logging level for general informational messages that highlight the progress of the application.
45
+ * Represents a logging level for general informational messages that highlight
46
+ * the progress of the application.
44
47
  */
45
48
  Info = 3,
46
49
  /**
47
- * Represents a logging level for potentially harmful situations that require attention.
50
+ * Represents a logging level for potentially harmful situations that require
51
+ * attention.
48
52
  */
49
53
  Warning = 4,
50
54
  /**
51
- * Represents a logging level for error conditions that do not require immediate action but should be noted.
55
+ * Represents a logging level for error conditions that do not require immediate
56
+ * action but should be noted.
52
57
  */
53
58
  Error = 5,
54
59
  /**
55
- * Represents a logging level for critical errors that require immediate attention and may cause the application to terminate.
60
+ * Represents a logging level for critical errors that require immediate attention
61
+ * and may cause the application to terminate.
56
62
  */
57
63
  Fatal = 6
58
64
  }
@@ -62,7 +68,7 @@ export enum LogLevel {
62
68
  * in the Slack message.
63
69
  * @public
64
70
  */
65
- export type IncludeAwareProps = {
71
+ export type IncludeAwareOptions = {
66
72
  include: boolean
67
73
  }
68
74
 
@@ -71,10 +77,101 @@ export type IncludeAwareProps = {
71
77
  * in the Slack message, along with an optional value.
72
78
  * @public
73
79
  */
74
- export type IncludeAwareWithValueProps = IncludeAwareProps & {
80
+ export type IncludeAwareWithValueOptions = IncludeAwareOptions & {
75
81
  value?: string
76
82
  }
77
83
 
84
+ /**
85
+ * Enum representing the type of footer in a Slack message.
86
+ * @public
87
+ */
88
+ export enum FooterType {
89
+ /**
90
+ * Represents a plain text footer. Text is not formatted and appears as-is.
91
+ */
92
+ PlainText = 'plain_text',
93
+ /**
94
+ * Represents a footer with Markdown formatting. Text can include formatting
95
+ * such as bold, italics, and links.
96
+ */
97
+ Markdown = 'mrkdwn'
98
+ }
99
+
100
+ /**
101
+ * Options for the footer of a Slack message. "type" is ignored if "value" is
102
+ * not defined.
103
+ * @public
104
+ */
105
+ export type FooterOptions = IncludeAwareWithValueOptions & {
106
+ type?: FooterType
107
+ }
108
+
109
+ /**
110
+ * Enum representing how to group results.
111
+ * @public
112
+ */
113
+ export enum GroupResultsBy {
114
+ /**
115
+ * Groups results by the tool name. Particularly, groups by the runs[].tool.driver.name
116
+ * property from the SARIF file(s).
117
+ */
118
+ ToolName = 0,
119
+ /**
120
+ * Groups results by the run. It provides the result from each run individually.
121
+ */
122
+ Run = 1,
123
+ /**
124
+ * Does not group results. It provides the result from all the runs from all
125
+ * the provided SARIF files.
126
+ */
127
+ Total = 2,
128
+ }
129
+
130
+ /**
131
+ * Enum representing how to calculate results.
132
+ * @public
133
+ */
134
+ export enum CalculateResultsBy {
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,
143
+ /**
144
+ * Calculates results by the security severity of the findings: Critical, High,
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.
149
+ */
150
+ Severity = 1,
151
+ }
152
+
153
+ /**
154
+ * Options for how to output the results in the Slack message.
155
+ * @public
156
+ */
157
+ export type SarifToSlackOutput = {
158
+ groupBy: GroupResultsBy,
159
+ calculateBy: CalculateResultsBy,
160
+ }
161
+
162
+ /**
163
+ * Options for logging.
164
+ * @public
165
+ */
166
+ export type LogOptions = {
167
+ level?: LogLevel,
168
+ /**
169
+ * More details here: https://github.com/fullstack-build/tslog?tab=readme-ov-file#pretty-templates-and-styles-color-settings
170
+ */
171
+ template?: string,
172
+ colored?: boolean,
173
+ }
174
+
78
175
  /**
79
176
  * Options for the SarifToSlackService.
80
177
  * @public
@@ -86,9 +183,10 @@ export type SarifToSlackServiceOptions = {
86
183
  username?: string,
87
184
  iconUrl?: string,
88
185
  color?: string,
89
- logLevel?: LogLevel | string,
90
- header?: IncludeAwareWithValueProps,
91
- footer?: IncludeAwareWithValueProps,
92
- actor?: IncludeAwareWithValueProps,
93
- run?: IncludeAwareProps,
186
+ log?: LogOptions,
187
+ header?: IncludeAwareWithValueOptions,
188
+ footer?: FooterOptions,
189
+ actor?: IncludeAwareWithValueOptions,
190
+ run?: IncludeAwareOptions,
191
+ output?: SarifToSlackOutput,
94
192
  }
@@ -0,0 +1,44 @@
1
+ import type { ReportingDescriptor, Result, Run } from "sarif";
2
+
3
+ export function findRuleByResult(run: Run, result: Result): ReportingDescriptor | undefined {
4
+ const ruleData: { id?: string, index?: number } = {}
5
+
6
+ if (result.rule) {
7
+ if (result.rule?.index) {
8
+ ruleData.index = result.rule.index
9
+ }
10
+ if (result.rule?.id) {
11
+ ruleData.id = result.rule.id
12
+ }
13
+ }
14
+
15
+ if (!ruleData.index && result.ruleIndex) {
16
+ ruleData.index = result.ruleIndex
17
+ }
18
+
19
+ if (ruleData.index
20
+ && run.tool.driver?.rules
21
+ && ruleData.index < run.tool.driver.rules.length) {
22
+ return run.tool.driver.rules[ruleData.index]
23
+ }
24
+
25
+ // If failed to find rule by index then try to find by ruleId
26
+ if (result.ruleId && run.tool.driver?.rules) {
27
+ return run.tool.driver.rules.find(
28
+ (r: ReportingDescriptor): boolean => r.id === result.ruleId
29
+ )
30
+ }
31
+
32
+ return undefined
33
+ }
34
+
35
+ export type RuleProperty = 'security-severity' | 'problem.severity'
36
+
37
+ export function tryGetRulePropertyByResult<T>(run: Run, result: Result, propertyName: RuleProperty): T | undefined {
38
+ const rule: ReportingDescriptor | undefined = findRuleByResult(run, result)
39
+ if (rule && rule.properties && propertyName in rule.properties) {
40
+ return rule.properties[propertyName] as T
41
+ }
42
+
43
+ return undefined
44
+ }
@@ -0,0 +1,21 @@
1
+ import { Map as ImmutableMap } from 'immutable'
2
+ import {
3
+ SecurityLevel,
4
+ SecurityLevelOrder,
5
+ SecuritySeverity,
6
+ SecuritySeverityOrder
7
+ } from '../model/types'
8
+
9
+ export function sortSecurityLevelMap(map: ImmutableMap<SecurityLevel, number>): ImmutableMap<SecurityLevel, number> {
10
+ return map.sortBy(
11
+ (_: number, level: SecurityLevel): SecurityLevel => level,
12
+ (a: SecurityLevel, b: SecurityLevel): number => SecurityLevelOrder.indexOf(a) - SecurityLevelOrder.indexOf(b),
13
+ ).asImmutable()
14
+ }
15
+
16
+ export function sortSecuritySeverityMap(map: ImmutableMap<SecuritySeverity, number>): ImmutableMap<SecuritySeverity, number> {
17
+ return map.sortBy(
18
+ (_: number, severity: SecuritySeverity): SecuritySeverity => severity,
19
+ (a: SecuritySeverity, b: SecuritySeverity): number => SecuritySeverityOrder.indexOf(a) - SecuritySeverityOrder.indexOf(b),
20
+ ).asImmutable()
21
+ }
package/src/version.ts ADDED
@@ -0,0 +1,3 @@
1
+ // This file is autogenerated by scripts/save-version.sh
2
+ // Do not edit it manually!
3
+ export const LIB_VERSION = '0.2.1'