@eurekadevsecops/radar 1.0.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eurekadevsecops/radar",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Radar is an open-source orchestrator of security scanners.",
5
5
  "homepage": "https://www.eurekadevsecops.com/radar",
6
6
  "keywords": [
@@ -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/owasp-dep-scan/dep-scan depscan --src /app --reports-dir /output/depscan --report-name depscan.sarif --report-template /input/sarif.j2 2>&1
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
@@ -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 -t -v $1:/app -v $2:/input -v $3:/home/output radar/opengrep:latest /app 2>&1
7
+ docker run --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/eurekadevsecops/radar-opengrep 2>&1
@@ -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. The list of
52
- available scanners is filtered by the given SCANNERS and CATEGORIES on the
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 = args.CATEGORIES ?? 'sast'
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
  }
@@ -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: run.tool.driver.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)
@@ -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
- }