# How SkillSpector Calculates Its Risk Scoring Algorithm: A Deep Dive into NVIDIA’s Security Scanner

> Discover how SkillSpector calculates its risk scoring algorithm, aggregating findings, applying multipliers, and mapping results for clear security recommendations.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: deep-dive
- Published: 2026-06-21

---

**SkillSpector's risk scoring algorithm aggregates security findings by severity, applies a multiplier for executable scripts, and maps the result to a severity band and installation recommendation.**

SkillSpector is NVIDIA’s open-source security scanner for AI skill bundles. At the heart of its reporting module lies a deterministic **risk scoring algorithm** that quantifies security threats into a single 0–100 score. This calculation, implemented in the private helper `_compute_risk_score` within [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py), transforms raw vulnerability findings into actionable installation guidance.

## Base Point Values and Severity Weights

The algorithm begins with a simple additive model where each finding contributes fixed points based on its severity level. This "v1 rules" approach ensures consistent scoring regardless of the total number of files scanned.

In [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) (lines 81–92), the function iterates over the findings list and accumulates points according to the following scale:

- **CRITICAL** findings add **50** points
- **HIGH** severity findings add **25** points
- **MEDIUM** severity findings add **10** points
- **LOW** severity findings add **5** points

## The Executable Script Multiplier

After calculating the base subtotal, the algorithm checks for **executable scripts** within the scanned bundle. If the state flag `has_executable_scripts` evaluates to `True`, the current subtotal is multiplied by **1.3** and rounded down to the nearest integer.

This multiplier is applied between lines 93–95 in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py). The final result is then clamped to the range **0–100** to ensure the score remains within bounds.

## Severity Band Mapping and Installation Recommendations

### Mapping Numeric Scores to Labels

The numeric score maps to a human-readable severity band through the ordered list `_RISK_SEVERITY_BANDS`. The logic at lines 96–100 implements the following thresholds:

- **≥ 81** → **CRITICAL**
- **≥ 51** → **HIGH**
- **≥ 21** → **MEDIUM**
- **< 21** → **LOW**

### Installation Recommendations

Each severity band maps to a specific recommendation via the `_RISK_RECOMMENDATION` dictionary (defined at lines 55–60 and applied at line 101):

- **LOW** → **SAFE**
- **MEDIUM** → **CAUTION**
- **HIGH** and **CRITICAL** → **DO_NOT_INSTALL**

## Source Code Implementation and Usage

The core logic resides in the private function `_compute_risk_score` inside [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py). This helper accepts a list of `Finding` objects (defined in [`src/skillspector/models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/models.py)) and a boolean flag indicating executable presence.

The function returns a tuple of `(score, severity_band, recommendation)`, which the `report` node injects into the final output under the `risk_assessment` section.

Here is a practical example demonstrating the calculation:

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

# Example findings: one CRITICAL and one MEDIUM

findings = [
    Finding(rule_id="S001", severity="CRITICAL", message="Arbitrary code execution", 
            file="main.py", start_line=1, end_line=5, confidence=0.95),
    Finding(rule_id="S002", severity="MEDIUM", message="Hard-coded secret", 
            file="config.yml", start_line=10, end_line=10, confidence=0.8),
]

# Bundle contains executable scripts, triggering the 1.3x multiplier

has_executable_scripts = True

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

print(f"Score: {score}")                    # Output: 78 ((50+10)*1.3=78)

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

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

```

You can also observe this calculation via the command line interface:

```bash

# Using the CLI (performs the same calculation internally)

skillspector scan path/to/skill_bundle --output-format json

# The JSON output includes:

# {

#   "risk_assessment": {

#     "score": 78,

#     "severity": "HIGH",

#     "recommendation": "DO_NOT_INSTALL"

#   }

# }

```

## Summary

- **Base scoring**: Fixed points per severity (50/25/10/5) summed in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py) lines 81–92.
- **Executable multiplier**: 1.3x boost when `has_executable_scripts` is True, applied at lines 93–95 with clamping to 0–100.
- **Severity mapping**: Numeric scores translate to CRITICAL (≥81), HIGH (≥51), MEDIUM (≥21), or LOW (<21) bands at lines 96–100.
- **Final recommendation**: Maps to SAFE, CAUTION, or DO_NOT_INSTALL based on the severity band.

## Frequently Asked Questions

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

The algorithm clamps the final score to a maximum of **100**. Even if the base points multiplied by 1.3 exceed 100, the result is capped at this ceiling before severity band mapping occurs.

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

The **1.3 multiplier** can push a MEDIUM score into HIGH territory, changing the recommendation from **CAUTION** to **DO_NOT_INSTALL**. This reflects the increased danger of executable code combined with security findings.

### Where does SkillSpector store the risk score during execution?

The computed score, severity band, and recommendation are stored in the application state (defined in [`src/skillspector/state.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/state.py)) under the `risk_assessment` section, making them available to downstream nodes and the final report formatter.

### Can I customize the point values or severity thresholds?

Currently, the point values (50/25/10/5) and band thresholds (81/51/21) are hardcoded as **v1 rules** in [`src/skillspector/nodes/report.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/report.py). To modify them, you would need to edit the source code and adjust the `_RISK_SEVERITY_BANDS` list and the scoring logic in `_compute_risk_score`.