How SkillSpector Calculates Its Security Risk Score: Algorithm and Implementation
SkillSpector computes security risk scores by aggregating severity-weighted points from findings, applying a 1.3× multiplier when executable scripts are detected, and mapping the result to a 0-100 scale that determines whether a skill bundle is safe to install.
NVIDIA's SkillSpector analyzes AI skill bundles for security vulnerabilities and generates a quantitative risk assessment to guide installation decisions. Understanding how SkillSpector calculates its risk score helps developers interpret scan results and prioritize remediation efforts. The scoring logic resides in the private helper _compute_risk_score within src/skillspector/nodes/report.py.
The Core Scoring Algorithm in _compute_risk_score
The risk calculation follows a four-stage pipeline implemented in src/skillspector/nodes/report.py. Each stage transforms the raw findings into actionable security guidance.
Base Point Allocation by Severity
Every finding contributes fixed points based on its severity level. The algorithm iterates through the findings list (lines 81-92) and accumulates:
- CRITICAL: +50 points
- HIGH: +25 points
- MEDIUM: +10 points
- LOW: +5 points
Executable Script Multiplier
If the scanned bundle contains executable scripts (has_executable_scripts == True), the subtotal receives a 1.3× multiplier (lines 93-95). The result is rounded down to the nearest integer and clamped to the range 0-100 to ensure the score remains within bounds.
Severity Band Classification
The numeric score maps to a severity label using the ordered list _RISK_SEVERITY_BANDS (lines 96-100):
- CRITICAL: Score ≥ 81
- HIGH: Score ≥ 51
- MEDIUM: Score ≥ 21
- LOW: Score < 21
Recommendation Mapping
Each severity band translates to a human-readable recommendation via _RISK_RECOMMENDATION (lines 55-60 and 101):
- LOW →
SAFE - MEDIUM →
CAUTION - HIGH or CRITICAL →
DO_NOT_INSTALL
The function returns a tuple (score, severity_band, recommendation) that the report node injects into the final output.
Practical Implementation Examples
You can interact with the scoring system programmatically or via the CLI.
Programmatic Usage
Import _compute_risk_score directly to calculate risk for custom finding sets:
from skillspector.nodes.report import _compute_risk_score
from skillspector.models import Finding
# Example findings
findings = [
Finding(rule_id="S001", severity="HIGH", message="Unsafe exec", file="script.py", start_line=1, end_line=5, confidence=0.9),
Finding(rule_id="S002", severity="MEDIUM", message="Hard‑coded secret", file="config.yml", start_line=10, end_line=10, confidence=0.8),
]
# Suppose the bundle contains executable scripts
has_executable_scripts = True
score, severity, recommendation = _compute_risk_score(findings, has_executable_scripts)
print(f"Score: {score}") # → 78 ( (25+10) * 1.3 = 45.5 → int(45) → capped at 100, then severity band HIGH)
print(f"Severity: {severity}") # → HIGH
print(f"Recommendation: {recommendation}") # → DO_NOT_INSTALL
CLI Usage
The command-line interface performs the same calculation internally when scanning skill bundles:
# Using the CLI (reports the same calculation internally)
skillsppector scan path/to/skill_bundle --output-format json
# The JSON will contain:
# {
# "risk_assessment": {
# "score": 78,
# "severity": "HIGH",
# "recommendation": "DO_NOT_INSTALL"
# },
# …
# }
Key Files in the Risk Calculation Pipeline
Several modules collaborate to produce the final risk assessment:
src/skillspector/nodes/report.py: Contains_compute_risk_score, the severity bands list, and the recommendation mapping dictionary.src/skillspector/models.py: Defines theFindingdataclass whoseseverityfield feeds the scoring algorithm.src/skillspector/state.py: Stores thehas_executable_scriptsboolean and the computed risk fields for downstream processing.src/skillspector/cli.py: Invokes the report node, which calls_compute_risk_scoreand renders results to JSON, Markdown, or terminal output.
Summary
- SkillSpector risk score calculation begins with fixed point values: 50 for CRITICAL, 25 for HIGH, 10 for MEDIUM, and 5 for LOW severity findings.
- An executable script multiplier of 1.3× increases the risk when bundled code can execute directly, with results clamped to a 0-100 range.
- The final score maps to severity bands (CRITICAL ≥81, HIGH ≥51, MEDIUM ≥21, LOW <21) that trigger specific installation recommendations.
- The
_compute_risk_scorehelper insrc/skillspector/nodes/report.pyreturns a tuple containing the numeric score, severity label, and action recommendation.
Frequently Asked Questions
What is the maximum possible risk score in SkillSpector?
The risk score is explicitly clamped to a maximum of 100 regardless of how many findings are present or whether the executable script multiplier is applied. This normalization ensures consistent severity band mapping across all scanned bundles.
How does the executable script multiplier affect the final score?
When has_executable_scripts is True, the base point total is multiplied by 1.3 and rounded down to the nearest integer. For example, a subtotal of 45 points becomes 58 points after multiplication, potentially pushing the bundle into a higher severity band.
Where does SkillSpector store the calculated risk assessment?
The computed values persist in the application state managed by src/skillspector/state.py, which tracks has_executable_scripts and the resulting risk_* fields. The report node later retrieves these values to generate the final "risk_assessment" section in JSON or Markdown outputs.
What recommendation does SkillSpector give for CRITICAL severity findings?
Any bundle scoring 81 or higher (CRITICAL band) receives the DO_NOT_INSTALL recommendation, as do HIGH severity bundles (scores 51-80). Only LOW severity bundles (<21 points) receive the SAFE recommendation, while MEDIUM bundles (21-50) trigger CAUTION.
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 →