Skip to content

Commit

Permalink
feat(BUILD-1563): SBOM action and reusable workflow (#2)
Browse files Browse the repository at this point in the history
* feat(BUILD-1563): SBOM action and reusable workflow

renovate config

Reviewed-by malena-ebert-sonarsource, drautureau-sonarsource
  • Loading branch information
julien-carsique-sonarsource authored May 9, 2022
1 parent dd86254 commit 725910c
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>SonarSource/renovate-config:re-team"
]
}
37 changes: 37 additions & 0 deletions .github/workflows/test-action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test action
on:
pull_request:
push:
branches: [ '**' ]
release:
types:
- created
workflow_dispatch:

jobs:
test-action:
name: Test SonarSource/gh-action_sbom on alpine:latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
path: ./
- uses: ./ # SonarSource/gh-action_sbom
with:
image: alpine:latest
filename: test-action-bom.json
upload-artifact: true
upload-release-assets: true
env:
GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }}
GPG_PRIVATE_KEY_BASE64: ${{ secrets.GPG_PRIVATE_KEY_BASE64 }}
- run: |
test -f test-action-bom.json.asc
echo "test-action-bom.json:"
head test-action-bom.json
- uses: ./ # SonarSource/gh-action_sbom
with:
image: alpine:latest
filename: test2-action-bom.json
upload-artifact: false
upload-release-assets: false
30 changes: 30 additions & 0 deletions .github/workflows/test-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Test reusable workflow
on:
pull_request:
push:
branches: [ '**' ]
release:
types:
- created
workflow_dispatch:

jobs:
test-workflow:
name: Test SonarSource/gh-action_sbom on alpine:latest
uses: ./.github/workflows/workflow.yml
with:
image: alpine:latest
filename: test-workflow-bom.json
upload-artifact: true
upload-release-assets: true
secrets:
GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }}
GPG_PRIVATE_KEY_BASE64: ${{ secrets.GPG_PRIVATE_KEY_BASE64 }}
test2-workflow:
name: Test SonarSource/gh-action_sbom on alpine:latest
uses: ./.github/workflows/workflow.yml
with:
image: alpine:latest
filename: test-workflow-bom.json
upload-artifact: false
upload-release-assets: false
84 changes: 84 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: "CycloneDX SBOM reusable workflow"

on:
workflow_call:
inputs:
image:
required: true
type: string
description: "The Docker image to scan."
# description: "Newline-delimited list of Docker images to scan"
default: "example/image_name:tag"
filename:
required: true
type: string
description: "The generated SBOM file name"
# description: "Newline-delimited list of generated SBOM file names"
default: "bom.json"
upload-artifact:
required: false
type: boolean
description: "Attach the SBOM to the workflow"
default: true
upload-release-assets:
required: false
type: boolean
description: "Attach the SBOM to the release"
default: true
syft-version:
required: false
type: string
description: "Syft version"
default: v0.45.1
secrets:
GPG_PRIVATE_KEY_PASSPHRASE:
required: false
description: "Required when 'upload-artifact' is true"
GPG_PRIVATE_KEY_BASE64:
required: false
description: "Required when 'upload-artifact' is true"

jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: anchore/[email protected]
with:
image: ${{ inputs.image }}
artifact-name: ${{ inputs.filename }}
output-file: ${{ inputs.filename }}
format: cyclonedx-json
syft-version: ${{ inputs.syft-version }}
upload-artifact: ${{ inputs.upload-artifact }}
upload-release-assets: ${{ inputs.upload-release-assets }}
env:
SYFT_QUIET: true
- name: Sign CycloneDX SBOM
if: inputs.upload-artifact
shell: bash
env:
GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }}
GPG_PRIVATE_KEY_BASE64: ${{ secrets.GPG_PRIVATE_KEY_BASE64 }}
run: |
if [[ ! -v GPG_PRIVATE_KEY_PASSPHRASE || ! -v GPG_PRIVATE_KEY_BASE64 ]]; then exit 0; fi
export GNUPGHOMEDIR=gnupg/
mkdir --parent "${GNUPGHOMEDIR}"
echo "${GPG_PRIVATE_KEY_PASSPHRASE}" | gpg --batch --pinentry-mode loopback --passphrase-fd 0 --quiet --import <(echo "${GPG_PRIVATE_KEY_BASE64}")
echo "${GPG_PRIVATE_KEY_PASSPHRASE}" | gpg --batch --pinentry-mode loopback --passphrase-fd 0 --quiet --detach-sign --armor "${{ inputs.filename }}"
rm -rf "${GNUPGHOMEDIR}"
- uses: actions/upload-artifact@v3
if: inputs.upload-artifact
with:
name: "${{ inputs.filename }}"
path: |
${{ inputs.filename }}
${{ inputs.filename }}.asc
- name: Upload binaries to release
if: inputs.upload-release-assets && startsWith(github.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ github.token }}
file_glob: true
file: "${{ inputs.filename }}?(.asc)"
tag: ${{ github.ref }}
overwrite: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vscode
.idea/
*.iml
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
repos:
- repo: meta
hooks:
- id: check-hooks-apply
- id: check-useless-excludes
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: trailing-whitespace
args: [ --markdown-linebreak-ext=md ]
- id: end-of-file-fixer
- id: check-added-large-files
- id: check-yaml
- id: check-json
- id: pretty-format-json
args: [--autofix, --indent, "4", --no-sort-keys]
files: ^.github/renovate.json
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.14.0
hooks:
- id: check-renovate
128 changes: 127 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,128 @@
# gh-action_sbom
GitHub Action for SBOM generation

Action and reusable workflow for Docker SBOM generation from GitHub workflows.

The generation of Software Bill of Material (SBOM) is implemented with [Syft](https://github.com/anchore/syft)
and [sbom-action](https://github.com/anchore/sbom-action).

The SBOM files are signed and eventually attached to the workflow and to the release.

## Usage

The BOM file is signed if the `upload-artifact` parameter is true and the GPG secrets are provided.

### GitHub Action

```yaml
jobs:
job-calling-action:
steps:
- uses: SonarSource/gh-action_sbom@v1
with:
image: example/image_name:tag
filename: bom.json
upload-artifact: true
upload-release-assets: true
env:
GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }}
GPG_PRIVATE_KEY_BASE64: ${{ secrets.GPG_PRIVATE_KEY_BASE64 }}
```
### GitHub Reusable Workflow
:warning: The strategy property is not supported in any job that calls a reusable workflow.
See https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations
```yaml
jobs:
job-calling-workflow:
uses: SonarSource/gh-action_sbom/.github/workflows/workflow.yml@v1
with:
image: example/image_name:tag
filename: bom.json
upload-artifact: true
upload-release-assets: true
secrets:
GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }}
GPG_PRIVATE_KEY_BASE64: ${{ secrets.GPG_PRIVATE_KEY_BASE64 }}
```
## Versioning
Using the versioned semantic [tags](#Tags) is recommended for security and reliability.
See [GitHub: Using tags for release management](https://docs.github.com/en/actions/creating-actions/about-custom-actions#using-tags-for-release-management)
and [GitHub: Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/keeping-your-actions-up-to-date-with-dependabot)
.
For convenience, it is possible to use the [branches](#Branches) following the major releases.
### Tags
This repository is released following semantic versioning,
ie: [`1.0.0`](https://github.com/SonarSource/gh-action_sbom/releases/tag/1.0.0).

```yaml
jobs:
job-calling-workflow:
uses: SonarSource/gh-action_sbom/.github/workflows/[email protected]
job-calling-action:
steps:
- uses: SonarSource/[email protected]
```

### Branches

The `master` branch shall not be referenced by end-users.

Branches prefixed with a `v` are pointers to the last major versions, ie: [`v1`](https://github.com/SonarSource/gh-action_sbom/tree/v1).

```yaml
jobs:
job-calling-workflow:
uses: SonarSource/gh-action_sbom/.github/workflows/workflow.yml@v1
job-calling-action:
steps:
- uses: SonarSource/gh-action_sbom@v1
```

Note: use only branches with precaution and confidence in the provider.

## Development

The development is done on `master` and the `branch-*` maintenance branches.

### Release

Create a release from a maintained branches, then update the `v*` shortcut:

```shell
git fetch --tags
git update-ref -m "reset: update branch v1 to tag 1.0.0" refs/heads/v1 1.0.0
git push origin v1
```

## FAQ

### Warning Unexpected input

> ```
> Warning: Unexpected input(s) 'upload-artifact', 'upload-release-assets',
> valid inputs are ['path', 'image', 'registry-username', 'registry-password', 'format', 'github-token', 'artifact-name', 'output-file', 'syft-version', 'dependency-snapshot']
> ```

The warning can be ignored, see anchore/sbom-action#269

## References

[Xtranet/RE/Artifact Management#GitHub Actions](https://xtranet-sonarsource.atlassian.net/wiki/spaces/RE/pages/872153170/Artifact+Management#GitHub-Actions)

[Semantic Versioning 2.0.0](https://semver.org/)

[GitHub: About Custom Actions](https://docs.github.com/en/actions/creating-actions/about-custom-actions)

[Syft](https://github.com/anchore/syft)

[Syft GitHub Action for SBOM Generation](https://github.com/anchore/sbom-action)
66 changes: 66 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: "CycloneDX SBOM action"
description: "Generate CycloneDX SBOM with Syft"

inputs:
image:
required: true
description: "The Docker image to scan."
# description: "Newline-delimited list of Docker images to scan"
default: "example/image_name:tag"
filename:
required: true
description: "The generated SBOM file name"
# description: "Newline-delimited list of generated SBOM file names"
default: "bom.json"
upload-artifact:
required: false
description: "Attach the SBOM to the workflow"
default: true
upload-release-assets:
required: false
description: "Attach the SBOM to the release"
default: true
syft-version:
required: false
description: "Syft version"
default: v0.45.1

runs:
using: 'composite'
steps:
- uses: anchore/[email protected]
with:
image: ${{ inputs.image }}
artifact-name: ${{ inputs.filename }}
output-file: ${{ inputs.filename }}
format: cyclonedx-json
syft-version: ${{ inputs.syft-version }}
upload-artifact: ${{ inputs.upload-artifact }}
upload-release-assets: ${{ inputs.upload-release-assets }}
env:
SYFT_QUIET: true
- name: Sign CycloneDX SBOM
if: inputs.upload-artifact == 'true' && env.GPG_PRIVATE_KEY_PASSPHRASE && env.GPG_PRIVATE_KEY_BASE64
shell: bash
run: |
export GNUPGHOMEDIR=gnupg/
mkdir --parent "${GNUPGHOMEDIR}"
echo "${GPG_PRIVATE_KEY_PASSPHRASE}" | gpg --batch --pinentry-mode loopback --passphrase-fd 0 --quiet --import <(echo "${GPG_PRIVATE_KEY_BASE64}")
echo "${GPG_PRIVATE_KEY_PASSPHRASE}" | gpg --batch --pinentry-mode loopback --passphrase-fd 0 --quiet --detach-sign --armor "${{ inputs.filename }}"
rm -rf "${GNUPGHOMEDIR}"
- uses: actions/upload-artifact@v3
if: inputs.upload-artifact == 'true'
with:
name: "${{ inputs.filename }}"
path: |
${{ inputs.filename }}
${{ inputs.filename }}.asc
- name: Upload binaries to release
if: inputs.upload-release-assets == 'true' && startsWith(github.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ github.token }}
file_glob: true
file: "${{ inputs.filename }}?(.asc)"
tag: ${{ github.ref }}
overwrite: true

0 comments on commit 725910c

Please sign in to comment.