@eurekadevsecops/radar 1.6.1 → 1.7.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 +12 -1
- package/scanners/gitleaks/run.sh +12 -1
- package/scanners/grype/run.sh +12 -1
- package/scanners/opengrep/run.sh +12 -1
- package/src/commands/scan.js +2 -2
- package/src/util/git/index.js +18 -1
- package/src/util/runner.js +37 -6
- package/src/util/sarif/transforms/normalize.js +18 -6
package/package.json
CHANGED
package/scanners/depscan/run.sh
CHANGED
|
@@ -4,4 +4,15 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
trap cleanup TERM INT
|
|
9
|
+
|
|
10
|
+
cleanup()
|
|
11
|
+
{
|
|
12
|
+
PID=$(cat $3/depscan.cid)
|
|
13
|
+
docker stop $PID
|
|
14
|
+
rm $3/depscan.cid
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
docker run --cidfile $3/depscan.cid --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/eurekadevsecops/radar-depscan 2>&1
|
package/scanners/gitleaks/run.sh
CHANGED
|
@@ -4,4 +4,15 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
trap cleanup TERM INT
|
|
9
|
+
|
|
10
|
+
cleanup()
|
|
11
|
+
{
|
|
12
|
+
PID=$(cat $3/gitleaks.cid)
|
|
13
|
+
docker stop $PID
|
|
14
|
+
rm $3/gitleaks.cid
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
docker run --cidfile $3/gitleaks.cid --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/grype/run.sh
CHANGED
|
@@ -4,4 +4,15 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
trap cleanup TERM INT
|
|
9
|
+
|
|
10
|
+
cleanup()
|
|
11
|
+
{
|
|
12
|
+
PID=$(cat $3/grype.cid)
|
|
13
|
+
docker stop $PID
|
|
14
|
+
rm $3/grype.cid
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
docker run --cidfile $3/grype.cid --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/eurekadevsecops/radar-grype 2>&1
|
package/scanners/opengrep/run.sh
CHANGED
|
@@ -4,4 +4,15 @@
|
|
|
4
4
|
# $3 - Path to the output folder where scan results should be stored
|
|
5
5
|
|
|
6
6
|
set -e
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
trap cleanup TERM INT
|
|
9
|
+
|
|
10
|
+
cleanup()
|
|
11
|
+
{
|
|
12
|
+
PID=$(cat $3/opengrep.cid)
|
|
13
|
+
docker stop $PID
|
|
14
|
+
rm $3/opengrep.cid
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
docker run --cidfile $3/opengrep.cid --rm -v $1:/app -v $2:/input -v $3:/output ghcr.io/eurekadevsecops/radar-opengrep 2>&1
|
package/src/commands/scan.js
CHANGED
|
@@ -150,8 +150,8 @@ module.exports = {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// Send telemetry: git metadata.
|
|
153
|
+
const metadata = git.metadata(target)
|
|
153
154
|
if (telemetry.enabled && scanID) {
|
|
154
|
-
const metadata = git.metadata(target)
|
|
155
155
|
await telemetry.send(`scans/:scanID/metadata`, { scanID }, { metadata })
|
|
156
156
|
await telemetry.sendSensitive(`scans/:scanID/metadata`, { scanID }, { metadata })
|
|
157
157
|
}
|
|
@@ -173,7 +173,7 @@ module.exports = {
|
|
|
173
173
|
|
|
174
174
|
// Transform scan findings: treat warnings and notes as errors, and normalize location paths.
|
|
175
175
|
if (escalations) results.sarif = SARIF.transforms.escalate(results.sarif, escalations)
|
|
176
|
-
SARIF.transforms.normalize(results.sarif, target)
|
|
176
|
+
SARIF.transforms.normalize(results.sarif, target, metadata, git.root(target))
|
|
177
177
|
|
|
178
178
|
// Write findings to the destination SARIF file.
|
|
179
179
|
if (outfile) fs.writeFileSync(outfile, JSON.stringify(results.sarif))
|
package/src/util/git/index.js
CHANGED
|
@@ -84,6 +84,23 @@ git rev-list --abbrev=4 --abbrev-commit --all | \
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function root(folder) {
|
|
88
|
+
try {
|
|
89
|
+
// Get the full OS path to the root of the repo.
|
|
90
|
+
const root = execSync('git rev-parse --show-toplevel', { cwd: folder }).toString().trim()
|
|
91
|
+
return root
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
type: 'error',
|
|
95
|
+
error: {
|
|
96
|
+
code: 'E_GIT_METADATA',
|
|
97
|
+
details: error
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
87
103
|
module.exports = {
|
|
88
|
-
metadata
|
|
104
|
+
metadata,
|
|
105
|
+
root
|
|
89
106
|
}
|
package/src/util/runner.js
CHANGED
|
@@ -11,16 +11,40 @@ const runAll = async ({ scanners, target, assets, outdir, quiet, log }) => {
|
|
|
11
11
|
// Results will include the stdout log and the final combined SARIF object.
|
|
12
12
|
const results = { log: '' }
|
|
13
13
|
|
|
14
|
-
// Run all scanners. This will produce one output SARIF file per scanner.
|
|
14
|
+
// Run all scanners (concurrently). This will produce one output SARIF file per scanner.
|
|
15
|
+
const jobs = []
|
|
16
|
+
const processes = []
|
|
15
17
|
for (const scanner of scanners) {
|
|
16
|
-
|
|
18
|
+
jobs.push(runScanner(processes, { scanner, target, assets, outdir, quiet, log, display: {
|
|
17
19
|
begin: () => scanner.name,
|
|
18
20
|
progress: (label, duration) => `${scanner.name} [${humanize.duration(duration)}]`,
|
|
19
21
|
success: (label) => label,
|
|
20
22
|
error: (label) => label
|
|
21
|
-
}})
|
|
23
|
+
}}))
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
// Wait for scanner runs to complete.
|
|
27
|
+
await Promise.all(jobs)
|
|
28
|
+
.then(async (result) => {
|
|
29
|
+
for (const job of jobs) {
|
|
30
|
+
results.log += await job
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
.catch((error) => {
|
|
34
|
+
// Early exit: If a job fails, kill all the other jobs. This ensures
|
|
35
|
+
// that the CLI doesn't hang waiting for all jobs to complete when
|
|
36
|
+
// we know already that the scan failed.
|
|
37
|
+
let exitCode = null
|
|
38
|
+
for (const p of processes) {
|
|
39
|
+
if (p.killed || p.exitCode !== null) {
|
|
40
|
+
exitCode = p.exitCode
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
p.kill('SIGKILL')
|
|
44
|
+
}
|
|
45
|
+
if (exitCode) throw `Command exited with exit code ${exitCode}: ${error}`
|
|
46
|
+
})
|
|
47
|
+
|
|
24
48
|
// Merge all output SARIF files into one and load it into a JS object.
|
|
25
49
|
const consolidated = path.join(outdir, 'scan.sarif')
|
|
26
50
|
await SARIF.transforms.merge(consolidated, scanners.map(s => path.join(outdir, `${s.name}.sarif`)))
|
|
@@ -29,7 +53,7 @@ const runAll = async ({ scanners, target, assets, outdir, quiet, log }) => {
|
|
|
29
53
|
return results
|
|
30
54
|
}
|
|
31
55
|
|
|
32
|
-
const runScanner = async ({ scanner, target, assets, outdir, quiet, log, display }) => {
|
|
56
|
+
const runScanner = async (processes, { scanner, target, assets, outdir, quiet, log, display }) => {
|
|
33
57
|
let label = display.begin()
|
|
34
58
|
const spinner = new Spinner()
|
|
35
59
|
if (!quiet) spinner.start(label)
|
|
@@ -51,12 +75,19 @@ const runScanner = async ({ scanner, target, assets, outdir, quiet, log, display
|
|
|
51
75
|
cmd = cmd.replaceAll('${output}', outdir)
|
|
52
76
|
/* eslint-enable no-template-curly-in-string */
|
|
53
77
|
|
|
54
|
-
|
|
78
|
+
// Run the child process. Store the process information (like pid)
|
|
79
|
+
// in case we need to kill the process. Capture stdout log.
|
|
80
|
+
const promise = exec(cmd)
|
|
81
|
+
processes.push(promise.child)
|
|
82
|
+
const { stdout } = await promise
|
|
55
83
|
runLog = stdout
|
|
56
84
|
|
|
57
85
|
if (!quiet) spinner.success(display.success(label))
|
|
58
86
|
} catch (error) {
|
|
59
|
-
if
|
|
87
|
+
// Display an error spinner, but if the child process
|
|
88
|
+
// was killed then don't display anything.
|
|
89
|
+
if (!quiet && !error.killed) spinner.error(display.error(label))
|
|
90
|
+
if (error.killed) spinner.stop()
|
|
60
91
|
|
|
61
92
|
let message = `${error}`
|
|
62
93
|
if (error.stdout) message += error.stdout
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
const path = require('node:path')
|
|
2
|
-
module.exports = (sarif, dir) => {
|
|
2
|
+
module.exports = (sarif, dir, git, root) => {
|
|
3
3
|
// Normalize findings.
|
|
4
4
|
for (const run of sarif.runs) {
|
|
5
|
-
if (!run.results) continue
|
|
6
|
-
for (const result of run.results) {
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
// Record the source repo location and the relative target subfolder within the repo.
|
|
7
|
+
run.originalUriBaseIds = {
|
|
8
|
+
"SOURCE": {
|
|
9
|
+
"uri": git.repo.url.https,
|
|
10
|
+
"description": "Source origin for the target being scanned (ie. git repo URL)."
|
|
11
|
+
},
|
|
12
|
+
"TARGET": {
|
|
13
|
+
"uri": `${path.relative(root, dir)}`,
|
|
14
|
+
"uriBaseId": "SOURCE",
|
|
15
|
+
"description": "Scan target (subfolder) within the source repo or folder."
|
|
16
|
+
}
|
|
17
|
+
}
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
// Make all physical locations for the result relative to the scan directory.
|
|
20
|
+
if (!run.results) continue
|
|
21
|
+
for (const result of run.results) {
|
|
12
22
|
for (const location of result.locations) {
|
|
13
23
|
if (location.physicalLocation?.artifactLocation?.uri?.startsWith('/app')) {
|
|
14
24
|
let file = path.relative('/app', location.physicalLocation.artifactLocation.uri)
|
|
25
|
+
if (result?.message?.text) result.message.text = result.message.text.replace(location.physicalLocation.artifactLocation.uri, file)
|
|
15
26
|
location.physicalLocation.artifactLocation.uri = file
|
|
16
27
|
}
|
|
17
28
|
else if (location.physicalLocation?.artifactLocation?.uri?.startsWith('/')) {
|
|
18
29
|
let file = path.relative('/', location.physicalLocation.artifactLocation.uri)
|
|
30
|
+
if (result?.message?.text) result.message.text = result.message.text.replace(location.physicalLocation.artifactLocation.uri, file)
|
|
19
31
|
location.physicalLocation.artifactLocation.uri = file
|
|
20
32
|
}
|
|
21
33
|
}
|