Skip to content
Open
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
36 changes: 34 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,15 @@ inputs:
required: false
default: "true"
cdx:
description: "Write CycloneDX 1.6 SBOM to a timestamped .cdx.json file"
description: "Write CycloneDX 1.6 SBOM to a timestamped .cdx.json file (deprecated alias — prefer sbom: cyclonedx)"
required: false
default: "false"
sbom:
description: "SBOM format to emit: cyclonedx or spdx (SPDX 2.3). Takes precedence over the deprecated cdx boolean when set."
required: false
default: ""
sbom-inventory-only:
description: "Omit the vulnerability overlay from SBOM output, leaving a pure inventory (requires sbom to be set)"
required: false
default: "false"
ca-cert:
Expand Down Expand Up @@ -244,6 +252,8 @@ runs:
INPUT_SARIF: ${{ inputs.sarif }}
INPUT_NO_CACHE: ${{ inputs.no-cache }}
INPUT_CDX: ${{ inputs.cdx }}
INPUT_SBOM: ${{ inputs.sbom }}
INPUT_SBOM_INVENTORY_ONLY: ${{ inputs.sbom-inventory-only }}
INPUT_CA_CERT: ${{ inputs.ca-cert }}
INPUT_CHECK_OVERRIDES: ${{ inputs.check-overrides }}
INPUT_CHECK_MAINTENANCE: ${{ inputs.check-maintenance }}
Expand Down Expand Up @@ -296,10 +306,32 @@ runs:
args+=("--no-cache")
fi

if [[ "${INPUT_CDX}" == "true" ]]; then
sbom_input=$(echo "${INPUT_SBOM:-}" | tr '[:upper:]' '[:lower:]')
if [[ -n "${sbom_input}" ]]; then
case "${sbom_input}" in
cyclonedx|cyclone-dx|cdx)
args+=("--sbom" "cyclonedx")
;;
spdx|spdx2.3|spdx-2.3|spdx_2.3)
args+=("--sbom" "spdx")
;;
*)
echo "::error::Invalid sbom input '${INPUT_SBOM}'. Valid values: cyclonedx, spdx."
exit 1
;;
esac
elif [[ "${INPUT_CDX}" == "true" ]]; then
args+=("--cdx")
fi

if [[ "${INPUT_SBOM_INVENTORY_ONLY}" == "true" ]]; then
if [[ -z "${sbom_input}" && "${INPUT_CDX}" != "true" ]]; then
echo "::warning::sbom-inventory-only is set without sbom/cdx output — it will have no effect."
else
args+=("--sbom-inventory-only")
fi
fi

if [[ -n "${INPUT_CA_CERT}" ]]; then
args+=("--ca-cert" "${INPUT_CA_CERT}")
fi
Expand Down
37 changes: 37 additions & 0 deletions tests/github-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,41 @@ describe("GitHub Action contract", () => {
expect(actionDefinition.indexOf("INPUT_CHECK_LICENSES")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf('args+=("--check-licenses")')).toBeGreaterThan(-1);
});

it("exposes an sbom input so SPDX output is reachable from the Action", () => {
expect(actionDefinition.indexOf(" sbom:")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf("INPUT_SBOM")).toBeGreaterThan(-1);
// Both supported formats must be wired through to the CLI.
expect(actionDefinition.indexOf('args+=("--sbom" "cyclonedx")')).toBeGreaterThan(-1);
expect(actionDefinition.indexOf('args+=("--sbom" "spdx")')).toBeGreaterThan(-1);
});

it("keeps the deprecated cdx boolean working as a cyclonedx alias", () => {
expect(actionDefinition.indexOf(" cdx:")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf('args+=("--cdx")')).toBeGreaterThan(-1);
// sbom takes precedence: the cdx branch must be the fallback (elif), not
// an independent if, so setting both does not emit two SBOM flags.
const sbomBranch = actionDefinition.indexOf('sbom_input=$(echo "${INPUT_SBOM:-}"');
const cdxBranch = actionDefinition.indexOf('elif [[ "${INPUT_CDX}" == "true" ]]');
expect(sbomBranch).toBeGreaterThan(-1);
expect(cdxBranch).toBeGreaterThan(sbomBranch);
});

it("rejects unknown sbom formats instead of silently scanning without an SBOM", () => {
expect(actionDefinition.indexOf("Invalid sbom input")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf("Valid values: cyclonedx, spdx")).toBeGreaterThan(-1);
});

it("supports sbom-inventory-only and warns when no SBOM output is enabled", () => {
expect(actionDefinition.indexOf("sbom-inventory-only:")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf("INPUT_SBOM_INVENTORY_ONLY")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf('args+=("--sbom-inventory-only")')).toBeGreaterThan(-1);
expect(actionDefinition.indexOf("without sbom/cdx output")).toBeGreaterThan(-1);
});

it("normalizes sbom aliases (case-insensitive cdx/spdx2.3 variants)", () => {
expect(actionDefinition.indexOf("tr '[:upper:]' '[:lower:]'")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf("cyclone-dx|cdx")).toBeGreaterThan(-1);
expect(actionDefinition.indexOf("spdx|spdx2.3|spdx-2.3")).toBeGreaterThan(-1);
});
});
4 changes: 3 additions & 1 deletion website/docs/github-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ For usage patterns and complete workflow examples, see [Workflow Integration](./
|---|---|---|---|
| `sarif` | `false` | Write SARIF 2.1.0 output to a timestamped `.sarif` file for GitHub Code Scanning upload | `sarif: "true"` |
| `report` | _(none)_ | Write an HTML report to this directory path; `--no-open` is applied automatically | `report: "./cve-report"` |
| `cdx` | `false` | Write a CycloneDX 1.6 SBOM to a timestamped `.cdx.json` file | `cdx: "true"` |
| `sbom` | _(none)_ | SBOM format to emit: `cyclonedx` (1.6) or `spdx` (2.3). Takes precedence over `cdx` when set | `sbom: spdx` |
| `sbom-inventory-only` | `false` | Omit the vulnerability overlay from SBOM output, leaving a pure inventory (requires `sbom` or `cdx`) | `sbom-inventory-only: "true"` |
| `cdx` | `false` | _(Deprecated — prefer `sbom: cyclonedx`.)_ Write a CycloneDX 1.6 SBOM to a timestamped `.cdx.json` file | `cdx: "true"` |

---

Expand Down