Skip to content
BlackSnufkin edited this page May 4, 2026 · 2 revisions

HTTP API Reference

Complete catalogue of LitterBox's HTTP endpoints. Pages render Jinja templates; /api/* routes return JSON. The frontend uses both β€” page routes for the SSR shell and /api/* for live polling.

LitterBox is not authenticated. It's designed to run on a private analyst network. Don't expose port 1337 to the internet.


Pages (HTML)

Method Path Purpose
GET / Dashboard β€” sandbox health, scanner inventory, EDR fleet, recent samples
GET /upload Upload drop-zone (file + EDR profile picker)
GET /results/<analysis_type>/<target> Saved-view for a sample (analysis_type = static / dynamic / info / holygrail)
GET /results/edr/<profile>/<target> Saved-view for one EDR profile run
GET /analyze/<analysis_type>/<target> Live runner β€” POSTs from JS to itself to start, then polls
GET /analyze/edr/<profile>/<target> Live EDR runner
GET /analyze/all/<target> All-in-one fan-out β€” Static + every reachable EDR in parallel
GET /whiskers EDR fleet detail page
GET /doppelganger?type=blender|fuzzy[&hash=...] Doppelganger comparison page
GET /holygrail BYOVD driver upload + analysis page
GET /summary Cross-sample summary index
GET /files All-samples summary (also a JSON endpoint β€” see below)
GET /report/<target> Redirects to /api/report/<target>?download=true

Intake

Method Path Body Returns
POST /upload multipart file {message, file_info: {md5, size, original_name, ...}}
POST /holygrail multipart file (.sys) {message, file_info, next_step}
POST /validate/<pid> β€” 200 {status: valid} / 404 {error}
DELETE /file/<target> β€” {status, message, details: {upload, result, analysis}} β€” removes upload file, results folder, and PE-Sieve analysis sub-folders

Analysis dispatch

Method Path Body Returns
POST /analyze/static/<hash> β€” {status: 'success', results: {...}} (sync, ~1–10s)
POST /analyze/dynamic/<hash-or-pid> optional {args: [str, ...]} {status: 'success', results: {...}} (sync, longer β€” runs payload)
POST /analyze/edr/<profile>/<hash> optional {args, xor_key} Phase-1 dict; Phase-2 runs in a server-side daemon thread (poll the GET)
GET /holygrail?hash=<hash> β€” BYOVD analysis result for a previously-uploaded driver

Status codes for analysis:

  • 200 β€” completed (or Phase-1 settled in the EDR case)
  • 202 β€” early termination (process exited during init, dynamic only)
  • 409 β€” busy (EDR agent lock held by another run)
  • 500 β€” analyzer error
  • 502 β€” agent unreachable / agent error (EDR only)

Result retrieval (JSON)

Method Path Returns
GET /api/results/info/<target> file_info.json (hashes, entropy, PE info)
GET /api/results/static/<target> static_analysis_results.json
GET /api/results/dynamic/<target> dynamic_analysis_results.json
GET /api/results/holygrail/<target> byovd_results.json
GET /api/results/edr/<target> {profiles: [...]} β€” index of saved EDR runs for the target
GET /api/results/edr/<profile>/<target> edr_<profile>_results.json
GET /api/results/risk/<target> {risk_score, risk_level, risk_factors: [...]} β€” recomputed each call
GET /api/report/<target>[?download=true] Full HTML report β€” inline, or as Content-Disposition: attachment

EDR fleet

Method Path Returns
GET /api/edr/profiles {profiles: [{name, display_name, kind, agent_url, ...}]} β€” secrets stripped
GET /api/edr/agents/status[?refresh=1] Cached reachability snapshot for every profile (Whiskers + backend); ?refresh=1 forces a synchronous re-probe
GET /api/edr/fibratus/<profile>/alerts/since?from=<iso>[&until=<iso>] Test passthrough β€” query Whiskers's event-log endpoint without dispatching a payload (validates the Fibratus β†’ eventlog β†’ Whiskers wire)

System

Method Path Returns
GET /health Unified sandbox status β€” {status, issues, sandbox: {...}, scanners: {rows, counts}, edr_agents: {agents}}. One fetch covers boot validation + scanner inventory + EDR fleet reachability
GET /files {driver_based, payload_based, pid_based} β€” full sample inventory
POST /cleanup Clears Uploads/, Results/, doppelganger DB, PE-Sieve analysis folders, HolyGrail results

/api/edr/agents/status is still available for callers that only need the focused EDR-fleet payload (e.g. the all-in-one preflight, the /whiskers agents page) β€” it shares the same TTL-cached probe as /health's edr_agents block. There's no separate /api/system/scanners route any more β€” read scanners.rows from /health.


Doppelganger

Method Path Body Returns
GET /doppelganger?type=blender β€” Page with latest blender scan
GET /doppelganger?type=fuzzy β€” Page with fuzzy-DB stats
GET /doppelganger?type=<t>&hash=<h> β€” Comparison result
POST /doppelganger {type: blender, operation: scan} {processes: [...]} β€” host snapshot
POST /doppelganger {type: fuzzy, operation: create_db, folder_path, extensions?} {stats}
POST /doppelganger {type: fuzzy, operation: analyze, hash, threshold?} {results}

EDR result statuses

/api/results/edr/<profile>/<target> returns one of:

status Meaning
polling_alerts Phase 1 succeeded; Phase 2 still in flight
completed Phase 2 done with at least zero alerts collected
blocked_by_av AV intercepted at write/spawn β€” run gated on AV-block path
partial Phase 1 ok but backend alert query failed (sub_status: elastic_unreachable / elastic_error)
agent_unreachable Phase 1 transport failure to Whiskers (not persisted to disk)
busy Whiskers lock held (not persisted to disk)
error Phase 1 generic failure (not persisted to disk)

agent_unreachable, busy, and pre-execution error are intentionally not saved to JSON β€” they're transient transport failures, not real findings. The frontend treats them as ephemeral and the saved-view route correctly 404s.


Polling cadence

For long-running EDR dispatches the frontend uses an adaptive poll:

POLL_INTERVAL_MS  = 2000        # base
POLL_MAX_MS       = 15000       # ceiling
POLL_BACKOFF      = 1.5         # multiplier on each tick where alerts didn't change

Movement (total_alerts increased, or status changed) snaps the interval back to 2s. document.visibilityState === 'hidden' pauses the loop entirely.


CORS / auth

There is no auth and CORS is permissive (Access-Control-Allow-Origin: * for /api/*). Run on a private network only.


See also

πŸ“Œ LitterBox Β· self-hosted payload analysis sandbox

Release


πŸš€ Getting Started

πŸ“Š Pipelines & Pages

πŸ”¬ Scanners Β· 4 modules

πŸ›°οΈ EDR Integration
πŸ”Œ API & Clients
βš™οΈ Configuration & Dev

Releases Β· CHANGELOG Β· Issues Β· README

Clone this wiki locally