vexctl

repository·main·Indexed 42 days ago

https://github.com/openvex/vexctl

A command-line tool for managing Vulnerability Exploitability eXchange (VEX) data. vexctl allows users to create, merge, and sign OpenVEX documents, convert CSAF documents to OpenVEX, and filter SARIF security scanner results to remove non-exploitable vulnerability alerts. It supports wrapping VEX documents in signed in-toto attestations using Sigstore and attaching them to OCI images via the OCI referrers API.

Tokens
7.8K
Snippets
27
Records
36
Agent score
53%

What's inside vexctl

  1. Install vexctl

    main

    You can install vexctl using several methods depending on your environment:

    Homebrew

    Install the latest tagged version via Homebrew:

    brew install vexctl

    From Source (Go)

    If you have Go 1.21 or later, you can use go install to download the required toolchain and install the tool automatically:

    go install github.com/openvex/vexctl@latest

    For older Go versions, ensure you have a current toolchain installed from go.dev/dl first.

    Prebuilt Binaries

    Download signed binaries for Linux, macOS, and Windows from the releases page. After downloading, make the binary executable and add it to your PATH.

    To verify the integrity of a downloaded binary using cosign, use the provided *.sigstore.json bundle:

    cosign verify-blob \
      --bundle vexctl-linux-amd64.sigstore.json \
      --certificate-identity-regexp='^https://github.com/openvex/vexctl/' \
      --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
      vexctl-linux-amd64
  2. Use vexctl to work with VEX data

    main

    vexctl is a command-line tool designed for working with Vulnerability Exploitability eXchange (VEX) data. It allows users to interpret security scanner results by attaching VEX information to container images and filtering result sets based on that information to identify which vulnerabilities are relevant to a project.

    Core capabilities include:

    • Attesting and attaching VEX statements to container images.
    • Filtering vulnerability result sets using VEX data.
    • Creating, listing, and merging VEX documents.
    # General usage pattern
    vexctl [command] [flags]
  3. Validation rules for VEX statements

    main

    When constructing a VEX statement via the CLI, the following validation rules apply:

    • Vulnerability ID: A vulnerability ID is mandatory.
    • Status: A valid impact status is required.
    • Product Requirement: At least one product must be specified.
    • Status-Specific Fields:
      • --justification and --impact-statement can only be used when the status is not_affected.
      • --action-statement can only be used when the status is affected.
    • Product/Subcomponent Constraint: You cannot specify --subcomponents if you are providing multiple --product entries, as it becomes ambiguous which subcomponent belongs to which product.
    • Aliases: Aliases cannot be identical to the primary vulnerability ID, and duplicates are not allowed.
  4. Use SOURCE_DATE_EPOCH for reproducible timestamps

    main
    To ensure reproducible VEX documents, vexctl honors the SOURCE_DATE_EPOCH environment variable. If this variable is set, the timestamp and (if applicable) the action_statement_timestamp in the VEX statement will be set to the date provided by the environment variable instead of the current system time.
  5. How golden templates work for product matching

    main

    Golden templates use generic identifiers to act as blueprints for specific artifacts. When you add a statement to a template using an unversioned PURL, vexctl will automatically include that statement in any generated document where the product matches that PURL, regardless of version, tag, or repository.

    Example Template Statement: If your template contains:

    "statements": [
        {
          "vulnerability": { "name": "CVE-1234-5678" },
          "products": [ { "@id": "pkg:oci/test" } ],
          "status": "fixed",
          "timestamp": "2023-12-05T05:04:34.77929922Z"
        }
    ]

    This statement will be matched and included in generated documents for:

    • pkg:oci/test@sha256:f87abf1735e79b70407288f665316644d414dbf7bdf38c2f1c8e3a541d304d84 (Versioned PURL)
    • pkg:oci/test?tag=latest (Image with tag)
    • pkg:oci/test?tag=latest&repository_url=ghcr.io/openvex (Image with tag and repository)
  6. Generate VEX data from golden templates

    main

    The generate subcommand creates a new VEX document for a specific product by reading from a directory of golden templates. It filters the templates to find statements that match the provided product ID.

    Note: In the current version, vexctl only matches products; subcomponents are not considered when filtering statements.

    Workflow:

    1. Create a templates directory using --init.
    2. Add generic statements to your templates using vexctl add (e.g., adding a statement for pkg:oci/test will apply to all versions/tags of that image).
    3. Run generate with a specific product ID to produce the final document.

    Metadata & Environment Variables:

    • If no document ID is specified, one is generated using its canonicalization hash.
    • vexctl honors the SOURCE_DATE_EPOCH environment variable (UNIX time or RFC3339) to set the document timestamp.
    # Example: Generate a document for a specific OCI image digest
    vexctl generate --templates=".openvex/templates/" \
      --product="pkg:oci/test@sha256:f87abf1735e79b70407288f665316644d414dbf7bdf38c2f1c8e3a541d304d84&repository_url=ghcr.io/openvex"
  7. Initialize a new templates directory with `vexctl generate --init`

    main

    To use the template-based generation feature, you must first create a directory containing 'golden templates'. Golden templates are standard OpenVEX documents that use generic identifiers (like unversioned PURLs) to define statements that should be applied to all matching products.

    Use the --init flag to create a new templates directory and an initial empty OpenVEX document at the path specified by --templates (or -t).

    vexctl generate --init --templates=".openvex/templates"
  8. Convert a CSAF document to OpenVEX using vexctl merge

    main

    You can use vexctl merge to detect and read VEX statements embedded within CSAF (Common Security Advisory Framework) documents. When run against a single CSAF file, vexctl will output a new OpenVEX document containing the extracted statements.

    vexctl merge examples/csaf/csaf.json 
  9. Verify SARIF report filtering results

    main

    To verify that vexctl filter is working correctly, you can compare the count of the results array in the original SARIF report against the count in the filtered output.

    1. Count original results: cat <sarif_file> | jq '.runs[0].results | length'
    2. Count filtered results: vexctl filter <sarif_file> <vex_file> | jq '.runs[0].results | length'

    If the filter is successful, the second count should be lower than the first by the number of vulnerabilities addressed in the VEX file.

    vexctl filter \
       examples/sarif/nginx-grype.sarif.json \
       examples/sarif/sample-1statement.openvex.json | jq '.runs[0].results | length'
  10. Compose a single OpenVEX document from multiple formats

    main

    The vexctl merge command supports reading VEX data from multiple documents in different formats (such as CSAF and OpenVEX) and composing them into a single unified OpenVEX document. This allows you to aggregate vulnerability statements from various sources into one file.

    vexctl merge examples/csaf/csaf.json examples/csaf/openvex.json
  11. Test vexctl filtering with SARIF and OpenVEX examples

    main

    You can use the provided SARIF and OpenVEX example files to test the filtering capabilities of vexctl. The SARIF files contain vulnerability scan results, and the OpenVEX files contain statements that mark specific vulnerabilities as fixed or not_affected.

    Available OpenVEX Examples

    FilenameDetails
    sample-1statement.openvex.jsonOpenVEX document with one statement filtering CVE-2023-27103
    sample-2vulns.openvex.jsonDocument with two statements filtering CVE-2023-27103 and CVE-2007-5686
    sample-history.openvex.jsonSample document with multiple statements forming a VEX history for CVE-2023-27103

    How to filter SARIF reports

    To filter a SARIF report using a VEX file, use the vexctl filter command. This command will output a version of the SARIF document to STDERR where the results marked as fixed or not_affected in the OpenVEX document have been removed.

    vexctl filter examples/sarif/nginx-grype.sarif.json examples/sarif/sample-1statement.openvex.json
  12. Attest and attach VEX statements to container images

    main

    You can wrap VEX documents in signed attestations and attach them to OCI images. vexctl uses Sigstore bundles and the OCI referrers API (compatible with cosign v3).

    Key flags:

    • --attach: Attaches the attestation to the image.
    • --sign: Signs the attestation.
    • --format dsse: Writes a bare DSSE envelope instead of a Sigstore bundle.
    • --attach-method legacy: Uses the older cosign tag layout (e.g., .att tags) for compatibility with older tooling.
    # Attest and attach VEX statements in mydata.vex.json to a container image:
    vexctl attest --attach --sign mydata.vex.json cgr.dev/image@sha256:e4cf37d568d195b4..