Hariommm/libraries-scanner
0
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.")