{"id":19281766,"url":"https://github.com/scality/sbom","last_synced_at":"2026-03-19T09:04:13.787Z","repository":{"id":228857450,"uuid":"720168521","full_name":"scality/sbom","owner":"scality","description":null,"archived":false,"fork":false,"pushed_at":"2024-04-15T11:54:46.000Z","size":20,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":21,"default_branch":"main","last_synced_at":"2024-04-15T15:06:39.805Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scality.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-11-17T18:19:03.000Z","updated_at":"2024-04-22T10:38:36.714Z","dependencies_parsed_at":"2024-04-04T19:52:06.096Z","dependency_job_id":"7422899b-5af1-446e-8335-9ff552a30e04","html_url":"https://github.com/scality/sbom","commit_stats":null,"previous_names":["scality/sbom"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fsbom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fsbom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fsbom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scality%2Fsbom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scality","download_url":"https://codeload.github.com/scality/sbom/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240386076,"owners_count":19793123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T21:24:06.311Z","updated_at":"2026-03-19T09:04:13.771Z","avatar_url":"https://github.com/scality.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Generate SBOM GitHub Action\n\n[![GitHub release](https://img.shields.io/github/release/scality/sbom.svg)](https://github.com/scality/sbom/releases/latest)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/scality/sbom/blob/main/LICENSE)\n\nA GitHub Action for creating a software bill of materials (SBOM)\nusing [Syft](https://github.com/anchore/syft), with optional vulnerability scanning using [Grype](https://github.com/anchore/grype).\n\n## Basic Usage\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: /usr/local/bin\n```\n\nThis will create SBOM result file like `/tmp/sbom/bin_sbom.json` in the default output directory `/tmp/sbom`. \n\n## Configuration\n\n### scality/sbom\n\nThe main [SBOM action](action.yaml) is responsible for generating SBOMs.\n\n| Parameter            | Description                                                                                 | Default      |\n| -------------------- | ------------------------------------------------------------------------------------------- | ------------ |\n| `grype_version`      | Grype version to use                                                                        | `0.108.0`     |\n| `syft_version`       | Syft version to use                                                                         | `1.42.0`     |\n| `target`             | The target to scan (path or image)                                                          | `./`         |\n| `target_type`        | Type of target to scan (file, directory, image, iso)                                        | `file`       |\n| `output_format`      | Format of the generated SBOM \u003cbr\u003e (cyclonedx-json cyclonedx-xml github-json spdx-json spdx-tag-value syft-json syft-table syft-text template) | `cyclonedx-json` |\n| `output_file`        | A specific file location to store the SBOM                                                  |              |\n| `output_dir`         | Directory to store generated SBOM files                                                     | `/tmp/sbom`  |\n| `exclude_mediatypes` | Media types to exclude for images (comma-separated)                                         |              |\n| `distro`             | Linux distribution of the target (if not auto-detected)                                     |              |\n| `name`               | Override the detected name of the target                                                    |              |\n| `version`            | Override the detected version of the target                                                 |              |\n| `merge`              | Merge multiple SBOMs into a single file                                                     | `false`      |\n| `merge_hierarchical` | Merge multiple SBOMs into a single hierarchical file                                        | `false`      |\n| `vuln`               | Enable vulnerability scanning                                                               | `false`      |\n| `vuln_output_format` | Format for the vulnerability report when `vuln` is enabled\u003cbr\u003e(supports `json`, `html`, `csv`, `table`, or comma-separated values like `html,json`) | `cyclonedx-json`|\n| `vuln_output_file`   | A specific file location to store the vulnerability report                                  |              |\n\n## Example Usage\n\n### Scan with a specific format\n\nUse the `output_format` and `vuln_output_format` parameters to choose the SBOM and vulnerability report formats:\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: ./artifacts\n    output_format: cyclonedx-json  # SBOM format\n    vuln: true                     # Enable vulnerability scanning\n    vuln_output_format: html       # Generate HTML vulnerability report\n```\n\nThe HTML format provides an interactive report with a dynamic table for better visualization of vulnerabilities, allowing for easier filtering and sorting.\n\n### Multiple vulnerability report formats\n\nYou can generate multiple formats simultaneously by using comma-separated values:\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: ./artifacts\n    vuln: true\n    vuln_output_format: html,json  # Generate both HTML and JSON reports\n```\n\n### Specify target type explicitly\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: myimage.tar\n    target_type: image\n```\n\n### Scan image directly from a registry\n\nYou can scan container images directly from a registry without downloading them first:\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: docker.io/alpine:3.14\n    target_type: image\n```\n\n### Exclude mediatypes for container images\n\nFor images (like those built using Oras) that use custom mediatypes not supported by Skopeo:\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: ./images\n    target_type: image\n    exclude_mediatypes: \"application/my-configuration+json,text/nginx-config\"\n```\n\n### Enable vulnerability scanning\n\n```yaml\n- uses: scality/sbom@v2\n  with:\n    target: ./\n    vuln: true\n```\n\n### Full Example\n\n```yaml\nname: \"Generate SBOM\"\non:\n  workflow_dispatch:\n  workflow_call:\njobs:\n  generate-sbom:\n    runs-on: ubuntu-22.04\n    env:\n      BASE_PATH: ${{ github.workspace }}/workdir\n      SBOM_PATH: ${{ github.workspace }}/artifacts/sbom\n    steps:\n      - name: Create directories\n        shell: bash\n        run: |\n          mkdir -p ${{ env.BASE_PATH }}/repo\n          mkdir -p ${{ env.BASE_PATH }}/iso\n          mkdir -p ${{ env.SBOM_PATH }}\n          \n      - name: Checkout repo for scanning\n        uses: actions/checkout@v4  \n        with:  \n          fetch-depth: 0  \n          fetch-tags: true\n          path: ${{ env.BASE_PATH }}/repo/myrepo\n          \n      - name: Generate SBOM for repository\n        uses: scality/sbom@v2\n        with:\n          target: ${{ env.BASE_PATH }}/repo/myrepo\n          target_type: file\n          output_dir: ${{ env.SBOM_PATH }}\n          \n      - name: Download artifacts\n        shell: bash\n        env:\n          ARTIFACTS_URL: ${{ steps.artifacts.outputs.link }}\n          ARTIFACTS_USER: ${{ secrets.ARTIFACTS_USER }}\n          ARTIFACTS_PASSWORD: ${{ secrets.ARTIFACTS_PASSWORD }}\n        run: |\n          echo \"Downloading my.iso from $ARTIFACTS_URL\"\n          curl -sSfL -o ${{ env.BASE_PATH }}/iso/my.iso -u $ARTIFACTS_USER:$ARTIFACTS_PASSWORD $ARTIFACTS_URL/my.iso\n          \n      - name: Generate SBOM for ISO\n        uses: scality/sbom@v2\n        with:\n          target: ${{ env.BASE_PATH }}/iso/my.iso\n          target_type: iso\n          version: \"1.0.0\"\n          output_dir: ${{ env.SBOM_PATH }}\n          vuln: true\n          vuln_output_format: html\n          merge: true\n          merge_hierarchical: true\n          \n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: sbom-files\n          path: ${{ env.SBOM_PATH }}/*.json\n```\n\n## CLI Usage\n\nThe SBOM tool can also be run directly from the command line using environment variables for configuration.\n\n### Install dependencies\n\n```bash\npip install -r requirements.txt\n```\n\n### Basic CLI commands\n\n```bash\n# Install the required scanners (syft, grype)\npython3 ./src/main.py install\n\n# Scan a target\npython3 ./src/main.py scan\n```\n\n### CLI Examples\n\nScan a local directory:\n\n```bash\nINPUT_OUTPUT_DIR=/tmp/sbom INPUT_TARGET_TYPE=file INPUT_TARGET=./src \\\n  python3 ./src/main.py scan\n```\n\nScan a container image from a registry:\n\n```bash\nINPUT_OUTPUT_DIR=/tmp/sbom INPUT_TARGET_TYPE=image INPUT_TARGET=docker.io/alpine:3.14 \\\n  python3 ./src/main.py scan\n```\n\nScan an image tarball:\n\n```bash\nINPUT_OUTPUT_DIR=/tmp/sbom INPUT_TARGET_TYPE=image INPUT_TARGET=./myimage.tar \\\n  python3 ./src/main.py scan\n```\n\nScan with vulnerability analysis:\n\n```bash\nINPUT_OUTPUT_DIR=/tmp/sbom INPUT_TARGET_TYPE=file INPUT_TARGET=./src INPUT_VULN=true \\\n  python3 ./src/main.py scan\n```\n\nGenerate HTML vulnerability report:\n\n```bash\nINPUT_OUTPUT_DIR=/tmp/sbom INPUT_TARGET_TYPE=image INPUT_TARGET=docker.io/alpine:3.14 \\\n  INPUT_VULN=true INPUT_VULN_OUTPUT_FORMAT=html \\\n  python3 ./src/main.py scan\n```\n\nScan an ISO file with merging enabled:\n\n```bash\nINPUT_OUTPUT_DIR=/tmp/sbom INPUT_TARGET_TYPE=iso INPUT_TARGET=./my.iso \\\n  INPUT_VERSION=1.0.0 INPUT_MERGE=true INPUT_VULN=true \\\n  python3 ./src/main.py scan\n```\n\n## CycloneDX Metadata\n\nIn the generated SBOM files, you will find CycloneDX metadata. Examples include:\n\n- **For container images:**\n\n```json\n{\n    \"$schema\": \"http://cyclonedx.org/schema/bom-1.6.schema.json\",\n    \"bomFormat\": \"CycloneDX\",\n    \"specVersion\": \"1.6\",\n    \"serialNumber\": \"urn:uuid:984d102d-0992-4dae-be80-ba551bc2079a\",\n    \"version\": 1,\n    \"metadata\": {\n        \"timestamp\": \"2024-05-07T09:43:34Z\",\n        \"tools\": {\n            \"components\": [\n                {\n                    \"type\": \"application\",\n                    \"author\": \"anchore\",\n                    \"name\": \"syft\",\n                    \"version\": \"1.42.0\"\n                }\n            ]\n        },\n        \"component\": {\n            \"bom-ref\": \"1b58496ca93cc57d\",\n            \"type\": \"container\",\n            \"name\": \"my.iso:alpine\",\n            \"version\": \"1.1.1\"\n        }\n    }\n}\n```\n\n- **For ISO files:**\n\n```json\n{\n    \"$schema\": \"http://cyclonedx.org/schema/bom-1.6.schema.json\",\n    \"bomFormat\": \"CycloneDX\",\n    \"specVersion\": \"1.6\",\n    \"serialNumber\": \"urn:uuid:db2bc22b-a5e5-49a9-9d02-61a18480ead4\",\n    \"version\": 1,\n    \"metadata\": {\n        \"timestamp\": \"2024-05-07T09:41:46Z\",\n        \"tools\": {\n            \"components\": [\n                {\n                    \"type\": \"application\",\n                    \"author\": \"anchore\",\n                    \"name\": \"syft\",\n                    \"version\": \"1.42.0\"\n                }\n            ]\n        },\n        \"component\": {\n            \"bom-ref\": \"4a057776eee09e2f\",\n            \"type\": \"file\",\n            \"name\": \"my.iso\",\n            \"version\": \"1.0.0\"\n        }\n    }\n}\n```\n\n## Core Workflow\n\n```mermaid\nflowchart TD\n    subgraph inputs[\"Input Sources\"]\n        ENV[\"Environment Variables\u003cbr\u003eINPUT_TARGET, INPUT_TARGET_TYPE, etc.\"]\n    end\n\n    subgraph core[\"Core Components\"]\n        CONFIG[\"config/inputs.py\u003cbr\u003eget_inputs()\"]\n        MAIN[\"main.py\u003cbr\u003escan()\"]\n        PROVIDER_FACTORY[\"providers/base.py\u003cbr\u003eget_provider()\"]\n    end\n\n    subgraph providers[\"Providers Layer\"]\n        BASE_PROVIDER[\"providers/base.py\u003cbr\u003eBaseProvider\"]\n        FILE_PROVIDER[\"providers/file.py\u003cbr\u003eFileProvider\"]\n        ISO_PROVIDER[\"providers/iso.py\u003cbr\u003eIsoProvider\"]\n        IMAGE_PROVIDER[\"providers/image.py\u003cbr\u003eImageProvider\"]\n    end\n\n    subgraph tools[\"External Tools\"]\n        SYFT_TOOL[\"Syft CLI\"]\n        GRYPE_TOOL[\"Grype CLI\"]\n    end\n\n    subgraph outputs[\"Output Files\"]\n        SBOM[\"SBOM File\u003cbr\u003e/tmp/sbom/sbom_name_version.json\"]\n        VULN[\"Vulnerability Report\u003cbr\u003e/tmp/sbom/vuln_name_version.json\"]\n    end\n\n    ENV --\u003e CONFIG\n    CONFIG --\u003e MAIN\n    MAIN --\u003e PROVIDER_FACTORY\n    \n    PROVIDER_FACTORY --\u003e BASE_PROVIDER\n    PROVIDER_FACTORY -- \"target-type: file\" --\u003e FILE_PROVIDER\n    PROVIDER_FACTORY -- \"target-type: iso\" --\u003e ISO_PROVIDER\n    PROVIDER_FACTORY -- \"target-type: image\" --\u003e IMAGE_PROVIDER\n    \n    BASE_PROVIDER -- \"sbom()\" --\u003e SYFT_TOOL\n    BASE_PROVIDER -- \"vuln()\" --\u003e GRYPE_TOOL\n    \n    SYFT_TOOL --\u003e SBOM\n    GRYPE_TOOL --\u003e VULN\n    SBOM -- \"Input for vulnerability scan\" --\u003e GRYPE_TOOL\n```\n\n## Image Processing Workflow\n\n```mermaid\nflowchart TD\n    subgraph inputs[\"Image Input Types\"]\n        REMOTE[\"Remote Image Reference\u003cbr\u003edocker.io/library/alpine:latest\"]\n        TARBALL[\"Image Tarball\u003cbr\u003emyimage.tar\"]\n        DIR[\"Directory of Images\u003cbr\u003e/path/to/images/\"]\n    end\n    \n    subgraph detect[\"Detection \u0026 Preprocessing\"]\n        DISCOVER[\"_discover_images()\"]\n        MEDIATYPE[\"check_mediatype()\"]\n    end\n    \n    subgraph convert[\"Image Conversion\"]\n        CONVERT[\"convert_image_to_oci()\"]\n        SKOPEO[\"skopeo copy\"]\n    end\n    \n    subgraph process[\"Processing Path\"]\n        SINGLE[\"_scan_single_image()\"]\n        MULTI[\"_scan_image_directory()\"]\n        \n        VULN_SINGLE[\"_scan_single_vuln()\"]\n        VULN_MULTI[\"_scan_multiple_vulns()\"]\n    end\n    \n    subgraph outputs[\"Output Files\"]\n        SINGLE_SBOM[\"Single SBOM\u003cbr\u003e/tmp/sbom/image_name_version.json\"]\n        MULTI_SBOMS[\"Multiple SBOMs\u003cbr\u003e/tmp/sbom/image_name_version.json\"]\n        \n        SINGLE_VULN[\"Single Vuln Report\u003cbr\u003e/tmp/sbom/image_name_version_vuln.json\"]\n        MULTI_VULNS[\"Multiple Vuln Reports\u003cbr\u003e/tmp/sbom/image_name_version_vuln.json\"]\n    end\n    \n    REMOTE --\u003e SINGLE\n    TARBALL --\u003e SINGLE\n    DIR --\u003e MULTI\n    \n    MULTI --\u003e DISCOVER\n    DISCOVER --\u003e MEDIATYPE\n    \n    MEDIATYPE -- \"Not excluded\" --\u003e CONVERT\n    CONVERT --\u003e SKOPEO\n    \n    SINGLE --\u003e SYFT_TOOL\n    SKOPEO --\u003e SYFT_TOOL\n    \n    SYFT_TOOL --\u003e SINGLE_SBOM\n    SYFT_TOOL --\u003e MULTI_SBOMS\n    \n    SINGLE_SBOM -- \"If vuln=true\" --\u003e VULN_SINGLE\n    MULTI_SBOMS -- \"If vuln=true\" --\u003e VULN_MULTI\n    \n    VULN_SINGLE --\u003e GRYPE_TOOL\n    VULN_MULTI --\u003e GRYPE_TOOL\n    \n    GRYPE_TOOL --\u003e SINGLE_VULN\n    GRYPE_TOOL --\u003e MULTI_VULNS\n```\n\n## Data Flow Explanation\n\n### Input Processing\n\n1. Environment variables (from GitHub Actions) are read by `config/inputs.py`.\n2. CLI arguments are processed by Click in `main.py`.\n3. The input sources are combined with CLI arguments taking precedence.\n\n### Provider Selection \u0026 SBOM Generation\n\n1. The target type is determined from the provided inputs.\n2. The `get_provider()` factory function selects the appropriate provider.\n3. The provider’s `sbom()` method invokes Syft to generate the SBOM, which is saved with a standardized naming scheme: `{target_type}_{name}_{version}.json`.\n\n### Vulnerability Scanning (if enabled)\n\n1. If `vuln` is enabled, the provider’s `vuln()` method uses Grype to scan the SBOM.\n2. Grype generates a vulnerability report saved as: `{target_type}_{name}_{version}_vuln.json`.\n\n## Merge Explanation\n\nThe merge is per default not hierarchical for the `components` field of a `component`. This means that components that were contained in the `components` of an already present component will just be added as new components under the SBOMs’ `components` sections. The `--hierarchical` flag allows for hierarchical merges. This affects only the top level components of the merged SBOM. The structured of nested components is preserved in both cases (except the removal of already present components), as shown for *component 4* in the image below.\n\n```mermaid\nflowchart TD\n    subgraph \"SBOM 1\"\n        0[\"Component 0 \u003cbr\u003e metadata component\"]\n        01[\"Component 1\"]\n        02[\"Component 2\"]\n        \n        0 --\u003e|contains| 01\n        0 --\u003e|contains| 02\n    end\n```\n\n```mermaid\nflowchart TD\n    subgraph \"SBOM 2\"\n        1[\"Component 1 \u003cbr\u003e metadata component\"]\n        13[\"Component 3\"]\n        14[\"Component 4\"]\n        15[\"Component 5\"]\n        \n        1 --\u003e|contains| 13\n        1 --\u003e|contains| 14\n        14 --\u003e|contains| 15\n    end\n```\n\n### Default merge:\n\n```mermaid\nflowchart TD\n    subgraph \"Merged SBOM\"\n        0[\"Component 0 \u003cbr\u003e metadata component\"]\n        1[\"Component 1\"]\n        2[\"Component 2\"]\n        3[\"Component 3\"]\n        4[\"Component 4\"]\n        5[\"Component 5\"]\n        \n        0 --\u003e|contains| 1\n        0 --\u003e|contains| 2\n        0 --\u003e|contains| 3\n        0 --\u003e|contains| 4\n        4 --\u003e|contains| 5\n    end\n```\n\n### Hierarchical merge:\n\n```mermaid\nflowchart TD\n    subgraph \"Merged SBOM\"\n        0[\"Component 0 \u003cbr\u003e metadata component\"]\n        1[\"Component 1\"]\n        2[\"Component 2\"]\n        3[\"Component 3\"]\n        4[\"Component 4\"]\n        5[\"Component 5\"]\n        \n        0 --\u003e|contains| 1\n        0 --\u003e|contains| 2\n        1 --\u003e|contains| 3\n        1 --\u003e|contains| 4\n        4 --\u003e|contains| 5\n    end\n```\n\n### References\n- [CycloneDX Specification](https://cyclonedx.org/docs/1.6/json/)\n- [CycloneDX Merge](https://festo-se.github.io/cyclonedx-editor-validator/usage/merge.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscality%2Fsbom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscality%2Fsbom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscality%2Fsbom/lists"}