docs consolidation and others

This commit is contained in:
master
2026-01-06 19:02:21 +02:00
parent d7bdca6d97
commit 4789027317
849 changed files with 16551 additions and 66770 deletions

View File

@@ -0,0 +1,308 @@
# Cosign Interoperability Guide
This document describes how to verify StellaOps attestations using [cosign](https://github.com/sigstore/cosign) and how to import cosign-created attestations into StellaOps.
## Overview
StellaOps attestations use the [DSSE (Dead Simple Signing Envelope)](https://github.com/secure-systems-lab/dsse) format and OCI Distribution Spec 1.1 referrers API for attachment, which is compatible with cosign's attestation workflow.
**Sprint Reference:** `SPRINT_20251228_002_BE_oci_attestation_attach` (T6)
## Verifying StellaOps Attestations with Cosign
### Basic Verification
```bash
# Verify any attestation attached to an image
cosign verify-attestation \
--type custom \
--certificate-identity-regexp '.*' \
--certificate-oidc-issuer-regexp '.*' \
registry.example.com/app:v1.0.0
# Verify a specific predicate type
cosign verify-attestation \
--type stellaops.io/predicates/scan-result@v1 \
--certificate-identity-regexp '.*' \
--certificate-oidc-issuer-regexp '.*' \
registry.example.com/app:v1.0.0
```
### Verification with Trust Roots
StellaOps supports both keyless (Sigstore Fulcio) and key-based signing:
#### Keyless Verification (Sigstore)
```bash
# Verify attestation signed with keyless mode
cosign verify-attestation \
--type stellaops.io/predicates/scan-result@v1 \
--certificate-identity 'scanner@stellaops.io' \
--certificate-oidc-issuer 'https://oauth2.sigstore.dev/auth' \
registry.example.com/app:v1.0.0
```
#### Key-Based Verification
```bash
# Verify attestation signed with a specific key
cosign verify-attestation \
--type stellaops.io/predicates/scan-result@v1 \
--key /path/to/public-key.pem \
registry.example.com/app:v1.0.0
```
### Rekor Transparency Log Verification
When StellaOps attestations are recorded in Rekor, cosign automatically verifies the inclusion proof:
```bash
# Verify with Rekor inclusion proof
cosign verify-attestation \
--type stellaops.io/predicates/scan-result@v1 \
--certificate-identity-regexp '.*' \
--certificate-oidc-issuer-regexp '.*' \
--rekor-url https://rekor.sigstore.dev \
registry.example.com/app:v1.0.0
# Skip Rekor verification (offline environments)
cosign verify-attestation \
--type stellaops.io/predicates/scan-result@v1 \
--key /path/to/public-key.pem \
--insecure-ignore-tlog \
registry.example.com/app:v1.0.0
```
## StellaOps Predicate Types
StellaOps uses the following predicate type URIs:
| Predicate Type | Description | cosign `--type` |
|----------------|-------------|-----------------|
| `stellaops.io/predicates/scan-result@v1` | Vulnerability scan results | `stellaops.io/predicates/scan-result@v1` |
| `stellaops.io/predicates/sbom@v1` | Software Bill of Materials | `stellaops.io/predicates/sbom@v1` |
| `stellaops.io/predicates/vex@v1` | Vulnerability Exploitability eXchange | `stellaops.io/predicates/vex@v1` |
| `https://slsa.dev/provenance/v1` | SLSA Provenance | `slsaprovenance` |
### Predicate Type Aliases
For convenience, cosign supports type aliases:
```bash
# These are equivalent for SLSA provenance
cosign verify-attestation --type slsaprovenance ...
cosign verify-attestation --type https://slsa.dev/provenance/v1 ...
```
## Importing Cosign Attestations into StellaOps
StellaOps can consume attestations created by cosign:
### CLI Import
```bash
# Fetch cosign attestation and import to StellaOps
cosign download attestation registry.example.com/app:v1.0.0 > attestation.json
# Import into StellaOps
stella attest import \
--envelope attestation.json \
--image registry.example.com/app:v1.0.0
```
### API Import
```bash
curl -X POST https://stellaops.example.com/api/v1/attestations/import \
-H "Content-Type: application/json" \
-d @attestation.json
```
## Annotation Compatibility
StellaOps uses the following annotations on attestation manifests:
| Annotation Key | Description | Cosign Equivalent |
|----------------|-------------|-------------------|
| `org.opencontainers.image.created` | Creation timestamp | Standard OCI |
| `dev.stellaops/predicate-type` | Predicate type URI | `dev.cosignproject.cosign/predicateType` |
| `dev.stellaops/tenant` | StellaOps tenant ID | Custom |
| `dev.stellaops/scan-id` | Associated scan ID | Custom |
| `dev.sigstore.cosign/signature` | Signature placeholder | Standard Sigstore |
### Custom Annotations
You can add custom annotations when attaching attestations:
```bash
# Stella CLI with custom annotations
stella attest attach \
--image registry.example.com/app:v1.0.0 \
--attestation scan.json \
--annotation "org.example/team=security" \
--annotation "org.example/policy-version=2.0"
```
## Media Types
StellaOps attestations use standard media types:
| Media Type | Usage |
|------------|-------|
| `application/vnd.dsse.envelope.v1+json` | DSSE envelope containing attestation |
| `application/vnd.in-toto+json` | In-toto attestation payload |
| `application/vnd.oci.image.manifest.v1+json` | OCI manifest for referrers |
## Trust Root Configuration
### Sigstore Trust Roots
For keyless verification, configure the Sigstore trust bundle:
```yaml
# stellaops.yaml
attestation:
trustRoots:
sigstore:
enabled: true
fulcioUrl: https://fulcio.sigstore.dev
rekorUrl: https://rekor.sigstore.dev
ctlogUrl: https://ctfe.sigstore.dev
```
### Custom Trust Roots
For enterprise deployments with private Sigstore instances:
```yaml
# stellaops.yaml
attestation:
trustRoots:
sigstore:
enabled: true
fulcioUrl: https://fulcio.internal.example.com
rekorUrl: https://rekor.internal.example.com
trustedRootPem: /etc/stellaops/sigstore-root.pem
```
### Air-Gapped Environments
For offline verification:
```yaml
# stellaops.yaml
attestation:
trustRoots:
offline: true
bundlePath: /etc/stellaops/trust-bundle.json
```
## Policy Integration
Attestation verification can be integrated into admission control policies:
### Gatekeeper/OPA Policy Example
```rego
package stellaops.attestation
deny[msg] {
input.kind == "Pod"
container := input.spec.containers[_]
image := container.image
# Require scan attestation
not has_valid_attestation(image, "stellaops.io/predicates/scan-result@v1")
msg := sprintf("Image %v missing valid scan attestation", [image])
}
has_valid_attestation(image, predicate_type) {
attestation := stellaops.get_attestation(image, predicate_type)
stellaops.verify_attestation(attestation)
}
```
### Kyverno Policy Example
```yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-stellaops-attestation
spec:
validationFailureAction: Enforce
rules:
- name: check-scan-attestation
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "*"
attestations:
- predicateType: stellaops.io/predicates/scan-result@v1
attestors:
- entries:
- keyless:
issuer: https://oauth2.sigstore.dev/auth
subject: scanner@stellaops.io
```
## Troubleshooting
### Common Issues
#### No Attestations Found
```bash
# List all attestations attached to an image
cosign tree registry.example.com/app:v1.0.0
# Or use stella CLI
stella attest oci-list --image registry.example.com/app:v1.0.0
```
#### Signature Verification Failed
Check that you're using the correct verification key or identity:
```bash
# Inspect the attestation to see signer identity
cosign verify-attestation \
--type stellaops.io/predicates/scan-result@v1 \
--certificate-identity-regexp '.*' \
--certificate-oidc-issuer-regexp '.*' \
--output text \
registry.example.com/app:v1.0.0 | jq '.optional.Issuer, .optional.Subject'
```
#### Rekor Entry Not Found
If the attestation was created without Rekor submission:
```bash
cosign verify-attestation \
--insecure-ignore-tlog \
--key /path/to/public-key.pem \
registry.example.com/app:v1.0.0
```
### Debug Mode
Enable verbose output for troubleshooting:
```bash
COSIGN_EXPERIMENTAL=1 cosign verify-attestation \
--verbose \
--type stellaops.io/predicates/scan-result@v1 \
registry.example.com/app:v1.0.0
```
## References
- [Cosign Documentation](https://docs.sigstore.dev/cosign/overview/)
- [DSSE Specification](https://github.com/secure-systems-lab/dsse)
- [In-toto Attestation Framework](https://in-toto.io/)
- [OCI Distribution Spec 1.1 Referrers](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers)
- [StellaOps Attestor Architecture](../modules/attestor/architecture.md)

View File

@@ -0,0 +1,217 @@
# SBOM Interoperability Testing
## Overview
StellaOps SBOM interoperability tests ensure compatibility with third-party security tools in the ecosystem. The tests validate that StellaOps-generated SBOMs can be consumed by popular tools like Grype, and that findings parity remains above 95%.
## Test Coverage
### SBOM Formats
| Format | Version | Status | Parity Target |
|--------|---------|--------|---------------|
| CycloneDX | 1.6 | ✅ Supported | 95%+ |
| SPDX | 3.0.1 | ✅ Supported | 95%+ |
### Third-Party Tools
| Tool | Purpose | Version | Status |
|------|---------|---------|--------|
| Syft | SBOM Generation | Latest | ✅ Compatible |
| Grype | Vulnerability Scanning | Latest | ✅ Compatible |
| cosign | Attestation | Latest | ✅ Compatible |
## Parity Expectations
### What is Parity?
Parity measures how closely StellaOps vulnerability findings match those from third-party tools like Grype when scanning the same SBOM.
**Formula:**
```
Parity % = (Matching Findings / Total Unique Findings) × 100
```
**Target:** ≥95% parity for both CycloneDX and SPDX formats
### Known Differences
The following differences are **acceptable** and expected:
#### 1. VEX Application
- **Difference:** StellaOps applies VEX documents, Grype may not
- **Impact:** StellaOps may show fewer vulnerabilities
- **Acceptable:** Yes - this is a feature, not a bug
#### 2. Feed Coverage
- **Difference:** Tool-specific vulnerability databases
- **Examples:**
- StellaOps may have distro-specific feeds Grype lacks
- Grype may have GitHub Advisory feeds StellaOps doesn't prioritize
- **Acceptable:** Within 5% tolerance
#### 3. Version Matching Semantics
- **Difference:** Interpretation of version ranges
- **Examples:**
- SemVer vs non-SemVer handling
- Epoch handling in RPM/Debian packages
- **Acceptable:** When using distro-native comparators
#### 4. Package Identification (PURL)
- **Difference:** PURL generation strategies
- **Examples:**
- `pkg:npm/package` vs `pkg:npm/package@version`
- Namespace handling
- **Acceptable:** When functionally equivalent
## Running Interop Tests
### Prerequisites
Install required tools:
```bash
# Install Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Install cosign
curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
```
### Local Execution
```bash
# Run all interop tests
dotnet test tests/interop/StellaOps.Interop.Tests
# Run CycloneDX tests only
dotnet test tests/interop/StellaOps.Interop.Tests --filter "Format=CycloneDX"
# Run SPDX tests only
dotnet test tests/interop/StellaOps.Interop.Tests --filter "Format=SPDX"
# Run parity tests
dotnet test tests/interop/StellaOps.Interop.Tests --filter "Category=Parity"
```
### CI Execution
Interop tests run automatically on:
- Pull requests affecting scanner or SBOM code
- Nightly schedule (6 AM UTC)
- Manual workflow dispatch
See `.gitea/workflows/interop-e2e.yml` for CI configuration.
## Test Images
The following container images are used for interop testing:
| Image | Purpose | Characteristics |
|-------|---------|-----------------|
| `alpine:3.18` | Distro packages | APK packages, minimal |
| `debian:12-slim` | Distro packages | DEB packages, medium |
| `ubuntu:22.04` | Distro packages | DEB packages, larger |
| `node:20-alpine` | Language packages | NPM packages |
| `python:3.12-slim` | Language packages | Pip packages |
| `golang:1.22-alpine` | Language packages | Go modules |
## Troubleshooting
### Parity Below Threshold
If parity drops below 95%:
1. **Check for feed updates**
- Grype may have newer vulnerability data
- Update StellaOps feeds
2. **Review differences**
- Run parity analysis: `dotnet test --filter "Category=Parity" --logger "console;verbosity=detailed"`
- Categorize differences using `FindingsParityAnalyzer`
3. **Validate with golden corpus**
- Compare against known-good results in `bench/golden-corpus/categories/interop/`
4. **Update acceptable differences**
- Document new acceptable differences in this README
- Adjust tolerance if justified
### Tool Installation Failures
If Syft/Grype/cosign fail to install:
```bash
# Check versions
syft --version
grype --version
cosign version
# Reinstall if needed
rm /usr/local/bin/{syft,grype,cosign}
# Re-run installation commands
```
### SBOM Validation Failures
If SBOMs fail schema validation:
1. Verify format version:
```bash
jq '.specVersion' sbom-cyclonedx.json # Should be "1.6"
jq '.spdxVersion' sbom-spdx.json # Should be "SPDX-3.0"
```
2. Validate against official schemas:
```bash
# CycloneDX
npm install -g @cyclonedx/cdx-cli
cdx-cli validate --input-file sbom-cyclonedx.json
# SPDX (TODO: Add SPDX validation tool)
```
## Continuous Improvement
### Adding New Test Cases
1. Add new image to test matrix in `*RoundTripTests.cs`
2. Update `TestImages` member data
3. Run locally to verify
4. Submit PR with updated tests
### Updating Parity Thresholds
Current threshold: **95%**
To adjust:
1. Document justification in sprint file
2. Update `tolerancePercent` parameter in test calls
3. Update this README
### Tool Version Pinning
Tools are currently installed from `latest`. To pin versions:
1. Update `.gitea/workflows/interop-e2e.yml`
2. Specify version in install commands
3. Document version compatibility in this README
## References
- [CycloneDX 1.6 Specification](https://cyclonedx.org/docs/1.6/)
- [SPDX 3.0.1 Specification](https://spdx.github.io/spdx-spec/v3.0/)
- [Syft Documentation](https://github.com/anchore/syft)
- [Grype Documentation](https://github.com/anchore/grype)
- [cosign Documentation](https://github.com/sigstore/cosign)
## Contacts
For questions about interop testing:
- **Sprint:** SPRINT_5100_0003_0001
- **Owner:** QA Team
- **Dependencies:** Sprint 5100.0001.0002 (Evidence Index)

View File

@@ -0,0 +1,630 @@
# Cosign Integration Guide
> **Status:** Ready for Production
> **Last Updated:** 2025-12-23
> **Prerequisites:** Cosign v2.x, StellaOps CLI v1.5+
---
## Overview
This guide explains how to integrate StellaOps with [Cosign](https://docs.sigstore.dev/cosign/overview/), the signing tool from the Sigstore project. StellaOps can verify and ingest SBOM attestations signed with Cosign, enabling seamless interoperability with the broader supply chain security ecosystem.
**Key Capabilities:**
- ✅ Verify Cosign-signed attestations (SPDX + CycloneDX)
- ✅ Extract SBOMs from Cosign DSSE envelopes
- ✅ Upload attested SBOMs to StellaOps for scanning
- ✅ Offline verification with bundled trust roots
- ✅ Custom trust root configuration for air-gapped environments
---
## Quick Start
### 1. Verify a Cosign-Signed Attestation
```bash
# Verify attestation and extract SBOM
stella attest verify \
--envelope attestation.dsse.json \
--root /path/to/fulcio-root.pem \
--extract-sbom sbom.json
# Upload extracted SBOM for scanning
stella sbom upload \
--file sbom.json \
--artifact myapp:v1.2.3
```
### 2. End-to-End: Cosign → StellaOps
```bash
# Step 1: Sign SBOM with Cosign
cosign attest --predicate sbom.spdx.json \
--type spdx \
--key cosign.key \
myregistry/myapp:v1.2.3
# Step 2: Fetch attestation
cosign verify-attestation myregistry/myapp:v1.2.3 \
--key cosign.pub \
--type spdx \
--output-file attestation.dsse.json
# Step 3: Verify with StellaOps
stella attest verify \
--envelope attestation.dsse.json \
--extract-sbom sbom.spdx.json
# Step 4: Scan with StellaOps
stella scan sbom sbom.spdx.json \
--output results.json
```
---
## Supported Predicate Types
StellaOps supports standard SBOM predicate types used by Cosign:
| Predicate Type | Format | Cosign Flag | StellaOps Support |
|----------------|--------|-------------|-------------------|
| `https://spdx.dev/Document` | SPDX 3.0.1 | `--type spdx` | ✅ Full support |
| `https://spdx.org/spdxdocs/spdx-v2.3-*` | SPDX 2.3 | `--type spdx` | ✅ Full support |
| `https://cyclonedx.org/bom` | CycloneDX 1.4-1.7 | `--type cyclonedx` | ✅ Full support |
| `https://slsa.dev/provenance/v1` | SLSA v1.0 | `--type slsaprovenance` | ✅ Metadata only |
---
## Common Workflows
### Workflow 1: Keyless Signing (Fulcio)
**Use Case:** Sign attestations using ephemeral keys from Fulcio (requires OIDC).
```bash
# Step 1: Generate SBOM (using Syft as example)
syft myregistry/myapp:v1.2.3 -o spdx-json=sbom.spdx.json
# Step 2: Sign with Cosign (keyless)
cosign attest --predicate sbom.spdx.json \
--type spdx \
myregistry/myapp:v1.2.3
# Step 3: Verify with StellaOps (uses Sigstore public instance)
stella attest verify-image myregistry/myapp:v1.2.3 \
--type spdx \
--extract-sbom sbom-verified.spdx.json
# Step 4: Scan
stella scan sbom sbom-verified.spdx.json
```
**Trust Configuration:**
StellaOps defaults to the Sigstore public instance trust roots:
- Fulcio root: https://fulcio.sigstore.dev/api/v2/trustBundle
- Rekor instance: https://rekor.sigstore.dev
### Workflow 2: Key-Based Signing
**Use Case:** Sign attestations with your own keys (air-gapped environments).
```bash
# Step 1: Generate key pair (one-time)
cosign generate-key-pair
# Step 2: Sign SBOM
cosign attest --predicate sbom.spdx.json \
--type spdx \
--key cosign.key \
myregistry/myapp:v1.2.3
# Step 3: Export attestation
cosign verify-attestation myregistry/myapp:v1.2.3 \
--key cosign.pub \
--type spdx \
--output-file attestation.dsse.json
# Step 4: Verify with StellaOps (custom public key)
stella attest verify \
--envelope attestation.dsse.json \
--public-key cosign.pub \
--extract-sbom sbom.spdx.json
# Step 5: Upload to StellaOps
stella sbom upload --file sbom.spdx.json --artifact myapp:v1.2.3
```
### Workflow 3: CycloneDX Attestations
**Use Case:** Work with CycloneDX BOMs from Trivy.
```bash
# Step 1: Generate CycloneDX SBOM with Trivy
trivy image myregistry/myapp:v1.2.3 \
--format cyclonedx \
--output sbom.cdx.json
# Step 2: Sign with Cosign
cosign attest --predicate sbom.cdx.json \
--type cyclonedx \
--key cosign.key \
myregistry/myapp:v1.2.3
# Step 3: Fetch and verify
cosign verify-attestation myregistry/myapp:v1.2.3 \
--key cosign.pub \
--type cyclonedx \
--output-file attestation.dsse.json
stella attest verify \
--envelope attestation.dsse.json \
--public-key cosign.pub \
--extract-sbom sbom.cdx.json
# Step 4: Scan
stella scan sbom sbom.cdx.json
```
---
## CLI Reference
### `stella attest verify`
Verify a Cosign-signed attestation and optionally extract the SBOM.
```bash
stella attest verify [OPTIONS]
Options:
--envelope FILE DSSE envelope file (required)
--root FILE Fulcio root certificate (for keyless)
--public-key FILE Public key file (for key-based)
--extract-sbom FILE Extract SBOM to file
--offline Offline verification mode
--checkpoint FILE Rekor checkpoint for offline verification
--trust-root DIR Directory with trust roots
--output FILE Verification report output (JSON)
Examples:
# Keyless verification (Sigstore public instance)
stella attest verify --envelope attestation.dsse.json
# Key-based verification
stella attest verify \
--envelope attestation.dsse.json \
--public-key cosign.pub
# Extract SBOM during verification
stella attest verify \
--envelope attestation.dsse.json \
--extract-sbom sbom.json
# Offline verification
stella attest verify \
--envelope attestation.dsse.json \
--offline \
--trust-root /opt/stellaops/trust-roots \
--checkpoint rekor-checkpoint.json
```
### `stella attest extract-sbom`
Extract SBOM from a DSSE envelope without verification.
```bash
stella attest extract-sbom [OPTIONS]
Options:
--envelope FILE DSSE envelope file (required)
--output FILE Output SBOM file (required)
--format FORMAT Force format (spdx|cyclonedx)
Example:
stella attest extract-sbom \
--envelope attestation.dsse.json \
--output sbom.spdx.json
```
### `stella attest verify-image`
Verify attestations attached to an OCI image.
```bash
stella attest verify-image IMAGE [OPTIONS]
Options:
--type TYPE Predicate type (spdx|cyclonedx|slsaprovenance)
--extract-sbom FILE Extract SBOM to file
--public-key FILE Public key (for key-based signing)
--offline Offline mode
Example:
stella attest verify-image myregistry/myapp:v1.2.3 \
--type spdx \
--extract-sbom sbom.spdx.json
```
---
## Trust Configuration
### Default Trust Roots (Public Sigstore)
StellaOps defaults to the Sigstore public instance:
```yaml
# Default configuration (built-in)
attestor:
trustRoots:
sigstore:
enabled: true
fulcioRootUrl: https://fulcio.sigstore.dev/api/v2/trustBundle
rekorInstanceUrl: https://rekor.sigstore.dev
cacheTTL: 24h
```
### Custom Trust Roots (Air-Gapped)
For air-gapped environments, provide trust roots offline:
```yaml
# /etc/stellaops/attestor.yaml
attestor:
trustRoots:
custom:
enabled: true
fulcioRoots:
- /opt/stellaops/trust-roots/fulcio-root.pem
- /opt/stellaops/trust-roots/fulcio-intermediate.pem
rekorPublicKeys:
- /opt/stellaops/trust-roots/rekor.pub
ctfePublicKeys:
- /opt/stellaops/trust-roots/ctfe.pub
```
**Trust Root Bundle Structure:**
```
/opt/stellaops/trust-roots/
├── fulcio-root.pem # Fulcio root CA
├── fulcio-intermediate.pem # Fulcio intermediate CA (optional)
├── rekor.pub # Rekor public key
├── ctfe.pub # Certificate Transparency log public key
└── checkpoints/ # Cached Rekor checkpoints
└── rekor-checkpoint.json
```
### Downloading Trust Roots
```bash
# Download Sigstore public trust bundle
curl -o trust-bundle.json \
https://tuf.sigstore.dev/targets/trusted_root.json
# Extract Fulcio roots
stella trust extract-fulcio-roots \
--bundle trust-bundle.json \
--output /opt/stellaops/trust-roots/
# Extract Rekor public keys
stella trust extract-rekor-keys \
--bundle trust-bundle.json \
--output /opt/stellaops/trust-roots/
```
---
## Offline Verification
### Prerequisites
1. Trust roots downloaded and extracted
2. Rekor checkpoint bundle downloaded
3. Attestation DSSE envelope available locally
### Workflow
```bash
# Step 1: Download trust bundle (online, one-time)
stella trust download --output /opt/stellaops/trust-roots/
# Step 2: Download Rekor checkpoint (online, periodic)
stella trust checkpoint download \
--output /opt/stellaops/trust-roots/checkpoints/rekor-checkpoint.json
# Step 3: Verify offline (air-gapped environment)
stella attest verify \
--envelope attestation.dsse.json \
--offline \
--trust-root /opt/stellaops/trust-roots \
--checkpoint /opt/stellaops/trust-roots/checkpoints/rekor-checkpoint.json \
--extract-sbom sbom.json
# Step 4: Scan offline
stella scan sbom sbom.json --offline
```
### Checkpoint Freshness
Rekor checkpoints should be refreshed periodically:
- **High Security:** Daily updates
- **Standard:** Weekly updates
- **Low Risk:** Monthly updates
Set a reminder to refresh checkpoints:
```bash
# Cron job (daily at 2 AM)
0 2 * * * /usr/local/bin/stella trust checkpoint download --output /opt/stellaops/trust-roots/checkpoints/
```
---
## Troubleshooting
### Error: "Unsupported predicate type"
**Cause:** The DSSE envelope contains a predicate type not supported by StellaOps.
**Solution:** Check the predicate type:
```bash
stella attest inspect --envelope attestation.dsse.json
# Output will show:
# Predicate Type: https://example.com/custom-type
# Supported: false
```
If the predicate is SPDX or CycloneDX, ensure you're using StellaOps CLI v1.5+.
### Error: "Signature verification failed"
**Cause:** The signature cannot be verified against the provided trust roots.
**Troubleshooting Steps:**
1. Check trust root configuration:
```bash
stella attest verify --envelope attestation.dsse.json --debug
```
2. Verify the public key matches:
```bash
# Extract public key from certificate in DSSE envelope
stella attest inspect --envelope attestation.dsse.json --show-cert
# Compare with your public key
cat cosign.pub
```
3. For keyless signing, ensure Fulcio root is correct:
```bash
# Test Fulcio connectivity
curl -v https://fulcio.sigstore.dev/api/v2/trustBundle
```
### Error: "Failed to extract SBOM"
**Cause:** The predicate payload is not a valid SBOM.
**Solution:** Inspect the predicate:
```bash
stella attest inspect --envelope attestation.dsse.json --show-predicate
```
Check if the predicate type matches the actual content:
- `https://spdx.dev/Document` should contain SPDX JSON
- `https://cyclonedx.org/bom` should contain CycloneDX JSON
### Warning: "Checkpoint is stale"
**Cause:** The Rekor checkpoint is older than the freshness threshold (default: 7 days).
**Solution:** Download a fresh checkpoint:
```bash
stella trust checkpoint download \
--output /opt/stellaops/trust-roots/checkpoints/rekor-checkpoint.json
```
---
## Best Practices
### 1. Verify Before Extraction
Always verify the attestation signature before extracting the SBOM:
```bash
# ✅ GOOD: Verify then extract
stella attest verify \
--envelope attestation.dsse.json \
--extract-sbom sbom.json
# ❌ BAD: Extract without verification
stella attest extract-sbom \
--envelope attestation.dsse.json \
--output sbom.json
```
### 2. Use Keyless Signing for Public Images
For public container images, use keyless signing (Fulcio):
- No key management overhead
- Identity verified via OIDC
- Transparent via Rekor
```bash
# Keyless signing (recommended for public images)
cosign attest --predicate sbom.spdx.json \
--type spdx \
myregistry/publicapp:v1.0.0
```
### 3. Use Key-Based Signing for Private/Air-Gapped
For private registries or air-gapped environments, use key-based signing:
- Full control over keys
- No external dependencies
- Works offline
```bash
# Key-based signing (recommended for private/air-gapped)
cosign attest --predicate sbom.spdx.json \
--type spdx \
--key cosign.key \
myregistry/privateapp:v1.0.0
```
### 4. Automate Trust Root Updates
Set up automated trust root updates:
```bash
#!/bin/bash
# /usr/local/bin/update-trust-roots.sh
set -e
TRUST_DIR=/opt/stellaops/trust-roots
# Download latest trust bundle
stella trust download --output $TRUST_DIR --force
# Download fresh checkpoint
stella trust checkpoint download --output $TRUST_DIR/checkpoints/
# Verify trust roots
stella trust verify --trust-root $TRUST_DIR
echo "Trust roots updated successfully"
```
### 5. Include Attestation in CI/CD
Integrate attestation verification into your CI/CD pipeline:
**GitHub Actions Example:**
```yaml
name: Verify SBOM Attestation
on:
push:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install StellaOps CLI
run: |
curl -sSfL https://cli.stellaops.io/install.sh | sh
sudo mv stella /usr/local/bin/
- name: Verify attestation
run: |
stella attest verify-image \
${{ env.IMAGE_REF }} \
--type spdx \
--extract-sbom sbom.spdx.json
- name: Scan SBOM
run: |
stella scan sbom sbom.spdx.json \
--policy production \
--fail-on blocked
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: scan-results
path: sbom.spdx.json
```
---
## Advanced Topics
### Multi-Signature Verification
Cosign supports multiple signatures on a single attestation. StellaOps verifies all signatures:
```bash
# Attestation with multiple signatures
cosign verify-attestation myregistry/myapp:v1.2.3 \
--key cosign-key1.pub \
--key cosign-key2.pub \
--type spdx \
--output-file attestation.dsse.json
# StellaOps verifies all signatures
stella attest verify \
--envelope attestation.dsse.json \
--public-key cosign-key1.pub \
--public-key cosign-key2.pub \
--require-all-signatures
```
### Custom Predicate Types
If you have custom predicate types, register them with StellaOps:
```yaml
# /etc/stellaops/attestor.yaml
attestor:
predicates:
custom:
- type: https://example.com/custom-sbom@v1
parser: custom-sbom-parser
schema: /opt/stellaops/schemas/custom-sbom.schema.json
```
### Batch Verification
Verify multiple attestations in batch:
```bash
# Create batch file
cat > batch.txt <<EOF
attestation1.dsse.json
attestation2.dsse.json
attestation3.dsse.json
EOF
# Batch verify
stella attest verify-batch \
--input batch.txt \
--public-key cosign.pub \
--output-dir verified-sboms/
```
---
## References
### External Documentation
- [Cosign Documentation](https://docs.sigstore.dev/cosign/overview/)
- [Sigstore Trust Root Specification](https://github.com/sigstore/root-signing)
- [in-toto Attestation Specification](https://github.com/in-toto/attestation)
- [SPDX 3.0.1 Specification](https://spdx.github.io/spdx-spec/v3.0.1/)
- [CycloneDX 1.6 Specification](https://cyclonedx.org/docs/1.6/)
### StellaOps Documentation
- [Attestor Architecture](../modules/attestor/architecture.md)
- [Standard Predicate Types](../modules/attestor/predicate-parsers.md)
- [CLI Reference](../API_CLI_REFERENCE.md)
- [Offline Kit Guide](../OFFLINE_KIT.md)
---
## Feedback
Found an issue or have a suggestion? Please report it:
- GitHub: https://github.com/stella-ops/stella-ops/issues
- Docs: https://docs.stellaops.io/integrations/cosign
- Community: https://community.stellaops.io/c/integrations
---
**Last Updated:** 2025-12-23
**Applies To:** StellaOps CLI v1.5+, Cosign v2.x

View File

@@ -0,0 +1,49 @@
# Artifacts Schema (DOCS-ORCH-34-004)
Last updated: 2025-11-25
## Purpose
Describe artifact kinds produced by Orchestrator runs and how they are stored, hashed, and referenced.
## Artifact kinds
- **log**: NDJSON log fragment for a step/run.
- **metrics**: Prometheus/OpenMetrics snapshot for a step/run.
- **output**: arbitrary task output (JSON, NDJSON, binary), content-addressed.
- **manifest**: bundle manifest listing artifacts and hashes.
## Schema (common fields)
```json
{
"kind": "log|metrics|output|manifest",
"tenant": "acme",
"dagId": "string",
"runId": "string",
"stepId": "string",
"contentType": "application/json",
"hash": "sha256:<hex>",
"size": 1234,
"createdUtc": "2025-11-25T00:00:00Z",
"traceId": "optional",
"encryption": "none|aes256-gcm",
"compression": "none|gzip"
}
```
## Storage rules
- Content-addressed by `sha256` (lowercase hex). Filenames may use `<hash>`; metadata kept in Mongo with tenant scoping.
- Immutable; new versions create new hashes.
- Optional encryption: AES-256-GCM with keys from Authority `secretRef`; never store keys alongside artifacts.
- Compression optional (gzip) but hash is computed on compressed bytes; record `compression`.
## Access & security
- Tenant-scoped reads; artifacts cannot be shared across tenants.
- No secrets stored; redact before writing. Logs/metrics already redacted at source.
- Access control enforced via orchestrator scopes; audit log every download/export.
## Offline posture
- Artifacts may be exported as tarball with manifest (`manifest` kind) that lists hash, size, compression/encryption flags.
- Imports verify manifest hash and per-artifact hash before accepting.
## Determinism
- Hash and size recorded at creation; manifests sorted by `kind`, then `dagId`, `runId`, `stepId`, `hash`.
- Timestamps UTC ISO-8601; NDJSON ordering stable.

View File

@@ -0,0 +1,169 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stella-ops.org/schemas/calibration-manifest/1.0.0",
"title": "Calibration Manifest",
"description": "Record of trust vector calibration based on post-mortem truth comparison",
"type": "object",
"required": ["manifestId", "sourceId", "epochNumber", "calibratedAt"],
"properties": {
"manifestId": {
"type": "string",
"description": "Unique identifier for this calibration record"
},
"sourceId": {
"type": "string",
"description": "VEX source being calibrated"
},
"tenant": {
"type": "string",
"description": "Tenant scope (optional for global calibration)"
},
"epochNumber": {
"type": "integer",
"description": "Calibration epoch number",
"minimum": 1
},
"previousVector": {
"$ref": "#/$defs/TrustVectorValues"
},
"calibratedVector": {
"$ref": "#/$defs/TrustVectorValues"
},
"delta": {
"$ref": "#/$defs/CalibrationDelta"
},
"comparison": {
"$ref": "#/$defs/ComparisonResult"
},
"detectedBias": {
"type": "string",
"description": "Detected bias type, if any",
"enum": ["optimistic_bias", "pessimistic_bias", "scope_bias", "none"]
},
"configuration": {
"$ref": "#/$defs/CalibrationConfiguration"
},
"calibratedAt": {
"type": "string",
"description": "When calibration was performed",
"format": "date-time"
},
"manifestDigest": {
"type": "string",
"description": "SHA256 digest of this manifest",
"pattern": "^sha256:[a-f0-9]{64}$"
}
},
"$defs": {
"TrustVectorValues": {
"type": "object",
"description": "Trust vector component values",
"required": ["provenance", "coverage", "replayability"],
"properties": {
"provenance": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"coverage": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"replayability": {
"type": "number",
"minimum": 0,
"maximum": 1
}
}
},
"CalibrationDelta": {
"type": "object",
"description": "Adjustment applied to trust vector",
"properties": {
"deltaP": {
"type": "number",
"description": "Change in provenance score"
},
"deltaC": {
"type": "number",
"description": "Change in coverage score"
},
"deltaR": {
"type": "number",
"description": "Change in replayability score"
}
}
},
"ComparisonResult": {
"type": "object",
"description": "Result of comparing claims to post-mortem truth",
"required": ["sourceId", "accuracy"],
"properties": {
"sourceId": {
"type": "string"
},
"accuracy": {
"type": "number",
"description": "Accuracy score (0-1)",
"minimum": 0,
"maximum": 1
},
"totalClaims": {
"type": "integer",
"description": "Total claims evaluated",
"minimum": 0
},
"correctClaims": {
"type": "integer",
"description": "Claims matching post-mortem truth",
"minimum": 0
},
"evaluationPeriodStart": {
"type": "string",
"format": "date-time"
},
"evaluationPeriodEnd": {
"type": "string",
"format": "date-time"
}
}
},
"CalibrationConfiguration": {
"type": "object",
"description": "Configuration used for calibration",
"properties": {
"learningRate": {
"type": "number",
"description": "Learning rate per epoch",
"default": 0.02
},
"maxAdjustmentPerEpoch": {
"type": "number",
"description": "Maximum adjustment per epoch",
"default": 0.05
},
"minValue": {
"type": "number",
"description": "Minimum trust component value",
"default": 0.10
},
"maxValue": {
"type": "number",
"description": "Maximum trust component value",
"default": 1.00
},
"momentumFactor": {
"type": "number",
"description": "Momentum factor for smoothing",
"default": 0.9
},
"accuracyThreshold": {
"type": "number",
"description": "Threshold above which no calibration is needed",
"default": 0.95
}
}
}
}
}

View File

@@ -0,0 +1,137 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stella-ops.org/schemas/claim-score/1.0.0",
"title": "Claim Score",
"description": "VEX claim scoring result using the trust lattice formula: ClaimScore = BaseTrust * M * F",
"type": "object",
"required": ["sourceId", "status", "claimScore"],
"properties": {
"sourceId": {
"type": "string",
"description": "Identifier of the VEX source"
},
"status": {
"type": "string",
"description": "VEX status claimed",
"enum": ["affected", "not_affected", "fixed", "under_investigation"]
},
"trustVector": {
"$ref": "#/$defs/TrustVectorScores"
},
"baseTrust": {
"type": "number",
"description": "Computed base trust from trust vector",
"minimum": 0,
"maximum": 1
},
"claimStrength": {
"$ref": "#/$defs/ClaimStrength"
},
"strengthMultiplier": {
"type": "number",
"description": "Strength multiplier (M) based on evidence quality",
"minimum": 0,
"maximum": 1
},
"freshnessMultiplier": {
"type": "number",
"description": "Freshness decay multiplier (F)",
"minimum": 0,
"maximum": 1
},
"freshnessDetails": {
"$ref": "#/$defs/FreshnessDetails"
},
"claimScore": {
"type": "number",
"description": "Final claim score = BaseTrust * M * F",
"minimum": 0,
"maximum": 1
},
"scopeSpecificity": {
"type": "integer",
"description": "Scope specificity level (higher = more specific)",
"minimum": 0
},
"issuedAt": {
"type": "string",
"description": "When the VEX claim was issued",
"format": "date-time"
},
"evaluatedAt": {
"type": "string",
"description": "When the score was computed",
"format": "date-time"
}
},
"$defs": {
"TrustVectorScores": {
"type": "object",
"description": "Trust vector component scores",
"properties": {
"provenance": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"coverage": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"replayability": {
"type": "number",
"minimum": 0,
"maximum": 1
}
}
},
"ClaimStrength": {
"type": "object",
"description": "Claim strength evidence classification",
"properties": {
"level": {
"type": "string",
"description": "Strength level",
"enum": [
"exploitability_with_reachability",
"config_with_evidence",
"vendor_blanket",
"under_investigation"
]
},
"multiplier": {
"type": "number",
"description": "Corresponding multiplier value",
"enum": [1.00, 0.80, 0.60, 0.40]
}
}
},
"FreshnessDetails": {
"type": "object",
"description": "Freshness decay calculation details",
"properties": {
"ageDays": {
"type": "number",
"description": "Age of the claim in days"
},
"halfLifeDays": {
"type": "number",
"description": "Half-life used for decay calculation",
"default": 90
},
"floor": {
"type": "number",
"description": "Minimum freshness value",
"default": 0.35
},
"decayValue": {
"type": "number",
"description": "Computed decay value before floor application",
"minimum": 0,
"maximum": 1
}
}
}
}
}

View File

@@ -0,0 +1,84 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stella-ops.org/schemas/trust-vector/1.0.0",
"title": "Trust Vector",
"description": "3-component trust vector for VEX sources (Provenance, Coverage, Replayability)",
"type": "object",
"required": ["provenance", "coverage", "replayability"],
"properties": {
"sourceId": {
"type": "string",
"description": "Identifier of the VEX source"
},
"sourceClass": {
"type": "string",
"description": "Classification of the source",
"enum": ["vendor", "distro", "internal", "hub", "attestation"]
},
"provenance": {
"type": "number",
"description": "Cryptographic and process integrity score [0..1]",
"minimum": 0,
"maximum": 1
},
"coverage": {
"type": "number",
"description": "Scope match precision score [0..1]",
"minimum": 0,
"maximum": 1
},
"replayability": {
"type": "number",
"description": "Determinism and input pinning score [0..1]",
"minimum": 0,
"maximum": 1
},
"weights": {
"$ref": "#/$defs/TrustWeights"
},
"baseTrust": {
"type": "number",
"description": "Computed base trust: wP*P + wC*C + wR*R",
"minimum": 0,
"maximum": 1
},
"computedAt": {
"type": "string",
"description": "Timestamp when this vector was computed",
"format": "date-time"
},
"version": {
"type": "string",
"description": "Version of the trust vector configuration"
}
},
"$defs": {
"TrustWeights": {
"type": "object",
"description": "Weights for trust vector components",
"properties": {
"provenance": {
"type": "number",
"description": "Weight for provenance component (wP)",
"minimum": 0,
"maximum": 1,
"default": 0.45
},
"coverage": {
"type": "number",
"description": "Weight for coverage component (wC)",
"minimum": 0,
"maximum": 1,
"default": 0.35
},
"replayability": {
"type": "number",
"description": "Weight for replayability component (wR)",
"minimum": 0,
"maximum": 1,
"default": 0.20
}
}
}
}
}

View File

@@ -0,0 +1,194 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stella-ops.org/schemas/verdict-manifest/1.0.0",
"title": "Verdict Manifest",
"description": "A signed, immutable record of a VEX decisioning outcome that enables deterministic replay and audit compliance.",
"type": "object",
"required": [
"manifestId",
"tenant",
"assetDigest",
"vulnerabilityId",
"inputs",
"result",
"policyHash",
"latticeVersion",
"evaluatedAt",
"manifestDigest"
],
"properties": {
"manifestId": {
"type": "string",
"description": "Unique identifier in format: verd:{tenant}:{asset_short}:{vuln_id}:{timestamp}",
"pattern": "^verd:[a-z0-9-]+:[a-f0-9]+:[A-Z0-9-]+:[0-9]+$"
},
"tenant": {
"type": "string",
"description": "Tenant identifier for multi-tenancy",
"minLength": 1
},
"assetDigest": {
"type": "string",
"description": "SHA256 digest of the asset/SBOM",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"vulnerabilityId": {
"type": "string",
"description": "CVE, GHSA, or vendor vulnerability identifier",
"minLength": 1
},
"inputs": {
"$ref": "#/$defs/VerdictInputs"
},
"result": {
"$ref": "#/$defs/VerdictResult"
},
"policyHash": {
"type": "string",
"description": "SHA256 hash of the policy configuration",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"latticeVersion": {
"type": "string",
"description": "Semantic version of the trust lattice algorithm",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"evaluatedAt": {
"type": "string",
"description": "ISO 8601 UTC timestamp of evaluation",
"format": "date-time"
},
"manifestDigest": {
"type": "string",
"description": "SHA256 digest of the canonical manifest (excluding this field)",
"pattern": "^sha256:[a-f0-9]{64}$"
}
},
"$defs": {
"VerdictInputs": {
"type": "object",
"description": "All inputs pinned for deterministic replay",
"required": ["sbomDigests", "vulnFeedSnapshotIds", "vexDocumentDigests", "clockCutoff"],
"properties": {
"sbomDigests": {
"type": "array",
"description": "SHA256 digests of SBOM documents used",
"items": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$"
}
},
"vulnFeedSnapshotIds": {
"type": "array",
"description": "Identifiers for vulnerability feed snapshots",
"items": {
"type": "string"
}
},
"vexDocumentDigests": {
"type": "array",
"description": "SHA256 digests of VEX documents considered",
"items": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$"
}
},
"reachabilityGraphIds": {
"type": "array",
"description": "Identifiers for call graph snapshots",
"items": {
"type": "string"
}
},
"clockCutoff": {
"type": "string",
"description": "Timestamp used for freshness calculations",
"format": "date-time"
}
}
},
"VerdictResult": {
"type": "object",
"description": "The verdict and explanation",
"required": ["status", "confidence", "explanations"],
"properties": {
"status": {
"type": "string",
"description": "Final verdict status",
"enum": ["affected", "not_affected", "fixed", "under_investigation"]
},
"confidence": {
"type": "number",
"description": "Numeric confidence score",
"minimum": 0,
"maximum": 1
},
"explanations": {
"type": "array",
"description": "Per-source breakdown of scoring",
"items": {
"$ref": "#/$defs/VerdictExplanation"
}
},
"evidenceRefs": {
"type": "array",
"description": "Links to attestations and proof bundles",
"items": {
"type": "string"
}
}
}
},
"VerdictExplanation": {
"type": "object",
"description": "Explanation of how a source contributed to the verdict",
"required": ["sourceId", "reason", "claimScore"],
"properties": {
"sourceId": {
"type": "string",
"description": "Identifier of the VEX source"
},
"reason": {
"type": "string",
"description": "Human-readable explanation"
},
"provenanceScore": {
"type": "number",
"description": "Provenance component of trust vector",
"minimum": 0,
"maximum": 1
},
"coverageScore": {
"type": "number",
"description": "Coverage component of trust vector",
"minimum": 0,
"maximum": 1
},
"replayabilityScore": {
"type": "number",
"description": "Replayability component of trust vector",
"minimum": 0,
"maximum": 1
},
"strengthMultiplier": {
"type": "number",
"description": "Claim strength multiplier (M)",
"minimum": 0,
"maximum": 1
},
"freshnessMultiplier": {
"type": "number",
"description": "Freshness decay multiplier (F)",
"minimum": 0,
"maximum": 1
},
"claimScore": {
"type": "number",
"description": "Final claim score = BaseTrust * M * F",
"minimum": 0,
"maximum": 1
}
}
}
}
}