Add property-based tests for SBOM/VEX document ordering and Unicode normalization determinism
- Implement `SbomVexOrderingDeterminismProperties` for testing component list and vulnerability metadata hash consistency. - Create `UnicodeNormalizationDeterminismProperties` to validate NFC normalization and Unicode string handling. - Add project file for `StellaOps.Testing.Determinism.Properties` with necessary dependencies. - Introduce CI/CD template validation tests including YAML syntax checks and documentation content verification. - Create validation script for CI/CD templates ensuring all required files and structures are present.
This commit is contained in:
@@ -231,9 +231,264 @@ cosign verify-attestation \
|
||||
|
||||
See [Cosign Verification Examples](./cosign-verification-examples.md) for more details.
|
||||
|
||||
---
|
||||
|
||||
# Aggregated Attestation Bundle Format
|
||||
|
||||
This section describes the StellaOps Attestation Bundle format for aggregating multiple attestations for long-term verification.
|
||||
|
||||
## Overview
|
||||
|
||||
Aggregated attestation bundles collect multiple attestations from a time period into a single verifiable package. This enables:
|
||||
|
||||
- **Long-term verification** of keyless-signed artifacts after certificate expiry
|
||||
- **Organizational endorsement** via optional org-key signature
|
||||
- **Offline verification** with bundled Rekor inclusion proofs
|
||||
- **Regulatory compliance** with audit-ready evidence packages
|
||||
|
||||
## Bundle Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"bundleId": "sha256:abc123...",
|
||||
"version": "1.0",
|
||||
"createdAt": "2025-12-26T02:00:00Z",
|
||||
"periodStart": "2025-12-01T00:00:00Z",
|
||||
"periodEnd": "2025-12-31T23:59:59Z",
|
||||
"attestationCount": 1542,
|
||||
"tenantId": "tenant-1",
|
||||
"orgKeyFingerprint": "sha256:def456..."
|
||||
},
|
||||
"attestations": [
|
||||
{
|
||||
"entryId": "uuid-1",
|
||||
"rekorUuid": "24296fb2...",
|
||||
"rekorLogIndex": 12345678,
|
||||
"artifactDigest": "sha256:...",
|
||||
"predicateType": "verdict.stella/v1",
|
||||
"signedAt": "2025-12-15T10:30:00Z",
|
||||
"signingMode": "keyless",
|
||||
"signingIdentity": {
|
||||
"issuer": "https://token.actions.githubusercontent.com",
|
||||
"subject": "repo:org/repo:ref:refs/heads/main",
|
||||
"san": "https://github.com/org/repo/.github/workflows/release.yml@refs/heads/main"
|
||||
},
|
||||
"inclusionProof": {
|
||||
"checkpoint": {
|
||||
"origin": "rekor.sigstore.dev - ...",
|
||||
"size": 12000000,
|
||||
"rootHash": "base64...",
|
||||
"timestamp": "2025-12-15T10:30:05Z"
|
||||
},
|
||||
"path": ["base64hash1", "base64hash2", ...]
|
||||
},
|
||||
"envelope": {
|
||||
"payloadType": "application/vnd.in-toto+json",
|
||||
"payload": "base64...",
|
||||
"signatures": [{"sig": "base64...", "keyid": ""}],
|
||||
"certificateChain": ["base64cert1", ...]
|
||||
}
|
||||
}
|
||||
],
|
||||
"merkleTree": {
|
||||
"algorithm": "SHA256",
|
||||
"root": "sha256:abc123...",
|
||||
"leafCount": 1542
|
||||
},
|
||||
"orgSignature": {
|
||||
"keyId": "org-signing-key-2025",
|
||||
"algorithm": "ECDSA_P256",
|
||||
"signature": "base64...",
|
||||
"signedAt": "2025-12-26T02:05:00Z",
|
||||
"certificateChain": ["base64cert1", ...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### Metadata
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `bundleId` | string | Content-addressed ID: `sha256:<merkle_root>` |
|
||||
| `version` | string | Bundle schema version (currently "1.0") |
|
||||
| `createdAt` | ISO 8601 | Bundle creation timestamp (UTC) |
|
||||
| `periodStart` | ISO 8601 | Start of attestation collection period |
|
||||
| `periodEnd` | ISO 8601 | End of attestation collection period |
|
||||
| `attestationCount` | int | Number of attestations in bundle |
|
||||
| `tenantId` | string | Optional tenant identifier |
|
||||
| `orgKeyFingerprint` | string | Fingerprint of org signing key (if signed) |
|
||||
|
||||
### Attestations
|
||||
|
||||
Each attestation entry contains:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `entryId` | string | Unique entry identifier |
|
||||
| `rekorUuid` | string | Rekor transparency log UUID |
|
||||
| `rekorLogIndex` | long | Rekor log index |
|
||||
| `artifactDigest` | string | SHA256 digest of attested artifact |
|
||||
| `predicateType` | string | In-toto predicate type |
|
||||
| `signedAt` | ISO 8601 | When attestation was signed |
|
||||
| `signingMode` | string | `keyless`, `kms`, `hsm`, or `fido2` |
|
||||
| `signingIdentity` | object | Signer identity information |
|
||||
| `inclusionProof` | object | Rekor Merkle inclusion proof |
|
||||
| `envelope` | object | DSSE envelope with signatures and certificates |
|
||||
|
||||
### Merkle Tree
|
||||
|
||||
Deterministic Merkle tree over attestation hashes:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `algorithm` | string | Hash algorithm (always "SHA256") |
|
||||
| `root` | string | Merkle root: `sha256:<64-hex>` |
|
||||
| `leafCount` | int | Number of leaves (= attestation count) |
|
||||
|
||||
### Org Signature
|
||||
|
||||
Optional organizational endorsement:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `keyId` | string | Signing key identifier |
|
||||
| `algorithm` | string | `ECDSA_P256`, `Ed25519`, or `RSA_PSS_SHA256` |
|
||||
| `signature` | string | Base64-encoded signature |
|
||||
| `signedAt` | ISO 8601 | Signature timestamp |
|
||||
| `certificateChain` | array | PEM-encoded certificate chain |
|
||||
|
||||
## Determinism
|
||||
|
||||
Bundles are deterministic - same attestations produce same bundle:
|
||||
|
||||
1. **Attestation ordering**: Sorted by `entryId` lexicographically
|
||||
2. **Merkle tree**: Leaves computed as `SHA256(canonicalized_attestation_json)`
|
||||
3. **Bundle ID**: Derived from Merkle root: `sha256:<merkle_root>`
|
||||
4. **JSON serialization**: Canonical ordering (sorted keys, no whitespace)
|
||||
|
||||
## Verification
|
||||
|
||||
### Full Bundle Verification
|
||||
|
||||
```csharp
|
||||
using StellaOps.Attestor.Bundling.Verification;
|
||||
|
||||
var verifier = new AttestationBundleVerifier();
|
||||
var result = await verifier.VerifyAsync(bundle);
|
||||
|
||||
if (result.Valid)
|
||||
{
|
||||
Console.WriteLine($"Merkle root verified: {result.MerkleRootVerified}");
|
||||
Console.WriteLine($"Org signature verified: {result.OrgSignatureVerified}");
|
||||
Console.WriteLine($"Attestations verified: {result.AttestationsVerified}");
|
||||
}
|
||||
```
|
||||
|
||||
### Individual Attestation Verification
|
||||
|
||||
```csharp
|
||||
// Extract single attestation for verification
|
||||
var attestation = bundle.Attestations.First(a => a.ArtifactDigest == targetDigest);
|
||||
|
||||
// Verify inclusion proof against Rekor
|
||||
var proofValid = await RekorVerifier.VerifyInclusionAsync(
|
||||
attestation.RekorLogIndex,
|
||||
attestation.InclusionProof);
|
||||
|
||||
// Verify DSSE envelope signature
|
||||
var sigValid = await DsseVerifier.VerifyAsync(
|
||||
attestation.Envelope,
|
||||
attestation.SigningIdentity);
|
||||
```
|
||||
|
||||
## Storage
|
||||
|
||||
### S3/Object Storage
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
storage:
|
||||
backend: s3
|
||||
s3:
|
||||
bucket: stellaops-attestor
|
||||
prefix: bundles/
|
||||
objectLock: governance # WORM protection
|
||||
storageClass: STANDARD
|
||||
```
|
||||
|
||||
### Filesystem
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
storage:
|
||||
backend: filesystem
|
||||
filesystem:
|
||||
path: /var/lib/stellaops/attestor/bundles
|
||||
directoryPermissions: "0750"
|
||||
filePermissions: "0640"
|
||||
```
|
||||
|
||||
## Retention
|
||||
|
||||
Bundles follow configurable retention policies:
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `defaultMonths` | 24 | Standard retention period |
|
||||
| `minimumMonths` | 6 | Cannot be reduced below this |
|
||||
| `maximumMonths` | 120 | Maximum allowed retention |
|
||||
|
||||
### Tenant Overrides
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
retention:
|
||||
defaultMonths: 24
|
||||
tenantOverrides:
|
||||
tenant-gov: 84 # 7 years
|
||||
tenant-finance: 120 # 10 years
|
||||
```
|
||||
|
||||
## Export Formats
|
||||
|
||||
### JSON (Default)
|
||||
|
||||
Human-readable, suitable for debugging and audit:
|
||||
|
||||
```bash
|
||||
stella attestor bundle export --format json bundle-sha256-abc.json
|
||||
```
|
||||
|
||||
### CBOR
|
||||
|
||||
Compact binary format (~40% smaller):
|
||||
|
||||
```bash
|
||||
stella attestor bundle export --format cbor bundle-sha256-abc.cbor
|
||||
```
|
||||
|
||||
### Compression
|
||||
|
||||
Both formats support compression:
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
export:
|
||||
compression: zstd # none | gzip | zstd
|
||||
compressionLevel: 3
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Sigstore Bundle Specification](https://github.com/sigstore/cosign/blob/main/specs/BUNDLE_SPEC.md)
|
||||
- [Sigstore Protobuf Specs](https://github.com/sigstore/protobuf-specs)
|
||||
- [DSSE Specification](https://github.com/secure-systems-lab/dsse)
|
||||
- [RFC 6962 - Certificate Transparency](https://www.rfc-editor.org/rfc/rfc6962)
|
||||
- [Bundle Rotation Operations](./operations/bundle-rotation.md)
|
||||
|
||||
302
docs/modules/attestor/operations/bundle-rotation.md
Normal file
302
docs/modules/attestor/operations/bundle-rotation.md
Normal file
@@ -0,0 +1,302 @@
|
||||
# Bundle Rotation Operations Guide
|
||||
|
||||
This guide covers operational procedures for attestation bundle rotation in StellaOps.
|
||||
|
||||
## Overview
|
||||
|
||||
Bundle rotation is a scheduled process that aggregates attestations from a time period into a verifiable bundle. This enables long-term verification of keyless-signed artifacts beyond their certificate expiry.
|
||||
|
||||
## Rotation Schedule
|
||||
|
||||
### Default Schedule
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
schedule:
|
||||
cron: "0 2 1 * *" # Monthly on the 1st at 02:00 UTC
|
||||
cadence: monthly
|
||||
timezone: UTC
|
||||
skipWeekends: false
|
||||
```
|
||||
|
||||
### Cadence Options
|
||||
|
||||
| Cadence | Period | Use Case |
|
||||
|---------|--------|----------|
|
||||
| `weekly` | Previous 7 days | High-volume environments |
|
||||
| `monthly` | Previous month | Standard deployment (default) |
|
||||
| `quarterly` | Previous quarter | Low-volume, compliance-focused |
|
||||
|
||||
## Manual Rotation
|
||||
|
||||
### Trigger Immediate Rotation
|
||||
|
||||
```bash
|
||||
# Rotate current period
|
||||
stella attestor bundle rotate
|
||||
|
||||
# Rotate specific period
|
||||
stella attestor bundle rotate --start 2025-12-01 --end 2025-12-31
|
||||
|
||||
# Rotate for specific tenant
|
||||
stella attestor bundle rotate --tenant tenant-gov
|
||||
```
|
||||
|
||||
### API Trigger
|
||||
|
||||
```http
|
||||
POST /api/v1/bundles
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"periodStart": "2025-12-01T00:00:00Z",
|
||||
"periodEnd": "2025-12-31T23:59:59Z",
|
||||
"tenantId": null,
|
||||
"signWithOrgKey": true,
|
||||
"orgKeyId": "org-signing-key-2025"
|
||||
}
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Key Metrics
|
||||
|
||||
| Metric | Description | Alert Threshold |
|
||||
|--------|-------------|-----------------|
|
||||
| `attestor_bundle_created_total` | Bundles created | N/A (informational) |
|
||||
| `attestor_bundle_creation_duration_seconds` | Creation time | > 30 minutes |
|
||||
| `attestor_bundle_attestations_count` | Attestations per bundle | > 10,000 |
|
||||
| `attestor_bundle_size_bytes` | Bundle size | > 100 MB |
|
||||
| `attestor_bundle_retention_deleted_total` | Expired bundles deleted | N/A |
|
||||
|
||||
### Grafana Dashboard
|
||||
|
||||
Import the attestor observability dashboard:
|
||||
```bash
|
||||
stella observability import --dashboard attestor-bundling
|
||||
```
|
||||
|
||||
See: `docs/modules/attestor/operations/dashboards/attestor-observability.json`
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
# Check bundle rotation status
|
||||
stella attestor bundle status
|
||||
|
||||
# Sample output:
|
||||
# Last Rotation: 2025-12-01T02:00:00Z
|
||||
# Next Scheduled: 2026-01-01T02:00:00Z
|
||||
# Bundles This Month: 3
|
||||
# Total Attestations Bundled: 4,521
|
||||
# Status: Healthy
|
||||
```
|
||||
|
||||
## Retention Policy
|
||||
|
||||
### Configuration
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
retention:
|
||||
enabled: true
|
||||
defaultMonths: 24
|
||||
minimumMonths: 6
|
||||
maximumMonths: 120
|
||||
expiryAction: delete # delete | archive | markOnly
|
||||
archiveStorageTier: glacier
|
||||
gracePeriodDays: 30
|
||||
notifyBeforeExpiry: true
|
||||
notifyDaysBeforeExpiry: 30
|
||||
maxBundlesPerRun: 100
|
||||
```
|
||||
|
||||
### Retention Actions
|
||||
|
||||
| Action | Behavior |
|
||||
|--------|----------|
|
||||
| `delete` | Permanently remove expired bundles |
|
||||
| `archive` | Move to cold storage (S3 Glacier) |
|
||||
| `markOnly` | Mark as expired but retain |
|
||||
|
||||
### Manual Retention Enforcement
|
||||
|
||||
```bash
|
||||
# Preview expired bundles
|
||||
stella attestor bundle retention --dry-run
|
||||
|
||||
# Apply retention policy
|
||||
stella attestor bundle retention --apply
|
||||
|
||||
# Force delete specific bundle
|
||||
stella attestor bundle delete sha256:abc123...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bundle Creation Failed
|
||||
|
||||
**Symptoms:** Rotation job completes with errors
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
# View recent rotation logs
|
||||
stella logs --service attestor --filter "bundle rotation"
|
||||
|
||||
# Check attestor health
|
||||
stella attestor health
|
||||
```
|
||||
|
||||
**Common causes:**
|
||||
1. Database connection issues
|
||||
2. Insufficient attestations in period
|
||||
3. Org key unavailable for signing
|
||||
|
||||
### Large Bundle Size
|
||||
|
||||
**Symptoms:** Bundle exceeds size limits or takes too long
|
||||
|
||||
**Solutions:**
|
||||
1. Reduce `maxAttestationsPerBundle` to create multiple smaller bundles
|
||||
2. Increase `queryBatchSize` for faster database queries
|
||||
3. Enable compression for storage
|
||||
|
||||
```yaml
|
||||
attestor:
|
||||
bundling:
|
||||
aggregation:
|
||||
maxAttestationsPerBundle: 5000
|
||||
queryBatchSize: 1000
|
||||
```
|
||||
|
||||
### Org Key Signing Failed
|
||||
|
||||
**Symptoms:** Bundle created without org signature
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
# Verify org key availability
|
||||
stella signer keys list --type org
|
||||
|
||||
# Test key signing
|
||||
stella signer keys test org-signing-key-2025
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
1. Ensure KMS/HSM connectivity
|
||||
2. Verify key permissions
|
||||
3. Check key rotation schedule
|
||||
|
||||
### Retention Not Running
|
||||
|
||||
**Symptoms:** Expired bundles not being deleted
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
# Verify retention is enabled
|
||||
stella attestor bundle retention --status
|
||||
|
||||
# Check for blocked bundles
|
||||
stella attestor bundle list --status expired
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
1. Ensure `retention.enabled: true`
|
||||
2. Check grace period configuration
|
||||
3. Verify storage backend permissions
|
||||
|
||||
## Disaster Recovery
|
||||
|
||||
### Bundle Export
|
||||
|
||||
Export bundles for backup:
|
||||
|
||||
```bash
|
||||
# Export all bundles from a period
|
||||
stella attestor bundle export \
|
||||
--start 2025-01-01 \
|
||||
--end 2025-12-31 \
|
||||
--output /backup/bundles/
|
||||
|
||||
# Export specific bundle
|
||||
stella attestor bundle export sha256:abc123 --output bundle.json
|
||||
```
|
||||
|
||||
### Bundle Import
|
||||
|
||||
Restore bundles from backup:
|
||||
|
||||
```bash
|
||||
# Import bundle file
|
||||
stella attestor bundle import /backup/bundles/bundle-sha256-abc123.json
|
||||
|
||||
# Bulk import
|
||||
stella attestor bundle import /backup/bundles/*.json
|
||||
```
|
||||
|
||||
### Verification After Restore
|
||||
|
||||
```bash
|
||||
# Verify imported bundle
|
||||
stella attestor bundle verify sha256:abc123
|
||||
|
||||
# Verify all bundles
|
||||
stella attestor bundle verify --all
|
||||
```
|
||||
|
||||
## Runbooks
|
||||
|
||||
### Monthly Rotation Check
|
||||
|
||||
1. **Pre-rotation (1 day before):**
|
||||
```bash
|
||||
stella attestor bundle preview --period 2025-12
|
||||
```
|
||||
|
||||
2. **Post-rotation (rotation day + 1):**
|
||||
```bash
|
||||
stella attestor bundle list --created-after 2025-12-01
|
||||
stella attestor bundle verify --period 2025-12
|
||||
```
|
||||
|
||||
3. **Verify notifications sent:**
|
||||
Check Slack/Teams/Email for rotation summary
|
||||
|
||||
### Quarterly Audit
|
||||
|
||||
1. **List all bundles:**
|
||||
```bash
|
||||
stella attestor bundle list --format json > audit-report.json
|
||||
```
|
||||
|
||||
2. **Verify sample bundles:**
|
||||
```bash
|
||||
# Random sample of 10%
|
||||
stella attestor bundle verify --sample 0.1
|
||||
```
|
||||
|
||||
3. **Check retention compliance:**
|
||||
```bash
|
||||
stella attestor bundle retention --audit
|
||||
```
|
||||
|
||||
### Emergency Bundle Access
|
||||
|
||||
For urgent verification needs:
|
||||
|
||||
```bash
|
||||
# Extract specific attestation from bundle
|
||||
stella attestor bundle extract sha256:abc123 --entry-id uuid-1
|
||||
|
||||
# Verify attestation outside bundle
|
||||
stella attestor verify --envelope attestation.dsse
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Bundle Format Specification](../bundle-format.md)
|
||||
- [Attestor Architecture](../architecture.md)
|
||||
- [Observability Guide](./observability.md)
|
||||
- [Air-Gap Operations](../airgap.md)
|
||||
@@ -417,4 +417,26 @@ See `etc/policy-gates.yaml.sample` for complete gate configuration options.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-10-26 (Sprint 19).*
|
||||
## 12 · Related Product Advisories
|
||||
|
||||
The following product advisories provide strategic context for Policy Engine features:
|
||||
|
||||
- **[Consolidated: Diff-Aware Release Gates and Risk Budgets](../../product-advisories/CONSOLIDATED%20-%20Diff-Aware%20Release%20Gates%20and%20Risk%20Budgets.md)** — Master reference for risk budgets, delta verdicts, VEX trust scoring, and release gate policies. Key sections:
|
||||
- §2 Risk Budget Model: Service tier definitions and RP scoring formulas
|
||||
- §4 Delta Verdict Engine: Deterministic evaluation pipeline and replay contract
|
||||
- §5 Smart-Diff Algorithm: Material risk change detection rules
|
||||
- §7 VEX Trust Scoring: Confidence/freshness lattice for VEX source weighting
|
||||
|
||||
- **[Consolidated: Deterministic Evidence and Verdict Architecture](../../product-advisories/CONSOLIDATED%20-%20Deterministic%20Evidence%20and%20Verdict%20Architecture.md)** — Master reference for determinism guarantees, canonical serialization, and signing. Key sections:
|
||||
- §3 Canonical Serialization: RFC 8785 JCS + Unicode NFC rules
|
||||
- §5 Signing & Attestation: Keyless signing with Sigstore
|
||||
- §6 Proof-Carrying Reachability: Minimal proof chains
|
||||
- §8 Engine Architecture: Deterministic evaluation pipeline
|
||||
|
||||
- **[Determinism Specification](../../technical/architecture/determinism-specification.md)** — Technical specification for all digest algorithms (VerdictId, EvidenceId, GraphRevisionId, ManifestId) and canonicalization rules.
|
||||
|
||||
- **[Smart-Diff Technical Reference](../../product-advisories/archived/2025-12-21-moat-gap-closure/14-Dec-2025%20-%20Smart-Diff%20Technical%20Reference.md)** — Detailed algorithm specifications for reachability gates, delta computation, and call-stack analysis.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-12-26 (Sprint 006).*
|
||||
|
||||
@@ -184,8 +184,272 @@ var result = await budgetService.CheckBudget(environment, unknowns);
|
||||
// result.CumulativeUncertainty - total uncertainty score
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Risk Budget Enforcement
|
||||
|
||||
This section describes the risk budget enforcement system that tracks and controls release risk accumulation over time.
|
||||
|
||||
## Overview
|
||||
|
||||
Risk budgets limit the cumulative risk accepted during a budget window (typically monthly). Each release consumes risk points based on the vulnerabilities it introduces or carries forward. When a budget is exhausted, further high-risk releases are blocked.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Service Tiers
|
||||
|
||||
Services are classified by criticality, which determines their risk budget allocation:
|
||||
|
||||
| Tier | Name | Monthly Allocation | Description |
|
||||
|------|------|-------------------|-------------|
|
||||
| 0 | Internal | 300 RP | Internal-only, low business impact |
|
||||
| 1 | Customer-Facing Non-Critical | 200 RP | Customer-facing but non-critical |
|
||||
| 2 | Customer-Facing Critical | 120 RP | Critical customer-facing services |
|
||||
| 3 | Safety-Critical | 80 RP | Safety, financial, or data-critical |
|
||||
|
||||
### Budget Status Thresholds
|
||||
|
||||
Budget status transitions based on percentage consumed:
|
||||
|
||||
| Status | Threshold | Behavior |
|
||||
|--------|-----------|----------|
|
||||
| Green | < 40% consumed | Normal operations |
|
||||
| Yellow | 40-69% consumed | Increased caution, warnings triggered |
|
||||
| Red | 70-99% consumed | High-risk diffs frozen, only low-risk allowed |
|
||||
| Exhausted | >= 100% consumed | Incident and security fixes only |
|
||||
|
||||
### Budget Windows
|
||||
|
||||
- **Default cadence**: Monthly (YYYY-MM format)
|
||||
- **Reset behavior**: No carry-over; unused budget expires
|
||||
- **Window boundary**: UTC midnight on the 1st of each month
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Check Budget Status
|
||||
|
||||
```http
|
||||
GET /api/v1/policy/budget/status?serviceId={id}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"budgetId": "budget:my-service:2025-12",
|
||||
"serviceId": "my-service",
|
||||
"tier": 1,
|
||||
"window": "2025-12",
|
||||
"allocated": 200,
|
||||
"consumed": 85,
|
||||
"remaining": 115,
|
||||
"percentageUsed": 42.5,
|
||||
"status": "Yellow"
|
||||
}
|
||||
```
|
||||
|
||||
### Record Consumption
|
||||
|
||||
```http
|
||||
POST /api/v1/policy/budget/consume
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"serviceId": "my-service",
|
||||
"riskPoints": 25,
|
||||
"releaseId": "v1.2.3"
|
||||
}
|
||||
```
|
||||
|
||||
### Adjust Allocation (Earned Capacity)
|
||||
|
||||
```http
|
||||
POST /api/v1/policy/budget/adjust
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"serviceId": "my-service",
|
||||
"adjustment": 40,
|
||||
"reason": "MTTR improvement over 2 months"
|
||||
}
|
||||
```
|
||||
|
||||
### View History
|
||||
|
||||
```http
|
||||
GET /api/v1/policy/budget/history?serviceId={id}&window={yyyy-MM}
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
### Check Status
|
||||
|
||||
```bash
|
||||
stella budget status --service my-service
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Service: my-service
|
||||
Window: 2025-12
|
||||
Tier: Customer-Facing Non-Critical (1)
|
||||
Status: Yellow
|
||||
|
||||
Budget: 85 / 200 RP (42.5%)
|
||||
████████░░░░░░░░░░░░
|
||||
|
||||
Remaining: 115 RP
|
||||
```
|
||||
|
||||
### Consume Budget
|
||||
|
||||
```bash
|
||||
stella budget consume --service my-service --points 25 --reason "Release v1.2.3"
|
||||
```
|
||||
|
||||
### List All Budgets
|
||||
|
||||
```bash
|
||||
stella budget list --status Yellow,Red
|
||||
```
|
||||
|
||||
## Earned Capacity Replenishment
|
||||
|
||||
Services demonstrating improved reliability can earn additional budget capacity:
|
||||
|
||||
### Eligibility Criteria
|
||||
|
||||
1. **MTTR Improvement**: Mean Time to Remediate must improve for 2 consecutive windows
|
||||
2. **CFR Improvement**: Change Failure Rate must improve for 2 consecutive windows
|
||||
3. **No Major Incidents**: No P1 incidents in the evaluation period
|
||||
|
||||
### Increase Calculation
|
||||
|
||||
- Minimum increase: 10% of base allocation
|
||||
- Maximum increase: 20% of base allocation
|
||||
- Scale: Proportional to improvement magnitude
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
Service: payment-api (Tier 2, base 120 RP)
|
||||
MTTR: 48h → 36h → 24h (50% improvement)
|
||||
CFR: 15% → 12% → 8% (47% improvement)
|
||||
|
||||
Earned capacity: +20% = 24 RP
|
||||
New allocation: 144 RP for next window
|
||||
```
|
||||
|
||||
## Notifications
|
||||
|
||||
Budget threshold transitions trigger notifications:
|
||||
|
||||
### Warning (Yellow)
|
||||
|
||||
Sent when budget reaches 40% consumption:
|
||||
|
||||
```
|
||||
Subject: [Warning] Risk Budget at 40% for my-service
|
||||
|
||||
Your risk budget for my-service has reached the warning threshold.
|
||||
|
||||
Current: 80 / 200 RP (40%)
|
||||
Status: Yellow
|
||||
|
||||
Consider pausing non-critical changes until the next budget window.
|
||||
```
|
||||
|
||||
### Critical (Red/Exhausted)
|
||||
|
||||
Sent when budget reaches 70% or 100%:
|
||||
|
||||
```
|
||||
Subject: [Critical] Risk Budget Exhausted for my-service
|
||||
|
||||
Your risk budget for my-service has been exhausted.
|
||||
|
||||
Current: 200 / 200 RP (100%)
|
||||
Status: Exhausted
|
||||
|
||||
Only security fixes and incident responses are allowed.
|
||||
Contact the Platform team for emergency capacity.
|
||||
```
|
||||
|
||||
### Channels
|
||||
|
||||
Notifications are sent via:
|
||||
- Email (to service owners)
|
||||
- Slack (to designated channel)
|
||||
- Microsoft Teams (to designated channel)
|
||||
- Webhooks (for integration)
|
||||
|
||||
## Database Schema
|
||||
|
||||
```sql
|
||||
CREATE TABLE policy.budget_ledger (
|
||||
budget_id TEXT PRIMARY KEY,
|
||||
service_id TEXT NOT NULL,
|
||||
tenant_id TEXT,
|
||||
tier INTEGER NOT NULL,
|
||||
window TEXT NOT NULL,
|
||||
allocated INTEGER NOT NULL,
|
||||
consumed INTEGER NOT NULL DEFAULT 0,
|
||||
status TEXT NOT NULL DEFAULT 'green',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE(service_id, window)
|
||||
);
|
||||
|
||||
CREATE TABLE policy.budget_entries (
|
||||
entry_id TEXT PRIMARY KEY,
|
||||
service_id TEXT NOT NULL,
|
||||
window TEXT NOT NULL,
|
||||
release_id TEXT NOT NULL,
|
||||
risk_points INTEGER NOT NULL,
|
||||
consumed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
FOREIGN KEY (service_id, window) REFERENCES policy.budget_ledger(service_id, window)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_budget_entries_service_window ON policy.budget_entries(service_id, window);
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
# etc/policy.yaml
|
||||
policy:
|
||||
riskBudget:
|
||||
enabled: true
|
||||
windowCadence: monthly # monthly | weekly | sprint
|
||||
carryOver: false
|
||||
defaultTier: 1
|
||||
|
||||
tiers:
|
||||
0: { name: Internal, allocation: 300 }
|
||||
1: { name: CustomerFacingNonCritical, allocation: 200 }
|
||||
2: { name: CustomerFacingCritical, allocation: 120 }
|
||||
3: { name: SafetyCritical, allocation: 80 }
|
||||
|
||||
thresholds:
|
||||
yellow: 40
|
||||
red: 70
|
||||
exhausted: 100
|
||||
|
||||
notifications:
|
||||
enabled: true
|
||||
channels: [email, slack]
|
||||
aggregationWindow: 1h # Debounce rapid transitions
|
||||
|
||||
earnedCapacity:
|
||||
enabled: true
|
||||
requiredImprovementWindows: 2
|
||||
minIncreasePercent: 10
|
||||
maxIncreasePercent: 20
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Unknown Budget Gates](./unknowns-budget-gates.md)
|
||||
- [Verdict Attestations](../attestor/verdict-format.md)
|
||||
- [BudgetCheckPredicate Model](../../api/attestor/budget-check-predicate.md)
|
||||
- [Risk Point Scoring](./risk-point-scoring.md)
|
||||
- [Diff-Aware Release Gates](./diff-aware-gates.md)
|
||||
|
||||
@@ -31,6 +31,13 @@ Scanner analyses container images layer-by-layer, producing deterministic SBOM f
|
||||
- `docs/modules/scanner/architecture.md`
|
||||
- `docs/modules/scanner/implementation_plan.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/product-advisories/CONSOLIDATED - Diff-Aware Release Gates and Risk Budgets.md` — Master reference for delta verdicts, smart-diff algorithms, and determinism requirements that Scanner must honor.
|
||||
|
||||
## Related Product Advisories
|
||||
- **[Consolidated: Diff-Aware Release Gates and Risk Budgets](../../product-advisories/CONSOLIDATED%20-%20Diff-Aware%20Release%20Gates%20and%20Risk%20Budgets.md)** — Risk budgets, delta verdicts, smart-diff algorithms
|
||||
- **[Consolidated: Deterministic Evidence and Verdict Architecture](../../product-advisories/CONSOLIDATED%20-%20Deterministic%20Evidence%20and%20Verdict%20Architecture.md)** — Determinism guarantees, canonical serialization, keyless signing
|
||||
- **[Determinism Specification](../../technical/architecture/determinism-specification.md)** — Technical spec for digest algorithms and canonicalization rules
|
||||
- **[Smart-Diff Technical Reference](../../product-advisories/archived/2025-12-21-moat-gap-closure/14-Dec-2025%20-%20Smart-Diff%20Technical%20Reference.md)** — Detailed reachability gate and call-stack analysis specs
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to `DOING`/`DONE` in both correspoding sprint file `/docs/implplan/SPRINT_*.md` and the local `TASKS.md` when you start or finish work.
|
||||
|
||||
@@ -1,99 +1,40 @@
|
||||
# Keyless Signing Guide
|
||||
|
||||
This guide explains how to configure and use keyless signing with Sigstore Fulcio for CI/CD pipelines.
|
||||
|
||||
## Overview
|
||||
|
||||
Keyless signing uses ephemeral X.509 certificates from Sigstore Fulcio, eliminating the need for persistent signing keys. This approach is ideal for CI/CD pipelines where key management is complex and error-prone.
|
||||
Keyless signing eliminates the need to manage long-lived signing keys by using short-lived X.509 certificates (~10 minute TTL) issued by Fulcio based on OIDC identity tokens. This approach:
|
||||
|
||||
### How It Works
|
||||
- **Zero key management**: No secrets to rotate or protect
|
||||
- **Identity-bound signatures**: Signatures are cryptographically tied to the CI/CD identity
|
||||
- **Non-repudiation**: Audit trail via Rekor transparency log
|
||||
- **Industry standard**: Compatible with Sigstore ecosystem (cosign, gitsign, etc.)
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ CI Pipeline │────▶│ OIDC Provider│────▶│ Fulcio │────▶│ Rekor │
|
||||
│ │ │ (GitHub/GL) │ │ (Sigstore) │ │ (Sigstore) │
|
||||
│ 1. Get token │ │ 2. Issue JWT │ │ 3. Issue cert│ │ 4. Log entry │
|
||||
│ │ │ (5 min) │ │ (10 min) │ │ (permanent) │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
|
||||
│ │
|
||||
│ │
|
||||
└───────────── Attestation with cert + Rekor proof ───────────┘
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ CI Runner │────▶│ OIDC Token │────▶│ Fulcio │────▶│ Ephemeral │
|
||||
│ (GitHub/GL) │ │ Provider │ │ CA │ │ Cert │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Sign DSSE │
|
||||
│ Envelope │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
1. **OIDC Token**: Pipeline requests identity token from CI platform
|
||||
2. **Fulcio Certificate**: Token exchanged for short-lived signing certificate (~10 min)
|
||||
3. **Ephemeral Key**: Private key exists only in memory during signing
|
||||
4. **Rekor Logging**: Signature logged to transparency log for verification after cert expiry
|
||||
|
||||
### Key Benefits
|
||||
|
||||
| Benefit | Description |
|
||||
|---------|-------------|
|
||||
| **Zero Key Management** | No secrets to rotate, store, or protect |
|
||||
| **Identity Binding** | Signatures tied to OIDC identity (repo, branch, workflow) |
|
||||
| **Audit Trail** | All signatures logged to Rekor transparency log |
|
||||
| **Short-lived Certs** | Minimizes exposure window (~10 minutes) |
|
||||
| **Industry Standard** | Adopted by Kubernetes, npm, PyPI, and major ecosystems |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. StellaOps CLI installed
|
||||
2. CI platform with OIDC support (GitHub Actions, GitLab CI, Gitea)
|
||||
3. Network access to Fulcio and Rekor (or private instances)
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
name: Sign Container Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build-and-sign:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build and Push Image
|
||||
id: build
|
||||
run: |
|
||||
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
|
||||
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
|
||||
echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/${{ github.repository }}:${{ github.sha }} | cut -d@ -f2)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Keyless Sign
|
||||
uses: stella-ops/sign-action@v1
|
||||
with:
|
||||
artifact-digest: ${{ steps.build.outputs.digest }}
|
||||
artifact-type: image
|
||||
```
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Sign with ambient OIDC token (in CI environment)
|
||||
stella attest sign --keyless --artifact sha256:abc123...
|
||||
|
||||
# Sign with explicit token
|
||||
STELLAOPS_OIDC_TOKEN="..." stella attest sign --keyless --artifact sha256:abc123...
|
||||
|
||||
# Verify signature (checks Rekor proof)
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "repo:myorg/myrepo:ref:refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
```
|
||||
1. **CI runner provides OIDC token** - GitHub Actions, GitLab CI, etc. provide ambient identity tokens
|
||||
2. **Token exchanged for certificate** - Fulcio validates the OIDC token and issues a short-lived certificate
|
||||
3. **Ephemeral key generation** - A new ECDSA P-256 or Ed25519 key is generated per signing operation
|
||||
4. **DSSE signing** - The payload is signed using the ephemeral key
|
||||
5. **Certificate attached** - The Fulcio certificate is included in the signed bundle for verification
|
||||
|
||||
## Configuration
|
||||
|
||||
### Signer Configuration
|
||||
### Basic Configuration
|
||||
|
||||
```yaml
|
||||
# etc/signer.yaml
|
||||
@@ -107,21 +48,12 @@ signer:
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
oidc:
|
||||
issuer: "https://authority.internal"
|
||||
clientId: "signer-keyless"
|
||||
useAmbientToken: true
|
||||
algorithms:
|
||||
preferred: "ECDSA_P256"
|
||||
allowed: ["ECDSA_P256", "Ed25519"]
|
||||
certificate:
|
||||
rootBundlePath: "/etc/stellaops/fulcio-roots.pem"
|
||||
validateChain: true
|
||||
requireSCT: true
|
||||
```
|
||||
|
||||
### Private Fulcio Instance
|
||||
|
||||
For air-gapped or high-security environments, deploy a private Fulcio instance:
|
||||
For air-gapped or private deployments:
|
||||
|
||||
```yaml
|
||||
signer:
|
||||
@@ -129,145 +61,170 @@ signer:
|
||||
keyless:
|
||||
fulcio:
|
||||
url: "https://fulcio.internal.example.com"
|
||||
oidc:
|
||||
issuer: "https://keycloak.internal.example.com/realms/stellaops"
|
||||
certificate:
|
||||
rootBundlePath: "/etc/stellaops/private-fulcio-roots.pem"
|
||||
rootBundlePath: "/etc/stellaops/fulcio-roots.pem"
|
||||
additionalRoots:
|
||||
- |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjzCCATSgAwIBAgIRANZl...
|
||||
-----END CERTIFICATE-----
|
||||
```
|
||||
|
||||
## Identity Verification
|
||||
|
||||
### Identity Constraints
|
||||
|
||||
When verifying signatures, specify which identities are trusted:
|
||||
Restrict which identities are allowed to sign:
|
||||
|
||||
```yaml
|
||||
signer:
|
||||
signing:
|
||||
keyless:
|
||||
identity:
|
||||
expectedIssuers:
|
||||
- "https://token.actions.githubusercontent.com"
|
||||
- "https://gitlab.com"
|
||||
expectedSubjectPatterns:
|
||||
- "^https://github\.com/myorg/.*$"
|
||||
- "^project_path:mygroup/myproject:.*$"
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Sign Artifacts
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
sign:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # Required for OIDC token
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install StellaOps CLI
|
||||
run: |
|
||||
curl -sSL https://get.stella-ops.io | bash
|
||||
|
||||
- name: Sign with keyless mode
|
||||
run: |
|
||||
stella sign --mode keyless \
|
||||
--image ghcr.io/${{ github.repository }}:${{ github.sha }}
|
||||
```
|
||||
|
||||
### GitLab CI
|
||||
|
||||
```yaml
|
||||
sign:
|
||||
image: registry.stella-ops.io/cli:latest
|
||||
id_tokens:
|
||||
SIGSTORE_ID_TOKEN:
|
||||
aud: sigstore
|
||||
script:
|
||||
- stella sign --mode keyless --image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
```
|
||||
|
||||
## Algorithm Support
|
||||
|
||||
| Algorithm | Status | Use Case |
|
||||
|-----------|--------|----------|
|
||||
| ECDSA P-256 | Preferred | Default, widest compatibility |
|
||||
| Ed25519 | Supported | Better performance, growing adoption |
|
||||
|
||||
Configure preferred algorithm:
|
||||
|
||||
```yaml
|
||||
signer:
|
||||
signing:
|
||||
keyless:
|
||||
algorithms:
|
||||
preferred: "ECDSA_P256"
|
||||
allowed: ["ECDSA_P256", "Ed25519"]
|
||||
```
|
||||
|
||||
## Signed Bundle Format
|
||||
|
||||
The keyless signing produces a DSSE envelope with embedded certificate:
|
||||
|
||||
```json
|
||||
{
|
||||
"payloadType": "application/vnd.in-toto+json",
|
||||
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEi...",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "",
|
||||
"sig": "MEUCIQD..."
|
||||
}
|
||||
],
|
||||
"certificateChain": [
|
||||
"-----BEGIN CERTIFICATE-----\nMIIC...",
|
||||
"-----BEGIN CERTIFICATE-----\nMIIB..."
|
||||
],
|
||||
"signingMode": "keyless",
|
||||
"signingIdentity": {
|
||||
"issuer": "https://token.actions.githubusercontent.com",
|
||||
"subject": "https://github.com/org/repo/.github/workflows/ci.yml@refs/heads/main"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
Bundles signed with keyless mode can be verified using:
|
||||
|
||||
```bash
|
||||
stella attest verify \
|
||||
--artifact sha256:abc123... \
|
||||
--certificate-identity "repo:myorg/myrepo:ref:refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
# Verify a signed bundle
|
||||
stella verify --bundle verdict.json \
|
||||
--expected-issuer "https://token.actions.githubusercontent.com" \
|
||||
--expected-subject "https://github.com/myorg/myrepo/*"
|
||||
```
|
||||
|
||||
### Platform Identity Patterns
|
||||
|
||||
#### GitHub Actions
|
||||
|
||||
| Pattern | Matches |
|
||||
|---------|---------|
|
||||
| `repo:org/repo:.*` | Any ref in repository |
|
||||
| `repo:org/repo:ref:refs/heads/main` | Main branch only |
|
||||
| `repo:org/repo:ref:refs/tags/v.*` | Version tags |
|
||||
| `repo:org/repo:environment:production` | Production environment |
|
||||
|
||||
**Issuer:** `https://token.actions.githubusercontent.com`
|
||||
|
||||
#### GitLab CI
|
||||
|
||||
| Pattern | Matches |
|
||||
|---------|---------|
|
||||
| `project_path:group/project:.*` | Any ref in project |
|
||||
| `project_path:group/project:ref_type:branch:ref:main` | Main branch |
|
||||
| `project_path:group/project:ref_protected:true` | Protected refs only |
|
||||
|
||||
**Issuer:** `https://gitlab.com` (or self-hosted URL)
|
||||
|
||||
## Long-Term Verification
|
||||
|
||||
### The Problem
|
||||
|
||||
Fulcio certificates expire in ~10 minutes. How do you verify signatures months later?
|
||||
|
||||
### The Solution: Rekor Proofs
|
||||
|
||||
```
|
||||
At signing time:
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Signature + Certificate + Signed-Certificate-Timestamp (SCT) │
|
||||
│ ↓ │
|
||||
│ Logged to Rekor │
|
||||
│ ↓ │
|
||||
│ Merkle Inclusion Proof returned │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
|
||||
At verification time (even years later):
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 1. Check signature is valid (using cert public key) │
|
||||
│ 2. Check SCT proves cert was logged when valid │
|
||||
│ 3. Check Rekor inclusion proof (entry was logged) │
|
||||
│ 4. Check signing time was within cert validity window │
|
||||
│ ↓ │
|
||||
│ Signature is valid! ✓ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Attestation Bundles
|
||||
|
||||
For air-gapped verification, StellaOps bundles attestations with proofs:
|
||||
|
||||
```bash
|
||||
# Export bundle with Rekor proofs
|
||||
stella attest export-bundle \
|
||||
--image sha256:abc123... \
|
||||
--include-proofs \
|
||||
--output attestation-bundle.json
|
||||
|
||||
# Verify offline
|
||||
stella attest verify --offline \
|
||||
--bundle attestation-bundle.json \
|
||||
--artifact sha256:abc123...
|
||||
```
|
||||
The verification process:
|
||||
1. Validates the certificate chain to Fulcio roots
|
||||
2. Verifies the signature using the certificate's public key
|
||||
3. Checks identity claims match expectations
|
||||
4. Optionally validates SCT (Signed Certificate Timestamp)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
### Common Issues
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `OIDC token expired` | Token older than 5 minutes | Re-acquire token before signing |
|
||||
| `Fulcio unavailable` | Network issues | Check connectivity, increase timeout |
|
||||
| `Certificate chain invalid` | Wrong Fulcio roots | Update root bundle |
|
||||
| `Identity mismatch` | Wrong verify constraints | Check issuer and identity patterns |
|
||||
| `Rekor proof missing` | Logging failed | Retry signing, check Rekor status |
|
||||
**OIDC token not available**
|
||||
- Ensure id-token: write permission in GitHub Actions
|
||||
- Ensure id_tokens is configured in GitLab CI
|
||||
- Check ACTIONS_ID_TOKEN_REQUEST_URL environment variable
|
||||
|
||||
### Debug Mode
|
||||
**Fulcio returns 401**
|
||||
- OIDC token may have expired (default 5-10 min validity)
|
||||
- Audience mismatch - ensure token is for sigstore
|
||||
- Issuer not trusted by Fulcio instance
|
||||
|
||||
**Certificate chain validation failed**
|
||||
- Root certificate bundle may be outdated
|
||||
- Private Fulcio instance roots not configured
|
||||
- Certificate expired (Fulcio certs are ~10 min TTL)
|
||||
|
||||
### Debug Logging
|
||||
|
||||
Enable verbose logging:
|
||||
|
||||
```bash
|
||||
# Enable verbose logging
|
||||
STELLAOPS_LOG_LEVEL=debug stella attest sign --keyless --artifact sha256:...
|
||||
|
||||
# Inspect certificate details
|
||||
stella attest inspect --artifact sha256:... --show-cert
|
||||
STELLAOPS_LOG_LEVEL=debug stella sign --mode keyless ...
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Always verify identity**: Never accept `.*` as the full identity pattern
|
||||
2. **Require Rekor proofs**: Use `--require-rekor` for production verification
|
||||
3. **Pin OIDC issuers**: Only trust expected issuers
|
||||
4. **Use environment constraints**: More specific than branch names
|
||||
5. **Monitor signing activity**: Alert on unexpected identities
|
||||
|
||||
### Threat Model
|
||||
|
||||
| Threat | Mitigation |
|
||||
|--------|------------|
|
||||
| Stolen OIDC token | Short lifetime (~5 min), audience binding |
|
||||
| Fulcio compromise | Certificate Transparency (SCT), multiple roots |
|
||||
| Rekor compromise | Multiple witnesses, checkpoints, consistency proofs |
|
||||
| Private key theft | Ephemeral keys, never persisted |
|
||||
1. **Ephemeral keys never persist** - Keys exist only in memory during signing
|
||||
2. **Short-lived certificates** - ~10 minute validity limits exposure window
|
||||
3. **Identity verification** - Always configure expectedIssuers and expectedSubjectPatterns in production
|
||||
4. **SCT validation** - Enable requireSct: true for public Fulcio instances
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Signer Architecture](../architecture.md)
|
||||
- [Attestor Bundle Format](../../attestor/bundle-format.md)
|
||||
- [Air-Gap Verification](../../../airgap/attestation-verification.md)
|
||||
- [CI/CD Integration](../../../guides/cicd-signing.md)
|
||||
|
||||
## External Resources
|
||||
|
||||
- [DSSE Envelope Format](../dsse-format.md)
|
||||
- [CI/CD Gate Integration](../../policy/guides/cicd-gates.md)
|
||||
- [Sigstore Documentation](https://docs.sigstore.dev/)
|
||||
- [Fulcio Overview](https://docs.sigstore.dev/certificate_authority/overview/)
|
||||
- [Rekor Transparency Log](https://docs.sigstore.dev/logging/overview/)
|
||||
- [cosign Keyless Signing](https://docs.sigstore.dev/signing/quickstart/)
|
||||
|
||||
@@ -20,6 +20,8 @@ Web provides the Angular 17 single-page application (SPA) frontend for StellaOps
|
||||
- VEX statement review and approval workflows
|
||||
- Task pack execution monitoring
|
||||
- Admin console for configuration and user management
|
||||
- **Unified Triage Experience** - Smart-Diff Compare View, Triage Canvas, Risk Dashboard
|
||||
- **Risk Budget Visualization** - Burn-up charts, heatmaps, exception ledger
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -59,10 +61,22 @@ npx playwright test
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Architecture: `./architecture.md` (if exists)
|
||||
### Triage Experience
|
||||
- [Unified Triage Specification](./unified-triage-specification.md) - Consolidated triage requirements
|
||||
- [Smart-Diff UI Architecture](./smart-diff-ui-architecture.md) - Compare view design
|
||||
- [Triage Component Catalog](./triage-component-catalog.md) - Angular component documentation
|
||||
- [Competitive Triage Patterns](./competitive-triage-patterns.md) - Industry comparison
|
||||
|
||||
### Module Dependencies
|
||||
- UI Module: `../ui/` (shared UI components)
|
||||
- Gateway: `../gateway/`
|
||||
- Authority: `../authority/`
|
||||
- Gateway: `../gateway/` (API access)
|
||||
- Authority: `../authority/` (authentication)
|
||||
- VulnExplorer: `../vulnexplorer/` (vulnerability data)
|
||||
|
||||
### Implementation Sprints
|
||||
- [Smart-Diff Compare](../../implplan/SPRINT_20251226_012_FE_smart_diff_compare.md)
|
||||
- [Triage Canvas](../../implplan/SPRINT_20251226_013_FE_triage_canvas.md)
|
||||
- [Risk Dashboard](../../implplan/SPRINT_20251226_004_FE_risk_dashboard.md)
|
||||
|
||||
## Current Status
|
||||
|
||||
|
||||
154
docs/modules/web/competitive-triage-patterns.md
Normal file
154
docs/modules/web/competitive-triage-patterns.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Competitive Triage UI Patterns - Design Document
|
||||
|
||||
> **Sprint:** SPRINT_20251226_010_FE_visual_diff_enhancements
|
||||
> **Task:** VD-ENH-09
|
||||
> **Status:** Complete
|
||||
> **Author:** Implementation Team
|
||||
> **Date:** 2025-12-26
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document captures competitive insights from leading vulnerability management tools and recommends patterns for adoption in StellaOps' visual diff and triage UI.
|
||||
|
||||
## Competitive Analysis
|
||||
|
||||
### Snyk — Reachability + Continuous Context
|
||||
|
||||
**What they do:**
|
||||
- Reachability analysis builds call graphs to determine if vulnerable code is actually reachable
|
||||
- Risk scores factor in reachability, not just CVSS severity
|
||||
- Static program analysis combined with AI and expert curation
|
||||
- Continuous monitoring tracks issues over time as projects are rescanned
|
||||
|
||||
**Adoption recommendation:** ✅ **Already implemented**
|
||||
- `GraphDiffComponent` visualizes reachability graphs with call paths
|
||||
- Hover highlighting shows connected paths from entry points to sinks
|
||||
- Plain language explanations help users understand "why" a finding matters
|
||||
|
||||
### Anchore — Vulnerability Annotations & VEX Export
|
||||
|
||||
**What they do:**
|
||||
- Vulnerability annotation workflows via UI or API
|
||||
- Labels: "not applicable", "mitigated", "under investigation"
|
||||
- Export as OpenVEX and CycloneDX VEX formats
|
||||
- Curated reasoning reduces redundant triage downstream
|
||||
|
||||
**Adoption recommendation:** ✅ **Already implemented**
|
||||
- `TriageWorkspaceComponent` provides VEX decisioning with trust levels
|
||||
- `DeltaVerdict` backend exports signed VEX statements
|
||||
- Attestable exception objects with expiries and audit trails
|
||||
|
||||
### Prisma Cloud — Runtime Defense
|
||||
|
||||
**What they do:**
|
||||
- Runtime profiling and behavioral baselines for containers
|
||||
- Process, file, and network rule enforcement
|
||||
- Learning models detect anomalies
|
||||
- Runtime context during operational incidents
|
||||
|
||||
**Adoption recommendation:** ⚠️ **Partial - Signals module**
|
||||
- `Signals` module provides runtime observation correlation
|
||||
- Hot symbol index tracks runtime function execution
|
||||
- Integration with FuncProof links runtime observations to static analysis
|
||||
|
||||
---
|
||||
|
||||
## Recommended UI Patterns
|
||||
|
||||
### 1. Unified Triage Canvas
|
||||
|
||||
**Pattern:** Single view combining static analysis with runtime evidence
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ TRIAGE CANVAS │
|
||||
├──────────────────┬────────────────────┬─────────────────────────┤
|
||||
│ Graph View │ Evidence Panel │ Decision Panel │
|
||||
│ │ │ │
|
||||
│ ┌─────┐ │ • SBOM Component │ ○ Not Affected │
|
||||
│ │main │────► │ • VEX Statement │ ○ Under Investigation │
|
||||
│ └─────┘ │ │ • Reachability │ ○ Affected │
|
||||
│ ▼ │ • Runtime Obs. │ ○ Fixed │
|
||||
│ ┌─────┐ │ • Policy Match │ │
|
||||
│ │vuln │ │ │ [Record Decision] │
|
||||
│ └─────┘ │ │ │
|
||||
└──────────────────┴────────────────────┴─────────────────────────┘
|
||||
```
|
||||
|
||||
**Implementation:** Already complete via `TriageWorkspaceComponent` + `GraphDiffComponent`
|
||||
|
||||
### 2. Exploitability Scoring Visualization
|
||||
|
||||
**Pattern:** Visual risk score breakdown showing contributing factors
|
||||
|
||||
| Component | Weight | Score | Visualization |
|
||||
|-----------|--------|-------|---------------|
|
||||
| Reachability | 25% | 95 | ████████░░ |
|
||||
| VEX Coverage | 20% | 90 | █████████░ |
|
||||
| SBOM Completeness | 20% | 85 | ████████░░ |
|
||||
| Runtime Evidence | 20% | 88 | ████████░░ |
|
||||
| Policy Freshness | 15% | 92 | █████████░ |
|
||||
|
||||
**Implementation:** `ProofTreeComponent` displays trust score breakdown with donut chart
|
||||
|
||||
### 3. Attack Path Diagrams
|
||||
|
||||
**Pattern:** Entry point → vulnerable function path highlighting
|
||||
|
||||
- Color-coded paths (green=safe, red=vulnerable, amber=uncertain)
|
||||
- Hop count indicators
|
||||
- Confidence levels per path segment
|
||||
- Interactive path exploration with zoom-to-fit
|
||||
|
||||
**Implementation:** `GraphDiffComponent` with `findPath()` and path highlighting
|
||||
|
||||
### 4. Evidence Provenance Indicators
|
||||
|
||||
**Pattern:** Visual indicators showing evidence source and trust level
|
||||
|
||||
| Indicator | Meaning |
|
||||
|-----------|---------|
|
||||
| 🔒 Signed | DSSE-signed evidence |
|
||||
| ✓ Verified | Signature verified |
|
||||
| ⚡ Runtime | Observed at runtime |
|
||||
| 📋 Policy | Policy-derived |
|
||||
| 👤 Manual | Human decision |
|
||||
|
||||
**Implementation:** `ProofTreeComponent` with evidence chunk icons
|
||||
|
||||
---
|
||||
|
||||
## Adoption Status
|
||||
|
||||
| Pattern | Status | Component |
|
||||
|---------|--------|-----------|
|
||||
| Reachability graphs | ✅ Complete | `GraphDiffComponent` |
|
||||
| VEX decisioning | ✅ Complete | `TriageWorkspaceComponent` |
|
||||
| Attack path visualization | ✅ Complete | `GraphDiffComponent` + path highlighting |
|
||||
| Evidence provenance | ✅ Complete | `ProofTreeComponent` |
|
||||
| Plain language explanations | ✅ Complete | `PlainLanguageService` |
|
||||
| Runtime observation correlation | ✅ Complete | `Signals` module integration |
|
||||
| Offline replay packs | ✅ Complete | Evidence bundle export |
|
||||
| Trust score breakdown | ✅ Complete | `ProofTreeComponent` donut chart |
|
||||
|
||||
---
|
||||
|
||||
## Differentiation Strategy
|
||||
|
||||
StellaOps differentiates from competitors by unifying these patterns into a single, evidence-rich, policy-driven triage experience:
|
||||
|
||||
1. **Evidence-first:** Every decision is backed by cryptographic evidence
|
||||
2. **Policy-driven:** VEX as core policy objects, not just export format
|
||||
3. **Attestable:** Exceptions are attestable contracts with audit trails
|
||||
4. **Offline-capable:** Same UI/interactions work in air-gapped environments
|
||||
5. **Deterministic:** Reproducible verdicts across runs and environments
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Snyk Reachability Analysis](https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/reachability-analysis)
|
||||
- [Anchore Vulnerability Annotations](https://docs.anchore.com/current/docs/vulnerability_management/vuln_annotations/)
|
||||
- [Prisma Cloud Runtime Defense](https://docs.prismacloud.io/en/compute-edition/30/admin-guide/runtime-defense/runtime-defense-containers)
|
||||
@@ -1,9 +1,9 @@
|
||||
# Smart-Diff UI Architecture
|
||||
|
||||
**Version:** 1.0
|
||||
**Status:** Draft
|
||||
**Last Updated:** 2025-12-22
|
||||
**Sprint Reference:** SPRINT_4200_0002_0003
|
||||
**Version:** 1.1
|
||||
**Status:** Active
|
||||
**Last Updated:** 2025-12-26
|
||||
**Sprint Reference:** SPRINT_20251226_012_FE_smart_diff_compare
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -352,7 +352,9 @@ For large deltas (> 100 items), the items pane uses virtual scrolling:
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Sprint: Delta Compare View UI](../../implplan/SPRINT_4200_0002_0003_delta_compare_view.md)
|
||||
- [Sprint: Delta Compare Backend API](../../implplan/SPRINT_4200_0002_0006_delta_compare_api.md)
|
||||
- [Unified Triage Specification](./unified-triage-specification.md) - Consolidated triage experience requirements
|
||||
- [Triage Component Catalog](./triage-component-catalog.md) - Angular component documentation
|
||||
- [Sprint: Smart-Diff Compare View](../../implplan/SPRINT_20251226_012_FE_smart_diff_compare.md) - Current implementation sprint
|
||||
- [Sprint: Triage Canvas](../../implplan/SPRINT_20251226_013_FE_triage_canvas.md) - Unified triage canvas sprint
|
||||
- [Sprint: Risk Dashboard](../../implplan/SPRINT_20251226_004_FE_risk_dashboard.md) - Risk budget visualization sprint
|
||||
- [Smart-Diff CLI Reference](../../cli/smart-diff-cli.md)
|
||||
- [Advisory: Smart Diff - Reproducibility as a Feature](../../product-advisories/archived/22-Dec-2025/21-Dec-2025%20-%20Smart%20Diff%20-%20Reproducibility%20as%20a%20Feature.md)
|
||||
|
||||
445
docs/modules/web/triage-component-catalog.md
Normal file
445
docs/modules/web/triage-component-catalog.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# Triage Component Catalog
|
||||
|
||||
**Version:** 1.0
|
||||
**Status:** Active
|
||||
**Last Updated:** 2025-12-26
|
||||
**Sprint:** SPRINT_20251226_014_DOCS_triage_consolidation
|
||||
|
||||
## Overview
|
||||
|
||||
This document catalogs all Angular components used in the unified triage experience, including the Smart-Diff Compare View, Triage Canvas, and Risk Dashboard. Each component is documented with its responsibilities, inputs/outputs, and relationships.
|
||||
|
||||
## Component Hierarchy
|
||||
|
||||
```
|
||||
src/Web/StellaOps.Web/src/app/
|
||||
├── features/
|
||||
│ ├── triage/
|
||||
│ │ ├── triage-canvas/
|
||||
│ │ │ ├── triage-canvas.component.ts [Container]
|
||||
│ │ │ ├── triage-list.component.ts
|
||||
│ │ │ ├── triage-detail.component.ts
|
||||
│ │ │ ├── ai-recommendation-panel.component.ts
|
||||
│ │ │ ├── vex-decision-modal.component.ts
|
||||
│ │ │ └── vex-history.component.ts
|
||||
│ │ └── compare/
|
||||
│ │ ├── compare-view.component.ts [Container]
|
||||
│ │ ├── baseline-selector.component.ts
|
||||
│ │ ├── trust-indicators.component.ts
|
||||
│ │ ├── delta-summary-strip.component.ts
|
||||
│ │ ├── three-pane-layout.component.ts
|
||||
│ │ ├── categories-pane.component.ts
|
||||
│ │ ├── items-pane.component.ts
|
||||
│ │ ├── proof-pane.component.ts
|
||||
│ │ └── export-actions.component.ts
|
||||
│ ├── risk-budget/
|
||||
│ │ ├── risk-dashboard.component.ts [Container]
|
||||
│ │ ├── burn-up-chart.component.ts
|
||||
│ │ ├── unknowns-heatmap.component.ts
|
||||
│ │ ├── delta-table.component.ts
|
||||
│ │ ├── exception-ledger.component.ts
|
||||
│ │ └── kpi-tiles.component.ts
|
||||
│ └── vulnerabilities/
|
||||
│ └── vulnerability-detail.component.ts
|
||||
└── shared/
|
||||
└── components/
|
||||
├── confidence-badge.component.ts
|
||||
├── determinism-badge.component.ts
|
||||
├── severity-indicator.component.ts
|
||||
└── evidence-chain.component.ts
|
||||
```
|
||||
|
||||
## Container Components
|
||||
|
||||
### TriageCanvasComponent
|
||||
|
||||
**Location:** `features/triage/triage-canvas/triage-canvas.component.ts`
|
||||
**Sprint:** SPRINT_20251226_013_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Main container for the unified triage experience. Orchestrates list, detail, and decision panels.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| initialVulnId | string? | Pre-select vulnerability by ID |
|
||||
| environment | string? | Filter by environment |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| triageComplete | EventEmitter<VexDecision> | Emitted when triage decision saved |
|
||||
| queueExhausted | EventEmitter<void> | Emitted when all items triaged |
|
||||
|
||||
**Child Components:**
|
||||
- TriageListComponent
|
||||
- TriageDetailComponent
|
||||
- AiRecommendationPanel
|
||||
- VexDecisionModalComponent
|
||||
- VexHistoryComponent
|
||||
|
||||
---
|
||||
|
||||
### CompareViewComponent
|
||||
|
||||
**Location:** `features/triage/compare/compare-view.component.ts`
|
||||
**Sprint:** SPRINT_20251226_012_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Three-pane Smart-Diff comparison view with baseline selection and proof display.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| currentDigest | string | Digest of current scan |
|
||||
| baselineDigest | string? | Digest of baseline (auto-selected if not provided) |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| baselineChanged | EventEmitter<string> | New baseline selected |
|
||||
| exportRequested | EventEmitter<ExportFormat> | Export action triggered |
|
||||
|
||||
**Child Components:**
|
||||
- BaselineSelectorComponent
|
||||
- TrustIndicatorsComponent
|
||||
- DeltaSummaryStripComponent
|
||||
- ThreePaneLayoutComponent
|
||||
- ExportActionsComponent
|
||||
|
||||
---
|
||||
|
||||
### RiskDashboardComponent
|
||||
|
||||
**Location:** `features/risk-budget/risk-dashboard.component.ts`
|
||||
**Sprint:** SPRINT_20251226_004_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Risk budget visualization with burn-up charts, heatmaps, and exception ledger.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| serviceId | string | Service to display budget for |
|
||||
| window | BudgetWindow | Budget window (monthly, weekly) |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| exceptionCreated | EventEmitter<Exception> | New exception added |
|
||||
| thresholdAlert | EventEmitter<ThresholdAlert> | Budget threshold crossed |
|
||||
|
||||
**Child Components:**
|
||||
- BurnUpChartComponent
|
||||
- UnknownsHeatmapComponent
|
||||
- DeltaTableComponent
|
||||
- ExceptionLedgerComponent
|
||||
- KpiTilesComponent
|
||||
|
||||
---
|
||||
|
||||
## Presentation Components
|
||||
|
||||
### TriageListComponent
|
||||
|
||||
**Location:** `features/triage/triage-canvas/triage-list.component.ts`
|
||||
**Sprint:** SPRINT_20251226_013_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Paginated, filterable list of vulnerabilities for triage.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| vulnerabilities | Vulnerability[] | List of vulnerabilities |
|
||||
| selectedId | string? | Currently selected vulnerability |
|
||||
| filters | TriageFilters | Active filters |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| selectionChange | EventEmitter<Vulnerability> | Vulnerability selected |
|
||||
| bulkAction | EventEmitter<BulkActionRequest> | Bulk triage requested |
|
||||
|
||||
**Features:**
|
||||
- Virtual scrolling (cdk-virtual-scroll) for large lists
|
||||
- Filter chips: severity, KEV, exploitability, fix-available
|
||||
- Quick actions: "Mark Not Affected", "Request Analysis"
|
||||
|
||||
---
|
||||
|
||||
### VexDecisionModalComponent
|
||||
|
||||
**Location:** `features/triage/triage-canvas/vex-decision-modal.component.ts`
|
||||
**Sprint:** SPRINT_20251226_013_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Modal for creating/editing VEX decisions with full form controls.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| vulnerability | Vulnerability | Target vulnerability |
|
||||
| existingDecision | VexDecision? | Decision to edit |
|
||||
| suggestedJustification | string? | AI-suggested justification |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| save | EventEmitter<VexDecision> | Decision saved |
|
||||
| cancel | EventEmitter<void> | Modal cancelled |
|
||||
|
||||
**Form Fields:**
|
||||
- Status: NotAffected, AffectedMitigated, AffectedUnmitigated, Fixed
|
||||
- Justification type (matches VexJustificationType enum)
|
||||
- Evidence references (PR, Ticket, Doc, Commit links)
|
||||
- Scope: environments and projects
|
||||
- Validity window: NotBefore/NotAfter dates
|
||||
- "Sign as Attestation" checkbox
|
||||
|
||||
---
|
||||
|
||||
### ThreePaneLayoutComponent
|
||||
|
||||
**Location:** `features/triage/compare/three-pane-layout.component.ts`
|
||||
**Sprint:** SPRINT_20251226_012_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Responsive three-column layout for Categories, Items, and Proof panes.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| delta | Delta | Computed delta with items |
|
||||
| selectedCategory | Category? | Currently selected category |
|
||||
| selectedItem | DeltaItem? | Currently selected item |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| categorySelected | EventEmitter<Category> | Category clicked |
|
||||
| itemSelected | EventEmitter<DeltaItem> | Item clicked |
|
||||
|
||||
**Layout Behavior:**
|
||||
- Desktop: 3 columns (20% / 40% / 40%)
|
||||
- Tablet: 2 columns (collapsed categories)
|
||||
- Mobile: Single pane with navigation
|
||||
|
||||
---
|
||||
|
||||
### BurnUpChartComponent
|
||||
|
||||
**Location:** `features/risk-budget/burn-up-chart.component.ts`
|
||||
**Sprint:** SPRINT_20251226_004_FE
|
||||
**Status:** TODO
|
||||
|
||||
**Purpose:** Risk budget burn-up chart showing budget line vs actual risk over time.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| budgetData | BudgetTimeSeries | Historical budget data |
|
||||
| releaseDate | Date | Target release date |
|
||||
| showMarkers | boolean | Show milestone markers |
|
||||
|
||||
**Outputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| pointClicked | EventEmitter<DataPoint> | Chart point clicked |
|
||||
|
||||
**Chart Features:**
|
||||
- X-axis: Calendar dates
|
||||
- Y-axis: Risk points
|
||||
- Lines: Budget (flat), Actual (cumulative)
|
||||
- Shaded regions: Headroom (green), Overrun (red)
|
||||
- Markers: Feature freeze, pen-test, dependency bumps
|
||||
|
||||
---
|
||||
|
||||
## Shared Components
|
||||
|
||||
### ConfidenceBadgeComponent
|
||||
|
||||
**Location:** `shared/components/confidence-badge.component.ts`
|
||||
**Status:** COMPLETE
|
||||
|
||||
**Purpose:** Displays confidence level with color-coded visual indicator.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| confidence | number | 0-1 confidence value |
|
||||
| showValue | boolean | Display numeric value |
|
||||
|
||||
---
|
||||
|
||||
### DeterminismBadgeComponent
|
||||
|
||||
**Location:** `shared/components/determinism-badge.component.ts`
|
||||
**Status:** COMPLETE
|
||||
|
||||
**Purpose:** Shows determinism status with hash verification.
|
||||
|
||||
**Inputs:**
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| hash | string | Determinism hash |
|
||||
| verified | boolean | Hash verification status |
|
||||
| copyable | boolean | Show copy button |
|
||||
|
||||
---
|
||||
|
||||
## Service Layer
|
||||
|
||||
### TriageService
|
||||
|
||||
**Location:** `core/services/triage.service.ts`
|
||||
**Sprint:** SPRINT_20251226_013_FE
|
||||
|
||||
**Methods:**
|
||||
```typescript
|
||||
getVulnerabilities(filters: TriageFilters): Observable<Page<Vulnerability>>
|
||||
getVulnerability(id: string): Observable<Vulnerability>
|
||||
getReachability(id: string): Observable<CallGraphSlice>
|
||||
```
|
||||
|
||||
### VexDecisionService
|
||||
|
||||
**Location:** `core/services/vex-decision.service.ts`
|
||||
**Sprint:** SPRINT_20251226_013_FE
|
||||
|
||||
**Methods:**
|
||||
```typescript
|
||||
create(decision: CreateVexDecision): Observable<VexDecision>
|
||||
update(id: string, decision: UpdateVexDecision): Observable<VexDecision>
|
||||
getHistory(vulnId: string): Observable<VexDecision[]>
|
||||
```
|
||||
|
||||
### CompareService
|
||||
|
||||
**Location:** `core/services/compare.service.ts`
|
||||
**Sprint:** SPRINT_20251226_012_FE
|
||||
|
||||
**Methods:**
|
||||
```typescript
|
||||
getBaselineRecommendations(digest: string): Observable<BaselineRecommendation[]>
|
||||
computeDelta(current: string, baseline: string): Observable<Delta>
|
||||
getTrustIndicators(deltaId: string): Observable<TrustIndicators>
|
||||
```
|
||||
|
||||
### RiskBudgetService
|
||||
|
||||
**Location:** `core/services/risk-budget.service.ts`
|
||||
**Sprint:** SPRINT_20251226_004_FE
|
||||
|
||||
**Methods:**
|
||||
```typescript
|
||||
getBudgetStatus(serviceId: string): Observable<BudgetStatus>
|
||||
getBurnUpData(serviceId: string, window: BudgetWindow): Observable<BudgetTimeSeries>
|
||||
createException(exception: CreateException): Observable<Exception>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Interaction Diagrams
|
||||
|
||||
### Triage Flow
|
||||
|
||||
```
|
||||
User Action Component Service
|
||||
│ │ │
|
||||
│ Select vulnerability │ │
|
||||
├────────────────────────────►│ TriageListComponent │
|
||||
│ ├─────────────────────────────►│
|
||||
│ │ │ getVulnerability()
|
||||
│ │◄─────────────────────────────┤
|
||||
│ │ │
|
||||
│ │ TriageDetailComponent │
|
||||
│ ├─────────────────────────────►│
|
||||
│ │ │ getReachability()
|
||||
│ │◄─────────────────────────────┤
|
||||
│ │ │
|
||||
│ Click "Mark Not Affected" │ │
|
||||
├────────────────────────────►│ VexDecisionModalComponent │
|
||||
│ │ │
|
||||
│ Submit form │ │
|
||||
├────────────────────────────►│ │
|
||||
│ ├─────────────────────────────►│
|
||||
│ │ │ VexDecisionService.create()
|
||||
│ │◄─────────────────────────────┤
|
||||
│ │ │
|
||||
│ │ Update list, advance queue │
|
||||
│◄────────────────────────────┤ │
|
||||
```
|
||||
|
||||
### Compare Flow
|
||||
|
||||
```
|
||||
User Action Component Service
|
||||
│ │ │
|
||||
│ Navigate to /compare/:id │ │
|
||||
├────────────────────────────►│ CompareViewComponent │
|
||||
│ ├─────────────────────────────►│
|
||||
│ │ │ getBaselineRecommendations()
|
||||
│ │◄─────────────────────────────┤
|
||||
│ │ │
|
||||
│ │ Auto-select baseline │
|
||||
│ ├─────────────────────────────►│
|
||||
│ │ │ computeDelta()
|
||||
│ │◄─────────────────────────────┤
|
||||
│ │ │
|
||||
│ │ ThreePaneLayoutComponent │
|
||||
│ │ ├ CategoriesPaneComponent │
|
||||
│ │ ├ ItemsPaneComponent │
|
||||
│ │ └ ProofPaneComponent │
|
||||
│ │ │
|
||||
│ Select category │ │
|
||||
├────────────────────────────►│ │
|
||||
│ │ Filter items by category │
|
||||
│ │ │
|
||||
│ Select item │ │
|
||||
├────────────────────────────►│ │
|
||||
│ │ Display proof in right pane │
|
||||
│◄────────────────────────────┤ │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Requirements
|
||||
|
||||
All triage components must meet WCAG 2.1 AA compliance:
|
||||
|
||||
| Requirement | Implementation |
|
||||
|-------------|----------------|
|
||||
| Keyboard navigation | Tab/Arrow/Enter/Escape, documented shortcuts |
|
||||
| Focus management | Visible focus indicators, logical tab order |
|
||||
| Screen reader | ARIA labels, live regions for updates |
|
||||
| Color contrast | 4.5:1 minimum for text, 3:1 for UI elements |
|
||||
| Error messages | Associated with inputs, announced immediately |
|
||||
|
||||
---
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Component behavior (selection, filtering, expansion)
|
||||
- Signal/computed derivations
|
||||
- Form validation
|
||||
|
||||
### Integration Tests
|
||||
- Service API calls
|
||||
- Route navigation
|
||||
- State persistence
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
- Full triage workflow
|
||||
- Comparison workflow
|
||||
- Keyboard navigation
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Unified Triage Specification](./unified-triage-specification.md)
|
||||
- [Smart-Diff UI Architecture](./smart-diff-ui-architecture.md)
|
||||
- [Angular Component Guidelines](https://angular.dev/guide/components)
|
||||
Reference in New Issue
Block a user