forgeseal

June 23, 2026 ยท View on GitHub

Supply chain security for JavaScript, TypeScript, Python, Go, Rust, and Java/Gradle projects. Generates CycloneDX SBOMs from lockfiles, signs them with Sigstore (keyless), produces SLSA provenance attestations, and manages VEX vulnerability documents.

Built for EU Cyber Resilience Act (CRA) compliance. forgeseal's own releases are attested by forgeseal.

Features

CapabilityDescription
SBOM GenerationCycloneDX v1.4/v1.5/v1.6 from any JS/TS, Python, Go, Rust, or Java/Gradle lockfile (JSON and XML output)
Sigstore SigningKeyless signing via Fulcio + Rekor transparency log (OIDC identity)
SLSA ProvenanceIn-toto attestations with SLSA v1 provenance predicate
VEX ManagementOpenVEX v0.2 document CRUD, automated triage via OSV.dev, CVSS severity classification
VerificationValidate signatures, bundles, and attestation integrity
PipelineSingle command: SBOM, sign, attest, triage

Supported Lockfiles

Package ManagerFileParser
npmpackage-lock.jsonv2 and v3 schemas
Yarn Classicyarn.lockv1 text format (state machine)
Yarn Berryyarn.lockv2/v3/v4 YAML (auto-detected via __metadata: header)
pnpmpnpm-lock.yamlv6 and v9 schemas
Bunbun.lockJSONC text format (Bun v1.2+)
Bun (binary)bun.lockbShells out to bun CLI; prefers bun.lock when both exist
piprequirements.txtPinned (==) dependencies with optional hashes
Poetrypoetry.lockTOML v2 format with full dependency graph
PDMpdm.lockTOML format with groups based dev detection
uvuv.lockTOML format (Astral uv v0.1+)
Go modulesgo.mod + go.sumParses require/replace directives and pairs with h1: hashes
CargoCargo.lockTOML v3 format with SHA-256 checksums; skips workspace roots
Gradlegradle.lockfileLine-oriented group:artifact:version=configurations with test-only dev detection

Detection Priority

When a project contains lockfiles for multiple ecosystems, forgeseal picks the first match from the ordered list below. Ecosystem buckets are ordered JS/TS > Python > Go > Rust > Java, and within each ecosystem the most precise lockfile wins.

RankFileEcosystem
1bun.lockbJS/TS (Bun binary)
2bun.lockJS/TS (Bun text)
3pnpm-lock.yamlJS/TS (pnpm)
4yarn.lockJS/TS (Yarn Classic or Berry, decided by content)
5package-lock.jsonJS/TS (npm)
6uv.lockPython (uv)
7poetry.lockPython (Poetry)
8pdm.lockPython (PDM)
9requirements.txtPython (pip)
10go.modGo
11Cargo.lockRust
12gradle.lockfileJava / Gradle

Notes:

  • If both bun.lockb and bun.lock exist, forgeseal prefers bun.lock so no bun CLI is required.
  • yarn.lock is probed for a __metadata: header to decide Yarn v1 (classic) vs Yarn v2+ (Berry).
  • In practice a project targets a single ecosystem, so this priority list only comes into play for mixed repos.

Note on Yarn Berry hashes: Yarn Berry uses a proprietary checksum format that is not compatible with standard SRI hashes or CycloneDX hash algorithms. Components from Yarn Berry lockfiles will be included in the SBOM without integrity hashes. This is a format limitation, not a forgeseal bug.

Python PURL normalization: PyPI package names are normalized per PEP 503 (lowercase, underscores and dots replaced with hyphens). This ensures correct lookups against OSV.dev for vulnerability triage.

Installation

# Homebrew (macOS / Linux)
brew install sns45/tap/forgeseal

# Quick install (latest release)
curl -sSL https://raw.githubusercontent.com/sns45/forgeseal/main/scripts/install.sh | sh

# Go
go install github.com/sns45/forgeseal/cmd/forgeseal@latest

# Docker
docker run --rm -v $(pwd):/src ghcr.io/sns45/forgeseal pipeline --dir /src

# From source
git clone https://github.com/sns45/forgeseal.git && cd forgeseal && make build

Requires Go 1.23+ for building from source.

Quick Start

# Generate an SBOM from a JS/TS project
forgeseal sbom --dir .

# Generate an SBOM from a Python project (uv, poetry, pdm, or pip)
forgeseal sbom --dir ./my-python-app

# Generate an SBOM from a Go project (go.mod + go.sum)
forgeseal sbom --dir ./my-go-service

# Generate an SBOM from a Rust project (Cargo.lock)
forgeseal sbom --dir ./my-rust-crate

# Generate an SBOM from a Gradle project (gradle.lockfile)
forgeseal sbom --dir ./my-gradle-app

# Full pipeline: SBOM + sign + attest + VEX triage
forgeseal pipeline --dir . --output-dir ./forgeseal-output --vex-triage

# Sign an existing artifact
forgeseal sign --artifact sbom.cdx.json

# Verify a signed artifact
forgeseal verify --artifact sbom.cdx.json --bundle sbom.cdx.json.sigstore.json

Commands

forgeseal sbom

Generate a CycloneDX SBOM from a lockfile.

forgeseal sbom --dir ./my-project
forgeseal sbom --lockfile ./package-lock.json --output-format xml
forgeseal sbom --include-dev --spec-version 1.6 -o sbom.json
FlagDefaultDescription
--dir.Project directory to scan
--lockfile(auto-detect)Explicit lockfile path
--spec-version1.5CycloneDX spec version: 1.4, 1.5, 1.6
--output-formatjsonOutput format: json or xml
--include-devfalseInclude devDependencies
--no-depsfalseOnly direct dependencies

Output includes: metadata (timestamp, tool info, root component), components with PURLs and integrity hashes, and a dependency graph.

forgeseal sign

Sign an artifact with Sigstore keyless signing.

forgeseal sign --artifact sbom.cdx.json
forgeseal sign --artifact sbom.cdx.json --identity-token $OIDC_TOKEN
FlagDefaultDescription
--artifact(required)Path to the artifact to sign
--identity-token(auto)Explicit OIDC token; auto-detected in GitHub Actions
--fulcio-urlhttps://fulcio.sigstore.devFulcio CA instance
--rekor-urlhttps://rekor.sigstore.devRekor transparency log
--bundle<artifact>.sigstore.jsonOutput path for the Sigstore bundle

The signing flow: obtain OIDC token, generate ephemeral ECDSA P-256 keypair, sign the artifact, and produce a .sigstore.json bundle. In GitHub Actions, the OIDC token is obtained automatically when the workflow has permissions: id-token: write.

forgeseal attest

Generate a SLSA v1 provenance attestation as an in-toto statement.

forgeseal attest --subject sbom.cdx.json
forgeseal attest --subject sbom.cdx.json --sign --repo https://github.com/org/repo --commit abc123
FlagDefaultDescription
--subjectPath to the artifact being attested
--subject-digestExplicit sha256:... digest
--repo(auto in CI)Source repository URI
--commit(auto in CI)Source commit SHA
--signtrueSign the attestation with Sigstore DSSE

CI environment variables (GITHUB_REPOSITORY, GITHUB_SHA, etc.) are automatically detected.

forgeseal vex

Manage Vulnerability Exploitability eXchange (VEX) documents.

# Create an empty VEX document
forgeseal vex create -o vex.json

# Add a statement
forgeseal vex add --vex vex.json --cve CVE-2024-1234 \
  --product "pkg:npm/my-app@1.0.0" \
  --status not_affected \
  --justification vulnerable_code_not_present

# List statements
forgeseal vex list --vex vex.json

# Automated triage: scan SBOM against OSV.dev
forgeseal vex triage --sbom sbom.cdx.json -o vex.json
forgeseal vex triage --sbom sbom.cdx.json --format cyclonedx -o sbom-with-vex.json

VEX statuses: not_affected, affected, fixed, under_investigation

Justifications (for not_affected): component_not_present, vulnerable_code_not_present, vulnerable_code_cannot_be_controlled_by_adversary, vulnerable_code_not_in_execute_path, inline_mitigations_already_exist

The triage subcommand queries the OSV.dev batch API, batching PURLs in groups of 1000, classifies each vulnerability by CVSS severity (critical, high, medium, low), and generates under_investigation stubs for each discovered vulnerability. Use --fail-on <severity> to fail the command when vulnerabilities at or above the threshold are found.

forgeseal verify

Verify Sigstore bundles and attestations.

forgeseal verify --artifact sbom.cdx.json --bundle sbom.cdx.json.sigstore.json
forgeseal verify --attestation sbom.cdx.json.intoto.jsonl
FlagDefaultDescription
--artifactPath to the artifact
--bundlePath to .sigstore.json bundle
--attestationPath to SLSA attestation
--expected-issuerExpected OIDC issuer for identity verification
--expected-identityExpected signer identity (regex)

forgeseal pipeline

Run the full security pipeline in one command.

forgeseal pipeline --dir . --output-dir ./forgeseal-output
forgeseal pipeline --dir . --vex-triage --identity-token $TOKEN
FlagDefaultDescription
--dir.Project directory
--output-dir./forgeseal-outputDirectory for all generated artifacts
--lockfile(auto-detect)Explicit lockfile path
--signtrueSign artifacts with Sigstore
--attesttrueGenerate SLSA provenance
--vex-triagefalseRun VEX triage against OSV.dev
--fail-on(none)Fail if vulnerabilities at or above severity: critical, high, medium, low
--include-devfalseInclude devDependencies
--identity-token(auto)Explicit OIDC token

Setting --fail-on automatically enables VEX triage.

Pipeline steps:

  1. Parse lockfile, generate CycloneDX SBOM (sbom.cdx.json)
  2. Sign SBOM (sbom.cdx.json.sigstore.json)
  3. Generate SLSA provenance attestation (sbom.cdx.json.intoto.jsonl), optionally sign it
  4. Query OSV.dev and generate VEX document (vex.json)

Vulnerability summary: When VEX triage runs, forgeseal prints a severity breakdown to stderr:

  ๐Ÿ”ด CRITICAL 2 CRITICAL   ๐ŸŸก HIGH 5 HIGH   ๐ŸŸ  MEDIUM 12 MEDIUM   โšช LOW 3 LOW

  CRITICAL:
    lodash@4.17.20                 Prototype Pollution                          CVE-2021-23337
    express@4.17.1                 Path Traversal                               CVE-2024-29041

  Scanned 156 components, found 42 vulnerabilities

CI gating: Use --fail-on to block builds when vulnerabilities exceed a threshold:

# Fail if any critical or high vulnerabilities found
forgeseal pipeline --dir . --fail-on high

# Fail only on critical
forgeseal pipeline --dir . --fail-on critical

GitHub Action

Available on the GitHub Marketplace.

name: Supply Chain Security
on: [push]

permissions:
  id-token: write  # Required for Sigstore keyless signing
  contents: read

jobs:
  forgeseal:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: sns45/forgeseal@v1
        id: forgeseal
        with:
          dir: '.'
          fail-on: high  # Fail CI if high or critical vulnerabilities found

      - uses: actions/upload-artifact@v4
        with:
          name: supply-chain-artifacts
          path: ./forgeseal-output/

Action Inputs

InputDefaultDescription
commandpipelineCommand to run: pipeline, sbom, sign, attest, vex
dir.Project directory
output-dir./forgeseal-outputOutput directory for artifacts
signtrueSign artifacts with Sigstore keyless signing
attesttrueGenerate SLSA v1 provenance attestation
vex-triagetrueRun VEX vulnerability triage against OSV.dev
fail-on(none)Fail if vulnerabilities at or above severity: critical, high, medium, low
include-devfalseInclude devDependencies in SBOM
versionlatestforgeseal version to install

Action Outputs

OutputDescription
sbom-pathPath to the generated CycloneDX SBOM
bundle-pathPath to the Sigstore signature bundle
attestation-pathPath to the SLSA provenance attestation
vex-pathPath to the VEX document
vuln-countNumber of vulnerabilities found by VEX triage

The action downloads a pre-built binary from GitHub Releases (no Docker build overhead). OIDC tokens are obtained automatically when the workflow has permissions: id-token: write.

Configuration

forgeseal reads .forgeseal.yaml from the project directory or home directory. All settings can be overridden via flags or environment variables (prefixed FORGESEAL_).

sbom:
  spec_version: "1.5"
  format: json
  include_dev: false

sign:
  fulcio_url: https://fulcio.sigstore.dev
  rekor_url: https://rekor.sigstore.dev

attest:
  auto_sign: true

vex:
  format: openvex
  osv_ecosystem: npm

Environment variable mapping: FORGESEAL_SBOM_SPEC_VERSION, FORGESEAL_SIGN_FULCIO_URL, etc.

Architecture

cmd/forgeseal/          Entrypoint
internal/
  cli/                  Cobra commands (sbom, sign, attest, vex, verify, pipeline)
  config/               Viper configuration loading
  lockfile/             10 parsers (6 JS/TS + 4 Python) + auto-detection + domain types
  sbom/                 CycloneDX BOM generation, ecosystem aware PURL construction (npm + PyPI), dependency mapping
  signing/              Sigstore signer interface, bundle serialization
  provenance/           SLSA v1 attestation builder, CI environment detection
  vex/                  OpenVEX CRUD, OSV.dev client, CycloneDX VEX embedding
  verify/               Bundle and attestation verification

Key Design Decisions

Parser interface. Each lockfile format implements Parser with Parse(ctx, io.Reader), Type(), and Filenames(). Binary formats (bun.lockb) additionally implement FileParser with ParseFile(ctx, path). Detection iterates a priority ordered registry and uses content inspection for yarn v1 vs Berry disambiguation.

PURL construction. Package URLs follow the PURL spec. npm scoped packages like @babel/core@7.24.0 become pkg:npm/babel/core@7.24.0 (the @ prefix is stripped from the namespace per spec). PyPI packages use pkg:pypi/ with PEP 503 normalized names (e.g., Flask becomes pkg:pypi/flask@3.0.2). The ecosystem is determined automatically from the lockfile type.

Signing abstraction. The Signer interface provides SignBlob (raw content) and SignDSSE (in-toto envelope). The current implementation generates ephemeral ECDSA P-256 signatures. Full Sigstore integration (Fulcio certificate issuance + Rekor log recording) can be added by depending on sigstore-go without changing the interface.

VEX triage. PURLs extracted from the SBOM are batched in groups of 1000 and sent to the OSV.dev /v1/querybatch endpoint. Both npm and PyPI PURLs are supported; the correct ecosystem is encoded in the PURL type prefix. Each vulnerability is classified by CVSS v3 severity (critical 9.0+, high 7.0+, medium 4.0+, low 0.1+). Results are mapped to VEX stubs with severity metadata. The --fail-on flag enables CI gating by exiting non-zero when vulnerabilities meet or exceed the specified threshold.

Output Artifacts

FileContent
sbom.cdx.jsonCycloneDX SBOM (JSON)
sbom.cdx.json.sigstore.jsonSigstore bundle for the SBOM
sbom.cdx.json.intoto.jsonlSLSA v1 provenance attestation
sbom.cdx.json.intoto.jsonl.sigstore.jsonSigstore bundle for the attestation
vex.jsonOpenVEX document with vulnerability triage results

Development

make build       # Build to ./bin/forgeseal
make test        # Run tests with race detection
make lint        # Run golangci-lint
make install     # Install to $GOPATH/bin
make clean       # Remove build artifacts

Cross-platform releases (Linux, macOS, Windows; amd64 + arm64) are handled by GoReleaser.

License

Apache 2.0