Risk Model Overview
SecurAtlas uses a quantitative risk model that produces a score from 0 to 100 for each tenant, where 0 is the worst and 100 is the best security posture. The score is derived from control maturity levels, evidence coverage, framework compliance, and integration findings.
The risk score is not a probability of breach. It is a composite measure of security posture completeness and control effectiveness.
Score Components
The risk score is computed from four weighted categories:
| Category | Weight | Source |
|---|
| Control Maturity | 35% | Average maturity across all enabled controls |
| Evidence Coverage | 25% | Percentage of controls with accepted evidence |
| Framework Compliance | 25% | Weighted average of framework readiness scores |
| Integration Findings | 15% | Severity-weighted count of open findings |
function computeTenantRiskScore(tenant: TenantMetrics): number {
const maturityScore = tenant.avgControlMaturity / 5 * 100;
const evidenceScore = tenant.evidenceCoverage * 100;
const frameworkScore = tenant.avgFrameworkReadiness;
const findingsScore = Math.max(0, 100 - tenant.findingsPenalty);
return Math.round(
maturityScore * 0.35 +
evidenceScore * 0.25 +
frameworkScore * 0.25 +
findingsScore * 0.15
);
}
Data Storage
tenant_risk_snapshots
Point-in-time risk scores are stored for trending:
tenant_risk_snapshots (
id uuid PRIMARY KEY,
tenant_id uuid REFERENCES tenants,
risk_score_1_100 int, -- 0 = worst, 100 = best
category_breakdown jsonb, -- Scores per category
annualized_loss_expectancy numeric, -- ALE in dollars
computed_at timestamptz,
snapshot_type text -- 'nightly', 'manual', 'event'
)
category_breakdown JSONB
The category_breakdown field stores per-category scores:
{
"control_maturity": 72,
"evidence_coverage": 58,
"framework_compliance": 65,
"integration_findings": 88,
"subcategories": {
"access_control": 80,
"data_protection": 55,
"incident_response": 42,
"network_security": 70,
"vendor_management": 60
}
}
Annualized Loss Expectancy (ALE)
ALE provides a dollar estimate of potential annual loss based on the risk posture:
Where:
- SLE (Single Loss Expectancy) is estimated from tenant size and industry benchmarks
- ARO (Annual Rate of Occurrence) is derived from the inverse of the risk score
ALE is an estimate for executive reporting, not an actuarial calculation. It uses industry-average breach cost data scaled to the tenant’s employee count and industry.
Risk Drivers
The v_tenant_top_risk_drivers view surfaces the most impactful factors:
-- Returns top risk factors ordered by impact
SELECT * FROM v_tenant_top_risk_drivers
WHERE tenant_id = 'uuid-here'
ORDER BY impact_score DESC
LIMIT 10;
Example output:
| driver_type | driver_name | impact_score | recommendation |
|---|
| finding | Users without MFA | 18 | Enable MFA for all users |
| control | Incident Response Plan | 15 | Implement and test IR plan |
| evidence | Access Review Records | 12 | Upload quarterly access reviews |
Maturity to Risk Relationship
Control maturity directly influences the risk score:
| Maturity Level | Score Contribution | Description |
|---|
| Not Implemented (0) | 0% | No risk reduction |
| Planned (1) | 20% | Intent documented |
| Implemented (2) | 50% | Control deployed |
| Managed (3) | 75% | Measured and reviewed |
| Optimized (4-5) | 100% | Continuous improvement |
Nightly Risk Recompute
A pg_cron job recalculates risk scores every night at 2 AM UTC:
-- pg_cron job definition
SELECT cron.schedule(
'nightly_risk_recompute',
'0 2 * * *',
$$SELECT fn_recompute_all_tenant_risk_scores()$$
);
The fn_recompute_all_tenant_risk_scores() function:
- Iterates over all active tenants
- Queries current control maturity, evidence coverage, framework readiness, and findings
- Computes the composite score
- Inserts a new
tenant_risk_snapshots row with snapshot_type = 'nightly'
Risk scores are also recomputed on significant events (evidence accepted, findings resolved) with snapshot_type = 'event' to keep dashboards responsive between nightly runs.