Add unit tests for RabbitMq and Udp transport servers and clients
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling. - Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options. - Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation. - Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios. - Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling. - Included tests for UdpTransportOptions to verify default values and modification capabilities. - Enhanced service registration tests for Udp transport services in the dependency injection container.
This commit is contained in:
286
docs/contracts/risk-scoring.md
Normal file
286
docs/contracts/risk-scoring.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# Risk Scoring Contract (66-002)
|
||||
|
||||
**Contract ID:** `CONTRACT-RISK-SCORING-002`
|
||||
**Version:** 1.0
|
||||
**Status:** Published
|
||||
**Last Updated:** 2025-12-05
|
||||
|
||||
## Overview
|
||||
|
||||
This contract defines the risk scoring interface used by the Policy Engine to calculate and prioritize vulnerability findings. It covers job requests, results, risk profiles, and signal definitions.
|
||||
|
||||
## Implementation References
|
||||
|
||||
- **Scoring Models:** `src/Policy/StellaOps.Policy.Engine/Scoring/RiskScoringModels.cs`
|
||||
- **Risk Profile:** `src/Policy/StellaOps.Policy.RiskProfile/Models/RiskProfileModel.cs`
|
||||
- **Attestation Schema:** `src/Attestor/StellaOps.Attestor.Types/schemas/stellaops-risk-profile.v1.schema.json`
|
||||
|
||||
## Data Models
|
||||
|
||||
### RiskScoringJobRequest
|
||||
|
||||
Request to create a risk scoring job.
|
||||
|
||||
```json
|
||||
{
|
||||
"tenant_id": "string",
|
||||
"context_id": "string",
|
||||
"profile_id": "string",
|
||||
"findings": [
|
||||
{
|
||||
"finding_id": "string",
|
||||
"component_purl": "pkg:npm/lodash@4.17.20",
|
||||
"advisory_id": "CVE-2024-1234",
|
||||
"trigger": "created|updated|enriched|vex_applied"
|
||||
}
|
||||
],
|
||||
"priority": "low|normal|high|emergency",
|
||||
"correlation_id": "string (optional)",
|
||||
"requested_at": "2025-12-05T00:00:00Z (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
### RiskScoringJob
|
||||
|
||||
A queued or completed risk scoring job.
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "string",
|
||||
"tenant_id": "string",
|
||||
"context_id": "string",
|
||||
"profile_id": "string",
|
||||
"profile_hash": "sha256:...",
|
||||
"findings": [...],
|
||||
"priority": "normal",
|
||||
"status": "queued|running|completed|failed|cancelled",
|
||||
"requested_at": "2025-12-05T00:00:00Z",
|
||||
"started_at": "2025-12-05T00:00:01Z (optional)",
|
||||
"completed_at": "2025-12-05T00:00:02Z (optional)",
|
||||
"correlation_id": "string (optional)",
|
||||
"error_message": "string (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
### RiskScoringResult
|
||||
|
||||
Result of scoring a single finding.
|
||||
|
||||
```json
|
||||
{
|
||||
"finding_id": "string",
|
||||
"profile_id": "string",
|
||||
"profile_version": "1.0.0",
|
||||
"raw_score": 0.75,
|
||||
"normalized_score": 0.85,
|
||||
"severity": "high",
|
||||
"signal_values": {
|
||||
"cvss": 7.5,
|
||||
"kev": true,
|
||||
"reachability": 0.9
|
||||
},
|
||||
"signal_contributions": {
|
||||
"cvss": 0.4,
|
||||
"kev": 0.3,
|
||||
"reachability": 0.3
|
||||
},
|
||||
"override_applied": "kev-boost (optional)",
|
||||
"override_reason": "Known Exploited Vulnerability (optional)",
|
||||
"scored_at": "2025-12-05T00:00:02Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Risk Profile Model
|
||||
|
||||
### RiskProfileModel
|
||||
|
||||
Defines how findings are scored and prioritized.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "default-profile",
|
||||
"version": "1.0.0",
|
||||
"description": "Default risk profile for vulnerability prioritization",
|
||||
"extends": "base-profile (optional)",
|
||||
"signals": [
|
||||
{
|
||||
"name": "cvss",
|
||||
"source": "nvd",
|
||||
"type": "numeric",
|
||||
"path": "/cvss/base_score",
|
||||
"transform": "normalize_10",
|
||||
"unit": "score"
|
||||
},
|
||||
{
|
||||
"name": "kev",
|
||||
"source": "cisa",
|
||||
"type": "boolean",
|
||||
"path": "/kev/in_catalog"
|
||||
},
|
||||
{
|
||||
"name": "reachability",
|
||||
"source": "scanner",
|
||||
"type": "numeric",
|
||||
"path": "/reachability/score"
|
||||
}
|
||||
],
|
||||
"weights": {
|
||||
"cvss": 0.4,
|
||||
"kev": 0.3,
|
||||
"reachability": 0.3
|
||||
},
|
||||
"overrides": {
|
||||
"severity": [
|
||||
{
|
||||
"when": { "kev": true },
|
||||
"set": "critical"
|
||||
}
|
||||
],
|
||||
"decisions": [
|
||||
{
|
||||
"when": { "kev": true, "reachability": { "$gt": 0.8 } },
|
||||
"action": "deny",
|
||||
"reason": "KEV with high reachability"
|
||||
}
|
||||
]
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Signal Types
|
||||
|
||||
| Type | Description | Value Range |
|
||||
|------|-------------|-------------|
|
||||
| `boolean` | True/false signal | `true` / `false` |
|
||||
| `numeric` | Numeric signal | `0.0` to `1.0` (normalized) |
|
||||
| `categorical` | Categorical signal | String values |
|
||||
|
||||
### Severity Levels
|
||||
|
||||
| Level | JSON Value | Priority |
|
||||
|-------|------------|----------|
|
||||
| Critical | `"critical"` | 1 (highest) |
|
||||
| High | `"high"` | 2 |
|
||||
| Medium | `"medium"` | 3 |
|
||||
| Low | `"low"` | 4 |
|
||||
| Informational | `"informational"` | 5 (lowest) |
|
||||
|
||||
### Decision Actions
|
||||
|
||||
| Action | Description |
|
||||
|--------|-------------|
|
||||
| `allow` | Finding is acceptable, no action required |
|
||||
| `review` | Finding requires manual review |
|
||||
| `deny` | Finding is not acceptable, blocks promotion |
|
||||
|
||||
## Scoring Algorithm
|
||||
|
||||
### Score Calculation
|
||||
|
||||
```
|
||||
raw_score = Σ(signal_value × weight) for all signals
|
||||
normalized_score = clamp(raw_score, 0.0, 1.0)
|
||||
```
|
||||
|
||||
### VEX Gate Provider
|
||||
|
||||
The VEX gate provider short-circuits scoring when a VEX denial is present:
|
||||
|
||||
```csharp
|
||||
if (signals.HasVexDenial)
|
||||
return 0.0; // Fully mitigated
|
||||
|
||||
return Math.Max(signals.Values); // Otherwise, max signal
|
||||
```
|
||||
|
||||
### CVSS + KEV Provider
|
||||
|
||||
```csharp
|
||||
score = clamp01((cvss / 10.0) + kevBonus)
|
||||
where kevBonus = kev ? 0.2 : 0.0
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Submit Scoring Job
|
||||
|
||||
```
|
||||
POST /api/v1/risk/jobs
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"tenant_id": "...",
|
||||
"context_id": "...",
|
||||
"profile_id": "...",
|
||||
"findings": [...]
|
||||
}
|
||||
|
||||
Response: 202 Accepted
|
||||
{
|
||||
"job_id": "...",
|
||||
"status": "queued"
|
||||
}
|
||||
```
|
||||
|
||||
### Get Job Status
|
||||
|
||||
```
|
||||
GET /api/v1/risk/jobs/{job_id}
|
||||
|
||||
Response: 200 OK
|
||||
{
|
||||
"job_id": "...",
|
||||
"status": "completed",
|
||||
"results": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### Get Finding Score
|
||||
|
||||
```
|
||||
GET /api/v1/risk/findings/{finding_id}/score
|
||||
|
||||
Response: 200 OK
|
||||
{
|
||||
"finding_id": "...",
|
||||
"normalized_score": 0.85,
|
||||
"severity": "high",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Finding Change Events
|
||||
|
||||
Events that trigger rescoring:
|
||||
|
||||
| Event | JSON Value | Description |
|
||||
|-------|------------|-------------|
|
||||
| Created | `"created"` | New finding discovered |
|
||||
| Updated | `"updated"` | Finding metadata changed |
|
||||
| Enriched | `"enriched"` | New signals available |
|
||||
| VEX Applied | `"vex_applied"` | VEX status changed |
|
||||
|
||||
## Determinism Guarantees
|
||||
|
||||
1. **Reproducible scores:** Same inputs always produce same outputs
|
||||
2. **Profile versioning:** Profile hash included in results for traceability
|
||||
3. **Signal ordering:** Signals processed in deterministic order
|
||||
4. **Timestamp precision:** UTC ISO-8601 with millisecond precision
|
||||
|
||||
## Unblocks
|
||||
|
||||
This contract unblocks the following tasks:
|
||||
|
||||
- LEDGER-RISK-67-001
|
||||
- LEDGER-RISK-68-001
|
||||
- LEDGER-RISK-69-001
|
||||
- POLICY-RISK-67-003
|
||||
- POLICY-RISK-68-001
|
||||
- POLICY-RISK-68-002
|
||||
|
||||
## Related Contracts
|
||||
|
||||
- [Advisory Key Contract](./advisory-key.md) - Advisory ID canonicalization
|
||||
- [VEX Lens Contract](./vex-lens.md) - VEX evidence for scoring
|
||||
- [Export Bundle Contract](./export-bundle.md) - Score digest in exports
|
||||
Reference in New Issue
Block a user