chrisgillham/oss-trust-framework
GitHub: chrisgillham/oss-trust-framework
Stars: 0 | Forks: 0
# OSS Trust Framework
**Open Source Supply Chain Trust Validation Pipeline**
A multi-gate security framework that validates open source dependency updates before they reach your application — including hardened defenses against CI/CD pipeline compromise attacks (Miasma, Shai-Hulud, TanStack, Bitwarden) and a strictly controlled expedited lane for zero-day CVE patches.
## Why This Exists
Two distinct supply chain attack patterns are defeating traditional defenses:
**Pattern 1 — Speed attacks.** A compromised maintainer account publishes a malicious release. Automated dependency tooling ingests it within minutes, before anyone notices. The attacker wins the race.
**Pattern 2 — CI/CD pipeline compromise (Miasma class).** An attacker compromises a legitimate employee's GitHub account, pushes orphan commits bypassing PR review, and exploits `id-token: write` CI/CD permissions to publish backdoored packages via OIDC trusted publishing. The packages are *correctly signed* — the signature is real. Traditional signature verification passes. The attack is invisible to every conventional control.
This framework addresses both patterns with layered, independent gates — most of which cannot be bypassed even by a compromised maintainer or CI/CD pipeline.
## Key Benefits
### Stops attacks that valid signatures can't catch
The Miasma class of attacks produces packages with legitimate Sigstore signatures — because the attacker controls a real CI/CD pipeline. Gate 2.5 catches this by auditing *how* the publish happened: was there a PR? Did it go through normal merge? Does the attacker need to compromise *multiple* independent systems simultaneously to defeat it?
### Structural defense — not just better tooling
Each gate queries sources that are architecturally independent of the compromised repository. An attacker controlling a single GitHub account cannot manipulate NVD, OSV, OpenSSF Scorecard, and the npm provenance attestation registry at the same time. Defeating this framework requires compromising multiple independent systems simultaneously.
### Zero-day patches move fast without moving unsafely
The 72-hour age hold is the highest-ROI control in the framework, but it creates a gap when a legitimate zero-day patch drops. The expedited lane bypasses *only* the age gate, with machine-verified CVE confirmation, 2-of-3 MFA quorum approval, and a 6-hour token TTL. Speed and safety are not in conflict.
### Fully auditable by design
Every gate decision, zero-day exception, and approval event emits a structured SIEM event. Ticket linkage is mandatory for exceptions. Monthly retrospectives are enforced by circuit breaker. The framework is designed to be examined — including by auditors who weren't in the room when decisions were made.
### Drop-in CI/CD integration
The GitHub Actions workflow (`dep-trust-check.yml`) fires automatically on any lock file change, comments gate results on the PR, and fails the build on block or quarantine outcomes. No per-repo configuration needed once the framework is deployed at the org level.
## Architecture
Dependency update request
│
▼
┌──────────────────────┐ < 24 h, no CVE ──► BLOCKED
│ Gate 1: Age Hold │
│ 24 h hard block │ Zero-day CVE filed? ──► Expedited Lane ──────────────┐
│ 72 h soft hold │ │
└──────────┬───────────┘ │
│ ≥ 24 h │
▼ │
┌──────────────────────┐ Repo mismatch ──► BLOCKED ◄── Miasma: fork/ │
│ Gate 2: Provenance │ employee acct │
│ Attestation + │ No attestation ──► QUARANTINE │
│ Publisher Allowlist │ (sourceRepositoryURI vs trusted_publishers.yaml) │
└──────────┬───────────┘ │
│ │
▼ │
┌──────────────────────┐ Orphan commit ──► BLOCKED ◄── Miasma: direct push │
│ Gate 2.5: CI/CD │ id-token:write ──► QUARANTINE ◄── Miasma: OIDC abuse │
│ Pipeline Audit │ No PR review ──► BLOCKED ◄── Miasma: no oversight│
│ [2.5a] Orphan commits │
│ [2.5b] Workflow perms │
│ [2.5c] PR provenance│ │
└──────────┬───────────┘ │
│ ◄── Rejoins here ────────┘
▼
┌──────────────────────┐ Score < threshold ──► QUARANTINE
│ Gate 3: Out-of-Band │ Active CVE ──► QUARANTINE
│ Trust Aggregation │ (OpenSSF · OSV · deps.dev · GHSA)
└──────────┬───────────┘
│
▼
┌──────────────────────┐ New transitive dep ──► QUARANTINE
│ Gate 4: SBOM Delta │ Hash mismatch ──► QUARANTINE
│ + Hash Pin │
└──────────┬───────────┘
│
▼
┌──────────────────────┐ Cloud cred harvest ──► BLOCKED ◄── Miasma: IMDS/OIDC
│ Gate 5: Behavioral │ Registry publish ──► BLOCKED ◄── Miasma: re-publish
│ Sandbox (gVisor) │ Other malicious ──► BLOCKED
│ + Miasma Patterns │
└──────────┬───────────┘
│
▼
┌──────────────┐
│ Staged Rollout│──► APPROVED
│ 72 h canary │ (ZD lane: immediate + 48 h alert window)
└──────────────┘
### Zero-Day Expedited Lane
Bypasses the **age gate only**. All other gates remain mandatory.
CVE validated (NVD + OSV + GHSA — 2-of-3 independent sources required)
│
▼
Quorum approval (2-of-3 named approvers · MFA required · requester excluded)
│
▼
Provenance attestation + timing check (must postdate CVE publication)
│
▼
CI/CD audit Gates 2.5a–c (mandatory — compromised-account patches are still caught)
│
▼
Behavioral sandbox (gVisor · no network · Miasma patterns active)
│
▼
Audit record (SIEM event + ticket link mandatory before deploy)
│
▼
Immediate full-fleet deploy + 48 h elevated alert window
## Quickstart
pip install oss-trust-framework
# Run the full pipeline against a single package
oss-trust check \
--package requests \
--version 2.32.3 \
--ecosystem PyPI \
--github-repo psf/requests
# Request a zero-day expedited exception
oss-trust zeroday request \
--cve CVE-2024-XXXXX \
--package requests \
--version 2.32.4 \
--requester security@yourorg.com
# Approve a pending zero-day request (run by each named approver)
oss-trust zeroday approve \
--request-id abc123def456 \
--approver-id approver_001 \
--mfa-token 123456
# Check request status
oss-trust zeroday status --request-id abc123def456
## Installation
# From PyPI
pip install oss-trust-framework
# From source
git clone https://github.com/YOUR_ORG/oss-trust-framework
cd oss-trust-framework
pip install -e ".[dev]"
cp .env.example .env
## Configuration
### Core pipeline settings (`config/pipeline.yaml`)
age_gate:
hard_block_hours: 24 # < 24 h: auto-blocked regardless of source
hold_hours: 72 # 24–72 h: human approval required
cicd_audit: # Gate 2.5 — Miasma/Shai-Hulud controls
orphan_commits:
enabled: true
action_on_orphan: block
workflow_permissions:
enabled: true
action_on_finding: quarantine
pr_provenance:
enabled: true
min_pr_reviewers: 1
action_on_direct_push: block
sandbox:
runtime: gvisor
network: none
behavioral_patterns:
enabled: true
block_on_critical: true # IMDS, OIDC token, credential file reads
zero_day:
required_approvers: 2
token_ttl_hours: 6
circuit_breakers:
max_exceptions_per_24h: 3
### Trusted publisher allowlist (`config/trusted_publishers.yaml`)
Maps package names to their canonical GitHub source repo. A provenance attestation pointing to any other repository is treated as a CRITICAL finding and blocked.
npm:
"express": "expressjs/express"
"@redhat-cloud-services/frontend-components": "RedHatInsights/frontend-components"
PyPI:
"requests": "psf/requests"
"cryptography": "pyca/cryptography"
require_attestation: # Missing attestation = BLOCK (not just quarantine)
npm:
- "@redhat-cloud-services/frontend-components"
Populate this file from your actual dependency graph. Any package not listed still passes Gate 2 — it just doesn't get the repo-match verification.
## CI/CD Integration
### GitHub Actions — automatic PR gate
The included workflow fires on any lock file change (`package-lock.json`, `requirements*.txt`, `Cargo.lock`, `go.sum`) and comments gate results directly on the PR:
- name: Validate dependency update
uses: YOUR_ORG/oss-trust-framework/.github/actions/validate@main
with:
package: ${{ env.PACKAGE_NAME }}
version: ${{ env.PACKAGE_VERSION }}
ecosystem: ${{ env.ECOSYSTEM }}
github-repo: ${{ env.GITHUB_REPO }}
github-token: ${{ secrets.GITHUB_TOKEN }}
See [`.github/workflows/dep-trust-check.yml`](.github/workflows/dep-trust-check.yml) for the full workflow with PR commenting, artifact upload, and matrix strategy across multiple changed packages.
## Gate Reference
| Gate | Controls | Fail action | Bypassable? |
|---|---|---|---|
| **1 — Age** | Release timestamp vs 24 h / 72 h thresholds | Block / Hold | Age only — with CVE + MFA quorum |
| **2 — Provenance** | Sigstore attestation present; `sourceRepositoryURI` matches allowlist | Block (mismatch) · Quarantine (missing) | No |
| **2.5a — Orphan commits** | Release tag commit reachable from default branch via BFS graph walk | Block | No |
| **2.5b — Workflow permissions** | `id-token: write` in publishing workflow without branch protection + CODEOWNERS | Quarantine | No |
| **2.5c — PR provenance** | Release backed by merged PR with ≥ 1 approving reviewer | Block (no PR) · Quarantine (no review) | No |
| **3 — OOB Trust** | OpenSSF Scorecard ≥ threshold; zero active CVEs via OSV + deps.dev + GHSA | Quarantine | No |
| **4 — SBOM delta** | No unexpected transitive deps; lock file hash unchanged | Quarantine | No |
| **5 — Behavioral sandbox** | gVisor install-time execution; 18 named Miasma-class behavioral patterns | Block | No |
## Miasma / Shai-Hulud Attack Coverage
The [Miasma attack](https://devops.com/shai-hulud-clone-miasma-compromises-32-red-hat-npm-packages/) (also observed in TanStack and Bitwarden compromises) uses a legitimate CI/CD pipeline as the attack surface. Packages are correctly signed — conventional signature verification passes. The same fundamental pattern was used across all three incidents.
### How the attack works
1. Attacker compromises a legitimate employee's GitHub account
2. Uses it to push orphan commits (no PR, no review, no merge queue)
3. The repository's CI/CD workflow has `id-token: write` permission
4. Workflow requests a short-lived OIDC token from GitHub and uses it to publish directly to npm's trusted publishing endpoint — no stored secret required
5. The published package is signed with a valid Sigstore signature pointing to the real org's CI pipeline
6. Payload harvests GCP/Azure/AWS cloud identities from the host environment
7. Miasma v2 generates a unique encrypted payload per infection — hash-based IOCs are version-specific and useless across deployments
### How each attack step is blocked
| Attack step | Gate | Mechanism |
|---|---|---|
| Orphan commit pushed, bypassing PR | **2.5a** | BFS walk from default branch tip; tag commit not reachable → BLOCK |
| No code review on malicious commit | **2.5c** | Commit→PR API check; no merged PR → BLOCK |
| `id-token: write` exploited for OIDC publish | **2.5b** | Workflow YAML parsed; dangerous perm + no env protection → QUARANTINE |
| Published from employee account, not canonical org | **2** | `sourceRepositoryURI` in attestation checked against `trusted_publishers.yaml` → BLOCK |
| Cloud credential harvesting at runtime | **5** | MIASMA-001/002 patterns: IMDS and GCP metadata requests → BLOCK |
| OIDC token requested from install context | **5** | MIASMA-010: `token.actions.githubusercontent.com` network access → BLOCK |
| Re-publish to npm from install script | **5** | PUBLISH-001: PUT to `registry.npmjs.org` during install → BLOCK |
| Unique encrypted payload defeats hash IOCs | **5** | Behavior-matched, not hash-matched — encryption is irrelevant |
### Why this is structurally hard to defeat
Bypassing this framework requires compromising all of the following simultaneously:
- The package registry's provenance attestation system (Sigstore/npm)
- NVD, OSV, and GitHub Security Advisories (for the zero-day lane)
- OpenSSF Scorecard and deps.dev (for Gate 3)
- The behavioral sandbox runtime (gVisor with no network access)
- The quorum approval process (2-of-3 named individuals with MFA)
No single compromised account, repository, or CI/CD pipeline is sufficient.
## Zero-Day Lane Circuit Breakers
The expedited lane automatically suspends under these conditions to prevent abuse or normalization of the bypass path:
| Condition | Action |
|---|---|
| > 3 exception requests in 24 hours | Lane suspended pending CISO review |
| Same requester files two exceptions within 48 hours | Second request escalates to CISO sign-off |
| Any exception-deployed package receives a new CVE within 30 days | Lane suspended; retrospective triggered |
| Monthly retrospective finds process violations | Lane suspended until remediation confirmed |
Exception tokens expire after 6 hours. Re-approval is required after expiry — there is no token extension.
## Out-of-Band Trust Sources (Gate 3)
All sources queried independently of the package repository. A compromised repo cannot influence these results.
| Source | API endpoint | What it provides |
|---|---|---|
| OpenSSF Scorecard | `api.securityscorecards.dev` | Automated security hygiene score (CI, branch protection, code review, signing) |
| deps.dev (Google) | `api.deps.dev/v3alpha/...` | Dependency graph, version history velocity, known advisories |
| OSV.dev | `api.osv.dev/v1/query` | Cross-ecosystem CVE database — checks if patch version appears in fixed list |
| GitHub Security Advisories | `api.github.com/advisories` | Manually reviewed, high-confidence signal |
| npm Advisory DB | `npm audit` | npm-specific compromise and vulnerability history |
## Behavioral Patterns (Gate 5)
18 named patterns covering the Miasma attack profile. Matched by event type, not payload hash — encryption and obfuscation do not defeat pattern matching.
| Pattern ID | Category | Severity | Description |
|---|---|---|---|
| MIASMA-001 | Cloud metadata | CRITICAL | AWS/Azure IMDS request (169.254.169.254) |
| MIASMA-002 | Cloud metadata | CRITICAL | GCP metadata server request |
| MIASMA-010 | OIDC token | CRITICAL | GitHub Actions OIDC token endpoint request |
| MIASMA-011 | OIDC token | HIGH | Google Cloud OIDC token request |
| PUBLISH-001 | Registry publish | CRITICAL | npm PUT during package install |
| PUBLISH-002 | Registry publish | CRITICAL | PyPI upload during package install |
| CRED-001 | Credential file | CRITICAL | Kubernetes service account token read |
| CRED-002–005 | Credential file | HIGH | GCP / AWS / Azure / SSH credential file reads |
| ENV-002 | Env var harvest | CRITICAL | Access to `OIDC_PACKAGES`, `GITHUB_TOKEN`, `CI_TOKEN` |
| PROC-002 | Process injection | CRITICAL | `curl` piped to shell from install script |
## Project Structure
oss-trust-framework/
├── src/
│ ├── age_check/
│ │ └── checker.py # Gate 1 — multi-ecosystem registry timestamp fetching
│ ├── signature/
│ │ └── provenance.py # Gate 2 — npm/PyPI attestation + publisher repo allowlist
│ ├── cicd_audit/ # Gate 2.5 — CI/CD pipeline audit (Miasma class)
│ │ ├── orphan_commits.py # 2.5a — BFS commit graph walk; detects direct pushes
│ │ ├── workflow_permissions.py # 2.5b — dangerous perm detection + compensating controls
│ │ └── pr_provenance.py # 2.5c — release must trace to reviewed merged PR
│ ├── trust/
│ │ └── aggregator.py # Gate 3 — concurrent OpenSSF/OSV/deps.dev/GHSA queries
│ ├── sbom/ # Gate 4 — SBOM delta and hash pinning (stub)
│ ├── sandbox/
│ │ └── behavioral_patterns.py # Gate 5 — 18 Miasma-class behavioral signatures
│ ├── zeroday/
│ │ └── validator.py # CVE machine-validation + quorum approval manager
│ └── pipeline/
│ ├── orchestrator.py # Full pipeline runner; standard and zero-day routing
│ └── cli.py # oss-trust check / zeroday request/approve/status
├── tests/
│ ├── test_age_check.py # Gate 1: registry API mocks, threshold boundary cases
│ ├── test_cicd_audit.py # Gate 2.5: orphan, workflow, PR provenance
│ ├── test_behavioral_patterns.py # Gate 5: all Miasma patterns + clean install baseline
│ └── test_zeroday_quorum.py # ZD lane: expiry, duplicate vote, MFA, self-approval
├── config/
│ ├── pipeline.yaml # All thresholds, gate config, circuit breakers
│ └── trusted_publishers.yaml # Publisher repo allowlist (populate from your dep graph)
├── .github/
│ └── workflows/
│ └── dep-trust-check.yml # PR gate: auto-runs on lock file changes
├── .env.example
├── pyproject.toml
├── CONTRIBUTING.md
└── LICENSE
## License
MIT — see [LICENSE](LICENSE).
## References
- [Miasma attack analysis — devops.com](https://devops.com/shai-hulud-clone-miasma-compromises-32-red-hat-npm-packages/)
- [OpenSSF Scorecard](https://securityscorecards.dev)
- [Sigstore / cosign](https://docs.sigstore.dev)
- [OSV — Open Source Vulnerabilities](https://osv.dev)
- [Google deps.dev](https://deps.dev)
- [SLSA Framework](https://slsa.dev)
- [npm provenance attestations](https://docs.npmjs.com/generating-provenance-statements)
- [PyPI provenance / Trusted Publishers](https://docs.pypi.org/trusted-publishers/)
- [gVisor container sandbox](https://gvisor.dev)
- [Socket.dev supply chain analysis](https://socket.dev)