| name | supply-chain-secure-publish |
| description | Use when publishing Rust crates to crates.io or managing crate repositories. Provides supply chain attack countermeasures including crates.io token management, CI/CD publishing pipelines, and pre-publish security checklists. Adapted from Shai-Hulud npm attack lessons. |
| allowed-tools | Read, Write, Edit, Bash, Grep, Glob |
| user-invocable | true |
Supply Chain Secure Publish (Cargo / crates.io)
This skill provides guidelines for securely publishing and maintaining Rust crates on crates.io, adapted from the Shai-Hulud supply chain attacks. While crates.io has different mechanics than npm, the same principles of token management, CI/CD security, and access control apply.
When to Apply
Apply these guidelines when:
- Publishing crate updates to crates.io
- Managing crates.io API tokens
- Setting up CI/CD publishing pipelines
- Auditing existing crate publishing workflows
crates.io vs npm Publishing
| Aspect | npm | crates.io |
|---|
| Publish | npm publish | cargo publish |
| Auth | npm token | crates.io API token |
| 2FA | Supported | NOT yet supported (as of 2025) |
| Unpublish | Within 72 hours | Yank only (code remains available) |
| Provenance | npm provenance (OIDC) | Not yet available |
| Build scripts | preinstall/postinstall | build.rs included in package |
| Immutability | Mutable (can unpublish + republish) | Immutable (yank does not delete) |
Key Risk
crates.io does NOT support 2FA for publishing. This makes token security even more critical than npm.
crates.io Token Management
Token Types
cargo login -- --token-type scoped --scope publish:<crate-name>
Token Security Rules
| Rule | Implementation |
|---|
| Scope to specific crates | Never use tokens with access to all crates |
| Set expiration | 90 days maximum |
| Use CI-specific tokens | Separate tokens for CI and local development |
| Store in secrets manager | Never in files, env vars in CI only |
| Rotate regularly | At least every 90 days |
Token Storage
| Location | Safe? | Notes |
|---|
~/.cargo/credentials.toml | RISKY | Shai-Hulud-style malware targets credential files |
| CI/CD secrets | YES | Encrypted, scoped, auditable |
| Environment variable in CI | YES | Ephemeral, per-job |
Credential File Protection
chmod 600 ~/.cargo/credentials.toml
test -f .cargo/credentials.toml && echo "WARNING: credentials in project directory!"
Pre-Publish Security Checklist
1. Package Contents Audit
cargo package --list
cargo package --no-verify
ls -la target/package/*.crate
Verify:
2. Cargo.toml include/exclude
Use include (allowlist) rather than exclude (denylist):
[package]
include = [
"src/**/*",
"Cargo.toml",
"LICENSE",
"README.md",
]
3. build.rs Review
4. Dependency Review
cargo audit
cargo deny check
cargo build --locked
CI/CD Publishing Pipeline
Secure Release Workflow
name: Publish
on:
push:
tags:
- 'v*'
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@<SHA>
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@<SHA>
with:
toolchain: stable
- name: Audit
run: |
cargo install cargo-audit
cargo audit
- name: Test
run: cargo test --locked
- name: Clippy
run: cargo clippy --locked -- -D warnings
- name: Verify package
run: cargo package --locked
- name: Publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
run: cargo publish --locked
Pipeline Security Rules
| Rule | Implementation |
|---|
| Tag-triggered only | Only publish on version tags |
| Locked build | --locked on all cargo commands |
| Audit gate | cargo audit must pass |
| Test gate | All tests must pass |
| Minimal permissions | contents: read only |
| Scoped token | Token scoped to specific crate |
| Timeout | timeout-minutes on all jobs |
Yanking (crates.io's Alternative to Unpublish)
If a compromised version is published:
cargo yank --version 1.2.3
Yanking vs npm Unpublish
| Action | crates.io (yank) | npm (unpublish) |
|---|
| New installs | Blocked | Blocked |
| Existing users | Still works | Broken |
| Code deletion | NO - code remains | YES - code removed |
| Version reuse | NO - cannot reuse | YES (after 24h) |
crates.io's immutability is a security advantage: once a known-good version is published, it cannot be replaced with a malicious version.
Multi-Owner Security
Crate Ownership Management
cargo owner --list <crate>
cargo owner --add <github-user> <crate>
cargo owner --remove <github-user> <crate>
Rules
- Minimize owners - only people who actively publish
- Use team ownership where possible
- Monitor ownership changes on crates.io
- All owners must use scoped tokens
- Rotate tokens when team members leave
Emergency Response: Crate Compromise
-
Yank the compromised version immediately:
cargo yank --version <compromised-version>
-
Revoke ALL crates.io tokens:
- crates.io -> Account Settings -> API Tokens -> Revoke
-
Publish a clean patch version from verified source
-
Rotate GitHub tokens and secrets
-
File a RustSec advisory: https://github.com/RustSec/advisory-db
-
Notify downstream users via crate documentation and GitHub
References