Reframes verdict/score language across templates, JS renderers, MCP and
GrumpyCats client docs so findings read as feedback on the operator's own
payload rather than threat judgments. Risk Score becomes Detection Score,
Risk Factors becomes Triggering Indicators, Suspicious Imports becomes
Sensitive Imports, CheckPlz "Threat detected" becomes "Signature triggered",
HolyGrail "Dangerous Imports" becomes "Critical Imports". Dynamic-analysis
warning modal on the static results page now accepts command-line arguments
(pre-populated from last run) and persists them via localStorage, matching
the upload-page flow.
GrumpyCats:
- grumpycat.py: replace the 130-line if/elif chain in main() with a
COMMAND_HANDLERS dispatch table (each subcommand is now a small
_cmd_* function), parallelize get_comprehensive_results across a
ThreadPoolExecutor (4 reads in ~one round-trip instead of four),
add get_risk_assessment for the new /api/results/<target>/risk
endpoint, drop the unused _file_cache, dead imports
(hashlib, Tuple, Any), and the unreliable __del__.
- LitterBoxMCP.py: full rewrite onto modern FastMCP. Fixes broken
import (was pointing at optimized_litterbox_client), replaces the
removed mcp.serve(host=..., port=...) API with mcp.run(transport=...),
routes logs to stderr (required for stdio transport), drops the
handle_api_operation try/except envelope (FastMCP converts
exceptions to MCP errors automatically), drops the
LitterBoxMCPClient indirection, makes every tool async with
asyncio.to_thread, adds Annotated[..., Field(description=...)]
parameter docs, drops shutdown_client (server lifecycle isn't an
LLM concern), defaults bind to 127.0.0.1, and trims the 5
150-200-line OPSEC prompts to 4 focused 15-25-line ones.
- install_mcp.py: new installer modeled on ida-pro-mcp's approach.
Auto-detects the project venv Python, supports six clients
(claude-code project + global, claude-desktop, cursor, windsurf,
vscode-project), idempotent JSON merge that preserves existing
MCP servers, atomic .tmp+rename writes, dependency check that
warns if mcp/requests are missing. --list / --install / --uninstall
/ --print modes.
- README.md: rewritten to match — three-component framing, accurate
dependency list (mcp + requests, not the fictional fastmcp
package), full installer reference, current 22 MCP tools and 4
prompts.
Upstream issues + small fixes:
- app/static/js/upload/core.js: wire up the missing
macroDetectionNotes element (upstream issue: scanning .xls files
threw "can't access property 'innerHTML', elements.macroDetectionNotes
is undefined").
- app/templates/report.html: Stringnalyzer section now expands every
non-empty bucket (URLs, IPs, domains, file paths, etc.) into a full
code block instead of showing a truncated 3-item, 140-char sample.
Categories expanded from 7 to 16, capped at 100 items per category
with "and N more" overflow.
- app/blueprints/api.py: /api/results/<target>/risk endpoint
returning {risk_score, risk_level, risk_factors} (upstream PR).
- app/templates/dynamic_info.html: Process Telemetry summary panel
no longer mixes a flush chip-row with an inset 3-column kvgrid;
uses the lb-hash-row label/value pattern instead and surfaces
image_path + commandline when RedEdr provides them.
When the analysed sample is a `.sys` driver and HolyGrail has been run,
the downloaded report now leads with a HolyGrail BYOVD section instead
of burying it after the dynamic-scan stuff that doesn't apply to
drivers.
- helpers._load_file_data also loads byovd_results.json (when present)
and threads it through `data['byovd_results']` into the report.
- generate_html_report computes a HolyGrail score Python-side (port of
holygrail/core.js's calculateScore) and exposes is_driver_report,
byovd_score, byovd_label, byovd_class, av_killer_imports to the
template.
- For driver reports the hero "Risk Assessment" card swaps to "BYOVD
Potential" with the HolyGrail score and pill, and the chip row
swaps the YARA/PE-Sieve/Moneta/Patriot/HSB counts (all zero on
drivers — they don't run dynamic scans) for LOLDrivers / Win10 /
Win11 / Critical Imports.
- The HolyGrail BYOVD section moves above File Information / Risk
Factors so it's the first thing a defender sees in the report.
- Non-driver reports are unchanged.
Shell:
- Brand (logo + name) moves from sidebar header into the titlebar so the
top-left reads `[logo] LitterBox | <breadcrumb>` instead of having two
separate brand strips.
- The bottom IDE-style status bar is removed; it duplicated the
sidebar-foot status indicator. The sidebar foot now also shows the
version on the right (`[dot] Active v5.0.0`).
- Titlebar height bumped 36 → 44px and the brand mark to 28px so the
logo has breathing room and balances the wordmark.
- file_info header: Back / Static Analysis / Dynamic Analysis buttons
consolidated into the panel header on the right. Dynamic gets a
yellow border to flag that it executes the payload.
Bug fix:
- /results/<hash>/static was rendering YARA as "Suspicious" with a
matching count even when the static scan returned zero matches. Cause:
RouteHelpers.get_detection_counts(data) prefers `dynamic_results` if
any dynamic scan exists, so the static page was rendering its row
count from the dynamic scope while the row's match list came from the
static scope. render_analysis_info now extracts counts from the actual
analysis_results being rendered.
Misc:
- Drop unused .lb-tb-right, .lb-tb-version, .lb-sidebar-brand, and all
.lb-statusbar* rules from style.css; remove dead statusbar_left block
override in error.html.
- Bump app version to 5.0.0 in Config/config.yaml.
Full visual redesign onto a dense terminal/IDE layout: breadcrumb titlebar,
iconed sidebar, optional tab row, IDE-style status bar, JetBrains Mono
throughout, semantic severity palette, and a calm-red rule that reserves
bright red for severity tags, destructive buttons, and the brand dot.
Splits the 1,598-line tools.js into a 66-line registry plus one module per
scanner under app/static/js/results/tools/ (yara, checkplz, stringnalyzer,
pe_sieve, moneta, patriot, hsb, rededr, summary), with shared rendering
helpers in tools/_shared.js. Each module exports
{id, elementId, statsElementId?, render(results, ctx)}, and the registry
does lazy DOM lookups so a module loaded on a page that doesn't have its
tab silently no-ops.
Switches Tailwind from the precompiled v2.2.19 file (~2.8MB) to a v4 build
generated locally by the standalone CLI binary (~284KB). End-user setup is
unchanged: the committed tailwind.min.css ships ready to use; only the
maintainer needs the binary, which lives outside the repo.
Fixes:
- ModalHandler crash on dynamic results pages (null-deref against the old
.bg-gray-900 selector)
- AnalysisCore.updateStageToComplete null-deref against removed
stage-indicator markup
- summary.render silently skipped because elementId pointed at the
removed summaryWrapper div — points at scannerResultsBody now
- Per-tool render failures no longer suppress the rest of the rendering
- Drag-and-drop highlight no longer null-derefs against the removed
.upload-icon selector
- Upload "Unsupported file type" false positive — extensions now sourced
from window.serverConfig instead of DOM scraping
- XSS hardening at user-data interpolation sites in results renderers
Visual unification:
- New :root design tokens + .lb-* component classes in style.css
(cards, buttons, badges, section headers, hash display, empty
state, animated grid backdrop, critical-state pulse).
- holygrail.html and byovd_info.html lose their cyber-themed inline
<style> blocks (567 + 164 lines) and the cyber-card / cyber-chip /
cyber-button / verdict-* classes; both pages now draw from the
shared lb-* vocabulary. The cyber-glow accent is preserved but
applied only on critical/severe states instead of page-wide.
- Templates swept to use component classes: file_info, summary,
results, dynamic_info, static_info, doppelganger, error, upload,
partials/_macros. JS renderers in results/tools.js,
results/renderers.js, and the holygrail/byovd/upload core.js
modules updated to emit the new classes. Risk-level 4-way
conditional in file_info.html collapsed to a single
lb-badge-{{ risk_level|lower }} lookup.
- Bug fix: duplicate .logo-wrapper rule in style.css merged.
Self-contained report (report.html rewritten):
- Drops the https://cdn.tailwindcss.com runtime-JIT dependency and
the inline tailwind.config script. No <script> tags anywhere.
- All CSS inlined in a single <style> block: design tokens, the
subset of lb-* component classes the report uses, and only the
layout rules the report itself needs.
- Hand-written typography and layout: tabular-numeric risk scores,
restrained pill badges, severity-coded detection chips,
generous whitespace, dedicated @media print.
- Status icons use locked SVG dimensions so the green checkmark on
clean scans no longer renders at default-huge size.
- The LitterBox logo is embedded as a base64 PNG data URI so the
brand strip displays correctly when the file is downloaded and
opened offline. Logo sized at 64x64 in the brand strip.
CHANGELOG.md: v4.2.0 entry extended with "UI design system &
visual unification" and "Fully self-contained downloadable report"
sections.
No backend or API changes. Setup story unchanged. No new
dependencies; deploy stays Python-only with the precompiled
tailwind.min.css.
Backend (Python):
- Split app/routes.py (1,389 lines) into 6 Flask blueprints (upload, analysis,
results, doppelganger, management, api) under app/blueprints/, plus
service modules (rendering, summary, tool_check, error_handling) under
app/services/, and the shared RouteHelpers class in app/helpers.py.
app/__init__.py wires shared deps via app.extensions['litterbox'].
- Split app/utils.py (1,400 lines) into the app/utils/ package with
single-concern modules: file_io, validators, path_manager, risk_analyzer,
forensics, json_helpers, reporting. No facade — every caller migrated.
- Extracted BaseSubprocessAnalyzer in app/analyzers/base.py; refactored 9
subprocess analyzers (yara/checkplz/stringnalyzer static; yara/pe_sieve/
moneta/patriot/hsb/hollows_hunter dynamic) as thin subclasses that only
declare config + implement _parse_output.
Frontend (JS):
- Split results.js (2,060), holygrail.js (1,025), byovd_info.js (1,069),
and upload.js (974) into per-concern ES6 modules under
app/static/js/{results,holygrail,byovd,upload}/.
- Added app/static/js/utils/ with shared helpers: escape, formatters,
severity, fetch, modals, dom (single source of truth for escapeHtml,
formatBytes, severity-color mapping, etc.).
- Converted base.js, summary.js, blender.js, fuzzy.js to ES6 modules;
every <script> tag now uses type="module". window.X assignments preserved
so inline onclick handlers in templates keep resolving.
- Targeted XSS hardening at user-data interpolation sites in results
renderers (str.data, hex_dump, scan_info.target, list items).
Templates:
- New app/templates/partials/_macros.html with reusable scanner-table
macros + 3-card status grid; static_info.html and dynamic_info.html
migrated to use them, eliminating identical-HTML duplication.
CSS:
- Fixed broken @apply in .drag-over (no Tailwind build pipeline → @apply
was silently ignored, leaving drag-and-drop visual feedback broken).
Replaced with raw CSS equivalent.
- Dedented stray 8-space-indented block (lines 127-end) for consistency.
- Added header comment documenting the no-build-pipeline constraint.
Gitignore:
- Anchored Results/, Uploads/, DoppelgangerDB/Blender/, and Scanners/*
patterns to repo root with leading slash so they don't shadow same-
named directories elsewhere (notably the new app/static/js/results/
module directory and app/blueprints/results.py).
- Added /Scanners/PE-Sieve/process_*/ for runtime scan artifacts.