From 49bdb9aa8ba6978abf2ff9e27d1496eae6b9daa1 Mon Sep 17 00:00:00 2001 From: Sushant Gautam Date: Tue, 17 Feb 2026 10:34:00 +0000 Subject: [PATCH 1/5] Update LICENSE link in README for direct access to license details --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aed89d5..eed0898 100644 --- a/README.md +++ b/README.md @@ -456,4 +456,4 @@ Tor-StΓ₯le Hansen (Specialist Director, Ministry of Defense Norway) ## License -MIT License - see [LICENSE](LICENSE) for details. +MIT License - see [LICENSE](https://github.com/kelkalot/simpleaudit/blob/main/LICENSE) for details. From ef6354c6f0a679d021daf1192824867ed09f5ba0 Mon Sep 17 00:00:00 2001 From: Sushant Gautam Date: Tue, 24 Feb 2026 16:43:53 +0000 Subject: [PATCH 2/5] Implement authentication for SimpleAudit visualizer and update README --- simpleaudit/visualization/README.md | 10 ++ .../visualization/scenario_viewer.html | 2 +- simpleaudit/visualization/server.py | 45 ++++++- simpleaudit/visualization/visualizer.html | 115 ++++++++++++++++-- 4 files changed, 158 insertions(+), 14 deletions(-) diff --git a/simpleaudit/visualization/README.md b/simpleaudit/visualization/README.md index 680be76..867d63f 100644 --- a/simpleaudit/visualization/README.md +++ b/simpleaudit/visualization/README.md @@ -1,6 +1,16 @@ # SimpleAudit Visualization Visual tools for reviewing SimpleAudit results with an interactive web interface. +## πŸ” Authentication + +To require a secret key for the CLI server, set the `SIMPLEAUDIT_VISUALIZER_SECRET` environment variable: + +```bash +export SIMPLEAUDIT_VISUALIZER_SECRET="mysecret" +simpleaudit serve --results_dir ./my_audit_results +``` + When set, the web UI prompts for the key on first load. + ## πŸ“Š Two Ways to Visualize Results diff --git a/simpleaudit/visualization/scenario_viewer.html b/simpleaudit/visualization/scenario_viewer.html index 0777446..5ec2241 100644 --- a/simpleaudit/visualization/scenario_viewer.html +++ b/simpleaudit/visualization/scenario_viewer.html @@ -68,7 +68,7 @@

- SimpleAudit Scenario Viewer + SimpleAudit Scenario Viewer

Upload a JSON file to view audit results

diff --git a/simpleaudit/visualization/server.py b/simpleaudit/visualization/server.py index f8ec122..210fe18 100644 --- a/simpleaudit/visualization/server.py +++ b/simpleaudit/visualization/server.py @@ -13,6 +13,9 @@ app = FastAPI(title="SimpleAudit Visualizer") +# read secret from environment variable; if blank, authentication is disabled +SECRET = os.getenv("SIMPLEAUDIT_VISUALIZER_SECRET", "") + # Global variable to store results directory RESULTS_DIR = None @@ -103,7 +106,7 @@ async def root(): with open(html_path, "r", encoding="utf-8") as f: content = f.read() - + return HTMLResponse(content=content) @@ -135,7 +138,36 @@ async def favicon(): return FileResponse(favicon_path, media_type="image/png") -@app.get("/api/files") + +# --- authentication helpers ------------------------------------------------ +from fastapi import Request, Depends, status + +def check_secret(request: Request): + """Raise HTTP 401 if a secret is configured and the request does not + provide the correct value in an X-Secret header. When no secret is + configured the check is a no-op. + """ + if not SECRET: + return + token = request.headers.get("X-Secret") + if token != SECRET: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized") + +@app.get("/api/auth") +async def auth_check(request: Request): + """Endpoint used by the frontend to verify a key and learn if auth + is enabled. When the server has no secret configured it still + returns 200 but sets ``enabled`` to False. + """ + try: + check_secret(request) + except HTTPException as exc: + # propagate unauthorized status + raise + + return JSONResponse(content={"ok": True, "enabled": bool(SECRET)}) + +@app.get("/api/files", dependencies=[Depends(check_secret)]) async def get_files(): """Get the file tree of JSON files in the results directory.""" if not RESULTS_DIR: @@ -149,7 +181,7 @@ async def get_files(): return JSONResponse(content={"tree": tree}) -@app.get("/api/json/{file_path:path}") +@app.get("/api/json/{file_path:path}", dependencies=[Depends(check_secret)]) async def get_json_file(file_path: str): """Get the contents of a specific JSON file.""" if not RESULTS_DIR: @@ -186,8 +218,11 @@ def start_server(results_dir: str, host: str = "127.0.0.1", port: int = 8000): host: Host to bind to port: Port to run on """ - global RESULTS_DIR - + global RESULTS_DIR, SECRET + # make sure we pick up the environment variable in case it was + # changed after the module was imported (e.g. during testing) + SECRET = os.getenv("SIMPLEAUDIT_VISUALIZER_SECRET", "") + # Resolve to absolute path from current working directory RESULTS_DIR = os.path.abspath(os.path.join(os.getcwd(), results_dir)) diff --git a/simpleaudit/visualization/visualizer.html b/simpleaudit/visualization/visualizer.html index b4e91a0..82597f3 100644 --- a/simpleaudit/visualization/visualizer.html +++ b/simpleaudit/visualization/visualizer.html @@ -23,6 +23,78 @@ } } +