audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
176
docs/modules/policy/determinization-api.md
Normal file
176
docs/modules/policy/determinization-api.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Determinization Gate API Reference
|
||||
|
||||
> **Audience:** Backend integrators, policy operators, and security engineers working with CVE observations.
|
||||
> **Sprint:** 20260106_001_003_POLICY_determinization_gates
|
||||
|
||||
This document describes the Determinization Gate API and the `GuardedPass` verdict status for uncertain CVE observations.
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
The Determinization Gate evaluates CVE observations against uncertainty thresholds and produces verdicts based on available evidence (EPSS, VEX, reachability, runtime, backport signals). It introduces `GuardedPass` status for observations that don't have enough evidence for a confident determination but don't exceed risk thresholds.
|
||||
|
||||
### Key Components
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `DeterminizationGate` | Policy gate that evaluates uncertainty and produces verdicts |
|
||||
| `DeterminizationPolicy` | Rule set for allow/quarantine/escalate decisions |
|
||||
| `SignalUpdateHandler` | Handles signal updates and triggers re-evaluation |
|
||||
| `DeterminizationGateMetrics` | OpenTelemetry metrics for observability |
|
||||
|
||||
---
|
||||
|
||||
## 2. PolicyVerdictStatus
|
||||
|
||||
Policy evaluations return a `PolicyVerdictStatus` indicating the outcome:
|
||||
|
||||
| Status | Code | Description | Monitoring Required |
|
||||
|--------|------|-------------|---------------------|
|
||||
| `Pass` | 0 | Finding meets policy requirements. No action needed. | No |
|
||||
| `Blocked` | 1 | Finding fails policy checks; must be remediated. | No |
|
||||
| `Ignored` | 2 | Finding deliberately ignored via policy exception. | No |
|
||||
| `Warned` | 3 | Finding passes but with warnings. | No |
|
||||
| `Deferred` | 4 | Decision deferred; needs additional evidence. | No |
|
||||
| `Escalated` | 5 | Decision escalated for human review. | No |
|
||||
| `RequiresVex` | 6 | VEX statement required to make decision. | No |
|
||||
| `GuardedPass` | 7 | Finding allowed with runtime monitoring guardrails. | **Yes** |
|
||||
|
||||
---
|
||||
|
||||
## 3. GuardedPass Status
|
||||
|
||||
`GuardedPass` is a specialized status for CVE observations with uncertain evidence. It enables "allow with guardrails" semantics.
|
||||
|
||||
### 3.1 When Issued
|
||||
|
||||
- Observation has insufficient evidence for full determination (high entropy)
|
||||
- Trust score below thresholds but above blocking level
|
||||
- Non-production environments with moderate uncertainty
|
||||
- Reachability status is unknown but not confirmed reachable
|
||||
|
||||
### 3.2 GuardRails Object
|
||||
|
||||
When `GuardedPass` is returned, the verdict includes a `guardRails` object:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "GuardedPass",
|
||||
"reason": "Uncertain observation allowed with guardrails in staging",
|
||||
"matchedRule": "GuardedAllowNonProd",
|
||||
"guardRails": {
|
||||
"enableRuntimeMonitoring": true,
|
||||
"reviewInterval": "7.00:00:00",
|
||||
"epssEscalationThreshold": 0.4,
|
||||
"escalatingReachabilityStates": ["Reachable", "ObservedReachable"],
|
||||
"maxGuardedDuration": "30.00:00:00",
|
||||
"policyRationale": "Auto-allowed: entropy=0.45, trust=0.38, env=staging"
|
||||
},
|
||||
"suggestedObservationState": "PendingDeterminization",
|
||||
"uncertaintyScore": {
|
||||
"entropy": 0.45,
|
||||
"completeness": 0.55,
|
||||
"tier": "Medium",
|
||||
"missingSignals": ["runtime", "backport"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Consumer Responsibilities
|
||||
|
||||
When receiving `GuardedPass`:
|
||||
|
||||
1. **Enable Runtime Monitoring:** Start observing the component for vulnerable code execution
|
||||
2. **Schedule Review:** Set up periodic review at `reviewInterval`
|
||||
3. **EPSS Escalation:** Auto-escalate if EPSS score exceeds `epssEscalationThreshold`
|
||||
4. **Reachability Escalation:** Auto-escalate if reachability transitions to escalating states
|
||||
5. **Duration Limit:** Convert to `Blocked` if guard duration exceeds `maxGuardedDuration`
|
||||
|
||||
---
|
||||
|
||||
## 4. Determinization Rules
|
||||
|
||||
The gate evaluates rules in priority order:
|
||||
|
||||
| Priority | Rule | Condition | Result |
|
||||
|----------|------|-----------|--------|
|
||||
| 10 | RuntimeEscalation | Runtime evidence shows vulnerable code loaded | Escalated |
|
||||
| 20 | EpssQuarantine | EPSS score exceeds threshold | Blocked |
|
||||
| 25 | ReachabilityQuarantine | Code proven reachable | Blocked |
|
||||
| 30 | ProductionEntropyBlock | High entropy in production | Blocked |
|
||||
| 40 | StaleEvidenceDefer | Evidence is stale | Deferred |
|
||||
| 50 | GuardedAllowNonProd | Uncertain in non-prod | GuardedPass |
|
||||
| 60 | UnreachableAllow | Unreachable with high confidence | Pass |
|
||||
| 65 | VexNotAffectedAllow | VEX not_affected from trusted issuer | Pass |
|
||||
| 70 | SufficientEvidenceAllow | Low entropy, high trust | Pass |
|
||||
| 80 | GuardedAllowModerateUncertainty | Moderate uncertainty, reasonable trust | GuardedPass |
|
||||
| 100 | DefaultDefer | No rule matched | Deferred |
|
||||
|
||||
---
|
||||
|
||||
## 5. Environment Thresholds
|
||||
|
||||
Thresholds vary by deployment environment:
|
||||
|
||||
| Environment | MinConfidence | MaxEntropy | EPSS Threshold | Require Reachability |
|
||||
|-------------|---------------|------------|----------------|---------------------|
|
||||
| Production | 0.75 | 0.3 | 0.3 | Yes |
|
||||
| Staging | 0.60 | 0.5 | 0.4 | Yes |
|
||||
| Development | 0.40 | 0.7 | 0.6 | No |
|
||||
|
||||
---
|
||||
|
||||
## 6. Signal Update Events
|
||||
|
||||
The Determinization Gate subscribes to signal updates for automatic re-evaluation:
|
||||
|
||||
| Event Type | Description |
|
||||
|------------|-------------|
|
||||
| `epss.updated` | EPSS score changed |
|
||||
| `vex.updated` | VEX statement added/modified |
|
||||
| `reachability.updated` | Reachability analysis completed |
|
||||
| `runtime.updated` | Runtime observation recorded |
|
||||
| `backport.updated` | Backport detection result |
|
||||
| `observation.state_changed` | Observation state transition |
|
||||
|
||||
---
|
||||
|
||||
## 7. Metrics
|
||||
|
||||
OpenTelemetry metrics for monitoring:
|
||||
|
||||
| Metric | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `stellaops_policy_determinization_evaluations_total` | Counter | Total evaluations by status/environment/rule |
|
||||
| `stellaops_policy_determinization_rule_matches_total` | Counter | Rule matches by rule name/status/environment |
|
||||
| `stellaops_policy_observation_state_transitions_total` | Counter | State transitions by from/to state/trigger |
|
||||
| `stellaops_policy_determinization_entropy` | Histogram | Distribution of entropy scores |
|
||||
| `stellaops_policy_determinization_trust_score` | Histogram | Distribution of trust scores |
|
||||
| `stellaops_policy_determinization_evaluation_duration_ms` | Histogram | Evaluation latency |
|
||||
|
||||
---
|
||||
|
||||
## 8. Service Registration
|
||||
|
||||
Add determinization services via dependency injection:
|
||||
|
||||
```csharp
|
||||
// Register all determinization services
|
||||
services.AddDeterminizationEngine();
|
||||
|
||||
// Or as part of full Policy Engine registration
|
||||
services.AddPolicyEngine(); // Includes determinization
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Related Documentation
|
||||
|
||||
- [Determinization Library](./determinization-architecture.md) - Core determinization models
|
||||
- [Policy Engine Architecture](./architecture.md) - Overall policy engine design
|
||||
- [Signal Snapshot Models](../../api/signals/reachability-contract.md) - Signal data structures
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-01-07 (Sprint 20260106_001_003)*
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#### Activation configuration wiring
|
||||
|
||||
- **Helm ConfigMap.** `deploy/helm/stellaops/values*.yaml` now include a `policy-engine-activation` ConfigMap. The chart automatically injects it via `envFrom` into both the Policy Engine and Policy Gateway pods, so overriding the ConfigMap data updates the services with no manifest edits.
|
||||
- **Helm ConfigMap.** `devops/helm/stellaops/values*.yaml` now include a `policy-engine-activation` ConfigMap. The chart automatically injects it via `envFrom` into both the Policy Engine and Policy Gateway pods, so overriding the ConfigMap data updates the services with no manifest edits.
|
||||
- **Type safety.** Quote ConfigMap values (e.g., `"true"`, `"false"`) because Kubernetes ConfigMaps carry string data. This mirrors the defaults checked into the repo and keeps `helm template` deterministic.
|
||||
- **File-based overrides (optional).** The Policy Engine host already probes `/config/policy-engine/activation.yaml`, `../etc/policy-engine.activation.yaml`, and ambient `policy-engine.activation.yaml` files beside the binary. Mounting the ConfigMap as a file at `/config/policy-engine/activation.yaml` works immediately if/when we add a volume.
|
||||
- **Offline/Compose.** Compose/offline bundles can continue exporting `STELLAOPS_POLICY_ENGINE__ACTIVATION__*` variables directly; the ConfigMap wiring simply mirrors those keys for Kubernetes clusters.
|
||||
|
||||
369
docs/modules/policy/schemas/stellaops.suppression.v1.schema.json
Normal file
369
docs/modules/policy/schemas/stellaops.suppression.v1.schema.json
Normal file
@@ -0,0 +1,369 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://stellaops.dev/schemas/stellaops.suppression.v1.schema.json",
|
||||
"title": "StellaOps Suppression Witness v1",
|
||||
"description": "A DSSE-signable suppression witness documenting why a vulnerability is not exploitable",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"witness_schema",
|
||||
"witness_id",
|
||||
"artifact",
|
||||
"vuln",
|
||||
"suppression_type",
|
||||
"evidence",
|
||||
"confidence",
|
||||
"observed_at"
|
||||
],
|
||||
"properties": {
|
||||
"witness_schema": {
|
||||
"type": "string",
|
||||
"const": "stellaops.suppression.v1",
|
||||
"description": "Schema version identifier"
|
||||
},
|
||||
"witness_id": {
|
||||
"type": "string",
|
||||
"pattern": "^sup:sha256:[a-f0-9]{64}$",
|
||||
"description": "Content-addressed witness ID (e.g., 'sup:sha256:...')"
|
||||
},
|
||||
"artifact": {
|
||||
"$ref": "#/definitions/WitnessArtifact",
|
||||
"description": "The artifact (SBOM, component) this witness relates to"
|
||||
},
|
||||
"vuln": {
|
||||
"$ref": "#/definitions/WitnessVuln",
|
||||
"description": "The vulnerability this witness concerns"
|
||||
},
|
||||
"suppression_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Unreachable",
|
||||
"LinkerGarbageCollected",
|
||||
"FeatureFlagDisabled",
|
||||
"PatchedSymbol",
|
||||
"GateBlocked",
|
||||
"CompileTimeExcluded",
|
||||
"VexNotAffected",
|
||||
"FunctionAbsent",
|
||||
"VersionNotAffected",
|
||||
"PlatformNotAffected"
|
||||
],
|
||||
"description": "The type of suppression (unreachable, patched, gate-blocked, etc.)"
|
||||
},
|
||||
"evidence": {
|
||||
"$ref": "#/definitions/SuppressionEvidence",
|
||||
"description": "Evidence supporting the suppression claim"
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"maximum": 1.0,
|
||||
"description": "Confidence level in this suppression [0.0, 1.0]"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Optional expiration date for time-bounded suppressions (UTC ISO-8601)"
|
||||
},
|
||||
"observed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When this witness was generated (UTC ISO-8601)"
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Optional justification narrative"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"WitnessArtifact": {
|
||||
"type": "object",
|
||||
"required": ["sbom_digest", "component_purl"],
|
||||
"properties": {
|
||||
"sbom_digest": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[a-f0-9]{64}$",
|
||||
"description": "SHA-256 digest of the SBOM"
|
||||
},
|
||||
"component_purl": {
|
||||
"type": "string",
|
||||
"pattern": "^pkg:",
|
||||
"description": "Package URL of the vulnerable component"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"WitnessVuln": {
|
||||
"type": "object",
|
||||
"required": ["id", "source", "affected_range"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Vulnerability identifier (e.g., 'CVE-2024-12345')"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "Vulnerability source (e.g., 'NVD', 'OSV', 'GHSA')"
|
||||
},
|
||||
"affected_range": {
|
||||
"type": "string",
|
||||
"description": "Affected version range expression"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"SuppressionEvidence": {
|
||||
"type": "object",
|
||||
"required": ["witness_evidence"],
|
||||
"properties": {
|
||||
"witness_evidence": {
|
||||
"$ref": "#/definitions/WitnessEvidence"
|
||||
},
|
||||
"unreachability": {
|
||||
"$ref": "#/definitions/UnreachabilityEvidence"
|
||||
},
|
||||
"patched_symbol": {
|
||||
"$ref": "#/definitions/PatchedSymbolEvidence"
|
||||
},
|
||||
"function_absent": {
|
||||
"$ref": "#/definitions/FunctionAbsentEvidence"
|
||||
},
|
||||
"gate_blocked": {
|
||||
"$ref": "#/definitions/GateBlockedEvidence"
|
||||
},
|
||||
"feature_flag": {
|
||||
"$ref": "#/definitions/FeatureFlagEvidence"
|
||||
},
|
||||
"vex_statement": {
|
||||
"$ref": "#/definitions/VexStatementEvidence"
|
||||
},
|
||||
"version_range": {
|
||||
"$ref": "#/definitions/VersionRangeEvidence"
|
||||
},
|
||||
"linker_gc": {
|
||||
"$ref": "#/definitions/LinkerGcEvidence"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"WitnessEvidence": {
|
||||
"type": "object",
|
||||
"required": ["callgraph_digest"],
|
||||
"properties": {
|
||||
"callgraph_digest": {
|
||||
"type": "string",
|
||||
"description": "BLAKE3 digest of the call graph used"
|
||||
},
|
||||
"surface_digest": {
|
||||
"type": "string",
|
||||
"description": "SHA-256 digest of the attack surface manifest"
|
||||
},
|
||||
"analysis_config_digest": {
|
||||
"type": "string",
|
||||
"description": "SHA-256 digest of the analysis configuration"
|
||||
},
|
||||
"build_id": {
|
||||
"type": "string",
|
||||
"description": "Build identifier for the analyzed artifact"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"UnreachabilityEvidence": {
|
||||
"type": "object",
|
||||
"required": ["analyzed_entrypoints", "unreachable_symbol", "analysis_method", "graph_digest"],
|
||||
"properties": {
|
||||
"analyzed_entrypoints": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Number of entrypoints analyzed"
|
||||
},
|
||||
"unreachable_symbol": {
|
||||
"type": "string",
|
||||
"description": "Vulnerable symbol that was confirmed unreachable"
|
||||
},
|
||||
"analysis_method": {
|
||||
"type": "string",
|
||||
"description": "Analysis method (static, dynamic, hybrid)"
|
||||
},
|
||||
"graph_digest": {
|
||||
"type": "string",
|
||||
"description": "Graph digest for reproducibility"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"FunctionAbsentEvidence": {
|
||||
"type": "object",
|
||||
"required": ["function_name", "binary_digest", "verification_method"],
|
||||
"properties": {
|
||||
"function_name": {
|
||||
"type": "string",
|
||||
"description": "Vulnerable function name"
|
||||
},
|
||||
"binary_digest": {
|
||||
"type": "string",
|
||||
"description": "Binary digest where function was checked"
|
||||
},
|
||||
"verification_method": {
|
||||
"type": "string",
|
||||
"description": "Verification method (symbol table scan, disassembly, etc.)"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"GateBlockedEvidence": {
|
||||
"type": "object",
|
||||
"required": ["detected_gates", "gate_coverage_percent", "effectiveness"],
|
||||
"properties": {
|
||||
"detected_gates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DetectedGate"
|
||||
},
|
||||
"description": "Detected gates along all paths to vulnerable code"
|
||||
},
|
||||
"gate_coverage_percent": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Minimum gate coverage percentage [0, 100]"
|
||||
},
|
||||
"effectiveness": {
|
||||
"type": "string",
|
||||
"description": "Gate effectiveness assessment"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"DetectedGate": {
|
||||
"type": "object",
|
||||
"required": ["type", "guard_symbol", "confidence"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Gate type (authRequired, inputValidation, rateLimited, etc.)"
|
||||
},
|
||||
"guard_symbol": {
|
||||
"type": "string",
|
||||
"description": "Symbol that implements the gate"
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"maximum": 1.0,
|
||||
"description": "Confidence level (0.0 - 1.0)"
|
||||
},
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"description": "Human-readable detail about the gate"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PatchedSymbolEvidence": {
|
||||
"type": "object",
|
||||
"required": ["vulnerable_symbol", "patched_symbol", "symbol_diff"],
|
||||
"properties": {
|
||||
"vulnerable_symbol": {
|
||||
"type": "string",
|
||||
"description": "Vulnerable symbol identifier"
|
||||
},
|
||||
"patched_symbol": {
|
||||
"type": "string",
|
||||
"description": "Patched symbol identifier"
|
||||
},
|
||||
"symbol_diff": {
|
||||
"type": "string",
|
||||
"description": "Symbol diff showing the patch"
|
||||
},
|
||||
"patch_ref": {
|
||||
"type": "string",
|
||||
"description": "Patch commit or release reference"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"VexStatementEvidence": {
|
||||
"type": "object",
|
||||
"required": ["vex_id", "vex_author", "vex_status", "vex_digest"],
|
||||
"properties": {
|
||||
"vex_id": {
|
||||
"type": "string",
|
||||
"description": "VEX statement identifier"
|
||||
},
|
||||
"vex_author": {
|
||||
"type": "string",
|
||||
"description": "VEX statement author/authority"
|
||||
},
|
||||
"vex_status": {
|
||||
"type": "string",
|
||||
"enum": ["not_affected", "fixed"],
|
||||
"description": "VEX statement status"
|
||||
},
|
||||
"vex_digest": {
|
||||
"type": "string",
|
||||
"description": "Content digest of the VEX document"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"FeatureFlagEvidence": {
|
||||
"type": "object",
|
||||
"required": ["flag_name", "flag_state", "verification_source"],
|
||||
"properties": {
|
||||
"flag_name": {
|
||||
"type": "string",
|
||||
"description": "Feature flag name/key"
|
||||
},
|
||||
"flag_state": {
|
||||
"type": "string",
|
||||
"description": "Feature flag state (off, disabled)"
|
||||
},
|
||||
"verification_source": {
|
||||
"type": "string",
|
||||
"description": "Source of flag verification (config file, runtime)"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"VersionRangeEvidence": {
|
||||
"type": "object",
|
||||
"required": ["actual_version", "affected_range", "comparison_method"],
|
||||
"properties": {
|
||||
"actual_version": {
|
||||
"type": "string",
|
||||
"description": "Actual version of the component"
|
||||
},
|
||||
"affected_range": {
|
||||
"type": "string",
|
||||
"description": "Affected version range from advisory"
|
||||
},
|
||||
"comparison_method": {
|
||||
"type": "string",
|
||||
"description": "Version comparison method used"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"LinkerGcEvidence": {
|
||||
"type": "object",
|
||||
"required": ["removed_symbol", "linker_method", "verification_digest"],
|
||||
"properties": {
|
||||
"removed_symbol": {
|
||||
"type": "string",
|
||||
"description": "Symbol removed by linker GC"
|
||||
},
|
||||
"linker_method": {
|
||||
"type": "string",
|
||||
"description": "Linker garbage collection method"
|
||||
},
|
||||
"verification_digest": {
|
||||
"type": "string",
|
||||
"description": "Digest of final binary for verification"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user