Project node-addon-slsa

Expand description

GitHub repo npm version API docs Ask DeepWiki CI status Test coverage Supply-chain score

node-addon-slsa

Verifies that an npm package and its prebuilt native addon binary were produced by the same GitHub Actions workflow run. Uses sigstore for npm provenance and the Rekor transparency log for binary verification. Aborts npm install with a SECURITY error if any check fails.

No authentication required. No GITHUB_TOKEN.

Private repositories: the attest-public action logs repository name, workflow paths, commit SHAs, and run URLs to the public Rekor transparency log. Source code stays private.

Trusts GitHub Actions (build environment, attestation authority) and the sigstore public-good instance (Fulcio CA, Rekor). If either is compromised, verification may pass for malicious artifacts.

Threat Mitigation
Tampered npm package sigstore provenance verification
Tampered GitHub release Rekor transparency log + sigstore
Mismatched artifacts Same workflow run check via Run Invocation URI
Man-in-the-middle on download SHA-256 hash verified against signed attestation
Path traversal via addon.path Resolved path must stay within package directory
  • Compromised CI workflow — attestations will be valid for malicious code. This tool verifies provenance, not intent.
  • Compromised maintainer account — write access to the repository allows producing legitimately attested malicious builds.
  • Dependency confusion — verifies a single package, not its transitive dependency tree.
  • Version 0.0.0 — verification is skipped (local development). Never publish 0.0.0 to npm.
{
"name": "my-native-addon",
"version": "1.0.0",
"repository": {
"url": "git+https://github.com/owner/repo.git"
},
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"addon": {
"path": "./dist/my_addon.node",
"url": "https://github.com/owner/repo/releases/download/v{version}/my_addon-v{version}-{platform}-{arch}.node.gz"
},
"scripts": {
"postinstall": "slsa wget",
"pack-addon": "slsa pack"
},
"dependencies": {
"node-addon-slsa": "0.7.1"
}
}
  • addon.path — where the addon is installed (relative to package root)
  • addon.url — download template; {version}, {platform}, {arch} resolve at install time. Any origin is accepted — verification is hash-based against the sigstore/Rekor attestation, so the download host is a mirror, not a trust anchor. GitHub Releases is the usual choice; custom CDNs work the same as long as the bytes match.
  • postinstallslsa wget downloads, verifies, and installs the binary on npm install. Pair it with requireAddon: pnpm ≥ 10 blocks postinstall scripts by default, so consumers may never run this hook.
  • pack-addonslsa pack gzip-compresses the binary for release
  • repositorygithub.com URL (HTTPS, SSH, with or without .git). Determines the expected source repository for attestation checks.
jobs:
build-addon:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, macos-15, windows-2025]
runs-on: ${{ matrix.os }}
permissions:
contents: write # release upload
id-token: write # OIDC token for sigstore
attestations: write # build provenance
steps:
- uses: actions/checkout@v6
# ... set up toolchain, build native addon ...
- name: Compress binary for release
run: npx slsa pack
- name: Attest binary provenance
uses: vadimpiven/node-addon-slsa/attest-public@<commit-sha> # pin to SHA
with:
subject-path: dist/my_addon-v*.node.gz
- name: Upload binary to release
uses: softprops/action-gh-release@v2
with:
files: dist/my_addon-v*.node.gz

publish:
needs: build-addon
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # npm provenance via OIDC
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm publish --provenance --access public

Pin the attest-public action to a commit SHA, not a mutable tag.

Each matrix runner produces a platform-specific binary. The {platform} and {arch} placeholders resolve to process.platform and process.arch at install time.

import { requireAddon } from "node-addon-slsa";

type MyAddon = { greet(name: string): string };

export const addon = await requireAddon<MyAddon>();

Walks up from the caller's file to the enclosing package.json, then downloads and provenance-verifies the binary if missing. Subsequent calls are a stat plus require — safe to invoke at module load.

  • T defaults to unknown; supply the addon's type at the call site.
  • Pass { from: import.meta.url } when the caller lives outside the consuming package (e.g. a re-export wrapper).
  • RequireAddonOptions extends VerifyOptions; see error handling for failure modes.
Command / Option Purpose
slsa wget Download, verify, and install the native addon
slsa pack Gzip-compress the native addon for release
--help, -h Show usage information
SLSA_DEBUG=1 Debug logging to stderr
import {
verifyPackageProvenance,
verifyAddonProvenance,
requireAddon,
isProvenanceError,
sha256Hex,
semVerString,
githubRepo,
} from "node-addon-slsa";
import type { PackageProvenance, VerifyOptions } from "node-addon-slsa";

// Verify npm package provenance via sigstore.
// Returns { runInvocationURI, verifyAddon() }.
const provenance: PackageProvenance = await verifyPackageProvenance({
packageName: "my-native-addon",
version: semVerString("1.0.0"),
repo: githubRepo("owner/repo"),
});

// Verify the addon binary was produced by the same workflow run.
await provenance.verifyAddon({ sha256: sha256Hex(hexHash) });

// Standalone binary verification when you already have a URI.
await verifyAddonProvenance({
sha256: sha256Hex(hexHash),
runInvocationURI,
repo: githubRepo("owner/repo"),
});

// Runtime loader: verify-on-demand, then require the addon.
// Supply the addon's type as T (defaults to `unknown`).
const addon = await requireAddon<MyAddon>();
Type Constructor Purpose
GitHubRepo githubRepo(value) GitHub owner/repo slug
SemVerString semVerString(value) Strict semver (no v prefix)
Sha256Hex sha256Hex(value) Lowercase hex-encoded SHA-256 (64 chars)
RunInvocationURI runInvocationURI(value) GitHub Actions run invocation URL

Constructors validate at runtime and throw TypeError on invalid input.

All options have sensible defaults. Pass only what you need:

await verifyPackageProvenance({
packageName: "my-native-addon",
version: semVerString("1.0.0"),
repo: githubRepo("owner/repo"),
// All below are optional:
timeoutMs: 60_000, // per-request timeout (default: 30s)
retryCount: 5, // retries after first attempt (default: 2)
trustMaterial, // pre-loaded via loadTrustMaterial()
dispatcher, // custom undici Dispatcher
});
  • ProvenanceError — verification failed (tampered artifact, mismatched provenance). Do not retry.
  • Error — transient issue (network timeout, service unavailable). Safe to retry.
try {
await provenance.verifyAddon({ sha256 });
} catch (err) {
if (isProvenanceError(err)) {
// Security failure — do not use this package version
} else {
// Transient — safe to retry
}
}
  • Node.js >=22.12.0
  • npm package published with --provenance
  • Binary attested with vadimpiven/node-addon-slsa/attest-public

Namespaces§

Dispatcher

Type Aliases§

BundleVerifier

Sigstore bundle verifier created by createVerifier() from the sigstore package.

FetchOptions

Options controlling HTTP fetch behavior (timeouts, retries, cancellation).

GitHubRepo

GitHub owner/repo slug.

PackageProvenance

Returned by verifyPackageProvenance after npm provenance checks pass.

RequireAddonOptions
RunInvocationURI
SemVerString

Strict semver string (no v prefix): major.minor.patch[-pre][+build]. The template literal type is intentionally wider than the runtime check in semVerString because TypeScript cannot express the full regex.

Sha256Hex
TrustMaterial
VerifyOptions

Verification options: extends FetchOptions with attestation-specific limits.

Functions§

githubRepo

Type constructors.

isProvenanceError

Error handling.

loadTrustMaterial

Verification.

requireAddon

Runtime addon loader.

runInvocationURI

Type constructors.

semVerString

Type constructors.

sha256Hex

Type constructors.

verifyAddonProvenance

Verification.

verifyPackageProvenance

Verification.

Classes§

ProvenanceError

Thrown when provenance verification detects a security issue. The message is prefixed with SECURITY: and includes remediation advice.

Interfaces§

Dispatcher

Dispatcher is the core API used to dispatch requests.