@eurekadevsecops/radar 1.0.1 → 1.1.0
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.
- package/package.json +1 -1
- package/scanners/depscan/run.sh +1 -1
- package/scanners/gitleaks/run.sh +1 -1
- package/scanners/opengrep/run.sh +1 -1
- package/src/commands/scan.js +17 -6
- package/src/util/sarif/display_findings.js +3 -3
- package/src/util/sarif/merge.js +5 -2
- package/src/util/sarif/summarize.js +1 -1
- package/scanners/depscan/sarif.j2 +0 -90
- package/scanners/opengrep/rules.yaml +0 -69031
package/package.json
CHANGED
package/scanners/depscan/run.sh
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
docker run --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/
|
|
7
|
+
docker run --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/eurekadevsecops/radar-depscan 2>&1
|
|
8
8
|
cp $3/depscan/depscan.sarif $3/depscan.sarif
|
package/scanners/gitleaks/run.sh
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
docker run --rm -v $1:/app -v $2:/input -v $3:/output zricethezav/gitleaks dir -f sarif -r /output/gitleaks.sarif /app 2>&1
|
|
7
|
+
docker run --rm -v $1:/app -v $2:/input -v $3:/output zricethezav/gitleaks dir --exit-code 0 -f sarif -r /output/gitleaks.sarif /app 2>&1
|
package/scanners/opengrep/run.sh
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
docker run --rm -
|
|
7
|
+
docker run --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/eurekadevsecops/radar-opengrep 2>&1
|
package/src/commands/scan.js
CHANGED
|
@@ -46,11 +46,12 @@ module.exports = {
|
|
|
46
46
|
some may belong to multiple categories. You could run all available SAST
|
|
47
47
|
scanners, for example, by passing in SAST as the value for the CATEGORIES
|
|
48
48
|
option. Values are case-insensitive. Multiple values should be comma-separated.
|
|
49
|
-
Defaults to "SAST"
|
|
49
|
+
Defaults to "SAST", unless you use the SCANNERS option in which case the
|
|
50
|
+
default value for CATEGORIES is ignored. To run all scanners across all
|
|
51
|
+
categories, use the value "all" for CATEGORIES.
|
|
50
52
|
|
|
51
|
-
You can specify both SCANNERS and CATEGORIES at the same time.
|
|
52
|
-
|
|
53
|
-
command line, and any matching scanners are then used for the scan.
|
|
53
|
+
You can specify both SCANNERS and CATEGORIES at the same time. This will run
|
|
54
|
+
all scanners provided in both options.
|
|
54
55
|
|
|
55
56
|
By default, findings are displayed as high, moderate, and low. This is the
|
|
56
57
|
'security' severity format. Findings can also be displayed as errors, warnings,
|
|
@@ -87,10 +88,16 @@ module.exports = {
|
|
|
87
88
|
args.TARGET = path.normalize(args.TARGET ?? process.cwd())
|
|
88
89
|
args.FORMAT = args.FORMAT ?? 'security'
|
|
89
90
|
if (args.FORMAT !== 'sarif' && args.FORMAT !== 'security') throw new Error('FORMAT must be one of \'sarif\' or \'security\'')
|
|
90
|
-
args.CATEGORIES
|
|
91
|
+
if (args.CATEGORIES && !['all', 'sca', 'sast', 'dast'].includes(args.CATEGORIES.toLowerCase())) throw new Error(`CATEGORIES must be one of 'all', 'SCA', 'SAST', or 'DAST'`)
|
|
92
|
+
if (!args.SCANNERS && !args.CATEGORIES) args.CATEGORIES = 'sast'
|
|
93
|
+
if (!args.SCANNERS && (args.CATEGORIES === 'all')) args.CATEGORIES = ''
|
|
94
|
+
if (args.SCANNERS) {
|
|
95
|
+
const unknownScanners = args.SCANNERS.split(',').filter(name => !availableScanners.find(scanner => scanner.name === name))
|
|
96
|
+
if (unknownScanners.length > 1) throw new Error(`Unknown scanners: ${unknownScanners.join(', ')}`)
|
|
97
|
+
else if (unknownScanners.length === 1) throw new Error(`Unknown scanner: ${unknownScanners[0]}`)
|
|
98
|
+
}
|
|
91
99
|
|
|
92
100
|
// Set scan parameters.
|
|
93
|
-
// const target = args.TARGET ? path.resolve(args.TARGET) : "$PWD" // target to scan
|
|
94
101
|
const target = path.resolve(args.TARGET) // target to scan
|
|
95
102
|
const assets = path.join(__dirname, '..', '..', 'scanners') // scanner assets
|
|
96
103
|
const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'radar-')) // output directory
|
|
@@ -114,8 +121,12 @@ module.exports = {
|
|
|
114
121
|
return false
|
|
115
122
|
})
|
|
116
123
|
|
|
124
|
+
// At least one scanner must be selected in order to have a successful scan.
|
|
125
|
+
if (scanners.length === 0) throw new Error('No available scanners selected.')
|
|
126
|
+
|
|
117
127
|
// Run scanners.
|
|
118
128
|
let isScanCompleted = true
|
|
129
|
+
log(`Running ${scanners.length} of ${availableScanners.length} scanners:`)
|
|
119
130
|
for (const scanner of scanners) {
|
|
120
131
|
let label = scanner.name
|
|
121
132
|
const spinner = new Spinner()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const levels = require('./levels')
|
|
2
2
|
module.exports = async (summary, format, log) => {
|
|
3
|
-
for (const finding of summary.notes) log(`${finding.artifact.name}:${finding.artifact.line}: ` + `${levels[format].single.note}`.bold + `${levels[format].single.suffix}:` + ` ${finding.message}\n`)
|
|
4
|
-
for (const finding of summary.warnings) log(`${finding.artifact.name}:${finding.artifact.line}: ` + `${levels[format].single.warning}`.bold.yellow + `${levels[format].single.suffix}:` + ` ${finding.message}\n`)
|
|
5
|
-
for (const finding of summary.errors) log(`${finding.artifact.name}:${finding.artifact.line}: ` + `${levels[format].single.error}`.bold.red + `${levels[format].single.suffix}:` + ` ${finding.message}\n`)
|
|
3
|
+
for (const finding of summary.notes) log(`${finding.artifact.name}:${finding.artifact.line}: ` + `${levels[format].single.note}`.bold + `${levels[format].single.suffix}:` + ` ${finding.tool}:` + ` ${finding.message}\n`)
|
|
4
|
+
for (const finding of summary.warnings) log(`${finding.artifact.name}:${finding.artifact.line}: ` + `${levels[format].single.warning}`.bold.yellow + `${levels[format].single.suffix}:` + ` ${finding.tool}:` + ` ${finding.message}\n`)
|
|
5
|
+
for (const finding of summary.errors) log(`${finding.artifact.name}:${finding.artifact.line}: ` + `${levels[format].single.error}`.bold.red + `${levels[format].single.suffix}:` + ` ${finding.tool}:` + ` ${finding.message}\n`)
|
|
6
6
|
}
|
package/src/util/sarif/merge.js
CHANGED
|
@@ -12,6 +12,7 @@ module.exports = {
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const fs = require('node:fs')
|
|
15
|
+
const path = require('node:path')
|
|
15
16
|
module.exports = async (files, outfile) => {
|
|
16
17
|
const sarif = {
|
|
17
18
|
version: '2.1.0',
|
|
@@ -25,14 +26,16 @@ module.exports = async (files, outfile) => {
|
|
|
25
26
|
for (const run of scan.runs) {
|
|
26
27
|
const tool = {
|
|
27
28
|
driver: {
|
|
28
|
-
name:
|
|
29
|
+
name: path.parse(file).name,
|
|
29
30
|
semanticVersion: run.tool.driver.semanticVersion,
|
|
30
31
|
informationUri: run.tool.driver.informationUri,
|
|
31
|
-
properties: run.tool.driver.properties,
|
|
32
|
+
properties: run.tool.driver.properties ?? {},
|
|
32
33
|
rules: []
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
tool.driver.properties.officialName = run.tool.driver.name
|
|
38
|
+
|
|
36
39
|
const rules = new Map()
|
|
37
40
|
for (const result of run.results) {
|
|
38
41
|
rules.set(result.ruleId, true)
|
|
@@ -26,7 +26,7 @@ module.exports = (sarif, dir) => {
|
|
|
26
26
|
|
|
27
27
|
for (const rule of run.tool.driver.rules) {
|
|
28
28
|
if (rule.id === result.ruleId) {
|
|
29
|
-
const level = rule
|
|
29
|
+
const level = rule?.defaultConfiguration?.level ?? 'error'
|
|
30
30
|
if (level === 'error' || level === 'warning' || level === 'note') {
|
|
31
31
|
finding.level = level
|
|
32
32
|
summary[`${finding.level}s`].push(finding)
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "2.1.0",
|
|
3
|
-
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
4
|
-
"runs": [
|
|
5
|
-
{
|
|
6
|
-
"tool": {
|
|
7
|
-
"driver": {
|
|
8
|
-
"name": "{{ metadata.tools.components[1].name }}",
|
|
9
|
-
"semanticVersion": "{{ metadata.tools.components[1].version }}",
|
|
10
|
-
"informationUri": "https://github.com/owasp-dep-scan/dep-scan",
|
|
11
|
-
"properties": {
|
|
12
|
-
"protocol_version": "v1.0.0",
|
|
13
|
-
"scanner_name": "{{ metadata.tools.components[1].name }}",
|
|
14
|
-
"scanner_version": "{{ metadata.tools.components[1].version }}",
|
|
15
|
-
"db": "https://github.com/AppThreat/vulnerability-db",
|
|
16
|
-
"scan_mode": "source"
|
|
17
|
-
},
|
|
18
|
-
"rules": [ {% for vuln in vulnerabilities %}{% set package = vuln['bom-ref'].split(':')[1] %}
|
|
19
|
-
{
|
|
20
|
-
"id": "{{ vuln['bom-ref'] }}",
|
|
21
|
-
"shortDescription": {
|
|
22
|
-
"text": "Vulnerable pkg: {{ package }}\nCVE: {{ vuln.id }}\nFix: {{ vuln.recommendation }}\n\n{% for prop in vuln.properties %}{{ prop.name }}: {{ prop.value }}\n{% endfor %}"
|
|
23
|
-
},
|
|
24
|
-
"fullDescription": {
|
|
25
|
-
"text": {{ vuln.description | tojson }}
|
|
26
|
-
},
|
|
27
|
-
"help": {
|
|
28
|
-
"text": "{{ vuln.recommendation }}"
|
|
29
|
-
},
|
|
30
|
-
"helpUri": "{% if vuln.source and vuln.source.url %}{{ vuln.source.url }}{% elif vuln.id and 'NPM-' in vuln.id %}https://osv.dev/vulnerability/{{ vuln.id.split('/')[0] }}{% else %}https://unknownhelpuri.com{% endif %}",
|
|
31
|
-
"properties": {
|
|
32
|
-
"tags": [
|
|
33
|
-
{% for prop in vuln.properties %}{% if 'Used' in prop.value -%}
|
|
34
|
-
"{{ 'Used' }}",
|
|
35
|
-
{% endif -%}{% if 'Reachable' in prop.value -%}
|
|
36
|
-
"{{ 'Reachable' }}",
|
|
37
|
-
{% endif -%}{% if 'Confirmed' in prop.value -%}
|
|
38
|
-
"{{ 'Confirmed' }}",
|
|
39
|
-
{% endif -%}{% if 'Exploits' in prop.value -%}
|
|
40
|
-
"{{ 'Exploits' }}",
|
|
41
|
-
{% endif -%}{% if 'PoC' in prop.value -%}
|
|
42
|
-
"{{ 'PoC' }}",
|
|
43
|
-
{% endif -%}{% if 'true' in prop.value and 'prioritized' in prop.name -%}
|
|
44
|
-
"{{ 'Prioritized' }}",
|
|
45
|
-
{% endif -%}{% endfor %}{% if 'MAL-' in vuln.id -%}
|
|
46
|
-
"{{ 'Malware' }}",
|
|
47
|
-
{% endif -%}"{{ vuln['id'] }}"
|
|
48
|
-
]
|
|
49
|
-
}
|
|
50
|
-
}{% if not loop.last %},{% endif %}
|
|
51
|
-
{% endfor %}
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
"results": [ {% for vuln in vulnerabilities %}{% set package = vuln['bom-ref'].split(':')[1] %}
|
|
56
|
-
{
|
|
57
|
-
"ruleId": "{{ vuln['bom-ref'] }}",
|
|
58
|
-
"level": {% if vuln.ratings[0].severity in ['critical','high'] -%}
|
|
59
|
-
"{{ 'error' }}",
|
|
60
|
-
{% endif -%}{% if vuln.ratings[0].severity in ['medium'] -%}
|
|
61
|
-
"{{ 'warning' }}",
|
|
62
|
-
{% endif -%}{% if vuln.ratings[0].severity in ['low'] -%}
|
|
63
|
-
"{{ 'note' }}",
|
|
64
|
-
{% endif -%}
|
|
65
|
-
"message": {
|
|
66
|
-
"text": "Vulnerability {{ vuln.id }} in pkg {{ package }}"
|
|
67
|
-
},
|
|
68
|
-
"locations": [
|
|
69
|
-
{
|
|
70
|
-
"physicalLocation": {
|
|
71
|
-
"artifactLocation": {
|
|
72
|
-
"uri": "lockfile",
|
|
73
|
-
"uriBaseId": "%SRCROOT%"
|
|
74
|
-
},
|
|
75
|
-
"region": {
|
|
76
|
-
"startLine": 1
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
"message": {
|
|
80
|
-
"text": "Vulnerability {{ vuln.id }} in pkg {{ package }}"
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
]
|
|
84
|
-
}
|
|
85
|
-
{% if not loop.last %},{% endif %}
|
|
86
|
-
{% endfor %}
|
|
87
|
-
]
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
}
|