Skip to content

Latest commit

 

History

History
399 lines (296 loc) · 15.9 KB

File metadata and controls

399 lines (296 loc) · 15.9 KB

forgeseal

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

Capability Description
SBOM Generation CycloneDX v1.4/v1.5/v1.6 from any JS/TS, Python, Go, Rust, or Java/Gradle lockfile (JSON and XML output)
Sigstore Signing Keyless signing via Fulcio + Rekor transparency log (OIDC identity)
SLSA Provenance In-toto attestations with SLSA v1 provenance predicate
VEX Management OpenVEX v0.2 document CRUD, automated triage via OSV.dev, CVSS severity classification
Verification Validate signatures, bundles, and attestation integrity
Pipeline Single command: SBOM, sign, attest, triage

Supported Lockfiles

Package Manager File Parser
npm package-lock.json v2 and v3 schemas
Yarn Classic yarn.lock v1 text format (state machine)
Yarn Berry yarn.lock v2/v3/v4 YAML (auto-detected via __metadata: header)
pnpm pnpm-lock.yaml v6 and v9 schemas
Bun bun.lock JSONC text format (Bun v1.2+)
Bun (binary) bun.lockb Shells out to bun CLI; prefers bun.lock when both exist
pip requirements.txt Pinned (==) dependencies with optional hashes
Poetry poetry.lock TOML v2 format with full dependency graph
PDM pdm.lock TOML format with groups based dev detection
uv uv.lock TOML format (Astral uv v0.1+)
Go modules go.mod + go.sum Parses require/replace directives and pairs with h1: hashes
Cargo Cargo.lock TOML v3 format with SHA-256 checksums; skips workspace roots
Gradle gradle.lockfile Line-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.

Rank File Ecosystem
1 bun.lockb JS/TS (Bun binary)
2 bun.lock JS/TS (Bun text)
3 pnpm-lock.yaml JS/TS (pnpm)
4 yarn.lock JS/TS (Yarn Classic or Berry, decided by content)
5 package-lock.json JS/TS (npm)
6 uv.lock Python (uv)
7 poetry.lock Python (Poetry)
8 pdm.lock Python (PDM)
9 requirements.txt Python (pip)
10 go.mod Go
11 Cargo.lock Rust
12 gradle.lockfile Java / 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
Flag Default Description
--dir . Project directory to scan
--lockfile (auto-detect) Explicit lockfile path
--spec-version 1.5 CycloneDX spec version: 1.4, 1.5, 1.6
--output-format json Output format: json or xml
--include-dev false Include devDependencies
--no-deps false Only 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
Flag Default Description
--artifact (required) Path to the artifact to sign
--identity-token (auto) Explicit OIDC token; auto-detected in GitHub Actions
--fulcio-url https://fulcio.sigstore.dev Fulcio CA instance
--rekor-url https://rekor.sigstore.dev Rekor transparency log
--bundle <artifact>.sigstore.json Output 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
Flag Default Description
--subject Path to the artifact being attested
--subject-digest Explicit sha256:... digest
--repo (auto in CI) Source repository URI
--commit (auto in CI) Source commit SHA
--sign true Sign 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
Flag Default Description
--artifact Path to the artifact
--bundle Path to .sigstore.json bundle
--attestation Path to SLSA attestation
--expected-issuer Expected OIDC issuer for identity verification
--expected-identity Expected 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
Flag Default Description
--dir . Project directory
--output-dir ./forgeseal-output Directory for all generated artifacts
--lockfile (auto-detect) Explicit lockfile path
--sign true Sign artifacts with Sigstore
--attest true Generate SLSA provenance
--vex-triage false Run VEX triage against OSV.dev
--fail-on (none) Fail if vulnerabilities at or above severity: critical, high, medium, low
--include-dev false Include 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

Input Default Description
command pipeline Command to run: pipeline, sbom, sign, attest, vex
dir . Project directory
output-dir ./forgeseal-output Output directory for artifacts
sign true Sign artifacts with Sigstore keyless signing
attest true Generate SLSA v1 provenance attestation
vex-triage true Run VEX vulnerability triage against OSV.dev
fail-on (none) Fail if vulnerabilities at or above severity: critical, high, medium, low
include-dev false Include devDependencies in SBOM
version latest forgeseal version to install

Action Outputs

Output Description
sbom-path Path to the generated CycloneDX SBOM
bundle-path Path to the Sigstore signature bundle
attestation-path Path to the SLSA provenance attestation
vex-path Path to the VEX document
vuln-count Number 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

File Content
sbom.cdx.json CycloneDX SBOM (JSON)
sbom.cdx.json.sigstore.json Sigstore bundle for the SBOM
sbom.cdx.json.intoto.jsonl SLSA v1 provenance attestation
sbom.cdx.json.intoto.jsonl.sigstore.json Sigstore bundle for the attestation
vex.json OpenVEX 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