Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ The inputs `image`, `path`, and `sbom` are mutually exclusive to specify the sou
| `sbom` | The SBOM to scan | N/A |
| `registry-username` | The registry username to use when authenticating to an external registry | |
| `registry-password` | The registry password to use when authenticating to an external registry | |
| `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` |
| `output-format` | Set the output parameter after successful action execution. Valid choices are "json" and "sarif" | `sarif` |
| `severity-cutoff` | Optionally specify the minimum vulnerability severity to trigger a failure. 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"` |
| `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` |
| `output-format` | Set the output parameter after successful action execution. Valid choices are `json`, `sarif`, and `table`, where `table` output will print to the console instead of generating a file. | `sarif` |
| `severity-cutoff` | Optionally specify the minimum vulnerability severity to trigger a failure. 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

Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ inputs:
required: false
default: "true"
output-format:
description: 'Set the output parameter after successful action execution. Valid choices are "json" and "sarif".'
description: 'Set the output parameter after successful action execution. Valid choices are "json", "sarif", and "table".'
required: false
default: "sarif"
severity-cutoff:
Expand Down
43 changes: 24 additions & 19 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ async function runScan({ source, failBuild, severityCutoff, outputFormat }) {
}

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

if (core.isDebug()) {
Expand Down Expand Up @@ -227,34 +227,39 @@ async function runScan({ source, failBuild, severityCutoff, outputFormat }) {
core.debug(cmdOutput);
}

if (outputFormat === "sarif") {
const SARIF_FILE = "./results.sarif";
fs.writeFileSync(SARIF_FILE, cmdOutput);
out.sarif = SARIF_FILE;
} else {
const REPORT_FILE = "./results.json";
fs.writeFileSync(REPORT_FILE, cmdOutput);
out.report = REPORT_FILE;
}

if (failBuild === true && exitCode > 0) {
core.setFailed(
`Failed minimum severity level. Found vulnerabilities with level ${severityCutoff} or higher`
);
switch (outputFormat) {
case "sarif": {
const SARIF_FILE = "./results.sarif";
fs.writeFileSync(SARIF_FILE, cmdOutput);
out.sarif = SARIF_FILE;
break;
}
case "json": {
const REPORT_FILE = "./results.json";
fs.writeFileSync(REPORT_FILE, cmdOutput);
out.report = REPORT_FILE;
break;
}
default: // e.g. table
core.info(cmdOutput);
}

// 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
// a grype problem
if (exitCode > 0) {
if (!severityCutoff) {
// There was a non-zero exit status but it wasn't because of failing severity, this must be
// a grype problem
core.warning("grype had a non-zero exit status when running");
} else if (failBuild === true) {
core.setFailed(
`Failed minimum severity level. Found vulnerabilities with level '${severityCutoff}' or higher`
);
} else {
// There is a non-zero exit status code with severity cut off, although there is still a chance this is grype
// that is broken, it will most probably be a failed severity. Using warning here will make it bubble up in the
// Actions UI
core.warning(
`Failed minimum severity level. Found vulnerabilities with level ${severityCutoff} or higher`
`Failed minimum severity level. Found vulnerabilities with level '${severityCutoff}' or higher`
);
}
}
Expand Down
43 changes: 24 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ async function runScan({ source, failBuild, severityCutoff, outputFormat }) {
}

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

if (core.isDebug()) {
Expand Down Expand Up @@ -212,34 +212,39 @@ async function runScan({ source, failBuild, severityCutoff, outputFormat }) {
core.debug(cmdOutput);
}

if (outputFormat === "sarif") {
const SARIF_FILE = "./results.sarif";
fs.writeFileSync(SARIF_FILE, cmdOutput);
out.sarif = SARIF_FILE;
} else {
const REPORT_FILE = "./results.json";
fs.writeFileSync(REPORT_FILE, cmdOutput);
out.report = REPORT_FILE;
}

if (failBuild === true && exitCode > 0) {
core.setFailed(
`Failed minimum severity level. Found vulnerabilities with level ${severityCutoff} or higher`
);
switch (outputFormat) {
case "sarif": {
const SARIF_FILE = "./results.sarif";
fs.writeFileSync(SARIF_FILE, cmdOutput);
out.sarif = SARIF_FILE;
break;
}
case "json": {
const REPORT_FILE = "./results.json";
fs.writeFileSync(REPORT_FILE, cmdOutput);
out.report = REPORT_FILE;
break;
}
default: // e.g. table
core.info(cmdOutput);
}

// 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
// a grype problem
if (exitCode > 0) {
if (!severityCutoff) {
// There was a non-zero exit status but it wasn't because of failing severity, this must be
// a grype problem
core.warning("grype had a non-zero exit status when running");
} else if (failBuild === true) {
core.setFailed(
`Failed minimum severity level. Found vulnerabilities with level '${severityCutoff}' or higher`
);
} else {
// There is a non-zero exit status code with severity cut off, although there is still a chance this is grype
// that is broken, it will most probably be a failed severity. Using warning here will make it bubble up in the
// Actions UI
core.warning(
`Failed minimum severity level. Found vulnerabilities with level ${severityCutoff} or higher`
`Failed minimum severity level. Found vulnerabilities with level '${severityCutoff}' or higher`
);
}
}
Expand Down
43 changes: 43 additions & 0 deletions tests/action_args.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,47 @@ describe("Github action args", () => {
spyInput.mockRestore();
spyOutput.mockRestore();
});

it("runs with table output", async () => {
const inputs = {
image: "localhost:5000/match-coverage/debian:latest",
"fail-build": "true",
"output-format": "table",
"severity-cutoff": "medium",
};
const spyInput = jest.spyOn(core, "getInput").mockImplementation((name) => {
try {
return inputs[name];
} finally {
inputs[name] = true;
}
});

const outputs = {};
const spyOutput = jest
.spyOn(core, "setOutput")
.mockImplementation((name, value) => {
outputs[name] = value;
});

let stdout = "";
const spyStdout = jest.spyOn(core, "info").mockImplementation((value) => {
stdout += value;
});

await run();

Object.keys(inputs).map((name) => {
expect(inputs[name]).toBe(true);
});

expect(stdout).toContain("VULNERABILITY");

expect(outputs["sarif"]).toBeFalsy();
expect(outputs["json"]).toBeFalsy();

spyInput.mockRestore();
spyOutput.mockRestore();
spyStdout.mockRestore();
});
});