-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathapp.py
More file actions
96 lines (83 loc) Β· 4.79 KB
/
Copy pathapp.py
File metadata and controls
96 lines (83 loc) Β· 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""SkillSpector Dashboard β Streamlit visual frontend for NVIDIA/SkillSpector CLI."""
import json, os, shutil, subprocess
import pandas as pd
import streamlit as st
st.set_page_config(page_title="SkillSpector", page_icon="π‘οΈ", layout="wide")
# ββ Environment guard ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def _find_cli():
if p := shutil.which("skillspector"): return p
for p in ["skillspector/.venv/Scripts/skillspector.exe",
"skillspector/.venv/bin/skillspector"]:
if os.path.exists(p): return os.path.abspath(p)
return None
CLI = _find_cli()
if not CLI:
st.error("β `skillspector` not found. Run `pip install skillspector` then reload.")
st.stop()
# ββ Sidebar ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
with st.sidebar:
st.title("π‘οΈ SkillSpector")
mode = st.radio("Source", ["Local Path", "GitHub URL"])
label = "Directory / file path" if mode == "Local Path" else "GitHub HTTPS URL"
target = st.text_input(label, placeholder="e.g. skillspector/tests/fixtures/malicious_skill")
no_llm = st.checkbox("Static only (no LLM)", value=True)
scan = st.button("π Run Scan", use_container_width=True, type="primary")
# ββ Scanner ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def run_scan(target: str, no_llm: bool) -> dict | None:
cmd = [CLI, "scan", target, "--format", "json"] + (["--no-llm"] if no_llm else [])
try:
r = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8",
errors="replace", env={**os.environ, "PYTHONIOENCODING": "utf-8"})
raw = r.stdout.strip()
return json.loads(raw[raw.find("{") : raw.rfind("}") + 1])
except (json.JSONDecodeError, ValueError) as e:
st.error(f"JSON parse error: {e}"); st.code(r.stdout[:2000]); return None
except Exception as e:
st.error(f"Scan failed: {e}"); return None
# ββ Helper: flatten issue fields from real JSON schema βββββββββββββββββββββββ
# Real schema: id, severity, category, location.{file, start_line}, finding, explanation, remediation
def _flat(i: dict) -> dict:
loc = i.get("location") or {}
msg = i.get("finding") or i.get("explanation") or "β"
return {"Rule": i.get("id","β"), "Severity": i.get("severity","β"),
"Category": i.get("category","β"),
"File": loc.get("file","β"), "Line": loc.get("start_line","β"),
"Message": msg[:120]}
if scan:
if not target.strip():
st.warning("Enter a path or URL in the sidebar first.")
else:
with st.spinner("Scanningβ¦"):
st.session_state["data"] = run_scan(target.strip(), no_llm)
# ββ Dashboard ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if d := st.session_state.get("data"):
ra = d.get("risk_assessment", {})
score = ra.get("score", 0)
sev = ra.get("severity", "LOW").upper()
rec = ra.get("recommendation", "SAFE").replace("_", " ")
COLOR = {"CRITICAL":"π΄","HIGH":"π ","MEDIUM":"π‘","LOW":"π’"}.get(sev,"π’")
issues = d.get("issues", [])
st.markdown(f"## {COLOR} Risk Score: **{score}/100** β {rec}")
st.divider()
c1, c2, c3 = st.columns(3)
c1.metric("Severity Band", sev)
c2.metric("Issues Found", len(issues))
c3.metric("Files Scanned", len(d.get("components", [])))
# Severity chart
counts = pd.Series({s: sum(1 for i in issues if i.get("severity","").upper()==s)
for s in ("CRITICAL","HIGH","MEDIUM","LOW")}).rename("Findings")
st.bar_chart(counts)
# Table + expanders
if issues:
st.dataframe(pd.DataFrame([_flat(i) for i in issues]),
use_container_width=True, hide_index=True)
for i in issues:
f = _flat(i)
with st.expander(f"[{f['Severity']}] {f['Rule']} β {f['File']} βΊ {f['Message'][:60]}"):
st.write(i.get("explanation") or i.get("finding") or "No details.")
if i.get("remediation"):
st.info(i["remediation"])
else:
st.success("β
No security issues detected. Skill appears safe.")
else:
st.info("π Enter a target in the sidebar and click **Run Scan** to begin.")