Safeguard
Supply Chain Security•

Validate CycloneDX, SPDX and VEX Files with the Safeguard CLI

Run safeguard validate on CycloneDX, SPDX, CSAF and OpenVEX files on your own machine, add --strict for CI, and fix what fails with the assistant.

Safeguard Security Team
18 min read

To validate CycloneDX, SPDX and VEX files with Safeguard, install the Safeguard CLI and run safeguard validate on a file, a list of files, or a folder with --recursive. It detects CycloneDX JSON or XML, SPDX JSON, YAML, tag-value or RDF, and CSAF or OpenVEX, and --strict turns warnings into errors for CI.

You do not have to give up the AI you already use to work this way. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. If your team prefers a chat window, connect Claude or ChatGPT to the Safeguard MCP server and ask about the same SBOMs and findings from the chat you already use.

This guide is for the person who has to hand an SBOM or a VEX document to someone else: a customer, an auditor, a procurement team, or the next stage of your own pipeline. Every Safeguard command and flag below comes from the CLI's own help text or the Safeguard docs linked at the end.

What you will have at the end

Work through this guide and you will have:

  • The Safeguard CLI installed and signed in on your own machine.
  • An SPDX SBOM generated for a project folder with safeguard sbom.
  • A habit of running safeguard validate on every SBOM and VEX file before it leaves your hands, with a JSON or text report you can keep.
  • A small OpenVEX document you wrote, validated and understood.
  • A pipeline step that validates every SBOM your build produces, with --strict so warnings count.
  • A way to fix a failing file with the assistant, which edits it only with your approval.

You need a terminal on macOS, Linux or Windows, a project folder with a lock file (the sample here is a Node.js service called payments-api), and a Safeguard account.

What validation checks, and what it does not

safeguard validate checks documents against their format specifications. That answers one question: is this a well formed document of the format it claims to be? It does not tell you whether the component list is complete, whether the versions match what you shipped, or whether a not_affected statement is true. Keep those two questions apart and most SBOM arguments get shorter.

Here is what the CLI accepts, next to what each format's own specification says.

FormatWhat safeguard validate acceptsWho maintains the formatWorth knowing from the spec
CycloneDXJSON and XMLOWASP Foundation and Ecma International (ECMA-424)Version 1.7 is current. bomFormat and specVersion are the required top-level fields in the JSON schema.
SPDXJSON, YAML, tag-value and RDFThe SPDX project; ISO/IEC 5962:2021Version 3.0 is current and 2.3 is the previous release. SPDX 2.3 lists tag:value, JSON, YAML 1.2, RDF/XML and spreadsheets.
CSAF VEXCSAF documentsOASIS (CSAF 2.0, an OASIS Standard dated 18 November 2022)Profile 5 is VEX. Its documents use the category csaf_vex.
OpenVEXOpenVEX documentsThe OpenVEX project (spec repository under CC0-1.0)A minimal JSON-LD format. A statement's status is not_affected, affected, fixed or under_investigation.

A few rules from the specs are worth checking by eye, because they are where VEX documents most often go wrong:

  • In OpenVEX, a not_affected statement must carry a justification or an impact_statement. The justification values are component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary and inline_mitigations_already_exist.
  • In CSAF VEX, each product listed as known_not_affected needs a matching impact statement in the vulnerability's flags or threats, and each product listed as known_affected needs a matching action statement in remediations.
  • In CycloneDX, VEX lives in the vulnerabilities array. The 1.6 schema allows analysis.state values of resolved, resolved_with_pedigree, exploitable, in_triage, false_positive and not_affected, and justifications such as code_not_present and code_not_reachable.

Do not assume safeguard validate enforces every one of these rules for you. Its help text says it validates against the format specifications. Run it, read the report, and then read the statements themselves.

Do the work properly with the Safeguard CLI

The Safeguard CLI scans code, container images and SBOMs on your own machine, sends only the results to the platform, and includes an assistant that can fix what it finds. It also writes code, not only scans, so the same tool that tells you a document is broken can edit it, with your approval. Validation is one command among many, and it is easier to keep up when the tool that runs it is already open in your terminal.

Install

On macOS and Linux:

curl -fsSL https://cli.safeguard.sh/install | bash

On Windows, in PowerShell:

irm https://cli.safeguard.sh/install.ps1 | iex

It is a single static binary, so there is no Node, Python or JVM to install first. It runs on macOS, Linux and Windows, on x86_64 or arm64, and Windows is x86_64 only. The installer verifies the download against the release's checksums.txt. Confirm the install with safeguard --version. If you want a walkthrough for your system, see installing the Safeguard CLI on macOS or on Linux.

First session

cd ~/projects/payments-api
safeguard

Running safeguard with no arguments opens the interactive terminal. Type /login and approve the session in your browser. Then /scan . scans the current folder. You can also ask in plain language, for example "how many critical vulns do we have" or "validate every SBOM in ./dist and tell me what failed". The assistant has tools for your local files and for your Safeguard data, and it picks whichever the question needs. Type @ to attach a file from the folder you started in, so @dist/payments-api.cdx.json puts that SBOM in front of the assistant. /resume picks up the last session in that folder.

Fix a failing document with the assistant

When a file fails, ask the assistant to fix it: "the OpenVEX file in ./vex fails validation, find out why and fix it". It reads the report and the file, proposes the change, and asks before it writes. That is the same flow you can watch in this recorded session, where the job is a dependency fix rather than an SBOM fix.

The recording below is a real CLI session on a Mac with a sample project. It installs with the curl command (checksum verified), runs safeguard --version, opens the interactive terminal and asks "fix the lodash CVE in this repo". The assistant reads the finding, the files and the advisory, asks before each command (press y or n), edits package.json to lodash 4.17.21, runs npm install and npm test with approval, and prints a summary with a suggested commit message. It pushes nothing. It then shows /permissions, /model and /scan ..

Click through it at your own pace, or open it full screen.

Your own model and plan

/model shows or switches the AI model. The plan you already pay for works here, as covered at the top of this guide; see the CLI docs or /model for connecting a model.

Approvals

/permissions sets how much the assistant may do before it asks. In CLI v11.11.0 the modes are:

  • Ask: reads run; edits and commands ask first.
  • Accept edits: edits in this folder and read-only commands run; other commands ask.
  • Plan: read and plan only.
  • Auto: read-only commands and what the policy allows run; anything else asks. This is the default.
  • Strict: pre-approved actions only, for CI.

There is also a --yolo flag that approves everything. Keep it for throwaway sandboxes only. For validation work, Ask or Accept edits is a comfortable fit: running safeguard validate is a read, and any change to an SBOM or VEX file stops for you.

Hand the job over from the desktop app

A CLI session is live. It shows in the web app, the desktop app and the phone app, and other devices can send work to it. So you can start validation in the terminal and finish it from the Safeguard desktop app.

The recording below is a real desktop app session. The CLI session shows in the desktop app's conversation list, and "2 connected" shows the computer, the browser and Safeguard cloud. You ask for work on your computer, it asks which computer and folder, you pick the folder, then Approve or Deny each command and file edit. It ends with the upgrade done and tests passing.

Click through it at your own pace, or open it full screen.

CI with safeguard exec

safeguard exec "summarise the critical findings in this repository" runs one non-interactive assistant turn, for CI. Its default permission mode is strict: nothing is asked, and anything not pre-approved by the policy file is denied. It refuses to run outside a git repository unless you pass --skip-git-repo-check, and --output-format json gives a machine readable result. The guide to safeguard exec in CI covers it in depth.

Step by step: validate your first documents

This walkthrough uses payments-api, a Node.js service with a package-lock.json. Swap in your own folder.

1. Install dependencies so the SBOM is complete

An SBOM is only as good as the lock files it reads. The Safeguard troubleshooting docs say to install dependencies first and make sure lock files exist.

cd ~/projects/payments-api
npm install
ls -la package-lock.json

2. Generate an SPDX SBOM

safeguard sbom writes an SPDX-JSON SBOM using the same Syft-backed path the scanner uses, and stops before vulnerability matching.

safeguard sbom --dir . --output dist/payments-api.spdx.json

Without --output, the file lands in the current folder as a name ending in -sbom.json. For a container image, use safeguard sbom --image with the image reference.

safeguard sbom writes SPDX only. If a customer asks for CycloneDX, produce it with your build tooling or with Syft, which lists cyclonedx-json among its output formats, and validate it the same way.

3. Validate one file

safeguard validate dist/payments-api.spdx.json

The format is detected automatically. Read the result before you go further: a clean first run tells you the generator and the validator agree.

4. Validate several files, or a whole folder

safeguard validate dist/*.json
safeguard validate ./dist --recursive

Use the folder form when your build writes one SBOM per service or per image.

5. Force the format when detection is not what you expect

safeguard validate dist/payments-api.cdx.xml --format cyclonedx-xml

--format (or -f) forces a specific format; leave it empty to auto-detect. Run safeguard validate --help to see the format names your version accepts.

6. Treat warnings as errors, and stop early when you want to

safeguard validate ./dist --recursive --strict
safeguard validate ./dist --recursive --fail-fast

--strict treats warnings as errors. --fail-fast stops at the first validation error, which is handy on a large folder when you only want to know whether anything is broken.

7. Keep a report

safeguard validate ./dist --recursive --strict -o validation-report.json
safeguard validate ./dist --recursive --report-format text -o validation-report.txt

-o writes the report to a file. --report-format takes json (the default) or text. Keep the JSON one with your release artifacts; it is the evidence that you checked.

8. Write and validate an OpenVEX document

Create vex/payments-api.openvex.json. Replace the placeholders with a real vulnerability ID, your package URL and your name.

{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "@id": "https://example.com/vex/payments-api-2026-10-21",
  "author": "Payments Team",
  "timestamp": "2026-10-21T09:00:00Z",
  "version": 1,
  "statements": [
    {
      "vulnerability": { "name": "CVE-YYYY-NNNN" },
      "products": [
        { "@id": "pkg:npm/payments-api@2.4.0" }
      ],
      "status": "not_affected",
      "justification": "vulnerable_code_not_in_execute_path"
    }
  ]
}

Then validate it:

safeguard validate vex/payments-api.openvex.json --strict

Notice the justification. The OpenVEX spec requires a not_affected statement to carry one, or an impact_statement. A statement that says "not affected" with no reason is the kind of thing a reviewer will send back.

9. Scan the SBOM you just validated

Validation and scanning are separate steps. Once the file passes, match it against vulnerability databases:

safeguard scan --sbom dist/payments-api.spdx.json --output out/

safeguard scan --sbom takes SPDX or CycloneDX and detects which. Results land as JSON files under the output folder, including vuln.json and license.json. If you want a quick look with nothing uploaded at all, safeguard findings sca --dir . runs a scanner locally and prints its findings in one normalized list, without registering a project.

Validate in a pipeline

The Safeguard CLI runs in any CI system as a plain shell step; Safeguard does not publish a GitHub Action or CI plugin. The CI/CD docs ask for a Linux or macOS runner with bash and curl, and recommend pinning the CLI version with SAFEGUARD_VERSION so a new release does not change your builds.

A GitHub Actions job that builds an SBOM and validates everything in dist:

name: sbom
on: [push]
jobs:
  validate-sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Install the Safeguard CLI
        run: curl -fsSL https://cli.safeguard.sh/install | SAFEGUARD_VERSION=11.11.0 SAFEGUARD_INSTALL_DIR="$HOME/bin" bash
      - name: Validate SBOM and VEX files
        run: |
          export PATH="$HOME/bin:$PATH"
          safeguard validate ./dist --recursive --strict -o validation-report.json
      - name: Keep the report
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: sbom-validation
          path: validation-report.json

Three notes on this job:

  1. The version shown is the one we used on 21 October 2026. The installer resolves the latest release from https://cli.safeguard.sh/releases/latest.txt, so check that file for the current version string before you pin. The step that produces dist is yours: your build tool, Syft, or safeguard sbom. Note that safeguard sbom and safeguard scan need the Syft and Grype tools on the PATH or in TOOLS_DIR, which the CLI downloads on first interactive use. Validation alone does not scan.
  2. Before you rely on the step to block a release, test it. Commit a deliberately broken copy of an SBOM on a branch and confirm the step fails. The docs do not list exit codes for validate, so prove the behaviour on your own runner rather than assuming it.
  3. if: always() keeps the report even when validation fails, which is when you need it most.

Policies and gates are evaluated in the platform, not by the CLI. If you want your organisation's rules to decide whether a build ships, use safeguard ci scan with --fail-on-gate, and keep safeguard validate as the format check that runs before anything is shared.

Troubleshooting and common mistakes

Troubleshooting

command not found: safeguard. Run which -a safeguard (or Get-Command safeguard -All on Windows). The first match on your PATH wins, and an old build can shadow a new one. On Windows, open a new shell after installing, because the PATH change does not reach shells that were already open.

The wrong format is detected. Pass --format explicitly. This happens most with XML files and with files whose names do not hint at their format.

A folder validates nothing. Pass --recursive when the SBOMs sit in subfolders, and check you are in the folder you think you are in with pwd.

It passes on your laptop and fails in CI. Check whether CI runs with --strict. Warnings that pass locally become errors there. Also check the CLI version on both machines with safeguard --version; pin SAFEGUARD_VERSION so they match.

safeguard sbom or safeguard scan says a tool is missing. Run safeguard once interactively so it can complete first-run setup and download its scanner toolchain. safeguard update refreshes vulnerability databases for scanners already installed; it does not install missing ones.

Anything else. Run safeguard doctor. It checks environment, sign-in, tools and policy and changes nothing. Logs are in ~/.sg/logs/safeguard.log.

Common mistakes

  • Treating valid as correct. A schema-valid SBOM can still miss half your dependencies. Validate the format, then check the content against your lock files.
  • Validating a different build from the one you ship. Generate the SBOM in the same job that produces the release artifact, and validate that exact file.
  • Hand editing a generated SBOM and not revalidating. One stray comma breaks a JSON document. Validate after every edit, or let the assistant make the edit and run the check.
  • not_affected with no reason. OpenVEX requires a justification or impact statement, and CSAF VEX requires an impact statement for known_not_affected products. Write the reason as if the reader will check it, because some will.
  • Mixing SPDX versions. SPDX 3.0 and 2.3 differ. Know which one your consumer expects before you send a file.
  • Using --yolo on your real machine to save clicks. Keep it for throwaway sandboxes. Accept edits gives you speed on file edits without handing over every command.

FAQ

Can I keep my Claude, ChatGPT or Codex plan if I switch to Safeguard?

Yes. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. You can also connect Claude or ChatGPT to the Safeguard MCP server, as shown in connecting Claude to the Safeguard MCP server, and use Safeguard from the chat you already use. See the CLI docs or /model for connecting a model.

Which formats does safeguard validate support?

CycloneDX in JSON and XML, SPDX in JSON, YAML, tag-value and RDF, and VEX as CSAF or OpenVEX. It auto-detects the format unless you pass --format.

Is a valid SBOM a complete SBOM?

No. Validation checks the document against its specification. Completeness depends on how the SBOM was generated, which is why installing dependencies and keeping lock files matters before you run safeguard sbom.

Can safeguard sbom write CycloneDX?

Its help text says it generates SPDX-JSON. For CycloneDX, use your build tooling or Syft's cyclonedx-json output, then run safeguard validate on the result. safeguard scan --sbom accepts either format.

What is the difference between CSAF VEX and OpenVEX?

CSAF is an OASIS standard for security advisories, and VEX is one of its five profiles, with the document category csaf_vex. OpenVEX is a smaller JSON-LD format built only for VEX statements and meant to work with any SBOM format. safeguard validate checks both.

How do I fail a build when an SBOM is invalid?

Run safeguard validate with --strict in a pipeline step and keep the report with -o. Then break a file on purpose on a test branch to confirm the step fails on your runner before you depend on it.

Does my source code leave my machine?

The CLI scans on your machine and sends only results to the platform. When the local runner runs platform scan jobs, your source code never leaves the machine; only SBOMs and findings are uploaded. safeguard findings uploads nothing at all.

Does the CLI enforce our SBOM policies?

No. Policies and gates are evaluated in the platform. The CLI's job is to generate, validate and scan; safeguard ci scan --fail-on-gate lets a pipeline wait for the platform's verdict.

Try it yourself

Before you validate anything, the CLI has to be running on your machine. This demo sets up the runner on your own machine: pick the platform, start it, approve it with its device code, then use the same CLI to scan from the terminal.

Click through it at your own pace, or open it full screen.

When you are ready on your own machine, get the CLI from the Safeguard download page and keep the CLI docs open beside it. Generate one SBOM, validate it with --strict, and write one VEX statement with a real justification. That is the whole habit.

Quick check

Which flag makes safeguard validate treat warnings as errors?

--strict. Use it in CI so a warning that would pass on a laptop stops the build.

An OpenVEX statement says not_affected. What else must it include?

A justification or an impact_statement, per the OpenVEX specification. The justification values include vulnerable_code_not_in_execute_path and component_not_present.

What SBOM format does safeguard sbom write?

SPDX-JSON. For CycloneDX, generate the file with your build tooling or Syft, then validate it with safeguard validate.

Sources

Last reviewed: 21 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.