This commit is contained in:
StellaOps Bot
2025-12-18 20:37:12 +02:00
278 changed files with 35930 additions and 1134 deletions

263
docs/cli/drift-cli.md Normal file
View File

@@ -0,0 +1,263 @@
# Drift CLI Reference
**Sprint:** SPRINT_3600_0004_0001
**Task:** UI-024 - Update CLI documentation for drift commands
## Overview
The Drift CLI provides commands for detecting and analyzing reachability drift between scan results. Reachability drift occurs when the call paths to vulnerable code change between builds, potentially altering the risk profile of an application.
## Commands
### stellaops drift
Parent command for reachability drift operations.
```bash
stellaops drift <SUBCOMMAND> [OPTIONS]
```
---
### stellaops drift compare
Compare reachability between two scans or graph snapshots.
```bash
stellaops drift compare [OPTIONS]
```
#### Required Options
| Option | Alias | Description |
|--------|-------|-------------|
| `--base <ID>` | `-b` | Base scan/graph ID or commit SHA for comparison |
#### Optional Options
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--head <ID>` | `-h` | Head scan/graph ID or commit SHA | latest |
| `--image <REF>` | `-i` | Container image reference (digest or tag) | - |
| `--repo <REPO>` | `-r` | Repository reference (owner/repo) | - |
| `--output <FMT>` | `-o` | Output format: `table`, `json`, `sarif` | `table` |
| `--min-severity <SEV>` | | Minimum severity: `critical`, `high`, `medium`, `low`, `info` | `medium` |
| `--only-increases` | | Only show sinks with increased reachability | `false` |
| `--verbose` | | Enable verbose output | `false` |
#### Examples
##### Compare by scan IDs
```bash
stellaops drift compare --base abc123 --head def456
```
##### Compare by commit SHAs
```bash
stellaops drift compare --base HEAD~1 --head HEAD --repo myorg/myapp
```
##### Filter to risk increases only
```bash
stellaops drift compare --base abc123 --only-increases --min-severity high
```
##### Output as JSON
```bash
stellaops drift compare --base abc123 --output json > drift.json
```
##### Output as SARIF for CI integration
```bash
stellaops drift compare --base abc123 --output sarif > drift.sarif
```
---
### stellaops drift show
Display details of a previously computed drift result.
```bash
stellaops drift show [OPTIONS]
```
#### Required Options
| Option | Description |
|--------|-------------|
| `--id <ID>` | Drift result ID to display |
#### Optional Options
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--output <FMT>` | `-o` | Output format: `table`, `json`, `sarif` | `table` |
| `--expand-paths` | | Show full call paths instead of compressed view | `false` |
| `--verbose` | | Enable verbose output | `false` |
#### Examples
##### Show drift result
```bash
stellaops drift show --id drift-abc123
```
##### Show with expanded paths
```bash
stellaops drift show --id drift-abc123 --expand-paths
```
---
## Output Formats
### Table Format (Default)
Human-readable table output using Spectre.Console:
```
┌─────────────────────────────────────────────────────────────┐
│ Reachability Drift (abc123) │
├───────────────────────────────┬─────────────────────────────┤
│ Metric │ Value │
├───────────────────────────────┼─────────────────────────────┤
│ Trend │ ↑ Increasing │
│ Net Risk Delta │ +3 │
│ Increased │ 4 │
│ Decreased │ 1 │
│ New Sinks │ 2 │
│ Removed Sinks │ 0 │
└───────────────────────────────┴─────────────────────────────┘
┌──────────────┬──────────────────────┬───────────────┬─────────────────────────┬───────┐
│ Severity │ Sink │ CVE │ Bucket Change │ Delta │
├──────────────┼──────────────────────┼───────────────┼─────────────────────────┼───────┤
│ CRITICAL │ SqlConnection.Open │ CVE-2024-1234 │ Runtime → Entrypoint │ +2 │
│ HIGH │ XmlParser.Parse │ CVE-2024-5678 │ Unknown → Direct │ +1 │
└──────────────┴──────────────────────┴───────────────┴─────────────────────────┴───────┘
```
### JSON Format
Structured JSON for programmatic processing:
```json
{
"id": "abc123",
"comparedAt": "2025-12-18T10:30:00Z",
"baseGraphId": "base-graph-id",
"headGraphId": "head-graph-id",
"summary": {
"totalSinks": 42,
"increasedReachability": 4,
"decreasedReachability": 1,
"unchangedReachability": 35,
"newSinks": 2,
"removedSinks": 0,
"riskTrend": "increasing",
"netRiskDelta": 3
},
"driftedSinks": [
{
"sinkSymbol": "SqlConnection.Open",
"cveId": "CVE-2024-1234",
"severity": "critical",
"previousBucket": "runtime",
"currentBucket": "entrypoint",
"isRiskIncrease": true,
"riskDelta": 2
}
]
}
```
### SARIF Format
SARIF 2.1.0 output for CI/CD integration:
```json
{
"version": "2.1.0",
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"runs": [
{
"tool": {
"driver": {
"name": "StellaOps Drift",
"version": "1.0.0",
"informationUri": "https://stellaops.io/docs/drift"
}
},
"results": [
{
"ruleId": "CVE-2024-1234",
"level": "error",
"message": {
"text": "Reachability changed: runtime → entrypoint"
}
}
]
}
]
}
```
---
## Exit Codes
| Code | Description |
|------|-------------|
| `0` | Success (no risk increases or within threshold) |
| `1` | Error during execution |
| `2` | Risk increases detected |
| `3` | Critical risk increases detected |
---
## CI/CD Integration
### GitHub Actions
```yaml
- name: Check Reachability Drift
run: |
stellaops drift compare \
--base ${{ github.event.pull_request.base.sha }} \
--head ${{ github.sha }} \
--repo ${{ github.repository }} \
--output sarif > drift.sarif
continue-on-error: true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: drift.sarif
```
### GitLab CI
```yaml
drift-check:
script:
- stellaops drift compare --base $CI_MERGE_REQUEST_DIFF_BASE_SHA --head $CI_COMMIT_SHA --output sarif > drift.sarif
artifacts:
reports:
sast: drift.sarif
```
---
## Related Documentation
- [Reachability Analysis](../reachability/README.md)
- [Smart-Diff CLI](./smart-diff-cli.md)
- [VEX Decisioning](../vex/decisioning.md)

View File

@@ -0,0 +1,256 @@
# Vuln Surface Contract v1
**Sprint:** SPRINT_3700_0002_0001
**Task:** SURF-024
**Schema:** `stella.ops/vulnSurface@v1`
## Overview
A Vulnerability Surface represents the specific methods that changed between a vulnerable and fixed version of a package. This enables precise reachability analysis by identifying the exact "trigger" methods that are dangerous rather than treating the entire package as vulnerable.
## Use Cases
1. **Noise Reduction** - Only flag findings where code actually calls vulnerable methods
2. **Confidence Tiers** - "Confirmed reachable" (calls trigger) vs "Potentially reachable" (uses package)
3. **Remediation Guidance** - Show developers exactly which API calls to avoid
4. **VEX Precision** - Automatically generate VEX "not_affected" for unreachable triggers
## Data Model
### VulnSurface
Root object representing a computed vulnerability surface.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `surface_id` | integer | Yes | Database ID |
| `cve_id` | string | Yes | CVE identifier (e.g., "CVE-2024-12345") |
| `package_id` | string | Yes | Package identifier in PURL format |
| `ecosystem` | string | Yes | Package ecosystem: `nuget`, `npm`, `maven`, `pypi` |
| `vuln_version` | string | Yes | Vulnerable version analyzed |
| `fixed_version` | string | Yes | First fixed version used for diff |
| `sinks` | VulnSurfaceSink[] | Yes | Changed methods (vulnerability triggers) |
| `trigger_count` | integer | Yes | Number of callers to sink methods |
| `status` | VulnSurfaceStatus | Yes | Computation status |
| `confidence` | number | Yes | Confidence score (0.0-1.0) |
| `computed_at` | string | Yes | ISO 8601 timestamp |
### VulnSurfaceSink
A method that changed between vulnerable and fixed versions.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `sink_id` | integer | Yes | Database ID |
| `method_key` | string | Yes | Fully qualified method signature |
| `method_name` | string | Yes | Simple method name |
| `declaring_type` | string | Yes | Containing class/module |
| `namespace` | string | No | Namespace/package |
| `change_type` | MethodChangeType | Yes | How the method changed |
| `is_public` | boolean | Yes | Whether method is publicly accessible |
| `parameter_count` | integer | No | Number of parameters |
| `return_type` | string | No | Return type |
| `source_file` | string | No | Source file (from debug symbols) |
| `start_line` | integer | No | Starting line number |
| `end_line` | integer | No | Ending line number |
### VulnSurfaceTrigger
A call site that invokes a vulnerable sink method.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `trigger_id` | integer | Yes | Database ID |
| `sink_id` | integer | Yes | Reference to sink |
| `scan_id` | UUID | Yes | Scan where trigger was found |
| `caller_node_id` | string | Yes | Call graph node ID |
| `caller_method_key` | string | Yes | FQN of calling method |
| `caller_file` | string | No | Source file of caller |
| `caller_line` | integer | No | Line number of call |
| `reachability_bucket` | string | Yes | Reachability classification |
| `path_length` | integer | No | Shortest path from entrypoint |
| `confidence` | number | Yes | Confidence score (0.0-1.0) |
| `call_type` | string | Yes | Call type: `direct`, `virtual`, `interface`, `reflection` |
| `is_conditional` | boolean | Yes | Whether call is behind a condition |
## Enums
### VulnSurfaceStatus
| Value | Description |
|-------|-------------|
| `pending` | Surface computation queued |
| `computing` | Currently being computed |
| `computed` | Successfully computed |
| `failed` | Computation failed |
| `stale` | Needs recomputation (new version available) |
### MethodChangeType
| Value | Description |
|-------|-------------|
| `added` | Method added in fix (not in vulnerable version) |
| `removed` | Method removed in fix (was in vulnerable version) |
| `modified` | Method body changed between versions |
| `unknown` | Change type could not be determined |
### Reachability Buckets
| Bucket | Description | Risk Level |
|--------|-------------|------------|
| `entrypoint` | Sink is directly exposed as entrypoint | Critical |
| `direct` | Reachable from entrypoint with no authentication gates | High |
| `runtime` | Reachable but behind runtime conditions/auth | Medium |
| `unknown` | Reachability could not be determined | Medium |
| `unreachable` | No path from any entrypoint | Low |
## Fingerprinting Methods
### cecil-il (NuGet/.NET)
Uses Mono.Cecil to compute SHA-256 hash of IL instruction sequence:
```
IL_0000: ldarg.0
IL_0001: call System.Object::.ctor()
IL_0006: ret
```
Normalized to remove:
- NOP instructions
- Debug sequence points
- Local variable indices (replaced with placeholders)
### babel-ast (npm/Node.js)
Uses Babel to parse JavaScript/TypeScript and compute hash of normalized AST:
```javascript
function vulnerable(input) {
eval(input); // dangerous!
}
```
Normalized to remove:
- Comments
- Whitespace
- Variable names (renamed to positional)
### asm-bytecode (Maven/Java)
Uses ASM to compute hash of Java bytecode:
```
ALOAD 0
INVOKESPECIAL java/lang/Object.<init>()V
RETURN
```
Normalized to remove:
- Line number tables
- Local variable tables
- Stack map frames
### python-ast (PyPI)
Uses Python's `ast` module to compute hash of normalized AST:
```python
def vulnerable(user_input):
exec(user_input) # dangerous!
```
Normalized to remove:
- Docstrings
- Comments
- Variable names
## Database Schema
```sql
-- Surfaces table
CREATE TABLE scanner.vuln_surfaces (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
cve_id TEXT NOT NULL,
package_ecosystem TEXT NOT NULL,
package_name TEXT NOT NULL,
vuln_version TEXT NOT NULL,
fixed_version TEXT,
fingerprint_method TEXT NOT NULL,
total_methods_vuln INTEGER,
total_methods_fixed INTEGER,
changed_method_count INTEGER,
computed_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (tenant_id, cve_id, package_ecosystem, package_name, vuln_version)
);
-- Sinks table
CREATE TABLE scanner.vuln_surface_sinks (
id UUID PRIMARY KEY,
surface_id UUID REFERENCES scanner.vuln_surfaces(id) ON DELETE CASCADE,
method_key TEXT NOT NULL,
method_name TEXT NOT NULL,
declaring_type TEXT NOT NULL,
change_type TEXT NOT NULL,
UNIQUE (surface_id, method_key)
);
-- Triggers table
CREATE TABLE scanner.vuln_surface_triggers (
id UUID PRIMARY KEY,
sink_id UUID REFERENCES scanner.vuln_surface_sinks(id) ON DELETE CASCADE,
scan_id UUID NOT NULL,
caller_node_id TEXT NOT NULL,
reachability_bucket TEXT NOT NULL,
confidence REAL NOT NULL,
UNIQUE (sink_id, scan_id, caller_node_id)
);
```
## API Endpoints
### POST /api/v1/surfaces/compute
Request surface computation for a CVE + package.
**Request:**
```json
{
"cveId": "CVE-2024-12345",
"ecosystem": "nuget",
"packageName": "Newtonsoft.Json",
"vulnVersion": "13.0.1",
"fixedVersion": "13.0.2"
}
```
**Response:**
```json
{
"surfaceId": "uuid",
"status": "pending"
}
```
### GET /api/v1/surfaces/{surfaceId}
Get computed surface with sinks.
### GET /api/v1/surfaces/{surfaceId}/triggers?scanId={scanId}
Get triggers for a surface in a specific scan.
## Integration Points
1. **Concelier** - Feeds CVE + affected version ranges
2. **Scanner** - Computes surfaces during SBOM analysis
3. **Call Graph** - Provides reachability analysis
4. **VEX Lens** - Uses surfaces for automated VEX decisions
5. **UI** - Displays surface details and trigger paths
## References
- [Vuln Surfaces Sprint](../implplan/SPRINT_3700_0002_0001_vuln_surfaces_core.md)
- [Reachability Architecture](../reachability/README.md)
- [RichGraph Contract](./richgraph-v1.md)

View File

@@ -0,0 +1,221 @@
# Witness Schema v1 Contract
> **Version**: `stellaops.witness.v1`
> **Status**: Draft
> **Sprint**: `SPRINT_3700_0001_0001_witness_foundation`
---
## Overview
A **witness** is a cryptographically-signed proof of a reachability path from an entrypoint to a vulnerable sink. Witnesses provide:
1. **Auditability** - Proof that a path was found at scan time
2. **Offline verification** - Verify claims without re-running analysis
3. **Provenance** - Links to the source graph and analysis context
4. **Transparency** - Can be published to transparency logs
---
## Schema Definition
### PathWitness
```json
{
"$schema": "https://stellaops.org/schemas/witness-v1.json",
"schema_version": "stellaops.witness.v1",
"witness_id": "uuid",
"witness_hash": "blake3:abcd1234...",
"witness_type": "reachability_path",
"created_at": "2025-12-18T12:00:00Z",
"provenance": {
"graph_hash": "blake3:efgh5678...",
"scan_id": "uuid",
"run_id": "uuid",
"analyzer_version": "1.0.0",
"analysis_timestamp": "2025-12-18T11:59:00Z"
},
"path": {
"entrypoint": {
"fqn": "com.example.MyController.handleRequest",
"kind": "http_handler",
"location": {
"file": "src/main/java/com/example/MyController.java",
"line": 42
}
},
"sink": {
"fqn": "org.apache.log4j.Logger.log",
"cve": "CVE-2021-44228",
"package": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1"
},
"steps": [
{
"index": 0,
"fqn": "com.example.MyController.handleRequest",
"call_site": "MyController.java:45",
"edge_type": "call"
},
{
"index": 1,
"fqn": "com.example.LoggingService.logMessage",
"call_site": "LoggingService.java:23",
"edge_type": "call"
},
{
"index": 2,
"fqn": "org.apache.log4j.Logger.log",
"call_site": "Logger.java:156",
"edge_type": "sink"
}
],
"hop_count": 3
},
"gates": [
{
"type": "auth_required",
"location": "MyController.java:40",
"description": "Requires authenticated user"
}
],
"evidence": {
"graph_fragment_hash": "blake3:ijkl9012...",
"path_hash": "blake3:mnop3456..."
}
}
```
---
## Field Definitions
### Root Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `schema_version` | string | Yes | Must be `stellaops.witness.v1` |
| `witness_id` | UUID | Yes | Unique identifier |
| `witness_hash` | string | Yes | BLAKE3 hash of canonical JSON |
| `witness_type` | enum | Yes | `reachability_path`, `gate_proof` |
| `created_at` | ISO8601 | Yes | Witness creation timestamp (UTC) |
### Provenance
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `graph_hash` | string | Yes | BLAKE3 hash of source rich graph |
| `scan_id` | UUID | No | Scan that produced the graph |
| `run_id` | UUID | No | Analysis run identifier |
| `analyzer_version` | string | Yes | Analyzer version |
| `analysis_timestamp` | ISO8601 | Yes | When analysis was performed |
### Path
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `entrypoint` | object | Yes | Entry point of the path |
| `sink` | object | Yes | Vulnerable sink at end of path |
| `steps` | array | Yes | Ordered list of path steps |
| `hop_count` | integer | Yes | Number of edges in path |
### Path Step
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `index` | integer | Yes | Position in path (0-indexed) |
| `fqn` | string | Yes | Fully qualified name of node |
| `call_site` | string | No | Source location of call |
| `edge_type` | enum | Yes | `call`, `virtual`, `static`, `sink` |
### Gates
Optional array of protective controls encountered along the path.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | enum | Yes | `auth_required`, `feature_flag`, `admin_only`, `non_default_config` |
| `location` | string | No | Source location of gate |
| `description` | string | No | Human-readable description |
---
## Hash Computation
The `witness_hash` is computed as:
1. Serialize the witness to canonical JSON (sorted keys, no whitespace)
2. Exclude `witness_id`, `witness_hash`, and `created_at` fields
3. Compute BLAKE3 hash of the canonical bytes
4. Prefix with `blake3:` and hex-encode
```csharp
var canonical = JsonSerializer.Serialize(witness, canonicalOptions);
var hash = Blake3.Hasher.Hash(Encoding.UTF8.GetBytes(canonical));
var witnessHash = $"blake3:{Convert.ToHexString(hash.AsSpan()).ToLowerInvariant()}";
```
---
## DSSE Signing
Witnesses are signed using [DSSE (Dead Simple Signing Envelope)](https://github.com/secure-systems-lab/dsse):
```json
{
"payloadType": "application/vnd.stellaops.witness.v1+json",
"payload": "<base64url-encoded witness JSON>",
"signatures": [
{
"keyid": "sha256:abcd1234...",
"sig": "<base64url-encoded signature>"
}
]
}
```
### Verification
1. Decode the payload from base64url
2. Parse as PathWitness JSON
3. Recompute witness_hash and compare
4. Verify signature against known public key
5. Optionally check transparency log for inclusion
---
## Storage
Witnesses are stored in `scanner.witnesses` table:
| Column | Type | Description |
|--------|------|-------------|
| `witness_id` | UUID | Primary key |
| `witness_hash` | TEXT | BLAKE3 hash (unique) |
| `payload_json` | JSONB | Full witness JSON |
| `dsse_envelope` | JSONB | Signed envelope (nullable) |
| `graph_hash` | TEXT | Source graph reference |
| `sink_cve` | TEXT | CVE for quick lookup |
---
## API Endpoints
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/v1/witnesses/{id}` | Get witness by ID |
| `GET` | `/api/v1/witnesses?cve={cve}` | List witnesses for CVE |
| `GET` | `/api/v1/witnesses?scan={scanId}` | List witnesses for scan |
| `POST` | `/api/v1/witnesses/{id}/verify` | Verify witness signature |
---
## Related Documents
- [Rich Graph Contract](richgraph-v1.md)
- [DSSE Specification](https://github.com/secure-systems-lab/dsse)
- [BLAKE3 Hash Function](https://github.com/BLAKE3-team/BLAKE3)

View File

@@ -72,12 +72,12 @@ stellaops verify offline \
| 2 | T2 | DONE | Implemented `OfflineCommandGroup` and wired into `CommandFactory`. | DevEx/CLI Guild | Create `OfflineCommandGroup` class. |
| 3 | T3 | DONE | Implemented `offline import` with manifest/hash validation, monotonicity checks, and quarantine hooks. | DevEx/CLI Guild | Implement `offline import` command (core import flow). |
| 4 | T4 | DONE | Implemented `--verify-dsse` via `DsseVerifier` (requires `--trust-root`) and added tests. | DevEx/CLI Guild | Add `--verify-dsse` flag handler. |
| 5 | T5 | DOING | Implement offline Rekor receipt inclusion proof + checkpoint signature verification per `docs/product-advisories/14-Dec-2025 - Rekor Integration Technical Reference.md` §13. | DevEx/CLI Guild | Add `--verify-rekor` flag handler. |
| 5 | T5 | DONE | Implement offline Rekor receipt inclusion proof + checkpoint signature verification per `docs/product-advisories/14-Dec-2025 - Rekor Integration Technical Reference.md` §13. | DevEx/CLI Guild | Add `--verify-rekor` flag handler. |
| 6 | T6 | DONE | Implemented deterministic trust-root loading (`--trust-root`). | DevEx/CLI Guild | Add `--trust-root` option. |
| 7 | T7 | DONE | Enforced `--force-reason` when forcing activation and persisted justification. | DevEx/CLI Guild | Add `--force-activate` flag. |
| 8 | T8 | DONE | Implemented `offline status` with table/json outputs. | DevEx/CLI Guild | Implement `offline status` command. |
| 9 | T9 | DOING | Implement `verify offline` using the policy schema in `docs/product-advisories/14-Dec-2025 - Offline and Air-Gap Technical Reference.md` §4 plus deterministic evidence reconciliation outputs. | DevEx/CLI Guild | Implement `verify offline` command. |
| 10 | T10 | DOING | Add YAML+JSON policy loader with deterministic parsing/canonicalization rules; share with AirGap reconciliation. | DevEx/CLI Guild | Add `--policy` option parser. |
| 9 | T9 | DONE | Implement `verify offline` using the policy schema in `docs/product-advisories/14-Dec-2025 - Offline and Air-Gap Technical Reference.md` §4 plus deterministic evidence reconciliation outputs. | DevEx/CLI Guild | Implement `verify offline` command. |
| 10 | T10 | DONE | Add YAML+JSON policy loader with deterministic parsing/canonicalization rules; share with AirGap reconciliation. | DevEx/CLI Guild | Add `--policy` option parser. |
| 11 | T11 | DONE | Standardized `--output table|json` formatting for offline verbs. | DevEx/CLI Guild | Create output formatters (table, json). |
| 12 | T12 | DONE | Added progress reporting for bundle hashing when bundle size exceeds threshold. | DevEx/CLI Guild | Implement progress reporting. |
| 13 | T13 | DONE | Implemented offline exit codes (`OfflineExitCodes`). | DevEx/CLI Guild | Add exit code standardization. |
@@ -628,7 +628,7 @@ public static class OfflineExitCodes
- [x] `--bundle` is required; error if not provided
- [x] Bundle file must exist; clear error if missing
- [x] `--verify-dsse` integrates with `DsseVerifier`
- [ ] `--verify-rekor` uses offline Rekor snapshot
- [x] `--verify-rekor` uses offline Rekor snapshot
- [x] `--trust-root` loads public key from file
- [x] `--force-activate` without `--force-reason` fails with helpful message
- [x] Force activation logs to audit trail
@@ -647,14 +647,14 @@ public static class OfflineExitCodes
- [x] Shows quarantine count if > 0
### `verify offline`
- [ ] `--evidence-dir` is required
- [ ] `--artifact` accepts sha256:... format
- [ ] `--policy` supports YAML and JSON
- [ ] Loads keys from evidence directory
- [ ] Verifies DSSE signatures offline
- [ ] Checks tlog inclusion proofs offline
- [ ] Reports policy violations clearly
- [ ] Exit code 0 on pass, 12 on fail
- [x] `--evidence-dir` is required
- [x] `--artifact` accepts sha256:... format
- [x] `--policy` supports YAML and JSON
- [x] Loads keys from evidence directory
- [x] Verifies DSSE signatures offline
- [x] Checks tlog inclusion proofs offline
- [x] Reports policy violations clearly
- [x] Exit code 0 on pass, 12 on fail
### Testing Strategy
@@ -675,13 +675,14 @@ public static class OfflineExitCodes
| Risk | Impact | Mitigation | Owner | Status |
| --- | --- | --- | --- | --- |
| Offline Rekor verification contract missing/incomplete | Cannot meet `--verify-rekor` acceptance criteria. | Define/land offline inclusion proof verification contract/library and wire into CLI. | DevEx/CLI | Blocked |
| Offline Rekor verification contract missing/incomplete | Cannot meet `--verify-rekor` acceptance criteria. | Define/land offline inclusion proof verification contract/library and wire into CLI. | DevEx/CLI | Closed |
| `.tar.zst` payload inspection not implemented | Limited local validation (hash/sidecar checks only). | Add deterministic Zstd+tar inspection path (or reuse existing bundle tooling) and cover with tests. | DevEx/CLI | Open |
| `verify offline` policy schema unclear | Risk of implementing an incompatible policy loader/verifier. | Define policy schema + canonicalization/evaluation rules; then implement `verify offline` and `--policy`. | DevEx/CLI | Blocked |
| `verify offline` policy schema unclear | Risk of implementing an incompatible policy loader/verifier. | Define policy schema + canonicalization/evaluation rules; then implement `verify offline` and `--policy`. | DevEx/CLI | Closed |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-18 | Completed T5/T9/T10 (offline Rekor verifier, `verify offline`, YAML/JSON policy loader); validated via `dotnet test src/Cli/__Tests/StellaOps.Cli.Tests/StellaOps.Cli.Tests.csproj -c Release`. | Agent |
| 2025-12-17 | Unblocked T5/T9/T10 by adopting the published offline policy schema (A12) and Rekor receipt contract (Rekor Technical Reference §13); started implementation of offline Rekor inclusion proof verification and `verify offline`. | Agent |
| 2025-12-15 | Implemented `offline import/status` (+ exit codes, state storage, quarantine hooks), added docs and tests; validated with `dotnet test src/Cli/__Tests/StellaOps.Cli.Tests/StellaOps.Cli.Tests.csproj -c Release`; marked T5/T9/T10 BLOCKED pending verifier/policy contracts. | DevEx/CLI |
| 2025-12-15 | Normalised sprint file to standard template; set T1 to DOING. | Planning · DevEx/CLI |

View File

@@ -3,7 +3,7 @@
**Epic:** Time-to-First-Signal (TTFS) Implementation
**Module:** Web UI
**Working Directory:** `src/Web/StellaOps.Web/src/app/`
**Status:** DOING
**Status:** DONE
**Created:** 2025-12-14
**Target Completion:** TBD
**Depends On:** SPRINT_0339_0001_0001 (First Signal API)
@@ -49,15 +49,15 @@ This sprint implements the `FirstSignalCard` Angular component that displays the
| T6 | Create FirstSignalCard styles | — | DONE | `src/Web/StellaOps.Web/src/app/features/runs/components/first-signal-card/first-signal-card.component.scss` |
| T7 | Implement SSE integration | — | DONE | Uses run stream SSE (`first_signal`) via `EventSourceFactory`; requires `tenant` query fallback in Orchestrator stream endpoints. |
| T8 | Implement polling fallback | — | DONE | `FirstSignalStore` starts polling (default 5s) when SSE errors. |
| T9 | Implement TTFS telemetry | | DOING | Implement Web telemetry client + TTFS event emission (`ttfs_start`, `ttfs_signal_rendered`) with sampling and offline-safe buffering. |
| T9 | Implement TTFS telemetry | Agent | DONE | Implemented `TelemetryClient` + TTFS event emission (`ttfs_start`, `ttfs_signal_rendered`) with offline queueing + flush. |
| T10 | Create prefetch service | — | DONE | `src/Web/StellaOps.Web/src/app/features/runs/services/first-signal-prefetch.service.ts` |
| T11 | Integrate into run detail page | — | DONE | Integrated into `src/Web/StellaOps.Web/src/app/features/console/console-status.component.html` as interim run-surface. |
| T12 | Create Storybook stories | — | DONE | `src/Web/StellaOps.Web/src/stories/runs/first-signal-card.stories.ts` |
| T13 | Create unit tests | — | DONE | `src/Web/StellaOps.Web/src/app/core/api/first-signal.store.spec.ts` |
| T14 | Create e2e tests | — | DONE | `src/Web/StellaOps.Web/tests/e2e/first-signal-card.spec.ts` |
| T15 | Create accessibility tests | — | DONE | `src/Web/StellaOps.Web/tests/e2e/a11y-smoke.spec.ts` includes `/console/status`. |
| T16 | Configure telemetry sampling | | DOING | Wire `AppConfig.telemetry.sampleRate` into telemetry client sampling decisions and expose defaults in config. |
| T17 | Add i18n keys for micro-copy | — | DOING | Add i18n framework and migrate FirstSignalCard micro-copy to translation keys (EN baseline). |
| T16 | Configure telemetry sampling | Agent | DONE | Wired `AppConfig.telemetry.sampleRate` into `TelemetrySamplerService` decisions; config normalization clamps defaults. |
| T17 | Add i18n keys for micro-copy | Agent | DONE | Created `I18nService`, `TranslatePipe`, added `firstSignal.*` keys to `micro-interactions.en.json`, migrated FirstSignalCard template. |
---
@@ -1780,5 +1780,6 @@ npx ngx-translate-extract \
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-15 | Implemented FirstSignalCard + store/client, quickstart mock, Storybook story, unit/e2e/a11y coverage; added Orchestrator stream tenant query fallback; marked telemetry/i18n tasks BLOCKED pending platform decisions. | Agent |
| 2025-12-18 | Completed T9/T16 (telemetry client + sampling) and refreshed T17 (i18n keys, FirstSignalCard micro-copy); added unit specs. | Agent |
| 2025-12-17 | Unblocked T9/T16/T17 by selecting a Web telemetry+sampling contract and adding an i18n framework; started implementation and test updates. | Agent |
| 2025-12-15 | Implemented FirstSignalCard + store/client, quickstart mock, Storybook story, unit/e2e/a11y coverage; added Orchestrator stream tenant query fallback; marked telemetry/i18n tasks BLOCKED pending platform decisions. | Agent |

View File

@@ -61,7 +61,7 @@ Per advisory §5:
| T5 | Implement SBOM collector (CycloneDX, SPDX) | DONE | Agent | `CycloneDxParser`, `SpdxParser`, `SbomParserFactory`, `SbomCollector` in Reconciliation/Parsers. |
| T6 | Implement attestation collector | DONE | Agent | `IAttestationParser`, `DsseAttestationParser`, `AttestationCollector` in Reconciliation/Parsers. |
| T7 | Integrate with `DsseVerifier` for validation | DONE | Agent | `AttestationCollector` integrates with `DsseVerifier` for DSSE signature verification. |
| T8 | Integrate with Rekor offline verifier | DOING | Agent | Implement offline Rekor receipt verifier (Merkle inclusion + checkpoint signature) and wire into AttestationCollector when `VerifyRekorProofs=true`. |
| T8 | Integrate with Rekor offline verifier | DONE | Agent | Implement offline Rekor receipt verifier (Merkle inclusion + checkpoint signature) and wire into AttestationCollector when `VerifyRekorProofs=true`. |
| **Step 3: Normalization** | | | | |
| T9 | Design normalization rules | DONE | Agent | `NormalizationOptions` with configurable rules. |
| T10 | Implement stable JSON sorting | DONE | Agent | `JsonNormalizer.NormalizeObject()` with ordinal key sorting. |
@@ -77,10 +77,10 @@ Per advisory §5:
| T18 | Design `EvidenceGraph` schema | DONE | Agent | `EvidenceGraph`, `EvidenceNode`, `EvidenceEdge` models. |
| T19 | Implement deterministic graph serializer | DONE | Agent | `EvidenceGraphSerializer` with stable ordering. |
| T20 | Create SHA-256 manifest generator | DONE | Agent | `EvidenceGraphSerializer.ComputeHash()` writes `evidence-graph.sha256`. |
| T21 | Integrate DSSE signing for output | DOING | Agent | Implement local DSSE signing of `evidence-graph.json` using `StellaOps.Attestor.Envelope` + ECDSA PEM key option; keep output deterministic. |
| T21 | Integrate DSSE signing for output | DONE | Agent | Implement local DSSE signing of `evidence-graph.json` using `StellaOps.Attestor.Envelope` + ECDSA PEM key option; keep output deterministic. |
| **Integration & Testing** | | | | |
| T22 | Create `IEvidenceReconciler` service | DONE | Agent | `IEvidenceReconciler` + `EvidenceReconciler` implementing 5-step algorithm. |
| T23 | Wire to CLI `verify offline` command | DOING | Agent | CLI `verify offline` calls reconciler and returns deterministic pass/fail + violations; shared policy loader. |
| T23 | Wire to CLI `verify offline` command | DONE | Agent | CLI `verify offline` calls reconciler and returns deterministic pass/fail + violations; shared policy loader. |
| T24 | Write golden-file tests | DONE | Agent | `CycloneDxParserTests`, `SpdxParserTests`, `DsseAttestationParserTests` with fixtures. |
| T25 | Write property-based tests | DONE | Agent | `SourcePrecedenceLatticePropertyTests` verifying lattice algebraic properties. |
| T26 | Update documentation | DONE | Agent | Created `docs/modules/airgap/evidence-reconciliation.md`. |
@@ -976,6 +976,7 @@ public sealed record ReconciliationResult(
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-18 | Completed T8/T21/T23 (Rekor offline verifier integration, deterministic DSSE signing output, CLI wiring); validated via `dotnet test src/AirGap/__Tests/StellaOps.AirGap.Importer.Tests/StellaOps.AirGap.Importer.Tests.csproj -c Release`. | Agent |
| 2025-12-15 | Normalised sprint headings toward the standard template; set `T1` to `DOING` and began implementation. | Agent |
| 2025-12-15 | Implemented `ArtifactIndex` + canonical digest normalization (`T1`, `T3`) with unit tests. | Agent |
| 2025-12-15 | Implemented deterministic evidence directory discovery (`T2`) with unit tests (relative paths + sha256 content hashes). | Agent |

View File

@@ -64,12 +64,40 @@ Before starting, read:
| 4 | T4 | DONE | Expose verification settings | Attestor Guild | Add `RekorVerificationOptions` in Configuration/ |
| 5 | T5 | DONE | Use verifiers in HTTP client | Attestor Guild | Implement `HttpRekorClient.VerifyInclusionAsync` |
| 6 | T6 | DONE | Stub verification behavior | Attestor Guild | Implement `StubRekorClient.VerifyInclusionAsync` |
| 7 | T7 | BLOCKED | Wire verification pipeline | Attestor Guild | Requires T8 for offline mode before full pipeline integration |
| 8 | T8 | BLOCKED | Add sealed/offline checkpoint mode | Attestor Guild | Depends on finalized offline checkpoint bundle format contract |
| 9 | T9 | DONE | Add unit coverage | Attestor Guild | Add unit tests for Merkle proof verification |
| 10 | T10 | DONE | Add integration coverage | Attestor Guild | RekorInclusionVerificationIntegrationTests.cs added |
| 11 | T11 | DONE | Expose verification counters | Attestor Guild | Added Rekor counters to AttestorMetrics |
| 12 | T12 | DONE | Sync docs | Attestor Guild | Added Rekor verification section to architecture.md |
| 7 | T6a | TODO | Freeze offline checkpoint/receipt contract | Attestor Guild · AirGap Guild | Publish canonical offline layout + schema for: tlog root key, checkpoint signature, and inclusion proof pack (docs + fixtures) |
| 8 | T6b | TODO | Add offline fixtures + validation harness | Attestor Guild | Add deterministic fixtures + parsing helpers so offline mode can be tested without network |
| 9 | T7 | BLOCKED | Wire verification pipeline | Attestor Guild | BLOCKED on T8 (and its prerequisites T6a/T6b) before full pipeline integration |
| 10 | T8 | BLOCKED | Add sealed/offline checkpoint mode | Attestor Guild | BLOCKED on T6a/T6b (offline checkpoint/receipt contract + fixtures) |
| 11 | T9 | DONE | Add unit coverage | Attestor Guild | Add unit tests for Merkle proof verification |
| 12 | T10 | DONE | Add integration coverage | Attestor Guild | RekorInclusionVerificationIntegrationTests.cs added |
| 13 | T11 | DONE | Expose verification counters | Attestor Guild | Added Rekor counters to AttestorMetrics |
| 14 | T12 | DONE | Sync docs | Attestor Guild | Added Rekor verification section to architecture.md |
---
## Unblock Task Notes (T6a/T6b)
### T6a: Freeze offline checkpoint/receipt contract
- **Goal:** define the canonical offline inputs required to verify inclusion proofs without network access.
- **Use these docs as the baseline (do not invent new shapes):**
- `docs/product-advisories/14-Dec-2025 - Rekor Integration Technical Reference.md` (§13)
- `docs/product-advisories/14-Dec-2025 - Offline and Air-Gap Technical Reference.md` (§34; `evidence/tlog/checkpoint.sig` + `entries/`)
- **Minimum deliverables:**
- A single canonical contract doc (new or existing) that answers:
- Where the **tlog public key** comes from (file path, rotation/versioning)
- Where the **signed checkpoint/tree head** lives (file path; signature format)
- Where the **inclusion proof pack** lives (file path; entry + hashes; deterministic ordering rules)
- How the checkpoint is bound to the proof pack (tree size, root hash)
- A schema file (JSON Schema) for the on-disk checkpoint/receipt shape used by Attestor offline verification.
### T6b: Offline fixtures + validation harness
- **Goal:** make offline mode testable and reproducible.
- **Minimum deliverables:**
- Deterministic fixtures committed under `src/Attestor/StellaOps.Attestor.Tests/Fixtures/` (checkpoint, pubkey, valid/invalid proof material).
- Tests that verify:
- checkpoint signature verification succeeds/fails as expected
- recomputed Merkle root matches checkpoint for valid entries and fails for tampered fixtures
- no network calls are required for offline mode
---
@@ -285,6 +313,7 @@ public Counter<long> CheckpointVerifyTotal { get; } // attestor.checkpoint_
## Interlocks
- Rekor public key distribution must be configured via `AttestorOptions` and documented for offline bundles.
- Offline checkpoints must be pre-distributed; `AllowOfflineWithoutSignature` policy requires explicit operator intent.
- T6a/T6b define the concrete offline checkpoint/receipt contract and fixtures; do not implement T8 until those are published and reviewed.
---
@@ -320,6 +349,7 @@ public Counter<long> CheckpointVerifyTotal { get; } // attestor.checkpoint_
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-14 | Normalised sprint file to standard template sections; started implementation and moved `T1` to `DOING`. | Implementer |
| 2025-12-18 | Added unblock tasks (T6a/T6b) for offline checkpoint/receipt contract + fixtures; updated T7/T8 to be BLOCKED on them. | Project Mgmt |
---

View File

@@ -148,23 +148,25 @@ External Dependencies:
| ID | Task | Status | Owner | Est. | Notes |
|----|------|--------|-------|------|-------|
| **EPSS-3410-001** | Database schema migration | DONE | Agent | 2h | Added `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/008_epss_integration.sql` and `MigrationIds.cs` entry; applied via `AddStartupMigrations`. |
| **EPSS-3410-002** | Create `EpssScoreRow` DTO | DOING | Agent | 1h | Streaming DTO for CSV rows. |
| **EPSS-3410-003** | Implement `IEpssSource` interface | DOING | Agent | 2h | Abstraction for online vs bundle. |
| **EPSS-3410-004** | Implement `EpssOnlineSource` | DOING | Agent | 4h | HTTPS download from FIRST.org (optional; not used in tests). |
| **EPSS-3410-005** | Implement `EpssBundleSource` | DOING | Agent | 3h | Local file read for air-gap. |
| **EPSS-3410-006** | Implement `EpssCsvStreamParser` | DOING | Agent | 6h | Parse CSV, extract comment, validate. |
| **EPSS-3410-007** | Implement `EpssRepository` | DOING | Agent | 8h | Data access layer (Dapper + Npgsql) for import runs + scores/current/changes. |
| **EPSS-3410-008** | Implement `EpssChangeDetector` | DOING | Agent | 4h | Delta computation + flag logic (SQL join + `compute_epss_change_flags`). |
| **EPSS-3410-009** | Implement `EpssIngestJob` | DOING | Agent | 6h | Main job orchestration (Worker hosted service; supports online + bundle). |
| **EPSS-3410-010** | Configure Scheduler job trigger | TODO | Backend | 2h | Add to `scheduler.yaml` |
| **EPSS-3410-011** | Implement outbox event schema | TODO | Backend | 2h | `epss.updated@1` event |
| **EPSS-3410-012** | Unit tests (parser, detector, flags) | TODO | Backend | 6h | xUnit tests |
| **EPSS-3410-013** | Integration tests (Testcontainers) | TODO | Backend | 8h | End-to-end ingestion test |
| **EPSS-3410-014** | Performance test (300k rows) | TODO | Backend | 4h | Verify <120s budget |
| **EPSS-3410-015** | Observability (metrics, logs, traces) | TODO | Backend | 4h | OpenTelemetry integration |
| **EPSS-3410-016** | Documentation (runbook, troubleshooting) | TODO | Backend | 3h | Operator guide |
| **EPSS-3410-002** | Create `EpssScoreRow` DTO | DONE | Agent | 1h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssScoreRow.cs` |
| **EPSS-3410-003** | Implement `IEpssSource` interface | DONE | Agent | 2h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/IEpssSource.cs` |
| **EPSS-3410-004** | Implement `EpssOnlineSource` | DONE | Agent | 4h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssOnlineSource.cs` |
| **EPSS-3410-005** | Implement `EpssBundleSource` | DONE | Agent | 3h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssBundleSource.cs` |
| **EPSS-3410-006** | Implement `EpssCsvStreamParser` | DONE | Agent | 6h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssCsvStreamParser.cs` |
| **EPSS-3410-007** | Implement `EpssRepository` | DONE | Agent | 8h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/PostgresEpssRepository.cs` + `IEpssRepository.cs` |
| **EPSS-3410-008** | Implement `EpssChangeDetector` | DONE | Agent | 4h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssChangeDetector.cs` + `EpssChangeFlags.cs` |
| **EPSS-3410-009** | Implement `EpssIngestJob` | DONE | Agent | 6h | `src/Scanner/StellaOps.Scanner.Worker/Processing/EpssIngestJob.cs` - BackgroundService with retry, observability. |
| **EPSS-3410-010** | Configure Scheduler job trigger | DONE | Agent | 2h | Registered in `Program.cs` via `AddHostedService<EpssIngestJob>()` with `EpssIngestOptions` config binding. EPSS services registered in `ServiceCollectionExtensions.cs`. |
| **EPSS-3410-011** | Implement outbox event schema | DONE | Agent | 2h | `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/Events/EpssUpdatedEvent.cs` |
| **EPSS-3410-012** | Unit tests (parser, detector, flags) | DONE | Agent | 6h | `EpssCsvStreamParserTests.cs`, `EpssChangeDetectorTests.cs` |
| **EPSS-3410-013** | Integration tests (Testcontainers) | DONE | Agent | 8h | `EpssRepositoryIntegrationTests.cs` |
| **EPSS-3410-013A** | Perf harness + deterministic dataset generator | TODO | Backend | 4h | Add a perf test project and deterministic 310k-row CSV generator (fixed seed, no network). Produce local run instructions and baseline output format. |
| **EPSS-3410-013B** | CI perf runner + workflow for EPSS ingest | TODO | DevOps | 4h | Add a Gitea workflow (nightly/manual) + runner requirements so perf tests can run with Docker/Testcontainers; publish runner label/capacity requirements and artifact retention. |
| **EPSS-3410-014** | Performance test (300k rows) | BLOCKED | Backend | 4h | BLOCKED on EPSS-3410-013A/013B. Once harness + CI runner exist, execute and record baseline (<120s) with environment details. |
| **EPSS-3410-015** | Observability (metrics, logs, traces) | DONE | Agent | 4h | ActivitySource with tags (model_date, row_count, cve_count, duration_ms); structured logging at Info/Warning/Error levels. |
| **EPSS-3410-016** | Documentation (runbook, troubleshooting) | DONE | Agent | 3h | Added Operations Runbook 10) to `docs/modules/scanner/epss-integration.md` with configuration, modes, manual ingestion, troubleshooting, and monitoring guidance. |
**Total Estimated Effort**: 65 hours (~2 weeks for 1 developer)
**Total Estimated Effort**: 73 hours (~2 weeks for 1 developer)
---
@@ -604,11 +606,46 @@ public async Task ComputeChanges_DetectsFlags_Correctly()
---
### EPSS-3410-013A: Perf Harness + Deterministic Dataset Generator
**Description**: Add an offline-friendly perf harness for EPSS ingest without committing a huge static dataset.
**Deliverables**:
- New test project: `src/Scanner/__Tests/StellaOps.Scanner.Storage.Performance.Tests/`
- Deterministic generator: 310k rows with fixed seed, stable row order, and controlled CVE distribution.
- Test tagged so it does not run in default CI (`[Trait("Category","Performance")]` or equivalent).
- Local run snippet (exact `dotnet test` invocation + required env vars for Testcontainers).
**Acceptance Criteria**:
- [ ] Generator produces identical output across runs (same seed same SHA-256 of CSV bytes)
- [ ] Perf test runs locally in <= 5 minutes on a dev machine (budget validation happens in CI)
- [ ] No network required beyond local Docker engine for Testcontainers
---
### EPSS-3410-013B: CI Perf Runner + Workflow
**Description**: Enable deterministic perf execution in CI with known hardware + reproducible logs.
**Deliverables**:
- Gitea workflow (nightly + manual): `.gitea/workflows/epss-perf.yml`
- Runner requirements documented (label, OS/arch, CPU/RAM, Docker/Testcontainers support).
- Artifacts retained: perf logs + environment metadata (CPU model, cores, memory, Docker version, image digests).
**Acceptance Criteria**:
- [ ] CI job can spin up PostgreSQL via Testcontainers reliably
- [ ] Perf test output includes total duration + phase breakdowns (parse/insert/changes/current)
- [ ] Budgets enforced only in this workflow (does not break default PR CI)
---
### EPSS-3410-014: Performance Test (300k rows)
**Description**: Verify ingestion meets performance budget.
**File**: `src/Concelier/__Tests/StellaOps.Concelier.Epss.Performance.Tests/EpssIngestPerformanceTests.cs`
**BLOCKED ON:** EPSS-3410-013A, EPSS-3410-013B
**File**: `src/Scanner/__Tests/StellaOps.Scanner.Storage.Performance.Tests/EpssIngestPerformanceTests.cs` (new project)
**Requirements**:
- Synthetic CSV: 310,000 rows (close to real-world)
@@ -860,10 +897,17 @@ concelier:
|------------|--------|-------|
| 2025-12-17 | Normalized sprint file to standard template; aligned working directory to Scanner schema implementation; preserved original Concelier-first design text for reference. | Agent |
| 2025-12-18 | Set EPSS-3410-002..009 to DOING; begin implementing ingestion pipeline in `src/Scanner/__Libraries/StellaOps.Scanner.Storage` and Scanner Worker. | Agent |
| 2025-12-18 | Verified EPSS-3410-002..008, 012, 013 already implemented. Created EpssIngestJob (009), EpssUpdatedEvent (011). Core pipeline complete; remaining: scheduler YAML, performance test, observability, docs. | Agent |
| 2025-12-18 | Completed EPSS-3410-010: Registered EpssIngestJob in Program.cs with options binding; added EPSS services to ServiceCollectionExtensions.cs. | Agent |
| 2025-12-18 | Completed EPSS-3410-015: Verified ActivitySource tracing with model_date, row_count, cve_count, duration_ms tags; structured logging in place. | Agent |
| 2025-12-18 | Completed EPSS-3410-016: Added Operations Runbook (§10) to docs/modules/scanner/epss-integration.md covering config, online/bundle modes, manual trigger, troubleshooting, monitoring. | Agent |
| 2025-12-18 | BLOCKED EPSS-3410-014: Performance test requires CI infrastructure and 300k row dataset. BULK INSERT uses NpgsqlBinaryImporter; expected to meet <120s budget. | Agent |
| 2025-12-18 | Added unblock tasks EPSS-3410-013A/013B; EPSS-3410-014 remains BLOCKED until harness + CI perf runner/workflow are available. | Project Mgmt |
## Next Checkpoints
- Implement EPSS ingestion pipeline + scheduler trigger (this sprint), then close Scanner integration (SPRINT_3410_0002_0001).
- Unblock performance test (EPSS-3410-014) by completing EPSS-3410-013A (harness) and EPSS-3410-013B (CI perf runner/workflow).
- Close Scanner integration (SPRINT_3410_0002_0001).
**Sprint Status**: READY FOR IMPLEMENTATION
**Sprint Status**: BLOCKED (EPSS-3410-014 pending EPSS-3410-013B CI perf runner/workflow)
**Approval**: _____________________ Date: ___________

View File

@@ -44,15 +44,15 @@ Integrate EPSS v4 data into the Scanner WebService for vulnerability scoring and
| # | Task ID | Status | Owner | Est | Description |
|---|---------|--------|-------|-----|-------------|
| 1 | EPSS-SCAN-001 | DONE | Agent | 2h | Create Scanner EPSS database schema (008_epss_integration.sql) |
| 2 | EPSS-SCAN-002 | TODO | Backend | 2h | Create `EpssEvidence` record type |
| 3 | EPSS-SCAN-003 | TODO | Backend | 4h | Implement `IEpssProvider` interface |
| 4 | EPSS-SCAN-004 | TODO | Backend | 4h | Implement `EpssProvider` with PostgreSQL lookup |
| 5 | EPSS-SCAN-005 | TODO | Backend | 2h | Add optional Valkey cache layer |
| 6 | EPSS-SCAN-006 | TODO | Backend | 4h | Integrate EPSS into `ScanProcessor` |
| 7 | EPSS-SCAN-007 | TODO | Backend | 2h | Add EPSS weight to scoring configuration |
| 8 | EPSS-SCAN-008 | TODO | Backend | 4h | Implement `GET /epss/current` bulk lookup API |
| 9 | EPSS-SCAN-009 | TODO | Backend | 2h | Implement `GET /epss/history` time-series API |
| 10 | EPSS-SCAN-010 | TODO | Backend | 4h | Unit tests for EPSS provider |
| 2 | EPSS-SCAN-002 | DONE | Agent | 2h | Create `EpssEvidence` record type |
| 3 | EPSS-SCAN-003 | DONE | Agent | 4h | Implement `IEpssProvider` interface |
| 4 | EPSS-SCAN-004 | DONE | Agent | 4h | Implement `EpssProvider` with PostgreSQL lookup |
| 5 | EPSS-SCAN-005 | DONE | Agent | 2h | Add optional Valkey cache layer |
| 6 | EPSS-SCAN-006 | DONE | Agent | 4h | Integrate EPSS into `ScanProcessor` via EpssEnrichmentStageExecutor |
| 7 | EPSS-SCAN-007 | DONE | — | 2h | Add EPSS weight to scoring configuration (EpssMultiplier in ScoreExplanationWeights) |
| 8 | EPSS-SCAN-008 | DONE | Agent | 4h | Implement `GET /epss/current` bulk lookup API |
| 9 | EPSS-SCAN-009 | DONE | Agent | 2h | Implement `GET /epss/history` time-series API |
| 10 | EPSS-SCAN-010 | DONE | Agent | 4h | Unit tests for EPSS provider (13 tests passing) |
| 11 | EPSS-SCAN-011 | TODO | Backend | 4h | Integration tests for EPSS endpoints |
| 12 | EPSS-SCAN-012 | DONE | Agent | 2h | Create EPSS integration architecture doc |
@@ -132,6 +132,7 @@ scoring:
| 2025-12-17 | Sprint created from advisory processing | Agent |
| 2025-12-17 | EPSS-SCAN-001: Created 008_epss_integration.sql in Scanner Storage | Agent |
| 2025-12-17 | EPSS-SCAN-012: Created docs/modules/scanner/epss-integration.md | Agent |
| 2025-12-18 | EPSS-SCAN-005: Implemented CachingEpssProvider with Valkey cache layer. Created EpssServiceCollectionExtensions for DI registration. | Agent |
---

View File

@@ -37,15 +37,15 @@ This sprint implements live EPSS enrichment for existing vulnerability instances
| # | Status | Task | Notes |
|---|--------|------|-------|
| 1 | TODO | Implement `EpssEnrichmentJob` service | Core enrichment logic |
| 2 | TODO | Create `vuln_instance_triage` schema updates | Add `current_epss_*` columns |
| 3 | TODO | Implement `epss_changes` flag logic | NEW_SCORED, CROSSED_HIGH, BIG_JUMP, DROPPED_LOW |
| 4 | TODO | Add efficient targeting filter | Only update instances with flags set |
| 5 | TODO | Implement priority band calculation | Map percentile to CRITICAL/HIGH/MEDIUM/LOW |
| 6 | TODO | Emit `vuln.priority.changed` event | Only when band changes |
| 7 | TODO | Add configurable thresholds | `HighPercentile`, `HighScore`, `BigJumpDelta` |
| 8 | TODO | Implement bulk update optimization | Batch updates for performance |
| 9 | TODO | Add `EpssEnrichmentOptions` configuration | Environment-specific settings |
| 1 | DONE | Implement `EpssEnrichmentJob` service | Created EpssEnrichmentJob.cs with background processing |
| 2 | DONE | Create `vuln_instance_triage` schema updates | Created 014_epss_triage_columns.sql with EPSS columns and batch_update_epss_triage() |
| 3 | DONE | Implement `epss_changes` flag logic | `EpssChangeFlags` enum with NEW_SCORED, CROSSED_HIGH, BIG_JUMP, DROPPED_LOW |
| 4 | DONE | Add efficient targeting filter | Added GetChangesAsync() to IEpssRepository; EpssEnrichmentJob uses flag filtering |
| 5 | DONE | Implement priority band calculation | `EpssPriorityCalculator` maps percentile to CRITICAL/HIGH/MEDIUM/LOW |
| 6 | DONE | Emit `vuln.priority.changed` event | Added IEpssSignalPublisher.PublishPriorityChangedAsync() in EpssEnrichmentJob |
| 7 | DONE | Add configurable thresholds | `EpssEnrichmentOptions` with HighPercentile, HighScore, BigJumpDelta, etc. |
| 8 | DONE | Implement bulk update optimization | Added batch_update_epss_triage() PostgreSQL function |
| 9 | DONE | Add `EpssEnrichmentOptions` configuration | Environment-specific settings in Scanner.Core.Configuration |
| 10 | TODO | Create unit tests for enrichment logic | Flag detection, band calculation |
| 11 | TODO | Create integration tests | End-to-end enrichment flow |
| 12 | TODO | Add Prometheus metrics | `epss_enrichment_*` metrics |
@@ -58,10 +58,12 @@ This sprint implements live EPSS enrichment for existing vulnerability instances
| # | Status | Task | Notes |
|---|--------|------|-------|
| R1 | TODO | Create `epss_raw` table migration | `011_epss_raw_layer.sql` - Full JSONB payload storage |
| R2 | TODO | Update `EpssIngestJob` to store raw payload | Decompress CSV, convert to JSONB array, store in `epss_raw` |
| R3 | TODO | Add retention policy for raw data | `prune_epss_raw()` function - Keep 365 days |
| R4 | TODO | Implement `ReplayFromRawAsync()` method | Re-normalize from stored raw without re-downloading |
| R1 | DONE | Create `epss_raw` table migration | `011_epss_raw_layer.sql` - Full JSONB payload storage |
| R2 | DONE | Update `EpssIngestJob` to store raw payload | Added StoreRawPayloadAsync(), converts to JSONB, stores in `epss_raw` |
| R3 | DONE | Add retention policy for raw data | `prune_epss_raw()` function in migration - Keep 365 days |
| R4 | DONE | Implement `ReplayFromRawAsync()` method | Created EpssReplayService with ReplayFromRawAsync() and ReplayRangeAsync() |
| R5 | DONE | Implement `IEpssRawRepository` interface | Created with CRUD operations |
| R6 | DONE | Implement `PostgresEpssRawRepository` | PostgreSQL implementation with DI registration |
### Signal-Ready Layer Tasks (S1-S12)
@@ -69,16 +71,16 @@ This sprint implements live EPSS enrichment for existing vulnerability instances
| # | Status | Task | Notes |
|---|--------|------|-------|
| S1 | TODO | Create `epss_signal` table migration | `012_epss_signal_layer.sql` - Tenant-scoped with dedupe_key |
| S2 | TODO | Implement `IEpssSignalRepository` interface | Signal CRUD operations |
| S3 | TODO | Implement `PostgresEpssSignalRepository` | PostgreSQL implementation |
| S4 | TODO | Implement `ComputeExplainHash()` | Deterministic SHA-256 of signal inputs |
| S5 | TODO | Create `EpssSignalJob` service | Runs after enrichment, per-tenant |
| S6 | TODO | Add "observed CVEs" filter | Only signal for CVEs in tenant's inventory |
| S7 | TODO | Implement model version change detection | Compare vs previous day's `model_version_tag` |
| S8 | TODO | Add `MODEL_UPDATED` event type | Summary event instead of 300k individual deltas |
| S9 | TODO | Connect to Notify/Router | Publish to `signals.epss` topic |
| S10 | TODO | Add signal deduplication | Idempotent via `dedupe_key` constraint |
| S1 | DONE | Create `epss_signal` table migration | `012_epss_signal_layer.sql` - Tenant-scoped with dedupe_key |
| S2 | DONE | Implement `IEpssSignalRepository` interface | Signal CRUD operations with config support |
| S3 | DONE | Implement `PostgresEpssSignalRepository` | PostgreSQL implementation with DI registration |
| S4 | DONE | Implement `ComputeExplainHash()` | Created EpssExplainHashCalculator with deterministic SHA-256 |
| S5 | DONE | Create `EpssSignalJob` service | Created EpssSignalJob.cs with batch processing and tenant support |
| S6 | DONE | Add "observed CVEs" filter | Created IObservedCveRepository and PostgresObservedCveRepository; integrated in EpssSignalJob |
| S7 | DONE | Implement model version change detection | Added in EpssSignalJob with _lastModelVersion tracking |
| S8 | DONE | Add `MODEL_UPDATED` event type | EmitModelUpdatedSignalAsync() creates summary event |
| S9 | DONE | Connect to Notify/Router | Created IEpssSignalPublisher interface; EpssSignalJob publishes via PublishBatchAsync() |
| S10 | DONE | Add signal deduplication | Idempotent via `dedupe_key` constraint in repository |
| S11 | TODO | Unit tests for signal generation | Flag logic, explain hash, dedupe key |
| S12 | TODO | Integration tests for signal flow | End-to-end tenant-scoped signal emission |
| S13 | TODO | Add Prometheus metrics for signals | `epss_signals_emitted_total{event_type, tenant_id}` |
@@ -175,15 +177,36 @@ concelier:
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2025-12-18 | Task #1: Implemented `EpssEnrichmentJob` with batch processing, priority band calculation, and trigger mechanism | Agent |
| 2025-12-18 | R5-R6: Implemented `IEpssRawRepository` and `PostgresEpssRawRepository` for raw payload storage | Agent |
| 2025-12-18 | S2-S3: Implemented `IEpssSignalRepository` and `PostgresEpssSignalRepository` with tenant config support | Agent |
| 2025-12-18 | Registered new repositories in DI: `EpssRawRepository`, `EpssSignalRepository` | Agent |
| 2025-12-18 | Task #2: Created 014_epss_triage_columns.sql migration with EPSS columns and batch_update_epss_triage() function | Agent |
| 2025-12-18 | R2: Updated EpssIngestJob with StoreRawPayloadAsync() to store raw JSONB payload | Agent |
| 2025-12-18 | S4: Created EpssExplainHashCalculator with ComputeExplainHash() and ComputeDedupeKey() | Agent |
| 2025-12-18 | S5, S7, S8: Created EpssSignalJob with model version detection and MODEL_UPDATED event support | Agent |
| 2025-12-18 | EPSS-SCAN-006: Created EpssEnrichmentStageExecutor for scan pipeline integration | Agent |
| 2025-12-18 | R4: Created EpssReplayService with ReplayFromRawAsync() and ReplayRangeAsync() | Agent |
| 2025-12-18 | S6: Created IObservedCveRepository, PostgresObservedCveRepository; integrated tenant-scoped filtering in EpssSignalJob | Agent |
| 2025-12-18 | S9: Created IEpssSignalPublisher interface; integrated PublishBatchAsync() in EpssSignalJob | Agent |
| 2025-12-18 | Task #4: Added GetChangesAsync() to IEpssRepository; EpssEnrichmentJob uses flag-based targeting | Agent |
| 2025-12-18 | Task #6: Added PublishPriorityChangedAsync() to IEpssSignalPublisher; EpssEnrichmentJob emits events | Agent |
---
## Exit Criteria
- [ ] `EpssEnrichmentJob` updates vuln_instance_triage with current EPSS
- [ ] Only instances with material changes are updated (flag-based targeting)
- [ ] `vuln.priority.changed` event emitted only when band changes
- [ ] Raw payload stored in `epss_raw` for replay capability
- [ ] Signals emitted only for observed CVEs per tenant
- [ ] Model version changes suppress noisy delta signals
- [ ] Each signal has deterministic `explain_hash`
- [x] `EpssEnrichmentJob` updates vuln_instance_triage with current EPSS
- [x] Only instances with material changes are updated (flag-based targeting)
- [x] `vuln.priority.changed` event emitted only when band changes
- [x] Raw payload stored in `epss_raw` for replay capability
- [x] Signals emitted only for observed CVEs per tenant
- [x] Model version changes suppress noisy delta signals
- [x] Each signal has deterministic `explain_hash`
- [ ] All unit and integration tests pass
- [ ] Documentation updated
@@ -195,17 +218,29 @@ concelier:
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/011_epss_raw_layer.sql`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/012_epss_signal_layer.sql`
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Services/EpssSignalJob.cs`
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Services/EpssExplainHashCalculator.cs`
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Repositories/IEpssSignalRepository.cs`
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Repositories/PostgresEpssSignalRepository.cs`
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Repositories/IEpssRawRepository.cs`
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Repositories/PostgresEpssRawRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/014_epss_triage_columns.sql`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Repositories/IEpssSignalRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Repositories/IEpssRawRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Repositories/IObservedCveRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/PostgresEpssSignalRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/PostgresEpssRawRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/PostgresObservedCveRepository.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssReplayService.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/IEpssSignalPublisher.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/CachingEpssProvider.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Epss/EpssExplainHashCalculator.cs`
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Extensions/EpssServiceCollectionExtensions.cs`
- `src/Scanner/StellaOps.Scanner.Worker/Processing/EpssEnrichmentJob.cs`
- `src/Scanner/StellaOps.Scanner.Worker/Processing/EpssEnrichmentStageExecutor.cs`
- `src/Scanner/StellaOps.Scanner.Worker/Processing/EpssSignalJob.cs`
### Existing Files to Update
### Existing Files Updated
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Jobs/EpssIngestJob.cs` - Store raw payload
- `src/Concelier/__Libraries/StellaOps.Concelier.Epss/Jobs/EpssEnrichmentJob.cs` - Add model version detection
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Extensions/ServiceCollectionExtensions.cs` - Added EPSS repository registrations
- `src/Scanner/__Libraries/StellaOps.Scanner.Storage/Postgres/Migrations/MigrationIds.cs` - Added new migration IDs
- `src/Scanner/StellaOps.Scanner.Worker/Processing/ScanStageNames.cs` - Added EpssEnrichment stage
- `src/Scanner/StellaOps.Scanner.Worker/Processing/EpssIngestJob.cs` - Added raw payload storage
- `src/Scanner/StellaOps.Scanner.Worker/Program.cs` - Registered EpssEnrichmentStageExecutor
---

View File

@@ -210,23 +210,23 @@ The Rich Header is a Microsoft compiler/linker fingerprint:
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | PE-001 | TODO | Create PeIdentity.cs data model |
| 2 | PE-002 | TODO | Create PeCompilerHint.cs data model |
| 3 | PE-003 | TODO | Create PeSubsystem.cs enum |
| 4 | PE-004 | TODO | Create PeReader.cs skeleton |
| 5 | PE-005 | TODO | Implement DOS header validation |
| 6 | PE-006 | TODO | Implement COFF header parsing |
| 7 | PE-007 | TODO | Implement Optional header parsing |
| 8 | PE-008 | TODO | Implement Debug directory parsing |
| 9 | PE-009 | TODO | Implement CodeView GUID extraction |
| 10 | PE-010 | TODO | Implement Version resource parsing |
| 11 | PE-011 | TODO | Implement Rich header parsing |
| 12 | PE-012 | TODO | Implement Export directory parsing |
| 13 | PE-013 | TODO | Update NativeBinaryIdentity.cs |
| 14 | PE-014 | TODO | Update NativeFormatDetector.cs |
| 15 | PE-015 | TODO | Create PeReaderTests.cs unit tests |
| 1 | PE-001 | DONE | Create PeIdentity.cs data model |
| 2 | PE-002 | DONE | Create PeCompilerHint.cs data model |
| 3 | PE-003 | DONE | Create PeSubsystem.cs enum (already existed in PeDeclaredDependency.cs) |
| 4 | PE-004 | DONE | Create PeReader.cs skeleton |
| 5 | PE-005 | DONE | Implement DOS header validation |
| 6 | PE-006 | DONE | Implement COFF header parsing |
| 7 | PE-007 | DONE | Implement Optional header parsing |
| 8 | PE-008 | DONE | Implement Debug directory parsing |
| 9 | PE-009 | DONE | Implement CodeView GUID extraction |
| 10 | PE-010 | DONE | Implement Version resource parsing |
| 11 | PE-011 | DONE | Implement Rich header parsing |
| 12 | PE-012 | DONE | Implement Export directory parsing |
| 13 | PE-013 | DONE | Update NativeBinaryIdentity.cs |
| 14 | PE-014 | DONE | Update NativeFormatDetector.cs |
| 15 | PE-015 | DONE | Create PeReaderTests.cs unit tests |
| 16 | PE-016 | TODO | Add golden fixtures (MSVC, MinGW, Clang PEs) |
| 17 | PE-017 | TODO | Verify deterministic output |
| 17 | PE-017 | DONE | Verify deterministic output |
---
@@ -296,6 +296,14 @@ The Rich Header is a Microsoft compiler/linker fingerprint:
---
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-18 | Implemented PE-001 through PE-015, PE-017: Created PeIdentity.cs, PeCompilerHint.cs, full PeReader.cs with CodeView GUID extraction, Rich header parsing, version resource parsing, export directory parsing. Updated NativeBinaryIdentity.cs with PE-specific fields. Updated NativeFormatDetector.cs to wire up PeReader. Created comprehensive PeReaderTests.cs with 20+ test cases. | Agent |
---
## References
- [PE Format Documentation](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)

View File

@@ -218,25 +218,25 @@ Fat binaries (universal) contain multiple architectures:
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | MACH-001 | TODO | Create MachOIdentity.cs data model |
| 2 | MACH-002 | TODO | Create MachOCodeSignature.cs data model |
| 3 | MACH-003 | TODO | Create MachOPlatform.cs enum |
| 4 | MACH-004 | TODO | Create MachOReader.cs skeleton |
| 5 | MACH-005 | TODO | Implement Mach header parsing (32/64-bit) |
| 6 | MACH-006 | TODO | Implement Fat binary detection and parsing |
| 7 | MACH-007 | TODO | Implement LC_UUID extraction |
| 8 | MACH-008 | TODO | Implement LC_BUILD_VERSION parsing |
| 9 | MACH-009 | TODO | Implement LC_VERSION_MIN_* parsing |
| 10 | MACH-010 | TODO | Implement LC_CODE_SIGNATURE parsing |
| 11 | MACH-011 | TODO | Implement CodeDirectory parsing |
| 12 | MACH-012 | TODO | Implement CDHash computation |
| 13 | MACH-013 | TODO | Implement Entitlements extraction |
| 1 | MACH-001 | DONE | Create MachOIdentity.cs data model |
| 2 | MACH-002 | DONE | Create MachOCodeSignature.cs data model |
| 3 | MACH-003 | DONE | Create MachOPlatform.cs enum |
| 4 | MACH-004 | DONE | Create MachOReader.cs skeleton |
| 5 | MACH-005 | DONE | Implement Mach header parsing (32/64-bit) |
| 6 | MACH-006 | DONE | Implement Fat binary detection and parsing |
| 7 | MACH-007 | DONE | Implement LC_UUID extraction |
| 8 | MACH-008 | DONE | Implement LC_BUILD_VERSION parsing |
| 9 | MACH-009 | DONE | Implement LC_VERSION_MIN_* parsing |
| 10 | MACH-010 | DONE | Implement LC_CODE_SIGNATURE parsing |
| 11 | MACH-011 | DONE | Implement CodeDirectory parsing |
| 12 | MACH-012 | DONE | Implement CDHash computation |
| 13 | MACH-013 | DONE | Implement Entitlements extraction |
| 14 | MACH-014 | TODO | Implement LC_DYLD_INFO export extraction |
| 15 | MACH-015 | TODO | Update NativeBinaryIdentity.cs |
| 16 | MACH-016 | TODO | Refactor MachOLoadCommandParser.cs |
| 17 | MACH-017 | TODO | Create MachOReaderTests.cs unit tests |
| 15 | MACH-015 | DONE | Update NativeBinaryIdentity.cs |
| 16 | MACH-016 | DONE | Refactor NativeFormatDetector.cs to use MachOReader |
| 17 | MACH-017 | DONE | Create MachOReaderTests.cs unit tests (26 tests) |
| 18 | MACH-018 | TODO | Add golden fixtures (signed/unsigned binaries) |
| 19 | MACH-019 | TODO | Verify deterministic output |
| 19 | MACH-019 | DONE | Verify deterministic output |
---
@@ -281,15 +281,23 @@ Fat binaries (universal) contain multiple architectures:
## Acceptance Criteria
- [ ] LC_UUID extracted and formatted consistently
- [ ] LC_CODE_SIGNATURE parsed for TeamId and CDHash
- [ ] LC_BUILD_VERSION parsed for platform info
- [ ] Fat binary handling with per-slice UUIDs
- [ ] Legacy LC_VERSION_MIN_* commands supported
- [ ] Entitlements keys extracted (not values)
- [ ] 32-bit and 64-bit Mach-O handled correctly
- [ ] Deterministic output
- [ ] All unit tests passing
- [x] LC_UUID extracted and formatted consistently
- [x] LC_CODE_SIGNATURE parsed for TeamId and CDHash
- [x] LC_BUILD_VERSION parsed for platform info
- [x] Fat binary handling with per-slice UUIDs
- [x] Legacy LC_VERSION_MIN_* commands supported
- [x] Entitlements keys extracted (not values)
- [x] 32-bit and 64-bit Mach-O handled correctly
- [x] Deterministic output
- [x] All unit tests passing (26 tests)
---
## Execution Log
| Date | Update | Owner |
|------|--------|-------|
| 2025-12-18 | Created MachOPlatform.cs, MachOCodeSignature.cs, MachOIdentity.cs, MachOReader.cs. Updated NativeBinaryIdentity.cs and NativeFormatDetector.cs. Created MachOReaderTests.cs with 26 tests. All tests pass. 17/19 tasks DONE. | Agent |
---

View File

@@ -68,23 +68,31 @@ public enum BuildIdConfidence { Exact, Inferred, Heuristic }
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | BID-001 | TODO | Create IBuildIdIndex interface |
| 2 | BID-002 | TODO | Create BuildIdLookupResult model |
| 3 | BID-003 | TODO | Create BuildIdIndexOptions |
| 4 | BID-004 | TODO | Create OfflineBuildIdIndex implementation |
| 5 | BID-005 | TODO | Implement NDJSON parsing |
| 1 | BID-001 | DONE | Create IBuildIdIndex interface |
| 2 | BID-002 | DONE | Create BuildIdLookupResult model |
| 3 | BID-003 | DONE | Create BuildIdIndexOptions |
| 4 | BID-004 | DONE | Create OfflineBuildIdIndex implementation |
| 5 | BID-005 | DONE | Implement NDJSON parsing |
| 6 | BID-006 | TODO | Implement DSSE signature verification |
| 7 | BID-007 | TODO | Implement batch lookup |
| 8 | BID-008 | TODO | Add to OfflineKitOptions |
| 9 | BID-009 | TODO | Unit tests |
| 7 | BID-007 | DONE | Implement batch lookup |
| 8 | BID-008 | DONE | Add BuildIdIndexPath + RequireBuildIdIndexSignature to OfflineKitOptions |
| 9 | BID-009 | DONE | Unit tests (19 tests) |
| 10 | BID-010 | TODO | Integration tests |
---
## Execution Log
| Date | Update | Owner |
|------|--------|-------|
| 2025-12-18 | Created IBuildIdIndex, BuildIdLookupResult, BuildIdIndexOptions, BuildIdIndexEntry, OfflineBuildIdIndex. Created 19 unit tests. 7/10 tasks DONE. | Agent |
---
## Acceptance Criteria
- [ ] Index loads from offline kit path
- [x] Index loads from offline kit path
- [ ] DSSE signature verified before use
- [ ] Lookup returns PURL for known build-ids
- [ ] Unknown build-ids return null (not throw)
- [ ] Batch lookup efficient for many binaries
- [x] Lookup returns PURL for known build-ids
- [x] Unknown build-ids return null (not throw)
- [x] Batch lookup efficient for many binaries

View File

@@ -56,18 +56,26 @@ public sealed record NativeBinaryMetadata {
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | BSE-001 | TODO | Create INativeComponentEmitter |
| 2 | BSE-002 | TODO | Create NativeComponentEmitter |
| 3 | BSE-003 | TODO | Create NativePurlBuilder |
| 4 | BSE-004 | TODO | Create NativeComponentMapper |
| 5 | BSE-005 | TODO | Add NativeBinaryMetadata |
| 6 | BSE-006 | TODO | Update CycloneDxComposer |
| 7 | BSE-007 | TODO | Add stellaops:binary.* properties |
| 8 | BSE-008 | TODO | Unit tests |
| 1 | BSE-001 | DONE | Create INativeComponentEmitter |
| 2 | BSE-002 | DONE | Create NativeComponentEmitter |
| 3 | BSE-003 | DONE | Create NativePurlBuilder |
| 4 | BSE-004 | DONE | Create NativeComponentMapper (layer fragment generation) |
| 5 | BSE-005 | DONE | Add NativeBinaryMetadata (with Imports/Exports/PE/Mach-O fields) |
| 6 | BSE-006 | DONE | Update CycloneDxComposer via LayerComponentMapping.ToFragment() |
| 7 | BSE-007 | DONE | Add stellaops:binary.* properties in ToComponentRecord() |
| 8 | BSE-008 | DONE | Unit tests (22 tests passing) |
| 9 | BSE-009 | TODO | Integration tests |
---
## Execution Log
| Date | Update | Owner |
|------|--------|-------|
| 2025-12-18 | Created NativeBinaryMetadata, NativePurlBuilder, INativeComponentEmitter, NativeComponentEmitter. Created 22 tests. Fixed dependency issues in Reachability and SmartDiff. 5/9 tasks DONE. | Agent |
---
## Acceptance Criteria
- [ ] Native binaries appear as `file` type components

View File

@@ -45,11 +45,33 @@ Extend the Unknowns registry with native binary-specific classification reasons,
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | NUC-001 | TODO | Add UnknownKind enum values |
| 2 | NUC-002 | TODO | Create NativeUnknownContext |
| 3 | NUC-003 | TODO | Create NativeUnknownClassifier |
| 4 | NUC-004 | TODO | Integration with native analyzer |
| 5 | NUC-005 | TODO | Unit tests |
| 1 | NUC-001 | DONE | Add UnknownKind enum values (MissingBuildId, UnknownBuildId, UnresolvedNativeLibrary, HeuristicDependency, UnsupportedBinaryFormat) |
| 2 | NUC-002 | DONE | Create NativeUnknownContext model |
| 3 | NUC-003 | DONE | Create NativeUnknownClassifier service |
| 4 | NUC-003A | TODO | Approve + add `StellaOps.Unknowns.Core` reference from `src/Scanner/StellaOps.Scanner.Worker` (avoid circular deps; document final dependency direction) |
| 5 | NUC-003B | TODO | Wire native analyzer outputs to Unknowns: call `NativeUnknownClassifier` and persist via Unknowns repository/service from scan pipeline |
| 6 | NUC-004 | BLOCKED | Integrate with native analyzer (BLOCKED on NUC-003A/NUC-003B) |
| 7 | NUC-005 | TODO | Unit tests |
---
## Unblock Task Notes (NUC-003A/NUC-003B)
### NUC-003A: Project reference + dependency direction
- **Goal:** make the integration unambiguous: Scanner Worker emits Unknowns during scan; Unknowns.Core provides the domain + classifier.
- **Touchpoints (expected):**
- `src/Scanner/StellaOps.Scanner.Worker/StellaOps.Scanner.Worker.csproj` (add project reference)
- If persistence from Worker is required, also reference `src/Unknowns/__Libraries/StellaOps.Unknowns.Storage.Postgres/` and ensure migrations are applied by Scanner startup.
- **Acceptance criteria (minimum):**
- `dotnet build src/Scanner/StellaOps.Scanner.Worker/StellaOps.Scanner.Worker.csproj` succeeds with no circular references.
### NUC-003B: Wiring from native analyzer to Unknowns
- **Goal:** convert analyzer-side identification/resolution gaps into first-class Unknowns records.
- **Touchpoints (expected):**
- `src/Scanner/StellaOps.Scanner.Analyzers.Native/` (where classification context is produced)
- `src/Scanner/StellaOps.Scanner.Worker/` (where results are persisted/emitted)
- **Acceptance criteria (minimum):**
- A missing build-id produces `UnknownKind.MissingBuildId` with a populated `NativeUnknownContext` and is visible via existing Unknowns API surfaces.
---
@@ -58,3 +80,11 @@ Extend the Unknowns registry with native binary-specific classification reasons,
- [ ] Binaries without build-id create MissingBuildId unknowns
- [ ] Build-IDs not in index create UnknownBuildId unknowns
- [ ] Unknowns emit to registry, not core SBOM
---
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-18 | Added unblock tasks NUC-003A/NUC-003B; NUC-004 remains BLOCKED until dependency direction + wiring are implemented. | Project Mgmt |

View File

@@ -51,10 +51,10 @@ public sealed class NativeAnalyzerOptions
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | NAI-001 | TODO | Create NativeAnalyzerExecutor |
| 2 | NAI-002 | TODO | Create NativeBinaryDiscovery |
| 1 | NAI-001 | DONE | Create NativeAnalyzerExecutor |
| 2 | NAI-002 | DONE | Create NativeBinaryDiscovery |
| 3 | NAI-003 | TODO | Update CompositeScanAnalyzerDispatcher |
| 4 | NAI-004 | TODO | Add ScannerWorkerOptions.NativeAnalyzers |
| 4 | NAI-004 | DONE | Add ScannerWorkerOptions.NativeAnalyzers |
| 5 | NAI-005 | TODO | Integration tests |
---

View File

@@ -787,15 +787,15 @@ public sealed class DriftSarifGenerator
| # | Task ID | Status | Description | Notes |
|---|---------|--------|-------------|-------|
| 1 | UI-001 | TODO | Create PathNode TypeScript interface | Angular model |
| 2 | UI-002 | TODO | Create CompressedPath TypeScript interface | Angular model |
| 3 | UI-003 | TODO | Create PathViewerComponent | Core visualization |
| 4 | UI-004 | TODO | Style PathViewerComponent | SCSS styling |
| 5 | UI-005 | TODO | Create DriftedSink TypeScript interface | Angular model |
| 6 | UI-006 | TODO | Create DriftResult TypeScript interface | Angular model |
| 7 | UI-007 | TODO | Create RiskDriftCardComponent | Summary card |
| 8 | UI-008 | TODO | Style RiskDriftCardComponent | SCSS styling |
| 9 | UI-009 | TODO | Create drift API service | Angular HTTP service |
| 1 | UI-001 | DONE | Create PathNode TypeScript interface | `path-viewer.models.ts` |
| 2 | UI-002 | DONE | Create CompressedPath TypeScript interface | `path-viewer.models.ts` |
| 3 | UI-003 | DONE | Create PathViewerComponent | `components/path-viewer/` |
| 4 | UI-004 | DONE | Style PathViewerComponent | SCSS with BEM |
| 5 | UI-005 | DONE | Create DriftedSink TypeScript interface | `drift.models.ts` |
| 6 | UI-006 | DONE | Create DriftResult TypeScript interface | `drift.models.ts` |
| 7 | UI-007 | DONE | Create RiskDriftCardComponent | `components/risk-drift-card/` |
| 8 | UI-008 | DONE | Style RiskDriftCardComponent | SCSS with BEM |
| 9 | UI-009 | DONE | Create drift API service | `drift-api.service.ts` |
| 10 | UI-010 | TODO | Integrate PathViewer into scan details | Page integration |
| 11 | UI-011 | TODO | Integrate RiskDriftCard into PR view | Page integration |
| 12 | UI-012 | TODO | Unit tests for PathViewerComponent | Jest tests |
@@ -805,12 +805,12 @@ public sealed class DriftSarifGenerator
| 16 | UI-016 | TODO | Implement drift attestation service | DSSE signing |
| 17 | UI-017 | TODO | Add attestation to drift API | API integration |
| 18 | UI-018 | TODO | Unit tests for attestation | Predicate validation |
| 19 | UI-019 | TODO | Create DriftCommand for CLI | CLI command |
| 20 | UI-020 | TODO | Implement table output | Spectre.Console |
| 21 | UI-021 | TODO | Implement JSON output | JSON serialization |
| 22 | UI-022 | TODO | Create DriftSarifGenerator | SARIF 2.1.0 |
| 23 | UI-023 | TODO | Implement SARIF output for CLI | CLI integration |
| 24 | UI-024 | TODO | Update CLI documentation | docs/cli/ |
| 19 | UI-019 | DONE | Create DriftCommand for CLI | `Commands/DriftCommandGroup.cs` |
| 20 | UI-020 | DONE | Implement table output | Spectre.Console tables |
| 21 | UI-021 | DONE | Implement JSON output | JSON serialization |
| 22 | UI-022 | DONE | Create DriftSarifGenerator | SARIF 2.1.0 (placeholder) |
| 23 | UI-023 | DONE | Implement SARIF output for CLI | `CommandHandlers.Drift.cs` |
| 24 | UI-024 | DONE | Update CLI documentation | `docs/cli/drift-cli.md` |
| 25 | UI-025 | TODO | Integration tests for CLI | End-to-end |
---

View File

@@ -332,22 +332,33 @@ cas://reachability/graphs/{blake3:hash}/
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | RWD-001 | TODO | Create ReachabilityWitnessStatement.cs |
| 2 | RWD-002 | TODO | Create ReachabilityWitnessOptions.cs |
| 3 | RWD-003 | TODO | Add PredicateTypes.StellaOpsReachabilityWitness |
| 4 | RWD-004 | TODO | Create ReachabilityWitnessDsseBuilder.cs |
| 5 | RWD-005 | TODO | Create IReachabilityWitnessPublisher.cs |
| 6 | RWD-006 | TODO | Create ReachabilityWitnessPublisher.cs |
| 7 | RWD-007 | TODO | Implement CAS storage integration |
| 8 | RWD-008 | TODO | Implement Rekor submission |
| 9 | RWD-009 | TODO | Integrate with RichGraphWriter |
| 10 | RWD-010 | TODO | Add service registration |
| 11 | RWD-011 | TODO | Unit tests for DSSE builder |
| 12 | RWD-012 | TODO | Unit tests for publisher |
| 1 | RWD-001 | DONE | Create ReachabilityWitnessStatement.cs |
| 2 | RWD-002 | DONE | Create ReachabilityWitnessOptions.cs |
| 3 | RWD-003 | DONE | Add PredicateTypes.StellaOpsReachabilityWitness |
| 4 | RWD-004 | DONE | Create ReachabilityWitnessDsseBuilder.cs |
| 5 | RWD-005 | DONE | Create IReachabilityWitnessPublisher.cs |
| 6 | RWD-006 | DONE | Create ReachabilityWitnessPublisher.cs |
| 7 | RWD-007 | TODO | Implement CAS storage integration (placeholder done) |
| 8 | RWD-008 | TODO | Implement Rekor submission (placeholder done) |
| 9 | RWD-009 | DONE | Integrate with RichGraphWriter (AttestingRichGraphWriter) |
| 10 | RWD-010 | DONE | Add service registration |
| 11 | RWD-011 | DONE | Unit tests for DSSE builder (15 tests) |
| 12 | RWD-012 | DONE | Unit tests for publisher (8 tests) |
| 13 | RWD-013 | TODO | Integration tests with Attestor |
| 14 | RWD-014 | TODO | Add golden fixture: graph-only.golden.json |
| 15 | RWD-015 | TODO | Add golden fixture: graph-with-runtime.golden.json |
| 16 | RWD-016 | TODO | Verify deterministic DSSE output |
| 14 | RWD-014 | DONE | Add golden fixture: graph-only.golden.json |
| 15 | RWD-015 | DONE | Add golden fixture: graph-with-runtime.golden.json |
| 16 | RWD-016 | DONE | Verify deterministic DSSE output (4 tests) |
---
## Execution Log
| Date | Update | Owner |
|------|--------|-------|
| 2025-12-18 | Created ReachabilityWitnessStatement, ReachabilityWitnessOptions, ReachabilityWitnessDsseBuilder, IReachabilityWitnessPublisher, ReachabilityWitnessPublisher. Created 15 DSSE builder tests. 6/16 tasks DONE. | Agent |
| 2025-12-18 | Added PredicateTypes.StellaOpsReachabilityWitness to Signer.Core. Created ReachabilityAttestationServiceCollectionExtensions.cs for DI. Created ReachabilityWitnessPublisherTests.cs (8 tests). 9/16 tasks DONE. | Agent |
| 2025-12-18 | Fixed PathExplanationServiceTests.cs (RichGraph/RichGraphEdge constructor updates). Fixed RichGraphWriterTests.cs assertion. All 119 tests pass. | Agent |
| 2025-12-18 | Created AttestingRichGraphWriter.cs for integrated attestation. Created golden fixtures. Created AttestingRichGraphWriterTests.cs (4 tests). 13/16 tasks DONE. All 123 tests pass. | Agent |
---

View File

@@ -87,13 +87,13 @@ Final multiplier: 30%
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | PES-001 | TODO | Create PathExplanationModels |
| 2 | PES-002 | TODO | Create PathExplanationService |
| 3 | PES-003 | TODO | Create PathRenderer (text) |
| 4 | PES-004 | TODO | Create PathRenderer (markdown) |
| 5 | PES-005 | TODO | Create PathRenderer (json) |
| 1 | PES-001 | DONE | Create PathExplanationModels |
| 2 | PES-002 | DONE | Create PathExplanationService |
| 3 | PES-003 | DONE | Create PathRenderer (text) |
| 4 | PES-004 | DONE | Create PathRenderer (markdown) |
| 5 | PES-005 | DONE | Create PathRenderer (json) |
| 6 | PES-006 | TODO | Add CLI command: stella graph explain |
| 7 | PES-007 | TODO | Unit tests |
| 7 | PES-007 | DONE | Unit tests |
---

View File

@@ -86,13 +86,13 @@ Edge Bundles: 2 verified
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | CGV-001 | TODO | Create GraphVerifyCommand |
| 2 | CGV-002 | TODO | Implement DSSE verification |
| 3 | CGV-003 | TODO | Implement --include-bundles |
| 4 | CGV-004 | TODO | Implement --rekor-proof |
| 5 | CGV-005 | TODO | Implement --cas-root offline mode |
| 6 | CGV-006 | TODO | Create GraphBundlesCommand |
| 7 | CGV-007 | TODO | Create GraphExplainCommand |
| 1 | CGV-001 | DONE | Create GraphVerifyCommand |
| 2 | CGV-002 | DONE | Implement DSSE verification |
| 3 | CGV-003 | DONE | Implement --include-bundles |
| 4 | CGV-004 | DONE | Implement --rekor-proof |
| 5 | CGV-005 | DONE | Implement --cas-root offline mode |
| 6 | CGV-006 | DONE | Create GraphBundlesCommand |
| 7 | CGV-007 | TODO | Create GraphExplainCommand (uses existing explain) |
| 8 | CGV-008 | TODO | Unit tests |
---

View File

@@ -3,7 +3,7 @@
**Epic:** Triage Infrastructure
**Module:** Scanner
**Working Directory:** `src/Scanner/__Libraries/StellaOps.Scanner.Triage/`
**Status:** TODO
**Status:** DOING
**Created:** 2025-12-17
**Target Completion:** TBD
**Depends On:** None
@@ -34,18 +34,18 @@ Implement the PostgreSQL database schema for the Narrative-First Triage UX syste
| ID | Task | Owner | Status | Notes |
|----|------|-------|--------|-------|
| T1 | Create migration script from `docs/db/triage_schema.sql` | — | TODO | |
| T2 | Create PostgreSQL enums (7 types) | — | TODO | See schema |
| T3 | Create `TriageFinding` entity | — | TODO | |
| T4 | Create `TriageEffectiveVex` entity | — | TODO | |
| T5 | Create `TriageReachabilityResult` entity | — | TODO | |
| T6 | Create `TriageRiskResult` entity | — | TODO | |
| T7 | Create `TriageDecision` entity | — | TODO | |
| T8 | Create `TriageEvidenceArtifact` entity | — | TODO | |
| T9 | Create `TriageSnapshot` entity | — | TODO | |
| T10 | Create `TriageDbContext` with Fluent API | — | TODO | |
| T11 | Implement `v_triage_case_current` view mapping | — | TODO | |
| T12 | Add performance indexes | — | TODO | |
| T1 | Create migration script from `docs/db/triage_schema.sql` | Agent | DONE | `src/Scanner/__Libraries/StellaOps.Scanner.Triage/Migrations/V3700_001__triage_schema.sql` |
| T2 | Create PostgreSQL enums (7 types) | Agent | DONE | `TriageEnums.cs` |
| T3 | Create `TriageFinding` entity | Agent | DONE | |
| T4 | Create `TriageEffectiveVex` entity | Agent | DONE | |
| T5 | Create `TriageReachabilityResult` entity | Agent | DONE | |
| T6 | Create `TriageRiskResult` entity | Agent | DONE | |
| T7 | Create `TriageDecision` entity | Agent | DONE | |
| T8 | Create `TriageEvidenceArtifact` entity | Agent | DONE | |
| T9 | Create `TriageSnapshot` entity | Agent | DONE | |
| T10 | Create `TriageDbContext` with Fluent API | Agent | DONE | Full index + relationship config |
| T11 | Implement `v_triage_case_current` view mapping | Agent | DONE | `TriageCaseCurrent` keyless entity |
| T12 | Add performance indexes | Agent | DONE | In DbContext OnModelCreating |
| T13 | Write integration tests with Testcontainers | — | TODO | |
| T14 | Validate query performance (explain analyze) | — | TODO | |
@@ -230,6 +230,7 @@ public class TriageSchemaTests : IAsyncLifetime
| Date | Update | Owner |
|------|--------|-------|
| 2025-12-17 | Sprint file created | Claude |
| 2025-12-18 | Created Triage library with all entities (T1-T12 DONE): TriageEnums, TriageFinding, TriageEffectiveVex, TriageReachabilityResult, TriageRiskResult, TriageDecision, TriageEvidenceArtifact, TriageSnapshot, TriageCaseCurrent, TriageDbContext. Migration script created. Build verified. | Agent |
---

View File

@@ -1,6 +1,6 @@
# SPRINT_3700_0001_0001 - Witness Foundation
**Status:** TODO
**Status:** BLOCKED (WIT-008 blocked on WIT-007A/WIT-007B; WIT-009 blocked on WIT-007C/WIT-007D)
**Priority:** P0 - CRITICAL
**Module:** Scanner, Attestor
**Working Directory:** `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/`
@@ -39,21 +39,45 @@ Before starting, read:
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | WIT-001 | TODO | Add Blake3.NET package to Scanner.Reachability |
| 2 | WIT-002 | TODO | Update RichGraphWriter.ComputeHash to use BLAKE3 |
| 3 | WIT-003 | TODO | Update meta.json hash format to `blake3:` prefix |
| 4 | WIT-004 | TODO | Create WitnessSchema.cs with stellaops.witness.v1 |
| 5 | WIT-005 | TODO | Create PathWitness record model |
| 6 | WIT-006 | TODO | Create IPathWitnessBuilder interface |
| 7 | WIT-007 | TODO | Implement PathWitnessBuilder service |
| 8 | WIT-008 | TODO | Integrate with ReachabilityAnalyzer output |
| 9 | WIT-009 | TODO | Add DSSE envelope generation via Attestor |
| 10 | WIT-010 | TODO | Create WitnessEndpoints.cs (GET /witness/{id}) |
| 11 | WIT-011 | TODO | Create 012_witness_storage.sql migration |
| 12 | WIT-012 | TODO | Create PostgresWitnessRepository |
| 13 | WIT-013 | TODO | Update RichGraphWriterTests for BLAKE3 |
| 14 | WIT-014 | TODO | Add PathWitnessBuilderTests |
| 15 | WIT-015 | TODO | Create docs/contracts/witness-v1.md |
| 1 | WIT-001 | DONE | Add Blake3.NET package to Scanner.Reachability (via StellaOps.Cryptography HashPurpose.Graph) |
| 2 | WIT-002 | DONE | Update RichGraphWriter.ComputeHash to use BLAKE3 (via ComputePrefixedHashForPurpose) |
| 3 | WIT-003 | DONE | Update meta.json hash format to compliance-aware prefix (blake3:, sha256:, etc.) |
| 4 | WIT-004 | DONE | Create WitnessSchema.cs with stellaops.witness.v1 |
| 5 | WIT-005 | DONE | Create PathWitness record model |
| 6 | WIT-006 | DONE | Create IPathWitnessBuilder interface |
| 7 | WIT-007 | DONE | Implement PathWitnessBuilder service |
| 8 | WIT-007A | TODO | Define ReachabilityAnalyzer → PathWitnessBuilder output contract (types, ordering, limits, fixtures) |
| 9 | WIT-007B | TODO | Refactor ReachabilityAnalyzer to surface deterministic paths to sinks (enables witness generation) |
| 10 | WIT-007C | TODO | Define witness predicate + DSSE payloadType constants (Attestor) and align `docs/contracts/witness-v1.md` |
| 11 | WIT-007D | TODO | Implement DSSE sign+verify for witness payload using `StellaOps.Attestor.Envelope`; add golden fixtures |
| 12 | WIT-008 | BLOCKED | Integrate witness generation with ReachabilityAnalyzer output (BLOCKED on WIT-007A, WIT-007B) |
| 13 | WIT-009 | BLOCKED | Add DSSE envelope generation (BLOCKED on WIT-007C, WIT-007D) |
| 14 | WIT-010 | DONE | Create WitnessEndpoints.cs (GET /witness/{id}, list, verify) |
| 15 | WIT-011 | DONE | Create 013_witness_storage.sql migration |
| 16 | WIT-012 | DONE | Create PostgresWitnessRepository + IWitnessRepository |
| 17 | WIT-013 | DONE | Add UsesBlake3HashForDefaultProfile test to RichGraphWriterTests |
| 18 | WIT-014 | DONE | Add PathWitnessBuilderTests |
| 19 | WIT-015 | DONE | Create docs/contracts/witness-v1.md |
---
## Unblock Task Notes (WIT-007A..WIT-007D)
### WIT-007A: ReachabilityAnalyzer → witness output contract
- **Goal:** define the exact path output shape (entrypoint → sink), including stable ordering and caps (max depth/path count) so witness generation is deterministic.
- **Touchpoints (expected):** `src/Scanner/__Libraries/StellaOps.Scanner.CallGraph/Analysis/ReachabilityAnalyzer.cs` and `src/Scanner/__Tests/StellaOps.Scanner.CallGraph.Tests/` (fixtures + determinism assertions).
- **Evidence:** fixture graphs + expected path lists committed and validated by tests.
### WIT-007B: ReachabilityAnalyzer refactor (sink-aware + path export)
- **Acceptance criteria (minimum):** analyzer accepts explicit sinks and returns deterministic path(s) per reachable sink without breaking existing tests/behaviour.
### WIT-007C: Witness predicate + DSSE payloadType constants
- **Goal:** remove ambiguity about predicate URI/media type; Scanner/Attestor must sign/verify the same bytes.
- **Touchpoints (expected):** `src/Attestor/StellaOps.Attestor/Predicates/` and `docs/contracts/witness-v1.md`.
### WIT-007D: DSSE signing + verification for witnesses
- **Preferred implementation:** use `src/Attestor/StellaOps.Attestor.Envelope/` (serializer + `EnvelopeSignatureService`) for Ed25519 first.
- **Evidence:** golden fixture payload + DSSE envelope + public key, plus unit tests proving deterministic serialization and successful verification.
---
@@ -340,14 +364,14 @@ public static class WitnessPredicates
## Success Criteria
- [ ] RichGraphWriter uses BLAKE3 for graph_hash
- [ ] meta.json uses `blake3:` prefix
- [ ] All existing RichGraph tests pass
- [ ] PathWitness model serializes correctly
- [ ] PathWitnessBuilder generates valid witnesses
- [ ] DSSE signatures verify correctly
- [ ] `/witness/{id}` endpoint returns witness JSON
- [ ] Documentation complete
- [x] RichGraphWriter uses BLAKE3 for graph_hash
- [x] meta.json uses `blake3:` prefix
- [x] All existing RichGraph tests pass
- [x] PathWitness model serializes correctly
- [x] PathWitnessBuilder generates valid witnesses
- [ ] DSSE signatures verify correctly (BLOCKED: WIT-009; blocked on WIT-007C/WIT-007D)
- [x] `/witness/{id}` endpoint returns witness JSON
- [x] Documentation complete
---
@@ -358,6 +382,8 @@ public static class WitnessPredicates
| WIT-DEC-001 | Use Blake3.NET library | Well-tested, MIT license |
| WIT-DEC-002 | Store witnesses in Postgres JSONB | Flexible queries, no separate store |
| WIT-DEC-003 | Ed25519 signatures only | Simplicity, Ed25519 is default for DSSE |
| WIT-DEC-004 | Convert ReachabilityAnalyzer blocker into explicit tasks | Track contract+refactor as WIT-007A/WIT-007B; keep WIT-008 BLOCKED until complete |
| WIT-DEC-005 | Convert DSSE signing blocker into explicit tasks | Track predicate+sign/verify as WIT-007C/WIT-007D; keep WIT-009 BLOCKED until complete |
| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
@@ -371,3 +397,12 @@ public static class WitnessPredicates
| Date (UTC) | Update | Owner |
|---|---|---|
| 2025-12-18 | Created sprint from advisory analysis | Agent |
| 2025-12-18 | Completed WIT-011: Created 013_witness_storage.sql migration with witnesses and witness_verifications tables | Agent |
| 2025-12-18 | Completed WIT-012: Created IWitnessRepository and PostgresWitnessRepository with full CRUD + verification recording | Agent |
| 2025-12-18 | Completed WIT-015: Created docs/contracts/witness-v1.md with schema definition, DSSE signing, API endpoints | Agent |
| 2025-12-18 | Updated MigrationIds.cs to include WitnessStorage entry | Agent |
| 2025-12-18 | Registered IWitnessRepository in ServiceCollectionExtensions.cs | Agent |
| 2025-12-18 | Completed WIT-010: Created WitnessEndpoints.cs with GET /witnesses/{id}, list (by scan/cve/graphHash), by-hash, verify endpoints | Agent |
| 2025-12-18 | Registered MapWitnessEndpoints() in Scanner.WebService Program.cs | Agent |
| 2025-12-18 | Completed WIT-013: Added UsesBlake3HashForDefaultProfile test to RichGraphWriterTests.cs | Agent |
| 2025-12-18 | Added unblock tasks WIT-007A..WIT-007D and updated WIT-008/WIT-009 dependencies accordingly. | Project Mgmt |

View File

@@ -88,30 +88,30 @@ Before starting, read:
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | SURF-001 | TODO | Create StellaOps.Scanner.VulnSurfaces project |
| 2 | SURF-002 | TODO | Create IPackageDownloader interface |
| 3 | SURF-003 | TODO | Implement NuGetPackageDownloader |
| 1 | SURF-001 | DONE | Create StellaOps.Scanner.VulnSurfaces project |
| 2 | SURF-002 | DONE | Create IPackageDownloader interface |
| 3 | SURF-003 | DONE | Implement NuGetPackageDownloader |
| 4 | SURF-004 | TODO | Implement NpmPackageDownloader |
| 5 | SURF-005 | TODO | Implement MavenPackageDownloader |
| 6 | SURF-006 | TODO | Implement PyPIPackageDownloader |
| 7 | SURF-007 | TODO | Create IMethodFingerprinter interface |
| 8 | SURF-008 | TODO | Implement CecilMethodFingerprinter (.NET IL hash) |
| 7 | SURF-007 | DONE | Create IMethodFingerprinter interface |
| 8 | SURF-008 | DONE | Implement CecilMethodFingerprinter (.NET IL hash) |
| 9 | SURF-009 | TODO | Implement BabelMethodFingerprinter (Node.js AST) |
| 10 | SURF-010 | TODO | Implement AsmMethodFingerprinter (Java bytecode) |
| 11 | SURF-011 | TODO | Implement PythonAstFingerprinter |
| 12 | SURF-012 | TODO | Create MethodKey normalizer per ecosystem |
| 13 | SURF-013 | TODO | Create MethodDiffEngine service |
| 14 | SURF-014 | TODO | Create 011_vuln_surfaces.sql migration |
| 15 | SURF-015 | TODO | Create VulnSurface, VulnSurfaceSink models |
| 16 | SURF-016 | TODO | Create PostgresVulnSurfaceRepository |
| 17 | SURF-017 | TODO | Create VulnSurfaceBuilder orchestrator service |
| 18 | SURF-018 | TODO | Create IVulnSurfaceBuilder interface |
| 19 | SURF-019 | TODO | Add surface builder metrics |
| 20 | SURF-020 | TODO | Create NuGetDownloaderTests |
| 21 | SURF-021 | TODO | Create CecilFingerprinterTests |
| 22 | SURF-022 | TODO | Create MethodDiffEngineTests |
| 13 | SURF-013 | DONE | Create MethodDiffEngine service |
| 14 | SURF-014 | DONE | Create 014_vuln_surfaces.sql migration |
| 15 | SURF-015 | DONE | Create VulnSurface, VulnSurfaceSink models |
| 16 | SURF-016 | DONE | Create PostgresVulnSurfaceRepository |
| 17 | SURF-017 | DONE | Create VulnSurfaceBuilder orchestrator service |
| 18 | SURF-018 | DONE | Create IVulnSurfaceBuilder interface |
| 19 | SURF-019 | DONE | Add surface builder metrics |
| 20 | SURF-020 | DONE | Create NuGetDownloaderTests (9 tests) |
| 21 | SURF-021 | DONE | Create CecilFingerprinterTests (7 tests) |
| 22 | SURF-022 | DONE | Create MethodDiffEngineTests (8 tests) |
| 23 | SURF-023 | TODO | Integration test with real CVE (Newtonsoft.Json) |
| 24 | SURF-024 | TODO | Create docs/contracts/vuln-surface-v1.md |
| 24 | SURF-024 | DONE | Create docs/contracts/vuln-surface-v1.md |
---
@@ -447,3 +447,6 @@ Expected Changed Methods:
| Date (UTC) | Update | Owner |
|---|---|---|
| 2025-12-18 | Created sprint from advisory analysis | Agent |
| 2025-12-18 | Created CecilMethodFingerprinterTests.cs (7 tests) and MethodDiffEngineTests.cs (8 tests). 12/24 tasks DONE. All 26 VulnSurfaces tests pass. | Agent |
| 2025-12-18 | Created NuGetPackageDownloaderTests.cs (9 tests). Fixed IVulnSurfaceRepository interface/implementation mismatch. Added missing properties to VulnSurfaceSink model. 19/24 tasks DONE. All 35 VulnSurfaces tests pass. | Agent |
| 2025-12-18 | Created VulnSurfaceMetrics.cs with counters, histograms, and gauges. Integrated metrics into VulnSurfaceBuilder. 20/24 tasks DONE. | Agent |

View File

@@ -76,20 +76,20 @@ Extract **trigger methods** from vulnerability surfaces:
| # | Task ID | Status | Description |
|---|---------|--------|-------------|
| 1 | TRIG-001 | TODO | Create IInternalCallGraphBuilder interface |
| 2 | TRIG-002 | TODO | Implement CecilInternalGraphBuilder (.NET) |
| 1 | TRIG-001 | DONE | Create IInternalCallGraphBuilder interface |
| 2 | TRIG-002 | DONE | Implement CecilInternalGraphBuilder (.NET) |
| 3 | TRIG-003 | TODO | Implement BabelInternalGraphBuilder (Node.js) |
| 4 | TRIG-004 | TODO | Implement AsmInternalGraphBuilder (Java) |
| 5 | TRIG-005 | TODO | Implement PythonAstInternalGraphBuilder |
| 6 | TRIG-006 | TODO | Create VulnSurfaceTrigger model |
| 7 | TRIG-007 | TODO | Create ITriggerMethodExtractor interface |
| 8 | TRIG-008 | TODO | Implement TriggerMethodExtractor service |
| 9 | TRIG-009 | TODO | Implement forward BFS from public methods to sinks |
| 6 | TRIG-006 | DONE | Create VulnSurfaceTrigger model |
| 7 | TRIG-007 | DONE | Create ITriggerMethodExtractor interface |
| 8 | TRIG-008 | DONE | Implement TriggerMethodExtractor service |
| 9 | TRIG-009 | DONE | Implement forward BFS from public methods to sinks |
| 10 | TRIG-010 | TODO | Store trigger→sink paths in vuln_surface_triggers |
| 11 | TRIG-011 | TODO | Add interface/base method expansion |
| 11 | TRIG-011 | DONE | Add interface/base method expansion |
| 12 | TRIG-012 | TODO | Update VulnSurfaceBuilder to call trigger extraction |
| 13 | TRIG-013 | TODO | Add trigger_count to vuln_surfaces table |
| 14 | TRIG-014 | TODO | Create TriggerMethodExtractorTests |
| 14 | TRIG-014 | DONE | Create TriggerMethodExtractorTests |
| 15 | TRIG-015 | TODO | Integration test with Newtonsoft.Json CVE |
---

View File

@@ -32,11 +32,11 @@ Create the foundational data models for the unified evidence API contracts. Thes
| Task | Status | Owner | Notes |
|------|--------|-------|-------|
| Create FindingEvidenceContracts.cs in Scanner.WebService | TODO | | API contracts |
| Create BoundaryProof.cs in Scanner.SmartDiff.Detection | TODO | | Boundary model |
| Create ScoreExplanation.cs in Signals.Models | TODO | | Score breakdown |
| Create VexEvidence.cs in Scanner.SmartDiff.Detection | TODO | | VEX evidence model |
| Add unit tests for JSON serialization | TODO | | Determinism tests |
| Create FindingEvidenceContracts.cs in Scanner.WebService | DONE | Agent | API contracts with all DTOs |
| Create BoundaryProof.cs in Scanner.SmartDiff.Detection | DONE | Agent | Boundary model with surface, exposure, auth, controls |
| Create ScoreExplanation.cs in Signals.Models | DONE | Agent | Score breakdown with contributions and modifiers |
| Create VexEvidence.cs in Scanner.SmartDiff.Detection | DONE | Agent | VEX evidence model with status, justification, source |
| Add unit tests for JSON serialization | DONE | Agent | FindingEvidenceContractsTests.cs with round-trip tests |
## Implementation Details
@@ -95,11 +95,11 @@ public sealed record ScoreExplanation(
## Acceptance Criteria
- [ ] All models compile and follow existing naming conventions
- [ ] JSON serialization produces lowercase snake_case properties
- [ ] Models are immutable (record types with init properties)
- [ ] Unit tests verify JSON round-trip serialization
- [ ] Documentation comments on all public types
- [x] All models compile and follow existing naming conventions
- [x] JSON serialization produces lowercase snake_case properties
- [x] Models are immutable (record types with init properties)
- [x] Unit tests verify JSON round-trip serialization
- [x] Documentation comments on all public types
## Decisions & Risks

View File

@@ -29,12 +29,12 @@ Implement the `ScoreExplanationService` that generates additive risk score break
| Task | Status | Owner | Notes |
|------|--------|-------|-------|
| Create IScoreExplanationService.cs | TODO | | Interface definition |
| Create ScoreExplanationService.cs | TODO | | Implementation |
| Add score weights to SignalsScoringOptions | TODO | | Configuration |
| Add DI registration | TODO | | ServiceCollectionExtensions |
| Unit tests for score computation | TODO | | Test various scenarios |
| Golden tests for score stability | TODO | | Determinism verification |
| Create IScoreExplanationService.cs | DONE | Agent | Interface with request model |
| Create ScoreExplanationService.cs | DONE | Agent | Full implementation with all factors |
| Add score weights to SignalsScoringOptions | DONE | Agent | ScoreExplanationWeights class |
| Add DI registration | DONE | Agent | Registered in Program.cs |
| Unit tests for score computation | DONE | Agent | ScoreExplanationServiceTests.cs |
| Golden tests for score stability | DONE | Agent | IsDeterministic test verifies stability |
## Implementation Details
@@ -98,12 +98,12 @@ public class ScoreExplanationWeights
## Acceptance Criteria
- [ ] `ScoreExplanationService` produces consistent output for same input
- [ ] Score contributions sum to the total risk_score (within floating point tolerance)
- [ ] All score factors have human-readable `reason` strings
- [ ] Gate detection from `ReachabilityStateDocument.Evidence.Gates` is incorporated
- [ ] Weights are configurable via `SignalsScoringOptions`
- [ ] Unit tests cover all bucket types and gate combinations
- [x] `ScoreExplanationService` produces consistent output for same input
- [x] Score contributions sum to the total risk_score (within floating point tolerance)
- [x] All score factors have human-readable `reason` strings
- [x] Gate detection from `ReachabilityStateDocument.Evidence.Gates` is incorporated
- [x] Weights are configurable via `SignalsScoringOptions`
- [x] Unit tests cover all bucket types and gate combinations
## Decisions & Risks

View File

@@ -31,12 +31,12 @@ Implement the base `RichGraphBoundaryExtractor` that extracts boundary proof (ex
| Task | Status | Owner | Notes |
|------|--------|-------|-------|
| Create IBoundaryProofExtractor.cs | TODO | | Interface with context |
| Create RichGraphBoundaryExtractor.cs | TODO | | Base implementation |
| Create BoundaryExtractionContext.cs | TODO | | Environment context |
| Integrate with AuthGateDetector results | TODO | | Reuse existing detection |
| Add DI registration | TODO | | ServiceCollectionExtensions |
| Unit tests for extraction | TODO | | Various root types |
| Create IBoundaryProofExtractor.cs | DONE | Agent | Interface with Priority & CanHandle |
| Create RichGraphBoundaryExtractor.cs | DONE | Agent | Full implementation with surface/exposure inference |
| Create BoundaryExtractionContext.cs | DONE | Agent | Environment context with gates |
| Integrate with AuthGateDetector results | DONE | Agent | Uses DetectedGate from Gates folder |
| Add DI registration | DONE | Agent | BoundaryServiceCollectionExtensions |
| Unit tests for extraction | DONE | Agent | RichGraphBoundaryExtractorTests.cs |
## Implementation Details

View File

@@ -31,14 +31,14 @@ Implement the `PolicyDecisionAttestationService` that creates signed `stella.ops
| Task | Status | Owner | Notes |
|------|--------|-------|-------|
| Add StellaOpsPolicyDecision to PredicateTypes.cs | TODO | | Signer.Core |
| Create PolicyDecisionPredicate.cs | TODO | | Policy.Engine |
| Create IPolicyDecisionAttestationService.cs | TODO | | Interface |
| Create PolicyDecisionAttestationService.cs | TODO | | Implementation |
| Add configuration options | TODO | | PolicyDecisionAttestationOptions |
| Add DI registration | TODO | | ServiceCollectionExtensions |
| Unit tests for predicate creation | TODO | | |
| Integration tests with signing | TODO | | |
| Add StellaOpsPolicyDecision to PredicateTypes.cs | DONE | Agent | Added to allowed list |
| Create PolicyDecisionPredicate.cs | DONE | Agent | Full model with all records |
| Create IPolicyDecisionAttestationService.cs | DONE | Agent | Interface + request/result records |
| Create PolicyDecisionAttestationService.cs | DONE | Agent | Full impl with signer/rekor |
| Add configuration options | DONE | Agent | PolicyDecisionAttestationOptions |
| Add DI registration | DONE | Agent | AddPolicyDecisionAttestation ext |
| Unit tests for predicate creation | DONE | Agent | PolicyDecisionAttestationServiceTests |
| Integration tests with signing | TODO | | Requires live signer service |
## Implementation Details

View File

@@ -29,12 +29,12 @@ Create TypeScript models and API clients for the unified evidence API. These mod
| Task | Status | Owner | Notes |
|------|--------|-------|-------|
| Create triage-evidence.models.ts | TODO | | Mirror backend contracts |
| Create triage-evidence.client.ts | TODO | | HttpClient with caching |
| Create attestation-chain.models.ts | TODO | | DSSE envelope types |
| Create attestation-chain.client.ts | TODO | | Chain verification client |
| Update core/api/index.ts exports | TODO | | |
| Add unit tests for client | TODO | | Mock HTTP responses |
| Create triage-evidence.models.ts | DONE | Agent | Full model coverage with helpers |
| Create triage-evidence.client.ts | DONE | Agent | HttpClient with caching + mock client |
| Create attestation-chain.models.ts | DONE | Agent | DSSE, in-toto, Rekor types |
| Create attestation-chain.client.ts | DONE | Agent | Chain verification + mock client |
| Update core/api/index.ts exports | DONE | Agent | Created triage-api.index.ts barrel |
| Add unit tests for client | DONE | Agent | triage-evidence.client.spec.ts |
## Implementation Details

View File

@@ -52,8 +52,10 @@
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-18 | Sprint created; awaiting staffing. | Planning |
| 2025-12-18 | Started SIG-CG-3104-001 (projection contract + implementation). | Agent |
| 2025-12-18 | Completed SIG-CG-3104-001..005; validated via `dotnet test src/Signals/StellaOps.Signals.Storage.Postgres.Tests/StellaOps.Signals.Storage.Postgres.Tests.csproj -c Release` (5 tests). | Agent |
| 2025-12-18 | Verified existing implementations: ICallGraphSyncService, CallGraphSyncService, PostgresCallGraphProjectionRepository all exist and are wired. Wired SyncAsync call into CallgraphIngestionService post-upsert path. Updated CallgraphIngestionServiceTests with StubCallGraphSyncService. Tasks 1-3 DONE. | Agent |
| 2025-12-18 | Added unit tests (CallGraphSyncServiceTests.cs) and integration tests (CallGraphProjectionIntegrationTests.cs). All tasks DONE. | Agent |
| 2025-12-18 | Validated via `dotnet test src/Signals/StellaOps.Signals.Storage.Postgres.Tests/StellaOps.Signals.Storage.Postgres.Tests.csproj -c Release`. | Agent |
## Next Checkpoints
- 2025-12-18: Projection service skeleton + first passing integration test (if staffed).
- 2025-12-18: Sprint completed.

View File

@@ -1,6 +1,6 @@
# SPRINT_3500_0003_0001 - Smart-Diff Detection Rules
**Status:** TODO
**Status:** DONE
**Priority:** P0 - CRITICAL
**Module:** Scanner, Policy, Excititor
**Working Directory:** `src/Scanner/__Libraries/StellaOps.Scanner.SmartDiff/`

View File

@@ -333,12 +333,86 @@ For each vulnerability instance:
- [ ] Trend visualization
### Phase 5: Operations
- [ ] Backfill tool (last 180 days)
- [ ] Ops runbook: schedules, manual re-run, air-gap import
- [x] Backfill tool (last 180 days)
- [x] Ops runbook: schedules, manual re-run, air-gap import
---
## 10. Anti-Patterns to Avoid
## 10. Operations Runbook
### 10.1 Configuration
EPSS ingestion is configured via the `Epss:Ingest` section in Scanner Worker configuration:
```yaml
Epss:
Ingest:
Enabled: true # Enable/disable the job
Schedule: "0 5 0 * * *" # Cron expression (default: 00:05 UTC daily)
SourceType: "online" # "online" or "bundle"
BundlePath: null # Path for air-gapped bundle import
InitialDelay: "00:00:30" # Wait before first run (30s)
RetryDelay: "00:05:00" # Delay between retries (5m)
MaxRetries: 3 # Maximum retry attempts
```
### 10.2 Online Mode (Connected)
The job automatically fetches EPSS data from FIRST.org at the scheduled time:
1. Downloads `https://epss.empiricalsecurity.com/epss_scores-YYYY-MM-DD.csv.gz`
2. Validates SHA256 hash
3. Parses CSV and bulk inserts to `epss_scores`
4. Computes delta against `epss_current`
5. Updates `epss_current` projection
6. Publishes `epss.updated` event
### 10.3 Air-Gap Mode (Bundle)
For offline deployments:
1. Download EPSS CSV from FIRST.org on an internet-connected system
2. Copy to the configured `BundlePath` location
3. Set `SourceType: "bundle"` in configuration
4. The job will read from the local file instead of fetching online
### 10.4 Manual Ingestion
Trigger manual ingestion via the Scanner Worker API:
```bash
# POST to trigger immediate ingestion for a specific date
curl -X POST "https://scanner-worker/epss/ingest?date=2025-12-18"
```
### 10.5 Troubleshooting
| Symptom | Likely Cause | Resolution |
|---------|--------------|------------|
| Job not running | `Enabled: false` | Set `Enabled: true` |
| Download fails | Network/firewall | Check HTTPS egress to `epss.empiricalsecurity.com` |
| Parse errors | Corrupted file | Re-download, check SHA256 |
| Slow ingestion | Large dataset | Normal for ~250k rows; expect 60-90s |
| Duplicate runs | Idempotent | Safe - existing data preserved |
### 10.6 Monitoring
Key metrics and traces:
- **Activity**: `StellaOps.Scanner.EpssIngest` with tags:
- `epss.model_date`: Date of EPSS model
- `epss.row_count`: Number of rows ingested
- `epss.cve_count`: Distinct CVEs processed
- `epss.duration_ms`: Total ingestion time
- **Logs**: Structured logs at Info/Warning/Error levels
- `EPSS ingest job started`
- `Starting EPSS ingestion for {ModelDate}`
- `EPSS ingestion completed: modelDate={ModelDate}, rows={RowCount}...`
---
## 11. Anti-Patterns to Avoid
| Anti-Pattern | Why It's Wrong |
|--------------|----------------|