Skip to content

Commit d3ddbd1

Browse files
committed
Fix SARIF export for PHPStan alerts
1 parent a1ec246 commit d3ddbd1

File tree

4 files changed

+139
-26
lines changed

4 files changed

+139
-26
lines changed

code-scanning-export/__tests__/sarif.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,53 @@ const openAlert: AlertType = {
122122
instances_url:
123123
'https://api.github.com/repos/mongodb/mongo-php-library/code-scanning/alerts/331/instances'
124124
}
125+
const phpstanAlert: AlertType = {
126+
number: 3,
127+
created_at: '2024-05-24T09:29:19Z',
128+
updated_at: '2024-05-24T09:29:23Z',
129+
url: 'https://api.github.com/repos/alcaeus/laravel-mongodb/code-scanning/alerts/3',
130+
html_url:
131+
'https://github.com/alcaeus/laravel-mongodb/security/code-scanning/3',
132+
state: 'open',
133+
fixed_at: null,
134+
dismissed_by: null,
135+
dismissed_at: null,
136+
dismissed_reason: null,
137+
dismissed_comment: null,
138+
rule: {
139+
id: 'new.static',
140+
severity: 'error',
141+
description: '',
142+
name: '',
143+
tags: []
144+
},
145+
tool: {
146+
name: 'PHPStan',
147+
guid: null,
148+
version: '1.11.x-dev@0055aac'
149+
},
150+
most_recent_instance: {
151+
ref: 'refs/heads/export-sarif-on-release',
152+
analysis_key: '.github/workflows/coding-standards.yml:analysis',
153+
environment: '{"php":"8.2"}',
154+
category: '.github/workflows/coding-standards.yml:analysis/php:8.2',
155+
state: 'open',
156+
commit_sha: '05cd5c7d1f6a16840fdf98b59e95cdde3c26bd77',
157+
message: {
158+
text: 'Unsafe usage of new static().'
159+
},
160+
location: {
161+
path: 'src/Query/Builder.php',
162+
start_line: 954,
163+
end_line: 954,
164+
start_column: 1,
165+
end_column: 0
166+
},
167+
classifications: []
168+
},
169+
instances_url:
170+
'https://api.github.com/repos/alcaeus/laravel-mongodb/code-scanning/alerts/3/instances'
171+
}
125172

126173
describe('createSarifReport', () => {
127174
it('generates a valid sarif report', () => {
@@ -194,4 +241,27 @@ describe('createSarifResult', () => {
194241
]
195242
})
196243
})
244+
245+
it('generates correct sarif for a phpstan alert', () => {
246+
expect(createSarifResult(phpstanAlert)).toEqual({
247+
ruleId: 'new.static',
248+
message: {
249+
text: 'Unsafe usage of new static().'
250+
},
251+
level: 'error',
252+
locations: [
253+
{
254+
physicalLocation: {
255+
artifactLocation: { uri: 'src/Query/Builder.php' },
256+
region: {
257+
startLine: 954,
258+
endLine: 954,
259+
startColumn: 1
260+
}
261+
}
262+
}
263+
],
264+
suppressions: []
265+
})
266+
})
197267
})

code-scanning-export/dist/index.js

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29344,10 +29344,9 @@ function createSarifReport(alerts) {
2934429344
};
2934529345
}
2934629346
results[alert.tool.name].results.push(createSarifResult(alert));
29347-
if (alert.rule.name &&
29348-
!results[alert.tool.name].tool.driver.rules[alert.rule.name]) {
29349-
results[alert.tool.name].tool.driver.rules[alert.rule.name] =
29350-
createSarifRule(alert.rule);
29347+
const ruleName = getRuleIdentifier(alert);
29348+
if (ruleName && !results[alert.tool.name].tool.driver.rules[ruleName]) {
29349+
results[alert.tool.name].tool.driver.rules[ruleName] = createSarifRule(alert.rule);
2935129350
}
2935229351
}
2935329352
return {
@@ -29377,7 +29376,7 @@ function createSarifRule(rule) {
2937729376
}
2937829377
function createSarifResult(alert) {
2937929378
return {
29380-
ruleId: alert.rule.name,
29379+
ruleId: getRuleIdentifier(alert),
2938129380
message: alert.most_recent_instance.message,
2938229381
level: alert.rule.severity,
2938329382
locations: createResultLocation(alert),
@@ -29393,12 +29392,7 @@ function createResultLocation(alert) {
2939329392
{
2939429393
physicalLocation: {
2939529394
artifactLocation: { uri: alert.most_recent_instance.location.path },
29396-
region: {
29397-
startLine: alert.most_recent_instance.location.start_line,
29398-
endLine: alert.most_recent_instance.location.end_line,
29399-
startColumn: alert.most_recent_instance.location.start_column,
29400-
endColumn: alert.most_recent_instance.location.end_column
29401-
}
29395+
region: createRegion(alert.most_recent_instance.location)
2940229396
}
2940329397
}
2940429398
];
@@ -29419,6 +29413,25 @@ function createResultSuppressions(alert) {
2941929413
}
2942029414
];
2942129415
}
29416+
function createRegion(location) {
29417+
const region = {};
29418+
if (location.start_line) {
29419+
region.startLine = location.start_line;
29420+
}
29421+
if (location.end_line) {
29422+
region.endLine = location.end_line;
29423+
}
29424+
if (location.start_column) {
29425+
region.startColumn = location.start_column;
29426+
}
29427+
if (location.end_column) {
29428+
region.endColumn = location.end_column;
29429+
}
29430+
return region;
29431+
}
29432+
function getRuleIdentifier(alert) {
29433+
return alert.rule.name ? alert.rule.name : alert.rule.id ? alert.rule.id : '';
29434+
}
2942229435

2942329436

2942429437
/***/ }),

code-scanning-export/src/main.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ type RepositoryInfo = {
1616
*/
1717
export async function run(): Promise<void> {
1818
const repositoryInfo = getRepositoryInfo()
19-
const ref = core.getInput('ref');
19+
const ref = core.getInput('ref')
2020

21-
core.debug(`Fetching open and dismissed alerts for repository ${repositoryInfo.owner}/${repositoryInfo.repo}#${ref}`)
21+
core.debug(
22+
`Fetching open and dismissed alerts for repository ${repositoryInfo.owner}/${repositoryInfo.repo}#${ref}`
23+
)
2224

2325
const alerts = await getAlerts(
2426
repositoryInfo.owner,

code-scanning-export/src/sarif.ts

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { components } from '@octokit/openapi-types'
22

33
export type AlertType = components['schemas']['code-scanning-alert-items']
44
export type RuleType = components['schemas']['code-scanning-alert-rule-summary']
5+
export type AlertLocationType =
6+
components['schemas']['code-scanning-alert-location']
57

68
type SarifReport = {
79
version: string
@@ -18,6 +20,13 @@ type SarifReport = {
1820
}[]
1921
}
2022

23+
type Region = {
24+
startLine?: number
25+
endLine?: number
26+
startColumn?: number
27+
endColumn?: number
28+
}
29+
2130
export function createSarifReport(alerts: AlertType[]): SarifReport {
2231
const results: {
2332
[index: string]: {
@@ -52,12 +61,12 @@ export function createSarifReport(alerts: AlertType[]): SarifReport {
5261

5362
results[alert.tool.name].results.push(createSarifResult(alert))
5463

55-
if (
56-
alert.rule.name &&
57-
!results[alert.tool.name].tool.driver.rules[alert.rule.name]
58-
) {
59-
results[alert.tool.name].tool.driver.rules[alert.rule.name] =
60-
createSarifRule(alert.rule)
64+
const ruleName = getRuleIdentifier(alert)
65+
66+
if (ruleName && !results[alert.tool.name].tool.driver.rules[ruleName]) {
67+
results[alert.tool.name].tool.driver.rules[ruleName] = createSarifRule(
68+
alert.rule
69+
)
6170
}
6271
}
6372

@@ -92,7 +101,7 @@ function createSarifRule(rule: RuleType): object {
92101

93102
export function createSarifResult(alert: AlertType): object {
94103
return {
95-
ruleId: alert.rule.name,
104+
ruleId: getRuleIdentifier(alert),
96105
message: alert.most_recent_instance.message,
97106
level: alert.rule.severity,
98107
locations: createResultLocation(alert),
@@ -109,12 +118,7 @@ function createResultLocation(alert: AlertType): object[] {
109118
{
110119
physicalLocation: {
111120
artifactLocation: { uri: alert.most_recent_instance.location.path },
112-
region: {
113-
startLine: alert.most_recent_instance.location.start_line,
114-
endLine: alert.most_recent_instance.location.end_line,
115-
startColumn: alert.most_recent_instance.location.start_column,
116-
endColumn: alert.most_recent_instance.location.end_column
117-
}
121+
region: createRegion(alert.most_recent_instance.location)
118122
}
119123
}
120124
]
@@ -138,3 +142,27 @@ function createResultSuppressions(alert: AlertType): object[] {
138142
}
139143
]
140144
}
145+
146+
function createRegion(location: AlertLocationType): Region {
147+
const region: Region = {}
148+
149+
if (location.start_line) {
150+
region.startLine = location.start_line
151+
}
152+
if (location.end_line) {
153+
region.endLine = location.end_line
154+
}
155+
156+
if (location.start_column) {
157+
region.startColumn = location.start_column
158+
}
159+
if (location.end_column) {
160+
region.endColumn = location.end_column
161+
}
162+
163+
return region
164+
}
165+
166+
function getRuleIdentifier(alert: AlertType): string {
167+
return alert.rule.name ? alert.rule.name : alert.rule.id ? alert.rule.id : ''
168+
}

0 commit comments

Comments
 (0)