CoolFace
Apppublic

Hariommm/libraries-scanner

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
app.py215 linesDownload Raw Back to root
1import streamlit as st2import requests3import pandas as pd4from datetime import datetime5import tempfile6import time7import plotly.express as px8import base649 10# --- 1. UI SETUP ---11st.set_page_config(page_title="Libraries Scanner", layout="wide", page_icon="๐Ÿ“ฆ")12st.title("๐Ÿ“ฆ Libraries Scanner")13st.markdown("Live Vulnerability (CVE) & Health Scanning Engine")14 15# --- 2. SECURITY ENGINE ---16def check_vulnerabilities(package, version, ecosystem):17    url = "https://api.osv.dev/v1/query"18    payload = {"version": version, "package": {"name": package, "ecosystem": ecosystem}}19    for _ in range(2): 20        try:21            r = requests.post(url, json=payload, timeout=7)22            if r.status_code == 200:23                data = r.json()24                count = len(data.get("vulns", []))25                return f"๐Ÿšจ {count} CVEs" if count > 0 else "โœ… Secure"26        except:27            time.sleep(1)28    return "โœ… Secure"29 30def classify_owner(name):31    if not name or name == "N/A": return "Unknown"32    org_k = ['team', 'foundation', 'project', 'org', 'inc', 'llc', 'group', 'maintainers']33    if any(k in str(name).lower() for k in org_k): return f"{name} (Organization)"34    return f"{name} (Individual)"35 36def get_health_status(date_str):37    if date_str in ["N/A", "Unknown", None]: return "โŒ Not Found"38    try:39        days = (datetime.now() - datetime.strptime(date_str, '%Y-%m-%d')).days40        if days <= 180: return "โœ… Healthy"41        elif days <= 365: return "โš ๏ธ Warning"42        else: return "โŒ Outdated"43    except: return "โŒ Error"44 45# --- 3. PDF GENERATOR ---46def create_pdf(df):47    try: 48        from fpdf import FPDF49    except ImportError: 50        return None51    def clean_text(text):52        text = str(text).replace("โœ…", "").replace("โŒ", "").replace("โš ๏ธ", "").replace("๐Ÿšจ", "")53        return text.encode('latin-1', 'ignore').decode('latin-1').strip()54    pdf = FPDF()55    pdf.add_page()56    pdf.set_font("Arial", 'B', 16)57    pdf.cell(190, 10, "Libraries Scanner - Audit Report", ln=True, align='C')58    pdf.ln(5)59    pdf.set_font("Arial", 'B', 10)60    cols, w = ["Library", "Health", "Version", "Security", "Owner"], [45, 30, 20, 25, 70]61    for c, width in zip(cols, w): pdf.cell(width, 10, c, 1, 0, 'C')62    pdf.ln()63    pdf.set_font("Arial", '', 9)64    for _, row in df.iterrows():65        pdf.cell(w[0], 10, clean_text(row['Library'])[:25], 1)66        pdf.cell(w[1], 10, clean_text(row['Health Status'])[:15], 1)67        pdf.cell(w[2], 10, clean_text(row['Version'])[:10], 1, 0, 'C')68        pdf.cell(w[3], 10, clean_text(row['Vulnerabilities'])[:15], 1, 0, 'C')69        pdf.cell(w[4], 10, clean_text(row['Owner'])[:38], 1); pdf.ln()70    with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:71        pdf.output(tmp.name)72        with open(tmp.name, "rb") as f: return f.read()73 74# --- 4. DATA FETCHING ---75@st.cache_data(ttl=60)76def get_pypi_data(package):77    pkg = str(package).strip().lower()78    try:79        r = requests.get(f"https://pypi.org/pypi/{pkg}/json", timeout=7)80        if r.status_code == 200:81            data = r.json()82            ver = data['info']['version']83            upd = data['releases'].get(ver, [{}])[0].get('upload_time', 'Unknown').split('T')[0]84            owner = classify_owner(data['info'].get('author') or "Community")85            urls = data['info'].get('project_urls') or {}86            src = urls.get('Source') or urls.get('Repository') or urls.get('Homepage')87            cve = check_vulnerabilities(pkg, ver, "PyPI")88            return {89                "Library": pkg, 90                "Health Status": get_health_status(upd), 91                "Vulnerabilities": cve, 92                "Version": ver, 93                "Last Updated": upd, 94                "Owner": owner, 95                "Source": src, 96                "Registry": "PyPI"97            }98    except: return None99    return None100 101@st.cache_data(ttl=60)102def get_npm_data(package):103    pkg = str(package).strip().lower()104    try:105        r = requests.get(f"https://registry.npmjs.org/{pkg}", timeout=7)106        if r.status_code == 200:107            data = r.json()108            ver = data['dist-tags']['latest']109            upd = data['time'].get(ver, "").split('T')[0]110            raw_auth = data.get('author')111            auth = raw_auth.get('name') if isinstance(raw_auth, dict) else raw_auth112            raw_repo = data.get('repository', {})113            src = raw_repo.get('url', '') if isinstance(raw_repo, dict) else raw_repo114            cve = check_vulnerabilities(pkg, ver, "npm")115            return {116                "Library": pkg, 117                "Health Status": get_health_status(upd), 118                "Vulnerabilities": cve, 119                "Version": ver, 120                "Last Updated": upd, 121                "Owner": classify_owner(auth or "Community"), 122                "Source": str(src).replace('git+', ''), 123                "Registry": "NPM"124            }125    except: return None126    return None127 128# --- 5. INTERFACE ---129with st.sidebar:130    # --- CUSTOM DRAWN CYBER LOGO ---131    svg_logo = """132    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">133      <defs>134        <filter id="neonGlow" x="-50%" y="-50%" width="200%" height="200%">135          <feGaussianBlur stdDeviation="3" result="blur"/>136          <feMerge>137            <feMergeNode in="blur"/>138            <feMergeNode in="SourceGraphic"/>139          </feMerge>140        </filter>141      </defs>142      <circle cx="100" cy="100" r="85" fill="none" stroke="#38bdf8" stroke-width="2" stroke-dasharray="15 10" opacity="0.7"/>143      <circle cx="100" cy="100" r="65" fill="none" stroke="#00FF41" stroke-width="4" stroke-dasharray="40 10 5 10" filter="url(#neonGlow)"/>144      <polygon points="85,65 135,100 85,135" fill="none" stroke="#E0E0E0" stroke-width="4" filter="url(#neonGlow)"/>145      <circle cx="95" cy="100" r="12" fill="#00FF41" filter="url(#neonGlow)"/>146      <line x1="15" y1="100" x2="35" y2="100" stroke="#38bdf8" stroke-width="3"/>147      <line x1="185" y1="100" x2="165" y2="100" stroke="#38bdf8" stroke-width="3"/>148      <line x1="100" y1="15" x2="100" y2="35" stroke="#38bdf8" stroke-width="3"/>149      <line x1="100" y1="185" x2="100" y2="165" stroke="#38bdf8" stroke-width="3"/>150    </svg>151    """152    b64_svg = base64.b64encode(svg_logo.encode('utf-8')).decode('utf-8')153    st.markdown(f'<img src="data:image/svg+xml;base64,{b64_svg}" width="120" style="display:block; margin:auto; margin-bottom: 20px;">', unsafe_allow_html=True)154    155    st.title("System Controls")156    with st.form("audit_form"):157        libs_input = st.text_area("Target Assets (e.g. axios, pandas):", "axios, requests, pandas", height=150)158        run_btn = st.form_submit_button("๐Ÿš€ Execute Scan", use_container_width=True)159 160if run_btn:161    lib_list = [l.strip() for l in libs_input.split(",") if l.strip()]162    results = []163    with st.spinner("Connecting to global registries..."):164        for lib in lib_list:165            pypi, npm = get_pypi_data(lib), get_npm_data(lib)166            if pypi: results.append(pypi)167            if npm: results.append(npm)168 169    if results:170        df = pd.DataFrame(results)171        st.success(f"โœ… Telemetry acquired for {len(results)} assets.")172        173        # Dashboard Summary174        c1, c2, c3 = st.columns(3)175        c1.metric("Total Assets", len(df))176        c2.metric("Outdated Modules", len(df[df["Health Status"].str.contains("โŒ")]))177        cve_count = len(df[df["Vulnerabilities"].str.contains("๐Ÿšจ")])178        if cve_count > 0: c3.error(f"๐Ÿšจ Critical CVEs: {cve_count}")179        else: c3.success("โœ… Perimeter Secure")180 181        # --- THE COOL VISUALIZATION (DONUT CHART) ---182        st.markdown("### ๐Ÿ“Š Ecosystem Health")183        fig = px.pie(184            df, 185            names='Health Status', 186            hole=0.65,187            color_discrete_sequence=['#00FF41', '#FF4B4B', '#FFC107']188        )189        fig.update_layout(190            paper_bgcolor="rgba(0,0,0,0)", 191            plot_bgcolor="rgba(0,0,0,0)",192            font=dict(family="monospace", color="#E0E0E0")193        )194        st.plotly_chart(fig, use_container_width=True)195 196        # --- THE INTERACTIVE GRID ---197        st.markdown("### ๐Ÿ“‹ Live Audit Data")198        col_config = {"Source": st.column_config.LinkColumn("Repository", display_text="View โ†—")}199        st.data_editor(200            df, 201            use_container_width=True, 202            column_config=col_config,203            hide_index=True,204            disabled=True # Prevents accidental edits205        )206        207        # Export Options208        st.markdown("---")209        colA, colB = st.columns(2)210        with colA: st.download_button("๐Ÿ’พ Export CSV", df.to_csv(index=False).encode('utf-8'), "audit.csv", "text/csv", use_container_width=True)211        with colB:212            pdf_bytes = create_pdf(df)213            if pdf_bytes: st.download_button("๐Ÿ“„ Generate PDF Report", pdf_bytes, "audit.pdf", "application/pdf", use_container_width=True)214    else:215        st.error("Connection failed or no valid targets identified.")