Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow input parameter output-format and output report to be used #187

Merged
merged 4 commits into from
Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ inputs:
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"
output-format:
description: 'Set the output parameter after successful action execution. Valid choices are "json" and "sarif".'
required: false
default: "sarif"
acs-report-enable:
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
Expand All @@ -28,6 +32,8 @@ inputs:
outputs:
sarif:
description: "Path to a SARIF report file for the image"
report:
description: "Path to a JSON report file for the image"
runs:
using: "node16"
main: "dist/index.js"
35 changes: 33 additions & 2 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,14 @@ async function run() {
const source = sourceInput();
const failBuild = core.getInput("fail-build") || "true";
const acsReportEnable = core.getInput("acs-report-enable") || "true";
const outputFormat = core.getInput("output-format") || "sarif";
const severityCutoff = core.getInput("severity-cutoff") || "medium";
const out = await runScan({
source,
failBuild,
acsReportEnable,
severityCutoff,
outputFormat,
});
Object.keys(out).map((key) => {
core.setOutput(key, out[key]);
Expand All @@ -118,7 +120,13 @@ async function run() {
}
}

async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
async function runScan({
source,
failBuild,
acsReportEnable,
severityCutoff,
outputFormat,
}) {
const out = {};

const env = {
Expand All @@ -139,6 +147,7 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
}

const SEVERITY_LIST = ["negligible", "low", "medium", "high", "critical"];
const FORMAT_LIST = ["sarif", "json"];
let cmdArgs = [];

if (core.isDebug()) {
Expand All @@ -149,10 +158,16 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {

acsReportEnable = acsReportEnable.toLowerCase() === "true";

if (outputFormat !== "sarif" && acsReportEnable) {
throw new Error(
`Invalid output-format selected. If acs-report-enabled is true (which is the default if it is omitted), the output-format parameter must be sarif or must be omitted`
);
}

if (acsReportEnable) {
cmdArgs.push("-o", "sarif");
} else {
cmdArgs.push("-o", "json");
cmdArgs.push("-o", outputFormat);
}

if (
Expand All @@ -166,6 +181,17 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
`Invalid severity-cutoff value is set to ${severityCutoff} - please ensure you are choosing either negligible, low, medium, high, or critical`
);
}
if (
!FORMAT_LIST.some(
(item) =>
typeof outputFormat.toLowerCase() === "string" &&
item === outputFormat.toLowerCase()
)
) {
throw new Error(
`Invalid output-format value is set to ${outputFormat} - please ensure you are choosing either json or sarif`
);
}

core.debug(`Installing grype version ${grypeVersion}`);
await installGrype(grypeVersion);
Expand All @@ -174,6 +200,7 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
core.debug("Fail Build: " + failBuild);
core.debug("Severity Cutoff: " + severityCutoff);
core.debug("ACS Enable: " + acsReportEnable);
core.debug("Output Format: " + outputFormat);

core.debug("Creating options for GRYPE analyzer");

Expand Down Expand Up @@ -225,6 +252,10 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
const SARIF_FILE = "./results.sarif";
fs.writeFileSync(SARIF_FILE, cmdOutput);
out.sarif = SARIF_FILE;
} else {
const REPORT_FILE = "./results.report";
fs.writeFileSync(REPORT_FILE, cmdOutput);
out.report = REPORT_FILE;
}

if (failBuild === true && exitCode > 0) {
Expand Down
35 changes: 33 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ async function run() {
const source = sourceInput();
const failBuild = core.getInput("fail-build") || "true";
const acsReportEnable = core.getInput("acs-report-enable") || "true";
const outputFormat = core.getInput("output-format") || "sarif";
const severityCutoff = core.getInput("severity-cutoff") || "medium";
const out = await runScan({
source,
failBuild,
acsReportEnable,
severityCutoff,
outputFormat,
});
Object.keys(out).map((key) => {
core.setOutput(key, out[key]);
Expand All @@ -103,7 +105,13 @@ async function run() {
}
}

async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
async function runScan({
source,
failBuild,
acsReportEnable,
severityCutoff,
outputFormat,
}) {
const out = {};

const env = {
Expand All @@ -124,6 +132,7 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
}

const SEVERITY_LIST = ["negligible", "low", "medium", "high", "critical"];
const FORMAT_LIST = ["sarif", "json"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably check that if outputFormat !== "sarif" && acsReportEnable, it should be an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Basically the acs-report-enable flag would be an alias for setting the output format to sarif, and setting both an error unless they match.

And I actually see no reason not to support the other output formats available on the Grype command line, excepting template which gets a lot more complicated (and a potential code injection point depending on what Go templates can do, I'm not familiar with them).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree -- the other PR I referenced is basically asking for table output, so we'll make some changes there for aligning this stuff.

let cmdArgs = [];

if (core.isDebug()) {
Expand All @@ -134,10 +143,16 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {

acsReportEnable = acsReportEnable.toLowerCase() === "true";

if (outputFormat !== "sarif" && acsReportEnable) {
throw new Error(
`Invalid output-format selected. If acs-report-enabled is true (which is the default if it is omitted), the output-format parameter must be sarif or must be omitted`
);
}

if (acsReportEnable) {
cmdArgs.push("-o", "sarif");
} else {
cmdArgs.push("-o", "json");
cmdArgs.push("-o", outputFormat);
}

if (
Expand All @@ -151,6 +166,17 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
`Invalid severity-cutoff value is set to ${severityCutoff} - please ensure you are choosing either negligible, low, medium, high, or critical`
);
}
if (
!FORMAT_LIST.some(
(item) =>
typeof outputFormat.toLowerCase() === "string" &&
item === outputFormat.toLowerCase()
)
) {
throw new Error(
`Invalid output-format value is set to ${outputFormat} - please ensure you are choosing either json or sarif`
);
}

core.debug(`Installing grype version ${grypeVersion}`);
await installGrype(grypeVersion);
Expand All @@ -159,6 +185,7 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
core.debug("Fail Build: " + failBuild);
core.debug("Severity Cutoff: " + severityCutoff);
core.debug("ACS Enable: " + acsReportEnable);
core.debug("Output Format: " + outputFormat);

core.debug("Creating options for GRYPE analyzer");

Expand Down Expand Up @@ -210,6 +237,10 @@ async function runScan({ source, failBuild, acsReportEnable, severityCutoff }) {
const SARIF_FILE = "./results.sarif";
fs.writeFileSync(SARIF_FILE, cmdOutput);
out.sarif = SARIF_FILE;
} else {
const REPORT_FILE = "./results.report";
fs.writeFileSync(REPORT_FILE, cmdOutput);
out.report = REPORT_FILE;
}

if (failBuild === true && exitCode > 0) {
Expand Down
2 changes: 2 additions & 0 deletions tests/grype_command.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe("Grype command", () => {
debug: "false",
failBuild: "false",
acsReportEnable: "true",
outputFormat: "sarif",
severityCutoff: "high",
version: "0.6.0",
});
Expand All @@ -40,6 +41,7 @@ describe("Grype command", () => {
source: "asdf",
failBuild: "false",
acsReportEnable: "false",
outputFormat: "json",
severityCutoff: "low",
version: "0.6.0",
});
Expand Down
1 change: 1 addition & 0 deletions tests/sarif_output.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const testSource = async (source, vulnerabilities) => {
debug: "false",
failBuild: "false",
acsReportEnable: "true",
outputFormat: "sarif",
severityCutoff: "medium",
});

Expand Down