A GitHub Action for creating a software bill of materials (SBOM) using Syft, with optional vulnerability scanning using Grype.
- uses: scality/[email protected]
with:
target: /usr/local/bin
This will create SBOM result file like /tmp/sbom/bin_sbom.json
in the default output directory /tmp/sbom
.
The main SBOM action is responsible for generating SBOMs.
Parameter | Description | Default |
---|---|---|
grype-version |
Grype version to use | 0.91.0 |
syft-version |
Syft version to use | 1.22.0 |
target |
The target to scan (file, directory, image, ISO, or repo) | ./ |
target-type |
Type of target to scan (file, directory, image, iso, repo) | file |
output-format |
Format of the generated SBOM | cyclonedx-json |
output-file |
A specific file location to store the SBOM | |
output-dir |
Directory to store generated SBOM files | /tmp/sbom |
exclude-mediatypes |
Media types to exclude for images (comma-separated) | |
distro |
Linux distribution of the target (if not auto-detected) | |
name |
Override the detected name of the target | |
version |
Override the detected version of the target | |
vuln |
Enable vulnerability scanning | false |
vuln-output-format |
Format for the vulnerability report (HTML or JSON) when vuln is enabled |
json |
vuln-output-file |
A specific file location to store the vulnerability report |
Use the output-format
and vuln-output-format
parameters to choose the SBOM and vulnerability report formats:
- uses: scality/[email protected]
with:
target: ./artifacts
output-format: cyclonedx-json # SBOM format
vuln: true # Enable vulnerability scanning
vuln-output-format: html # Vulnerability report format
- uses: scality/[email protected]
with:
target: myimage.tar
target-type: image
For images (like those built using Oras) that use custom mediatypes not supported by Skopeo:
- uses: scality/[email protected]
with:
target: ./images
target-type: image
exclude-mediatypes: "application/my-configuration+json,text/nginx-config"
- uses: scality/[email protected]
with:
target: ./
vuln: true
name: "Generate SBOM"
on:
workflow_dispatch:
workflow_call:
jobs:
generate-sbom:
runs-on: ubuntu-22.04
env:
BASE_PATH: ${{ github.workspace }}/workdir
SBOM_PATH: ${{ github.workspace }}/artifacts/sbom
steps:
- name: Create directories
shell: bash
run: |
mkdir -p ${{ env.BASE_PATH }}/repo
mkdir -p ${{ env.BASE_PATH }}/iso
mkdir -p ${{ env.SBOM_PATH }}
- name: Checkout repo for scanning
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
path: ${{ env.BASE_PATH }}/repo/myrepo
- name: Generate SBOM for repository
uses: scality/[email protected]
with:
target: ${{ env.BASE_PATH }}/repo/myrepo
target-type: file
output-dir: ${{ env.SBOM_PATH }}
- name: Download artifacts
shell: bash
env:
ARTIFACTS_URL: ${{ steps.artifacts.outputs.link }}
ARTIFACTS_USER: ${{ secrets.ARTIFACTS_USER }}
ARTIFACTS_PASSWORD: ${{ secrets.ARTIFACTS_PASSWORD }}
run: |
echo "Downloading my.iso from $ARTIFACTS_URL"
curl -sSfL -o ${{ env.BASE_PATH }}/iso/my.iso -u $ARTIFACTS_USER:$ARTIFACTS_PASSWORD $ARTIFACTS_URL/my.iso
- name: Generate SBOM for ISO
uses: scality/[email protected]
with:
target: ${{ env.BASE_PATH }}/iso/my.iso
target-type: iso
version: "1.0.0"
output-dir: ${{ env.SBOM_PATH }}
vuln: true
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: sbom-files
path: ${{ env.SBOM_PATH }}/*.json
In the generated SBOM files, you will find CycloneDX metadata. Examples include:
- For container images:
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:984d102d-0992-4dae-be80-ba551bc2079a",
"version": 1,
"metadata": {
"timestamp": "2024-05-07T09:43:34Z",
"tools": {
"components": [
{
"type": "application",
"author": "anchore",
"name": "syft",
"version": "1.21.0"
}
]
},
"component": {
"bom-ref": "1b58496ca93cc57d",
"type": "container",
"name": "my.iso:alpine",
"version": "1.1.1"
}
}
}
- For ISO files:
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:db2bc22b-a5e5-49a9-9d02-61a18480ead4",
"version": 1,
"metadata": {
"timestamp": "2024-05-07T09:41:46Z",
"tools": {
"components": [
{
"type": "application",
"author": "anchore",
"name": "syft",
"version": "1.21.0"
}
]
},
"component": {
"bom-ref": "4a057776eee09e2f",
"type": "file",
"name": "my.iso",
"version": "1.0.0"
}
}
}
HTML template for Grype vulnerability reports was modified from Grype Contrib.
flowchart TD
subgraph inputs["Input Sources"]
ENV["Environment Variables<br>INPUT_TARGET, INPUT_TARGET_TYPE, etc."]
end
subgraph core["Core Components"]
CONFIG["config/inputs.py<br>get_inputs()"]
MAIN["main.py<br>scan()"]
PROVIDER_FACTORY["providers/base.py<br>get_provider()"]
end
subgraph providers["Providers Layer"]
BASE_PROVIDER["providers/base.py<br>BaseProvider"]
FILE_PROVIDER["providers/file.py<br>FileProvider"]
ISO_PROVIDER["providers/iso.py<br>IsoProvider"]
IMAGE_PROVIDER["providers/image.py<br>ImageProvider"]
end
subgraph tools["External Tools"]
SYFT_TOOL["Syft CLI"]
GRYPE_TOOL["Grype CLI"]
end
subgraph outputs["Output Files"]
SBOM["SBOM File<br>/tmp/sbom/sbom_name_version.json"]
VULN["Vulnerability Report<br>/tmp/sbom/vuln_name_version.json"]
end
ENV --> CONFIG
CONFIG --> MAIN
MAIN --> PROVIDER_FACTORY
PROVIDER_FACTORY --> BASE_PROVIDER
PROVIDER_FACTORY -- "target-type: file" --> FILE_PROVIDER
PROVIDER_FACTORY -- "target-type: iso" --> ISO_PROVIDER
PROVIDER_FACTORY -- "target-type: image" --> IMAGE_PROVIDER
BASE_PROVIDER -- "sbom()" --> SYFT_TOOL
BASE_PROVIDER -- "vuln()" --> GRYPE_TOOL
SYFT_TOOL --> SBOM
GRYPE_TOOL --> VULN
SBOM -- "Input for vulnerability scan" --> GRYPE_TOOL
flowchart TD
subgraph inputs["Image Input Types"]
REMOTE["Remote Image Reference<br>docker.io/library/alpine:latest"]
TARBALL["Image Tarball<br>myimage.tar"]
DIR["Directory of Images<br>/path/to/images/"]
end
subgraph detect["Detection & Preprocessing"]
DISCOVER["_discover_images()"]
MEDIATYPE["check_mediatype()"]
end
subgraph convert["Image Conversion"]
CONVERT["convert_image_to_oci()"]
SKOPEO["skopeo copy"]
end
subgraph process["Processing Path"]
SINGLE["_scan_single_image()"]
MULTI["_scan_image_directory()"]
VULN_SINGLE["_scan_single_vuln()"]
VULN_MULTI["_scan_multiple_vulns()"]
end
subgraph outputs["Output Files"]
SINGLE_SBOM["Single SBOM<br>/tmp/sbom/image_name_version.json"]
MULTI_SBOMS["Multiple SBOMs<br>/tmp/sbom/image_name_version.json"]
SINGLE_VULN["Single Vuln Report<br>/tmp/sbom/image_name_version_vuln.json"]
MULTI_VULNS["Multiple Vuln Reports<br>/tmp/sbom/image_name_version_vuln.json"]
end
REMOTE --> SINGLE
TARBALL --> SINGLE
DIR --> MULTI
MULTI --> DISCOVER
DISCOVER --> MEDIATYPE
MEDIATYPE -- "Not excluded" --> CONVERT
CONVERT --> SKOPEO
SINGLE --> SYFT_TOOL
SKOPEO --> SYFT_TOOL
SYFT_TOOL --> SINGLE_SBOM
SYFT_TOOL --> MULTI_SBOMS
SINGLE_SBOM -- "If vuln=true" --> VULN_SINGLE
MULTI_SBOMS -- "If vuln=true" --> VULN_MULTI
VULN_SINGLE --> GRYPE_TOOL
VULN_MULTI --> GRYPE_TOOL
GRYPE_TOOL --> SINGLE_VULN
GRYPE_TOOL --> MULTI_VULNS
- Environment variables (from GitHub Actions) are read by
config/inputs.py
. - CLI arguments are processed by Click in
main.py
. - The input sources are combined with CLI arguments taking precedence.
- The target type is determined from the provided inputs.
- The
get_provider()
factory function selects the appropriate provider. - The provider’s
sbom()
method invokes Syft to generate the SBOM, which is saved with a standardized naming scheme:{target_type}_{name}_{version}.json
.
- If
vuln
is enabled, the provider’svuln()
method uses Grype to scan the SBOM. - Grype generates a vulnerability report saved as:
{target_type}_{name}_{version}_vuln.json
.