NVIDIA SkillSpector Guide: Scanning AI Skills for Security Risks with Static Analysis and SARIF Reports
print(“Batch scanning the whole corpus (static-only)…\n”)
summary_rows = []
all_findings = []
for skill in SKILLS:
res = scan(skill, use_llm=False, output_format=”json”)
fnds = findings_of(res)
summary_rows.append({
“skill”: skill.name,
“risk_score”: res.get(“risk_score”),
“severity”: res.get(“risk_severity”),
“recommendation”: res.get(“risk_recommendation”),
“num_findings”: len(fnds),
“has_executable”: res.get(“has_executable_scripts”),
})
for f in fnds:
all_findings.append({
“skill”: skill.name,
“rule_id”: f.get(“rule_id”),
“severity”: str(f.get(“severity”)),
“category”: f.get(“category”),
“message”: f.get(“message”),
“file”: f.get(“file”),
“line”: f.get(“start_line”),
“confidence”: f.get(“confidence”),
})
summary_df = pd.DataFrame(summary_rows).sort_values(“risk_score”, ascending=False)
findings_df = pd.DataFrame(all_findings)
print(“──── Risk summary ────”)
print(summary_df.to_string(index=False))
print(f”\nTotal findings across corpus: {len(findings_df)}\n”)
if not findings_df.empty:
print(“──── Findings by category ────”)
print(findings_df[“category”].value_counts().to_string())
print(“\n──── Findings by severity ────”)
print(findings_df[“severity”].value_counts().to_string())
print()
def _normalize_sev(s: str) -> str:
s = str(s).upper()
for level in (“CRITICAL”, “HIGH”, “MEDIUM”, “LOW”):
if level in s:
return level
return s
if not summary_df.empty:
fig, axes = plt.subplots(1, 3, figsize=(16, 4.5))
colors = {“CRITICAL”: “#7f1d1d”, “HIGH”: “#dc2626”,
“MEDIUM”: “#f59e0b”, “LOW”: “#16a34a”}
sev_norm = summary_df[“severity”].map(_normalize_sev)
axes[0].barh(summary_df[“skill”], summary_df[“risk_score”],
color=[colors.get(s, “#3b82f6”) for s in sev_norm])
axes[0].set_title(“Risk score per skill (0–100)”)
axes[0].set_xlim(0, 100)
axes[0].invert_yaxis()
for y, v in zip(summary_df[“skill”], summary_df[“risk_score”]):
axes[0].text((v or 0) + 1, y, str(v), va=”center”, fontsize=9)
if not findings_df.empty:
sev_counts = (findings_df[“severity”].map(_normalize_sev)
.value_counts()
.reindex([“CRITICAL”, “HIGH”, “MEDIUM”, “LOW”]).dropna())
axes[1].bar(sev_counts.index, sev_counts.values,
color=[colors.get(s, “#3b82f6”) for s in sev_counts.index])
axes[1].set_title(“Findings by severity”)
else:
axes[1].set_visible(False)
if not findings_df.empty:
cat_counts = findings_df[“category”].value_counts().head(10)
axes[2].barh(cat_counts.index[::-1], cat_counts.values[::-1], color=”#3b82f6″)
axes[2].set_title(“Top finding categories”)
else:
axes[2].set_visible(False)
plt.tight_layout()
out_png = WORKDIR / “skillspector_dashboard.png”
plt.savefig(out_png, dpi=120, bbox_inches=”tight”)
print(f”📊 Saved dashboard -> {out_png}”)
plt.show()
summary_rows = []
all_findings = []
for skill in SKILLS:
res = scan(skill, use_llm=False, output_format=”json”)
fnds = findings_of(res)
summary_rows.append({
“skill”: skill.name,
“risk_score”: res.get(“risk_score”),
“severity”: res.get(“risk_severity”),
“recommendation”: res.get(“risk_recommendation”),
“num_findings”: len(fnds),
“has_executable”: res.get(“has_executable_scripts”),
})
for f in fnds:
all_findings.append({
“skill”: skill.name,
“rule_id”: f.get(“rule_id”),
“severity”: str(f.get(“severity”)),
“category”: f.get(“category”),
“message”: f.get(“message”),
“file”: f.get(“file”),
“line”: f.get(“start_line”),
“confidence”: f.get(“confidence”),
})
summary_df = pd.DataFrame(summary_rows).sort_values(“risk_score”, ascending=False)
findings_df = pd.DataFrame(all_findings)
print(“──── Risk summary ────”)
print(summary_df.to_string(index=False))
print(f”\nTotal findings across corpus: {len(findings_df)}\n”)
if not findings_df.empty:
print(“──── Findings by category ────”)
print(findings_df[“category”].value_counts().to_string())
print(“\n──── Findings by severity ────”)
print(findings_df[“severity”].value_counts().to_string())
print()
def _normalize_sev(s: str) -> str:
s = str(s).upper()
for level in (“CRITICAL”, “HIGH”, “MEDIUM”, “LOW”):
if level in s:
return level
return s
if not summary_df.empty:
fig, axes = plt.subplots(1, 3, figsize=(16, 4.5))
colors = {“CRITICAL”: “#7f1d1d”, “HIGH”: “#dc2626”,
“MEDIUM”: “#f59e0b”, “LOW”: “#16a34a”}
sev_norm = summary_df[“severity”].map(_normalize_sev)
axes[0].barh(summary_df[“skill”], summary_df[“risk_score”],
color=[colors.get(s, “#3b82f6”) for s in sev_norm])
axes[0].set_title(“Risk score per skill (0–100)”)
axes[0].set_xlim(0, 100)
axes[0].invert_yaxis()
for y, v in zip(summary_df[“skill”], summary_df[“risk_score”]):
axes[0].text((v or 0) + 1, y, str(v), va=”center”, fontsize=9)
if not findings_df.empty:
sev_counts = (findings_df[“severity”].map(_normalize_sev)
.value_counts()
.reindex([“CRITICAL”, “HIGH”, “MEDIUM”, “LOW”]).dropna())
axes[1].bar(sev_counts.index, sev_counts.values,
color=[colors.get(s, “#3b82f6”) for s in sev_counts.index])
axes[1].set_title(“Findings by severity”)
else:
axes[1].set_visible(False)
if not findings_df.empty:
cat_counts = findings_df[“category”].value_counts().head(10)
axes[2].barh(cat_counts.index[::-1], cat_counts.values[::-1], color=”#3b82f6″)
axes[2].set_title(“Top finding categories”)
else:
axes[2].set_visible(False)
plt.tight_layout()
out_png = WORKDIR / “skillspector_dashboard.png”
plt.savefig(out_png, dpi=120, bbox_inches=”tight”)
print(f”📊 Saved dashboard -> {out_png}”)
plt.show()


