Trusted Publishing verifier for package URLs (purl)
tpverify is a CLI tool and library that analyzes packages to check for Trusted Publishing mechanisms like provenance attestations, OIDC-based publishing, and Sigstore signatures. It returns objective verification data, allowing you to make your own trust decisions.
- β PURL-based interface: Uses standard Package URLs for package identification
- π Multi-ecosystem support: npm, PyPI, RubyGems, Maven Central, and NuGet provenance verification
- π Signal-based reporting: Returns objective verification data without subjective scoring
- π Extensible architecture: Easy to add support for more ecosystems
- π¦ Dual interface: Use as a CLI tool or as a library in your own projects
# Clone the repository
git clone https://github.com/Pirikara/tpverify.git
cd tpverify
# Install dependencies
pnpm install
# Build the project
pnpm build
# Link for global usage (optional)
pnpm link --global# Check a specific package version
tpverify pkg:npm/sigstore@2.1.0
# Output:
# PURL: pkg:npm/sigstore@2.1.0
# Ecosystem: npm
# Package: sigstore
# Version: 2.1.0
#
# Signals:
# β npm.provenance.exists
# Package has 2 provenance attestation(s)
# β’ npm.repository.github
# Repository hosted on GitHub
# Value: git+https://github.com/sigstore/sigstore-js.gittpverify pkg:npm/left-pad@1.3.0 --format json
# Returns structured JSON with all signals# Use URL-encoded format for scoped packages
tpverify pkg:npm/%40typescript-eslint/parser@8.0.0import { inspectPurl } from "tpverify";
const report = await inspectPurl("pkg:npm/sigstore@2.1.0");
console.log(`Package: ${report.packageName}@${report.version}`);
console.log("Verification signals:");
for (const signal of report.signals) {
console.log(` ${signal.id}: ${signal.description}`);
if (signal.value !== null && typeof signal.value !== "boolean") {
console.log(` Value: ${signal.value}`);
}
}
// Make your own trust decision based on the signals
const hasProvenance = report.signals.some(
(s) => s.id === "npm.provenance.exists" && s.value === true
);
console.log(`Has provenance: ${hasProvenance}`);Inspects a package URL and returns verification signals.
Parameters:
purl: Package URL in the formatpkg:<ecosystem>/<name>@<version>
Returns:
interface TrustedPublishingReport {
purl: string;
ecosystem: string;
packageName: string;
version: string;
signals: TrustedPublishingSignal[];
rawMetadata?: unknown;
}
interface TrustedPublishingSignal {
id: string; // Signal identifier (e.g., "npm.provenance.exists")
value: unknown; // Signal value (boolean, string, null, etc.)
description: string; // Human-readable description
}tpverify checks packages for various trust signals. You decide which signals are important for your use case.
| Signal ID | Description |
|---|---|
npm.provenance.exists |
Package has npm provenance attestations (Sigstore) |
npm.repository.github |
Repository URL (GitHub or other) |
| Signal ID | Description |
|---|---|
pypi.attestations.exists |
Package has PEP 740 provenance attestations |
pypi.project.github |
Project URL (GitHub or other) |
| Signal ID | Description |
|---|---|
rubygems.trusted_publishing.supported |
RubyGems supports Trusted Publishing (OIDC) but verification data is not available via public API |
rubygems.mfa.required |
Package requires MFA for publishing |
rubygems.source_code.github |
Source code URL (GitHub or other) |
rubygems.sha256.exists |
Package has SHA256 checksum |
| Signal ID | Description |
|---|---|
maven.sigstore.exists |
Package has Sigstore attestation bundle (.sigstore.json) |
maven.scm.github |
Source code repository hosted on GitHub |
| Signal ID | Description |
|---|---|
nuget.trusted_publishing.supported |
NuGet.org supports Trusted Publishing (OIDC) for secure package publishing |
nuget.repository.github |
Package repository hosted on GitHub |
tpverify provides objective data; you make the trust decision. For example:
const report = await inspectPurl("pkg:npm/some-package@1.0.0");
// Example 1: Require provenance
const hasProvenance = report.signals.some(
(s) => s.id === "npm.provenance.exists" && s.value === true
);
if (!hasProvenance) {
throw new Error("Package does not have provenance attestations");
}
// Example 2: Check for GitHub repository
const hasGitHub = report.signals.some(
(s) => s.id === "npm.repository.github" &&
typeof s.value === "string" &&
s.value.includes("github.com")
);
// Example 3: Custom logic combining signals
const isTrusted = report.signals.some(s => s.id === "npm.provenance.exists" && s.value === true) &&
report.signals.some(s => s.id === "npm.repository.github");- β npm: Full support with provenance verification
- β PyPI: Full support with PEP 740 attestations
- β RubyGems: Support via indirect security indicators (Trusted Publishing exists but not verifiable via API)
- β Maven Central: Full support with Sigstore attestation bundles
- β NuGet: Support via Trusted Publishing indicators
# npm package (with provenance)
tpverify pkg:npm/sigstore@2.1.0
# β npm.provenance.exists
# β’ npm.repository.github
# PyPI package
tpverify pkg:pypi/requests@2.31.0
# β pypi.attestations.exists
# β’ pypi.project.github
# RubyGems package (with MFA)
tpverify pkg:gem/rails@8.1.1
# β’ rubygems.trusted_publishing.supported
# β rubygems.mfa.required
# β’ rubygems.source_code.github
# β’ rubygems.sha256.exists
# Maven Central package (with Sigstore)
tpverify pkg:maven/org.leplus/ristretto@2.0.0
# β maven.sigstore.exists
# β’ maven.scm.github
# NuGet package
tpverify pkg:nuget/Newtonsoft.Json@13.0.3
# β nuget.trusted_publishing.supported# Install dependencies
pnpm install
# Build
pnpm build
# Run tests
pnpm test
# Run CLI in development mode
pnpm dev pkg:npm/some-package@1.0.0MIT