SkillSpector Risk Score Algorithm: How It Calculates Security Risk
SkillSpector calculates risk by mapping finding severities to fixed points (CRITICAL=50, HIGH=25, MEDIUM=10, LOW=5), applying a 1.3× multiplier when executable scripts are present, clamping the result to 0–100, and translating the final value into severity bands and installation recommendations.
NVIDIA's SkillSpector evaluates AI skill packages for security vulnerabilities using a deterministic scoring system. The SkillSpector risk score algorithm aggregates detected findings into a numeric value that determines whether a skill is safe to install. This calculation occurs in the analysis pipeline's report generation phase before outputting final security guidance.
Five-Step Risk Calculation Process
The private _compute_risk_score function in src/skillspector/nodes/report.py implements the core algorithm through a sequential five-step process.
Step 1: Map Finding Severity to Points
Each security finding contributes a fixed number of points based on its severity level. According to the source code in src/skillspector/nodes/report.py, the point assignments are:
- CRITICAL → +50 points
- HIGH → +25 points
- MEDIUM → +10 points
- LOW (or missing severity) → +5 points
The algorithm sums these values across all findings in the scanned skill package.
Step 2: Apply Executable Script Multiplier
If the skill contains executable scripts—indicated by the has_executable_scripts boolean flag—the summed point total is multiplied by 1.3 and rounded down. This escalation reflects the increased deployment risk of installable scripts compared to static configuration files.
Step 3: Clamp to Valid Range
After multiplier application, the algorithm constrains the score to a 0–100 range using standard clamping logic. This ensures consistent numeric bounds regardless of how many vulnerabilities are detected.
Step 4: Derive Severity Band
The clamped numeric score maps to categorical risk bands using specific thresholds defined in the source:
- ≥ 81 → CRITICAL
- ≥ 51 → HIGH
- ≥ 21 → MEDIUM
- < 21 → LOW
Step 5: Generate Installation Recommendation
Each severity band triggers a specific security recommendation that appears in the final report:
- LOW → SAFE
- MEDIUM → CAUTION
- HIGH and CRITICAL → DO_NOT_INSTALL
The _compute_risk_score function returns a tuple containing the numeric score, severity band string, and recommendation string.
Implementation Architecture
The risk calculation integrates with SkillSpector's data models and state management. The Finding class in src/skillspector/models.py provides the severity attributes, while SkillspectorState in src/skillspector/state.py stores the has_executable_scripts flag and receives the final output.
Unit tests in tests/nodes/test_report.py validate the algorithm's behavior, including threshold boundaries and multiplier arithmetic.
Code Examples
Direct Calculation Using the Internal Function
Access the _compute_risk_score function directly to compute scores for custom finding sets:
from skillspector.nodes.report import _compute_risk_score
from skillspector.models import Finding
# Synthetic findings representing different severities
findings = [
Finding(rule_id="R1", severity="critical", file="a.py",
start_line=1, end_line=5, confidence=0.9, message="Critical issue"),
Finding(rule_id="R2", severity="high", file="b.py",
start_line=10, end_line=12, confidence=0.8, message="High issue"),
Finding(rule_id="R3", severity="medium", file="c.py",
start_line=20, end_line=20, confidence=0.7, message="Medium issue"),
]
# Calculate without executable scripts
score, band, recommendation = _compute_risk_score(
findings,
has_executable_scripts=False
)
print(score, band, recommendation)
# Output: 85 CRITICAL DO_NOT_INSTALL
Using the Public Report Node
Use the public report node function as the CLI does, passing a complete state object:
from skillspector.state import SkillspectorState
from skillspector.nodes.report import report
state = SkillspectorState(
findings=findings,
component_metadata=[{
"path": "a.py",
"type": "python",
"lines": 42,
"executable": False
}],
has_executable_scripts=False,
output_format="json",
)
result = report(state)
print(result["risk_score"]) # 85
print(result["risk_severity"]) # CRITICAL
print(result["risk_recommendation"]) # DO_NOT_INSTALL
Severity Thresholds and Edge Cases
The 1.3× executable script multiplier can elevate scores across band boundaries. For example, a base score of 63 becomes 81 after calculation (63 × 1.3 = 81.9, rounded down to 81), technically meeting the CRITICAL threshold of ≥81 despite originating from HIGH-severity findings.
Scores exactly matching threshold values adopt the higher severity band. A score of 51 maps to HIGH risk, while 50 remains MEDIUM.
Summary
- Point system: CRITICAL (50), HIGH (25), MEDIUM (10), LOW (5) points per finding
- Executable escalation: 1.3× multiplier when
has_executable_scriptsis True - Range limits: Clamped to 0–100 after multiplication
- Risk bands: ≥81 CRITICAL, ≥51 HIGH, ≥21 MEDIUM, <21 LOW
- Recommendations: SAFE, CAUTION, or DO_NOT_INSTALL based on final band
- Core implementation:
_compute_risk_scoreinsrc/skillspector/nodes/report.py
Frequently Asked Questions
How does SkillSpector handle multiple findings of the same severity?
The algorithm sums all finding points linearly without diminishing returns. Three CRITICAL findings contribute 150 points before clamping (50 × 3), which the clamping logic reduces to the maximum score of 100. This linear aggregation ensures that volume of vulnerabilities directly impacts the risk calculation.
What happens if a finding lacks a severity assignment?
Findings with missing or undefined severity values default to the LOW severity tier, contributing 5 points to the aggregate score. This conservative default ensures unclassified issues still impact the risk calculation rather than being silently ignored.
Can the executable script multiplier push a score into a higher risk band?
Yes. The 1.3× multiplier frequently elevates scores across threshold boundaries. For instance, a base score of 70 (HIGH) becomes 91 after multiplication (70 × 1.3 = 91), crossing into the CRITICAL band (≥81). This reflects the increased danger of executable code containing existing vulnerabilities.
Where is the risk score stored within SkillSpector's runtime state?
The calculated score persists in the SkillspectorState object defined in src/skillspector/state.py. The report node writes the final tuple values—numeric score, severity band, and recommendation—into the state dictionary, making them available for JSON, Terminal, or Markdown output formatting in downstream pipeline stages.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →