CI/CD
Recipes for a pipeline that already sends a scan: pick the right token, choose a gate, check pull requests, record releases, scan a monorepo, and send the results to other tools. For the first job, see Get started.
Pick a token for the pipeline #
Use the smallest token that does the job. If it leaks, less is exposed.
| Your pipeline | Who makes the token | What to select on Access tokens |
|---|---|---|
| The pipeline of one repository | An admin | The CI token (one repository) preset and the repository. The token can send and read that repository only. It expires in 90 days. |
| A CI server that builds many repositories of one team | An admin | Scope a department, the department, and the CI upload rights. A scan of a new repository puts the repository in that department. |
| Any pipeline, when you are not an admin | A member | The CI upload rights. The token acts as you: it stops when your user is disabled. |
- The Repository list shows only the repositories that the workspace has. For a new repository, send a first scan from your computer, or ask an admin to select Register repository on the Repositories page.
- To replace a token that expires: make the new token, change the CI secret, run the pipeline, then revoke the old token.
- The Last CLI column of Access tokens shows the CLI version that each token used last. Use it to find a job that runs an old CLI.
Access tokens explains each right and scope.
Choose the gate #
| Command | The job fails when | Use it on |
|---|---|---|
securechain sca | Never. It only reports. | Any branch, to keep the portal current. |
securechain sca --fail-on high | An open finding is high or critical. The other levels are critical, medium, and low. | The default branch and the release branches. |
securechain compare --base main --fail-on-new | The branch has an open finding that the latest scan of main does not have. | Pull requests and merge requests. See Pull and merge requests. |
securechain check | A project that uses TuxCare builds is not in the state that harden made. | Projects that use TuxCare builds. See Fix vulnerabilities. |
- Skip what your team ignored.
--baseline-portalmakesscaskip the findings that your team ignored in the portal. To turn it on for every run, setbaseline.portal: truein.securechain.yaml. - A portal that does not answer. The job then exits with code 4.
--on-portal-error warnmakes it go on with a warning. Use it when a build must never wait for the portal.
Pull and merge requests #
A pull request often inherits old findings from the base branch. compare --fail-on-new fails it only on the findings that it adds. The base branch needs a scan of its own, so scan the base branch on each push too.
GitHub Actions:
name: securechain
on:
push:
branches: [main]
pull_request:
jobs:
securechain:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan the branch
if: github.event_name == 'push'
uses: TuxCare/securechain-action@v0
with:
command: sca
args: --fail-on high
portal-token: ${{ secrets.SECURECHAIN_PORTAL_TOKEN }}
registry-login: none
- name: Fail on new findings
if: github.event_name == 'pull_request'
uses: TuxCare/securechain-action@v0
with:
command: compare
args: --base ${{ github.base_ref }} --fail-on-new
portal-token: ${{ secrets.SECURECHAIN_PORTAL_TOKEN }}
registry-login: none
GitLab CI:
securechain:
image: node:22
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- curl -fsSL https://securechain.tuxcare.com/get/securechain | sh
- securechain sca --fail-on high
securechain-new-findings:
image: node:22
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- curl -fsSL https://securechain.tuxcare.com/get/securechain | sh
- securechain compare --base "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" --fail-on-new
Jenkins (multibranch pipeline): CHANGE_TARGET is set for a pull request build.
sh '''
curl -fsSL https://securechain.tuxcare.com/get/securechain | sh
export PATH="$HOME/.local/bin:$PATH"
if [ -n "$CHANGE_TARGET" ]; then
securechain compare --base "$CHANGE_TARGET" --fail-on-new
else
securechain sca --fail-on high
fi
'''
The portal files the scan of a pull request under the branch that it comes from. The portal sends alerts for the default branch and the supported releases only, so a feature branch sends none.
Record a release #
A release is the list of packages of a build that you ship. The portal keeps it, checks it against each new advisory, and can export it as a VEX file. Record it from the job that builds the release:
securechain sca --release "$CI_COMMIT_TAG" # GitLab
securechain sca --release "$GITHUB_REF_NAME" # GitHub, on a tag push
securechain sca --release "$TAG_NAME" # Jenkins, with tag discovery on
- Scan the same commit and the same lockfile that you ship.
- An empty name stops the job with exit code 2. So a tag variable that is not set cannot make a release with no name.
- A release does not change. The same name from another commit stops the job with exit code 2.
--force-releasereplaces the release, and the portal records the replacement. --product-release "Acme 3.2"also adds the release to a product release. The repository must be in exactly one product, else the job stops with exit code 2.
With the GitHub action, pass the name in args, for example args: --release ${{ github.ref_name }}. The action gives args to the CLI as a list of words, with no shell. Use the action of release 0.1.19 or later.
More than one project in a repository #
- Run the CLI in each project directory, or give the directory with
--dir path/to/project. The portal keeps the scans of each directory and each set of package managers apart. It calls each one a slot. - The CLI finds the package managers by itself. To choose them, use
--ecosystem npm(repeat it for more), or setecosystemsin.securechain.yaml. - To leave a path out of the scan, for example test fixtures, add it to
ignorein.securechain.yaml. - The repository page of the portal shows a Slot list when a branch has two or more slots. The repository list counts the findings of all the slots.
Send the results to other tools #
| Format | Command | For |
|---|---|---|
| SARIF | securechain sca --output sarif > securechain.sarif | GitHub code scanning and other code-scanning tools. |
| JSON | securechain sca --output json > findings.json | Your own scripts. The JSON format is stable and has a schema_version. |
| Markdown | securechain sca --output markdown > findings.md | A comment on a pull request, or a job summary. |
| JUnit | securechain check --output junit > report.xml | The test report of GitLab and Jenkins. |
To upload the SARIF file to GitHub code scanning, add this step after the scan. The job needs the permission security-events: write:
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: securechain.sarif
To read the findings from a dashboard, use the REST API.
Pin the CLI version #
The install script installs the newest release. To run the same release in each job, pin it:
| Install method | How to pin |
|---|---|
| Install script | Set SECURECHAIN_VERSION=v0.1.22 before the script. |
| GitHub Action | Pin the action tag, or set the input version. |
| GitLab CI/CD component | Pin the component version, or set the input version. |
| Docker | Use a version tag, for example tuxcare/securechain:0.1.22, not latest. |
When the CLI is two or more minor releases behind, it prints a warning. When it is older than the portal accepts, it stops and tells you to upgrade. See Upgrade the CLI.
Exit codes in CI #
| Code | Meaning | What to do |
|---|---|---|
| 0 | The run finished. No finding failed the gate. | Nothing. |
| 1 | A finding failed the gate. | Read the findings in the job log or in the portal. |
| 2 | A wrong option, a configuration file that the CLI cannot read, or no package manager that it knows. | Read the message. |
| 3 | No token, or the portal refused it. | Check the secret variable. See Troubleshooting. |
| 4 | The portal did not answer in time. | Run the job again, or use --on-portal-error warn. |
Exit codes lists every code.
Next: Portal