Skip to content

Commit

Permalink
Fix 0.16.0 SARIF output & docs #114 (#115)
Browse files Browse the repository at this point in the history
Signed-off-by: Keith Zantow <[email protected]>
  • Loading branch information
kzantow authored Aug 26, 2021
1 parent 5fd84ca commit 4c02fc1
Show file tree
Hide file tree
Showing 13 changed files with 2,101 additions and 119 deletions.
7 changes: 7 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_size = 2
max_line_length = 80
ij_javascript_enforce_trailing_comma = keep
27 changes: 10 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,22 @@ Optionally, change the `fail-build` field to `false` to avoid failing the build

### Action Inputs

The only required key is `image`; all the other keys are optional. These are all the available keys to configure this action, along with its defaults:
The only required key is `image` or `path`; all the other keys are optional. These are all the available keys to configure this action, along with its defaults:

| Input Name | Description | Default Value |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `image` | The image to scan | N/A |
| `image` | The image to scan, this is mutually exclusive to `path` | N/A |
| `path` | The file path to scan, this is mutually exclusive to `image` | N/A |
| `debug` | Verbose logging output | `false` |
| `fail-build` | Fail the build if a vulnerability is found with a higher severity. That severity defaults to `"medium"` and can be set with `severity-cutoff`. | `false` |
| `grype-version` | An optional parameter to specify a specific version of `grype` to use for the scan. Default is the version locked to the scan-action release | `0.16.0` |
| `acs-report-enable` | Optionally, enable the feature that causes a result.sarif report to be generated after successful action execution. This report is compatible with GitHub Automated Code Scanning (ACS), as the artifact to upload for display as a Code Scanning Alert report. | `false` |
| `fail-build` | Fail the build if a vulnerability is found with a higher severity. That severity defaults to `"medium"` and can be set with `severity-cutoff`. | `true` |
| `acs-report-enable` | Generate a SARIF report and set the `sarif` output parameter after successful action execution. This report is compatible with GitHub Automated Code Scanning (ACS), as the artifact to upload for display as a Code Scanning Alert report. | `true` |
| `severity-cutoff` | With ACS reporting enabled, optionally specify the minimum vulnerability severity to trigger an "error" level ACS result. Valid choices are "negligible", "low", "medium", "high" and "critical". Any vulnerability with a severity less than this value will lead to a "warning" result. Default is "medium". | `"medium"` |

### Action Outputs

| Output Name | Description | Type |
| --------------- | ------------------------------------------------------------------- | ------ |
| vulnerabilities | Path to a JSON file with the list of vulnerabilities found in image | string |
| sarif | Path to a SARIF report file | string |

As a result of the action, you'll get a JSON file in the `anchore-reports` directory in the workspace:

- `vulnerabilities.json` - Vulnerabilities found in the image
| Output Name | Description | Type |
| ----------- | ----------------------------- | ------ |
| sarif | Path to the SARIF report file | string |

### Example Workflows

Expand All @@ -136,8 +131,6 @@ jobs:
with:
image: "localbuild/testimage:latest"
fail-build: true
- name: grype scan JSON results
run: for j in `ls ./anchore-reports/*.json`; do echo "---- ${j} ----"; cat ${j}; echo; done
```

Same example as above, but with Automated Code Scanning (ACS) feature enabled - with this example, the action will generate a SARIF report, which can be uploaded and then displayed as a Code Scanning Report in the GitHub UI.
Expand Down Expand Up @@ -184,8 +177,8 @@ For documentation on Grype itself, including other output capabilities, see the

Connect with the community directly on [slack](https://anchore.com/slack). These channels from Anchore's toolbox project are ideal for engaging development of help-related discussions:

- toolbox-dev
- toolbox-help
- grype-dev
- grype-help

[test]: https://github.com/anchore/scan-action
[test-img]: https://github.com/anchore/scan-action/workflows/Tests/badge.svg
27 changes: 11 additions & 16 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'Anchore Container Scan'
description: 'Scan docker containers with Grype for vulnerabilities'
name: "Anchore Container Scan"
description: "Scan docker containers with Grype for vulnerabilities"
branding:
color: blue
icon: check-circle
Expand All @@ -11,29 +11,24 @@ inputs:
description: 'The path to scan. This option is mutually exclusive with "image".'
required: false
debug:
description: 'Set this to any value to enable verbose debug output'
description: "Set this to any value to enable verbose debug output"
required: false
default: 'false'
default: "false"
fail-build:
description: 'Set to false to avoid failing based on severity-cutoff. Default is to fail when severity-cutoff is reached (or surpassed)'
required: false
default: 'true'
grype-version:
description: 'Optionally, specify the Grype version (e.g. 0.1.0) to use instead of the default version'
description: "Set to false to avoid failing based on severity-cutoff. Default is to fail when severity-cutoff is reached (or surpassed)"
required: false
default: "true"
acs-report-enable:
description: 'Optionally, enable feature that causes a result.sarif report to be generated after successful action execution. This report is compatible with GitHub Automated Code Scanning (ACS), as the artifact to upload for display as a Code Scanning Alert report.'
description: "Generate a SARIF report and set the `sarif` output parameter after successful action execution. This report is compatible with GitHub Automated Code Scanning (ACS), as the artifact to upload for display as a Code Scanning Alert report."
required: false
default: 'false'
default: "true"
severity-cutoff:
description: 'Optionally specify the minimum vulnerability severity to trigger an "error" level ACS result. Valid choices are "negligible", "low", "medium", "high" and "critical". Any vulnerability with a severity less than this value will lead to a "warning" result. Default is "medium".'
required: false
default: "medium"
outputs:
vulnerabilities:
description: 'The found vulnerabilities for the image'
sarif:
description: 'Path to a SARIF report file for the image'
description: "Path to a SARIF report file for the image"
runs:
using: 'node12'
main: 'dist/index.js'
using: "node12"
main: "dist/index.js"
80 changes: 45 additions & 35 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,27 @@ function dottedQuadFileVersion(version) {
return version;
}

function get_fix_versions(v) {
if (
v.vulnerability.fix &&
v.vulnerability.fix.state === "fixed" &&
v.vulnerability.fix.versions &&
v.vulnerability.fix.versions.length > 0
) {
return v.vulnerability.fix.versions.join(",");
}
return "";
}

function make_subtitle(v) {
let subtitle = `${v.vulnerability.description}`;
if (subtitle != "undefined") {
return subtitle;
}

if (v.vulnerability.fixedInVersion) {
return `Version ${v.artifact.version} is affected with an available fix in version ${v.vulnerability.fixedInVersion}`;
const fixVersions = get_fix_versions(v);
if (fixVersions) {
return `Version ${v.artifact.version} is affected with an available fix in versions ${fixVersions}`;
}

return `Version ${v.artifact.version} is affected with no fixes reported yet.`;
Expand All @@ -120,8 +133,13 @@ function grype_render_rules(vulnerabilities, source) {
ruleIDs.push(ruleID);
// Entirely possible to not have any links whatsoever
let link = v.vulnerability.id;
if ("links" in v.vulnerability) {
link = `[${v.vulnerability.id}](${v.vulnerability.links[0]})`;
if ("dataSource" in v.vulnerability) {
link = `[${v.vulnerability.id}](${v.vulnerability.dataSource})`;
} else if (
"urls" in v.vulnerability &&
v.vulnerability.urls.length > 0
) {
link = `[${v.vulnerability.id}](${v.vulnerability.urls[0]})`;
}

result.push({
Expand Down Expand Up @@ -149,7 +167,7 @@ function grype_render_rules(vulnerabilities, source) {
v.artifact.version +
"\n" +
"Fix Version: " +
"unknown" +
(get_fix_versions(v) || "none") +
"\n" +
"Type: " +
v.artifact.type +
Expand All @@ -175,7 +193,7 @@ function grype_render_rules(vulnerabilities, source) {
"|" +
v.artifact.version +
"|" +
"unknown" +
(get_fix_versions(v) || "none") +
"|" +
v.artifact.type +
"|" +
Expand Down Expand Up @@ -258,14 +276,12 @@ function grype_render_results(vulnerabilities, severity_cutoff_param, source) {
}

function vulnerabilities_to_sarif(
input_vulnerabilities,
grypeVulnerabilities,
severity_cutoff_param,
version,
source
) {
let rawdata = fs.readFileSync(input_vulnerabilities);
let parsed = JSON.parse(rawdata);
let vulnerabilities = parsed.matches;
let vulnerabilities = grypeVulnerabilities.matches;

const sarifOutput = {
$schema:
Expand Down Expand Up @@ -408,14 +424,12 @@ async function run() {
const failBuild = core.getInput("fail-build");
const acsReportEnable = core.getInput("acs-report-enable");
const severityCutoff = core.getInput("severity-cutoff");
const version = core.getInput("grype-version");
const out = await runScan({
source,
debug,
failBuild,
acsReportEnable,
severityCutoff,
version,
});
Object.keys(out).map((key) => {
core.setOutput(key, out[key]);
Expand All @@ -429,16 +443,14 @@ async function runScan({
source,
debug = "false",
failBuild = "true",
acsReportEnable = "false",
acsReportEnable = "true",
severityCutoff = "medium",
version = "",
}) {
const out = {};

const billOfMaterialsPath = "./anchore-reports/content.json";
const SEVERITY_LIST = ["negligible", "low", "medium", "high", "critical"];
let cmdArgs = [];
console.log(billOfMaterialsPath);

if (debug.toLowerCase() === "true") {
debug = "true";
cmdArgs = [`-vv`, `-o`, `json`];
Expand Down Expand Up @@ -471,12 +483,8 @@ async function runScan({
);
}

if (!version) {
version = `${grypeVersion}`;
}

core.debug(`Installing grype version ${version}`);
await installGrype(version);
core.debug(`Installing grype version ${grypeVersion}`);
await installGrype(grypeVersion);

core.debug("Image: " + source);
core.debug("Debug Output: " + debug);
Expand Down Expand Up @@ -504,21 +512,20 @@ async function runScan({
cmdOpts.ignoreReturnCode = true;

core.info("\nAnalyzing: " + source);
core.debug(`Running cmd: ${cmd} ` + cmdArgs.join(" "));
let exitCode = await exec(cmd, cmdArgs, cmdOpts);
let grypeVulnerabilities = JSON.parse(cmdOutput);

// handle output
fs.writeFileSync(
"./vulnerabilities.json",
JSON.stringify(grypeVulnerabilities)
);
const exitCode = await core.group("Grype Output", () => {
core.info(`Executing: ${cmd} ` + cmdArgs.join(" "));
return exec(cmd, cmdArgs, cmdOpts);
});

let grypeVulnerabilities = JSON.parse(cmdOutput);

if (acsReportEnable) {
try {
const serifOut = sarifGrypeGeneration(
grypeVulnerabilities,
severityCutoff.toLowerCase(),
version,
grypeVersion,
source
);
Object.assign(out, serifOut);
Expand All @@ -533,8 +540,6 @@ async function runScan({
);
}

out.vulnerabilities = "./vulnerabilities.json";

// If there is a non-zero exit status code there are a couple of potential reporting paths
if (failBuild === false && exitCode > 0) {
// There was a non-zero exit status but it wasn't because of failing severity, this must be
Expand All @@ -554,11 +559,16 @@ async function runScan({
return out;
}

function sarifGrypeGeneration(severity_cutoff_param, version, source) {
function sarifGrypeGeneration(
grypeVulnerabilities,
severity_cutoff_param,
version,
source
) {
// sarif generate section
const SARIF_FILE = "./results.sarif";
let sarifOutput = vulnerabilities_to_sarif(
"./vulnerabilities.json",
grypeVulnerabilities,
severity_cutoff_param,
version,
source
Expand Down
Loading

0 comments on commit 4c02fc1

Please sign in to comment.