Skip to main content

Goal

Run a SCANOSS scan on every pull request. Fail the check when the change contains open source code that your project’s scanoss.json does not approve yet, and keep the full result as a build artefact.

When to use it

  • You want to catch copied or vendored open source code before anyone merges it.
  • You already have a CI system and want SCANOSS as one more check in it.
  • You approve components through code review of scanoss.json (see Approve components with scanoss.json).
To also fail on licences or vulnerabilities, add the licence and vulnerability gate to the same job.

Prerequisites

  • A Linux x86-64 CI runner with bash, git, curl, jq, and sha256sum
  • SCANOSS_API_KEY available to the job as a secret environment variable
  • The pull request’s target branch available in the checkout, with enough history to find the merge base (a full clone is simplest)
  • scanoss/unapproved.jq committed to the repository (see Approve components)

The script

Save this as ci/scanoss-pr.sh in your repository and make it executable (chmod +x ci/scanoss-pr.sh).

What each step does

  1. Install. Downloads a pinned scanoss-cli release, checks it against the published checksums.txt, and puts it on the PATH. It goes into a temporary directory so a full scan does not fingerprint the binary.
  2. Choose the target. In changed mode, git diff lists the files the pull request adds or modifies compared to the merge base with BASE_REF. git checkout-index copies exactly those files into a temporary directory with the same relative paths. The script leaves out deleted files, because they cannot bring in new code. In full mode the script scans the whole repository, which suits small repositories or a nightly job.
  3. Settings. When the script scans the changed-files directory, the CLI cannot find the repository’s scanoss.json by itself, so the script passes it with --settings. Its BOM rules (approved components) and skip rules then apply. Because the copied files keep their relative paths, rules scoped to a path still match.
  4. Scan. The CLI fingerprints the files locally, uploads only the fingerprints, waits for the result, and writes the raw inventory to scanoss-results.json. --include licenses,vulns adds licence and vulnerability data, so the same file can feed the licence and vulnerability gate.
  5. Gate. The unapproved.jq filter lists every component detected in the code that scanoss.json doesn’t approve. When a rule pins a version, it approves only that version. Any line in the list fails the check.

What the output means

A failing run prints one line per unapproved component:
For each line, the reviewer decides:
  • The component is acceptable. Add it to bom.identify in scanoss.json in the same pull request, so reviewers see the change to scanoss.json in code review.
  • The match is not real, for example your own code that resembles a public project. Add a bom.ignore rule for that component, scoped to the path if you can.
  • The code should not be there. Remove it from the pull request.
See Approve components with scanoss.json for each rule.

How it fails the pipeline

scanoss-cli itself always exits 0 after a successful scan, whatever it finds. The jq gate in step 5 turns findings into a failure.

Run it in your CI system

All the logic is in the script. Your CI configuration only needs to check out the code with history, expose the secret, set BASE_REF, call the script, and keep scanoss-results.json. These two examples show the pattern. The same steps work in any CI system.

Variations

  • Scan the whole repository on a schedule. Run the same script with SCAN_MODE=full from a nightly job. A full scan also re-checks files that did not change, for example after you remove an approval from scanoss.json.
  • Use the container image instead of downloading the binary. Replace step 1 with docker run --rm -v "$PWD:/src" -e SCANOSS_API_KEY ghcr.io/scanoss/scanoss:<version> scan /src .... See Using Docker for the output-permission caveat.
  • Speed up large scans. Raise --threads, the number of fingerprint workers (default 10).