Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a05d02b

Browse files
committedNov 2, 2022
Add SARIF support
1 parent c22c21d commit a05d02b

File tree

8 files changed

+809
-0
lines changed

8 files changed

+809
-0
lines changed
 

‎CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### Features
2+
3+
* add support format: sarif ([#1045](https://github.com/htmlhint/HTMLHint/issues/1045))
4+
15
## [1.1.4](https://github.com/htmlhint/HTMLHint/compare/v1.1.3...v1.1.4) (2022-04-11)
26

37

‎dist/cli/formatters/sarif.js

+59
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎docs/user-guide/usage/options.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Options are:
2828
- `html`
2929
- `junit`
3030
- `markdown`
31+
- `sarif`
3132

3233
## `ignore`
3334

‎package-lock.json

+63
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"glob": "^8.0.3",
6464
"is-glob": "^4.0.3",
6565
"node-fetch": "^2.6.2",
66+
"node-sarif-builder": "^2.0.3",
6667
"strip-json-comments": "3.1.0",
6768
"xml": "1.0.1"
6869
},

‎src/cli/formatters/sarif.ts

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { FormatterCallback } from '../formatter'
2+
import {
3+
SarifBuilder,
4+
SarifResultBuilder,
5+
SarifRuleBuilder,
6+
SarifRunBuilder,
7+
} from 'node-sarif-builder'
8+
import * as path from 'path'
9+
import { pathToFileURL } from 'url'
10+
import { Result } from 'sarif'
11+
12+
const pkg = require('../../../package.json')
13+
14+
const sarifFormatter: FormatterCallback = function (formatter) {
15+
formatter.on('end', (event) => {
16+
const arrAllMessages = event.arrAllMessages
17+
18+
// SARIF builder
19+
const sarifBuilder = new SarifBuilder()
20+
21+
// SARIF Run builder
22+
const sarifRunBuilder = new SarifRunBuilder().initSimple({
23+
toolDriverName: 'HTMLHint',
24+
toolDriverVersion: pkg.version,
25+
url: 'https://htmlhint.com/',
26+
})
27+
28+
// SARIF rules
29+
const addedRuleSet = new Set<string>()
30+
arrAllMessages.forEach((result) => {
31+
result.messages.forEach((message) => {
32+
const rule = message.rule
33+
if (addedRuleSet.has(rule.id)) {
34+
return
35+
}
36+
addedRuleSet.add(rule.id)
37+
const sarifRuleBuiler = new SarifRuleBuilder().initSimple({
38+
ruleId: rule.id,
39+
shortDescriptionText: rule.description,
40+
helpUri: rule.link,
41+
})
42+
sarifRunBuilder.addRule(sarifRuleBuiler)
43+
})
44+
})
45+
46+
// Add SARIF results (individual errors)
47+
arrAllMessages.forEach((result) => {
48+
result.messages.forEach((message) => {
49+
const sarifResultBuilder = new SarifResultBuilder()
50+
const ruleId = message.rule.id
51+
const sarifResultInit = {
52+
level:
53+
message.type === 'info'
54+
? 'note'
55+
: (message.type.toString() as Result.level),
56+
messageText: message.message,
57+
ruleId: ruleId,
58+
fileUri: process.env.SARIF_URI_ABSOLUTE
59+
? pathToFileURL(result.file).toString()
60+
: path.relative(process.cwd(), result.file).replace(/\\/g, '/'),
61+
startLine: message.line,
62+
startColumn: message.col,
63+
endLine: message.line,
64+
endColumn: message.col,
65+
} as const
66+
sarifResultBuilder.initSimple(sarifResultInit)
67+
sarifRunBuilder.addResult(sarifResultBuilder)
68+
})
69+
})
70+
71+
sarifBuilder.addRun(sarifRunBuilder)
72+
console.log(sarifBuilder.buildSarifJsonString({ indent: true }))
73+
})
74+
}
75+
76+
module.exports = sarifFormatter

‎test/cli/formatters/sarif.sarif

+535
Large diffs are not rendered by default.

‎test/cli/formatters/sarif.spec.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const ChildProcess = require('child_process')
2+
const fs = require('fs')
3+
const path = require('path')
4+
const os = require('os')
5+
6+
describe('CLI', () => {
7+
describe('Formatter: sarif', () => {
8+
it('should have stdout output with formatter sarif', (done) => {
9+
const expectedFileContent = fs
10+
.readFileSync(path.resolve(__dirname, 'sarif.sarif'), 'utf8')
11+
.replace(
12+
/\{\{path\}\}/g,
13+
path.resolve(__dirname, 'example.html').replace(/\\/g, '\\\\')
14+
)
15+
16+
const expected = JSON.parse(expectedFileContent)
17+
18+
var child = ChildProcess.spawn('node', [
19+
path.resolve(__dirname, '../../../bin/htmlhint'),
20+
path.resolve(__dirname, 'example.html'),
21+
'--format',
22+
'sarif',
23+
])
24+
25+
child.stdout.on('data', function (stdout) {
26+
expect(stdout).not.toBe('')
27+
28+
if (os.platform() !== 'darwin') {
29+
const jsonStdout = JSON.parse(stdout)
30+
expect(typeof jsonStdout).toBe('object')
31+
expect(
32+
jsonStdout['runs'][0]['artifacts'][0]['location']['uri']
33+
).toContain('example.html')
34+
35+
const stdoutResults = jsonStdout['runs'][0]['results']
36+
const stdoutRules = jsonStdout['runs'][0]['tool']['driver']['rules']
37+
38+
expect(stdoutResults).toBeInstanceOf(Array)
39+
expect(stdoutResults.length).toBe(
40+
expected['runs'][0]['results'].length
41+
)
42+
43+
expect(stdoutRules).toBeInstanceOf(Array)
44+
expect(stdoutRules.length).toBe(
45+
expected['runs'][0]['tool']['driver']['rules'].length
46+
)
47+
48+
for (let i = 0; i < stdoutResults.length; i++) {
49+
expect(stdoutResults[i]).toEqual(expected['runs'][0]['results'][i])
50+
}
51+
52+
for (let i = 0; i < stdoutRules.length; i++) {
53+
expect(stdoutRules[i]).toEqual(
54+
expected['runs'][0]['tool']['driver']['rules'][i]
55+
)
56+
}
57+
}
58+
})
59+
60+
child.stderr.on('data', function (stderr) {
61+
expect(stderr).toBe('')
62+
})
63+
64+
child.on('close', function (code) {
65+
expect(code).toBe(1)
66+
done()
67+
})
68+
})
69+
})
70+
})

0 commit comments

Comments
 (0)
Please sign in to comment.