Skip to main content

Software Composition Analysis (SCA)

What is SCA?

SCA is a method for analyzing open-source components included in software to detect known vulnerabilities (CVEs). It tracks the full dependency graph based on an SBOM and enables immediate response when new CVEs are discovered. What an SBOM is is covered in SBOM Basics.

The configuration below is an example — a fully working implementation lives in the reference repository

The YAML and commands on this page are examples that show the essentials. For a complete, copy-and-run pipeline (including policy files and a sample app), see the Best Practice repository.


SBOM Generation — syft

Basic Usage

Bash
# CycloneDX JSON generation (recommended)
syft . -o cyclonedx-json=sbom.cdx.json

# Generate SPDX JSON
syft . -o spdx-json=sbom.spdx.json

# Analyze a container image
syft nginx:latest -o cyclonedx-json=sbom.cdx.json

Format Selection

FormatStewardRecommended Use
CycloneDX JSONOWASPSecurity and vulnerability management (grype integration)
SPDX JSONLinux FoundationSupply chain sharing and regulatory response

For security pipeline-centric workflows, CycloneDX JSON is recommended.

cdxgen

An alternative generator, from the CycloneDX project itself. Where syft reads what is on disk, cdxgen resolves through each language's own package manager, so it can recover dependencies a file-based scan does not see — which matters when a lockfile is absent or the tree is only fixed at build time. It covers more than 30 ecosystems and emits CycloneDX directly.

Bash
# CycloneDX JSON for the current project
npx @cyclonedx/cdxgen@latest -o sbom.cdx.json

TRUSCA uses cdxgen as its generator. Which one to choose comes down to whether your build resolves dependencies at install time (syft is enough) or at build time (cdxgen recovers what syft loses).


Vulnerability Scanning — grype

Full GitHub Actions Workflow

YAML
# .github/workflows/sca.yml

name: SCA — SBOM & Vulnerability Scan

on:
pull_request:
branches: [main, develop]

jobs:
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
format: cyclonedx-json
output-file: sbom.cdx.json

- name: Scan vulnerabilities
uses: anchore/scan-action@v7
with:
sbom: sbom.cdx.json
fail-build: true
severity-cutoff: high
config: .grype.yaml

- name: Upload SBOM artifact
uses: actions/upload-artifact@v7
with:
name: sbom-${{ github.sha }}
path: sbom.cdx.json
retention-days: 90

GitLab CI

YAML
# .gitlab-ci.yml (sca job section)

sca:
stage: test
image: ubuntu:22.04
script:
# The base ubuntu image does not include curl
- apt-get update -qq && apt-get install -y -qq curl ca-certificates
- curl -sSfL https://get.anchore.io/syft
| sh -s -- -b /usr/local/bin
- curl -sSfL https://get.anchore.io/grype
| sh -s -- -b /usr/local/bin
- syft . -o cyclonedx-json=sbom.cdx.json
- grype sbom:sbom.cdx.json --fail-on high --config .grype.yaml
artifacts:
paths:
- sbom.cdx.json
expire_in: 90 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
When grype reports sbom format not recognized

syft 1.51 and newer emit CycloneDX 1.7, which only grype 0.118 and newer can read. Upgrade both tools together, or generate at the older version with syft ... -o cyclonedx-json@1.6.

How this differs from the cicd-quick workflow

AI Coding — Quick CI/CD in 30 minutes carries a workflow of the same shape. If you already applied that one, adding the three items below turns it into what this page describes.

ItemQuick CI/CDThis page
Artifact retentionDefault (may be under 90 days)retention-days: 90 and expire_in 90d
Scan policy fileNone.grype.yaml for exception handling
Prohibited license checkShell script grepping the SBOMNone (licenses move to a policy gate)

Quick CI/CD exists to stand up a first gate in 30 minutes; this page goes on to exception handling and evidence retention. Keep one of the two workflows, not both.


Vulnerability Policy Design

Severity-Based SLA

The table below is an example of a hardened, organization-level SLA tuned for CI gates. For the KWG baseline (Critical 1 week, High 4 weeks) and the canonical response deadlines, see Vulnerability response deadlines and VEX.

SeverityCVSS RangeHardened SLA ExampleBuild Block
Critical9.0–10.024 hoursBlock
High7.0–8.97 daysBlock
Medium4.0–6.930 daysWarning only
Low0.1–3.9Next releaseIgnore

At initial adoption, it is recommended to block only Critical issues, then expand to High after the team is accustomed.

grype Policy File

Always record reason and approval date for ignore rules

For audit response, exceptions without evidence can become a compliance risk.

YAML
# .grype.yaml

fail-on-severity: high

ignore:
# not used in actual code paths — security team approved 2024-01-15
- vulnerability: CVE-2023-XXXXX
reason: 'confirmed function not used'
# test-only package
- package:
name: some-test-lib
type: npm

Trivy

An alternative scanner, from Aqua Security. It ships a single database that merges NVD, OSV, GHSA, EPSS and KEV, so exploit probability and known-exploited status arrive with the finding rather than from a second lookup. It also scans container images and IaC, not only SBOM files.

Bash
# Scan an SBOM you already have
trivy sbom sbom.cdx.json

# Or scan the project directly
trivy fs --scanners vuln,license .

TRUSCA uses Trivy as its matching engine, which is why its findings carry EPSS and KEV alongside CVSS.


Developer workstations belong in scanning scope

Everything above targeted repositories and container images. There is one more path code arrives through: the extensions a developer installs in an IDE. Extensions run with developer privileges and reach source and credentials, yet they never appear in package.json or a lockfile, so the SCA results above never show them.

  • GlassWorm spread through the Open VSX marketplace in 2025-10 with 35,800 installs and returned in a second wave in 2025-11. In 2026-03 a variant delivered through transitive dependencies affected 72 extensions, including ones impersonating Claude Code and Codex.
  • MaliciousCorgi posed as AI extensions on the VS Code marketplace and affected roughly 1.5 million developers in 2026-01.
  • Fifteen malicious AI plugins were confirmed on the JetBrains marketplace in 2026-06.

Three controls go together in practice: enforce an extension allowlist through organization policy, re-check the installed list on a schedule, and verify the publisher and linked repository before installing. The same controls extended to MCP servers and agent skills are in the tool and extension supply chain section of Agent and MCP Tool Governance.


The publish path and the install path are attack surfaces too

The scans above inspect packages that have already arrived. Attacks also happen on either side of that: the publish path, where a package is uploaded to a registry, and the install path, where a downloaded package runs scripts during installation.

Publishing: trusted publishing instead of long-lived tokens

If a maintainer's npm token leaks, malicious code ships as a new version of a legitimate package. Many of the npm supply chain attacks since the second half of 2025 took that route. npm has since tightened its token policy and retired classic tokens; the official documentation now states that only granular access tokens are supported (as of 2025-11).

The alternative is trusted publishing. The CI workflow proves its identity to the registry over OIDC and publishes with a short-lived credential issued on the spot, so no long-lived token has to sit in repository secrets. Published packages also carry a provenance attestation recording which workflow produced them, generated automatically without the --provenance flag. It currently works on the cloud runners of GitHub Actions, GitLab CI/CD and CircleCI; self-hosted runners are not supported yet. PyPI and RubyGems offer the same mechanism.

YAML
# .github/workflows/publish.yml - npm trusted publishing
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # required to mint the OIDC token
contents: read
steps:
- uses: actions/checkout@v7
- run: npm ci --ignore-scripts
- run: npm publish # published without NODE_AUTH_TOKEN

A provenance attestation only guarantees that the build came out of the declared pipeline. If the pipeline itself is compromised, a malicious build ships with the attestation still valid. Mini Shai-Hulud in 2026-05 is the case in point: 84 malicious packages were published carrying valid SLSA Build Level 3 attestations.

Installing: install hooks are arbitrary code execution

An npm package automatically runs its preinstall, install and postinstall scripts during installation. The Shai-Hulud lineage (2025-09 initial, 2025-11 2.0, 2026-05 Mini, 2026-08 CHAINDROP) and the postmark-mcp incident all used those hooks to get execution. A single npm install is someone else's code running on your machine.

  • In CI, install with npm ci --ignore-scripts and run scripts separately only for the packages whose build genuinely needs them.
  • pnpm already blocks them by default. List only the packages you reviewed and approved under allowBuilds (onlyBuiltDependencies before pnpm 11).
  • If you run an internal ingest gate, flag packages that carry install hooks for review. For how to build that gate, see section 3-1 of Open Source Process: From Use to Distribution.

MCP servers and agent skills installed by coding agents travel the same install path. The same controls extended to those tools are in the tool and extension supply chain section of Agent and MCP Tool Governance.


Using VEX

What is VEX (Vulnerability Exploitability eXchange)? It is a machine-readable document that specifies whether a particular CVE is actually exploitable in a given product. It formally expresses cases like "the CVE exists, but the affected code path is not used," reducing unnecessary alerts across downstream supply chains.

Practical use: Four formats are actually in use, CycloneDX VEX, OpenVEX, CSAF, and SPDX 3.0, and none has converged as the single standard. If a customer has not specified a format, starting with the same CycloneDX vulnerabilities field as your existing SBOM is the easiest to wire into your tooling. For the concepts, including the four status values, Vulnerability response deadlines and VEX is the canonical reference.


SBOM Retention Policy

Storage location: Store as CI/CD artifacts and connect to release tags to track SBOMs by version. Use GitHub Actions upload-artifact and GitLab artifacts.paths.

Retention period: ISO/IEC 18974 requires retention for the life of the program. In practice, permanent retention per release version is recommended.

When to update: Regenerate whenever dependencies change. Automatic generation at the PR level keeps it always up to date.


SBOM Analyzer

Upload SBOM files generated by syft, trivy, or cdxgen to automatically analyze vulnerabilities and generate response guidance.

Preview with a sample first (no API key required)

The same demo is available from the 5-minute quick start. Try it below, then run a real analysis on your own SBOM.

Run a real analysis on your own SBOM

This tool requires an Anthropic API key

It calls the Anthropic API directly from your browser. Enter your own Anthropic API key to use it right away. Your key and inputs are sent only from your browser to Anthropic (they never pass through a trustedoss server). Usage is billed to your own Anthropic account.


In Practice

TRUSCA's sca-self.yml generates a CycloneDX SBOM with cdxgen and then scans it with Trivy, on a daily schedule. Both tools are installed at pinned versions with checksum verification.

The reason for a scheduled run alongside the PR check is that vulnerabilities are disclosed after a release ships. That thread continues in Continuous Monitoring.

Self-Study

In-depth SBOM analysis with Claude Code

The analyzer above is available directly in your browser. If you need deeper analysis and automatic .grype.yaml policy file generation, use the agent below.

Prerequisite: Clone the Trusted OSS repository

Bash
cd agents/en/sbom-vuln-analyst
claude

The agent automatically performs the following:

  • Auto-detects CycloneDX / SPDX / grype results
  • Classifies by severity and suggests fixed versions
  • Generates .grype.yaml exception handling examples
  • Provides CI/CD pipeline integration guidance

Next Steps

Need an SCA engine you run every day?

The analyzer above is for one-off checks. If your whole team needs a continuously running, self-hosted SCA, move on to TRUSCA — an Apache-2.0 tool that manages vulnerabilities (CVEs), license compliance, and SBOMs in one UI.

  • Try it yourself: TRUSCA repository (Docker Compose or Helm deployment)
  • It is a path to a continuously operated SCA where budget, staffing, or an air-gapped network constrain the options.

The TRUSCA portal links a hosted demo instance you can open. The demo is for looking around, so run it in your own environment with the guide above for real use.