# How SkillSpector's Risk Scoring Algorithm Calculates Severity

> Discover how SkillSpector's risk scoring algorithm calculates severity. Learn about point values, multipliers, and clamping for accurate risk assessment.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: internals
- Published: 2026-06-18

---

**SkillSpector calculates severity by assigning fixed point values to each finding based on its severity level (CRITICAL=50, HIGH=25, MEDIUM=10, LOW=5), applying a 1.3x multiplier if the skill contains executable scripts, clamping the result to 0-100, and mapping it against defined thresholds to determine the final risk band and recommendation.**

The NVIDIA/SkillSpector repository implements a deterministic risk assessment pipeline that transforms raw security findings into actionable severity scores. At the heart of this system lies the `_compute_risk_score` function in the Report node, which aggregates individual finding severities into a normalized 0-100 risk score. This score drives the entire reporting workflow, from SARIF outputs to terminal displays.

## The Five-Step Calculation Process

The algorithm processes findings through a sequential pipeline that combines additive scoring with conditional multipliers and threshold-based mapping.

### Step 1: Assign Base Points by Severity

Each finding contributes a fixed number of points based on its severity annotation. As implemented in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) (lines 82-92), the scoring system uses the following weights:

- **CRITICAL** → **50 points**
- **HIGH** → **25 points**
- **MEDIUM** → **10 points**
- **LOW** → **5 points**

The function iterates through the findings list and accumulates these base values into a running subtotal.

### Step 2: Apply the Executable Script Multiplier

If the skill contains any executable scripts (indicated by the boolean flag `has_executable_scripts`), the subtotal from Step 1 is multiplied by **1.3** and rounded down to the nearest integer. This logic resides in lines 92-94 of [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py), reflecting the increased risk profile of skills that can execute arbitrary code.

### Step 3: Clamp to Valid Range

Following the multiplier application, the algorithm constrains the result to the interval **[0, 100]** (lines 94-95). This ensures that even configurations with numerous critical findings or executable scripts cannot exceed the defined maximum score.

### Step 4: Map to Risk Severity Bands

The clamped score is compared against ordered thresholds defined in the `_RISK_SEVERITY_BANDS` constant (lines 54-56). The algorithm selects the first band whose threshold is less than or equal to the calculated score:

- **81+** → **CRITICAL**
- **51-80** → **HIGH**
- **21-50** → **MEDIUM**
- **0-20** → **LOW**

### Step 5: Generate Human-Readable Recommendations

Finally, the severity band maps to a specific action via the `_RISK_RECOMMENDATION` dictionary (lines 56-60). This translates abstract severity levels into concrete guidance such as "SAFE", "CAUTION", or "DO_NOT_INSTALL".

## Implementation in the Codebase

The core logic resides in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) within the `_compute_risk_score` function. This helper returns a tuple of `(score, severity_band, recommendation)`, which the public `report(state)` function (lines 63-66) consumes to assemble the final analysis document.

The `Finding` model defined in [`src/skillspector/models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/models.py) provides the `severity` field that drives the initial point calculation, ensuring type-safe severity annotations throughout the pipeline.

## Practical Example

The following example demonstrates how multiple findings combine into a final risk assessment:

```python
from skillspector.nodes.report import _compute_risk_score
from skillspector.models import Finding

# Example findings with varying severities

findings = [
    Finding(rule_id="R1", severity="HIGH"),      # +25 points

    Finding(rule_id="R2", severity="LOW"),       # +5 points

    Finding(rule_id="R3", severity="CRITICAL"),  # +50 points

]

# Skill contains executable payload

has_executable_scripts = True

score, severity, recommendation = _compute_risk_score(
    findings, 
    has_executable_scripts
)

print(f"Score: {score}")                    # Output: 100 (104 clamped to 100)

print(f"Severity band: {severity}")       # Output: CRITICAL

print(f"Recommendation: {recommendation}") # Output: DO_NOT_INSTALL

```

In this scenario, the base calculation yields 80 points (25+5+50), which becomes 104 after the 1.3x multiplier (80 × 1.3 = 104). The clamping logic caps this at 100, triggering the CRITICAL severity band (≥81) and corresponding "DO_NOT_INSTALL" recommendation.

## Integration with the Reporting Pipeline

The `_compute_risk_score` function serves as the computational backend for SkillSpector's reporting infrastructure. The public `report(state)` helper invokes this calculation to populate SARIF output files, JSON/Markdown reports, and terminal color coding. By centralizing severity calculation in a single function, the codebase ensures consistent risk interpretation across all output formats.

## Summary

- **Base scoring**: Fixed points per severity (CRITICAL=50, HIGH=25, MEDIUM=10, LOW=5) in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) lines 82-92.
- **Risk multiplier**: 1.3x boost for skills with executable scripts, applied at lines 92-94.
- **Range enforcement**: Scores clamped to [0, 100] regardless of input volume.
- **Threshold mapping**: Bands defined at 81 (CRITICAL), 51 (HIGH), 21 (MEDIUM), and 0 (LOW).
- **Output**: Returns a tuple of `(score, severity_band, recommendation)` consumed by the reporting pipeline.

## Frequently Asked Questions

### What is the maximum possible risk score in SkillSpector?

The maximum risk score is **100**. Even if the base points plus executable script multiplier calculate to a higher value (e.g., 104), the algorithm clamps the result to the 0-100 range at lines 94-95 of [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py).

### How does the executable script multiplier affect the final severity?

The multiplier increases the base subtotal by **30%** (multiplied by 1.3 and rounded down). This can bump a skill from HIGH to CRITICAL severity if the multiplied score crosses the 81-point threshold. For example, a base score of 70 becomes 91 after multiplication, triggering the CRITICAL band.

### Where are the severity thresholds defined?

Thresholds are defined in the `_RISK_SEVERITY_BANDS` constant at lines 54-56 of [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py). The ordered dictionary maps integer thresholds to severity strings: 81→CRITICAL, 51→HIGH, 21→MEDIUM, and 0→LOW.

### What recommendation is assigned to a CRITICAL severity rating?

According to the `_RISK_RECOMMENDATION` mapping at lines 56-60, a CRITICAL severity typically maps to **"DO_NOT_INSTALL"** or equivalent high-risk warnings. The exact string depends on the static configuration defined in the Report node, but consistently indicates that the skill poses severe security risks.