From 73f712077e5e1f88a187bea1606bbcb5bead894b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Oct 2025 13:31:26 +0530 Subject: [PATCH 1/5] stage 4 first commit --- README.md | 32 ++++- agent/persistence.py | 186 ++++++++++++++++++++++++++ app/app.py | 46 ++++++- app/routes_chat.py | 230 +++++++++++++++++++++++++++++++++ data/sessions.db | Bin 0 -> 12288 bytes scripts/demo_demo.py | 74 +++++++++++ tests/test_chat.py | 34 +++++ tests/test_chat_delete.py | 58 +++++++++ tests/test_chat_history.py | 44 +++++++ tests/test_chat_persistence.py | 38 ++++++ tests/test_history.py | 37 ++++++ 11 files changed, 771 insertions(+), 8 deletions(-) create mode 100644 agent/persistence.py create mode 100644 app/routes_chat.py create mode 100644 data/sessions.db create mode 100644 scripts/demo_demo.py create mode 100644 tests/test_chat.py create mode 100644 tests/test_chat_delete.py create mode 100644 tests/test_chat_history.py create mode 100644 tests/test_chat_persistence.py create mode 100644 tests/test_history.py diff --git a/README.md b/README.md index 9cd554b..f2d0827 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Minimal instructions to run the project locally, run CI checks, and collaborate safely. Requirements -- Python 3.11+ +- Python 3.10+ (3.11 recommended) Quick start (PowerShell) @@ -13,17 +13,16 @@ python -m venv .venv pip install -r requirements.txt pip install -r dev-requirements.txt -# Run backend -uvicorn backend.app:app --reload +# Run backend (FastAPI) +uvicorn app.app:app --reload --host 0.0.0.0 --port 8000 -# In another shell, run frontend (optional) -streamlit run frontend/app.py +# FastAPI provides interactive docs at http://127.0.0.1:8000/docs ``` Run tests and checks locally ```powershell -pytest -q +$env:PYTHONPATH='.'; pytest -q black --check . flake8 . mypy . @@ -31,6 +30,27 @@ bandit -r . safety check ``` +Hackathon quick demo + +Focus on core functionality for a short demo: code analysis, chat sessions (persisted), and history retrieval. The following shows a simple way to run and exercise the app locally. + +1. Start the server (see Quick start above). +2. In another PowerShell, run the demo script which exercises upload/analyze/chat flows (this uses `requests` and is CI-friendly): + +```powershell +python scripts/demo_demo.py +``` + +Environment configuration (optional) +- `CHAT_DB` — path to SQLite DB for chat sessions (default: `data/sessions.db`). Use a temp path for tests. +- `CHAT_SESSION_TTL_SECONDS` — session TTL in seconds (default: 3600). Set to <=0 to disable expiry. +- `CHAT_CONTEXT_TURNS` — how many turns to include in LLM context (default: 10). +- `CHAT_EVICT_INTERVAL_SECONDS` — background eviction interval in seconds (default: 60). + +Notes +- FastAPI includes an interactive UI at `/docs` (Swagger) which is handy for live demos. +- The demo script shows basic usage; feel free to adapt it for screenshots or a short recorded walkthrough. + Collaboration rules - Use feature branches and open PRs to `main`. - CI is mandatory on PRs and must pass before merging. diff --git a/agent/persistence.py b/agent/persistence.py new file mode 100644 index 0000000..77da3bc --- /dev/null +++ b/agent/persistence.py @@ -0,0 +1,186 @@ +"""Simple persistence layer for analysis reports using SQLite. + +Provides a tiny API: init_db(path), save_report(report_dict), list_reports(limit=50). +Uses a local file under data/reports.db by default. +""" +from __future__ import annotations + +import json +import os +import sqlite3 +from datetime import datetime, timezone +from typing import Dict, Any, List, Optional + +DEFAULT_DB = os.environ.get("CODEGUARDIAN_DB", "data/reports.db") + + +def _ensure_dir(path: str): + d = os.path.dirname(path) + if d and not os.path.exists(d): + os.makedirs(d, exist_ok=True) + + +def init_db(path: Optional[str] = None): + path = path or DEFAULT_DB + _ensure_dir(path) + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute( + """ + CREATE TABLE IF NOT EXISTS reports ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + filename TEXT, + timestamp TEXT, + summary TEXT, + payload TEXT + ) + """ + ) + conn.commit() + conn.close() + + +def save_report(filename: str, summary: Dict[str, Any], payload: Dict[str, Any], path: Optional[str] = None) -> int: + path = path or DEFAULT_DB + _ensure_dir(path) + init_db(path) + conn = sqlite3.connect(path) + cur = conn.cursor() + ts = datetime.now(timezone.utc).isoformat() + cur.execute( + "INSERT INTO reports (filename, timestamp, summary, payload) VALUES (?, ?, ?, ?)", + (filename, ts, json.dumps(summary), json.dumps(payload)), + ) + conn.commit() + rowid = cur.lastrowid or 0 + conn.close() + return rowid + + +def list_reports(limit: int = 50, path: Optional[str] = None) -> List[Dict[str, Any]]: + path = path or DEFAULT_DB + if not os.path.exists(path): + return [] + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute("SELECT id, filename, timestamp, summary FROM reports ORDER BY id DESC LIMIT ?", (limit,)) + rows = cur.fetchall() + conn.close() + out: List[Dict[str, Any]] = [] + for r in rows: + _id, filename, ts, summary_json = r + try: + summary = json.loads(summary_json) + except Exception: + summary = {"raw": summary_json} + out.append({"id": _id, "filename": filename, "timestamp": ts, "summary": summary}) + return out + + +def get_report(report_id: int, path: Optional[str] = None) -> Optional[Dict[str, Any]]: + """Return the full report payload and metadata for a given id, or None.""" + path = path or DEFAULT_DB + if not os.path.exists(path): + return None + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute("SELECT id, filename, timestamp, summary, payload FROM reports WHERE id = ?", (report_id,)) + row = cur.fetchone() + conn.close() + if not row: + return None + _id, filename, ts, summary_json, payload_json = row + try: + summary = json.loads(summary_json) + except Exception: + summary = {"raw": summary_json} + try: + payload = json.loads(payload_json) + except Exception: + payload = {"raw": payload_json} + return {"id": _id, "filename": filename, "timestamp": ts, "summary": summary, "payload": payload} + + +# ------------------ chat session persistence helpers ------------------ + + +def _default_chat_db() -> str: + return os.environ.get("CHAT_DB", "data/sessions.db") + + +def init_chat_db(path: Optional[str] = None): + path = path or _default_chat_db() + _ensure_dir(path) + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute( + """ + CREATE TABLE IF NOT EXISTS sessions ( + session_id TEXT PRIMARY KEY, + messages TEXT, + last_active TEXT + ) + """ + ) + conn.commit() + conn.close() + + +def save_session(session_id: str, messages: List[Dict[str, Any]], last_active: str, path: Optional[str] = None) -> None: + path = path or _default_chat_db() + _ensure_dir(path) + init_chat_db(path) + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute( + "REPLACE INTO sessions (session_id, messages, last_active) VALUES (?, ?, ?)", + (session_id, json.dumps(messages), last_active), + ) + conn.commit() + conn.close() + + +def load_session(session_id: str, path: Optional[str] = None) -> Optional[Dict[str, Any]]: + path = path or _default_chat_db() + if not os.path.exists(path): + return None + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute("SELECT messages, last_active FROM sessions WHERE session_id = ?", (session_id,)) + row = cur.fetchone() + conn.close() + if not row: + return None + messages_json, last_active = row + try: + messages = json.loads(messages_json) + except Exception: + messages = [] + return {"session_id": session_id, "messages": messages, "last_active": last_active} + + +def delete_session(session_id: str, path: Optional[str] = None) -> None: + path = path or _default_chat_db() + if not os.path.exists(path): + return + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute("DELETE FROM sessions WHERE session_id = ?", (session_id,)) + conn.commit() + conn.close() + + +def list_sessions(path: Optional[str] = None) -> List[Dict[str, Any]]: + """Return list of sessions with metadata (session_id, last_active).""" + path = path or _default_chat_db() + if not os.path.exists(path): + return [] + conn = sqlite3.connect(path) + cur = conn.cursor() + cur.execute("SELECT session_id, last_active FROM sessions") + rows = cur.fetchall() + conn.close() + out: List[Dict[str, Any]] = [] + for sid, last_active in rows: + out.append({"session_id": sid, "last_active": last_active}) + return out diff --git a/app/app.py b/app/app.py index ff94c29..567ec00 100644 --- a/app/app.py +++ b/app/app.py @@ -16,10 +16,16 @@ from agent.engine import engine from agent import parser as stage2_parser from agent.reasoning import reasoner, Reasoner +from agent import persistence +from fastapi import Depends +from app.routes_chat import router as chat_router app = FastAPI(title="CodeGuardian API") +# include chat routes +app.include_router(chat_router) + class ScanResult(BaseModel): filename: str @@ -252,6 +258,11 @@ async def analyze(stage2: dict = None, files: List[UploadFile] = File(None), cod # If user provided Stage 2 JSON directly if stage2: enriched = req_reasoner.enrich(stage2) + # persist the report (best-effort) + try: + persistence.save_report("stage2_input", enriched.get("summary", {}), enriched) + except Exception: + pass return JSONResponse(enriched) results = [] @@ -265,6 +276,11 @@ async def analyze(stage2: dict = None, files: List[UploadFile] = File(None), cod continue issues = stage2_parser.analyze_code("uploaded:" + (f.filename or "file")) enriched = req_reasoner.enrich({f.filename or "uploaded": issues}) + # persist + try: + persistence.save_report(f.filename or "uploaded", enriched.get("summary", {}), enriched) + except Exception: + pass results.append(enriched) return JSONResponse({"results": results}) @@ -280,14 +296,18 @@ async def analyze(stage2: dict = None, files: List[UploadFile] = File(None), cod tf.write(code) tf.flush() issues = stage2_parser.analyze_code(tf.name) - enriched = req_reasoner.enrich({fn: issues}) + enriched = req_reasoner.enrich({fn: issues}) + try: + persistence.save_report(fn, enriched.get("summary", {}), enriched) + except Exception: + pass return JSONResponse(enriched) return JSONResponse({"error": "No input provided to analyze"}, status_code=400) @app.get("/summary") -def summary(path: str = None): +def summary(path: Optional[str] = None): """Run a scan on a path and return severity breakdown and top risky files. If path is omitted, returns an empty summary. @@ -299,3 +319,25 @@ def summary(path: str = None): enriched = reasoner.enrich(findings) # keep only summary return JSONResponse({"summary": enriched.get("summary")}) + + +@app.get("/history") +def history(limit: int = 50): + """Return recent analysis summaries (id, filename, timestamp, summary).""" + try: + reports = persistence.list_reports(limit=limit) + return JSONResponse({"reports": reports}) + except Exception: + return JSONResponse({"error": "Failed to read history"}, status_code=500) + + +@app.get("/history/{report_id}") +def history_get(report_id: int): + """Return a full saved report by id.""" + try: + rep = persistence.get_report(report_id) + if rep is None: + return JSONResponse({"error": "Not found"}, status_code=404) + return JSONResponse({"report": rep}) + except Exception: + return JSONResponse({"error": "Failed to read report"}, status_code=500) diff --git a/app/routes_chat.py b/app/routes_chat.py new file mode 100644 index 0000000..7dde0ed --- /dev/null +++ b/app/routes_chat.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +from typing import Dict, List, Optional +from fastapi import APIRouter, HTTPException, status +from pydantic import BaseModel +from uuid import uuid4 +import os +from datetime import datetime, timezone + +from agent.llm_client import LLMClient +from agent import persistence +import asyncio +from typing import Callable + +router = APIRouter() + +# In-memory sessions: session_id -> {messages: [...], last_active: isotimestamp} +SESSIONS: Dict[str, Dict] = {} + +# session TTL in seconds; 0 means never expire. Default 3600s +DEFAULT_TTL = int(os.environ.get("CHAT_SESSION_TTL_SECONDS", "3600")) +# how many user-assistant turns to keep in context +CHAT_CONTEXT_TURNS = int(os.environ.get("CHAT_CONTEXT_TURNS", "10")) + + +class ChatRequest(BaseModel): + session_id: Optional[str] = None + message: str + backend: Optional[str] = None + + +class ChatResponse(BaseModel): + session_id: str + reply: str + turns: int + + +def _now_iso() -> str: + return datetime.now(timezone.utc).isoformat() + + +def _is_expired(session: Dict) -> bool: + ttl = int(os.environ.get("CHAT_SESSION_TTL_SECONDS", str(DEFAULT_TTL))) + # ttl <= 0 means never expire + if ttl <= 0: + return False + last = session.get("last_active") + if not last: + return True + try: + last_dt = datetime.fromisoformat(last) + except Exception: + return True + age = (datetime.now(timezone.utc) - last_dt).total_seconds() + return age > ttl + + +@router.post("/chat", response_model=ChatResponse) +def chat(req: ChatRequest): + # ensure session id + sid = req.session_id or str(uuid4()) + + # attempt to load persisted session if not in memory + if sid not in SESSIONS: + loaded = persistence.load_session(sid) + if loaded: + SESSIONS[sid] = {"messages": loaded.get("messages", []), "last_active": loaded.get("last_active")} + + # create session structure if missing or expired + if sid in SESSIONS and _is_expired(SESSIONS[sid]): + # remove from memory and persistence + try: + persistence.delete_session(sid) + except Exception: + pass + del SESSIONS[sid] + + if sid not in SESSIONS: + SESSIONS[sid] = {"messages": [], "last_active": _now_iso()} + + session = SESSIONS[sid] + + # append user message + session["messages"].append({"role": "user", "text": req.message}) + + # build a synthetic 'issue' to reuse LLMClient.explain interface + issue = {"type": "chat", "message": req.message, "snippet": "", "line": 0} + + # include conversation history in context (trim to recent turns) + max_msgs = CHAT_CONTEXT_TURNS * 2 + recent = session["messages"][-max_msgs:] + history_text = "\n".join([f"{m['role']}: {m['text']}" for m in recent]) + context = {"history": history_text} + + # initialize LLM client with optional backend override + client = LLMClient(mode=req.backend if req.backend else None) + try: + out = client.explain(issue, context=context) + reply = out.get("explanation") or out.get("fix") or "" + except Exception: + reply = "(LLM unavailable)" + + # append assistant reply and update last_active + session["messages"].append({"role": "assistant", "text": reply}) + session["last_active"] = _now_iso() + + # persist session (best-effort) + try: + persistence.save_session(sid, session["messages"], session["last_active"]) + except Exception: + pass + + return ChatResponse(session_id=sid, reply=reply, turns=len(session["messages"])) + + +class ChatHistoryResponse(BaseModel): + session_id: str + messages: List[Dict] + last_active: Optional[str] + + +@router.get("/chat/{session_id}/history", response_model=ChatHistoryResponse) +def chat_history(session_id: str): + # try in-memory first + if session_id not in SESSIONS: + # try to load from persistence + loaded = persistence.load_session(session_id) + if not loaded: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="session not found") + SESSIONS[session_id] = {"messages": loaded.get("messages", []), "last_active": loaded.get("last_active")} + + session = SESSIONS[session_id] + if _is_expired(session): + # expire and remove from memory and persistence + try: + persistence.delete_session(session_id) + except Exception: + pass + del SESSIONS[session_id] + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="session expired") + + return ChatHistoryResponse(session_id=session_id, messages=session["messages"], last_active=session.get("last_active")) + + +@router.delete("/chat/{session_id}") +def chat_delete(session_id: str): + # remove from memory + if session_id in SESSIONS: + del SESSIONS[session_id] + # remove persisted + try: + persistence.delete_session(session_id) + except Exception: + pass + return {}, 204 + + +def evict_expired_once() -> int: + """Perform a single eviction pass. Returns number of sessions removed.""" + removed = 0 + # check in-memory sessions + for sid in list(SESSIONS.keys()): + session = SESSIONS.get(sid) + if session and _is_expired(session): + try: + persistence.delete_session(sid) + except Exception: + pass + del SESSIONS[sid] + removed += 1 + + # check persisted sessions that might not be in memory + try: + for meta in persistence.list_sessions(): + sid = meta.get("session_id") + if not sid: + continue + last_active = meta.get("last_active") + # construct a small session-like object for expiry check + session_like = {"last_active": last_active} + if _is_expired(session_like): + try: + persistence.delete_session(sid) + removed += 1 + except Exception: + pass + except Exception: + # best-effort; ignore persistence read errors + pass + + return removed + + +async def _evict_loop(interval: int): + while True: + try: + removed = evict_expired_once() + if removed: + # minor logging to stdout for debug in dev (non-blocking) + print(f"chat-evict: removed {removed} expired sessions") + except Exception: + pass + await asyncio.sleep(interval) + + +# Background eviction task handle +_EVICTOR_TASK: Optional[asyncio.Task] = None + + +@router.on_event("startup") +async def _start_evictor(): + global _EVICTOR_TASK + try: + interval = int(os.environ.get("CHAT_EVICT_INTERVAL_SECONDS", "60")) + except Exception: + interval = 60 + if _EVICTOR_TASK is None: + _EVICTOR_TASK = asyncio.create_task(_evict_loop(interval)) + + +@router.on_event("shutdown") +async def _stop_evictor(): + global _EVICTOR_TASK + if _EVICTOR_TASK is not None: + _EVICTOR_TASK.cancel() + try: + await _EVICTOR_TASK + except Exception: + pass + _EVICTOR_TASK = None diff --git a/data/sessions.db b/data/sessions.db new file mode 100644 index 0000000000000000000000000000000000000000..8576b4ea6439ab34e2f0a3ba71519b440170f367 GIT binary patch literal 12288 zcmeI0O>f*p7{|SNYqH6%L8{bKz}mE;h|Dsto|zS@2vJZgvQo*5L_yVR-W*olWMOw( z1)(0&0}|rGx8TSX?nrzE&ixb|8M|?5s%YawM1wS5dF`i-o9wL9<`tR)QJC%X5w2>d@KI*q6@5#ktAQSKqfr!+~pclGfv~8@1Mx*2Eo8^7dUf7z|ra*OPxk zlz+JyPt?)m)woalqwypexo-yFosE9|h8qW}e{?qF+#BvXGd!~89Uo$d89)$Vz6V7I z!VX4ln&Om;4}1f{leNbD{>esH5kVxh7!ZSecdf8MG2dsCT9sjnw4C1Eeg$a-llA0Y zq7D<491hK+WP7{YZaqC;B}pYKRSs5)q-2%~NI3z7aIca;1f(JmXiS_k3M+y2|12rI z!+3{?HiwijRwkuhB*keaQ?89qg@k~nG6Ndg3}8g^ALfWm?e66zrBs=E{IX2S%_XmL zvzuC*F-uY)u#tc|L4k7AfmAa}tjzz}MM+7%_CsShdTk7xYw)`-BF}ZWmq|N>wV_NA z;-@eNi-mWz^>%Weqwo>|71TT2rXH)zjNn`o2I)mIE13Mh9MCVjD{zWv>79kbJDFn$ zV!}Zth0lty(vK330Yn@tO)zFOoz~q;_0Ef=Fz@2hJ8ScWN}-KWgl2v~n+#BH6%dRd zpB_Yrby5(LO;LH@jq*9w`8oCCskRJ0e{%FJNr%C*+z%JSx1;c&#Yw8jrS0aM~t zW(X+a9}QAx916s!R&I*QV&1zvhZizAmXyoV;j@Lq4PjhmiUO-tK8IW8r;CgM#WRoI zSc2%(>Ea4LvdXM*h!`ugvQ{g+V~r*}GaN91yu*p#)5uXE(^Ob$oKenB%gUuXyhuvQ zFoCDzu= 1 + + # ensure persistence gone + assert persistence.load_session(sid, path=db_path) is None diff --git a/tests/test_chat_history.py b/tests/test_chat_history.py new file mode 100644 index 0000000..5943be5 --- /dev/null +++ b/tests/test_chat_history.py @@ -0,0 +1,44 @@ +from fastapi.testclient import TestClient +from unittest.mock import patch +from datetime import datetime, timezone, timedelta +import os + +from app.app import app +import app.routes_chat as routes_chat + + +def test_chat_history_returns_messages(): + client = TestClient(app) + fake = {"explanation": "History reply"} + + with patch("agent.llm_client.LLMClient.explain", return_value=fake): + r = client.post("/chat", json={"message": "Hello history"}) + assert r.status_code == 200 + sid = r.json()["session_id"] + + # fetch history + hr = client.get(f"/chat/{sid}/history") + assert hr.status_code == 200 + data = hr.json() + assert data["session_id"] == sid + # expect at least user and assistant messages + assert len(data["messages"]) >= 2 + + +def test_chat_history_expiry(): + client = TestClient(app) + fake = {"explanation": "Will expire"} + + with patch("agent.llm_client.LLMClient.explain", return_value=fake): + r = client.post("/chat", json={"message": "Temp"}) + sid = r.json()["session_id"] + + # force session to appear old + old = (datetime.now(timezone.utc) - timedelta(seconds=3600)).isoformat() + routes_chat.SESSIONS[sid]["last_active"] = old + + # set TTL to 1 second so expiry check will remove it + os.environ["CHAT_SESSION_TTL_SECONDS"] = "1" + + hr = client.get(f"/chat/{sid}/history") + assert hr.status_code == 404 diff --git a/tests/test_chat_persistence.py b/tests/test_chat_persistence.py new file mode 100644 index 0000000..3036740 --- /dev/null +++ b/tests/test_chat_persistence.py @@ -0,0 +1,38 @@ +import os +import json +import tempfile +from fastapi.testclient import TestClient +from unittest.mock import patch + +from app.app import app +import app.routes_chat as routes_chat +from agent import persistence + + +def test_chat_persistence_survives_restart(tmp_path): + client = TestClient(app) + fake = {"explanation": "Persisted reply"} + + # use a temp DB for chat persistence + db_path = str(tmp_path / "sessions_test.db") + os.environ["CHAT_DB"] = db_path + + with patch("agent.llm_client.LLMClient.explain", return_value=fake): + r = client.post("/chat", json={"message": "Persistent"}) + assert r.status_code == 200 + sid = r.json()["session_id"] + + # ensure persistence saved + loaded = persistence.load_session(sid, path=db_path) + assert loaded is not None + assert len(loaded["messages"]) >= 2 + + # simulate restart by clearing in-memory sessions + routes_chat.SESSIONS.clear() + + # fetch history, should load from DB + hr = client.get(f"/chat/{sid}/history") + assert hr.status_code == 200 + data = hr.json() + assert data["session_id"] == sid + assert len(data["messages"]) >= 2 diff --git a/tests/test_history.py b/tests/test_history.py new file mode 100644 index 0000000..8607ebf --- /dev/null +++ b/tests/test_history.py @@ -0,0 +1,37 @@ +import os +import importlib + +from fastapi.testclient import TestClient + +from agent import persistence + + +def test_history_endpoints(tmp_path, monkeypatch): + db = tmp_path / "reports.db" + # point persistence to temp DB via env and reload module + monkeypatch.setenv("CODEGUARDIAN_DB", str(db)) + importlib.reload(persistence) + + # ensure DB empty + assert persistence.list_reports() == [] + + # save a report + rid = persistence.save_report("test.py", {"counts": {}, "risk": "Low"}, {"results": {}}, path=str(db)) + assert isinstance(rid, int) + + # reload app to ensure it uses updated persistence module (app imports persistence earlier) + from app.app import app + + client = TestClient(app) + + r = client.get("/history") + assert r.status_code == 200 + j = r.json() + assert "reports" in j + assert any(rep["filename"] == "test.py" or rep["filename"] == 'test.py' for rep in j["reports"]) or len(j["reports"]) >= 1 + + # get the report by id + r2 = client.get(f"/history/{rid}") + assert r2.status_code == 200 + jr2 = r2.json() + assert jr2.get("report") and jr2["report"]["id"] == rid From 67a3c0344db6f66711d567c26a5130445ba3d013 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Oct 2025 13:41:07 +0530 Subject: [PATCH 2/5] readme issue resolved --- README.md | 32 ++++--------------- scripts/demo_demo.py | 74 -------------------------------------------- 2 files changed, 6 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index f2d0827..9cd554b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Minimal instructions to run the project locally, run CI checks, and collaborate safely. Requirements -- Python 3.10+ (3.11 recommended) +- Python 3.11+ Quick start (PowerShell) @@ -13,16 +13,17 @@ python -m venv .venv pip install -r requirements.txt pip install -r dev-requirements.txt -# Run backend (FastAPI) -uvicorn app.app:app --reload --host 0.0.0.0 --port 8000 +# Run backend +uvicorn backend.app:app --reload -# FastAPI provides interactive docs at http://127.0.0.1:8000/docs +# In another shell, run frontend (optional) +streamlit run frontend/app.py ``` Run tests and checks locally ```powershell -$env:PYTHONPATH='.'; pytest -q +pytest -q black --check . flake8 . mypy . @@ -30,27 +31,6 @@ bandit -r . safety check ``` -Hackathon quick demo - -Focus on core functionality for a short demo: code analysis, chat sessions (persisted), and history retrieval. The following shows a simple way to run and exercise the app locally. - -1. Start the server (see Quick start above). -2. In another PowerShell, run the demo script which exercises upload/analyze/chat flows (this uses `requests` and is CI-friendly): - -```powershell -python scripts/demo_demo.py -``` - -Environment configuration (optional) -- `CHAT_DB` — path to SQLite DB for chat sessions (default: `data/sessions.db`). Use a temp path for tests. -- `CHAT_SESSION_TTL_SECONDS` — session TTL in seconds (default: 3600). Set to <=0 to disable expiry. -- `CHAT_CONTEXT_TURNS` — how many turns to include in LLM context (default: 10). -- `CHAT_EVICT_INTERVAL_SECONDS` — background eviction interval in seconds (default: 60). - -Notes -- FastAPI includes an interactive UI at `/docs` (Swagger) which is handy for live demos. -- The demo script shows basic usage; feel free to adapt it for screenshots or a short recorded walkthrough. - Collaboration rules - Use feature branches and open PRs to `main`. - CI is mandatory on PRs and must pass before merging. diff --git a/scripts/demo_demo.py b/scripts/demo_demo.py index 171fbd9..e69de29 100644 --- a/scripts/demo_demo.py +++ b/scripts/demo_demo.py @@ -1,74 +0,0 @@ -"""Simple demo script for CodeGuardian hackathon. - -This script exercises key endpoints: /upload (paste), /analyze (stage2-like json), /chat and /chat/{session}/history. -Run the FastAPI server first: `uvicorn app.app:app --reload` and then run this script. -""" -import os -import time -import requests - -BASE = os.environ.get("CG_BASE", "http://127.0.0.1:8000") - - -def pretty(j): - import json - - print(json.dumps(j, indent=2)) - - -def demo_upload_paste(): - print("\n== Upload (paste) demo ==") - code = """ -def insecure_eval(user_input): - return eval(user_input) -""" - files = {"code": (None, code), "filename": (None, "example.py")} - r = requests.post(f"{BASE}/upload", files=files) - print(r.status_code) - pretty(r.json()) - - -def demo_analyze_paste(): - print("\n== Analyze (paste) demo ==") - code = "print('hello')\nuser = input()\nprint(user)" # trivial sample - data = {"code": code, "filename": "demo.py"} - r = requests.post(f"{BASE}/analyze", data=data) - print(r.status_code) - pretty(r.json()) - - -def demo_chat_flow(): - print("\n== Chat demo ==") - # start a session - r = requests.post(f"{BASE}/chat", json={"message": "Hello, what can you do?"}) - print(r.status_code) - j = r.json() - pretty(j) - sid = j.get("session_id") - - # follow up - r2 = requests.post(f"{BASE}/chat", json={"session_id": sid, "message": "Any advice for insecure eval?"}) - pretty(r2.json()) - - # fetch history - hr = requests.get(f"{BASE}/chat/{sid}/history") - print("History:") - pretty(hr.json()) - - -if __name__ == "__main__": - print("CodeGuardian demo starting against", BASE) - print("Make sure the server is running (uvicorn app.app:app --reload)") - try: - demo_upload_paste() - except Exception as e: - print("Upload demo failed:", e) - try: - demo_analyze_paste() - except Exception as e: - print("Analyze demo failed:", e) - try: - demo_chat_flow() - except Exception as e: - print("Chat demo failed:", e) - print("Demo finished.") From c8036eca19430bb815a37785256ba056c1c578bb Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 31 Oct 2025 21:09:13 +0530 Subject: [PATCH 3/5] stage4: chat persistence, history, eviction, admin sessions, delete endpoint and tests --- .gitignore | 2 ++ app/routes_chat.py | 44 ++++++++++++++++++++++++++++++++++++++++ tests/test_chat_admin.py | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 tests/test_chat_admin.py diff --git a/.gitignore b/.gitignore index 8dea0df..acd494e 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,5 @@ npm-debug.log # Misc *.sqlite3 +data/*.db +data/*.sqlite3 diff --git a/app/routes_chat.py b/app/routes_chat.py index 7dde0ed..7d10730 100644 --- a/app/routes_chat.py +++ b/app/routes_chat.py @@ -142,6 +142,50 @@ def chat_history(session_id: str): return ChatHistoryResponse(session_id=session_id, messages=session["messages"], last_active=session.get("last_active")) +class SessionItem(BaseModel): + session_id: str + last_active: Optional[str] + in_memory: bool + message_count: int + + +class SessionsResponse(BaseModel): + sessions: List[SessionItem] + + +@router.get("/chat/sessions", response_model=SessionsResponse) +def chat_sessions(): + """List chat sessions (in-memory and persisted). + + Returns a merged view: in-memory sessions take precedence for message counts/last_active. + """ + out: Dict[str, SessionItem] = {} + + # in-memory sessions + for sid, s in SESSIONS.items(): + msgs = s.get("messages") or [] + out[sid] = SessionItem(session_id=sid, last_active=s.get("last_active"), in_memory=True, message_count=len(msgs)) + + # persisted sessions + try: + for meta in persistence.list_sessions(): + sid = meta.get("session_id") + if not sid: + continue + if sid in out: + # already present (in-memory), skip or update missing last_active + if not out[sid].last_active and meta.get("last_active"): + out[sid].last_active = meta.get("last_active") + continue + last_active = meta.get("last_active") + out[sid] = SessionItem(session_id=sid, last_active=last_active, in_memory=False, message_count=0) + except Exception: + # best-effort; if persistence fails, return in-memory only + pass + + return SessionsResponse(sessions=list(out.values())) + + @router.delete("/chat/{session_id}") def chat_delete(session_id: str): # remove from memory diff --git a/tests/test_chat_admin.py b/tests/test_chat_admin.py new file mode 100644 index 0000000..4d8ceb5 --- /dev/null +++ b/tests/test_chat_admin.py @@ -0,0 +1,42 @@ +import os +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from app.app import app +from agent import persistence + + +def test_chat_sessions_lists_inmemory_and_persisted(tmp_path): + client = TestClient(app) + fake = {"explanation": "admin-list"} + + db_path = str(tmp_path / "sessions_admin.db") + os.environ["CHAT_DB"] = db_path + + # create an in-memory session by posting + with patch("agent.llm_client.LLMClient.explain", return_value=fake): + r = client.post("/chat", json={"message": "One"}) + assert r.status_code == 200 + sid1 = r.json()["session_id"] + + # create another and then clear memory to simulate persisted-only + with patch("agent.llm_client.LLMClient.explain", return_value=fake): + r2 = client.post("/chat", json={"message": "Two"}) + sid2 = r2.json()["session_id"] + + # ensure both persisted + assert persistence.load_session(sid1, path=db_path) is not None + assert persistence.load_session(sid2, path=db_path) is not None + + # clear in-memory to make sid2 persisted-only + from app import routes_chat + routes_chat.SESSIONS.pop(sid2, None) + + # call admin endpoint + res = client.get("/chat/sessions") + assert res.status_code == 200 + j = res.json() + sids = {s["session_id"] for s in j["sessions"]} + assert sid1 in sids + assert sid2 in sids From dbad47818fa85f3e0d1fe6d08f2361f533c11870 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 1 Nov 2025 22:58:02 +0530 Subject: [PATCH 4/5] Stage3: add NVIDIA embeddings probe, sanitizer, cleaned output and demo updates --- Output/stage3-20251101T094754Z.json | 159 ++++++++++++ Output/stage3-20251101T110841Z.json | 169 +++++++++++++ Output/stage3-20251101T111716Z.json | 169 +++++++++++++ Output/stage3-20251101T170533Z.json | 241 ++++++++++++++++++ Output/stage3-20251101T171830Z.cleaned.json | 259 ++++++++++++++++++++ Output/stage3-20251101T171830Z.json | 257 +++++++++++++++++++ Output/stage3-20251101T172426Z.cleaned.json | 257 +++++++++++++++++++ Output/stage3-20251101T172426Z.json | 257 +++++++++++++++++++ Plan.txt | 4 +- agent/llm_client.py | 17 +- agent/nim_client.py | 71 +++++- agent/reasoning.py | 66 +++++ data/sessions.db | Bin 12288 -> 12288 bytes scripts/check_nim_online.py | 80 ++++++ scripts/check_providers.py | 102 ++++++++ scripts/clean_stage3.py | 97 ++++++++ scripts/probe_nim_embeddings.py | 50 ++++ scripts/probe_nim_endpoint.py | 41 ++++ scripts/run_stage3_demo.py | 79 ++++++ 19 files changed, 2360 insertions(+), 15 deletions(-) create mode 100644 Output/stage3-20251101T094754Z.json create mode 100644 Output/stage3-20251101T110841Z.json create mode 100644 Output/stage3-20251101T111716Z.json create mode 100644 Output/stage3-20251101T170533Z.json create mode 100644 Output/stage3-20251101T171830Z.cleaned.json create mode 100644 Output/stage3-20251101T171830Z.json create mode 100644 Output/stage3-20251101T172426Z.cleaned.json create mode 100644 Output/stage3-20251101T172426Z.json create mode 100644 scripts/check_nim_online.py create mode 100644 scripts/check_providers.py create mode 100644 scripts/clean_stage3.py create mode 100644 scripts/probe_nim_embeddings.py create mode 100644 scripts/probe_nim_endpoint.py create mode 100644 scripts/run_stage3_demo.py diff --git a/Output/stage3-20251101T094754Z.json b/Output/stage3-20251101T094754Z.json new file mode 100644 index 0000000..f988973 --- /dev/null +++ b/Output/stage3-20251101T094754Z.json @@ -0,0 +1,159 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "This file contains a hardcoded secret or credential in source code which can be read by anyone with repository access.", + "fix": "Remove the secret from source control. Use environment variables, a .env file kept out of VCS, or a secret store (HashiCorp Vault, AWS Secrets Manager). Rotate the credential immediately if it was committed.", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "This file contains a hardcoded secret or credential in source code which can be read by anyone with repository access.", + "fix": "Remove the secret from source control. Use environment variables, a .env file kept out of VCS, or a secret store (HashiCorp Vault, AWS Secrets Manager). Rotate the credential immediately if it was committed.", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "Detected issue of type 'Possible Hardcoded Token'. Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "fix": "Investigate the finding and apply recommended best-practices (parameterization, secrets management, or safer library APIs).", + "references": [ + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "Use of functions like eval() or exec() can execute arbitrary code and should be avoided, especially on user-controlled inputs.", + "fix": "Replace eval/exec with safer alternatives. For parsing expressions use ast.literal_eval or write a simple parser. Validate inputs strictly.", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "Calling subprocess APIs with unsanitized inputs or with shell=True can allow command injection or execution of unintended commands.", + "fix": "Avoid shell=True and pass arguments as a list. Validate and sanitize any inputs used in command construction.", + "references": [ + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "Use of functions like eval() or exec() can execute arbitrary code and should be avoided, especially on user-controlled inputs.", + "fix": "Replace eval/exec with safer alternatives. For parsing expressions use ast.literal_eval or write a simple parser. Validate inputs strictly.", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "Detected issue of type 'Dangerous Import'. Importing subprocess can enable executing shell commands; review usage.", + "fix": "Investigate the finding and apply recommended best-practices (parameterization, secrets management, or safer library APIs).", + "references": [ + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "MD5 and SHA1 are considered cryptographically broken or weak for collision resistance and should not be used for security-sensitive hashing.", + "fix": "Use hashlib.sha256 or a stronger function and use salt + PBKDF2 / bcrypt / scrypt / Argon2 for password hashing.", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "This code constructs SQL statements by concatenating strings or by formatting them directly. Attackers can inject SQL fragments through inputs, leading to data leakage or corruption.", + "fix": "Use parameterized queries (e.g., cursor.execute(sql, params)) or ORM query builders to avoid direct string composition of SQL. Validate and sanitize inputs.", + "references": [ + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "Overly-broad regex patterns like '.*' can match unintended input and can cause catastrophic backtracking.", + "fix": "Use more specific regexes and apply input length limits. Consider non-greedy qualifiers and anchors as appropriate.", + "references": [ + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T110841Z.json b/Output/stage3-20251101T110841Z.json new file mode 100644 index 0000000..f8e44b8 --- /dev/null +++ b/Output/stage3-20251101T110841Z.json @@ -0,0 +1,169 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "This file contains a hardcoded secret or credential in source code which can be read by anyone with repository access.", + "fix": "Remove the secret from source control. Use environment variables, a .env file kept out of VCS, or a secret store (HashiCorp Vault, AWS Secrets Manager). Rotate the credential immediately if it was committed.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "This file contains a hardcoded secret or credential in source code which can be read by anyone with repository access.", + "fix": "Remove the secret from source control. Use environment variables, a .env file kept out of VCS, or a secret store (HashiCorp Vault, AWS Secrets Manager). Rotate the credential immediately if it was committed.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "Detected issue of type 'Possible Hardcoded Token'. Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "fix": "Investigate the finding and apply recommended best-practices (parameterization, secrets management, or safer library APIs).", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "Use of functions like eval() or exec() can execute arbitrary code and should be avoided, especially on user-controlled inputs.", + "fix": "Replace eval/exec with safer alternatives. For parsing expressions use ast.literal_eval or write a simple parser. Validate inputs strictly.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "Calling subprocess APIs with unsanitized inputs or with shell=True can allow command injection or execution of unintended commands.", + "fix": "Avoid shell=True and pass arguments as a list. Validate and sanitize any inputs used in command construction.", + "llm_used": "offline", + "references": [ + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "Use of functions like eval() or exec() can execute arbitrary code and should be avoided, especially on user-controlled inputs.", + "fix": "Replace eval/exec with safer alternatives. For parsing expressions use ast.literal_eval or write a simple parser. Validate inputs strictly.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "Detected issue of type 'Dangerous Import'. Importing subprocess can enable executing shell commands; review usage.", + "fix": "Investigate the finding and apply recommended best-practices (parameterization, secrets management, or safer library APIs).", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "MD5 and SHA1 are considered cryptographically broken or weak for collision resistance and should not be used for security-sensitive hashing.", + "fix": "Use hashlib.sha256 or a stronger function and use salt + PBKDF2 / bcrypt / scrypt / Argon2 for password hashing.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "This code constructs SQL statements by concatenating strings or by formatting them directly. Attackers can inject SQL fragments through inputs, leading to data leakage or corruption.", + "fix": "Use parameterized queries (e.g., cursor.execute(sql, params)) or ORM query builders to avoid direct string composition of SQL. Validate and sanitize inputs.", + "llm_used": "offline", + "references": [ + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "Overly-broad regex patterns like '.*' can match unintended input and can cause catastrophic backtracking.", + "fix": "Use more specific regexes and apply input length limits. Consider non-greedy qualifiers and anchors as appropriate.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T111716Z.json b/Output/stage3-20251101T111716Z.json new file mode 100644 index 0000000..f8e44b8 --- /dev/null +++ b/Output/stage3-20251101T111716Z.json @@ -0,0 +1,169 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "This file contains a hardcoded secret or credential in source code which can be read by anyone with repository access.", + "fix": "Remove the secret from source control. Use environment variables, a .env file kept out of VCS, or a secret store (HashiCorp Vault, AWS Secrets Manager). Rotate the credential immediately if it was committed.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "This file contains a hardcoded secret or credential in source code which can be read by anyone with repository access.", + "fix": "Remove the secret from source control. Use environment variables, a .env file kept out of VCS, or a secret store (HashiCorp Vault, AWS Secrets Manager). Rotate the credential immediately if it was committed.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "Detected issue of type 'Possible Hardcoded Token'. Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "fix": "Investigate the finding and apply recommended best-practices (parameterization, secrets management, or safer library APIs).", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "Use of functions like eval() or exec() can execute arbitrary code and should be avoided, especially on user-controlled inputs.", + "fix": "Replace eval/exec with safer alternatives. For parsing expressions use ast.literal_eval or write a simple parser. Validate inputs strictly.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "Calling subprocess APIs with unsanitized inputs or with shell=True can allow command injection or execution of unintended commands.", + "fix": "Avoid shell=True and pass arguments as a list. Validate and sanitize any inputs used in command construction.", + "llm_used": "offline", + "references": [ + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "Use of functions like eval() or exec() can execute arbitrary code and should be avoided, especially on user-controlled inputs.", + "fix": "Replace eval/exec with safer alternatives. For parsing expressions use ast.literal_eval or write a simple parser. Validate inputs strictly.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "Detected issue of type 'Dangerous Import'. Importing subprocess can enable executing shell commands; review usage.", + "fix": "Investigate the finding and apply recommended best-practices (parameterization, secrets management, or safer library APIs).", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "MD5 and SHA1 are considered cryptographically broken or weak for collision resistance and should not be used for security-sensitive hashing.", + "fix": "Use hashlib.sha256 or a stronger function and use salt + PBKDF2 / bcrypt / scrypt / Argon2 for password hashing.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-project-top-ten/", + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "This code constructs SQL statements by concatenating strings or by formatting them directly. Attackers can inject SQL fragments through inputs, leading to data leakage or corruption.", + "fix": "Use parameterized queries (e.g., cursor.execute(sql, params)) or ORM query builders to avoid direct string composition of SQL. Validate and sanitize inputs.", + "llm_used": "offline", + "references": [ + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "Overly-broad regex patterns like '.*' can match unintended input and can cause catastrophic backtracking.", + "fix": "Use more specific regexes and apply input length limits. Consider non-greedy qualifiers and anchors as appropriate.", + "llm_used": "offline", + "references": [ + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T170533Z.json b/Output/stage3-20251101T170533Z.json new file mode 100644 index 0000000..160ac7a --- /dev/null +++ b/Output/stage3-20251101T170533Z.json @@ -0,0 +1,241 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "The code snippet has a Hardcoded Secret issue. The password 'hunter2' is directly embedded in the source code, which is a significant security risk. If the source code is exposed to the public or accidentally committed to version control, sensitive information about the passwords used by the application will be accessible to unauthorized users.", + "fix": "To address this issue, you can replace the hardcoded password with an environment variable. Here's how to set up a Content Security Policy (CSP) and an environment variable to store the password securely:\n\n1. Update the `index.html` file to include a CSP header:\n\n \n\n2. Add a `.env` file in the same directory as your application:\n\n [secret]:\n password = 'your_secret_password_here'\n\n3. Create a variables file (e.g., `variables.env`) using your preferred configuration tool (e.g., node env tool, Python's `python-dotenv`, or .NET configuration):\n\n [secret]:\n password = publicity}${��config['.env']['[secret]']['password']}\n\n4. Include this file in your build process (e.g., npm, Python, or .NET)\n5. Retrieve the password from the environment variable in your code:\n\n import os\n password = os.environ.get('publicity-secret-password')`\n\nBy following these steps, you ensure that the password is encrypted and not exposed in the source code or committed to version control.", + "llm_used": "online", + "references": [ + { + "url": "https://nodejs DOTENV guide", + "description": "A library for secrets management in Node.js applications varies" + }, + { + "url": "https://docs饰器.net/section/placeholder", + "description": "Python library for secrets management" + }, + { + "url": "https://docs.microsoft.com/en-us/tech/xl-sponsored/password-policy", + "description": "Microsoft's approved password policy" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "{\n \"explanation\": \"The code snippet stores an API key directly in the source code (line 8: API_KEY = 'ABCD1234SECRETKEYSHOULDNOTBEHERE'). Hardcoding sensitive information such as API keys in source code is a significant security risk. If an attacker gains access to the source code, they can easily extract the API key and misuse it. This practice also compromises the integrity of the version control system, as the sensitive data is still committed alongside the code.\",\n \"fix\": \"Use environment variables or a secret manager to store the API key. Here's an example using environment variables:\",\n \"references\": [\n {\n \"name\": \"Hardcoded Secrets\",\n \"url\": \"https://snyk.co.uk/en/us/topics/python-security psychologists.list-code-security-practices\"\n },\n {\n \"name\": \"Secret Manager in Python\",\n \"url\": \"https://docs.travis-ci.com/compliance/flask-secret-manager-secure-environment-0d0a7f394248/\"\n },\n {\n \"name\": \"Node.js environment variables\",\n \"url\": \"https://nodejs.com/docs rehabilitating nodejs-config-getting-set.environ-variables/\"\n }\n ]\n}\n```", + "fix": "", + "llm_used": "online", + "references": [ + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "The code contains a hardcoded string 'ABCD1234SECRETKEYSHOULDNOTBEHERE' which may be a token or secret. Having sensitive information hardcoded in the source code is a common security risk. If this string is used for authenticating users or auditing logs, an attacker who gains access to the source code could potentially reverse engineer the secret and gain unauthorized access to sensitive systems or data.", + "fix": "Remove the hardcoded string 'ABCD1234SECRETKEYSHOULDNOTBEHERE' if it is not needed. If the secret is required in the code, consider implementing a secure secret management solution. Here's an example of how to remove it:\n\n```csharp\n// Remove the hardcoded string constant\nconst SECRETKEY = \"\"; // Or use a secure secret management solution\n// ...\n```", + "llm_used": "online", + "references": [ + { + "url": "https://developers.google.com/security/secureگذرانه-subsidies" + }, + { + "url": "https://owASP.org/Whitepapers/2019/Whitepaper-Security-In-Programming_V11.pdf" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "The given code is vulnerable to a code injection attack because it utilizes the `eval()` function with user input. `eval()` executes any sequence of code provided by the input, which can potentially lead to unexpected behavior or execution of malicious code by an attacker.", + "fix": "Replace the insecure `eval()` function with a safer alternative. Here's a simple fix using `ast.literal_eval()` which is designed to safely evaluate literals like strings, numbers and tuples.", + "llm_used": "online", + "references": [ + { + "url": "https://docs.python.org/3/library/ast.html#ast.literal_eval", + "description": "A function to safely evaluate a string containing a Python literal or container-update expression." + }, + { + "url": "https://globalые Indices of Vulnerabilities (CVEs) on the NIST CVEX database", + "description": "A database of known vulnerabilities in security standards, which can help with identifying and patching outdated libraries." + }, + { + "url": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy", + "description": "A W3C header that enables websites to be web-safe by restricting the sources of web content." + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "The code uses subprocess.run with shell=True, which is a vulnerable approach because it executes the command as if it were run in a shell, making it susceptible to command injection attacks. An attacker could inject malicious code in the 'ls -la' command, accessing sensitive system resources or other critical operations.", + "fix": "Avoid using shell=True and instead pass the command and its arguments as a list to subprocess.run. This ensures that the arguments are validated and executed safely. Here's the corrected line:\n\n'replace line 15 with:\nsubprocess.run(['ls', '-la'])", + "llm_used": "online", + "references": [ + { + "url": "https://docs.python.org/3/library/subprocess.html#subprocess.run", + "description": "Refer to the official Python documentation for the subprocess module for details on the correct usage of subprocess.run without shell=True." + }, + { + "url": "https://rationsoft.com/security-interviews/what-are-security-questions-for-python-designers", + "description": "For additional references on security best practices in Python development, visit this resource." + }, + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "{\n \"explanation\": \"The code attempts to unpickle data from a potentially untrusted binary string (`b` is a bytes object, and `not-a-pickle` is a string literal inside the bytes). However, `pickle.loads` is designed to unconditionally execute arbitrary Python object code, which can be exploited to execute arbitrary code if the unpickled data contains malicious code. This is a critical security risk as it allows for remote code execution from untrusted sources, leading to potential data breaches, information theft, or even manipulation of the system.\",\n \"fix\": \"Replace `pickle` with a safer alternative, such as the `json` module, which only parses JSON data and is less likely to execute code. If you must use `pickle`, ensure that you are unpickling data from trusted sources and consider using a safer deserialization method such as `dill` or `brotli`. Here's an example using JSON:\",\n \"fix\": \"obj = json.loads(b'{\"key\": \"value\"}')\\n\\n(Note: Ensure that the `json.loads` function is called with a bytes object or a string, not a binary string with non-base64 characters, such as the one provided in the original code.)\",\n \"references\": [\n {\n \"url\": \"https://docs.python.org/3/library/json.html#json-loads\",\n \"description\": \"Learn more about the JSON module in Python and how it can be used for safer deserialization.\"\n },\n {\n \"url\": \"https://pypi.org/project/brotli/\",\n \"description\": \"Consider using Brotli for more secure serialization and deserialization alternatives to pickle.\"\n },\n {\n \"url\": \"https://pypi.org/project/dill/\",\n \"description\": \"Explore the Dill library, which is an alternative to pickle that may offer some security improvements.\"\n }\n {\n \"url\": \"https://www,SIGNALTRIVIO\"\n }\n ]\n}", + "fix": "", + "llm_used": "online", + "references": [ + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "The code imports the `subprocess` module, which allows execution of system commands or processes. This is dangerous as it can lead to Command Injection vulnerabilities if not properly secured and validated.", + "fix": "Use `subprocess` with strict sandboxing, input validation, and shell escape protection. Consider using the `shlex.quote()` function to properly escape shell metacharacters when passing arguments to `subprocess.check_call()` or similar functions.", + "llm_used": "online", + "references": [ + { + "url": "https://docs.python.org/3/library/subprocess.html#subprocess-check-out-and-execution", + "description": "Python's official documentation for `subprocess` module, including guidelines for safe usage." + }, + { + "url": "https://www.security-upgrades.com/2009/08/stealth-eval.html", + "description": "SecurityUpgrades' advice on how to evaluate the effectiveness of various security controls, including sandboxing." + }, + { + "url": "https://owasp.org/CIS-v3.SEVERALounded safeguards/security-SecureDevelopment thanking open-source project that helps developers increase their code's resilience, including the use of subprocess with proper validation" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "The code uses `md5` for hashing, which is deprecated for security-sensitive operations. MD5 has known weaknesses and is not suitable for generating secure hashes.", + "fix": { + "description": "Replace `md5` with `sha256`.", + "code": { + "line": "26", + "snippet": "h = hashlib.sha256(b'data').hexdigest()" + } + }, + "llm_used": "online", + "references": [ + { + "name": "Use SHA-256 and SHA-3 for cryptographic purposes", + "url": "https://cryptosecurity.stackexchange.com/questions/45232/why-is-md5-and-sha1-considered-weak-hash-algorithms" + }, + { + "name": "Hashico documentation on SHA-256", + "url": "https://hashico.github.io/docs/pro Hornet a secure hash and salt storage" + }, + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "The code at line 36 uses a potentially unsafe way to build a SQL query via string formatting/concatenation. This can lead to SQL Injection vulnerabilities if user inputs are directly used.", + "fix": "Use parameterized queries to safely pass user data to SQL queries. In this example, we'll use Python's `format()` method with a whitelist of allowed characters for a simple fix. However, the recommended approach is to use Python's database connectors which support parameterized queries for more security and protection.", + "llm_used": "online", + "references": [ + { + "title": "SQL Injection (OWASP)", + "url": "https://owasp.org/Top10/2017-SQL-Injection.html" + }, + { + "title": "Parameterized Queries (SQL)", + "url": "https://en.wikipedia.org/wiki/SQL_parameterized_queries" + }, + { + "title": "Python Database API", + "url": "https://docs.python.org/3/library/sqlite3.html" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "{\n \"explanation\": \"The given regex pattern '.*' is overly-broad, allowing it to match the entire input string regardless of content. This can lead to excessive backtracking in the matching process, slowing down or even crashing the application. Additionally, such a broad pattern may also result in unintended matches that were not expected, potentially leading to security vulnerabilities in the application.\",\n \"fix\": {\n \"description\": \"Modify the regex pattern to anchor it to the entire input string and use a quantifier with a maximum limit for better performance and security.\",\n \"code\": \"pat = re.compile(r'^.*$')\" // Anchors the pattern to match the entire string, no change to the quantifier\n },\n \"references\": [\n {\n \"url\": \"https://stackoverflow.com/questions/30027144/what-is-the-difference-between-the-greedy-quantifier-and-the-non-greedy-quantifier\",\n \"description\": \"This Stack Overflow post discusses the use of quantifiers in regular expressions, including how to limit their greediness for performance reasons.\"\n },\n {\n \"url\": \"https://www.geeksforgeeks.org/python-tutors-re-grouping-characters/\",\n \"description\": \"This tutorial would explain in detail how regular expressions work in Python, including the need for specificity in the pattern matching process.\"\n }\n ]\n}", + "fix": "", + "llm_used": "online", + "references": [ + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T171830Z.cleaned.json b/Output/stage3-20251101T171830Z.cleaned.json new file mode 100644 index 0000000..aaa94d8 --- /dev/null +++ b/Output/stage3-20251101T171830Z.cleaned.json @@ -0,0 +1,259 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "The code snippet hardcodes the password 'hunter2' directly in the application's source code. This practice is dangerous because it exposes the secret to anyone who has access to the source code. If the code is shared, discussed, or submitted to a version control system, the password will become publicly known. This could lead to unauthorized access to the application or service that uses the password.", + "fix": "Replace the hardcoded password with an environment variable that holds the password value. This ensures that the password is not exposed in the source code and can be easily updated or retrieved from the environment.", + "llm_used": "online", + "references": [ + { + "url": "https://docs.python.org/3/library/os.environ.html", + "description": "Environment Variables in Python" + }, + { + "url": "https://docs.python.org/3/library/os.environ.html# Environment-Variables", + "description": "Python Environment Variables Management" + }, + { + "url": "https://security-center.github.io/secret managerial页面存档备份標帖https://github.com/python-one/accessible-secrets", + "description": "Python Secrets Manager" + }, + { + "url": "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + }, + { + "url": "https://owasp.org/www-project-top-ten/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "Hardcoding API keys directly into the source code is a security issue. By storing secrets like API keys in the source code, there is an increased risk of exposure through backup files, accidental submissions to version control, or if the code is ever shared or released prematurely. This can lead to unauthorized access or misuse of the API key, causing financial losses or other security breaches.", + "fix": "Use environment variables or a secret manager to store API keys in the application. Here's an example of how to configure an environment variable in your project:", + "llm_used": "online", + "references": [ + { + "url": "https://example.com/configuring-environment-variables-in-python" + }, + { + "url": "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" + }, + { + "url": "https://owasp.org/www-project-top-ten/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "The code contains a hardcoded and potentially sensitive token or secret key stored in plain text as a string constant. This issue is known as a Hardcoded Token vulnerability. It makes the application more insecure because if the token or secret is compromised, an attacker could use it to gain unauthorized access to sensitive information or perform malicious actions.", + "fix": "Remove the hardcoded token or secret from the code and consider using a secure method to generate, store, and access the token or secret. Here's an example fix assuming the string is a simple hardcoded key:---// Example fix (replace with the actual secure method)const SECRET_KEY = processPasswordSecret(); // Assuming a proper secure method is implemented---For the given example in Line 8, the fix would be to remove the entire line with the hardcoded token or secret:// Remove the hardcoded token or secret// const ABCD1234SECRETKEYSHOULDNOTBEHERE;", + "llm_used": "online", + "references": [ + { + "url": "https://www.opensecuritygroup.com/owasp-top-ten-lists-and-anagrams", + "description": "OWASP Top Ten Lists" + }, + { + "url": "https://whatnodoes.net/xkcd-120/", + "description": "Hardcode and XKCD 120" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "The given code uses `eval(user_input)` which can lead to code injection attacks if `user_input` is untrusted. An attacker can execute arbitrary code, leading to potential security vulnerabilities, data breaches, or system compromise.", + "fix": "Replace the insecure `eval()` usage with a safer alternative. Here's an example of how to use a restricted evaluator (Python's `ast.literal_eval()`) to safely evaluate simple Python literals.", + "llm_used": "online", + "references": [ + { + "url": "https://python.org/doc/howashtools/safety/", + "description": "Learn about safe ways to evaluate Python literals using `ast.literal_eval()`" + }, + { + "url": "https://owASP.org/Top10/Dos_ WhatsAppッシュ underestimate the importance of input validation", + "description": "Understand the risks of input validation and the importance of using secure methods like `ast.literal_eval()`" + }, + { + "url": "https://owasp.org/www-community/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "{ \"explanation\": \"The code uses subprocess.run with shell=True, which is a susceptible way of executing arbitrary commands. This leaves the application vulnerable to command injection attacks. An attacker could potentially inject malicious code through the command's arguments, leading to unauthorized access or modification of system resources. This issue is related to a Common Vulnerability Scoring System (CVSS) base score of 8, as it directly allows for remote code execution.\", \"fix\": \"Avoid using shell=True and instead pass the command and its arguments as separate arguments to subprocess.run. Ensure that inputs are properly validated to prevent injection attacks. Here's the corrected line:\", \"fix\": \"subprocess.run('ls', '-la')\", \"references\": [ { \"name\": \"Python Subprocess Safety\", \"url\": \"https://docs.python.org/3/library/subprocess.html#security-vulnerabilities\" }, { \"name\": \"Command Injection Prevention\", \"url\": \"https:// OWASP.org/Vulnerabilities-list/Command-injection\" }, { \"name\": \"CVSS Database\", \"url\": \"https://cune hostebin.org/cvss/\" // To search for the CVSS entry for this vulnerability patterns } ]}", + "fix": "", + "llm_used": "online", + "references": [ + { + "url": "https://cheatsheetseries.owasp.org/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "The code is attempting to use `pickle.loads()` with a suspicious input in the format of a string preceded by the literal 'not-a-pickle'. This is likely intended to obfuscate or mislead, but the actual issue is the lack of trust in the source of the input. Unpickling data from untrusted sources, such as user input or network data, can execute arbitrary code, leading to security vulnerabilities like Remote Code Execution (RCE) and information disclosure.", + "fix": "Validating and sanitizing the input to ensure it's a trusted source before unpickling is crucial. However, using `pickle` directly remains risky. Instead, consider using safer alternatives like Vault's Secret Service or AWS Secret Store for storing and managing sensitive data and tokens.", + "llm_used": "online", + "references": [ + { + "url": "https://owasp.org/www-community/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "The code imports the `subprocess` module, which allows execution of system commands directly from Python. Without proper restrictions or validation, this could lead to unintended shell command executions, potentially causing unauthorized access, data leakage, or other security vulnerabilities if the input is compromised.", + "fix": { + "description": "Use a safer way to execute commands without shell injection. Here's a suggestion using `os.system` instead, which does not take input but also has its own risks. Alternatively, use the Python `run` function in `pyfiglet` for a more secure approach to executing commands.", + "code": "import os\ncommand = 'ls'\nos.system(command)" + }, + "llm_used": "online", + "references": [ + { + "url": "https://security.stackexchange.com/questions/18713/be-careful-with-shell-command-execution-in-python" + }, + { + "url": "https://stackoverflow.com/questions/13117660/use-os-system-instead-of-subprocess-when-command-execution-is-needed" + }, + { + "url": "https://pypi.org/project/pyfiglet/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "The code uses the MD5 hash algorithm, which is considered weak and has been known to have collisions, making it unsuitable for security-critical purposes such as password hashing. Using MD5 can lead to vulnerabilities where different inputs may produce the same hash, granting unauthorized access.", + "fix": "Replace `md5` with a more secure algorithm like `sha256`. Here's the corrected line: `h = hashlib.sha256(b'data').hexdigest()`", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/security-top-10/2006/pet10b-java.html" + }, + { + "url": "https://passwordhashing.bestpractices.io/" + }, + { + "url": "https://www.ipa.go.jp/security/english/" + }, + { + "url": "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "The code at line 36 is vulnerable to SQL Injection because the 'query' variable is used directly in a cursor.execute() call without validation or sanitization. An attacker could inject malicious SQL code by manipulating the 'query' variable, leading to unauthorized data modification, data leakage, or other unintended consequences.", + "fix": "Replace the line with a parameterized query to prevent SQL Injection. Use placeholders for query parameters and pass them as a separate parameter to cursor.execute().", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/www.ReturningReturns/WSenheimans_12.html", + "description": "SQL Injection is a type of computer attack that involves the injection of unintended SQL or SQL-like code into a computer database management system." + }, + { + "url": "https://information-security.stackexchange.com/questions/5068/how-to-prevent-sql-injection-attacks", + "description": "SQL Injection can be prevented with proper input validation, canonicalization, and sanitization. Parameterized queries can further protect against SQL Injection." + }, + { + "url": "https://docs.python.org/3/library/sqlite3.html#using-parameterized-queries", + "description": "Python's sqlite3 and MySQLdb modules provide support for parameterized queries, making it easier to prevent SQL Injection." + }, + { + "url": "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html" + }, + { + "url": "https://owasp.org/www-community/attacks/SQL_Injection" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "The given regex pattern '.*' is overly-broad, potentially leading to excessive backtracking and unintended matches. This can cause performance issues or security vulnerabilities if the pattern is used to match input in a dynamic code execution context.", + "fix": "Replace '.*' with a more precise regex pattern. For example, if matching text, use '^[^\\n]*$' to match all non-newline characters or '^[^\\t\\n\\r\\v\\f]*$' for shorter lines. Always anchor your regex and use limit quantifiers to restrict the number of matches.", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/textbook/JavaSecurityHardeningTips152.html", + "description": "JP0B: Session Manager - Uncontrolled Session and Request which can be exploited through use of classids and instances." + }, + { + "url": "https://iðeland.fmi.pl/regex-regions/en/current/", + "description": "Regular Expressions (regex) in various programming languages" + }, + { + "url": "https://owasp.org/www-community/" + } + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T171830Z.json b/Output/stage3-20251101T171830Z.json new file mode 100644 index 0000000..035e3b1 --- /dev/null +++ b/Output/stage3-20251101T171830Z.json @@ -0,0 +1,257 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "The code snippet hardcodes the password 'hunter2' directly in the application's source code. This practice is dangerous because it exposes the secret to anyone who has access to the source code. If the code is shared, discussed, or submitted to a version control system, the password will become publicly known. This could lead to unauthorized access to the application or service that uses the password.", + "fix": "Replace the hardcoded password with an environment variable that holds the password value. This ensures that the password is not exposed in the source code and can be easily updated or retrieved from the environment.", + "llm_used": "online", + "references": [ + { + "name": "Environment Variables in Python", + "url": "https://docs.python.org/3/library/os.environ.html" + }, + { + "name": "Python Environment Variables Management", + "url": "https://docs.python.org/3/library/os.environ.html# Environment-Variables" + }, + { + "name": "Python Secrets Manager", + "url": "https://security-center.github.io/secret managerial页面存档备份標帖https://github.com/python-one/accessible-secrets" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "Hardcoding API keys directly into the source code is a security issue. By storing secrets like API keys in the source code, there is an increased risk of exposure through backup files, accidental submissions to version control, or if the code is ever shared or released prematurely. This can lead to unauthorized access or misuse of the API key, causing financial losses or other security breaches.", + "fix": "Use environment variables or a secret manager to store API keys in the application. Here's an example of how to configure an environment variable in your project:", + "llm_used": "online", + "references": [ + { + "url": "https://example.com/configuring-environment-variables-in-python" + }, + { + "url": "https:// OWASP.org/Top-Threats maka-_OAuth-Token-Vulnerability-2021-12" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "The code contains a hardcoded and potentially sensitive token or secret key stored in plain text as a string constant. This issue is known as a Hardcoded Token vulnerability. It makes the application more insecure because if the token or secret is compromised, an attacker could use it to gain unauthorized access to sensitive information or perform malicious actions.", + "fix": "Remove the hardcoded token or secret from the code and consider using a secure method to generate, store, and access the token or secret. Here's an example fix assuming the string is a simple hardcoded key:\n\n---\n\n// Example fix (replace with the actual secure method)\nconst SECRET_KEY = processPasswordSecret(); // Assuming a proper secure method is implemented\n---\n\nFor the given example in Line 8, the fix would be to remove the entire line with the hardcoded token or secret:\n\n// Remove the hardcoded token or secret\n// const ABCD1234SECRETKEYSHOULDNOTBEHERE;", + "llm_used": "online", + "references": [ + { + "name": "OWASP Top Ten Lists", + "url": "https://www.opensecuritygroup.com/owasp-top-ten-lists-and-anagrams" + }, + { + "name": "Hardcode and XKCD 120", + "url": "https://whatnodoes.net/xkcd-120/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "The given code uses `eval(user_input)` which can lead to code injection attacks if `user_input` is untrusted. An attacker can execute arbitrary code, leading to potential security vulnerabilities, data breaches, or system compromise.", + "fix": "Replace the insecure `eval()` usage with a safer alternative. Here's an example of how to use a restricted evaluator (Python's `ast.literal_eval()`) to safely evaluate simple Python literals.", + "llm_used": "online", + "references": [ + { + "url": "https://python.org/doc/howashtools/safety/", + "description": "Learn about safe ways to evaluate Python literals using `ast.literal_eval()`" + }, + { + "url": "https://owASP.org/Top10/Dos_ WhatsAppッシュ underestimate the importance of input validation", + "description": "Understand the risks of input validation and the importance of using secure methods like `ast.literal_eval()`" + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "{\n \"explanation\": \"The code uses subprocess.run with shell=True, which is a susceptible way of executing arbitrary commands. This leaves the application vulnerable to command injection attacks. An attacker could potentially inject malicious code through the command's arguments, leading to unauthorized access or modification of system resources. This issue is related to a Common Vulnerability Scoring System (CVSS) base score of 8, as it directly allows for remote code execution.\",\n \"fix\": \"Avoid using shell=True and instead pass the command and its arguments as separate arguments to subprocess.run. Ensure that inputs are properly validated to prevent injection attacks. Here's the corrected line:\",\n \"fix\": \"subprocess.run('ls', '-la')\",\n \"references\": [\n {\n \"name\": \"Python Subprocess Safety\",\n \"url\": \"https://docs.python.org/3/library/subprocess.html#security-vulnerabilities\"\n },\n {\n \"name\": \"Command Injection Prevention\",\n \"url\": \"https:// OWASP.org/Vulnerabilities-list/Command-injection\"\n },\n {\n \"name\": \"CVSS Database\",\n \"url\": \"https://cune hostebin.org/cvss/\" // To search for the CVSS entry for this vulnerability patterns\n }\n ]\n}", + "fix": "", + "llm_used": "online", + "references": [ + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "The code is attempting to use `pickle.loads()` with a suspicious input in the format of a string preceded by the literal 'not-a-pickle'. This is likely intended to obfuscate or mislead, but the actual issue is the lack of trust in the source of the input. Unpickling data from untrusted sources, such as user input or network data, can execute arbitrary code, leading to security vulnerabilities like Remote Code Execution (RCE) and information disclosure.", + "fix": "Validating and sanitizing the input to ensure it's a trusted source before unpickling is crucial. However, using `pickle` directly remains risky. Instead, consider using safer alternatives like Vault's Secret Service or AWS Secret Store for storing and managing sensitive data and tokens.", + "llm_used": "online", + "references": [ + { + "url": "https:// pickle.codeЂ « If you're not sure, you might want to read some more materials about the Python pickle module. In particular, this one is about the typical public concern about the functionality of the pickle module. Specifically the concern about its security and the recommended improvements, you can find it here. link (changes to fit example purposes)" + }, + { + "url": "https:// security Harrison.orgvasive " + }, + { + "url": "https:// netikax.com " + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "The code imports the `subprocess` module, which allows execution of system commands directly from Python. Without proper restrictions or validation, this could lead to unintended shell command executions, potentially causing unauthorized access, data leakage, or other security vulnerabilities if the input is compromised.", + "fix": { + "description": "Use a safer way to execute commands without shell injection. Here's a suggestion using `os.system` instead, which does not take input but also has its own risks. Alternatively, use the Python `run` function in `pyfiglet` for a more secure approach to executing commands.", + "code": "import os\ncommand = 'ls'\nos.system(command)" + }, + "llm_used": "online", + "references": [ + { + "title": "Be careful with shell command execution in Python", + "url": "https://security.stackexchange.com/questions/18713/be-careful-with-shell-command-execution-in-python" + }, + { + "title": "Use os.system instead of subprocess when command execution is needed", + "url": "https://stackoverflow.com/questions/13117660/use-os-system-instead-of-subprocess-when-command-execution-is-needed" + }, + { + "title": "Python run function based on figlet", + "url": "https://pypi.org/project/pyfiglet/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "The code uses the MD5 hash algorithm, which is considered weak and has been known to have collisions, making it unsuitable for security-critical purposes such as password hashing. Using MD5 can lead to vulnerabilities where different inputs may produce the same hash, granting unauthorized access.", + "fix": "Replace `md5` with a more secure algorithm like `sha256`. Here's the corrected line: `h = hashlib.sha256(b'data').hexdigest()`", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/security-top-10/2006/pet10b-java.html" + }, + { + "url": "https://passwordhashing.bestpractices.io/" + }, + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "The code at line 36 is vulnerable to SQL Injection because the 'query' variable is used directly in a cursor.execute() call without validation or sanitization. An attacker could inject malicious SQL code by manipulating the 'query' variable, leading to unauthorized data modification, data leakage, or other unintended consequences.", + "fix": "Replace the line with a parameterized query to prevent SQL Injection. Use placeholders for query parameters and pass them as a separate parameter to cursor.execute().", + "llm_used": "online", + "references": [ + { + "name": "SQL Injection", + "description": "SQL Injection is a type of computer attack that involves the injection of unintended SQL or SQL-like code into a computer database management system.", + "url": "https://owASP.org/www.ReturningReturns/WSenheimans_12.html" + }, + { + "name": "SQL Injection Mitigation", + "description": "SQL Injection can be prevented with proper input validation, canonicalization, and sanitization. Parameterized queries can further protect against SQL Injection.", + "url": "https://information-security.stackexchange.com/questions/5068/how-to-prevent-sql-injection-attacks" + }, + { + "name": "SQL Injection in Python", + "description": "Python's sqlite3 and MySQLdb modules provide support for parameterized queries, making it easier to prevent SQL Injection.", + "url": "https://docs.python.org/3/library/sqlite3.html#using-parameterized-queries" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "The given regex pattern '.*' is overly-broad, potentially leading to excessive backtracking and unintended matches. This can cause performance issues or security vulnerabilities if the pattern is used to match input in a dynamic code execution context.", + "fix": "Replace '.*' with a more precise regex pattern. For example, if matching text, use '^[^\\n]*$' to match all non-newline characters or '^[^\\t\\n\\r\\v\\f]*$' for shorter lines. Always anchor your regex and use limit quantifiers to restrict the number of matches.", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/textbook/JavaSecurityHardeningTips152.html", + "description": "JP0B: Session Manager - Uncontrolled Session and Request which can be exploited through use of classids and instances." + }, + { + "url": "https:// लगाते.OracleCSHR.com/choose-language/regex.html", + "description": "Basic regular expression in various programming languages with examples in C, C++, and SQL" + }, + { + "url": "https://iðeland.fmi.pl/regex-regions/en/current/", + "description": "Regular Expressions (regex) in various programming languages" + }, + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T172426Z.cleaned.json b/Output/stage3-20251101T172426Z.cleaned.json new file mode 100644 index 0000000..035e3b1 --- /dev/null +++ b/Output/stage3-20251101T172426Z.cleaned.json @@ -0,0 +1,257 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "The code snippet hardcodes the password 'hunter2' directly in the application's source code. This practice is dangerous because it exposes the secret to anyone who has access to the source code. If the code is shared, discussed, or submitted to a version control system, the password will become publicly known. This could lead to unauthorized access to the application or service that uses the password.", + "fix": "Replace the hardcoded password with an environment variable that holds the password value. This ensures that the password is not exposed in the source code and can be easily updated or retrieved from the environment.", + "llm_used": "online", + "references": [ + { + "name": "Environment Variables in Python", + "url": "https://docs.python.org/3/library/os.environ.html" + }, + { + "name": "Python Environment Variables Management", + "url": "https://docs.python.org/3/library/os.environ.html# Environment-Variables" + }, + { + "name": "Python Secrets Manager", + "url": "https://security-center.github.io/secret managerial页面存档备份標帖https://github.com/python-one/accessible-secrets" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "Hardcoding API keys directly into the source code is a security issue. By storing secrets like API keys in the source code, there is an increased risk of exposure through backup files, accidental submissions to version control, or if the code is ever shared or released prematurely. This can lead to unauthorized access or misuse of the API key, causing financial losses or other security breaches.", + "fix": "Use environment variables or a secret manager to store API keys in the application. Here's an example of how to configure an environment variable in your project:", + "llm_used": "online", + "references": [ + { + "url": "https://example.com/configuring-environment-variables-in-python" + }, + { + "url": "https:// OWASP.org/Top-Threats maka-_OAuth-Token-Vulnerability-2021-12" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "The code contains a hardcoded and potentially sensitive token or secret key stored in plain text as a string constant. This issue is known as a Hardcoded Token vulnerability. It makes the application more insecure because if the token or secret is compromised, an attacker could use it to gain unauthorized access to sensitive information or perform malicious actions.", + "fix": "Remove the hardcoded token or secret from the code and consider using a secure method to generate, store, and access the token or secret. Here's an example fix assuming the string is a simple hardcoded key:\n\n---\n\n// Example fix (replace with the actual secure method)\nconst SECRET_KEY = processPasswordSecret(); // Assuming a proper secure method is implemented\n---\n\nFor the given example in Line 8, the fix would be to remove the entire line with the hardcoded token or secret:\n\n// Remove the hardcoded token or secret\n// const ABCD1234SECRETKEYSHOULDNOTBEHERE;", + "llm_used": "online", + "references": [ + { + "name": "OWASP Top Ten Lists", + "url": "https://www.opensecuritygroup.com/owasp-top-ten-lists-and-anagrams" + }, + { + "name": "Hardcode and XKCD 120", + "url": "https://whatnodoes.net/xkcd-120/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "The given code uses `eval(user_input)` which can lead to code injection attacks if `user_input` is untrusted. An attacker can execute arbitrary code, leading to potential security vulnerabilities, data breaches, or system compromise.", + "fix": "Replace the insecure `eval()` usage with a safer alternative. Here's an example of how to use a restricted evaluator (Python's `ast.literal_eval()`) to safely evaluate simple Python literals.", + "llm_used": "online", + "references": [ + { + "url": "https://python.org/doc/howashtools/safety/", + "description": "Learn about safe ways to evaluate Python literals using `ast.literal_eval()`" + }, + { + "url": "https://owASP.org/Top10/Dos_ WhatsAppッシュ underestimate the importance of input validation", + "description": "Understand the risks of input validation and the importance of using secure methods like `ast.literal_eval()`" + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "{\n \"explanation\": \"The code uses subprocess.run with shell=True, which is a susceptible way of executing arbitrary commands. This leaves the application vulnerable to command injection attacks. An attacker could potentially inject malicious code through the command's arguments, leading to unauthorized access or modification of system resources. This issue is related to a Common Vulnerability Scoring System (CVSS) base score of 8, as it directly allows for remote code execution.\",\n \"fix\": \"Avoid using shell=True and instead pass the command and its arguments as separate arguments to subprocess.run. Ensure that inputs are properly validated to prevent injection attacks. Here's the corrected line:\",\n \"fix\": \"subprocess.run('ls', '-la')\",\n \"references\": [\n {\n \"name\": \"Python Subprocess Safety\",\n \"url\": \"https://docs.python.org/3/library/subprocess.html#security-vulnerabilities\"\n },\n {\n \"name\": \"Command Injection Prevention\",\n \"url\": \"https:// OWASP.org/Vulnerabilities-list/Command-injection\"\n },\n {\n \"name\": \"CVSS Database\",\n \"url\": \"https://cune hostebin.org/cvss/\" // To search for the CVSS entry for this vulnerability patterns\n }\n ]\n}", + "fix": "", + "llm_used": "online", + "references": [ + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "The code is attempting to use `pickle.loads()` with a suspicious input in the format of a string preceded by the literal 'not-a-pickle'. This is likely intended to obfuscate or mislead, but the actual issue is the lack of trust in the source of the input. Unpickling data from untrusted sources, such as user input or network data, can execute arbitrary code, leading to security vulnerabilities like Remote Code Execution (RCE) and information disclosure.", + "fix": "Validating and sanitizing the input to ensure it's a trusted source before unpickling is crucial. However, using `pickle` directly remains risky. Instead, consider using safer alternatives like Vault's Secret Service or AWS Secret Store for storing and managing sensitive data and tokens.", + "llm_used": "online", + "references": [ + { + "url": "https:// pickle.codeЂ « If you're not sure, you might want to read some more materials about the Python pickle module. In particular, this one is about the typical public concern about the functionality of the pickle module. Specifically the concern about its security and the recommended improvements, you can find it here. link (changes to fit example purposes)" + }, + { + "url": "https:// security Harrison.orgvasive " + }, + { + "url": "https:// netikax.com " + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "The code imports the `subprocess` module, which allows execution of system commands directly from Python. Without proper restrictions or validation, this could lead to unintended shell command executions, potentially causing unauthorized access, data leakage, or other security vulnerabilities if the input is compromised.", + "fix": { + "description": "Use a safer way to execute commands without shell injection. Here's a suggestion using `os.system` instead, which does not take input but also has its own risks. Alternatively, use the Python `run` function in `pyfiglet` for a more secure approach to executing commands.", + "code": "import os\ncommand = 'ls'\nos.system(command)" + }, + "llm_used": "online", + "references": [ + { + "title": "Be careful with shell command execution in Python", + "url": "https://security.stackexchange.com/questions/18713/be-careful-with-shell-command-execution-in-python" + }, + { + "title": "Use os.system instead of subprocess when command execution is needed", + "url": "https://stackoverflow.com/questions/13117660/use-os-system-instead-of-subprocess-when-command-execution-is-needed" + }, + { + "title": "Python run function based on figlet", + "url": "https://pypi.org/project/pyfiglet/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "The code uses the MD5 hash algorithm, which is considered weak and has been known to have collisions, making it unsuitable for security-critical purposes such as password hashing. Using MD5 can lead to vulnerabilities where different inputs may produce the same hash, granting unauthorized access.", + "fix": "Replace `md5` with a more secure algorithm like `sha256`. Here's the corrected line: `h = hashlib.sha256(b'data').hexdigest()`", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/security-top-10/2006/pet10b-java.html" + }, + { + "url": "https://passwordhashing.bestpractices.io/" + }, + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "The code at line 36 is vulnerable to SQL Injection because the 'query' variable is used directly in a cursor.execute() call without validation or sanitization. An attacker could inject malicious SQL code by manipulating the 'query' variable, leading to unauthorized data modification, data leakage, or other unintended consequences.", + "fix": "Replace the line with a parameterized query to prevent SQL Injection. Use placeholders for query parameters and pass them as a separate parameter to cursor.execute().", + "llm_used": "online", + "references": [ + { + "name": "SQL Injection", + "description": "SQL Injection is a type of computer attack that involves the injection of unintended SQL or SQL-like code into a computer database management system.", + "url": "https://owASP.org/www.ReturningReturns/WSenheimans_12.html" + }, + { + "name": "SQL Injection Mitigation", + "description": "SQL Injection can be prevented with proper input validation, canonicalization, and sanitization. Parameterized queries can further protect against SQL Injection.", + "url": "https://information-security.stackexchange.com/questions/5068/how-to-prevent-sql-injection-attacks" + }, + { + "name": "SQL Injection in Python", + "description": "Python's sqlite3 and MySQLdb modules provide support for parameterized queries, making it easier to prevent SQL Injection.", + "url": "https://docs.python.org/3/library/sqlite3.html#using-parameterized-queries" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "The given regex pattern '.*' is overly-broad, potentially leading to excessive backtracking and unintended matches. This can cause performance issues or security vulnerabilities if the pattern is used to match input in a dynamic code execution context.", + "fix": "Replace '.*' with a more precise regex pattern. For example, if matching text, use '^[^\\n]*$' to match all non-newline characters or '^[^\\t\\n\\r\\v\\f]*$' for shorter lines. Always anchor your regex and use limit quantifiers to restrict the number of matches.", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/textbook/JavaSecurityHardeningTips152.html", + "description": "JP0B: Session Manager - Uncontrolled Session and Request which can be exploited through use of classids and instances." + }, + { + "url": "https:// लगाते.OracleCSHR.com/choose-language/regex.html", + "description": "Basic regular expression in various programming languages with examples in C, C++, and SQL" + }, + { + "url": "https://iðeland.fmi.pl/regex-regions/en/current/", + "description": "Regular Expressions (regex) in various programming languages" + }, + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Output/stage3-20251101T172426Z.json b/Output/stage3-20251101T172426Z.json new file mode 100644 index 0000000..035e3b1 --- /dev/null +++ b/Output/stage3-20251101T172426Z.json @@ -0,0 +1,257 @@ +{ + "results": { + "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 7, + "snippet": "password = \"hunter2\"", + "message": "Avoid hardcoding passwords in source code; use environment variables or secret stores.", + "severity": "High", + "explanation": "The code snippet hardcodes the password 'hunter2' directly in the application's source code. This practice is dangerous because it exposes the secret to anyone who has access to the source code. If the code is shared, discussed, or submitted to a version control system, the password will become publicly known. This could lead to unauthorized access to the application or service that uses the password.", + "fix": "Replace the hardcoded password with an environment variable that holds the password value. This ensures that the password is not exposed in the source code and can be easily updated or retrieved from the environment.", + "llm_used": "online", + "references": [ + { + "name": "Environment Variables in Python", + "url": "https://docs.python.org/3/library/os.environ.html" + }, + { + "name": "Python Environment Variables Management", + "url": "https://docs.python.org/3/library/os.environ.html# Environment-Variables" + }, + { + "name": "Python Secrets Manager", + "url": "https://security-center.github.io/secret managerial页面存档备份標帖https://github.com/python-one/accessible-secrets" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Hardcoded Secret", + "line": 8, + "snippet": "API_KEY = \"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Avoid hardcoding API keys or credentials in source code; use environment variables or secret managers.", + "severity": "High", + "explanation": "Hardcoding API keys directly into the source code is a security issue. By storing secrets like API keys in the source code, there is an increased risk of exposure through backup files, accidental submissions to version control, or if the code is ever shared or released prematurely. This can lead to unauthorized access or misuse of the API key, causing financial losses or other security breaches.", + "fix": "Use environment variables or a secret manager to store API keys in the application. Here's an example of how to configure an environment variable in your project:", + "llm_used": "online", + "references": [ + { + "url": "https://example.com/configuring-environment-variables-in-python" + }, + { + "url": "https:// OWASP.org/Top-Threats maka-_OAuth-Token-Vulnerability-2021-12" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", + "https://owasp.org/www-project-top-ten/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible Hardcoded Token", + "line": 8, + "snippet": "\"ABCD1234SECRETKEYSHOULDNOTBEHERE\"", + "message": "Found a long string constant which might be a token or secret; verify and remove from code if sensitive.", + "severity": "High", + "explanation": "The code contains a hardcoded and potentially sensitive token or secret key stored in plain text as a string constant. This issue is known as a Hardcoded Token vulnerability. It makes the application more insecure because if the token or secret is compromised, an attacker could use it to gain unauthorized access to sensitive information or perform malicious actions.", + "fix": "Remove the hardcoded token or secret from the code and consider using a secure method to generate, store, and access the token or secret. Here's an example fix assuming the string is a simple hardcoded key:\n\n---\n\n// Example fix (replace with the actual secure method)\nconst SECRET_KEY = processPasswordSecret(); // Assuming a proper secure method is implemented\n---\n\nFor the given example in Line 8, the fix would be to remove the entire line with the hardcoded token or secret:\n\n// Remove the hardcoded token or secret\n// const ABCD1234SECRETKEYSHOULDNOTBEHERE;", + "llm_used": "online", + "references": [ + { + "name": "OWASP Top Ten Lists", + "url": "https://www.opensecuritygroup.com/owasp-top-ten-lists-and-anagrams" + }, + { + "name": "Hardcode and XKCD 120", + "url": "https://whatnodoes.net/xkcd-120/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 12, + "snippet": "res = eval(user_input)", + "message": "Use of eval() can lead to code injection or unexpected behavior. Avoid using it with untrusted input.", + "severity": "Medium", + "explanation": "The given code uses `eval(user_input)` which can lead to code injection attacks if `user_input` is untrusted. An attacker can execute arbitrary code, leading to potential security vulnerabilities, data breaches, or system compromise.", + "fix": "Replace the insecure `eval()` usage with a safer alternative. Here's an example of how to use a restricted evaluator (Python's `ast.literal_eval()`) to safely evaluate simple Python literals.", + "llm_used": "online", + "references": [ + { + "url": "https://python.org/doc/howashtools/safety/", + "description": "Learn about safe ways to evaluate Python literals using `ast.literal_eval()`" + }, + { + "url": "https://owASP.org/Top10/Dos_ WhatsAppッシュ underestimate the importance of input validation", + "description": "Understand the risks of input validation and the importance of using secure methods like `ast.literal_eval()`" + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Suspicious Subprocess Call", + "line": 15, + "snippet": "subprocess.run(\"ls -la\", shell=True)", + "message": "Use of subprocess APIs can run external commands; ensure inputs are sanitized. Detected shell=True which increases risk of injection.", + "severity": "Medium", + "explanation": "{\n \"explanation\": \"The code uses subprocess.run with shell=True, which is a susceptible way of executing arbitrary commands. This leaves the application vulnerable to command injection attacks. An attacker could potentially inject malicious code through the command's arguments, leading to unauthorized access or modification of system resources. This issue is related to a Common Vulnerability Scoring System (CVSS) base score of 8, as it directly allows for remote code execution.\",\n \"fix\": \"Avoid using shell=True and instead pass the command and its arguments as separate arguments to subprocess.run. Ensure that inputs are properly validated to prevent injection attacks. Here's the corrected line:\",\n \"fix\": \"subprocess.run('ls', '-la')\",\n \"references\": [\n {\n \"name\": \"Python Subprocess Safety\",\n \"url\": \"https://docs.python.org/3/library/subprocess.html#security-vulnerabilities\"\n },\n {\n \"name\": \"Command Injection Prevention\",\n \"url\": \"https:// OWASP.org/Vulnerabilities-list/Command-injection\"\n },\n {\n \"name\": \"CVSS Database\",\n \"url\": \"https://cune hostebin.org/cvss/\" // To search for the CVSS entry for this vulnerability patterns\n }\n ]\n}", + "fix": "", + "llm_used": "online", + "references": [ + "https://cheatsheetseries.owasp.org/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Function Usage", + "line": 21, + "snippet": "obj = pickle.loads(b\"not-a-pickle\")", + "message": "Unpickling data from untrusted sources can lead to remote code execution.", + "severity": "Medium", + "explanation": "The code is attempting to use `pickle.loads()` with a suspicious input in the format of a string preceded by the literal 'not-a-pickle'. This is likely intended to obfuscate or mislead, but the actual issue is the lack of trust in the source of the input. Unpickling data from untrusted sources, such as user input or network data, can execute arbitrary code, leading to security vulnerabilities like Remote Code Execution (RCE) and information disclosure.", + "fix": "Validating and sanitizing the input to ensure it's a trusted source before unpickling is crucial. However, using `pickle` directly remains risky. Instead, consider using safer alternatives like Vault's Secret Service or AWS Secret Store for storing and managing sensitive data and tokens.", + "llm_used": "online", + "references": [ + { + "url": "https:// pickle.codeЂ « If you're not sure, you might want to read some more materials about the Python pickle module. In particular, this one is about the typical public concern about the functionality of the pickle module. Specifically the concern about its security and the recommended improvements, you can find it here. link (changes to fit example purposes)" + }, + { + "url": "https:// security Harrison.orgvasive " + }, + { + "url": "https:// netikax.com " + }, + "https://owasp.org/www-community/" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Dangerous Import", + "line": 2, + "snippet": "import subprocess", + "message": "Importing subprocess can enable executing shell commands; review usage.", + "severity": "Medium", + "explanation": "The code imports the `subprocess` module, which allows execution of system commands directly from Python. Without proper restrictions or validation, this could lead to unintended shell command executions, potentially causing unauthorized access, data leakage, or other security vulnerabilities if the input is compromised.", + "fix": { + "description": "Use a safer way to execute commands without shell injection. Here's a suggestion using `os.system` instead, which does not take input but also has its own risks. Alternatively, use the Python `run` function in `pyfiglet` for a more secure approach to executing commands.", + "code": "import os\ncommand = 'ls'\nos.system(command)" + }, + "llm_used": "online", + "references": [ + { + "title": "Be careful with shell command execution in Python", + "url": "https://security.stackexchange.com/questions/18713/be-careful-with-shell-command-execution-in-python" + }, + { + "title": "Use os.system instead of subprocess when command execution is needed", + "url": "https://stackoverflow.com/questions/13117660/use-os-system-instead-of-subprocess-when-command-execution-is-needed" + }, + { + "title": "Python run function based on figlet", + "url": "https://pypi.org/project/pyfiglet/" + } + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Deprecated Hash", + "line": 26, + "snippet": "h = hashlib.md5(b\"data\").hexdigest()", + "message": "Use of md5 is deprecated for security-sensitive hashing. Use sha256 or stronger algorithms.", + "severity": "Medium", + "explanation": "The code uses the MD5 hash algorithm, which is considered weak and has been known to have collisions, making it unsuitable for security-critical purposes such as password hashing. Using MD5 can lead to vulnerabilities where different inputs may produce the same hash, granting unauthorized access.", + "fix": "Replace `md5` with a more secure algorithm like `sha256`. Here's the corrected line: `h = hashlib.sha256(b'data').hexdigest()`", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/security-top-10/2006/pet10b-java.html" + }, + { + "url": "https://passwordhashing.bestpractices.io/" + }, + "https://www.ipa.go.jp/security/english/", + "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Possible SQL Injection", + "line": 36, + "snippet": "cursor.execute(query)", + "message": "Detected SQL execution using a variable that appears to be built via string formatting/concatenation. Use parameterized queries.", + "severity": "High", + "explanation": "The code at line 36 is vulnerable to SQL Injection because the 'query' variable is used directly in a cursor.execute() call without validation or sanitization. An attacker could inject malicious SQL code by manipulating the 'query' variable, leading to unauthorized data modification, data leakage, or other unintended consequences.", + "fix": "Replace the line with a parameterized query to prevent SQL Injection. Use placeholders for query parameters and pass them as a separate parameter to cursor.execute().", + "llm_used": "online", + "references": [ + { + "name": "SQL Injection", + "description": "SQL Injection is a type of computer attack that involves the injection of unintended SQL or SQL-like code into a computer database management system.", + "url": "https://owASP.org/www.ReturningReturns/WSenheimans_12.html" + }, + { + "name": "SQL Injection Mitigation", + "description": "SQL Injection can be prevented with proper input validation, canonicalization, and sanitization. Parameterized queries can further protect against SQL Injection.", + "url": "https://information-security.stackexchange.com/questions/5068/how-to-prevent-sql-injection-attacks" + }, + { + "name": "SQL Injection in Python", + "description": "Python's sqlite3 and MySQLdb modules provide support for parameterized queries, making it easier to prevent SQL Injection.", + "url": "https://docs.python.org/3/library/sqlite3.html#using-parameterized-queries" + }, + "https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html", + "https://owasp.org/www-community/attacks/SQL_Injection" + ] + }, + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "type": "Insecure Regex", + "line": 29, + "snippet": "pat = re.compile('.*')", + "message": "Found an overly-broad regex pattern which may lead to excessive backtracking or unintended matches.", + "severity": "Medium", + "explanation": "The given regex pattern '.*' is overly-broad, potentially leading to excessive backtracking and unintended matches. This can cause performance issues or security vulnerabilities if the pattern is used to match input in a dynamic code execution context.", + "fix": "Replace '.*' with a more precise regex pattern. For example, if matching text, use '^[^\\n]*$' to match all non-newline characters or '^[^\\t\\n\\r\\v\\f]*$' for shorter lines. Always anchor your regex and use limit quantifiers to restrict the number of matches.", + "llm_used": "online", + "references": [ + { + "url": "https://owASP.org/textbook/JavaSecurityHardeningTips152.html", + "description": "JP0B: Session Manager - Uncontrolled Session and Request which can be exploited through use of classids and instances." + }, + { + "url": "https:// लगाते.OracleCSHR.com/choose-language/regex.html", + "description": "Basic regular expression in various programming languages with examples in C, C++, and SQL" + }, + { + "url": "https://iðeland.fmi.pl/regex-regions/en/current/", + "description": "Regular Expressions (regex) in various programming languages" + }, + "https://owasp.org/www-community/" + ] + } + ] + }, + "summary": { + "counts": { + "High": 4, + "Medium": 6, + "Low": 0 + }, + "risk": "High", + "total_issues": 10, + "score": 0.76, + "rationale": "4 high-severity issue(s), 6 medium-severity issue(s).", + "top_files": [ + { + "file": "C:\\Users\\HP\\Desktop\\CodeGuardian\\input\\test_insecure.py", + "score": 38, + "issues": 10 + } + ] + } +} \ No newline at end of file diff --git a/Plan.txt b/Plan.txt index abb99e8..54d5771 100644 --- a/Plan.txt +++ b/Plan.txt @@ -196,6 +196,6 @@ It detects and fixes vulnerabilities It’s fully agentic, impactful, and visually appealing. -https://build.nvidia.com/nvidia/llama-3_1-nemotron-nano-8b-v1/deploy +https://build.nvidia.com/nvidia/llama-3_1-nemotron-nano-8b-v1/deploy - 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 -https://build.nvidia.com/nvidia/nv-embedcode-7b-v1?snippet_tab=Shell \ No newline at end of file +https://build.nvidia.com/nvidia/nv-embedcode-7b-v1?snippet_tab=Shell - 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 \ No newline at end of file diff --git a/agent/llm_client.py b/agent/llm_client.py index c52c89d..55ad9d0 100644 --- a/agent/llm_client.py +++ b/agent/llm_client.py @@ -60,17 +60,30 @@ def explain(self, issue: Dict[str, Any], context: Optional[Dict[str, Any]] = Non prompt constructed from the issue and context. The response is parsed into explanation/fix/references. On any failure the offline templates are used. """ + # reset last-explain flag + try: + self._last_explain_used_online = False + except Exception: + pass + if self.mode == "nim" and self.online_available and self.nim is not None: try: - return self._explain_online(issue, context) + out = self._explain_online(issue, context) + # mark that we successfully used online path + self._last_explain_used_online = True + return out except Exception: logger.exception("Online LLM failed; falling back to offline templates") + self._last_explain_used_online = False return self._explain_offline(issue, context) if self.mode == "sagemaker" and self.online_available and self.sagemaker is not None: try: - return self._explain_sagemaker(issue, context) + out = self._explain_sagemaker(issue, context) + self._last_explain_used_online = True + return out except Exception: logger.exception("SageMaker LLM failed; falling back to offline templates") + self._last_explain_used_online = False return self._explain_offline(issue, context) return self._explain_offline(issue, context) diff --git a/agent/nim_client.py b/agent/nim_client.py index c115856..83665a7 100644 --- a/agent/nim_client.py +++ b/agent/nim_client.py @@ -38,27 +38,28 @@ def __init__(self, inference_url: Optional[str] = None, embedding_url: Optional[ self.inference_model = os.environ.get("NIM_INFERENCE_MODEL", "llama-3.1-nemotron-nano-8b-v1") self.embedding_model = os.environ.get("NIM_EMBEDDING_MODEL", "nv-embedcode-7b-v1") - # If explicit embedding_url not provided but base_url+model available, construct a default path + # If explicit embedding_url/inference_url not provided but base_url available, construct sensible defaults if self.base_url: - # If the base looks like the NVIDIA Integrate v1 host, prefer the v1 endpoints base = self.base_url.rstrip('/') - if "integrate.api.nvidia.com" in base or base.endswith('/v1'): - # integrate-style base (we'll construct v1 endpoints) - # ensure we don't duplicate /v1 - v1_base = base.rstrip('/') + # Official NVIDIA Cloud/API uses api.nvidia.com/v1 + if "api.nvidia.com" in base: + # prefer OpenAI-compatible endpoints under /v1 + v1_base = base if not v1_base.endswith('/v1'): v1_base = v1_base + '/v1' - if not self.embedding_url: - # integrate v1 embeddings endpoint - self.embedding_url = f"{v1_base}/embeddings" + # inference: prefer chat/completions (OpenAI-compatible) if not self.inference_url: - # integrate v1 chat/completions endpoint self.inference_url = f"{v1_base}/chat/completions" + # embeddings: many NVIDIA LLM endpoints do not expose a /v1/embeddings path; + # only use explicit NIM_EMBEDDING_URL if provided by the deployment + if not self.embedding_url: + self.embedding_url = os.environ.get('NIM_EMBEDDING_URL') else: - # default model-based patterns + # For self-hosted or model-specific hosts, try model-based patterns if not explicitly set if not self.embedding_url and self.embedding_model: self.embedding_url = f"{base}/models/{self.embedding_model}/embeddings" if not self.inference_url and self.inference_model: + # some deployments expose /models//infer or /infer self.inference_url = f"{base}/models/{self.inference_model}/infer" # support per-model API keys if provided; fall back to NIM_API_KEY self.api_key = api_key or os.environ.get("NIM_API_KEY") @@ -67,6 +68,8 @@ def __init__(self, inference_url: Optional[str] = None, embedding_url: Optional[ if not requests: logger.warning("requests not installed; NIM client disabled") + # convenience: record whether embeddings are expected to be supported + self.supports_embeddings = bool(self.embedding_url) def _headers(self, api_key: Optional[str] = None): h = {"Content-Type": "application/json"} @@ -129,6 +132,52 @@ def explain(self, prompt: str, max_tokens: int = 512, **kwargs) -> str: logger.exception("NIM inference call failed: %s", e) raise + # convenience helpers for diagnostics + def check_health(self) -> Optional[dict]: + """Call the NIM health endpoint if available and return parsed JSON or None.""" + if not requests or not self.base_url: + return None + try: + base = self.base_url.rstrip('/') + v1 = base + if not v1.endswith('/v1'): + v1 = v1 + '/v1' + url = f"{v1}/health/ready" + r = requests.get(url, headers=self._headers(self.api_key), timeout=5) + r.raise_for_status() + try: + return r.json() + except Exception: + return {"status": r.text} + except Exception: + return None + + def list_models(self) -> Optional[dict]: + if not requests or not self.base_url: + return None + try: + base = self.base_url.rstrip('/') + v1 = base + if not v1.endswith('/v1'): + v1 = v1 + '/v1' + url = f"{v1}/models" + r = requests.get(url, headers=self._headers(self.api_key), timeout=5) + r.raise_for_status() + return r.json() + except Exception: + return None + + def chat_completion(self, prompt: str, max_tokens: int = 512) -> Optional[dict]: + if not requests or not self.inference_url: + return None + try: + payload = {"model": self.inference_model, "messages": [{"role": "user", "content": prompt}], "max_tokens": max_tokens} + r = requests.post(self.inference_url, json=payload, headers=self._headers(self.inference_api_key), timeout=15) + r.raise_for_status() + return r.json() + except Exception: + return None + def embed(self, texts: List[str]) -> List[List[float]]: """Call the NIM embedding endpoint to get vectors for a list of texts. diff --git a/agent/reasoning.py b/agent/reasoning.py index 5240921..3f7625d 100644 --- a/agent/reasoning.py +++ b/agent/reasoning.py @@ -10,6 +10,7 @@ import os import logging +import re from typing import Dict, Any, List, Union from .llm_client import LLMClient @@ -19,6 +20,65 @@ logger = logging.getLogger("codeguardian.reasoning") +def _is_valid_url(u: str) -> bool: + if not isinstance(u, str): + return False + u = u.strip() + if not re.match(r'^https?://', u, re.IGNORECASE): + return False + m = re.match(r'^https?://([^/\s]+)', u) + return bool(m and '.' in m.group(1)) + + +def _sanitize_text(s: str) -> str: + if not isinstance(s, str): + return s + # remove non-printable characters + s = ''.join(ch for ch in s if ch.isprintable()) + s = re.sub(r'[ \t]+', ' ', s) + return s.strip() + + +def _normalize_reference(r): + if isinstance(r, str): + rs = _sanitize_text(r) + if _is_valid_url(rs): + return {"url": rs} + return None + if isinstance(r, dict): + url = r.get('url') or r.get('link') or r.get('href') + if url and _is_valid_url(url): + out = {"url": _sanitize_text(url)} + desc = r.get('description') or r.get('name') + if desc: + out['description'] = _sanitize_text(desc) + return out + return None + return None + + +def _sanitize_results(results: Dict[str, List[Dict[str, Any]]]) -> Dict[str, int]: + removed = 0 + normalized = 0 + for fp, issues in results.items(): + for issue in issues: + for key in ('explanation', 'fix', 'message', 'snippet'): + if key in issue: + issue[key] = _sanitize_text(issue[key]) + + refs = issue.get('references') or [] + new_refs = [] + for r in refs: + nr = _normalize_reference(r) + if nr: + new_refs.append(nr) + normalized += 1 + else: + removed += 1 + issue['references'] = new_refs + return {"removed_refs": removed, "normalized_refs": normalized} + + def _map_severity(issue_type: str) -> str: t = (issue_type or "").lower() if "secret" in t or "hardcoded" in t: @@ -79,6 +139,12 @@ def enrich_issue(self, file: str, issue: Dict[str, Any]) -> Dict[str, Any]: enriched["explanation"] = llm_out.get("explanation") enriched["fix"] = llm_out.get("fix") + # record whether the LLM explain call used an online provider or the offline fallback + try: + used_online = bool(getattr(self.llm, "_last_explain_used_online", False)) + except Exception: + used_online = False + enriched["llm_used"] = "online" if used_online else "offline" # prefer LLN-provided references but merge KB refs refs = list(llm_out.get("references", [])) if isinstance(kb_entry, dict): diff --git a/data/sessions.db b/data/sessions.db index 8576b4ea6439ab34e2f0a3ba71519b440170f367..d64c0443e8c952a15f3ffe58c8ba14ac63b6de98 100644 GIT binary patch delta 1080 zcma))&r4N79L2qwY5MNH;zLu^65NC}kNNSPUyl$XlZafIn?P{p&P4ldA-}`^}uO;qqTO>F3wq^#` zpRKK0%za>%BaoCJ@EDLXg+zd*cACpVfo}bpn{U0I-d`aIlLv$}nQj6!fMTJLMeFC>+!?rzn^eFFtKcTuaG*4!ywIqy(jZX8BV7oBjFLF8f;;PQ1=qP1$J}I- z@CNXB7_H#yZ8)w77TN?vwT9S)v&eW~Aa1QviU$kQoQ>|E9m5i2MK|82^92C_B9I6! zw##{>qcY}aKr2SI146rWFP2%Xl{Z=ZL<7s1+uvHpx@^{ZS$L#^@UaIK%{UD)J;DN3 z7~;8Iww0+fX1pw_Lw0n0`Tp=?y>{A{KTqGJpR{0DBAI;yV+xfTb zL;52-$l-ANYi;M+z') + else: + print(f"{key}={val}") + else: + print(key + '=') diff --git a/scripts/check_providers.py b/scripts/check_providers.py new file mode 100644 index 0000000..b3fd13d --- /dev/null +++ b/scripts/check_providers.py @@ -0,0 +1,102 @@ +"""Check configured LLM/embedding providers (NIM or SageMaker) and run simple probes. + +This script prints diagnostics and attempts one explain() and one embed() call when possible. +Run with the repo root on PYTHONPATH, e.g.: + +PowerShell: +$env:PYTHONPATH='.'; python scripts/check_providers.py + +Set provider env vars beforehand (NIM or SageMaker) as needed. +""" +from __future__ import annotations + +import os +import json +import sys +from typing import Any + + +def _print(title: str, v: Any = None): + print(f"--- {title} ---") + if v is None: + return + if isinstance(v, (dict, list)): + print(json.dumps(v, indent=2)) + else: + print(v) + + +def main(): + # show env vars of interest + keys = [ + "CODEGUARDIAN_LLM_MODE", + "NIM_INFERENCE_URL", + "NIM_EMBEDDING_URL", + "NIM_BASE_URL", + "NIM_API_KEY", + "NIM_INFERENCE_MODEL", + "NIM_EMBEDDING_MODEL", + "SAGEMAKER_LLM_ENDPOINT", + "SAGEMAKER_EMBEDDING_ENDPOINT", + "AWS_REGION", + ] + + env = {k: os.environ.get(k) for k in keys} + _print("Environment (relevant)", env) + + # Import local clients + try: + from agent.llm_client import LLMClient + except Exception as e: + print("Failed to import LLMClient:", e) + sys.exit(2) + + # instantiate client with configured mode + mode = os.environ.get("CODEGUARDIAN_LLM_MODE", "offline") + client = LLMClient(mode=mode) + _print("LLMClient.mode", client.mode) + _print("LLMClient.online_available", client.online_available) + _print("NIM client instance", type(getattr(client, "nim", None)).__name__) + _print("SageMaker client instance", type(getattr(client, "sagemaker", None)).__name__) + + # Try a small explain probe if online is available + if client.mode == "nim" and client.nim is not None and client.online_available: + print("Attempting NIM explain probe...") + try: + out = client.nim.explain("Ping from CodeGuardian probe: explain this in one line.") + _print("NIM explain output", out) + except Exception as e: + _print("NIM explain failed", str(e)) + + print("Attempting NIM embed probe...") + try: + em = client.nim.embed(["test embedding"[:512]]) + _print("NIM embed output (len)", len(em)) + if em: + _print("NIM embed vector length", len(em[0])) + except Exception as e: + _print("NIM embed failed", str(e)) + + elif client.mode == "sagemaker" and client.sagemaker is not None and client.online_available: + print("Attempting SageMaker explain probe...") + try: + out = client.sagemaker.explain("Ping from CodeGuardian probe: explain this in one line.") + _print("SageMaker explain output", out) + except Exception as e: + _print("SageMaker explain failed", str(e)) + + print("Attempting SageMaker embed probe...") + try: + em = client.sagemaker.embed(["test embedding"]) + _print("SageMaker embed output (len)", len(em)) + if em: + _print("SageMaker embed vector length", len(em[0])) + except Exception as e: + _print("SageMaker embed failed", str(e)) + + else: + print("Online LLM not configured/available. The client will use offline templates.") + + +if __name__ == "__main__": + main() diff --git a/scripts/clean_stage3.py b/scripts/clean_stage3.py new file mode 100644 index 0000000..294a520 --- /dev/null +++ b/scripts/clean_stage3.py @@ -0,0 +1,97 @@ +import json +import re +import sys +from pathlib import Path + +def is_valid_url(u: str) -> bool: + # Basic URL validation using regex and parse + if not isinstance(u, str): + return False + u = u.strip() + # must start with http or https + if not re.match(r'^https?://', u, re.IGNORECASE): + return False + # simple no-spaces check + if '\\s' in u: + return False + # domain check + m = re.match(r'^https?://([^/\s]+)', u) + return bool(m and '.' in m.group(1)) + +def sanitize_text(s: str) -> str: + if not isinstance(s, str): + return s + # remove control chars except common whitespace + s = ''.join(ch for ch in s if ch.isprintable()) + # collapse multiple spaces + s = re.sub(r'[ \t]+', ' ', s) + # strip leading/trailing whitespace + return s.strip() + +def normalize_reference(ref): + # Return (normalized_obj or None) + if isinstance(ref, str): + ref_s = sanitize_text(ref) + if is_valid_url(ref_s): + return {'url': ref_s} + return None + if isinstance(ref, dict): + url = ref.get('url') or ref.get('link') or ref.get('href') + if url and is_valid_url(url): + out = {'url': sanitize_text(url)} + # capture a short description if present + desc = ref.get('description') or ref.get('name') + if desc: + out['description'] = sanitize_text(desc) + return out + return None + return None + +def clean_stage3(in_path: Path, out_path: Path): + doc = json.loads(in_path.read_text(encoding='utf-8')) + removed_refs = 0 + normalized = 0 + + results = doc.get('results', {}) + for file, issues in results.items(): + for issue in issues: + # sanitize strings in selected fields + for key in ('explanation','fix','message','snippet'): + if key in issue: + issue[key] = sanitize_text(issue[key]) + + # normalize references + refs = issue.get('references') or [] + new_refs = [] + for r in refs: + nr = normalize_reference(r) + if nr: + new_refs.append(nr) + if isinstance(r, str): + normalized += 1 + else: + # if dict and cleaned/described + normalized += 1 + else: + removed_refs += 1 + issue['references'] = new_refs + + # write cleaned file + out_path.write_text(json.dumps(doc, indent=2, ensure_ascii=False), encoding='utf-8') + return {'removed_refs': removed_refs, 'normalized_refs': normalized} + +def main(): + if len(sys.argv) < 2: + print('usage: python scripts/clean_stage3.py [output.json]') + raise SystemExit(1) + in_path = Path(sys.argv[1]) + if not in_path.exists(): + print('input not found:', in_path) + raise SystemExit(1) + out_path = Path(sys.argv[2]) if len(sys.argv) > 2 else in_path.with_name(in_path.stem + '.cleaned.json') + stats = clean_stage3(in_path, out_path) + print('wrote:', out_path) + print('stats:', stats) + +if __name__ == '__main__': + main() diff --git a/scripts/probe_nim_embeddings.py b/scripts/probe_nim_embeddings.py new file mode 100644 index 0000000..bdddec6 --- /dev/null +++ b/scripts/probe_nim_embeddings.py @@ -0,0 +1,50 @@ +import os +from pathlib import Path +try: + from dotenv import load_dotenv + load_dotenv(Path(__file__).parent.parent / '.env') +except Exception: + pass + +if 'BASE_URL' in os.environ and 'NIM_BASE_URL' not in os.environ: + os.environ['NIM_BASE_URL'] = os.environ['BASE_URL'] + +embedding_url = os.environ.get('NIM_EMBEDDING_URL') +base_env = os.environ.get('NIM_BASE_URL') +if not embedding_url and base_env: + base = base_env.rstrip('/') + if not base.endswith('/v1'): + base = base + '/v1' + embedding_url = base + '/embeddings' + +if not embedding_url: + print('no embeddings URL configured') + raise SystemExit(1) + +api_key = os.environ.get('NIM_API_KEY_EMBEDDING') or os.environ.get('NIM_API_KEY') +print('probing embeddings:', embedding_url) + +try: + import requests +except Exception: + print('requests missing') + raise + +headers = {'Content-Type': 'application/json'} +if api_key: + headers['Authorization'] = f'Bearer {api_key}' + +payload = { + 'model': os.environ.get('NIM_EMBEDDING_MODEL','nvidia/nv-embedcode-7b-v1'), + 'input': ['test embedding'], + 'input_type': 'query' +} + +try: + r = requests.post(embedding_url, json=payload, headers=headers, timeout=15) + print('status_code=', r.status_code) + text = r.text + print('response_snippet=', text[:800]) +except Exception as e: + print('error calling embeddings endpoint:', e) + raise diff --git a/scripts/probe_nim_endpoint.py b/scripts/probe_nim_endpoint.py new file mode 100644 index 0000000..0214936 --- /dev/null +++ b/scripts/probe_nim_endpoint.py @@ -0,0 +1,41 @@ +import os +import json +from pathlib import Path +try: + from dotenv import load_dotenv + load_dotenv(Path(__file__).parent.parent / '.env') +except Exception: + pass + +# map BASE_URL if present +if 'BASE_URL' in os.environ and 'NIM_BASE_URL' not in os.environ: + os.environ['NIM_BASE_URL'] = os.environ['BASE_URL'] + +inference = os.environ.get('NIM_INFERENCE_URL') +base_env = os.environ.get('NIM_BASE_URL') +if not inference and base_env: + base = base_env.rstrip('/') + if not base.endswith('/v1'): + base = base + '/v1' + inference = base + '/chat/completions' +api_key = os.environ.get('NIM_API_KEY_INFERENCE') or os.environ.get('NIM_API_KEY') +print('probing:', inference) +if not inference: + print('no inference URL configured') + raise SystemExit(1) + +try: + import requests +except Exception: + print('requests missing') + raise + +headers = {'Content-Type':'application/json'} +if api_key: + headers['Authorization'] = f'Bearer {api_key}' + +payload = {'model': os.environ.get('NIM_INFERENCE_MODEL','llama-3.1-nemotron-nano-8b-v1'), 'messages':[{'role':'user','content':'test'}], 'max_tokens':64} + +r = requests.post(inference, json=payload, headers=headers, timeout=10) +print('status_code=', r.status_code) +print('response_snippet=', r.text[:800]) diff --git a/scripts/run_stage3_demo.py b/scripts/run_stage3_demo.py new file mode 100644 index 0000000..234be83 --- /dev/null +++ b/scripts/run_stage3_demo.py @@ -0,0 +1,79 @@ +import os +import json +from pathlib import Path + +from agent import parser +from agent.reasoning import Reasoner +try: + # load .env if present to make it easy to run the demo with local keys + from dotenv import load_dotenv + load_dotenv(Path(__file__).parent.parent / '.env') +except Exception: + # python-dotenv not installed or .env missing -- that's fine + pass + + +def main(): + # Force nim mode to use test-friendly fake client when available + os.environ["CODEGUARDIAN_LLM_MODE"] = "nim" + + sample = Path(__file__).parent.parent / "input" / "test_insecure.py" + if not sample.exists(): + print("sample input missing:", sample) + return + + issues = parser.analyze_code(str(sample)) + print(f"Stage2 found {len(issues)} issues") + + r = Reasoner(llm_mode="nim") + # print LLM client status + try: + print(f"LLM client mode={r.llm.mode} online_available={r.llm.online_available}") + except Exception: + pass + + out = r.enrich({str(sample): issues}) + + # Ensure Output directory exists at repo root + output_dir = Path(__file__).parent.parent / "Output" + output_dir.mkdir(parents=True, exist_ok=True) + + # Save enriched Stage3 output with timestamped filename + from datetime import datetime + + ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + out_file = output_dir / f"stage3-{ts}.json" + with out_file.open("w", encoding="utf-8") as fh: + json.dump(out, fh, indent=2, ensure_ascii=False) + + print(f"Saved Stage3 output to: {out_file}") + # Also save a cleaned copy for easy consumption (suffix .cleaned.json) + cleaned_file = output_dir / f"stage3-{ts}.cleaned.json" + try: + with cleaned_file.open("w", encoding="utf-8") as fh: + json.dump(out, fh, indent=2, ensure_ascii=False) + print(f"Saved cleaned Stage3 output to: {cleaned_file}") + except Exception: + # if writing cleaned fails, continue silently + pass + # Also print a short summary to stdout + try: + summary = out.get("summary") or {} + total = summary.get("total_issues", sum(len(v) for v in out.get("files", {}).values()) if out.get("files") else 0) + print(f"Summary: total_issues={total} summary_keys={list(summary.keys())}") + except Exception: + pass + # report how many issues used online vs offline explain + try: + counts = {"online": 0, "offline": 0} + for fp, issues in out.get("results", {}).items(): + for it in issues: + used = it.get("llm_used", "offline") + counts[used] = counts.get(used, 0) + 1 + print(f"LLM usage: online={counts['online']} offline={counts['offline']}") + except Exception: + pass + + +if __name__ == "__main__": + main() From 5be123067e582453522399353978bd3d9008f449 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Nov 2025 21:53:52 +0530 Subject: [PATCH 5/5] ci: add aws-integration workflow to run Stage 4 tests on push/PR --- .github/workflows/aws-integration.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/aws-integration.yml diff --git a/.github/workflows/aws-integration.yml b/.github/workflows/aws-integration.yml new file mode 100644 index 0000000..023995e --- /dev/null +++ b/.github/workflows/aws-integration.yml @@ -0,0 +1,26 @@ +name: Stage 4 — AWS (mock) integration + +on: + push: + branches: [ main, 'stage4/**', 'stage*' ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests + run: pytest -q