-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.ts
More file actions
129 lines (120 loc) · 4.53 KB
/
main.ts
File metadata and controls
129 lines (120 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {resolve, dirname} from 'path'
import {env} from 'process'
import {readFileSync, writeFileSync} from 'fs'
import * as core from '@actions/core'
import {DOMParser} from '@xmldom/xmldom'
import * as xpath from 'xpath'
import {JSONPath} from 'jsonpath-plus'
import {LogLevel, log} from './utils'
let sarifFilePath: string
let outputFilePath: string
let sarifResults: object
let cweXml: Document
let cweFilePath = resolve(dirname(process.argv[1]), '..//security-standards/owasp-top10-2021.xml')
const cweFileXmlNs = {cwe: 'http://cwe.mitre.org/cwe-6'}
let cweIdXpath = '/cwe:Weakness_Catalog/cwe:Weaknesses/cwe:Weakness/@ID'
let categoryXpath = '/cwe:Weakness_Catalog/cwe:Categories/cwe:Category[contains(@Name, "OWASP Top Ten 2021")]'
const categoryMembersXpath = 'cwe:Relationships/cwe:Has_Member/@CWE_ID'
const categoryNameAttr = '@Name'
const categoryNameReplaceSearch = 'OWASP Top Ten 2021 Category '
const codeQlCweTagPrefix = 'external/cwe/cwe-'
let securityStandardTag = 'owasp-top10-2021'
const codeQlTagsJsonPath = '$.runs[*].tool.extensions[*].rules[*].properties.tags'
// Simple CLI argument parser for non-GitHub Actions use
function parseCliArgs(): Record<string, string> {
const args: Record<string, string> = {}
for (let i = 2; i < process.argv.length; i++) {
const arg = process.argv[i]
if (arg.startsWith('--')) {
const key = arg.substring(2)
const value = process.argv[i + 1]
if (value && !value.startsWith('--')) {
args[key] = value
i++
}
}
}
return args
}
// Parse Actions or CLI inputs
if (env.GITHUB_ACTIONS === 'true') {
sarifFilePath = resolve(core.getInput('sarifFile'))
cweFilePath = resolve(core.getInput('cweFile') || cweFilePath)
cweIdXpath = core.getInput('cweIdXpath') || cweIdXpath
categoryXpath = core.getInput('cweCategoryXpath') || categoryXpath
securityStandardTag = core.getInput('securityStandardTag') || securityStandardTag
outputFilePath = resolve(core.getInput('outputFile') || sarifFilePath)
} else {
const argv = parseCliArgs()
if (!argv.sarifFile) {
log('Error: --sarifFile is required', LogLevel.Error)
process.exit(1)
}
sarifFilePath = resolve(argv.sarifFile)
cweFilePath = resolve(argv.cweFile || cweFilePath)
cweIdXpath = argv.cweIdXpath || cweIdXpath
categoryXpath = argv.cweCategoryXpath || categoryXpath
securityStandardTag = argv.securityStandardTag || securityStandardTag
outputFilePath = resolve(argv.outputFile || sarifFilePath)
}
log(`Using ${sarifFilePath} for SARIF file`)
log(`Using ${cweFilePath} for CWE file`)
log(`Using ${outputFilePath} for output file`)
// Load SARIF file
try {
sarifResults = JSON.parse(readFileSync(sarifFilePath, 'utf8'))
} catch (err) {
log(`Unable to load SARIF file`, LogLevel.Error)
core.setFailed(err as Error)
throw err
}
// Load security standard CWE XML file
try {
cweXml = new DOMParser().parseFromString(readFileSync(cweFilePath, 'utf8'))
} catch (err) {
log(`Unable to load CWE file`, LogLevel.Error)
core.setFailed(err as Error)
throw err
}
const select = xpath.useNamespaces(cweFileXmlNs)
const cweIds = (select(cweIdXpath, cweXml) as Attr[]).map(attribute => attribute.value)
const cweCategoryNodes = select(categoryXpath, cweXml) as Node[]
const cweCategories: {[k: string]: string[]} = {}
for (const cweCategoryNode of cweCategoryNodes) {
const memberCweIds = (select(categoryMembersXpath, cweCategoryNode) as Attr[]).map(attr => attr.value)
const categoryName = (select(categoryNameAttr, cweCategoryNode, true) as Attr).value.replace(categoryNameReplaceSearch, '')
for (const cweId of memberCweIds) {
cweCategories[cweId] = [...(cweCategories[cweId] || []), categoryName]
}
}
// Add tag to SARIF file
JSONPath({
path: codeQlTagsJsonPath,
json: sarifResults,
callback: (tags: string[]) => {
for (const tag of tags) {
if (tag.startsWith(codeQlCweTagPrefix)) {
const cweId = tag.replace(codeQlCweTagPrefix, '')
// Normalize CWE ID by converting to integer to remove leading zeros
const normalizedCweId = String(parseInt(cweId, 10))
// Skip if the CWE ID is not a valid number
if (normalizedCweId === 'NaN') {
continue
}
if (cweIds.includes(normalizedCweId)) {
tags.push(securityStandardTag)
tags.push(...cweCategories[normalizedCweId])
return
}
}
}
}
})
// Output SARIF file with tag added
try {
writeFileSync(outputFilePath, JSON.stringify(sarifResults))
} catch (err) {
log(`Unable to write SARIF file`, LogLevel.Error)
core.setFailed(err as Error)
throw err
}