save progress
This commit is contained in:
123
docs/modules/README.md
Normal file
123
docs/modules/README.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# StellaOps Module Documentation Index
|
||||
|
||||
This directory contains architecture documentation for all StellaOps modules.
|
||||
|
||||
## Module Categories
|
||||
|
||||
### Core Platform
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Authority](./authority/) | `src/Authority/` | Authentication, authorization, OAuth/OIDC, DPoP |
|
||||
| [Gateway](./gateway/) | `src/Gateway/` | API gateway with routing and transport abstraction |
|
||||
| [Router](./router/) | `src/Router/` | Transport-agnostic messaging (TCP/TLS/UDP/RabbitMQ/Valkey) |
|
||||
| [Platform](./platform/) | Cross-cutting | Platform architecture overview |
|
||||
|
||||
### Data Ingestion
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Concelier](./concelier/) | `src/Concelier/` | Vulnerability advisory ingestion and merge engine |
|
||||
| [Excititor](./excititor/) | `src/Excititor/` | VEX document ingestion and export |
|
||||
| [VexLens](./vex-lens/) | `src/VexLens/` | VEX consensus computation across issuers |
|
||||
| [VexHub](./vexhub/) | `src/VexHub/` | VEX distribution and exchange hub |
|
||||
| [IssuerDirectory](./issuer-directory/) | `src/IssuerDirectory/` | Issuer trust registry (CSAF publishers) |
|
||||
| [Feedser](./feedser/) | `src/Feedser/` | Evidence collection library for backport detection |
|
||||
| [Mirror](./mirror/) | `src/Mirror/` | Vulnerability feed mirror and distribution |
|
||||
|
||||
### Scanning & Analysis
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Scanner](./scanner/) | `src/Scanner/` | Container scanning with SBOM generation |
|
||||
| [BinaryIndex](./binaryindex/) | `src/BinaryIndex/` | Binary identity extraction and fingerprinting |
|
||||
| [AdvisoryAI](./advisory-ai/) | `src/AdvisoryAI/` | AI-assisted advisory analysis |
|
||||
| [Symbols](./symbols/) | `src/Symbols/` | Symbol resolution and debug information |
|
||||
| [ReachGraph](./reachgraph/) | `src/ReachGraph/` | Reachability graph service |
|
||||
|
||||
### Artifacts & Evidence
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Attestor](./attestor/) | `src/Attestor/` | in-toto/DSSE attestation generation |
|
||||
| [Signer](./signer/) | `src/Signer/` | Cryptographic signing operations |
|
||||
| [SbomService](./sbomservice/) | `src/SbomService/` | SBOM storage, versioning, and lineage ledger |
|
||||
| [EvidenceLocker](./evidence-locker/) | `src/EvidenceLocker/` | Sealed evidence storage and export |
|
||||
| [ExportCenter](./export-center/) | `src/ExportCenter/` | Batch export and report generation |
|
||||
| [Provenance](./provenance/) | `src/Provenance/` | SLSA/DSSE attestation tooling |
|
||||
| [Provcache](./provcache/) | Library | Provenance cache utilities |
|
||||
|
||||
### Policy & Risk
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Policy](./policy/) | `src/Policy/` | Policy engine with K4 lattice logic |
|
||||
| [RiskEngine](./riskengine/) | `src/RiskEngine/` | Risk scoring runtime |
|
||||
| [VulnExplorer](./vuln-explorer/) | `src/VulnExplorer/` | Vulnerability exploration and triage |
|
||||
| [Unknowns](./unknowns/) | `src/Unknowns/` | Unknown component tracking registry |
|
||||
|
||||
### Operations
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Scheduler](./scheduler/) | `src/Scheduler/` | Job scheduling and queue management |
|
||||
| [Orchestrator](./orchestrator/) | `src/Orchestrator/` | Workflow orchestration and task coordination |
|
||||
| [TaskRunner](./taskrunner/) | `src/TaskRunner/` | Task pack execution engine |
|
||||
| [Notify](./notify/) | `src/Notify/` | Notification toolkit (Email, Slack, Teams, Webhooks) |
|
||||
| [Notifier](./notifier/) | `src/Notifier/` | Notifications Studio host |
|
||||
| [PacksRegistry](./packsregistry/) | `src/PacksRegistry/` | Task packs registry |
|
||||
| [TimelineIndexer](./timelineindexer/) | `src/TimelineIndexer/` | Timeline event indexing |
|
||||
| [Replay](./replay/) | `src/Replay/` | Deterministic replay engine |
|
||||
|
||||
### Integration
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [CLI](./cli/) | `src/Cli/` | Command-line interface (Native AOT) |
|
||||
| [Zastava](./zastava/) | `src/Zastava/` | Container registry webhook observer |
|
||||
| [Web/UI](./ui/) | `src/Web/` | Angular 17 frontend SPA |
|
||||
| [API](./api/) | `src/Api/` | OpenAPI contracts and governance |
|
||||
| [Registry](./registry/) | `src/Registry/` | Container registry integration |
|
||||
|
||||
### Infrastructure
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Cryptography](./cryptography/) | `src/Cryptography/` | Crypto plugins (FIPS, eIDAS, GOST, SM, PQ) |
|
||||
| [Telemetry](./telemetry/) | `src/Telemetry/` | OpenTelemetry traces, metrics, logging |
|
||||
| [Graph](./graph/) | `src/Graph/` | Call graph and reachability data structures |
|
||||
| [Signals](./signals/) | `src/Signals/` | Runtime signal collection and correlation |
|
||||
| [AirGap](./airgap/) | `src/AirGap/` | Air-gapped deployment support |
|
||||
| [AOC](./aoc/) | `src/Aoc/` | Append-Only Contract enforcement |
|
||||
|
||||
### Testing & Benchmarks
|
||||
|
||||
| Module | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| [Benchmark](./benchmark/) | Scanner library | Competitive benchmarking (accuracy comparison) |
|
||||
| [Bench](./bench/) | `src/Bench/` | Performance benchmarks |
|
||||
|
||||
### Cross-Cutting Concepts
|
||||
|
||||
| Folder | Purpose |
|
||||
|--------|---------|
|
||||
| [Evidence](./evidence/) | Unified evidence model specification |
|
||||
| [Snapshot](./snapshot/) | Knowledge snapshot and replay concepts |
|
||||
| [Triage](./triage/) | Vulnerability triage workflows |
|
||||
| [DevOps](./devops/) | DevOps and CI/CD infrastructure |
|
||||
| [CI](./ci/) | CI pipeline documentation |
|
||||
|
||||
---
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
Each module folder should contain:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `README.md` | Quick overview, purpose, components |
|
||||
| `architecture.md` | Detailed architecture specification |
|
||||
| `AGENTS.md` | (Optional) Claude Code agent guidance |
|
||||
| `operations/` | (Optional) Operational runbooks |
|
||||
|
||||
See the [Documentation Template Standard](../implplan/SPRINT_1228_0001_DOCS_module_documentation_consolidation.md#documentation-template-standard) for the full architecture.md template.
|
||||
348
docs/modules/airgap/architecture.md
Normal file
348
docs/modules/airgap/architecture.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# component_architecture_airgap.md - **Stella Ops AirGap** (2025Q4)
|
||||
|
||||
> Air-gapped deployment controller, importer, and time anchor services.
|
||||
|
||||
> **Scope.** Implementation-ready architecture for **AirGap**: the controller, importer, and time anchor subsystems enabling StellaOps operation in disconnected/air-gapped environments with sealed-mode state management.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Enable **fully offline, air-gapped operation** of StellaOps with sealed-mode state management, bundle-based updates, and cryptographic time anchors for staleness detection.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* AirGap **does not** connect to external networks in sealed mode.
|
||||
* AirGap **does not** generate vulnerability data. It imports pre-packaged bundles.
|
||||
* Bundle verification is **mandatory**. Unsigned or tampered bundles are rejected.
|
||||
* Time anchors are **cryptographically verified** using Roughtime or RFC3161.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/AirGap/
|
||||
├─ StellaOps.AirGap.Controller/ # Seal/unseal state machine, status APIs
|
||||
│ ├─ Services/
|
||||
│ │ ├─ ISealingController.cs # Sealing state interface
|
||||
│ │ ├─ SealingController.cs # State machine implementation
|
||||
│ │ └─ StatusService.cs # Health and status endpoints
|
||||
│ └─ Models/
|
||||
│ ├─ SealState.cs # sealed | unsealed | transitioning
|
||||
│ └─ SealTransition.cs # Transition metadata
|
||||
│
|
||||
├─ StellaOps.AirGap.Importer/ # Bundle verification and import
|
||||
│ ├─ Services/
|
||||
│ │ ├─ IBundleVerifier.cs # DSSE/TUF verification
|
||||
│ │ ├─ BundleVerifier.cs # Verification implementation
|
||||
│ │ ├─ ICatalogImporter.cs # Catalog update interface
|
||||
│ │ └─ CatalogImporter.cs # Import orchestration
|
||||
│ └─ Models/
|
||||
│ ├─ ImportBundle.cs # Bundle metadata
|
||||
│ └─ ImportResult.cs # Import outcome
|
||||
│
|
||||
├─ StellaOps.AirGap.Time/ # Time anchor verification
|
||||
│ ├─ Services/
|
||||
│ │ ├─ ITimeAnchorService.cs # Time anchor interface
|
||||
│ │ ├─ RoughtimeAnchor.cs # Roughtime implementation
|
||||
│ │ └─ Rfc3161Anchor.cs # RFC3161 timestamp
|
||||
│ └─ Models/
|
||||
│ ├─ TimeAnchor.cs # Anchor record
|
||||
│ └─ StalenessResult.cs # Staleness calculation
|
||||
│
|
||||
├─ StellaOps.AirGap.Policy/ # Air-gap specific policy rules
|
||||
│ ├─ StellaOps.AirGap.Policy/
|
||||
│ ├─ StellaOps.AirGap.Policy.Analyzers/
|
||||
│ └─ StellaOps.AirGap.Policy.Tests/
|
||||
│
|
||||
├─ __Libraries/
|
||||
│ ├─ StellaOps.AirGap.Bundle/ # Bundle format and parsing
|
||||
│ └─ StellaOps.AirGap.Persistence/ # State persistence
|
||||
│
|
||||
├─ __Tests/ # Test projects
|
||||
│
|
||||
├─ scripts/ # Operational scripts
|
||||
│
|
||||
└─ AGENTS.md # Guild charter
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **PostgreSQL** - State persistence, import history
|
||||
* **Authority** - Scope enforcement (`airgap:seal`, `airgap:status:read`)
|
||||
* **Cryptography** - Bundle signature verification
|
||||
* **Object storage** - Bundle staging and quarantine
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 Seal State
|
||||
|
||||
```csharp
|
||||
public enum SealState
|
||||
{
|
||||
Unsealed, // Normal operation, network allowed
|
||||
Transitioning, // Sealing or unsealing in progress
|
||||
Sealed // Air-gapped, no network egress
|
||||
}
|
||||
|
||||
public sealed record SealStatus
|
||||
{
|
||||
public required SealState State { get; init; }
|
||||
public required DateTimeOffset LastTransition { get; init; }
|
||||
public required string TransitionedBy { get; init; }
|
||||
public DateTimeOffset? SealedSince { get; init; }
|
||||
public TimeAnchor? LastTimeAnchor { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Import Bundle
|
||||
|
||||
```json
|
||||
{
|
||||
"bundleId": "airgap-2025-01-15-abc123",
|
||||
"bundleType": "advisory-update",
|
||||
"version": "2025.01.15.001",
|
||||
"createdAt": "2025-01-15T10:30:00Z",
|
||||
"contents": [
|
||||
{
|
||||
"type": "concelier-snapshot",
|
||||
"digest": "sha256:abc123...",
|
||||
"path": "data/concelier-2025-01-15.tar.zst"
|
||||
},
|
||||
{
|
||||
"type": "trivy-db",
|
||||
"digest": "sha256:def456...",
|
||||
"path": "data/trivy-db-2025-01-15.tar.gz"
|
||||
}
|
||||
],
|
||||
"signature": {
|
||||
"keyId": "sha256:sigkey...",
|
||||
"algorithm": "ecdsa-p256",
|
||||
"value": "base64..."
|
||||
},
|
||||
"timeAnchor": {
|
||||
"source": "roughtime",
|
||||
"timestamp": "2025-01-15T10:25:00Z",
|
||||
"proof": "base64..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Time Anchor
|
||||
|
||||
```csharp
|
||||
public sealed record TimeAnchor
|
||||
{
|
||||
public required string Source { get; init; } // roughtime, rfc3161
|
||||
public required DateTimeOffset Timestamp { get; init; }
|
||||
public required byte[] Proof { get; init; } // Cryptographic proof
|
||||
public required string[] Servers { get; init; } // Servers used
|
||||
public required bool Verified { get; init; }
|
||||
}
|
||||
|
||||
public sealed record StalenessResult
|
||||
{
|
||||
public required TimeSpan Age { get; init; }
|
||||
public required bool IsStale { get; init; }
|
||||
public required TimeSpan StalenessThreshold { get; init; }
|
||||
public string? Warning { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) REST API (Controller + Importer)
|
||||
|
||||
All under `/api/v1/airgap`. Auth: **OpTok** with airgap scopes.
|
||||
|
||||
### Controller APIs
|
||||
|
||||
```
|
||||
GET /status → { state, lastTransition, timeAnchor }
|
||||
POST /seal → { transitionId, status: "transitioning" }
|
||||
POST /unseal → { transitionId, status: "transitioning" }
|
||||
GET /transitions/{id} → { transition details }
|
||||
```
|
||||
|
||||
### Importer APIs
|
||||
|
||||
```
|
||||
POST /bundles/upload multipart → { bundleId, status: "pending" }
|
||||
POST /bundles/{id}/verify → { valid: bool, details }
|
||||
POST /bundles/{id}/import → { importId, status: "importing" }
|
||||
GET /bundles/{id}/status → { status, progress, errors }
|
||||
GET /bundles → { bundles: BundleSummary[] }
|
||||
```
|
||||
|
||||
### Time APIs
|
||||
|
||||
```
|
||||
GET /time/anchor → { anchor: TimeAnchor, staleness }
|
||||
POST /time/anchor { source, proof } → { anchor, verified }
|
||||
GET /time/staleness → { staleness: StalenessResult }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) Configuration (YAML)
|
||||
|
||||
```yaml
|
||||
AirGap:
|
||||
Controller:
|
||||
InitialState: "unsealed"
|
||||
TransitionTimeoutSeconds: 300
|
||||
RequireApproval: true
|
||||
|
||||
Importer:
|
||||
StagingPath: "/data/airgap/staging"
|
||||
QuarantinePath: "/data/airgap/quarantine"
|
||||
MaxBundleSizeMb: 10240
|
||||
TrustRoots:
|
||||
- "sha256:abc123..." # StellaOps signing key
|
||||
AllowedBundleTypes:
|
||||
- "advisory-update"
|
||||
- "trivy-db"
|
||||
- "policy-pack"
|
||||
|
||||
Time:
|
||||
StalenessThresholdHours: 168 # 7 days
|
||||
RoughtimeServers:
|
||||
- "roughtime.cloudflare.com"
|
||||
- "roughtime.google.com"
|
||||
Rfc3161Servers:
|
||||
- "http://timestamp.digicert.com"
|
||||
RequireMultipleServers: true
|
||||
MinServerQuorum: 2
|
||||
|
||||
Quarantine:
|
||||
TtlDays: 30
|
||||
MaxQuotaMb: 51200
|
||||
|
||||
Postgres:
|
||||
ConnectionString: "Host=postgres;Database=airgap;..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Sealing State Machine
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ Unsealed │
|
||||
└──────┬───────┘
|
||||
│ seal()
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Transitioning│
|
||||
└──────┬───────┘
|
||||
│ complete
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Sealed │
|
||||
└──────┬───────┘
|
||||
│ unseal()
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Transitioning│
|
||||
└──────┬───────┘
|
||||
│ complete
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Unsealed │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
### Transition Actions
|
||||
|
||||
**Seal transition:**
|
||||
1. Verify pending work is complete
|
||||
2. Capture final time anchor
|
||||
3. Disable network egress
|
||||
4. Update state to Sealed
|
||||
|
||||
**Unseal transition:**
|
||||
1. Verify network connectivity
|
||||
2. Refresh time anchor
|
||||
3. Enable network egress
|
||||
4. Update state to Unsealed
|
||||
|
||||
---
|
||||
|
||||
## 7) Bundle Import Flow
|
||||
|
||||
```
|
||||
1. Upload bundle to staging
|
||||
└─ Validate manifest structure
|
||||
|
||||
2. Verify bundle
|
||||
├─ Check signature against trust roots
|
||||
├─ Verify content digests
|
||||
└─ Validate time anchor
|
||||
|
||||
3. Import bundle
|
||||
├─ Extract contents
|
||||
├─ Update target catalogs (Concelier, Trivy, etc.)
|
||||
└─ Record import in history
|
||||
|
||||
4. Cleanup or quarantine
|
||||
├─ Success: Remove from staging
|
||||
└─ Failure: Move to quarantine with TTL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8) Security & compliance
|
||||
|
||||
* **Signature verification**: All bundles must be signed
|
||||
* **Trust roots**: Configurable trust root keys
|
||||
* **Quarantine**: Failed imports isolated with TTL
|
||||
* **Audit trail**: All imports and state changes logged
|
||||
* **Scope enforcement**: Authority scopes for all operations
|
||||
* **Rollback prevention**: Version monotonicity enforced
|
||||
|
||||
---
|
||||
|
||||
## 9) Performance targets
|
||||
|
||||
* **Seal/unseal transition**: < 30s
|
||||
* **Bundle verification**: < 10s for 1GB bundle
|
||||
* **Bundle import**: < 60s for typical advisory update
|
||||
* **Time anchor verification**: < 5s
|
||||
|
||||
---
|
||||
|
||||
## 10) Observability
|
||||
|
||||
**Metrics:**
|
||||
* `airgap.state{state=sealed|unsealed|transitioning}`
|
||||
* `airgap.bundles.imported_total{type,result}`
|
||||
* `airgap.bundles.quarantined_total{reason}`
|
||||
* `airgap.time.staleness_seconds`
|
||||
* `airgap.time.anchor_age_seconds`
|
||||
|
||||
**Tracing:** Spans for transitions, imports, verifications.
|
||||
|
||||
---
|
||||
|
||||
## 11) Testing matrix
|
||||
|
||||
* **Seal/unseal tests**: State machine transitions
|
||||
* **Bundle tests**: Verification and import flows
|
||||
* **Quarantine tests**: Failed import handling
|
||||
* **Time tests**: Staleness calculations, anchor verification
|
||||
* **Integration tests**: Full offline workflow simulation
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Evidence reconciliation: `./evidence-reconciliation.md`
|
||||
* Exporter coordination: `./exporter-cli-coordination.md`
|
||||
* Mirror DSSE plan: `./mirror-dsse-plan.md`
|
||||
* Offline Kit: `../../24_OFFLINE_KIT.md`
|
||||
* Time anchor schema: `../../airgap/time-anchor-schema.md`
|
||||
37
docs/modules/aoc/README.md
Normal file
37
docs/modules/aoc/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# AOC (Append-Only Contracts)
|
||||
|
||||
**Status:** Implemented
|
||||
**Source:** `src/Aoc/`
|
||||
**Owner:** Platform Team
|
||||
|
||||
## Purpose
|
||||
|
||||
AOC provides compile-time enforcement of append-only contract rules during data ingestion. Uses Roslyn analyzers to prevent connectors from writing to fields that should only be computed by downstream merge/decisioning pipelines.
|
||||
|
||||
## Components
|
||||
|
||||
**Analyzers:**
|
||||
- `StellaOps.Aoc.Analyzers` - Roslyn DiagnosticAnalyzers (AOC0001, AOC0002, AOC0003)
|
||||
|
||||
**Libraries:**
|
||||
- `StellaOps.Aoc` - Core abstractions (IAocGuard)
|
||||
- `StellaOps.Aoc.AspNetCore` - ASP.NET Core integration
|
||||
|
||||
**CLI:**
|
||||
- `StellaOps.Aoc.Cli` - Manual validation tool
|
||||
|
||||
## Key Concepts
|
||||
|
||||
**Forbidden Fields** (ingestion-time writes forbidden):
|
||||
- `severity`, `cvss`, `cvss_vector` - Computed from CVSS + context
|
||||
- `effective_status`, `effective_range` - VEX consensus outcomes
|
||||
- `risk_score`, `reachability`, `asset_criticality` - Runtime analysis
|
||||
|
||||
**Derived Fields:**
|
||||
- Any field prefixed with `effective_*` is treated as derived and forbidden
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Architecture: `./architecture.md`
|
||||
- Concelier: `../concelier/` (uses AOC for connectors)
|
||||
- Excititor: `../excititor/` (uses AOC for VEX ingestion)
|
||||
126
docs/modules/aoc/architecture.md
Normal file
126
docs/modules/aoc/architecture.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# component_architecture_aoc.md - **Stella Ops AOC** (2025Q4)
|
||||
|
||||
> Append-Only Contract enforcement via Roslyn analyzers.
|
||||
|
||||
> **Scope.** Library architecture for **AOC** (Append-Only Contracts): Roslyn-based code analyzers that enforce data integrity rules during vulnerability ingestion.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Enforce **append-only contract rules** during data ingestion. Prevent connectors from writing to fields that should only be computed by downstream merge/decisioning pipelines (severity, CVSS, effective status, risk scores).
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* AOC provides **compile-time enforcement** via Roslyn analyzers.
|
||||
* AOC analyzers run on **ingestion code** (Connectors, Ingestion assemblies).
|
||||
* AOC **does not** run on merge/decisioning code (those are allowed to write derived fields).
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Aoc/
|
||||
├─ __Analyzers/
|
||||
│ └─ StellaOps.Aoc.Analyzers/ # Roslyn DiagnosticAnalyzers
|
||||
│ ├─ AocForbiddenFieldAnalyzer.cs # Main analyzer
|
||||
│ └─ ...
|
||||
│
|
||||
├─ __Libraries/
|
||||
│ ├─ StellaOps.Aoc/ # Core abstractions (IAocGuard, etc.)
|
||||
│ └─ StellaOps.Aoc.AspNetCore/ # ASP.NET Core integration
|
||||
│
|
||||
├─ StellaOps.Aoc.Cli/ # CLI for manual validation
|
||||
│
|
||||
└─ __Tests/
|
||||
├─ StellaOps.Aoc.Analyzers.Tests/
|
||||
├─ StellaOps.Aoc.AspNetCore.Tests/
|
||||
└─ StellaOps.Aoc.Tests/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Core concept
|
||||
|
||||
### 2.1 Forbidden Fields
|
||||
|
||||
During ingestion, the following fields are **forbidden** (computed by decisioning pipeline):
|
||||
|
||||
| Field | Reason |
|
||||
|-------|--------|
|
||||
| `severity` | Computed from CVSS + context |
|
||||
| `cvss` | Normalized from multiple sources |
|
||||
| `cvss_vector` | Parsed/validated post-merge |
|
||||
| `effective_status` | VEX consensus outcome |
|
||||
| `effective_range` | Merged affected ranges |
|
||||
| `merged_from` | Provenance tracking |
|
||||
| `consensus_provider` | VEX provider selection |
|
||||
| `reachability` | Runtime analysis result |
|
||||
| `asset_criticality` | Policy engine computation |
|
||||
| `risk_score` | Final risk calculation |
|
||||
|
||||
### 2.2 Derived Fields
|
||||
|
||||
Any field prefixed with `effective_` is treated as derived and forbidden in ingestion context.
|
||||
|
||||
---
|
||||
|
||||
## 3) Diagnostic Rules
|
||||
|
||||
| ID | Severity | Description |
|
||||
|----|----------|-------------|
|
||||
| `AOC0001` | Error | Forbidden field write detected |
|
||||
| `AOC0002` | Error | Derived field (effective_*) write detected |
|
||||
| `AOC0003` | Warning | Unguarded database write without IAocGuard validation |
|
||||
|
||||
---
|
||||
|
||||
## 4) Usage
|
||||
|
||||
### 4.1 Analyzer Reference
|
||||
|
||||
Add analyzer reference to connector projects:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StellaOps.Aoc.Analyzers" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### 4.2 Guard Usage
|
||||
|
||||
Wrap database writes with AOC guard:
|
||||
|
||||
```csharp
|
||||
public class MyConnector
|
||||
{
|
||||
private readonly IAocGuard _aocGuard;
|
||||
|
||||
public async Task IngestAsync(Advisory advisory, CancellationToken ct)
|
||||
{
|
||||
// Guard validates no forbidden fields are written
|
||||
await _aocGuard.ValidateAsync(advisory, ct);
|
||||
await _repository.InsertAsync(advisory, ct);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) Configuration
|
||||
|
||||
AOC analyzers activate based on assembly/namespace patterns:
|
||||
|
||||
- `*.Connector.*` assemblies
|
||||
- `*.Ingestion` assemblies
|
||||
- `*.Connector` assemblies
|
||||
- Namespaces containing `.Connector.` or `.Ingestion`
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Concelier: `../concelier/architecture.md` (uses AOC for connectors)
|
||||
* Excititor: `../excititor/architecture.md` (uses AOC for VEX ingestion)
|
||||
* Determinism: `../../telemetry/` (AOC ensures deterministic merge inputs)
|
||||
48
docs/modules/api/README.md
Normal file
48
docs/modules/api/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# API (OpenAPI Contracts)
|
||||
|
||||
**Status:** Implemented
|
||||
**Source:** `src/Api/`
|
||||
**Owner:** API Contracts Guild
|
||||
|
||||
## Purpose
|
||||
|
||||
API contains OpenAPI 3.1 specifications for all StellaOps services and governance rules for API consistency. This is a specifications repository, not a code module.
|
||||
|
||||
## Components
|
||||
|
||||
**OpenAPI Specs:**
|
||||
- `StellaOps.Api.OpenApi/` - Per-service OpenAPI 3.1 YAML specifications
|
||||
- `authority/openapi.yaml`
|
||||
- `scanner/openapi.yaml`
|
||||
- `scheduler/openapi.yaml`
|
||||
- `policy/openapi.yaml`
|
||||
- `graph/openapi.yaml`
|
||||
- `export-center/openapi.yaml`
|
||||
- `orchestrator/openapi.yaml`
|
||||
|
||||
**Shared Components:**
|
||||
- `_shared/schemas/` - Common schema definitions
|
||||
- `_shared/parameters/` - Shared parameters (paging, tenant)
|
||||
- `_shared/responses/` - Standard response definitions
|
||||
|
||||
**Aggregate Spec:**
|
||||
- `stella.yaml` - Composed aggregate specification
|
||||
|
||||
**Governance:**
|
||||
- `StellaOps.Api.Governance/` - API governance rules and standards
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Compose aggregate spec
|
||||
node src/Api/StellaOps.Api.OpenApi/compose.mjs
|
||||
|
||||
# Lint OpenAPI specs
|
||||
npm run api:lint
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Developer Portal: `../devportal/` (consumes these specs)
|
||||
- Gateway: `../gateway/` (routes based on these contracts)
|
||||
- Platform: `../platform/architecture-overview.md`
|
||||
37
docs/modules/bench/README.md
Normal file
37
docs/modules/bench/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Bench (Performance Benchmarks)
|
||||
|
||||
**Status:** Implemented
|
||||
**Source:** `src/Bench/`
|
||||
**Owner:** Platform Team
|
||||
|
||||
> **Note:** This folder documents **performance benchmarks**. For **competitive benchmarking** (accuracy comparison with other scanners), see [`../benchmark/`](../benchmark/).
|
||||
|
||||
## Purpose
|
||||
|
||||
Bench provides performance benchmark infrastructure for StellaOps modules. Measures throughput, latency, and resource usage to detect regressions and validate performance targets.
|
||||
|
||||
## Components
|
||||
|
||||
**Benchmark Projects:**
|
||||
- `StellaOps.Bench.LinkNotMerge` - Link-Not-Merge correlation performance
|
||||
- `StellaOps.Bench.LinkNotMerge.Vex` - LNM VEX statement performance
|
||||
- `StellaOps.Bench.Notify` - Notification delivery throughput
|
||||
- `StellaOps.Bench.PolicyEngine` - Policy evaluation performance
|
||||
- `StellaOps.Bench.ScannerAnalyzers` - Language analyzer performance
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Run all benchmarks
|
||||
dotnet run -c Release --project src/Bench/StellaOps.Bench/LinkNotMerge/StellaOps.Bench.LinkNotMerge
|
||||
|
||||
# Run with specific runtime
|
||||
dotnet run -c Release --project src/Bench/StellaOps.Bench/Notify/StellaOps.Bench.Notify
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Competitive Benchmark: `../benchmark/architecture.md`
|
||||
- Scanner: `../scanner/architecture.md`
|
||||
- Policy: `../policy/architecture.md`
|
||||
- Notify: `../notify/architecture.md`
|
||||
@@ -7,6 +7,8 @@ The Benchmark module provides infrastructure for validating and demonstrating St
|
||||
**Module Path**: `src/Scanner/__Libraries/StellaOps.Scanner.Benchmark/`
|
||||
**Status**: PLANNED (Sprint 7000.0001.0001)
|
||||
|
||||
> **Note:** This module focuses on **competitive benchmarking** (accuracy comparison with other scanners). For **performance benchmarks** of StellaOps modules (LinkNotMerge, Notify, PolicyEngine, Scanner.Analyzers), see `src/Bench/`.
|
||||
|
||||
---
|
||||
|
||||
## Mission
|
||||
|
||||
298
docs/modules/cryptography/architecture.md
Normal file
298
docs/modules/cryptography/architecture.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# component_architecture_cryptography.md - **Stella Ops Cryptography** (2025Q4)
|
||||
|
||||
> Pluggable cryptographic primitives supporting regional standards.
|
||||
|
||||
> **Scope.** Library architecture for **Cryptography**: pluggable cryptographic primitives enabling sovereign operation with regional crypto requirements (eIDAS, FIPS, GOST, SM, PQ) while maintaining deterministic signing operations.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide **algorithm-agile cryptographic primitives** that support regional compliance requirements while ensuring deterministic, reproducible signing operations across all StellaOps services.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Cryptography is a **library layer**, not a standalone service.
|
||||
* Cryptography **does not** manage keys. Key storage is handled by KMS, HSM, or Signer.
|
||||
* All cryptographic operations are **deterministic** for reproducibility.
|
||||
* Supports **offline operation** without external crypto services.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Cryptography/
|
||||
├─ StellaOps.Cryptography/ # Core abstractions and plugin loader
|
||||
│ ├─ ICryptoHash.cs # Hash computation interface
|
||||
│ ├─ ISignatureProvider.cs # Signature interface
|
||||
│ ├─ IKeyProvider.cs # Key loading interface
|
||||
│ ├─ CryptoProfileLoader.cs # Profile plugin loader
|
||||
│ └─ DefaultCryptoHash.cs # Default SHA-256/BLAKE3 implementation
|
||||
│
|
||||
├─ StellaOps.Cryptography.Profiles.Ecdsa/ # ECDSA signing profile
|
||||
│ ├─ EcdsaSignatureProvider.cs
|
||||
│ └─ Curves/
|
||||
│ ├─ P256Provider.cs # NIST P-256
|
||||
│ ├─ P384Provider.cs # NIST P-384
|
||||
│ └─ Secp256k1Provider.cs # Bitcoin curve
|
||||
│
|
||||
├─ StellaOps.Cryptography.Profiles.EdDsa/ # EdDSA signing profile
|
||||
│ ├─ EdDsaSignatureProvider.cs
|
||||
│ └─ Curves/
|
||||
│ ├─ Ed25519Provider.cs
|
||||
│ └─ Ed448Provider.cs
|
||||
│
|
||||
├─ plugins/ # External crypto plugins
|
||||
│ ├─ gost/ # GOST R 34.10-2012 (Russia)
|
||||
│ ├─ sm/ # SM2/SM3/SM4 (China)
|
||||
│ ├─ eidas/ # eIDAS qualified signatures (EU)
|
||||
│ └─ pq/ # Post-quantum (experimental)
|
||||
│
|
||||
└─ StellaOps.Cryptography.sln
|
||||
```
|
||||
|
||||
### Library projects under `src/__Libraries/`:
|
||||
|
||||
```
|
||||
src/__Libraries/
|
||||
├─ StellaOps.Cryptography.Tests/
|
||||
├─ StellaOps.Cryptography.Plugin.CryptoPro/ # GOST via CryptoPro CSP
|
||||
├─ StellaOps.Cryptography.Plugin.EIDAS/ # eIDAS qualified signatures
|
||||
├─ StellaOps.Cryptography.Plugin.SmSoft/ # SM2 software implementation
|
||||
├─ StellaOps.Cryptography.Plugin.SmRemote/ # SM2 via remote HSM
|
||||
├─ StellaOps.Cryptography.Plugin.OfflineVerification/
|
||||
├─ StellaOps.Cryptography.PluginLoader/
|
||||
└─ StellaOps.Cryptography.Kms/ # KMS integration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **.NET Cryptography APIs** - Built-in crypto primitives
|
||||
* **BouncyCastle** (optional) - Extended algorithm support
|
||||
* **CryptoPro CSP** (optional) - GOST support on Windows
|
||||
* **HSM/TPM** (optional) - Hardware security modules
|
||||
* **KMS** (optional) - Cloud key management services
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 Hash Computation
|
||||
|
||||
```csharp
|
||||
public interface ICryptoHash
|
||||
{
|
||||
string ComputeSha256(ReadOnlySpan<byte> data);
|
||||
string ComputeBlake3(ReadOnlySpan<byte> data);
|
||||
string ComputeHash(ReadOnlySpan<byte> data, HashAlgorithm algorithm);
|
||||
}
|
||||
|
||||
public enum HashAlgorithm
|
||||
{
|
||||
Sha256,
|
||||
Sha384,
|
||||
Sha512,
|
||||
Blake3_256,
|
||||
Blake3_512,
|
||||
Sm3, // China
|
||||
Streebog256, // Russia (GOST R 34.11-2012)
|
||||
Streebog512
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Signature Provider
|
||||
|
||||
```csharp
|
||||
public interface ISignatureProvider
|
||||
{
|
||||
string AlgorithmId { get; }
|
||||
string CurveId { get; }
|
||||
|
||||
Task<byte[]> SignAsync(
|
||||
ReadOnlyMemory<byte> data,
|
||||
IKeyProvider keyProvider,
|
||||
CancellationToken ct);
|
||||
|
||||
Task<bool> VerifyAsync(
|
||||
ReadOnlyMemory<byte> data,
|
||||
ReadOnlyMemory<byte> signature,
|
||||
ReadOnlyMemory<byte> publicKey,
|
||||
CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Crypto Profile
|
||||
|
||||
```csharp
|
||||
public sealed record CryptoProfile
|
||||
{
|
||||
public required string ProfileId { get; init; }
|
||||
public required string DisplayName { get; init; }
|
||||
public required HashAlgorithm PreferredHash { get; init; }
|
||||
public required string SignatureAlgorithm { get; init; }
|
||||
public required string KeyType { get; init; }
|
||||
public bool RequiresHsm { get; init; }
|
||||
public IReadOnlyList<string>? AllowedCurves { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) Supported Profiles
|
||||
|
||||
### 4.1 Built-in Profiles
|
||||
|
||||
| Profile | Hash | Signature | Use Case |
|
||||
|---------|------|-----------|----------|
|
||||
| `ecdsa-p256` | SHA-256 | ECDSA P-256 | Default, FIPS 140-2 |
|
||||
| `ecdsa-p384` | SHA-384 | ECDSA P-384 | Higher security |
|
||||
| `eddsa-ed25519` | SHA-512 | Ed25519 | Performance |
|
||||
| `ecdsa-secp256k1` | SHA-256 | ECDSA secp256k1 | Blockchain compat |
|
||||
|
||||
### 4.2 Plugin Profiles
|
||||
|
||||
| Profile | Hash | Signature | Region |
|
||||
|---------|------|-----------|--------|
|
||||
| `gost-2012` | Streebog-256 | GOST R 34.10-2012 | Russia |
|
||||
| `sm2` | SM3 | SM2 | China |
|
||||
| `eidas-rsa` | SHA-256 | RSA-PSS | EU (qualified) |
|
||||
| `eidas-ecdsa` | SHA-256 | ECDSA P-256 | EU (qualified) |
|
||||
|
||||
---
|
||||
|
||||
## 5) Plugin Architecture
|
||||
|
||||
### 5.1 Plugin Discovery
|
||||
|
||||
Plugins are loaded from `plugins/` directory:
|
||||
|
||||
```
|
||||
plugins/
|
||||
├─ gost/
|
||||
│ ├─ manifest.json
|
||||
│ └─ StellaOps.Cryptography.Plugin.Gost.dll
|
||||
└─ sm/
|
||||
├─ manifest.json
|
||||
└─ StellaOps.Cryptography.Plugin.Sm.dll
|
||||
```
|
||||
|
||||
### 5.2 Plugin Manifest
|
||||
|
||||
```json
|
||||
{
|
||||
"pluginId": "stellaops.crypto.gost",
|
||||
"version": "1.0.0",
|
||||
"profiles": ["gost-2012-256", "gost-2012-512"],
|
||||
"dependencies": {
|
||||
"cryptopro": "5.0+"
|
||||
},
|
||||
"platforms": ["win-x64", "linux-x64"]
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Plugin Interface
|
||||
|
||||
```csharp
|
||||
public interface ICryptoPlugin
|
||||
{
|
||||
string PluginId { get; }
|
||||
IReadOnlyList<CryptoProfile> Profiles { get; }
|
||||
|
||||
ISignatureProvider GetSignatureProvider(string profileId);
|
||||
ICryptoHash GetHashProvider(string profileId);
|
||||
|
||||
Task<bool> IsAvailableAsync(CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Configuration (YAML)
|
||||
|
||||
```yaml
|
||||
Cryptography:
|
||||
DefaultProfile: "ecdsa-p256"
|
||||
|
||||
Profiles:
|
||||
ecdsa-p256:
|
||||
Enabled: true
|
||||
PreferredHash: "sha256"
|
||||
|
||||
eddsa-ed25519:
|
||||
Enabled: true
|
||||
PreferredHash: "sha512"
|
||||
|
||||
gost-2012:
|
||||
Enabled: false # Requires CryptoPro
|
||||
RequiresHsm: false
|
||||
|
||||
sm2:
|
||||
Enabled: false
|
||||
HsmEndpoint: "https://hsm.example.cn"
|
||||
|
||||
Plugins:
|
||||
Directory: "/opt/stellaops/plugins/crypto"
|
||||
AutoLoad: true
|
||||
|
||||
Hsm:
|
||||
Provider: "pkcs11" # or "kms", "tpm"
|
||||
LibraryPath: "/usr/lib/softhsm/libsofthsm2.so"
|
||||
SlotId: 0
|
||||
|
||||
Kms:
|
||||
Provider: "aws" # or "azure", "gcp", "vault"
|
||||
Region: "us-east-1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7) Security considerations
|
||||
|
||||
* **Algorithm agility**: Easy migration to new algorithms
|
||||
* **Side-channel protection**: Constant-time implementations where available
|
||||
* **Key isolation**: Keys never exposed in memory dumps
|
||||
* **Determinism**: Same input produces same signature (where algorithm allows)
|
||||
* **Audit trail**: All crypto operations logged
|
||||
|
||||
---
|
||||
|
||||
## 8) Performance targets
|
||||
|
||||
* **SHA-256**: > 500 MB/s on modern hardware
|
||||
* **BLAKE3**: > 1 GB/s on modern hardware
|
||||
* **ECDSA P-256 sign**: < 1ms per signature
|
||||
* **Ed25519 sign**: < 0.5ms per signature
|
||||
* **HSM operations**: < 50ms (network latency dependent)
|
||||
|
||||
---
|
||||
|
||||
## 9) Testing matrix
|
||||
|
||||
* **Vector tests**: NIST/RFC test vectors for each algorithm
|
||||
* **Interoperability**: Cross-platform signature verification
|
||||
* **Determinism**: Same key + data = same signature
|
||||
* **Plugin tests**: Load/unload, availability detection
|
||||
* **HSM tests**: Mock HSM for CI, real HSM for integration
|
||||
|
||||
---
|
||||
|
||||
## 10) Offline Operation
|
||||
|
||||
For air-gapped deployments:
|
||||
|
||||
1. **Offline key bundles**: Pre-exported public keys for verification
|
||||
2. **Local plugins**: All plugins loaded from local filesystem
|
||||
3. **No KMS calls**: All operations use local keys or HSM
|
||||
4. **Trust bundles**: Pre-configured trust roots
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Multi-profile signing: `./multi-profile-signing-specification.md`
|
||||
* Signer module: `../signer/architecture.md`
|
||||
* Attestor module: `../attestor/architecture.md`
|
||||
* Offline operations: `../../24_OFFLINE_KIT.md`
|
||||
278
docs/modules/evidence-locker/architecture.md
Normal file
278
docs/modules/evidence-locker/architecture.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# component_architecture_evidence_locker.md - **Stella Ops EvidenceLocker** (2025Q4)
|
||||
|
||||
> Sealed, immutable storage for vulnerability scan evidence and audit logs.
|
||||
|
||||
> **Scope.** Implementation-ready architecture for **EvidenceLocker**: tamper-proof evidence chains for compliance and forensic analysis with content-addressable storage and cryptographic sealing.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide **immutable, sealed storage** for vulnerability scan evidence, audit logs, and compliance artifacts. Ensure tamper-proof evidence chains with cryptographic verification for forensic analysis and regulatory compliance.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* EvidenceLocker **stores** evidence; it does not generate verdicts.
|
||||
* EvidenceLocker **seals** bundles; signing is delegated to Signer.
|
||||
* Evidence is **immutable** once sealed; no modifications or deletions.
|
||||
* Supports **offline export** for air-gapped compliance audits.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/EvidenceLocker/StellaOps.EvidenceLocker/
|
||||
├─ StellaOps.EvidenceLocker.Core/ # Sealing, verification, chain validation
|
||||
│ ├─ Services/
|
||||
│ │ ├─ ISealingService.cs # Sealing interface
|
||||
│ │ ├─ SealingService.cs # Cryptographic sealing
|
||||
│ │ ├─ IVerificationService.cs # Verification interface
|
||||
│ │ └─ ChainValidator.cs # Evidence chain validation
|
||||
│ └─ Models/
|
||||
│ ├─ EvidenceBundle.cs # Bundle model
|
||||
│ ├─ EvidenceItem.cs # Individual evidence item
|
||||
│ └─ SealManifest.cs # Seal metadata
|
||||
│
|
||||
├─ StellaOps.EvidenceLocker.Infrastructure/ # Storage adapters, bundle management
|
||||
│ ├─ Storage/
|
||||
│ │ ├─ IEvidenceStore.cs # Storage interface
|
||||
│ │ ├─ FileSystemStore.cs # Local filesystem
|
||||
│ │ └─ ObjectStore.cs # S3/RustFS storage
|
||||
│ └─ Persistence/
|
||||
│ └─ PostgresRepository.cs # Metadata persistence
|
||||
│
|
||||
├─ StellaOps.EvidenceLocker.WebService/ # HTTP API for submission/retrieval
|
||||
│ └─ Program.cs
|
||||
│
|
||||
├─ StellaOps.EvidenceLocker.Worker/ # Background sealing and archival
|
||||
│ └─ SealingWorker.cs
|
||||
│
|
||||
└─ StellaOps.EvidenceLocker.Tests/ # Unit and integration tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **PostgreSQL** - Metadata storage (schema: `evidence_locker`)
|
||||
* **RustFS/S3** - Object storage for evidence bundles
|
||||
* **Signer** - Cryptographic sealing operations
|
||||
* **Authority** - Authentication and authorization
|
||||
* **ExportCenter** - Evidence bundle export
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 EvidenceBundle
|
||||
|
||||
```json
|
||||
{
|
||||
"bundleId": "eb-2025-01-15-abc123",
|
||||
"tenantId": "tenant-xyz",
|
||||
"createdAt": "2025-01-15T10:30:00.000000Z",
|
||||
"sealedAt": "2025-01-15T10:30:05.000000Z",
|
||||
"status": "sealed",
|
||||
"items": [
|
||||
{
|
||||
"itemId": "item-001",
|
||||
"type": "sbom",
|
||||
"format": "cyclonedx-json",
|
||||
"digest": "sha256:abc123...",
|
||||
"size": 45678,
|
||||
"casUri": "cas://evidence/items/abc123"
|
||||
},
|
||||
{
|
||||
"itemId": "item-002",
|
||||
"type": "scan-result",
|
||||
"format": "stellaops-findings-v1",
|
||||
"digest": "sha256:def456...",
|
||||
"size": 12345,
|
||||
"casUri": "cas://evidence/items/def456"
|
||||
}
|
||||
],
|
||||
"seal": {
|
||||
"algorithm": "sha256",
|
||||
"rootHash": "sha256:fedcba...",
|
||||
"signature": "base64...",
|
||||
"keyId": "sha256:keyabc..."
|
||||
},
|
||||
"chain": {
|
||||
"previousBundleId": "eb-2025-01-14-xyz789",
|
||||
"previousRootHash": "sha256:prevhash...",
|
||||
"sequenceNumber": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Evidence Item Types
|
||||
|
||||
| Type | Format | Description |
|
||||
|------|--------|-------------|
|
||||
| `sbom` | cyclonedx-json, spdx-json | Software Bill of Materials |
|
||||
| `scan-result` | stellaops-findings-v1 | Vulnerability scan findings |
|
||||
| `policy-verdict` | stellaops-verdict-v1 | Policy evaluation result |
|
||||
| `vex-statement` | openvex-v1 | VEX statement |
|
||||
| `audit-log` | ndjson | Audit trail events |
|
||||
| `attestation` | dsse-v1 | DSSE attestation envelope |
|
||||
|
||||
### 3.3 Seal Manifest
|
||||
|
||||
```csharp
|
||||
public sealed record SealManifest
|
||||
{
|
||||
public required string BundleId { get; init; }
|
||||
public required string RootHash { get; init; }
|
||||
public required string Algorithm { get; init; }
|
||||
public required DateTimeOffset SealedAt { get; init; }
|
||||
public required string KeyId { get; init; }
|
||||
public required byte[] Signature { get; init; }
|
||||
public required IReadOnlyList<string> ItemDigests { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) REST API (EvidenceLocker.WebService)
|
||||
|
||||
All under `/api/v1/evidence`. Auth: **OpTok**.
|
||||
|
||||
```
|
||||
POST /bundles { items: EvidenceItem[] } → { bundleId, status: "pending" }
|
||||
POST /bundles/{id}/items { item: EvidenceItem } → { itemId }
|
||||
POST /bundles/{id}/seal → { status: "sealed", seal: SealManifest }
|
||||
|
||||
GET /bundles/{id} → { bundle: EvidenceBundle }
|
||||
GET /bundles/{id}/items/{itemId} → binary content
|
||||
GET /bundles/{id}/verify → { valid: bool, details }
|
||||
|
||||
GET /bundles?tenant={id}&from={date}&to={date} → { bundles: BundleSummary[] }
|
||||
|
||||
POST /export { bundleIds: string[], format: "zip"|"tar" } → { exportId }
|
||||
GET /export/{id} → binary archive
|
||||
GET /export/{id}/status → { status, progress }
|
||||
|
||||
GET /healthz | /readyz | /metrics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) Configuration (YAML)
|
||||
|
||||
```yaml
|
||||
EvidenceLocker:
|
||||
Postgres:
|
||||
ConnectionString: "Host=postgres;Database=evidence_locker;..."
|
||||
|
||||
Storage:
|
||||
Provider: "rustfs" # or "filesystem", "s3"
|
||||
RustFs:
|
||||
Endpoint: "http://rustfs:8080"
|
||||
Bucket: "stellaops-evidence"
|
||||
Filesystem:
|
||||
BasePath: "/data/evidence"
|
||||
|
||||
Sealing:
|
||||
Policy: "immediate" # or "batch"
|
||||
BatchSize: 100
|
||||
BatchIntervalSeconds: 60
|
||||
Algorithm: "sha256"
|
||||
|
||||
Retention:
|
||||
DefaultDays: 2555 # 7 years
|
||||
ComplianceDays: 3650 # 10 years for regulated
|
||||
|
||||
Export:
|
||||
MaxBundlesPerExport: 1000
|
||||
CompressionLevel: 6
|
||||
|
||||
Authority:
|
||||
Issuer: "https://authority.stellaops.local"
|
||||
RequiredScopes: ["evidence:read", "evidence:write"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Sealing Process
|
||||
|
||||
### 6.1 Sealing Flow
|
||||
|
||||
```
|
||||
1. Bundle created (status: "pending")
|
||||
└─ Items added with content digests
|
||||
|
||||
2. Sealing triggered (immediate or batch)
|
||||
├─ Compute Merkle root from item digests
|
||||
├─ Include chain pointer (previous bundle hash)
|
||||
└─ Request signature from Signer
|
||||
|
||||
3. Bundle sealed (status: "sealed")
|
||||
└─ Immutable; no further modifications
|
||||
```
|
||||
|
||||
### 6.2 Chain Integrity
|
||||
|
||||
Evidence chains are linked via Merkle roots:
|
||||
|
||||
```
|
||||
Bundle N-1 Bundle N Bundle N+1
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ rootHash: H1│◄────────────│ prevHash: H1│◄─────────────│ prevHash: H2│
|
||||
│ seq: 41 │ │ rootHash: H2│ │ rootHash: H3│
|
||||
└─────────────┘ │ seq: 42 │ │ seq: 43 │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7) Security & compliance
|
||||
|
||||
* **Immutability**: Sealed bundles cannot be modified
|
||||
* **Tamper detection**: Merkle tree verification for all items
|
||||
* **Chain validation**: Linked bundle verification
|
||||
* **Encryption at rest**: Optional bundle encryption
|
||||
* **Access control**: Tenant-scoped with Authority tokens
|
||||
* **Audit trail**: All access logged
|
||||
|
||||
---
|
||||
|
||||
## 8) Performance targets
|
||||
|
||||
* **Item ingestion**: < 100ms P95 per item
|
||||
* **Sealing**: < 500ms P95 per bundle (up to 100 items)
|
||||
* **Verification**: < 200ms P95 per bundle
|
||||
* **Export**: < 5s P95 for 100 bundles
|
||||
|
||||
---
|
||||
|
||||
## 9) Observability
|
||||
|
||||
**Metrics:**
|
||||
* `evidence.bundles.created_total{tenant}`
|
||||
* `evidence.bundles.sealed_total{tenant}`
|
||||
* `evidence.items.ingested_total{type}`
|
||||
* `evidence.verification.duration_seconds`
|
||||
* `evidence.storage.bytes_total`
|
||||
|
||||
**Tracing:** Spans for ingestion, sealing, verification, export.
|
||||
|
||||
---
|
||||
|
||||
## 10) Testing matrix
|
||||
|
||||
* **Sealing tests**: Correct Merkle root computation
|
||||
* **Chain tests**: Linked bundle verification
|
||||
* **Tamper tests**: Detection of modified items
|
||||
* **Export tests**: Archive integrity verification
|
||||
* **Retention tests**: Policy enforcement
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Bundle packaging: `./bundle-packaging.md`
|
||||
* Attestation contract: `./attestation-contract.md`
|
||||
* Evidence bundle spec: `./evidence-bundle-v1.md`
|
||||
* ExportCenter: `../export-center/architecture.md`
|
||||
* Attestor: `../attestor/architecture.md`
|
||||
237
docs/modules/feedser/architecture.md
Normal file
237
docs/modules/feedser/architecture.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# component_architecture_feedser.md - **Stella Ops Feedser** (2025Q4)
|
||||
|
||||
> Evidence collection library for backport detection and binary fingerprinting.
|
||||
|
||||
> **Scope.** Library architecture for **Feedser**: patch signature extraction, binary fingerprinting, and evidence collection supporting the four-tier backport proof system. Consumed primarily by Concelier's ProofService layer.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide deterministic, cryptographic evidence collection for backport detection. Extract patch signatures from unified diffs and binary fingerprints from compiled code to enable high-confidence vulnerability status determination for packages where upstream fixes have been backported by distro maintainers.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Feedser is a **library**, not a standalone service. It does not expose REST APIs directly.
|
||||
* Feedser **does not** make vulnerability decisions. It provides evidence that feeds into VEX statements and Policy Engine evaluation.
|
||||
* Feedser **does not** store data. Storage is handled by consuming services (Concelier ProofService, Attestor).
|
||||
* All outputs are **deterministic** with canonical JSON serialization and stable hashing.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Feedser/
|
||||
├─ StellaOps.Feedser.Core/ # Patch signature extraction (HunkSig)
|
||||
│ ├─ HunkSigExtractor.cs # Unified diff parser and normalizer
|
||||
│ ├─ Models/
|
||||
│ │ ├─ PatchSignature.cs # Deterministic patch identifier
|
||||
│ │ ├─ HunkSignature.cs # Individual hunk with normalized content
|
||||
│ │ └─ DiffParseResult.cs # Parse output with file paths and hunks
|
||||
│ └─ Normalization/
|
||||
│ └─ WhitespaceNormalizer.cs # Whitespace/comment stripping
|
||||
│
|
||||
├─ StellaOps.Feedser.BinaryAnalysis/ # Binary fingerprinting engine
|
||||
│ ├─ BinaryFingerprintFactory.cs # Factory for fingerprinting strategies
|
||||
│ ├─ IBinaryFingerprinter.cs # Fingerprinter interface
|
||||
│ ├─ Models/
|
||||
│ │ ├─ BinaryFingerprint.cs # Fingerprint record with method/value
|
||||
│ │ └─ FingerprintMatchResult.cs # Match score and confidence
|
||||
│ └─ Fingerprinters/
|
||||
│ ├─ SimplifiedTlshFingerprinter.cs # TLSH fuzzy hashing
|
||||
│ └─ InstructionHashFingerprinter.cs # Instruction sequence hashing
|
||||
│
|
||||
├─ plugins/
|
||||
│ └─ concelier/ # Concelier integration plugin
|
||||
│
|
||||
└─ __Tests/
|
||||
└─ StellaOps.Feedser.Core.Tests/ # Unit tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **Concelier ProofService** - Primary consumer; orchestrates four-tier evidence collection
|
||||
* **Attestor ProofChain** - Consumes evidence for proof blob generation
|
||||
* **.NET 10** - Runtime target
|
||||
* No database dependencies (stateless library)
|
||||
* No external network dependencies
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 Patch Signature (Tier 3 Evidence)
|
||||
|
||||
```csharp
|
||||
public sealed record PatchSignature
|
||||
{
|
||||
public required string Id { get; init; } // Deterministic SHA256
|
||||
public required string FilePath { get; init; } // Source file path
|
||||
public required IReadOnlyList<HunkSignature> Hunks { get; init; }
|
||||
public required string ContentHash { get; init; } // BLAKE3-256 of normalized content
|
||||
public string? CommitId { get; init; } // Git commit SHA if available
|
||||
public string? UpstreamCve { get; init; } // Associated CVE
|
||||
}
|
||||
|
||||
public sealed record HunkSignature
|
||||
{
|
||||
public required int OldStart { get; init; }
|
||||
public required int NewStart { get; init; }
|
||||
public required string NormalizedContent { get; init; } // Whitespace-stripped
|
||||
public required string ContentHash { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Binary Fingerprint (Tier 4 Evidence)
|
||||
|
||||
```csharp
|
||||
public sealed record BinaryFingerprint
|
||||
{
|
||||
public required string Method { get; init; } // tlsh, instruction_hash
|
||||
public required string Value { get; init; } // Fingerprint value
|
||||
public required string TargetPath { get; init; } // Binary file path
|
||||
public string? FunctionName { get; init; } // Function if scoped
|
||||
public required string Architecture { get; init; } // x86_64, aarch64, etc.
|
||||
}
|
||||
|
||||
public sealed record FingerprintMatchResult
|
||||
{
|
||||
public required decimal Similarity { get; init; } // 0.0-1.0
|
||||
public required decimal Confidence { get; init; } // 0.0-1.0
|
||||
public required string Method { get; init; }
|
||||
public required BinaryFingerprint Query { get; init; }
|
||||
public required BinaryFingerprint Match { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Evidence Tier Confidence Levels
|
||||
|
||||
| Tier | Evidence Type | Confidence Range | Description |
|
||||
|------|--------------|------------------|-------------|
|
||||
| 1 | Distro Advisory | 0.95-0.98 | Official vendor/distro statement |
|
||||
| 2 | Changelog Mention | 0.75-0.85 | CVE mentioned in changelog |
|
||||
| 3 | Patch Signature (HunkSig) | 0.85-0.95 | Normalized patch hash match |
|
||||
| 4 | Binary Fingerprint | 0.55-0.85 | Compiled code similarity |
|
||||
|
||||
---
|
||||
|
||||
## 4) Core Components
|
||||
|
||||
### 4.1 HunkSigExtractor
|
||||
|
||||
Parses unified diff format and extracts normalized patch signatures:
|
||||
|
||||
```csharp
|
||||
public interface IHunkSigExtractor
|
||||
{
|
||||
PatchSignature Extract(string unifiedDiff, string? commitId = null);
|
||||
IReadOnlyList<PatchSignature> ExtractMultiple(string multiFileDiff);
|
||||
}
|
||||
```
|
||||
|
||||
**Normalization rules:**
|
||||
- Strip leading/trailing whitespace
|
||||
- Normalize line endings to LF
|
||||
- Remove C-style comments (optional)
|
||||
- Collapse multiple whitespace to single space
|
||||
- Sort hunks by (file_path, old_start) for determinism
|
||||
|
||||
### 4.2 BinaryFingerprintFactory
|
||||
|
||||
Factory for creating fingerprinters based on binary type and analysis requirements:
|
||||
|
||||
```csharp
|
||||
public interface IBinaryFingerprintFactory
|
||||
{
|
||||
IBinaryFingerprinter Create(FingerprintMethod method);
|
||||
IReadOnlyList<IBinaryFingerprinter> GetAll();
|
||||
}
|
||||
|
||||
public interface IBinaryFingerprinter
|
||||
{
|
||||
string Method { get; }
|
||||
BinaryFingerprint Extract(ReadOnlySpan<byte> binary, string path);
|
||||
FingerprintMatchResult Match(BinaryFingerprint query, BinaryFingerprint candidate);
|
||||
}
|
||||
```
|
||||
|
||||
**Fingerprinting methods:**
|
||||
|
||||
| Method | Description | Confidence | Use Case |
|
||||
|--------|-------------|------------|----------|
|
||||
| `tlsh` | TLSH fuzzy hash | 0.75-0.85 | General binary similarity |
|
||||
| `instruction_hash` | Normalized instruction sequences | 0.55-0.75 | Function-level matching |
|
||||
|
||||
---
|
||||
|
||||
## 5) Integration with Concelier
|
||||
|
||||
Feedser is consumed via `StellaOps.Concelier.ProofService.BackportProofService`:
|
||||
|
||||
```
|
||||
BackportProofService (Concelier)
|
||||
├─ Tier 1: Query advisory_observations (distro advisories)
|
||||
├─ Tier 2: Query changelogs via ISourceRepository
|
||||
├─ Tier 3: Query patches via IPatchRepository + HunkSigExtractor
|
||||
├─ Tier 4: Query binaries + BinaryFingerprintFactory
|
||||
└─ Aggregate → ProofBlob with combined confidence score
|
||||
```
|
||||
|
||||
The ProofService orchestrates evidence collection across all tiers and produces cryptographic proof blobs for downstream consumption.
|
||||
|
||||
---
|
||||
|
||||
## 6) Security & compliance
|
||||
|
||||
* **Determinism**: All outputs use canonical JSON with sorted keys, UTC timestamps
|
||||
* **Tamper evidence**: BLAKE3-256 content hashes for all signatures
|
||||
* **No secrets**: Library handles only public patch/binary data
|
||||
* **Offline capable**: No network dependencies in core library
|
||||
|
||||
---
|
||||
|
||||
## 7) Performance targets
|
||||
|
||||
* **Patch extraction**: < 10ms for typical unified diff (< 1000 lines)
|
||||
* **Binary fingerprinting**: < 100ms for 10MB ELF binary
|
||||
* **Memory**: Streaming processing for large binaries; no full file buffering
|
||||
* **Parallelism**: Thread-safe extractors; concurrent fingerprinting supported
|
||||
|
||||
---
|
||||
|
||||
## 8) Observability
|
||||
|
||||
Library consumers (ProofService) emit metrics:
|
||||
|
||||
* `feedser.hunk_extraction_duration_seconds`
|
||||
* `feedser.binary_fingerprint_duration_seconds`
|
||||
* `feedser.fingerprint_match_score{method}`
|
||||
* `feedser.evidence_tier_confidence{tier}`
|
||||
|
||||
---
|
||||
|
||||
## 9) Testing matrix
|
||||
|
||||
* **Unit tests**: HunkSigExtractor parsing, normalization edge cases
|
||||
* **Fingerprint tests**: Known binary pairs with expected similarity scores
|
||||
* **Determinism tests**: Same input produces identical output across runs
|
||||
* **Performance tests**: Large diff/binary processing within targets
|
||||
|
||||
---
|
||||
|
||||
## 10) Historical note
|
||||
|
||||
Concelier was formerly named "Feedser" (see `docs/airgap/airgap-mode.md`). The module was refactored:
|
||||
- **Feedser** retained as evidence collection library
|
||||
- **Concelier** became the advisory aggregation service consuming Feedser
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Concelier architecture: `../concelier/architecture.md`
|
||||
* Attestor ProofChain: `../attestor/architecture.md`
|
||||
* Backport proof system: `../../reachability/backport-proofs.md`
|
||||
60
docs/modules/mirror/architecture.md
Normal file
60
docs/modules/mirror/architecture.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# component_architecture_mirror.md - **Stella Ops Mirror** (2025Q4)
|
||||
|
||||
> Vulnerability feed mirror and distribution service.
|
||||
|
||||
> **Scope.** Architecture for **Mirror**: mirroring vulnerability feeds from upstream sources for offline distribution and reduced external dependencies.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide **local mirrors** of vulnerability feeds (NVD, OSV, GHSA, etc.) for offline operation and reduced latency. Enable air-gapped deployments to receive updates via bundle import.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Mirror **caches upstream feeds**; it does not originate vulnerability data.
|
||||
* Mirror **produces bundles** for air-gapped distribution.
|
||||
* Feeds are **cryptographically verified** before distribution.
|
||||
|
||||
---
|
||||
|
||||
## 1) Integration with Concelier
|
||||
|
||||
Mirror is primarily integrated as part of Concelier's federation layer:
|
||||
|
||||
```
|
||||
src/Concelier/__Libraries/
|
||||
└─ StellaOps.Concelier.Federation/ # Bundle export/import for offline
|
||||
```
|
||||
|
||||
The `StellaOpsMirror` connector in Concelier handles:
|
||||
- Upstream feed synchronization
|
||||
- Local cache management
|
||||
- Bundle generation for offline distribution
|
||||
|
||||
---
|
||||
|
||||
## 2) Bundle Format
|
||||
|
||||
```json
|
||||
{
|
||||
"bundleId": "mirror-nvd-2025-01-15",
|
||||
"source": "nvd",
|
||||
"timestamp": "2025-01-15T10:30:00Z",
|
||||
"contents": [
|
||||
{
|
||||
"path": "nvd/CVE-2025-*.json",
|
||||
"digest": "sha256:abc123..."
|
||||
}
|
||||
],
|
||||
"signature": { /* DSSE envelope */ }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Concelier: `../concelier/architecture.md`
|
||||
* AirGap: `../airgap/architecture.md`
|
||||
* Provenance observers: `./provenance/observers.md`
|
||||
38
docs/modules/notifier/README.md
Normal file
38
docs/modules/notifier/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Notifier (Notifications Studio Host)
|
||||
|
||||
**Status:** Implemented
|
||||
**Source:** `src/Notifier/`
|
||||
**Owner:** Notify Guild
|
||||
|
||||
> **Note:** Notifier is the **deployment host** for the Notifications Studio. For the underlying notification toolkit (engine, storage, queue, connectors), see [`../notify/`](../notify/).
|
||||
|
||||
## Purpose
|
||||
|
||||
Notifier provides the deployable WebService and Worker that compose the Notify libraries into the Notifications Studio experience. It's the entry point for notification delivery, rule management, and delivery history.
|
||||
|
||||
## Relationship to Notify
|
||||
|
||||
| Component | Path | Purpose |
|
||||
|-----------|------|---------|
|
||||
| **Notify** | `src/Notify/` | Reusable toolkit: engine, models, connectors, queue |
|
||||
| **Notifier** | `src/Notifier/` | Host: WebService and Worker that compose Notify |
|
||||
|
||||
Per **2025-11-02 module boundary decision**: Maintain separation for packaging, offline kit parity, and cross-module governance.
|
||||
|
||||
## Components
|
||||
|
||||
**Deployables:**
|
||||
- `StellaOps.Notifier.WebService` - REST API for rules/channels CRUD, test send, delivery browsing
|
||||
- `StellaOps.Notifier.Worker` - Event consumers, evaluators, renderers, delivery workers
|
||||
|
||||
**Integration Points:**
|
||||
- Uses `StellaOps.Notify.Models`, `StellaOps.Notify.Queue`
|
||||
- Channels: Slack, Teams, Email, Webhook (via Notify connectors)
|
||||
- Storage: PostgreSQL (notify schema)
|
||||
- Queue: Valkey Streams / NATS JetStream
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- Notify Architecture: `../notify/architecture.md`
|
||||
- Authority: `../authority/` (OAuth clients)
|
||||
- Scheduler: `../scheduler/` (event sources)
|
||||
100
docs/modules/packsregistry/architecture.md
Normal file
100
docs/modules/packsregistry/architecture.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# component_architecture_packsregistry.md - **Stella Ops PacksRegistry** (2025Q4)
|
||||
|
||||
> Task packs registry and distribution service.
|
||||
|
||||
> **Scope.** Implementation-ready architecture for **PacksRegistry**: the registry for task packs, policy packs, and analyzer packs that can be distributed to TaskRunner instances.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide a **centralized registry** for distributable task packs, policy packs, and analyzer bundles. Enable versioned pack management with integrity verification and air-gap support.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* PacksRegistry **stores and distributes** packs; it does not execute them.
|
||||
* Pack execution is handled by **TaskRunner**.
|
||||
* All packs are **content-addressed** with integrity verification.
|
||||
* Supports **offline distribution** via bundle export.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/PacksRegistry/StellaOps.PacksRegistry/
|
||||
├─ StellaOps.PacksRegistry.Core/ # Pack models, validation
|
||||
├─ StellaOps.PacksRegistry.Infrastructure/ # Storage, distribution
|
||||
├─ StellaOps.PacksRegistry.Persistence.EfCore/ # EF Core persistence
|
||||
├─ StellaOps.PacksRegistry.WebService/ # REST API
|
||||
├─ StellaOps.PacksRegistry.Worker/ # Background processing
|
||||
└─ StellaOps.PacksRegistry.Tests/
|
||||
|
||||
src/PacksRegistry/__Libraries/
|
||||
└─ StellaOps.PacksRegistry.Persistence/ # Persistence abstractions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **PostgreSQL** - Pack metadata storage
|
||||
* **RustFS/S3** - Pack content storage
|
||||
* **Authority** - Authentication and authorization
|
||||
* **TaskRunner** - Pack consumer
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 Pack
|
||||
|
||||
```json
|
||||
{
|
||||
"packId": "policy-baseline-v2",
|
||||
"version": "2.1.0",
|
||||
"type": "policy",
|
||||
"name": "Baseline Security Policy",
|
||||
"description": "Standard security policy pack",
|
||||
"digest": "sha256:abc123...",
|
||||
"size": 45678,
|
||||
"publishedAt": "2025-01-15T10:30:00Z",
|
||||
"author": "stellaops",
|
||||
"dependencies": [],
|
||||
"metadata": {
|
||||
"minRunnerVersion": "1.5.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Pack Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `policy` | Policy rule packs |
|
||||
| `analyzer` | Scanner analyzer packs |
|
||||
| `task` | TaskRunner task definitions |
|
||||
| `bundle` | Composite packs |
|
||||
|
||||
---
|
||||
|
||||
## 4) REST API
|
||||
|
||||
```
|
||||
GET /packs → { packs: PackSummary[] }
|
||||
GET /packs/{id} → { pack: Pack }
|
||||
GET /packs/{id}/versions → { versions: Version[] }
|
||||
GET /packs/{id}/{version} → binary content
|
||||
|
||||
POST /packs { manifest, content } → { packId }
|
||||
DELETE /packs/{id}/{version} → { deleted: bool }
|
||||
|
||||
GET /healthz | /readyz | /metrics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* TaskRunner: `../taskrunner/architecture.md`
|
||||
* Policy: `../policy/architecture.md`
|
||||
316
docs/modules/provenance/architecture.md
Normal file
316
docs/modules/provenance/architecture.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# component_architecture_provenance.md - **Stella Ops Provenance** (2025Q4)
|
||||
|
||||
> Provenance attestation library for SLSA/DSSE compliance.
|
||||
|
||||
> **Scope.** Library architecture for **Provenance**: shared libraries and tooling for generating, signing, and verifying provenance attestations (DSSE/SLSA). Used by evidence bundles, exports, and timeline verification flows.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide **deterministic, verifiable provenance attestations** for all StellaOps artifacts. Enable SLSA compliance through DSSE statement generation, Merkle tree construction, and cryptographic verification.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Provenance is a **library**, not a standalone service.
|
||||
* Provenance **does not** store attestations. Storage is handled by Attestor and EvidenceLocker.
|
||||
* Provenance **does not** hold signing keys. Key management is delegated to Signer/KMS.
|
||||
* All attestation outputs are **deterministic** with canonical JSON serialization.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Provenance/
|
||||
├─ StellaOps.Provenance.Attestation/ # Core attestation library
|
||||
│ ├─ AGENTS.md # Guild charter
|
||||
│ ├─ PromotionAttestation.cs # Promotion statement builder
|
||||
│ ├─ BuildModels.cs # SLSA build predicate models
|
||||
│ ├─ Signers.cs # Signer abstractions
|
||||
│ ├─ Verification.cs # Verification harness
|
||||
│ └─ Hex.cs # Hex encoding utilities
|
||||
│
|
||||
├─ StellaOps.Provenance.Attestation.Tool/ # CLI tool for attestation
|
||||
│ ├─ Program.cs
|
||||
│ └─ README.md
|
||||
│
|
||||
└─ __Tests/
|
||||
└─ StellaOps.Provenance.Attestation.Tests/
|
||||
├─ PromotionAttestationBuilderTests.cs
|
||||
├─ VerificationTests.cs
|
||||
├─ SignersTests.cs
|
||||
├─ MerkleTreeTests.cs
|
||||
└─ RotatingSignerTests.cs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **Signer** - Cryptographic signing operations
|
||||
* **Cryptography** - Hash computation, signature algorithms
|
||||
* **EvidenceLocker** - Consumes attestations for storage
|
||||
* **ExportCenter** - Attaches attestations to export bundles
|
||||
* **.NET 10** - Runtime target
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 DSSE Statement
|
||||
|
||||
Dead Simple Signing Envelope (DSSE) format:
|
||||
|
||||
```json
|
||||
{
|
||||
"payloadType": "application/vnd.in-toto+json",
|
||||
"payload": "<base64-encoded-statement>",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "sha256:abc123...",
|
||||
"sig": "<base64-signature>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 SLSA Provenance Predicate
|
||||
|
||||
```json
|
||||
{
|
||||
"_type": "https://in-toto.io/Statement/v1",
|
||||
"subject": [
|
||||
{
|
||||
"name": "pkg:oci/scanner@sha256:abc123",
|
||||
"digest": { "sha256": "abc123..." }
|
||||
}
|
||||
],
|
||||
"predicateType": "https://slsa.dev/provenance/v1",
|
||||
"predicate": {
|
||||
"buildDefinition": {
|
||||
"buildType": "https://stellaops.dev/build/v1",
|
||||
"externalParameters": {},
|
||||
"internalParameters": {},
|
||||
"resolvedDependencies": []
|
||||
},
|
||||
"runDetails": {
|
||||
"builder": {
|
||||
"id": "https://stellaops.dev/builders/scanner"
|
||||
},
|
||||
"metadata": {
|
||||
"invocationId": "build-2025-01-15-abc123",
|
||||
"startedOn": "2025-01-15T10:30:00Z",
|
||||
"finishedOn": "2025-01-15T10:35:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Promotion Attestation
|
||||
|
||||
For artifact promotion across environments:
|
||||
|
||||
```csharp
|
||||
public sealed class PromotionAttestation
|
||||
{
|
||||
public required string ArtifactDigest { get; init; }
|
||||
public required string SourceEnvironment { get; init; }
|
||||
public required string TargetEnvironment { get; init; }
|
||||
public required DateTimeOffset PromotedAt { get; init; }
|
||||
public required string ApprovedBy { get; init; }
|
||||
public required IReadOnlyList<string> PolicyDigests { get; init; }
|
||||
public string? MerkleRoot { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) Core Components
|
||||
|
||||
### 4.1 Signer Abstractions
|
||||
|
||||
```csharp
|
||||
public interface IAttestationSigner
|
||||
{
|
||||
string KeyId { get; }
|
||||
string Algorithm { get; }
|
||||
|
||||
Task<byte[]> SignAsync(
|
||||
ReadOnlyMemory<byte> payload,
|
||||
CancellationToken ct);
|
||||
}
|
||||
|
||||
public interface IRotatingSigner : IAttestationSigner
|
||||
{
|
||||
DateTimeOffset KeyNotBefore { get; }
|
||||
DateTimeOffset KeyNotAfter { get; }
|
||||
Task<IAttestationSigner> GetCurrentSignerAsync(CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Verification Harness
|
||||
|
||||
```csharp
|
||||
public interface IAttestationVerifier
|
||||
{
|
||||
Task<VerificationResult> VerifyAsync(
|
||||
DsseEnvelope envelope,
|
||||
VerificationOptions options,
|
||||
CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record VerificationResult
|
||||
{
|
||||
public required bool IsValid { get; init; }
|
||||
public required string KeyId { get; init; }
|
||||
public DateTimeOffset? SignedAt { get; init; }
|
||||
public IReadOnlyList<string>? Warnings { get; init; }
|
||||
public string? ErrorMessage { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Merkle Tree Utilities
|
||||
|
||||
For evidence chain verification:
|
||||
|
||||
```csharp
|
||||
public static class MerkleTree
|
||||
{
|
||||
public static string ComputeRoot(IEnumerable<string> leaves);
|
||||
public static IReadOnlyList<string> ComputePath(
|
||||
IReadOnlyList<string> leaves,
|
||||
int leafIndex);
|
||||
public static bool VerifyPath(
|
||||
string leaf,
|
||||
IReadOnlyList<string> path,
|
||||
string root);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) CLI Tool
|
||||
|
||||
`StellaOps.Provenance.Attestation.Tool` provides CLI commands:
|
||||
|
||||
```bash
|
||||
# Generate provenance attestation
|
||||
provenance-tool generate \
|
||||
--subject "pkg:oci/scanner@sha256:abc123" \
|
||||
--builder "stellaops/ci" \
|
||||
--output attestation.json
|
||||
|
||||
# Sign attestation
|
||||
provenance-tool sign \
|
||||
--input attestation.json \
|
||||
--key-id "kms://keys/signing-key" \
|
||||
--output attestation.dsse.json
|
||||
|
||||
# Verify attestation
|
||||
provenance-tool verify \
|
||||
--input attestation.dsse.json \
|
||||
--trust-root trust-bundle.json
|
||||
|
||||
# Generate promotion attestation
|
||||
provenance-tool promote \
|
||||
--artifact "sha256:abc123" \
|
||||
--from staging \
|
||||
--to production \
|
||||
--approver "user@example.com"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Security & compliance
|
||||
|
||||
* **SLSA L3 compliance**: Build provenance with hermetic builds
|
||||
* **Key rotation**: RotatingSigner supports key rotation with overlap
|
||||
* **Determinism**: Canonical JSON ensures reproducible digests
|
||||
* **Offline verification**: Trust bundles for air-gapped verification
|
||||
* **Threat model**: Reviewed before each release
|
||||
|
||||
---
|
||||
|
||||
## 7) Performance targets
|
||||
|
||||
* **Statement generation**: < 10ms for typical attestation
|
||||
* **Signing**: Depends on KMS (target < 100ms for HSM)
|
||||
* **Verification**: < 50ms for single signature
|
||||
* **Merkle root**: < 100ms for 10,000 leaves
|
||||
|
||||
---
|
||||
|
||||
## 8) Testing matrix
|
||||
|
||||
* **Serialization tests**: Deterministic JSON output across runs
|
||||
* **Signing tests**: Round-trip sign/verify
|
||||
* **Merkle tests**: Path generation and verification
|
||||
* **Rotation tests**: Key rotation with overlap handling
|
||||
* **Integration tests**: Full attestation flow with mock KMS
|
||||
|
||||
---
|
||||
|
||||
## 9) Sample Artifacts
|
||||
|
||||
Samples committed under `samples/provenance/`:
|
||||
|
||||
```
|
||||
samples/provenance/
|
||||
├─ slsa-provenance-v1.json # Sample SLSA statement
|
||||
├─ promotion-attestation.json # Sample promotion
|
||||
├─ trust-bundle.json # Sample trust root
|
||||
└─ verify-example.sh # Verification script
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10) Integration Points
|
||||
|
||||
### 10.1 EvidenceLocker
|
||||
|
||||
Evidence bundles include attestations:
|
||||
|
||||
```json
|
||||
{
|
||||
"bundleId": "eb-2025-01-15-abc123",
|
||||
"attestations": [
|
||||
{
|
||||
"type": "slsa-provenance",
|
||||
"dsse": { /* DSSE envelope */ }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 ExportCenter
|
||||
|
||||
Exports attach attestations to manifests:
|
||||
|
||||
```json
|
||||
{
|
||||
"exportId": "export-abc123",
|
||||
"manifest": { /* export manifest */ },
|
||||
"attestation": { /* DSSE envelope */ }
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 CLI
|
||||
|
||||
Scanner and other tools generate attestations:
|
||||
|
||||
```bash
|
||||
stella scan image:tag --attest --output sbom.cdx.json
|
||||
# Produces sbom.cdx.json + sbom.cdx.json.dsse
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Attestor architecture: `../attestor/architecture.md`
|
||||
* Signer architecture: `../signer/architecture.md`
|
||||
* EvidenceLocker: `../evidence-locker/architecture.md`
|
||||
* SLSA specification: https://slsa.dev/provenance/v1
|
||||
* DSSE specification: https://github.com/secure-systems-lab/dsse
|
||||
231
docs/modules/reachgraph/architecture.md
Normal file
231
docs/modules/reachgraph/architecture.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# ReachGraph Module Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
The **ReachGraph** module provides a unified store for reachability subgraphs, enabling fast, deterministic, audit-ready answers to "*exactly why* a dependency is reachable." It consolidates data from Scanner, Signals, and Attestor into content-addressed artifacts with edge-level explainability.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Before ReachGraph, reachability data was scattered across multiple modules:
|
||||
|
||||
| Module | Data | Limitation |
|
||||
|--------|------|------------|
|
||||
| Scanner.CallGraph | `CallGraphSnapshot` | No unified query API |
|
||||
| Signals | `ReachabilityFactDocument` | Runtime-focused, not auditable |
|
||||
| Attestor | PoE JSON | Per-CVE only, no slice queries |
|
||||
| Graph | Generic nodes/edges | Not optimized for "why reachable?" |
|
||||
|
||||
**Result**: Answering "why is lodash reachable?" required querying multiple systems with no guarantee of consistency or auditability.
|
||||
|
||||
## Solution
|
||||
|
||||
ReachGraph provides:
|
||||
|
||||
1. **Unified Schema**: Extends PoE subgraph format with edge explainability
|
||||
2. **Content-Addressed Store**: All artifacts identified by BLAKE3 digest
|
||||
3. **Slice Query API**: Fast queries by package, CVE, entrypoint, or file
|
||||
4. **Deterministic Replay**: Verify that same inputs produce same graph
|
||||
5. **DSSE Signing**: Offline-verifiable proofs
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Consumers │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Policy │ │ Web │ │ CLI │ │ Export │ │
|
||||
│ │ Engine │ │ Console │ │ │ │ Center │ │
|
||||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||||
└───────┼─────────────┼─────────────┼─────────────┼───────────────┘
|
||||
│ │ │ │
|
||||
└─────────────┴──────┬──────┴─────────────┘
|
||||
│
|
||||
┌────────────────────────────▼────────────────────────────────────┐
|
||||
│ ReachGraph WebService │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ REST API │ │
|
||||
│ │ POST /v1/reachgraphs GET /v1/reachgraphs/{d} │ │
|
||||
│ │ GET /v1/reachgraphs/{d}/slice POST /v1/reachgraphs/replay│ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ Slice Query Engine │ │
|
||||
│ │ - Package slice (by PURL) │ │
|
||||
│ │ - CVE slice (paths to vulnerable sinks) │ │
|
||||
│ │ - Entrypoint slice (reachable from entry) │ │
|
||||
│ │ - File slice (changed file impact) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ Replay Driver │ │
|
||||
│ │ - Rebuild graph from inputs │ │
|
||||
│ │ - Verify digest matches │ │
|
||||
│ │ - Log for audit trail │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────▼───────────────────────────────────┐
|
||||
│ ReachGraph Core Library │
|
||||
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
|
||||
│ │ Schema │ │ Serialization │ │ Signing │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ ReachGraphMin │ │ Canonical JSON │ │ DSSE Wrapper │ │
|
||||
│ │ EdgeExplanation│ │ BLAKE3 Digest │ │ Attestor Int. │ │
|
||||
│ │ Provenance │ │ Compression │ │ │ │
|
||||
│ └────────────────┘ └────────────────┘ └────────────────┘ │
|
||||
└─────────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────▼───────────────────────────────────┐
|
||||
│ Persistence Layer │
|
||||
│ ┌────────────────────────┐ ┌────────────────────────┐ │
|
||||
│ │ PostgreSQL │ │ Valkey │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ reachgraph.subgraphs │ │ Hot slice cache │ │
|
||||
│ │ reachgraph.slice_cache│ │ (30min TTL) │ │
|
||||
│ │ reachgraph.replay_log │ │ │ │
|
||||
│ └────────────────────────┘ └────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│
|
||||
┌─────────────────────────────┴───────────────────────────────────┐
|
||||
│ Producers │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Scanner │ │ Signals │ │ Attestor │ │
|
||||
│ │ CallGraph │ │ RuntimeFacts │ │ PoE │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Model
|
||||
|
||||
### ReachGraphMinimal (v1)
|
||||
|
||||
The core schema extends the PoE predicate format:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachgraph.min@v1",
|
||||
"artifact": {
|
||||
"name": "svc.payments",
|
||||
"digest": "sha256:abc123...",
|
||||
"env": ["linux/amd64"]
|
||||
},
|
||||
"scope": {
|
||||
"entrypoints": ["/app/bin/svc"],
|
||||
"selectors": ["prod"],
|
||||
"cves": ["CVE-2024-1234"]
|
||||
},
|
||||
"nodes": [...],
|
||||
"edges": [...],
|
||||
"provenance": {...},
|
||||
"signatures": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### Edge Explainability
|
||||
|
||||
Every edge carries metadata explaining *why* it exists:
|
||||
|
||||
| Type | Description | Example Guard |
|
||||
|------|-------------|---------------|
|
||||
| `Import` | Static import | - |
|
||||
| `DynamicLoad` | Runtime load | - |
|
||||
| `Reflection` | Reflective call | - |
|
||||
| `EnvGuard` | Env variable check | `DEBUG=true` |
|
||||
| `FeatureFlag` | Feature flag | `FEATURE_X=enabled` |
|
||||
| `PlatformArch` | Platform guard | `os=linux` |
|
||||
| `LoaderRule` | PLT/IAT/GOT | `RTLD_LAZY` |
|
||||
|
||||
### Content Addressing
|
||||
|
||||
All artifacts are identified by BLAKE3-256 digest:
|
||||
- Computed from canonical JSON (sorted keys, no nulls)
|
||||
- Signatures excluded from hash computation
|
||||
- Enables idempotent upserts and cache keying
|
||||
|
||||
## API Design
|
||||
|
||||
### Core Endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | `/v1/reachgraphs` | Upsert subgraph (idempotent) |
|
||||
| GET | `/v1/reachgraphs/{digest}` | Get full subgraph |
|
||||
| GET | `/v1/reachgraphs/{digest}/slice` | Query slice |
|
||||
| POST | `/v1/reachgraphs/replay` | Verify determinism |
|
||||
|
||||
### Slice Query Types
|
||||
|
||||
1. **Package Slice** (`?q=pkg:npm/lodash@4.17.21`)
|
||||
- Returns subgraph containing package and neighbors
|
||||
- Configurable depth and direction
|
||||
|
||||
2. **CVE Slice** (`?cve=CVE-2024-1234`)
|
||||
- Returns all paths from entrypoints to vulnerable sinks
|
||||
- Includes edge explanations for each hop
|
||||
|
||||
3. **Entrypoint Slice** (`?entrypoint=/app/bin/svc`)
|
||||
- Returns everything reachable from entry
|
||||
- Optionally filtered to paths reaching sinks
|
||||
|
||||
4. **File Slice** (`?file=src/**/*.ts`)
|
||||
- Returns impact of changed files
|
||||
- Useful for PR-based analysis
|
||||
|
||||
## Integration Points
|
||||
|
||||
### Upstream (Data Producers)
|
||||
|
||||
- **Scanner.CallGraph**: Produces nodes and edges with edge explanations
|
||||
- **Signals**: Provides runtime confirmation of reachability
|
||||
- **Attestor**: DSSE signing integration
|
||||
|
||||
### Downstream (Data Consumers)
|
||||
|
||||
- **Policy Engine**: `ReachabilityRequirementGate` queries slices
|
||||
- **Web Console**: "Why Reachable?" panel displays paths
|
||||
- **CLI**: `stella reachgraph slice/replay` commands
|
||||
- **ExportCenter**: Includes subgraphs in evidence bundles
|
||||
|
||||
## Determinism Guarantees
|
||||
|
||||
1. **Canonical Serialization**
|
||||
- Sorted object keys (lexicographic)
|
||||
- Sorted arrays by deterministic field
|
||||
- UTC ISO-8601 timestamps
|
||||
- No null fields (omit when null)
|
||||
|
||||
2. **Replay Verification**
|
||||
- POST `/v1/reachgraphs/replay` rebuilds from inputs
|
||||
- Returns `{match: true}` if digests match
|
||||
- Logs all attempts for audit trail
|
||||
|
||||
3. **Content Addressing**
|
||||
- Same content always produces same digest
|
||||
- Enables cache keying and deduplication
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
| Operation | Target Latency | Notes |
|
||||
|-----------|---------------|-------|
|
||||
| Slice query | P95 < 200ms | Cached in Valkey |
|
||||
| Full graph retrieval | P95 < 500ms | Compressed storage |
|
||||
| Upsert | P95 < 1s | Idempotent, gzip compression |
|
||||
| Replay | P95 < 5s | Depends on input size |
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Tenant Isolation**: RLS policies enforce at database level
|
||||
2. **Rate Limiting**: 100 req/min reads, 20 req/min writes
|
||||
3. **DSSE Signing**: All artifacts verifiable offline
|
||||
4. **Input Validation**: Schema validation on all requests
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Sprint 1227.0012.0001 - Core Library](../../implplan/SPRINT_1227_0012_0001_LB_reachgraph_core.md)
|
||||
- [Sprint 1227.0012.0002 - Store APIs](../../implplan/SPRINT_1227_0012_0002_BE_reachgraph_store.md)
|
||||
- [Sprint 1227.0012.0003 - Integration](../../implplan/SPRINT_1227_0012_0003_FE_reachgraph_integration.md)
|
||||
- [PoE Predicate Spec](../../../src/Attestor/POE_PREDICATE_SPEC.md)
|
||||
- [Module AGENTS.md](../../../src/__Libraries/StellaOps.ReachGraph/AGENTS.md)
|
||||
|
||||
---
|
||||
|
||||
_Last updated: 2025-12-27_
|
||||
265
docs/modules/replay/architecture.md
Normal file
265
docs/modules/replay/architecture.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# component_architecture_replay.md - **Stella Ops Replay** (2025Q4)
|
||||
|
||||
> Deterministic replay engine for vulnerability verdict reproducibility.
|
||||
|
||||
> **Scope.** Implementation-ready architecture for **Replay**: the deterministic replay engine ensuring vulnerability assessments can be reproduced byte-for-byte given the same inputs. Covers replay tokens, manifests, feed snapshots, and verification workflows.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Enable **deterministic reproducibility** of vulnerability verdicts. Given identical inputs (SBOM, policy, feeds, toolchain), the system MUST produce identical outputs. Replay provides the infrastructure to capture, store, and verify these deterministic execution chains.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Replay **does not** make vulnerability decisions. It captures the inputs and outputs of decision-making services.
|
||||
* Replay **does not** store SBOMs or vulnerability data. It stores references (digests) to content-addressed artifacts.
|
||||
* Replay tokens are **cryptographically bound** to input digests.
|
||||
* All timestamps are **UTC ISO-8601** with microsecond precision.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Replay/
|
||||
├─ StellaOps.Replay.WebService/ # Token issuance and verification API
|
||||
│ ├─ Program.cs # ASP.NET Core host
|
||||
│ └─ VerdictReplayEndpoints.cs # Minimal API endpoints
|
||||
└─ __Tests/
|
||||
└─ StellaOps.Replay.Core.Tests/ # Unit tests
|
||||
|
||||
src/__Libraries/
|
||||
├─ StellaOps.Replay.Core/ # Core replay manifest and validation
|
||||
│ ├─ ReplayManifest.cs # Manifest schema (v1, v2)
|
||||
│ ├─ ReplayManifestValidator.cs # Validation logic
|
||||
│ ├─ DeterministicHash.cs # Hash computation utilities
|
||||
│ ├─ PolicySimulationInputLock.cs # Input pinning for simulation
|
||||
│ └─ FeedSnapshot/
|
||||
│ └─ FeedSnapshotCoordinatorService.cs
|
||||
│
|
||||
├─ StellaOps.Audit.ReplayToken/ # Token generation and verification
|
||||
│ ├─ ReplayToken.cs # Token model
|
||||
│ ├─ ReplayTokenRequest.cs # Token request DTO
|
||||
│ ├─ IReplayTokenGenerator.cs # Generator interface
|
||||
│ └─ Sha256ReplayTokenGenerator.cs # SHA-256 based implementation
|
||||
│
|
||||
└─ StellaOps.Replay/ # Shared replay utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **PostgreSQL** - Token storage and manifest persistence
|
||||
* **Authority** - Authentication for token issuance/verification
|
||||
* **Cryptography** - Hash computation (SHA-256, BLAKE3)
|
||||
* **CAS (Content-Addressed Storage)** - Artifact storage for replay bundles
|
||||
* **Policy Engine** - Consumes replay manifests for deterministic simulation
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 ReplayManifest
|
||||
|
||||
The manifest captures all inputs required to reproduce a verdict:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "2.0",
|
||||
"scan": {
|
||||
"id": "scan-2025-01-15T10:30:00Z-abc123",
|
||||
"time": "2025-01-15T10:30:00.000000Z",
|
||||
"policyDigest": "sha256:abc123...",
|
||||
"scorePolicyDigest": "sha256:def456...",
|
||||
"feedSnapshot": "sha256:789abc...",
|
||||
"toolchain": "stellaops/scanner:1.7.3",
|
||||
"analyzerSetDigest": "sha256:feed12..."
|
||||
},
|
||||
"reachability": {
|
||||
"analysisId": "reach-xyz",
|
||||
"graphs": [
|
||||
{
|
||||
"kind": "static",
|
||||
"casUri": "cas://reachability/graphs/abc123",
|
||||
"hash": "blake3:a1b2c3d4...",
|
||||
"hashAlg": "blake3-256",
|
||||
"analyzer": "elf-callgraph",
|
||||
"version": "1.2.0"
|
||||
}
|
||||
],
|
||||
"runtimeTraces": [],
|
||||
"code_id_coverage": {
|
||||
"total_nodes": 1500,
|
||||
"nodes_with_symbol_id": 1200,
|
||||
"nodes_with_code_id": 1100,
|
||||
"coverage_percent": 73.33
|
||||
}
|
||||
},
|
||||
"proofSpines": [
|
||||
{
|
||||
"spineId": "spine-abc123",
|
||||
"artifactId": "pkg:npm/lodash@4.17.21",
|
||||
"vulnerabilityId": "CVE-2021-23337",
|
||||
"verdict": "not_affected",
|
||||
"segmentCount": 4,
|
||||
"rootHash": "sha256:fedcba...",
|
||||
"casUri": "cas://proofs/spines/abc123",
|
||||
"createdAt": "2025-01-15T10:30:05.000000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 ReplayToken
|
||||
|
||||
Cryptographic token binding inputs to outputs:
|
||||
|
||||
```csharp
|
||||
public sealed record ReplayToken
|
||||
{
|
||||
public required string TokenId { get; init; } // Unique token ID
|
||||
public required string InputDigest { get; init; } // Hash of all inputs
|
||||
public required string OutputDigest { get; init; } // Hash of verdict output
|
||||
public required DateTimeOffset IssuedAt { get; init; } // UTC timestamp
|
||||
public required string Issuer { get; init; } // Service that issued
|
||||
public string? Signature { get; init; } // DSSE signature
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 PolicySimulationInputLock
|
||||
|
||||
Captures pinned versions for deterministic policy simulation:
|
||||
|
||||
```csharp
|
||||
public sealed record PolicySimulationInputLock
|
||||
{
|
||||
public required string PolicyDigest { get; init; }
|
||||
public required string FeedSnapshotDigest { get; init; }
|
||||
public required string ScorePolicyDigest { get; init; }
|
||||
public required DateTimeOffset LockedAt { get; init; }
|
||||
public required IReadOnlyList<AnalyzerPin> AnalyzerPins { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) REST API (Replay.WebService)
|
||||
|
||||
All under `/api/v1/replay`. Auth: **OpTok** (DPoP/mTLS).
|
||||
|
||||
```
|
||||
POST /tokens { request: ReplayTokenRequest } → { token: ReplayToken }
|
||||
GET /tokens/{tokenId} → { token: ReplayToken, status }
|
||||
POST /tokens/{tokenId}/verify { manifest: ReplayManifest } → { valid: bool, details }
|
||||
|
||||
GET /manifests/{scanId} → { manifest: ReplayManifest }
|
||||
POST /manifests { manifest: ReplayManifest } → { manifestId }
|
||||
|
||||
GET /healthz | /readyz
|
||||
```
|
||||
|
||||
**Authorization Policies:**
|
||||
- `replay.token.read` - Read tokens and manifests
|
||||
- `replay.token.write` - Issue new tokens
|
||||
|
||||
---
|
||||
|
||||
## 5) Configuration (YAML)
|
||||
|
||||
```yaml
|
||||
Replay:
|
||||
Authority:
|
||||
Issuer: "https://authority.stellaops.local"
|
||||
RequireHttpsMetadata: true
|
||||
MetadataAddress: "https://authority.stellaops.local/.well-known/openid-configuration"
|
||||
Audiences: ["replay-service"]
|
||||
RequiredScopes: ["vuln:operate"]
|
||||
|
||||
Storage:
|
||||
ConnectionString: "Host=postgres;Database=replay;..."
|
||||
CasEndpoint: "http://rustfs:8080"
|
||||
|
||||
Tokens:
|
||||
Algorithm: "SHA256"
|
||||
ExpirationHours: 8760 # 1 year
|
||||
|
||||
Determinism:
|
||||
EnforceCanonicalJson: true
|
||||
HashAlgorithm: "blake3-256"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Determinism guarantees
|
||||
|
||||
### 6.1 Input pinning
|
||||
|
||||
All inputs that affect verdict output are captured:
|
||||
|
||||
| Input | Pinning Method | Storage |
|
||||
|-------|---------------|---------|
|
||||
| Policy YAML | Content digest | CAS |
|
||||
| Score policy | Content digest | CAS |
|
||||
| Feed snapshot | Snapshot digest + timestamp | CAS |
|
||||
| Toolchain | Image digest | Manifest |
|
||||
| Analyzers | Version + digest | Manifest |
|
||||
| Reachability graphs | BLAKE3 hash | CAS |
|
||||
|
||||
### 6.2 Output determinism
|
||||
|
||||
| Guarantee | Implementation |
|
||||
|-----------|----------------|
|
||||
| Canonical JSON | Sorted keys, no whitespace variation |
|
||||
| Stable ordering | Deterministic sort on all collections |
|
||||
| UTC timestamps | Microsecond precision, always UTC |
|
||||
| Hash stability | Same input → same hash |
|
||||
|
||||
---
|
||||
|
||||
## 7) Security & compliance
|
||||
|
||||
* **Token binding**: Tokens are cryptographically bound to input digests
|
||||
* **Non-repudiation**: DSSE signatures on tokens (optional)
|
||||
* **Audit trail**: All token operations logged with tenant context
|
||||
* **Immutability**: Manifests and tokens are append-only
|
||||
|
||||
---
|
||||
|
||||
## 8) Performance targets
|
||||
|
||||
* **Token issuance**: < 50ms P95
|
||||
* **Token verification**: < 100ms P95
|
||||
* **Manifest storage**: < 200ms P95
|
||||
* **Manifest retrieval**: < 50ms P95
|
||||
|
||||
---
|
||||
|
||||
## 9) Observability
|
||||
|
||||
**Metrics:**
|
||||
* `replay.tokens.issued_total{issuer}`
|
||||
* `replay.tokens.verified_total{result=valid|invalid}`
|
||||
* `replay.manifests.stored_total`
|
||||
* `replay.verification.duration_seconds`
|
||||
|
||||
**Tracing:** Spans for token operations, manifest storage, verification workflows.
|
||||
|
||||
---
|
||||
|
||||
## 10) Testing matrix
|
||||
|
||||
* **Determinism tests**: Same inputs produce identical tokens/manifests
|
||||
* **Round-trip tests**: Store → retrieve → verify produces same result
|
||||
* **Hash stability**: Canonical JSON hashing is stable across serialization
|
||||
* **Integration tests**: Full token lifecycle with Policy Engine
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Scanner determinism: `../scanner/deterministic-execution.md`
|
||||
* Policy simulation: `../policy/architecture.md`
|
||||
* Evidence attestation: `../attestor/architecture.md`
|
||||
* Replay protocol: `../../replay/DETERMINISTIC_REPLAY.md`
|
||||
325
docs/modules/riskengine/architecture.md
Normal file
325
docs/modules/riskengine/architecture.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# component_architecture_riskengine.md - **Stella Ops RiskEngine** (2025Q4)
|
||||
|
||||
> Risk scoring runtime with pluggable providers and explainability.
|
||||
|
||||
> **Scope.** Implementation-ready architecture for **RiskEngine**: the scoring runtime that computes Risk Scoring Profiles across deployments while preserving provenance and explainability. Covers scoring workers, providers, caching, and integration with Policy Engine.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Compute **deterministic, explainable risk scores** for vulnerabilities by aggregating signals from multiple data sources (EPSS, CVSS, KEV, VEX, reachability). Produce audit trails and explainability payloads for every scoring decision.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* RiskEngine **does not** make PASS/FAIL decisions. It provides scores to the Policy Engine.
|
||||
* RiskEngine **does not** own vulnerability data. It consumes from Concelier, Excititor, and Signals.
|
||||
* Scoring is **deterministic**: same inputs produce identical scores.
|
||||
* Supports **offline/air-gapped** operation via factor bundles.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/RiskEngine/StellaOps.RiskEngine/
|
||||
├─ StellaOps.RiskEngine.Core/ # Scoring orchestrators, provider contracts
|
||||
│ ├─ Providers/
|
||||
│ │ ├─ IRiskScoreProvider.cs # Provider interface
|
||||
│ │ ├─ EpssProvider.cs # EPSS score provider
|
||||
│ │ ├─ CvssKevProvider.cs # CVSS + KEV provider
|
||||
│ │ ├─ VexGateProvider.cs # VEX status provider
|
||||
│ │ ├─ FixExposureProvider.cs # Fix availability provider
|
||||
│ │ └─ DefaultTransformsProvider.cs # Score transformations
|
||||
│ ├─ Contracts/
|
||||
│ │ ├─ ScoreRequest.cs # Scoring request DTO
|
||||
│ │ └─ RiskScoreResult.cs # Scoring result with explanation
|
||||
│ └─ Services/
|
||||
│ ├─ RiskScoreWorker.cs # Scoring job executor
|
||||
│ └─ RiskScoreQueue.cs # Job queue management
|
||||
│
|
||||
├─ StellaOps.RiskEngine.Infrastructure/ # Persistence, caching, connectors
|
||||
│ └─ Stores/
|
||||
│ └─ InMemoryRiskScoreResultStore.cs
|
||||
│
|
||||
├─ StellaOps.RiskEngine.WebService/ # REST API for jobs and results
|
||||
│ └─ Program.cs
|
||||
│
|
||||
├─ StellaOps.RiskEngine.Worker/ # Background scoring workers
|
||||
│ ├─ Program.cs
|
||||
│ └─ Worker.cs
|
||||
│
|
||||
└─ StellaOps.RiskEngine.Tests/ # Unit and integration tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **PostgreSQL** - Score persistence and job state
|
||||
* **Concelier** - Vulnerability advisory data, EPSS scores
|
||||
* **Excititor** - VEX statements
|
||||
* **Signals** - Reachability and runtime signals
|
||||
* **Policy Engine** - Consumes risk scores for decision-making
|
||||
* **Authority** - Authentication and authorization
|
||||
* **Valkey/Redis** - Score caching (optional)
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 ScoreRequest
|
||||
|
||||
```csharp
|
||||
public sealed record ScoreRequest
|
||||
{
|
||||
public required string VulnerabilityId { get; init; } // CVE or vuln ID
|
||||
public required string ArtifactId { get; init; } // PURL or component ID
|
||||
public string? TenantId { get; init; }
|
||||
public string? ContextId { get; init; } // Scan or assessment ID
|
||||
public IReadOnlyList<string>? EnabledProviders { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 RiskScoreResult
|
||||
|
||||
```csharp
|
||||
public sealed record RiskScoreResult
|
||||
{
|
||||
public required string RequestId { get; init; }
|
||||
public required decimal FinalScore { get; init; } // 0.0-10.0
|
||||
public required string Tier { get; init; } // Critical/High/Medium/Low/Info
|
||||
public required DateTimeOffset ComputedAt { get; init; }
|
||||
public required IReadOnlyList<ProviderContribution> Contributions { get; init; }
|
||||
public required ExplainabilityPayload Explanation { get; init; }
|
||||
}
|
||||
|
||||
public sealed record ProviderContribution
|
||||
{
|
||||
public required string ProviderId { get; init; }
|
||||
public required decimal RawScore { get; init; }
|
||||
public required decimal Weight { get; init; }
|
||||
public required decimal WeightedScore { get; init; }
|
||||
public string? FactorSource { get; init; } // Where data came from
|
||||
public DateTimeOffset? FactorTimestamp { get; init; } // When factor was computed
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Provider Interface
|
||||
|
||||
```csharp
|
||||
public interface IRiskScoreProvider
|
||||
{
|
||||
string ProviderId { get; }
|
||||
decimal DefaultWeight { get; }
|
||||
TimeSpan CacheTtl { get; }
|
||||
|
||||
Task<ProviderResult> ComputeAsync(
|
||||
ScoreRequest request,
|
||||
CancellationToken ct);
|
||||
|
||||
Task<bool> IsHealthyAsync(CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) Score Providers
|
||||
|
||||
### 4.1 Built-in Providers
|
||||
|
||||
| Provider | Data Source | Weight | Description |
|
||||
|----------|-------------|--------|-------------|
|
||||
| `epss` | Concelier/EPSS | 0.25 | EPSS probability score (0-1 → 0-10) |
|
||||
| `cvss-kev` | Concelier | 0.30 | CVSS base + KEV boost |
|
||||
| `vex-gate` | Excititor | 0.20 | VEX status (affected/not_affected) |
|
||||
| `fix-exposure` | Concelier | 0.15 | Fix availability window |
|
||||
| `reachability` | Signals | 0.10 | Code path reachability |
|
||||
|
||||
### 4.2 Score Computation
|
||||
|
||||
```
|
||||
FinalScore = Σ(provider.weight × provider.score) / Σ(provider.weight)
|
||||
|
||||
Tier mapping:
|
||||
9.0-10.0 → Critical
|
||||
7.0-8.9 → High
|
||||
4.0-6.9 → Medium
|
||||
1.0-3.9 → Low
|
||||
0.0-0.9 → Info
|
||||
```
|
||||
|
||||
### 4.3 Provider Data Sources
|
||||
|
||||
```csharp
|
||||
public interface IEpssSources
|
||||
{
|
||||
Task<EpssScore?> GetScoreAsync(string cveId, CancellationToken ct);
|
||||
}
|
||||
|
||||
public interface ICvssKevSources
|
||||
{
|
||||
Task<CvssData?> GetCvssAsync(string cveId, CancellationToken ct);
|
||||
Task<bool> IsKevAsync(string cveId, CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) REST API (RiskEngine.WebService)
|
||||
|
||||
All under `/api/v1/risk`. Auth: **OpTok**.
|
||||
|
||||
```
|
||||
POST /scores { request: ScoreRequest } → { jobId }
|
||||
GET /scores/{jobId} → { result: RiskScoreResult, status }
|
||||
GET /scores/{jobId}/explain → { explanation: ExplainabilityPayload }
|
||||
|
||||
POST /batch { requests: ScoreRequest[] } → { batchId }
|
||||
GET /batch/{batchId} → { results: RiskScoreResult[], status }
|
||||
|
||||
GET /providers → { providers: ProviderInfo[] }
|
||||
GET /providers/{id}/health → { healthy: bool, lastCheck }
|
||||
|
||||
GET /healthz | /readyz | /metrics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Configuration (YAML)
|
||||
|
||||
```yaml
|
||||
RiskEngine:
|
||||
Postgres:
|
||||
ConnectionString: "Host=postgres;Database=risk;..."
|
||||
|
||||
Cache:
|
||||
Enabled: true
|
||||
Provider: "valkey"
|
||||
ConnectionString: "redis://valkey:6379"
|
||||
DefaultTtl: "00:15:00"
|
||||
|
||||
Providers:
|
||||
Epss:
|
||||
Enabled: true
|
||||
Weight: 0.25
|
||||
CacheTtl: "01:00:00"
|
||||
Source: "concelier"
|
||||
|
||||
CvssKev:
|
||||
Enabled: true
|
||||
Weight: 0.30
|
||||
KevBoost: 2.0
|
||||
|
||||
VexGate:
|
||||
Enabled: true
|
||||
Weight: 0.20
|
||||
NotAffectedScore: 0.0
|
||||
AffectedScore: 10.0
|
||||
|
||||
FixExposure:
|
||||
Enabled: true
|
||||
Weight: 0.15
|
||||
NoFixPenalty: 1.5
|
||||
|
||||
Reachability:
|
||||
Enabled: true
|
||||
Weight: 0.10
|
||||
UnreachableDiscount: 0.5
|
||||
|
||||
Worker:
|
||||
Concurrency: 4
|
||||
BatchSize: 100
|
||||
PollInterval: "00:00:05"
|
||||
|
||||
Offline:
|
||||
FactorBundlePath: "/data/risk-factors"
|
||||
AllowStaleData: true
|
||||
MaxStalenessHours: 168
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7) Security & compliance
|
||||
|
||||
* **AuthN/Z**: Authority-issued OpToks with `risk.score` scope
|
||||
* **Tenant isolation**: Scores scoped by tenant ID
|
||||
* **Audit trail**: All scoring decisions logged with inputs and factors
|
||||
* **No PII**: Only vulnerability and artifact identifiers processed
|
||||
|
||||
---
|
||||
|
||||
## 8) Performance targets
|
||||
|
||||
* **Single score**: < 100ms P95 (cached factors)
|
||||
* **Batch scoring**: < 500ms P95 for 100 items
|
||||
* **Provider health check**: < 1s timeout
|
||||
* **Cache hit rate**: > 80% for repeated CVEs
|
||||
|
||||
---
|
||||
|
||||
## 9) Observability
|
||||
|
||||
**Metrics:**
|
||||
* `risk.scores.computed_total{tier,provider}`
|
||||
* `risk.scores.duration_seconds`
|
||||
* `risk.providers.health{provider,status}`
|
||||
* `risk.cache.hits_total` / `risk.cache.misses_total`
|
||||
* `risk.batch.size_histogram`
|
||||
|
||||
**Tracing:** Spans for each provider contribution, cache operations, and aggregation.
|
||||
|
||||
**Logs:** Structured logs with `cve_id`, `artifact_id`, `tenant`, `final_score`.
|
||||
|
||||
---
|
||||
|
||||
## 10) Testing matrix
|
||||
|
||||
* **Provider tests**: Each provider returns expected scores for fixture data
|
||||
* **Aggregation tests**: Weighted combination produces correct final score
|
||||
* **Determinism tests**: Same inputs produce identical scores
|
||||
* **Cache tests**: Cache hit/miss behavior correct
|
||||
* **Offline tests**: Factor bundles load and score correctly
|
||||
* **Integration tests**: Full scoring pipeline with mocked data sources
|
||||
|
||||
---
|
||||
|
||||
## 11) Offline/Air-Gap Support
|
||||
|
||||
### Factor Bundles
|
||||
|
||||
Pre-computed factor data for offline operation:
|
||||
|
||||
```
|
||||
/data/risk-factors/
|
||||
├─ epss/
|
||||
│ └─ epss-2025-01-15.json.gz
|
||||
├─ cvss/
|
||||
│ └─ cvss-2025-01-15.json.gz
|
||||
├─ kev/
|
||||
│ └─ kev-2025-01-15.json
|
||||
└─ manifest.json
|
||||
```
|
||||
|
||||
### Staleness Handling
|
||||
|
||||
When operating offline, scores include staleness indicators:
|
||||
|
||||
```json
|
||||
{
|
||||
"finalScore": 7.2,
|
||||
"dataFreshness": {
|
||||
"epss": { "age": "48h", "stale": false },
|
||||
"kev": { "age": "24h", "stale": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Policy scoring: `../policy/architecture.md`
|
||||
* Concelier feeds: `../concelier/architecture.md`
|
||||
* Excititor VEX: `../excititor/architecture.md`
|
||||
* Signals reachability: `../signals/architecture.md`
|
||||
469
docs/modules/sbomservice/lineage/architecture.md
Normal file
469
docs/modules/sbomservice/lineage/architecture.md
Normal file
@@ -0,0 +1,469 @@
|
||||
# SBOM Lineage Graph Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
The SBOM Lineage Graph provides a Git-like visualization of container image ancestry with hover-to-proof micro-interactions. It enables auditors and developers to explore SBOM/VEX deltas across artifact versions, turning evidence into an explorable UX.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Lineage Graph
|
||||
|
||||
A directed acyclic graph (DAG) where:
|
||||
- **Nodes** represent artifact versions (SBOM snapshots)
|
||||
- **Edges** represent relationships between versions
|
||||
|
||||
### Edge Types
|
||||
|
||||
| Type | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| `parent` | Direct version succession | v1.0 → v1.1 of same image |
|
||||
| `build` | Same CI build produced multiple artifacts | Multi-arch build |
|
||||
| `base` | Derived from base image | `FROM alpine:3.19` |
|
||||
|
||||
### Node Attributes
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Node: sha256:abc123... │
|
||||
├─────────────────────────────────────┤
|
||||
│ Artifact: registry/app:v1.2 │
|
||||
│ Sequence: 42 │
|
||||
│ Created: 2025-12-28T10:30:00Z │
|
||||
│ Source: scanner │
|
||||
├─────────────────────────────────────┤
|
||||
│ Badges: │
|
||||
│ • 3 new vulns (🔴) │
|
||||
│ • 2 resolved (🟢) │
|
||||
│ • signature ✓ │
|
||||
├─────────────────────────────────────┤
|
||||
│ Replay Hash: sha256:def456... │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌─────────────────┐ ┌──────────────────┐
|
||||
│ Scanner │────▶│ SbomService │────▶│ VexLens │
|
||||
│ │ │ │ │ │
|
||||
│ • OCI Parse │ │ • Ledger Store │ │ • Consensus │
|
||||
│ • Ancestry │ │ • Edge Persist │ │ • Delta Compute │
|
||||
│ • SBOM Gen │ │ • Diff Engine │ │ • Status Track │
|
||||
└──────────────┘ └─────────────────┘ └──────────────────┘
|
||||
│ │ │
|
||||
└────────────────────┼───────────────────────┘
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Lineage API │
|
||||
│ │
|
||||
│ • Graph Query │
|
||||
│ • Diff Compute │
|
||||
│ • Export Pack │
|
||||
└─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Frontend UI │
|
||||
│ │
|
||||
│ • Lane View │
|
||||
│ • Hover Cards │
|
||||
│ • Compare Mode │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Component Architecture
|
||||
|
||||
### 1. OCI Ancestry Extractor (Scanner)
|
||||
|
||||
Extracts parent/base image information from OCI manifests.
|
||||
|
||||
```csharp
|
||||
public interface IOciAncestryExtractor
|
||||
{
|
||||
ValueTask<OciAncestry> ExtractAncestryAsync(
|
||||
string imageReference,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record OciAncestry(
|
||||
string ImageDigest,
|
||||
string? BaseImageDigest,
|
||||
string? BaseImageRef,
|
||||
IReadOnlyList<string> LayerDigests,
|
||||
IReadOnlyList<OciHistoryEntry> History);
|
||||
|
||||
public sealed record OciHistoryEntry(
|
||||
string CreatedBy,
|
||||
DateTimeOffset Created,
|
||||
bool EmptyLayer);
|
||||
```
|
||||
|
||||
**Implementation Notes:**
|
||||
- Parse OCI image config `history` field
|
||||
- Extract `FROM` instruction from first non-empty layer
|
||||
- Handle multi-stage builds by tracking layer boundaries
|
||||
- Fall back to layer digest heuristics when history unavailable
|
||||
|
||||
### 2. Lineage Edge Repository (SbomService)
|
||||
|
||||
Persists relationships between artifact versions.
|
||||
|
||||
```csharp
|
||||
public interface ISbomLineageEdgeRepository
|
||||
{
|
||||
ValueTask<LineageEdge> AddAsync(
|
||||
LineageEdge edge,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<IReadOnlyList<LineageEdge>> GetChildrenAsync(
|
||||
string parentDigest,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<IReadOnlyList<LineageEdge>> GetParentsAsync(
|
||||
string childDigest,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<LineageGraph> GetGraphAsync(
|
||||
string artifactDigest,
|
||||
Guid tenantId,
|
||||
int maxDepth,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record LineageEdge(
|
||||
Guid Id,
|
||||
string ParentDigest,
|
||||
string ChildDigest,
|
||||
LineageRelationship Relationship,
|
||||
Guid TenantId,
|
||||
DateTimeOffset CreatedAt);
|
||||
|
||||
public enum LineageRelationship
|
||||
{
|
||||
Parent,
|
||||
Build,
|
||||
Base
|
||||
}
|
||||
```
|
||||
|
||||
### 3. VEX Delta Repository (Excititor)
|
||||
|
||||
Tracks VEX status changes between artifact versions.
|
||||
|
||||
```csharp
|
||||
public interface IVexDeltaRepository
|
||||
{
|
||||
ValueTask<VexDelta> AddAsync(
|
||||
VexDelta delta,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<IReadOnlyList<VexDelta>> GetDeltasAsync(
|
||||
string fromDigest,
|
||||
string toDigest,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<IReadOnlyList<VexDelta>> GetDeltasByCveAsync(
|
||||
string cve,
|
||||
Guid tenantId,
|
||||
int limit,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record VexDelta(
|
||||
Guid Id,
|
||||
string FromArtifactDigest,
|
||||
string ToArtifactDigest,
|
||||
string Cve,
|
||||
VexStatus FromStatus,
|
||||
VexStatus ToStatus,
|
||||
VexDeltaRationale Rationale,
|
||||
string ReplayHash,
|
||||
string? AttestationDigest,
|
||||
Guid TenantId,
|
||||
DateTimeOffset CreatedAt);
|
||||
|
||||
public sealed record VexDeltaRationale(
|
||||
string Reason,
|
||||
string? EvidenceLink,
|
||||
IReadOnlyDictionary<string, string> Metadata);
|
||||
```
|
||||
|
||||
### 4. SBOM-Verdict Link Repository (SbomService)
|
||||
|
||||
Links SBOM versions to VEX consensus decisions.
|
||||
|
||||
```csharp
|
||||
public interface ISbomVerdictLinkRepository
|
||||
{
|
||||
ValueTask LinkAsync(
|
||||
SbomVerdictLink link,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<IReadOnlyList<SbomVerdictLink>> GetVerdictsBySbomAsync(
|
||||
Guid sbomVersionId,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<IReadOnlyList<SbomVerdictLink>> GetSbomsByCveAsync(
|
||||
string cve,
|
||||
Guid tenantId,
|
||||
int limit,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record SbomVerdictLink(
|
||||
Guid SbomVersionId,
|
||||
string Cve,
|
||||
Guid ConsensusProjectionId,
|
||||
VexStatus VerdictStatus,
|
||||
decimal ConfidenceScore,
|
||||
Guid TenantId,
|
||||
DateTimeOffset LinkedAt);
|
||||
```
|
||||
|
||||
### 5. Lineage Graph Service (SbomService)
|
||||
|
||||
Orchestrates lineage queries and diff computation.
|
||||
|
||||
```csharp
|
||||
public interface ILineageGraphService
|
||||
{
|
||||
ValueTask<LineageGraphResponse> GetLineageAsync(
|
||||
string artifactDigest,
|
||||
Guid tenantId,
|
||||
LineageQueryOptions options,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<LineageDiffResponse> GetDiffAsync(
|
||||
string fromDigest,
|
||||
string toDigest,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<LineageCompareResponse> CompareAsync(
|
||||
string digestA,
|
||||
string digestB,
|
||||
Guid tenantId,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record LineageQueryOptions(
|
||||
int MaxDepth = 10,
|
||||
bool IncludeVerdicts = true,
|
||||
bool IncludeBadges = true);
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### sbom_lineage_edges
|
||||
|
||||
```sql
|
||||
CREATE TABLE sbom_lineage_edges (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
parent_digest TEXT NOT NULL,
|
||||
child_digest TEXT NOT NULL,
|
||||
relationship TEXT NOT NULL CHECK (relationship IN ('parent', 'build', 'base')),
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (parent_digest, child_digest, tenant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_lineage_edges_parent ON sbom_lineage_edges(parent_digest, tenant_id);
|
||||
CREATE INDEX idx_lineage_edges_child ON sbom_lineage_edges(child_digest, tenant_id);
|
||||
CREATE INDEX idx_lineage_edges_created ON sbom_lineage_edges(tenant_id, created_at DESC);
|
||||
```
|
||||
|
||||
### vex_deltas
|
||||
|
||||
```sql
|
||||
CREATE TABLE vex_deltas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
from_artifact_digest TEXT NOT NULL,
|
||||
to_artifact_digest TEXT NOT NULL,
|
||||
cve TEXT NOT NULL,
|
||||
from_status TEXT NOT NULL,
|
||||
to_status TEXT NOT NULL,
|
||||
rationale JSONB NOT NULL DEFAULT '{}',
|
||||
replay_hash TEXT NOT NULL,
|
||||
attestation_digest TEXT,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (from_artifact_digest, to_artifact_digest, cve, tenant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_vex_deltas_to ON vex_deltas(to_artifact_digest, tenant_id);
|
||||
CREATE INDEX idx_vex_deltas_cve ON vex_deltas(cve, tenant_id);
|
||||
CREATE INDEX idx_vex_deltas_created ON vex_deltas(tenant_id, created_at DESC);
|
||||
```
|
||||
|
||||
### sbom_verdict_links
|
||||
|
||||
```sql
|
||||
CREATE TABLE sbom_verdict_links (
|
||||
sbom_version_id UUID NOT NULL,
|
||||
cve TEXT NOT NULL,
|
||||
consensus_projection_id UUID NOT NULL,
|
||||
verdict_status TEXT NOT NULL,
|
||||
confidence_score DECIMAL(5,4) NOT NULL,
|
||||
tenant_id UUID NOT NULL,
|
||||
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (sbom_version_id, cve, tenant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_verdict_links_cve ON sbom_verdict_links(cve, tenant_id);
|
||||
CREATE INDEX idx_verdict_links_projection ON sbom_verdict_links(consensus_projection_id);
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### GET /api/v1/lineage/{artifactDigest}
|
||||
|
||||
Returns the lineage graph for an artifact.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"artifact": "sha256:abc123...",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"digest": "sha256:abc123...",
|
||||
"artifactRef": "registry/app:v1.2",
|
||||
"sequenceNumber": 42,
|
||||
"createdAt": "2025-12-28T10:30:00Z",
|
||||
"source": "scanner",
|
||||
"badges": {
|
||||
"newVulns": 3,
|
||||
"resolvedVulns": 2,
|
||||
"signatureStatus": "valid"
|
||||
},
|
||||
"replayHash": "sha256:def456..."
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"from": "sha256:parent...",
|
||||
"to": "sha256:abc123...",
|
||||
"relationship": "parent"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/v1/lineage/diff
|
||||
|
||||
Returns component and VEX diffs between two versions.
|
||||
|
||||
**Query Parameters:**
|
||||
- `from` - Source artifact digest
|
||||
- `to` - Target artifact digest
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"sbomDiff": {
|
||||
"added": [
|
||||
{"purl": "pkg:npm/lodash@4.17.21", "version": "4.17.21", "license": "MIT"}
|
||||
],
|
||||
"removed": [
|
||||
{"purl": "pkg:npm/lodash@4.17.20", "version": "4.17.20", "license": "MIT"}
|
||||
],
|
||||
"versionChanged": [
|
||||
{"purl": "pkg:npm/axios@1.6.0", "fromVersion": "1.5.0", "toVersion": "1.6.0"}
|
||||
]
|
||||
},
|
||||
"vexDiff": [
|
||||
{
|
||||
"cve": "CVE-2024-1234",
|
||||
"fromStatus": "affected",
|
||||
"toStatus": "not_affected",
|
||||
"reason": "Component removed",
|
||||
"evidenceLink": "/evidence/abc123"
|
||||
}
|
||||
],
|
||||
"reachabilityDiff": [
|
||||
{
|
||||
"cve": "CVE-2024-5678",
|
||||
"fromStatus": "reachable",
|
||||
"toStatus": "unreachable",
|
||||
"pathsRemoved": 3,
|
||||
"gatesAdded": ["auth_required"]
|
||||
}
|
||||
],
|
||||
"replayHash": "sha256:ghi789..."
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/v1/lineage/export
|
||||
|
||||
Exports evidence pack for artifact(s).
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"artifactDigests": ["sha256:abc123..."],
|
||||
"includeAttestations": true,
|
||||
"sign": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"downloadUrl": "/exports/pack-xyz.zip",
|
||||
"bundleDigest": "sha256:bundle...",
|
||||
"expiresAt": "2025-12-28T11:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Caching Strategy
|
||||
|
||||
### Hover Card Cache (Valkey)
|
||||
|
||||
- **Key:** `lineage:hover:{tenantId}:{artifactDigest}`
|
||||
- **TTL:** 5 minutes
|
||||
- **Invalidation:** On new SBOM version or VEX update
|
||||
- **Target:** <150ms response time
|
||||
|
||||
### Compare Cache (Valkey)
|
||||
|
||||
- **Key:** `lineage:compare:{tenantId}:{digestA}:{digestB}`
|
||||
- **TTL:** 10 minutes
|
||||
- **Invalidation:** On new VEX data for either artifact
|
||||
|
||||
## Determinism Guarantees
|
||||
|
||||
1. **Node Ordering:** Sorted by `sequenceNumber DESC`, then `createdAt DESC`
|
||||
2. **Edge Ordering:** Sorted by `(from, to, relationship)` lexicographically
|
||||
3. **Component Diff:** Components sorted by `purl` (ordinal)
|
||||
4. **VEX Diff:** Sorted by `cve` (ordinal)
|
||||
5. **Replay Hash:** SHA256 of deterministically serialized inputs
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Tenant Isolation:** All queries scoped by `tenant_id`
|
||||
2. **Digest Validation:** Verify artifact digest format before queries
|
||||
3. **Rate Limiting:** Apply per-tenant rate limits on graph queries
|
||||
4. **Export Authorization:** Verify `lineage:export` scope for pack generation
|
||||
|
||||
## Metrics
|
||||
|
||||
| Metric | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `sbom_lineage_graph_queries_total` | Counter | Graph queries by tenant |
|
||||
| `sbom_lineage_diff_latency_seconds` | Histogram | Diff computation latency |
|
||||
| `sbom_lineage_hover_cache_hits_total` | Counter | Hover card cache hits |
|
||||
| `sbom_lineage_export_size_bytes` | Histogram | Evidence pack sizes |
|
||||
| `vex_deltas_created_total` | Counter | VEX deltas stored |
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Code | Description | HTTP Status |
|
||||
|------------|-------------|-------------|
|
||||
| `LINEAGE_NOT_FOUND` | Artifact not in lineage graph | 404 |
|
||||
| `LINEAGE_DEPTH_EXCEEDED` | Max depth limit reached | 400 |
|
||||
| `LINEAGE_DIFF_INVALID` | Same digest for from/to | 400 |
|
||||
| `LINEAGE_EXPORT_TOO_LARGE` | Pack exceeds size limit | 413 |
|
||||
319
docs/modules/sbomservice/lineage/schema.sql
Normal file
319
docs/modules/sbomservice/lineage/schema.sql
Normal file
@@ -0,0 +1,319 @@
|
||||
-- SBOM Lineage Graph Database Schema
|
||||
-- Version: 1.0.0
|
||||
-- Created: 2025-12-28
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: sbom_lineage_edges
|
||||
-- Purpose: Stores relationships between SBOM versions (parent/child, build, base)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sbom_lineage_edges (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Edge endpoints (using artifact digest as stable identifier)
|
||||
parent_digest TEXT NOT NULL,
|
||||
child_digest TEXT NOT NULL,
|
||||
|
||||
-- Relationship type
|
||||
relationship TEXT NOT NULL CHECK (relationship IN ('parent', 'build', 'base')),
|
||||
|
||||
-- Tenant isolation
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Prevent duplicate edges
|
||||
CONSTRAINT uq_lineage_edge UNIQUE (parent_digest, child_digest, tenant_id)
|
||||
);
|
||||
|
||||
-- Index for traversing from parent to children
|
||||
CREATE INDEX IF NOT EXISTS idx_lineage_edges_parent
|
||||
ON sbom_lineage_edges(parent_digest, tenant_id);
|
||||
|
||||
-- Index for traversing from child to parents
|
||||
CREATE INDEX IF NOT EXISTS idx_lineage_edges_child
|
||||
ON sbom_lineage_edges(child_digest, tenant_id);
|
||||
|
||||
-- Index for time-based queries
|
||||
CREATE INDEX IF NOT EXISTS idx_lineage_edges_created
|
||||
ON sbom_lineage_edges(tenant_id, created_at DESC);
|
||||
|
||||
-- Index for relationship filtering
|
||||
CREATE INDEX IF NOT EXISTS idx_lineage_edges_relationship
|
||||
ON sbom_lineage_edges(tenant_id, relationship);
|
||||
|
||||
COMMENT ON TABLE sbom_lineage_edges IS 'Stores directed edges between SBOM versions representing lineage relationships';
|
||||
COMMENT ON COLUMN sbom_lineage_edges.parent_digest IS 'SHA256 digest of parent artifact';
|
||||
COMMENT ON COLUMN sbom_lineage_edges.child_digest IS 'SHA256 digest of child artifact';
|
||||
COMMENT ON COLUMN sbom_lineage_edges.relationship IS 'Type of relationship: parent (version succession), build (same CI build), base (FROM instruction)';
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: vex_deltas
|
||||
-- Purpose: Tracks VEX status changes between artifact versions
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vex_deltas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Artifact pair
|
||||
from_artifact_digest TEXT NOT NULL,
|
||||
to_artifact_digest TEXT NOT NULL,
|
||||
|
||||
-- Vulnerability
|
||||
cve TEXT NOT NULL,
|
||||
|
||||
-- Status transition
|
||||
from_status TEXT NOT NULL,
|
||||
to_status TEXT NOT NULL,
|
||||
|
||||
-- Explanation
|
||||
rationale JSONB NOT NULL DEFAULT '{}',
|
||||
|
||||
-- Determinism
|
||||
replay_hash TEXT NOT NULL,
|
||||
|
||||
-- Signed attestation reference (if signed)
|
||||
attestation_digest TEXT,
|
||||
|
||||
-- Tenant isolation
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Prevent duplicate deltas
|
||||
CONSTRAINT uq_vex_delta UNIQUE (from_artifact_digest, to_artifact_digest, cve, tenant_id)
|
||||
);
|
||||
|
||||
-- Index for querying deltas by target artifact
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_deltas_to
|
||||
ON vex_deltas(to_artifact_digest, tenant_id);
|
||||
|
||||
-- Index for querying deltas by CVE
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_deltas_cve
|
||||
ON vex_deltas(cve, tenant_id);
|
||||
|
||||
-- Index for time-based queries
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_deltas_created
|
||||
ON vex_deltas(tenant_id, created_at DESC);
|
||||
|
||||
-- Index for finding status transitions
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_deltas_status
|
||||
ON vex_deltas(tenant_id, from_status, to_status);
|
||||
|
||||
-- GIN index for rationale JSON queries
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_deltas_rationale
|
||||
ON vex_deltas USING GIN (rationale);
|
||||
|
||||
COMMENT ON TABLE vex_deltas IS 'Tracks VEX status changes between artifact versions with rationale';
|
||||
COMMENT ON COLUMN vex_deltas.rationale IS 'JSON object with reason, evidenceLink, and metadata';
|
||||
COMMENT ON COLUMN vex_deltas.replay_hash IS 'SHA256 hash of inputs for deterministic replay verification';
|
||||
COMMENT ON COLUMN vex_deltas.attestation_digest IS 'SHA256 digest of signed delta verdict attestation';
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: sbom_verdict_links
|
||||
-- Purpose: Links SBOM versions to VEX consensus decisions
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sbom_verdict_links (
|
||||
-- SBOM version reference
|
||||
sbom_version_id UUID NOT NULL,
|
||||
|
||||
-- Vulnerability
|
||||
cve TEXT NOT NULL,
|
||||
|
||||
-- Consensus reference
|
||||
consensus_projection_id UUID NOT NULL,
|
||||
|
||||
-- Verdict snapshot
|
||||
verdict_status TEXT NOT NULL,
|
||||
confidence_score DECIMAL(5,4) NOT NULL CHECK (confidence_score >= 0 AND confidence_score <= 1),
|
||||
|
||||
-- Tenant isolation
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Audit
|
||||
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Composite primary key
|
||||
PRIMARY KEY (sbom_version_id, cve, tenant_id)
|
||||
);
|
||||
|
||||
-- Index for querying by CVE
|
||||
CREATE INDEX IF NOT EXISTS idx_verdict_links_cve
|
||||
ON sbom_verdict_links(cve, tenant_id);
|
||||
|
||||
-- Index for querying by consensus projection
|
||||
CREATE INDEX IF NOT EXISTS idx_verdict_links_projection
|
||||
ON sbom_verdict_links(consensus_projection_id);
|
||||
|
||||
-- Index for time-based queries
|
||||
CREATE INDEX IF NOT EXISTS idx_verdict_links_linked
|
||||
ON sbom_verdict_links(tenant_id, linked_at DESC);
|
||||
|
||||
-- Index for finding specific statuses
|
||||
CREATE INDEX IF NOT EXISTS idx_verdict_links_status
|
||||
ON sbom_verdict_links(tenant_id, verdict_status);
|
||||
|
||||
COMMENT ON TABLE sbom_verdict_links IS 'Links SBOM versions to VEX consensus decisions for traceability';
|
||||
COMMENT ON COLUMN sbom_verdict_links.confidence_score IS 'Consensus confidence score (0.0-1.0)';
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: vex_consensus_projections (migrated from in-memory VexLens)
|
||||
-- Purpose: Persistent storage for VEX consensus projections
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vex_consensus_projections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Target
|
||||
vulnerability_id TEXT NOT NULL,
|
||||
product_key TEXT NOT NULL,
|
||||
|
||||
-- Tenant isolation
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Consensus result
|
||||
status TEXT NOT NULL,
|
||||
confidence_score DECIMAL(5,4) NOT NULL CHECK (confidence_score >= 0 AND confidence_score <= 1),
|
||||
outcome TEXT NOT NULL,
|
||||
|
||||
-- Statistics
|
||||
statement_count INT NOT NULL DEFAULT 0,
|
||||
conflict_count INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- Timestamps
|
||||
computed_at TIMESTAMPTZ NOT NULL,
|
||||
stored_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- History linkage
|
||||
previous_projection_id UUID REFERENCES vex_consensus_projections(id),
|
||||
status_changed BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
-- Unique constraint for latest projection per (vuln, product, tenant, time)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_consensus_unique
|
||||
ON vex_consensus_projections(tenant_id, vulnerability_id, product_key, computed_at);
|
||||
|
||||
-- Index for finding status changes
|
||||
CREATE INDEX IF NOT EXISTS idx_consensus_status_changed
|
||||
ON vex_consensus_projections(tenant_id, status_changed, computed_at DESC)
|
||||
WHERE status_changed = TRUE;
|
||||
|
||||
-- Index for history traversal
|
||||
CREATE INDEX IF NOT EXISTS idx_consensus_previous
|
||||
ON vex_consensus_projections(previous_projection_id)
|
||||
WHERE previous_projection_id IS NOT NULL;
|
||||
|
||||
-- Index for product queries
|
||||
CREATE INDEX IF NOT EXISTS idx_consensus_product
|
||||
ON vex_consensus_projections(product_key, tenant_id);
|
||||
|
||||
COMMENT ON TABLE vex_consensus_projections IS 'Persistent VEX consensus projections with full history';
|
||||
COMMENT ON COLUMN vex_consensus_projections.outcome IS 'Consensus outcome: Unanimous, Majority, Plurality, ConflictResolved, NoData';
|
||||
COMMENT ON COLUMN vex_consensus_projections.status_changed IS 'True if status differs from previous projection';
|
||||
|
||||
-- ============================================================================
|
||||
-- EXTENSION: Add replay_hash to sbom_snapshots (alter existing table)
|
||||
-- ============================================================================
|
||||
|
||||
-- Note: This ALTER should be applied to existing sbom_snapshots table
|
||||
-- ALTER TABLE sbom_snapshots ADD COLUMN IF NOT EXISTS replay_hash TEXT;
|
||||
-- CREATE INDEX IF NOT EXISTS idx_sbom_snapshots_replay ON sbom_snapshots(replay_hash) WHERE replay_hash IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- FUNCTIONS: Helper functions for lineage queries
|
||||
-- ============================================================================
|
||||
|
||||
-- Function to get lineage depth from a starting node
|
||||
CREATE OR REPLACE FUNCTION get_lineage_depth(
|
||||
p_artifact_digest TEXT,
|
||||
p_tenant_id UUID,
|
||||
p_max_depth INT DEFAULT 10
|
||||
) RETURNS INT AS $$
|
||||
DECLARE
|
||||
v_depth INT := 0;
|
||||
v_current_count INT;
|
||||
BEGIN
|
||||
WITH RECURSIVE lineage AS (
|
||||
SELECT child_digest, 1 as depth
|
||||
FROM sbom_lineage_edges
|
||||
WHERE parent_digest = p_artifact_digest AND tenant_id = p_tenant_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT e.child_digest, l.depth + 1
|
||||
FROM sbom_lineage_edges e
|
||||
JOIN lineage l ON e.parent_digest = l.child_digest
|
||||
WHERE e.tenant_id = p_tenant_id AND l.depth < p_max_depth
|
||||
)
|
||||
SELECT COALESCE(MAX(depth), 0) INTO v_depth FROM lineage;
|
||||
|
||||
RETURN v_depth;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql STABLE;
|
||||
|
||||
-- Function to get all ancestors of an artifact
|
||||
CREATE OR REPLACE FUNCTION get_ancestors(
|
||||
p_artifact_digest TEXT,
|
||||
p_tenant_id UUID,
|
||||
p_max_depth INT DEFAULT 10
|
||||
) RETURNS TABLE (
|
||||
ancestor_digest TEXT,
|
||||
depth INT,
|
||||
relationship TEXT
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
WITH RECURSIVE ancestors AS (
|
||||
SELECT parent_digest, 1 as depth, e.relationship
|
||||
FROM sbom_lineage_edges e
|
||||
WHERE child_digest = p_artifact_digest AND tenant_id = p_tenant_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT e.parent_digest, a.depth + 1, e.relationship
|
||||
FROM sbom_lineage_edges e
|
||||
JOIN ancestors a ON e.child_digest = a.parent_digest
|
||||
WHERE e.tenant_id = p_tenant_id AND a.depth < p_max_depth
|
||||
)
|
||||
SELECT parent_digest, ancestors.depth, ancestors.relationship
|
||||
FROM ancestors
|
||||
ORDER BY depth, parent_digest;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql STABLE;
|
||||
|
||||
-- Function to get all descendants of an artifact
|
||||
CREATE OR REPLACE FUNCTION get_descendants(
|
||||
p_artifact_digest TEXT,
|
||||
p_tenant_id UUID,
|
||||
p_max_depth INT DEFAULT 10
|
||||
) RETURNS TABLE (
|
||||
descendant_digest TEXT,
|
||||
depth INT,
|
||||
relationship TEXT
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
WITH RECURSIVE descendants AS (
|
||||
SELECT child_digest, 1 as depth, e.relationship
|
||||
FROM sbom_lineage_edges e
|
||||
WHERE parent_digest = p_artifact_digest AND tenant_id = p_tenant_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT e.child_digest, d.depth + 1, e.relationship
|
||||
FROM sbom_lineage_edges e
|
||||
JOIN descendants d ON e.parent_digest = d.child_digest
|
||||
WHERE e.tenant_id = p_tenant_id AND d.depth < p_max_depth
|
||||
)
|
||||
SELECT child_digest, descendants.depth, descendants.relationship
|
||||
FROM descendants
|
||||
ORDER BY depth, child_digest;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql STABLE;
|
||||
|
||||
COMMENT ON FUNCTION get_lineage_depth IS 'Returns the maximum depth of descendants from an artifact';
|
||||
COMMENT ON FUNCTION get_ancestors IS 'Returns all ancestor artifacts up to max_depth';
|
||||
COMMENT ON FUNCTION get_descendants IS 'Returns all descendant artifacts up to max_depth';
|
||||
71
docs/modules/symbols/architecture.md
Normal file
71
docs/modules/symbols/architecture.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# component_architecture_symbols.md - **Stella Ops Symbols** (2025Q4)
|
||||
|
||||
> Symbol resolution and debug information management.
|
||||
|
||||
> **Scope.** Library and service architecture for **Symbols**: managing debug symbols, build IDs, and symbol-to-package mappings for reachability analysis.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide **symbol resolution infrastructure** for native binary analysis. Map symbols to packages, manage debug information, and support stripped binary analysis.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Symbols **resolves and maps** symbols; execution is handled by Scanner.
|
||||
* Debug symbols are **optional**; stripped binaries use heuristics.
|
||||
* Supports **offline symbol stores** for air-gapped operation.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Symbols/
|
||||
├─ StellaOps.Symbols/ # Core symbol resolution
|
||||
│ ├─ Services/
|
||||
│ │ ├─ ISymbolResolver.cs
|
||||
│ │ └─ BuildIdResolver.cs
|
||||
│ └─ Models/
|
||||
│ ├─ SymbolInfo.cs
|
||||
│ └─ BuildIdMapping.cs
|
||||
│
|
||||
└─ Integration with Scanner:
|
||||
└─ src/Scanner/__Libraries/StellaOps.Scanner.Symbols.Native/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Contracts & data model
|
||||
|
||||
### 2.1 Symbol Info
|
||||
|
||||
```json
|
||||
{
|
||||
"symbolId": "sha256:abc123",
|
||||
"name": "_ZN3foo3barEv",
|
||||
"demangledName": "foo::bar()",
|
||||
"sourceFile": "src/foo.cpp",
|
||||
"lineNumber": 42,
|
||||
"buildId": "abc123def456",
|
||||
"packagePurl": "pkg:deb/debian/libfoo@1.2.3"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Build ID Mapping
|
||||
|
||||
```json
|
||||
{
|
||||
"buildId": "abc123def456",
|
||||
"path": "/usr/lib/libfoo.so.1",
|
||||
"packagePurl": "pkg:deb/debian/libfoo@1.2.3",
|
||||
"debugPath": "/usr/lib/debug/.build-id/ab/c123def456.debug"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Scanner native analysis: `../scanner/architecture.md`
|
||||
* Reachability: `../../reachability/`
|
||||
74
docs/modules/timelineindexer/architecture.md
Normal file
74
docs/modules/timelineindexer/architecture.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# component_architecture_timelineindexer.md - **Stella Ops TimelineIndexer** (2025Q4)
|
||||
|
||||
> Timeline event indexing and query service.
|
||||
|
||||
> **Scope.** Implementation-ready architecture for **TimelineIndexer**: indexing and querying timeline events for vulnerability findings, scans, and policy evaluations.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide **fast, indexed access** to timeline events across all StellaOps services. Enable efficient querying of vulnerability history, scan timelines, and policy evaluation trails.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* TimelineIndexer **indexes events**; it does not generate them.
|
||||
* Events are received from **event streams** (NATS, Valkey).
|
||||
* Supports **time-range queries** with filtering.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/TimelineIndexer/StellaOps.TimelineIndexer/
|
||||
├─ StellaOps.TimelineIndexer.Core/ # Event models, indexing logic
|
||||
├─ StellaOps.TimelineIndexer.Infrastructure/ # Storage adapters
|
||||
├─ StellaOps.TimelineIndexer.WebService/ # Query API
|
||||
├─ StellaOps.TimelineIndexer.Worker/ # Event consumer
|
||||
└─ StellaOps.TimelineIndexer.Tests/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) External dependencies
|
||||
|
||||
* **PostgreSQL** - Event storage with time-series indexes
|
||||
* **NATS/Valkey** - Event stream consumption
|
||||
* **Authority** - Authentication
|
||||
|
||||
---
|
||||
|
||||
## 3) Contracts & data model
|
||||
|
||||
### 3.1 TimelineEvent
|
||||
|
||||
```json
|
||||
{
|
||||
"eventId": "evt-2025-01-15-abc123",
|
||||
"eventType": "scan.completed",
|
||||
"timestamp": "2025-01-15T10:30:00Z",
|
||||
"tenantId": "tenant-xyz",
|
||||
"subjectId": "image:sha256:abc123",
|
||||
"payload": { /* event-specific data */ }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) REST API
|
||||
|
||||
```
|
||||
GET /timeline?subject={id}&from={date}&to={date} → { events[] }
|
||||
GET /timeline/{eventId} → { event }
|
||||
GET /timeline/stats?subject={id} → { statistics }
|
||||
|
||||
GET /healthz | /readyz | /metrics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Signals: `../signals/architecture.md`
|
||||
* Scanner: `../scanner/architecture.md`
|
||||
@@ -1,5 +1,11 @@
|
||||
# StellaOps Console UI
|
||||
|
||||
**Status:** Implemented
|
||||
**Source:** `src/Web/StellaOps.Web/`
|
||||
**Owner:** UI Guild
|
||||
|
||||
> **Related:** See [`../web/`](../web/) for triage-specific UX documentation (Smart-Diff, Triage Canvas, Risk Dashboard).
|
||||
|
||||
The Console presents operator dashboards for scans, policies, VEX evidence, runtime posture, and admin workflows.
|
||||
|
||||
## Latest updates (2025-11-30)
|
||||
|
||||
70
docs/modules/unknowns/architecture.md
Normal file
70
docs/modules/unknowns/architecture.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# component_architecture_unknowns.md - **Stella Ops Unknowns** (2025Q4)
|
||||
|
||||
> Unknown component and symbol tracking registry.
|
||||
|
||||
> **Scope.** Library architecture for **Unknowns**: tracking unresolved components, symbols, and mappings that Scanner and other analyzers cannot definitively identify.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide a **structured registry** for tracking unknown components, unresolved symbols, and incomplete mappings. Enable visibility into coverage gaps and guide future enhancement priorities.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Unknowns is a **library layer** consumed by Scanner and Signals.
|
||||
* Unknowns **does not** guess identities. It records what cannot be determined.
|
||||
* All unknowns are **categorized** for actionability.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Unknowns/
|
||||
├─ __Libraries/
|
||||
│ ├─ StellaOps.Unknowns.Core/ # Unknown models, categorization
|
||||
│ ├─ StellaOps.Unknowns.Persistence/ # Storage abstractions
|
||||
│ └─ StellaOps.Unknowns.Persistence.EfCore/
|
||||
│
|
||||
└─ __Tests/
|
||||
├─ StellaOps.Unknowns.Core.Tests/
|
||||
└─ StellaOps.Unknowns.Persistence.Tests/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Contracts & data model
|
||||
|
||||
### 2.1 Unknown Record
|
||||
|
||||
```json
|
||||
{
|
||||
"unknownId": "unk-2025-01-15-abc123",
|
||||
"category": "symbol_unmapped",
|
||||
"context": {
|
||||
"scanId": "scan-xyz",
|
||||
"binaryPath": "/usr/lib/libfoo.so",
|
||||
"symbolName": "_ZN3foo3barEv"
|
||||
},
|
||||
"reason": "No PURL mapping available",
|
||||
"firstSeen": "2025-01-15T10:30:00Z",
|
||||
"occurrences": 42
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Categories
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| `component_unidentified` | Binary without package mapping |
|
||||
| `symbol_unmapped` | Symbol without PURL resolution |
|
||||
| `version_ambiguous` | Multiple version candidates |
|
||||
| `purl_invalid` | Malformed package URL |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* Scanner: `../scanner/architecture.md`
|
||||
* Signals: `../signals/architecture.md`
|
||||
@@ -4,6 +4,8 @@
|
||||
**Source:** `src/Web/`
|
||||
**Owner:** UI Guild
|
||||
|
||||
> **Note:** This folder documents triage-specific frontend features. For the comprehensive Web UI architecture, see [`../ui/architecture.md`](../ui/architecture.md).
|
||||
|
||||
## Purpose
|
||||
|
||||
Web provides the Angular 17 single-page application (SPA) frontend for StellaOps. Delivers the user interface for vulnerability exploration, policy management, scan results, SBOM visualization, and administrative functions.
|
||||
|
||||
105
docs/modules/web/architecture.md
Normal file
105
docs/modules/web/architecture.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# component_architecture_web.md - **Stella Ops Web** (2025Q4)
|
||||
|
||||
> Angular 17 frontend SPA for StellaOps console.
|
||||
|
||||
> **Scope.** Frontend architecture for **Web**: the Angular 17 single-page application providing the StellaOps console interface.
|
||||
|
||||
> **Note:** For the comprehensive Web UI architecture including all features, see [`../ui/architecture.md`](../ui/architecture.md). This file provides a condensed overview.
|
||||
|
||||
---
|
||||
|
||||
## 0) Mission & boundaries
|
||||
|
||||
**Mission.** Provide an **intuitive, responsive web interface** for StellaOps operators to manage scans, review findings, configure policies, and monitor system health.
|
||||
|
||||
**Boundaries.**
|
||||
|
||||
* Web is a **frontend-only** application. All data comes from backend APIs.
|
||||
* Web **does not** store sensitive data locally beyond session tokens.
|
||||
* Supports **offline-first** patterns for air-gapped console access.
|
||||
|
||||
---
|
||||
|
||||
## 1) Solution & project layout
|
||||
|
||||
```
|
||||
src/Web/StellaOps.Web/
|
||||
├─ src/
|
||||
│ ├─ app/
|
||||
│ │ ├─ core/ # Core services, guards, interceptors
|
||||
│ │ │ ├─ services/
|
||||
│ │ │ ├─ guards/
|
||||
│ │ │ └─ interceptors/
|
||||
│ │ ├─ shared/ # Shared components, pipes, directives
|
||||
│ │ │ ├─ components/
|
||||
│ │ │ ├─ pipes/
|
||||
│ │ │ └─ directives/
|
||||
│ │ ├─ features/ # Feature modules
|
||||
│ │ │ ├─ dashboard/
|
||||
│ │ │ ├─ scans/
|
||||
│ │ │ ├─ findings/
|
||||
│ │ │ ├─ policies/
|
||||
│ │ │ ├─ settings/
|
||||
│ │ │ └─ admin/
|
||||
│ │ └─ app.routes.ts
|
||||
│ ├─ assets/
|
||||
│ ├─ environments/
|
||||
│ └─ styles/
|
||||
├─ angular.json
|
||||
├─ package.json
|
||||
└─ tsconfig.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Technology stack
|
||||
|
||||
* **Framework**: Angular 17 with standalone components
|
||||
* **State management**: NgRx Signals
|
||||
* **UI components**: Angular Material
|
||||
* **HTTP**: Angular HttpClient with interceptors
|
||||
* **Routing**: Angular Router with lazy loading
|
||||
* **Build**: Angular CLI with esbuild
|
||||
|
||||
---
|
||||
|
||||
## 3) Key features
|
||||
|
||||
| Feature | Path | Description |
|
||||
|---------|------|-------------|
|
||||
| Dashboard | `/dashboard` | Overview metrics and alerts |
|
||||
| Scans | `/scans` | Scan history and details |
|
||||
| Findings | `/findings` | Vulnerability findings list |
|
||||
| Compare | `/compare` | Diff view between scans |
|
||||
| Policies | `/policies` | Policy configuration |
|
||||
| Settings | `/settings` | User and system settings |
|
||||
|
||||
---
|
||||
|
||||
## 4) Authentication
|
||||
|
||||
* **OIDC/OAuth2** via Authority module
|
||||
* **Token refresh** handled by interceptor
|
||||
* **Session timeout** with configurable duration
|
||||
|
||||
---
|
||||
|
||||
## 5) Configuration
|
||||
|
||||
```typescript
|
||||
// environment.ts
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: 'http://localhost:5000/api/v1',
|
||||
authorityUrl: 'http://localhost:5001',
|
||||
clientId: 'stellaops-web'
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
* UI module: `../ui/architecture.md`
|
||||
* Authority: `../authority/architecture.md`
|
||||
* Auth smoke tests: `../ui/operations/auth-smoke.md`
|
||||
Reference in New Issue
Block a user