Risk Scoring

Deep Dive Into Scoring

Deep Dive Into Scoring

This document explains, step by step and with numbers, exactly how each module's score is computed. It covers the three built-in calculators (DETECTION, ASM, BP), the volume-aware sigmoid, rule risk boosts, breakdown construction, and global score aggregation.


Table of Contents

  1. Scoring Pipeline Overview
  2. Constants & Core Formula
  3. DETECTION Module — Worked Example
  4. ASM Module — Worked Example
  5. BP Module — Worked Example
  6. Rule Risk Boost & Volume Dampening
  7. Breakdown Construction
  8. Global Score Aggregation — Worked Example
  9. Marginal Impact Tracking
  10. Quick Reference Table

1. Scoring Pipeline Overview

Every module score follows the same 7-step pipeline:

Snapshot Data
  │
  ▼
1. buildVariables()        ← Convert raw snapshot into a flat variable map
  │
  ▼
2. Rule Evaluation         ← Evaluate org rules → ruleRiskBoost
  │
  ▼
3. Volume Dampening        ← Dampen rule boost at low volumes
  │
  ▼
4. Formula Evaluation      ← Feed variables into formula → rawInput
  │
  ▼
5. Sigmoid Normalization   ← riskScore = 100 × (1 − e^(−rawInput / scaleFactor))
  │
  ▼
6. Health Score Inversion  ← score = 100 − riskScore
  │
  ▼
7. Breakdown + Impacts     ← Build ScoreBreakdown + marginal impacts

Each calculator (ASM, BP, DETECTION) inherits from BaseSnapshotCalculator and overrides:

  • buildVariables() — adds module-specific variables
  • getDefaultFormulaExpression() — the default formula string
  • evaluateDefaultFormula() — evaluates the formula given variables
  • getImpactVariables() — which variables to compute marginal impacts for
  • buildBreakdown() — module-specific breakdown categories

2. Constants & Core Formula

Global Constants

Constant Value Purpose
BASE_SCALE 500 Baseline scale factor — higher means more raw input needed to increase risk
VOLUME_INFLUENCE 200 How much totalCount stretches the scale factor

Scale Factor

$$ \text{scaleFactor} = 500 + \ln(1 + \text{totalCount}) \times 200 $$

totalCount scaleFactor
0 500.00
10 979.86
50 1285.69
100 1423.96
500 1742.52
1000 1882.47

Sigmoid (Risk Score)

$$ \text{riskScore} = 100 \times \left(1 - e^{-\frac{\text{rawInput}}{\text{scaleFactor}}}\right) $$

Health Score

$$ \text{score} = 100 - \text{riskScore} $$


3. DETECTION Module — Worked Example

Calculator: DetectionScoreCalculator

3.1 Default Formula

(urgentCount × 10 + criticalCount × 8 + highCount × 5 + mediumCount × 2 + lowCount × 0.5 + ruleRiskBoost)
  × (1 − resolutionRate / 200)
  + meanThreatScore × 0.3

3.2 Example Snapshot

Field Value
totalCount 120
openCount 85
resolvedCount 30
falsePositiveCount 5
severityDistribution urgent: 3, critical: 10, high: 25, medium: 30, low: 17
meanThreatScore 45
avgAgeHours 360
resolutionRate (30 / 120) × 100 = 25%

3.3 Step 1 — Build Variables

Base variables are computed from snapshot. Key ones for the formula:

Variable Value How Computed
urgentCount 3 From severityDistribution.urgent
criticalCount 10 From severityDistribution.critical
highCount 25 From severityDistribution.high
mediumCount 30 From severityDistribution.medium
lowCount 17 From severityDistribution.low
meanThreatScore 45 From threatScoreStats.mean
resolutionRate 25 (resolvedCount / totalCount) × 100
totalCount 120 Raw from snapshot
ruleRiskBoost 0 Assume no rules for this example

DETECTION also adds darkSourceCount, deepSourceCount, cleanSourceCount from extended.sourceDistribution, but these are not used in the default formula.

3.4 Step 2 — Evaluate Formula

Severity component:

$$ 3 \times 10 + 10 \times 8 + 25 \times 5 + 30 \times 2 + 17 \times 0.5 + 0 = 30 + 80 + 125 + 60 + 8.5 = 303.5 $$

Resolution factor:

$$ 1 - \frac{25}{200} = 1 - 0.125 = 0.875 $$

Threat influence:

$$ 45 \times 0.3 = 13.5 $$

rawInput:

$$ 303.5 \times 0.875 + 13.5 = 265.5625 + 13.5 = 279.0625 $$

3.5 Step 3 — Compute Scale Factor

$$ \text{scaleFactor} = 500 + \ln(1 + 120) \times 200 = 500 + \ln(121) \times 200 = 500 + 4.7958 \times 200 = 500 + 959.16 = 1459.16 $$

3.6 Step 4 — Sigmoid Normalization

$$ \text{riskScore} = 100 \times \left(1 - e^{-279.0625 / 1459.16}\right) = 100 \times \left(1 - e^{-0.1913}\right) $$

$$ e^{-0.1913} = 0.8260 $$

$$ \text{riskScore} = 100 \times (1 - 0.8260) = 100 \times 0.1740 = 17.40 $$

3.7 Step 5 — Health Score

$$ \text{score} = 100 - 17.40 = \boxed{82.60} $$

Interpretation: With 120 detections (3 urgent, 10 critical, 25 high), 25% resolution rate, and moderate threat score, the DETECTION module gets a health score of 82.60 — reflecting moderate risk.


4. ASM Module — Worked Example

Calculator: ASMScoreCalculator

4.1 Default Formula

(vulnUrgent × 10 + vulnCritical × 8 + vulnHigh × 5 + vulnMedium × 2 + vulnLow × 0.5)
  + (portExposureCount × 0.5 + subdomainCount × 0.2)
  + (meanThreatScore × 0.5)
  + ruleRiskBoost

4.2 Example Snapshot

Field Value
totalCount 75
openCount 60
resolvedCount 10
falsePositiveCount 5
severityDistribution urgent: 2, critical: 5, high: 18, medium: 20, low: 15
meanThreatScore 55
avgAgeHours 480
Extended (ASM-specific)
portExposureCount 30
subdomainCount 150
technologyCount 40
ctLogCount 200
dnsNameCount 85
vulnerabilitySeverity urgent: 1, critical: 4, high: 12, medium: 15, low: 8

4.3 Step 1 — Build Variables

ASM overrides buildVariables() to add vulnerability severity and exposure counts. The formula uses vulnUrgent, vulnCritical, etc. — these come from extended.vulnerabilitySeverity, NOT the base severityDistribution.

Variable Value Source
vulnUrgent 1 ext.vulnerabilitySeverity.urgent
vulnCritical 4 ext.vulnerabilitySeverity.critical
vulnHigh 12 ext.vulnerabilitySeverity.high
vulnMedium 15 ext.vulnerabilitySeverity.medium
vulnLow 8 ext.vulnerabilitySeverity.low
portExposureCount 30 ext.portExposureCount
subdomainCount 150 ext.subdomainCount
meanThreatScore 55 threatScoreStats.mean
totalCount 75 Raw snapshot
ruleRiskBoost 0 No rules for this example

4.4 Step 2 — Evaluate Formula

Vulnerability severity component:

$$ 1 \times 10 + 4 \times 8 + 12 \times 5 + 15 \times 2 + 8 \times 0.5 = 10 + 32 + 60 + 30 + 4 = 136 $$

Exposure component:

$$ 30 \times 0.5 + 150 \times 0.2 = 15 + 30 = 45 $$

Threat influence:

$$ 55 \times 0.5 = 27.5 $$

rawInput:

$$ 136 + 45 + 27.5 + 0 = 208.5 $$

4.5 Step 3 — Compute Scale Factor

$$ \text{scaleFactor} = 500 + \ln(1 + 75) \times 200 = 500 + \ln(76) \times 200 = 500 + 4.3307 \times 200 = 500 + 866.14 = 1366.14 $$

4.6 Step 4 — Sigmoid Normalization

$$ \text{riskScore} = 100 \times \left(1 - e^{-208.5 / 1366.14}\right) = 100 \times \left(1 - e^{-0.1526}\right) $$

$$ e^{-0.1526} = 0.8585 $$

$$ \text{riskScore} = 100 \times 0.1415 = 14.15 $$

4.7 Step 5 — Health Score

$$ \text{score} = 100 - 14.15 = \boxed{85.85} $$

Interpretation: 75 ASM items with 1 urgent and 4 critical vulnerabilities, 30 exposed ports, 150 subdomains → health score of 85.85. The high subdomain and port counts add to the exposure component, but the volume-aware sigmoid absorbs their impact at this scale.


5. BP Module — Worked Example

Calculator: BPScoreCalculator

5.1 Default Formula

(activePhishing × 10 + activeDarkWeb × 8 + activeLeakedCredentials × 8 + activeScams × 5
 + activeSocialMedia × 4 + activeRogueApps × 4 + activeCounterfeit × 3 + activeBinLeaks × 6
 + ruleRiskBoost) × (1 − takedownSuccessRate / 200)

Key difference: BP uses a takedown effectiveness multiplier. At 100% takedown success, threats are reduced by 50%. At 0% success, the full raw score is used.

5.2 Example Snapshot

Field Value
totalCount 45
openCount 32
resolvedCount 10
falsePositiveCount 3
severityDistribution urgent: 5, critical: 8, high: 10, medium: 6, low: 3
meanThreatScore 62
avgAgeHours 200
Extended (BP-specific)
activePhishing 8
activeDarkWeb 3
activeSocialMedia 5
activeLeakedCredentials 4
activeRogueApps 2
activeCounterfeit 1
activeBinLeaks 2
activeScams 3
takedownInProgress 6
takedownResolved 10
takedownSuccessRate 40

5.3 Step 1 — Build Variables

BP overrides buildVariables() to add brand-specific threat types and takedown metrics.

Variable Value Source
activePhishing 8 ext.activePhishing
activeDarkWeb 3 ext.activeDarkWeb
activeLeakedCredentials 4 ext.activeLeakedCredentials
activeScams 3 ext.activeScams
activeSocialMedia 5 ext.activeSocialMedia
activeRogueApps 2 ext.activeRogueApps
activeCounterfeit 1 ext.activeCounterfeit
activeBinLeaks 2 ext.activeBinLeaks
takedownSuccessRate 40 ext.takedownSuccessRate
totalCount 45 Raw snapshot
ruleRiskBoost 0 No rules for this example

5.4 Step 2 — Evaluate Formula

Raw threat score:

$$ 8 \times 10 + 3 \times 8 + 4 \times 8 + 3 \times 5 + 5 \times 4 + 2 \times 4 + 1 \times 3 + 2 \times 6 + 0 $$

$$ = 80 + 24 + 32 + 15 + 20 + 8 + 3 + 12 = 194 $$

Takedown factor:

$$ 1 - \frac{40}{200} = 1 - 0.2 = 0.8 $$

rawInput:

$$ 194 \times 0.8 = 155.2 $$

5.5 Step 3 — Compute Scale Factor

$$ \text{scaleFactor} = 500 + \ln(1 + 45) \times 200 = 500 + \ln(46) \times 200 = 500 + 3.8286 \times 200 = 500 + 765.72 = 1265.72 $$

5.6 Step 4 — Sigmoid Normalization

$$ \text{riskScore} = 100 \times \left(1 - e^{-155.2 / 1265.72}\right) = 100 \times \left(1 - e^{-0.1226}\right) $$

$$ e^{-0.1226} = 0.8846 $$

$$ \text{riskScore} = 100 \times 0.1154 = 11.54 $$

5.7 Step 5 — Health Score

$$ \text{score} = 100 - 11.54 = \boxed{88.46} $$

Interpretation: 45 brand protection incidents including 8 phishing and 3 dark web threats, with 40% takedown success rate → health score of 88.46. The takedown factor reduced the raw input by 20%, and the sigmoid keeps the score moderate given the relatively small volume.


6. Rule Risk Boost & Volume Dampening

Rules inject additional risk into the formula through ruleRiskBoost. This is dampened at low data volumes.

6.1 Rule Evaluation

  1. The calculator looks up rules linked to the module via ModuleTrigger
  2. The RuleEngine evaluates each active rule against the variable map
  3. Each triggered rule contributes a configured risk amount
  4. Total contribution = ruleRiskBoost

6.2 Volume Dampening

Before adding ruleRiskBoost to the formula, it's dampened:

$$ \text{volumeDampener} = \min\left(1,\ \sqrt{\frac{\text{totalCount}}{10}}\right) $$

$$ \text{ruleRiskBoost}_{\text{final}} = \text{ruleRiskBoost} \times \text{volumeDampener} $$

totalCount Dampener Effective Boost (if raw = 50)
1 0.32 15.81
5 0.71 35.36
10 1.00 50.00
50 1.00 50.00
100 1.00 50.00

Why: At very low volumes (< 10 items), rules shouldn't dominate the score. A single detection triggering a high-impact rule would otherwise disproportionately swing the score.

6.3 Example with Rules (DETECTION)

Using the DETECTION Module example from Section 3, but now with a rule that adds ruleRiskBoost = 80:

Volume dampening (totalCount = 120):

$$ \text{dampener} = \min(1,\ \sqrt{120 / 10}) = \min(1,\ 3.46) = 1.0 $$

At 120 items, the full boost applies. ruleRiskBoost = 80.

Revised severity component:

$$ 303.5 + 80 = 383.5 $$

rawInput:

$$ 383.5 \times 0.875 + 13.5 = 335.5625 + 13.5 = 349.0625 $$

riskScore:

$$ 100 \times (1 - e^{-349.0625 / 1459.16}) = 100 \times (1 - e^{-0.2392}) = 100 \times (1 - 0.7872) = 21.28 $$

Health score with rules:

$$ 100 - 21.28 = \boxed{78.72} $$

The rule dropped the score from 82.60 → 78.72 (a 3.88-point reduction).


7. Breakdown Construction

Each module produces a ScoreBreakdown with 5 standard categories plus module-specific extensions.

7.1 Standard Breakdown Fields

Field All Modules Meaning
severityScore Weighted sum of severity counts Higher = more severe distribution
volumeScore Volume measure Higher = more open items
threatScore meanThreatScore External threat intelligence score
ageScore (avgAgeHours / 720) × 100 How stale open items are (720h ≈ 30 days = 100%)
resolutionScore 100 − resolutionRate Lower resolution = higher score

7.2 Module-Specific Breakdown Differences

DETECTION:

  • severityScore: urgentCount × 10 + criticalCount × 8 + highCount × 5 + mediumCount × 2
  • volumeScore: openCount (raw count)
  • No extended fields

ASM:

  • severityScore: Uses vulnUrgent/Critical/High/Medium (vulnerability-specific, not general severity)
  • volumeScore: (openCount / max(1, totalCount)) × 100 (percentage, not raw count)
  • Extended:
    • portExposure: portExposureCount × 0.5
    • subdomainExposure: subdomainCount × 0.2
    • technologyExposure: technologyCount × 1.5

BP:

  • severityScore: urgentCount × 10 + criticalCount × 8 + highCount × 5 + mediumCount × 2
  • volumeScore: openCount (raw count)
  • Extended:
    • phishingImpact: activePhishing × 10
    • darkWebImpact: activeDarkWeb × 8
    • credentialLeakImpact: activeLeakedCredentials × 8
    • takedownEffectiveness: takedownSuccessRate
    • socialMediaRisk: activeSocialMedia × 4

7.3 Breakdown Example (DETECTION from Section 3)

Breakdown Field Calculation Value
severityScore 3×10 + 10×8 + 25×5 + 30×2 295
volumeScore 85 (openCount) 85
threatScore 45 (meanThreatScore) 45
ageScore (360 / 720) × 100 50.00
resolutionScore 100 − 25 75
formulaRiskScore sigmoid(rawInput − ruleRiskBoost, scaleFactor) 17.40
ruleRiskBoost 0 0
combinedRawInput 279.0625 279.06

8. Global Score Aggregation — Worked Example

After all modules are calculated, the orchestrator combines them into a single global score.

8.1 Formula

$$ \text{globalRiskScore} = \frac{\sum_{i} \text{riskScore}_i \times \text{weight}i}{\sum{i} \text{weight}_i} $$

$$ \text{globalScore} = 100 - \text{globalRiskScore} $$

Where the sum is over enabled modules only. Weights auto-normalize if they don't sum to 100.

8.2 Example

Using the three module scores we calculated above, with weights from org config:

Module riskScore Weight
DETECTION 17.40 40%
ASM 14.15 35%
BP 11.54 25%

Weighted risk contributions:

$$ \text{DETECTION}: \frac{17.40 \times 40}{100} = 6.96 $$

$$ \text{ASM}: \frac{14.15 \times 35}{100} = 4.9525 $$

$$ \text{BP}: \frac{11.54 \times 25}{100} = 2.885 $$

Total weighted risk:

$$ 6.96 + 4.9525 + 2.885 = 14.7975 $$

Total active weight:

$$ 40 + 35 + 25 = 100 $$

Since total weight = 100, no normalization needed:

$$ \text{globalRiskScore} = 14.7975 $$

$$ \text{globalScore} = 100 - 14.80 = \boxed{85.20} $$

8.3 Weight Normalization Example

If BP is disabled (only DETECTION 40% and ASM 35% active):

$$ \text{totalActiveWeight} = 40 + 35 = 75 $$

$$ \text{totalWeightedRisk} = 6.96 + 4.9525 = 11.9125 $$

$$ \text{globalRiskScore} = \frac{11.9125}{75} \times 100 = 15.88 $$

$$ \text{globalScore} = 100 - 15.88 = \boxed{84.12} $$

The system automatically re-normalizes remaining weights so disabled modules don't leave a gap.


9. Marginal Impact Tracking

The system computes, for each impact variable, how much the risk score drops if that variable decreases by 1. This gives volume-aware per-detection impact.

9.1 How It Works

For each variable in getImpactVariables():

  1. Compute current rawInput and riskScore
  2. Create a copy with that variable reduced by 1
  3. Recompute rawInput and riskScore with the tweaked variables
  4. marginalImpact = currentRisk − tweakedRisk

9.2 Example (DETECTION from Section 3)

Current state: rawInput = 279.06, riskScore = 17.40, scaleFactor = 1459.16

Marginal impact of resolving 1 **criticalCount**:

The critical weight in the formula is 8. Resolution factor is 0.875.

Tweaked rawInput:

$$ 279.0625 - (8 \times 0.875) = 279.0625 - 7.0 = 272.0625 $$

Tweaked riskScore:

$$ 100 \times (1 - e^{-272.0625 / 1459.16}) = 100 \times (1 - e^{-0.1864}) = 100 \times (1 - 0.8299) = 17.01 $$

Marginal impact:

$$ 17.40 - 17.01 = \boxed{0.39} $$

Resolving 1 critical detection reduces risk by 0.39 points.

Marginal impact of resolving 1 **urgentCount**:

Urgent weight = 10. Effective reduction = 10 × 0.875 = 8.75.

$$ \text{tweakedRaw} = 279.0625 - 8.75 = 270.3125 $$

$$ \text{tweakedRisk} = 100 \times (1 - e^{-270.3125 / 1459.16}) = 100 \times (1 - e^{-0.1852}) = 16.89 $$

$$ \text{marginal} = 17.40 - 16.89 = \boxed{0.51} $$

Resolving 1 urgent detection reduces risk by 0.51 points — more impactful than a critical.

9.3 Why Marginals Decrease at Scale

At higher volumes and rawInput values, the sigmoid flattens. Adding or removing one detection has less effect. This is intentional: at 1000 detections, resolving one matters less than when you have 10.


10. Quick Reference Table

Module Default Formulas

Module Default Formula Key Multiplier
DETECTION (urgentCount×10 + criticalCount×8 + highCount×5 + mediumCount×2 + lowCount×0.5 + ruleRiskBoost) × (1 − resolutionRate/200) + meanThreatScore×0.3 Resolution factor × Severity
ASM (vulnUrgent×10 + vulnCritical×8 + vulnHigh×5 + vulnMedium×2 + vulnLow×0.5) + (portExposureCount×0.5 + subdomainCount×0.2) + (meanThreatScore×0.5) + ruleRiskBoost Additive (severity + exposure + threat)
BP (activePhishing×10 + activeDarkWeb×8 + activeLeakedCredentials×8 + activeScams×5 + activeSocialMedia×4 + activeRogueApps×4 + activeCounterfeit×3 + activeBinLeaks×6 + ruleRiskBoost) × (1 − takedownSuccessRate/200) Takedown factor × Threat types

Severity Weights (Across Modules)

Severity Weight
Urgent 10
Critical 8
High 5
Medium 2
Low 0.5

Score Interpretation

Health Score Risk Level Meaning
90–100 Minimal Few or low-severity items, good resolution rate
70–89 Low Some moderate-to-high items, room for improvement
50–69 Moderate Significant open items with notable severity
30–49 High Many high/critical items with poor resolution
0–29 Critical Severe exposure across the board

Impact Variable Mapping

Module Impact Variables (marginal tracking)
DETECTION urgentCount, criticalCount, highCount, mediumCount, lowCount
ASM vulnUrgent, vulnCritical, vulnHigh, vulnMedium, vulnLow, portExposureCount, subdomainCount
BP activePhishing, activeDarkWeb, activeLeakedCredentials, activeScams, activeSocialMedia, activeRogueApps, activeCounterfeit, activeBinLeaks