Stave's risk reasoning engine transforms individual findings into compound risk assessments using three layers of scoring.
Assessor → Observation (CEL evaluator + snapshots)
RiskEngine → Inference (multipliers + chain engine)
Reporter → Attestation (structured reasoning output)
Environmental = base_impact × asset_sensitivity × exposure_vector
base_impact: 0-100, from controlparams.base_impactasset_sensitivity: phi=3.0, production=2.0, internal=1.0, dev=0.5exposure_vector: public=2.0, cross_account=1.5, vpc=1.0
Compound = environmental × chain_escalation × blast_multiplier
chain_escalation: 1=1.0x, 2=1.8x, 3+=2.5x (bounded)blast_multiplier: from controlparams.blast_radius.multiplier
Maps MITRE ATT&CK-aligned stages to worst severity:
initial_access— public S3, open SGs, public RDScredential_access— MFA failures, key rotationpersistence— IAM self-modification, break-glassexfiltration— encryption controlsdetection_evasion— CloudTrail, GuardDuty, Configresilience— backups, versioning, Object Lock
Chains are auto-discovered from the chains/ directory sibling to
your controls path. When you run stave apply --controls ./controls,
the engine automatically loads ./chains/*.yaml and evaluates them
against the active findings. Chains whose controls are all present
in the active policy set are evaluated; chains referencing excluded
controls are silently filtered.
Chain YAML format:
id: public_phi_exposure
description: PHI data reachable from public internet
controls:
- CTL.S3.PUBLIC.001
- CTL.S3.ENCRYPT.001
- CTL.S3.LOG.001
- CTL.CLOUDTRAIL.DATAREAD.001
escalation_threshold: 2
compound_severity: criticalBuilt-in chains (15): public_phi_exposure, root_compromise_path,
detection_blindness, identity_blast_radius,
unauthenticated_data_path, data_exfiltration_path,
cross_env_pivot, privilege_escalation_path,
supply_chain_ingress, compromised_secret_path,
recovery_integrity_failure, sovereignty_violation,
shadow_logic_escalation, crypto_concentration_failure,
third_party_exposure_path.
Controls opt into risk scoring via params:
params:
base_impact: 10
attack_stage: initial_access
chain_ids:
- public_phi_exposure
blast_radius:
type: detection
scope: account
multiplier: 2.5Controls without risk params default to safe values (0, 1.0).
The risk engine ranks all findings by exposure score to answer "what to fix first?" The ranking identifies long-lived, high-impact failures that persist undetected — the silent killers.
ExposureScore = BaseScore × DurationFactor × BlastMultiplier × ExposureMultiplier
| Factor | Source | Values |
|---|---|---|
| BaseScore | params.base_impact or severity fallback |
10-100 |
| DurationFactor | Evidence.UnsafeDurationHours stepped |
1.0 / 1.5 / 2.0 / 3.0 / 5.0 |
| BlastMultiplier | params.blast_radius.multiplier |
1.0-2.5 |
| ExposureMultiplier | Exposure.PrincipalScope |
1.0 (internal), 2.0 (public) |
Duration factor steps:
| Duration | Factor | Label |
|---|---|---|
| < 30 days | 1.0 | Recent |
| 30-89 days | 1.5 | Aging |
| 90-364 days | 2.0 | Stale |
| 365-1642 days | 3.0 | Long-lived |
| 1643+ days (4.5 years) | 5.0 | Silent killer |
Findings with days_blind > 300 are flagged as silent killers.
Top 10 exposures are shown in text output. The full ranked list
appears in JSON as top_exposures:
{
"top_exposures": [
{
"finding_index": 0,
"control_id": "CTL.S3.PUBLIC.001",
"asset_id": "arn:aws:s3:::phi-records-prod",
"exposure_score": 2500.0,
"breakdown": {
"base_score": 100,
"duration_factor": 5.0,
"blast_multiplier": 2.5,
"exposure_multiplier": 2.0,
"days_blind": 1826.0
},
"silent_killer": true
}
]
}Text output:
Top Exposures
-------------
1 2500.0 1826d CTL.S3.PUBLIC.001 s3:phi-records-prod
SILENT KILLER base=100 × duration=5.0 × blast=2.5 × exposure=2.0
The ranking is deterministic: same inputs always produce the same order. Ties are broken by ControlID then AssetID.
{
"chain_findings": [{
"chain": "public_phi_exposure",
"controls_failing": ["CTL.S3.PUBLIC.001", "CTL.S3.ENCRYPT.001"],
"compound_score": 126.0,
"severity": "CRITICAL",
"narrative": "PHI data reachable from public internet..."
}],
"attack_stage_summary": {
"initial_access": "CRITICAL",
"credential_access": "PASS",
"detection_evasion": "HIGH",
"resilience": "PASS"
}
}| File | Purpose |
|---|---|
internal/core/controldef/chain.go |
ChainDefinition type |
internal/core/evaluation/risk/calculator.go |
Environmental, Compound, ChainEscalation |
internal/core/evaluation/risk/chain_engine.go |
DetectChains |
internal/core/evaluation/risk/attack_stage.go |
BuildAttackStageSummary |
internal/core/evaluation/risk/exposure_rank.go |
ExposureRank, DurationFactor, RankExposures |
internal/app/eval/workflow.go |
Pipeline wiring (enrichWithRiskReasoning) |
chains/*.yaml |
Built-in chain definitions |