Skip to main content

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:
CategoryWeightSource
Control Maturity35%Average maturity across all enabled controls
Evidence Coverage25%Percentage of controls with accepted evidence
Framework Compliance25%Weighted average of framework readiness scores
Integration Findings15%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:
ALE = SLE x ARO
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_typedriver_nameimpact_scorerecommendation
findingUsers without MFA18Enable MFA for all users
controlIncident Response Plan15Implement and test IR plan
evidenceAccess Review Records12Upload quarterly access reviews

Maturity to Risk Relationship

Control maturity directly influences the risk score:
Maturity LevelScore ContributionDescription
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:
  1. Iterates over all active tenants
  2. Queries current control maturity, evidence coverage, framework readiness, and findings
  3. Computes the composite score
  4. 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.