-
-
Notifications
You must be signed in to change notification settings - Fork 240
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(formatters): add sarif formatter (#2532)
- Loading branch information
1 parent
3cbf047
commit 908c308
Showing
10 changed files
with
265 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,3 +33,4 @@ console.error(output); | |
|
||
- pretty | ||
- github-actions | ||
- sarif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import { DiagnosticSeverity } from '@stoplight/types'; | ||
import type { IRuleResult } from '@stoplight/spectral-core'; | ||
import { Ruleset } from '@stoplight/spectral-core'; | ||
import { sarif } from '../sarif'; | ||
|
||
const cwd = process.cwd(); | ||
const results: IRuleResult[] = [ | ||
{ | ||
code: 'operation-description', | ||
message: 'paths./pets.get.description is not truthy\nMessages can differ from the rule description', | ||
path: ['paths', '/pets', 'get', 'description'], | ||
severity: DiagnosticSeverity.Warning, | ||
source: `${cwd}/__tests__/fixtures/petstore.oas2.yaml`, | ||
range: { | ||
start: { | ||
line: 60, | ||
character: 8, | ||
}, | ||
end: { | ||
line: 71, | ||
character: 60, | ||
}, | ||
}, | ||
}, | ||
{ | ||
code: 'operation-tags', | ||
message: 'paths./pets.get.tags is not truthy', | ||
path: ['paths', '/pets', 'get', 'tags'], | ||
severity: DiagnosticSeverity.Error, | ||
source: `${cwd}/__tests__/fixtures/petstore.oas2.yaml`, | ||
range: { | ||
start: { | ||
line: 60, | ||
character: 8, | ||
}, | ||
end: { | ||
line: 71, | ||
character: 60, | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
describe('Sarif formatter', () => { | ||
test('should be formatted correctly', async () => { | ||
const sarifToolVersion = '6.11'; | ||
const ruleset = new Ruleset({ | ||
rules: { | ||
'operation-description': { | ||
description: 'paths./pets.get.description is not truthy', | ||
message: 'paths./pets.get.description is not truthy\nMessages can differ from the rule description', | ||
severity: DiagnosticSeverity.Error, | ||
given: '$.paths[*][*]', | ||
then: { | ||
field: 'description', | ||
function: function truthy() { | ||
return false; | ||
}, | ||
}, | ||
}, | ||
'operation-tags': { | ||
description: 'paths./pets.get.tags is not truthy', | ||
message: 'paths./pets.get.tags is not truthy\nMessages can differ from the rule description', | ||
severity: DiagnosticSeverity.Error, | ||
given: '$.paths[*][*]', | ||
then: { | ||
field: 'description', | ||
function: function truthy() { | ||
return false; | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const output = sarif( | ||
results, | ||
{ failSeverity: DiagnosticSeverity.Error }, | ||
{ ruleset, spectralVersion: sarifToolVersion }, | ||
); | ||
|
||
const outputObject = JSON.parse(output); | ||
expect(outputObject).toStrictEqual({ | ||
$schema: 'http://json.schemastore.org/sarif-2.1.0-rtm.6.json', | ||
version: '2.1.0', | ||
runs: [ | ||
{ | ||
tool: { | ||
driver: { | ||
name: 'spectral', | ||
rules: [ | ||
{ | ||
id: 'operation-description', | ||
shortDescription: { | ||
text: 'paths./pets.get.description is not truthy', | ||
}, | ||
}, | ||
{ | ||
id: 'operation-tags', | ||
shortDescription: { | ||
text: 'paths./pets.get.tags is not truthy', | ||
}, | ||
}, | ||
], | ||
version: sarifToolVersion, | ||
informationUri: 'https://github.com/stoplightio/spectral', | ||
}, | ||
}, | ||
results: [ | ||
{ | ||
level: 'warning', | ||
message: { | ||
text: 'paths./pets.get.description is not truthy\nMessages can differ from the rule description', | ||
}, | ||
ruleId: 'operation-description', | ||
locations: [ | ||
{ | ||
physicalLocation: { | ||
artifactLocation: { | ||
uri: '__tests__/fixtures/petstore.oas2.yaml', | ||
index: 0, | ||
}, | ||
region: { | ||
startLine: 61, | ||
startColumn: 9, | ||
endLine: 72, | ||
endColumn: 61, | ||
}, | ||
}, | ||
}, | ||
], | ||
ruleIndex: 0, | ||
}, | ||
{ | ||
level: 'error', | ||
message: { | ||
text: 'paths./pets.get.tags is not truthy', | ||
}, | ||
ruleId: 'operation-tags', | ||
locations: [ | ||
{ | ||
physicalLocation: { | ||
artifactLocation: { | ||
uri: '__tests__/fixtures/petstore.oas2.yaml', | ||
index: 0, | ||
}, | ||
region: { | ||
startLine: 61, | ||
startColumn: 9, | ||
endLine: 72, | ||
endColumn: 61, | ||
}, | ||
}, | ||
}, | ||
], | ||
ruleIndex: 1, | ||
}, | ||
], | ||
artifacts: [ | ||
{ | ||
sourceLanguage: 'YAML', | ||
location: { | ||
uri: '__tests__/fixtures/petstore.oas2.yaml', | ||
}, | ||
}, | ||
], | ||
}, | ||
], | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { DiagnosticSeverity, Dictionary } from '@stoplight/types'; | ||
import { relative } from '@stoplight/path'; | ||
import { SarifBuilder, SarifRunBuilder, SarifResultBuilder, SarifRuleBuilder } from 'node-sarif-builder'; | ||
import type { Result } from 'sarif'; | ||
import type { Formatter } from './types'; | ||
|
||
const OUTPUT_TYPES: Dictionary<Result.level, DiagnosticSeverity> = { | ||
[DiagnosticSeverity.Error]: 'error', | ||
[DiagnosticSeverity.Warning]: 'warning', | ||
[DiagnosticSeverity.Information]: 'note', | ||
[DiagnosticSeverity.Hint]: 'note', | ||
}; | ||
|
||
export const sarif: Formatter = (results, _, ctx) => { | ||
if (ctx === void 0) { | ||
throw Error('sarif formatter requires ctx'); | ||
} | ||
|
||
const sarifBuilder = new SarifBuilder({ | ||
$schema: 'http://json.schemastore.org/sarif-2.1.0-rtm.6.json', | ||
version: '2.1.0', | ||
runs: [], | ||
}); | ||
|
||
const sarifRunBuilder = new SarifRunBuilder().initSimple({ | ||
toolDriverName: 'spectral', | ||
toolDriverVersion: ctx.spectralVersion, | ||
url: 'https://github.com/stoplightio/spectral', | ||
}); | ||
|
||
// add rules | ||
for (const rule of Object.values(ctx.ruleset.rules)) { | ||
const sarifRuleBuilder = new SarifRuleBuilder().initSimple({ | ||
ruleId: rule.name, | ||
shortDescriptionText: rule.description ?? 'No description.', | ||
helpUri: rule.documentationUrl !== null ? rule.documentationUrl : undefined, | ||
}); | ||
sarifRunBuilder.addRule(sarifRuleBuilder); | ||
} | ||
|
||
// add results | ||
for (const result of results) { | ||
const sarifResultBuilder = new SarifResultBuilder(); | ||
const severity: DiagnosticSeverity = result.severity || DiagnosticSeverity.Error; | ||
sarifResultBuilder.initSimple({ | ||
level: OUTPUT_TYPES[severity] || 'error', | ||
messageText: result.message, | ||
ruleId: result.code.toString(), | ||
fileUri: relative(process.cwd(), result.source ?? '').replace(/\\/g, '/'), | ||
startLine: result.range.start.line + 1, | ||
startColumn: result.range.start.character + 1, | ||
endLine: result.range.end.line + 1, | ||
endColumn: result.range.end.character + 1, | ||
}); | ||
sarifRunBuilder.addResult(sarifResultBuilder); | ||
} | ||
|
||
sarifBuilder.addRun(sarifRunBuilder); | ||
return sarifBuilder.buildSarifJsonString({ indent: true }); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,13 @@ | ||
import { ISpectralDiagnostic } from '@stoplight/spectral-core'; | ||
import { ISpectralDiagnostic, Ruleset } from '@stoplight/spectral-core'; | ||
import type { DiagnosticSeverity } from '@stoplight/types'; | ||
|
||
export type FormatterOptions = { | ||
failSeverity: DiagnosticSeverity; | ||
}; | ||
|
||
export type Formatter = (results: ISpectralDiagnostic[], options: FormatterOptions) => string; | ||
export type FormatterContext = { | ||
ruleset: Ruleset; | ||
spectralVersion: string; | ||
}; | ||
|
||
export type Formatter = (results: ISpectralDiagnostic[], options: FormatterOptions, ctx?: FormatterContext) => string; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters