# Score Policy YAML Format **Sprint:** SPRINT_3402_0001_0001 **Status:** Complete ## Overview StellaOps uses a YAML-based configuration for deterministic vulnerability scoring. The score policy defines how different factors contribute to the final vulnerability score, ensuring reproducible and auditable results. ## Schema Version Current version: `score.v1` ## File Location By default, score policies are loaded from: - `etc/score-policy.yaml` (production) - `etc/score-policy.yaml.sample` (reference template) Override via environment variable: `STELLAOPS_SCORE_POLICY_PATH` ## Basic Structure ```yaml # Required fields policyVersion: score.v1 policyId: unique-policy-identifier # Optional metadata policyName: "My Organization's Scoring Policy" description: "Custom scoring weights for our security posture" # Weight distribution (must sum to 10000 basis points = 100%) weightsBps: baseSeverity: 2500 # 25% - CVSS base score contribution reachability: 2500 # 25% - Code reachability analysis evidence: 2500 # 25% - KEV, EPSS, exploit evidence provenance: 2500 # 25% - Supply chain trust signals ``` ## Weight Configuration Weights are specified in **basis points (bps)** where 10000 bps = 100%. This avoids floating-point precision issues and ensures weights always sum to exactly 100%. ### Example: Reachability-Heavy Profile ```yaml policyVersion: score.v1 policyId: reachability-focused weightsBps: baseSeverity: 2000 # 20% reachability: 4000 # 40% - Heavy emphasis on reachability evidence: 2000 # 20% provenance: 2000 # 20% ``` ### Example: Evidence-Heavy Profile ```yaml policyVersion: score.v1 policyId: evidence-focused weightsBps: baseSeverity: 2000 # 20% reachability: 2000 # 20% evidence: 4000 # 40% - Heavy emphasis on KEV/EPSS provenance: 2000 # 20% ``` ## Reachability Configuration Fine-tune how reachability analysis affects scores: ```yaml reachabilityConfig: reachableMultiplier: 1.5 # Boost for reachable code paths unreachableMultiplier: 0.3 # Reduction for unreachable code unknownMultiplier: 1.0 # Default when analysis unavailable ``` ### Multiplier Bounds - Minimum: 0.0 - Maximum: 2.0 (configurable) - Default for unknown: 1.0 (no adjustment) ## Evidence Configuration Configure how exploit evidence affects scoring: ```yaml evidenceConfig: kevWeight: 1.5 # Boost for KEV-listed vulnerabilities epssThreshold: 0.5 # EPSS score threshold for high-risk epssWeight: 1.2 # Weight multiplier for high EPSS ``` ### KEV Integration Known Exploited Vulnerabilities (KEV) from CISA are automatically boosted: - `kevWeight: 1.5` means 50% score increase for KEV-listed CVEs - Setting `kevWeight: 1.0` disables KEV boost ### EPSS Integration Exploit Prediction Scoring System (EPSS) provides probability-based risk: - `epssThreshold`: Minimum EPSS for applying the weight - `epssWeight`: Multiplier applied when EPSS exceeds threshold ## Provenance Configuration Configure how supply chain trust signals affect scoring: ```yaml provenanceConfig: signedBonus: 0.1 # 10% reduction for signed artifacts rekorVerifiedBonus: 0.2 # 20% reduction for Rekor-verified unsignedPenalty: -0.1 # 10% increase for unsigned artifacts ``` ### Trust Signals | Signal | Effect | Use Case | |--------|--------|----------| | `signedBonus` | Score reduction | Artifact has valid signature | | `rekorVerifiedBonus` | Score reduction | Signature in transparency log | | `unsignedPenalty` | Score increase | No signature present | ## Score Overrides Override scoring for specific CVEs or patterns: ```yaml overrides: # Exact CVE match - id: log4shell-critical match: cvePattern: "CVE-2021-44228" action: setScore: 10.0 reason: "Known critical RCE in production" # Pattern match - id: log4j-family match: cvePattern: "CVE-2021-442.*" action: multiplyScore: 1.2 reason: "Log4j family vulnerabilities" # Severity-based - id: low-severity-suppress match: severityEquals: "LOW" action: multiplyScore: 0.5 reason: "Reduce noise from low-severity findings" # Combined conditions - id: unreachable-medium match: severityEquals: "MEDIUM" reachabilityEquals: "UNREACHABLE" action: multiplyScore: 0.3 reason: "Medium + unreachable = low priority" ``` ### Override Actions | Action | Description | Example | |--------|-------------|---------| | `setScore` | Force specific score | `setScore: 10.0` | | `multiplyScore` | Apply multiplier | `multiplyScore: 0.5` | | `addScore` | Add/subtract value | `addScore: -2.0` | ### Match Conditions | Condition | Description | Example | |-----------|-------------|---------| | `cvePattern` | Regex match on CVE ID | `"CVE-2021-.*"` | | `severityEquals` | Exact severity match | `"HIGH"`, `"CRITICAL"` | | `reachabilityEquals` | Reachability state | `"REACHABLE"`, `"UNREACHABLE"`, `"UNKNOWN"` | | `packagePattern` | Package name regex | `"log4j.*"` | ## Complete Example ```yaml policyVersion: score.v1 policyId: production-v2024.12 policyName: "Production Security Policy" description: | Balanced scoring policy with emphasis on exploitability and reachability for production workloads. weightsBps: baseSeverity: 2000 reachability: 3000 evidence: 3000 provenance: 2000 reachabilityConfig: reachableMultiplier: 1.5 unreachableMultiplier: 0.4 unknownMultiplier: 1.0 evidenceConfig: kevWeight: 1.5 epssThreshold: 0.3 epssWeight: 1.3 provenanceConfig: signedBonus: 0.1 rekorVerifiedBonus: 0.15 unsignedPenalty: -0.05 overrides: - id: critical-rce match: cvePattern: "CVE-2021-44228|CVE-2022-22965" action: setScore: 10.0 reason: "Known critical RCE vulnerabilities" - id: unreachable-low match: severityEquals: "LOW" reachabilityEquals: "UNREACHABLE" action: multiplyScore: 0.2 reason: "Minimal risk: low severity + unreachable" ``` ## Validation Policies are validated against JSON Schema on load: 1. **Schema validation**: Structure and types 2. **Weight sum check**: `weightsBps` must sum to 10000 3. **Range checks**: Multipliers within bounds 4. **Override validation**: Valid patterns and actions ### Programmatic Validation ```csharp var validator = new ScorePolicyValidator(); var result = validator.Validate(policy); if (!result.IsValid) { foreach (var error in result.Errors) { Console.WriteLine(error); } } ``` ## Determinism For reproducible scoring: 1. **Policy Digest**: Each policy has a content-addressed digest 2. **Replay Manifest**: Digest is recorded in scan manifests 3. **Audit Trail**: Policy version tracked with every scan ### Digest Format ``` sha256:abc123def456... ``` The digest is computed from canonical JSON serialization of the policy, ensuring identical policies always produce identical digests. ## Migration ### From Hardcoded Weights 1. Export current weights to YAML format 2. Validate with `stellaops policy validate score.yaml` 3. Deploy to `etc/score-policy.yaml` 4. Restart services to load new policy ### Version Upgrades Future schema versions (e.g., `score.v2`) will include migration guides and backward compatibility notes. ## Related Documentation - [Architecture Overview](../07_HIGH_LEVEL_ARCHITECTURE.md) - [Determinism Technical Reference](../product-advisories/14-Dec-2025%20-%20Determinism%20and%20Reproducibility%20Technical%20Reference.md) - [Policy Engine Architecture](../modules/policy/architecture.md)