Security:
- getRepoTree now rejects non-empty subpaths that fail isSafeSubPath,
closing a path-disclosure that exposed _darcs/ via the tree UI.
- Tree/blob breadcrumbs HTML-escape labels and percent-encode hrefs
so a directory name containing HTML or URL-structural characters
can no longer inject markup or break out of the URL.
- serveClone enforces a strict two-segment allowlist
(hashed_inventory, inventories/_, patches/_, pristine.hashed/_,
optional packs/basic.tar.gz + packs/patches.tar.gz) instead of
serving every file that passed isSafeSubPath.
- readRepoDescription and getRepoBlob use strict ByteString +
explicit UTF-8 validation under try; a NUL-byte sniff keeps binary
files from being escaped character by character.
- All request-time canonicalizePath calls go through a new
safeCanonicalize helper that degrades IO errors to 404 instead of
leaking framework 500s.
- parsePortPure rejects empty, whitespace, hex, and non-digit port
strings, replacing the partial Text.Read.read on startup.
Performance:
- getRepoPatch hash-filters before rendering diffs; a missing hash no
longer walks and renders the entire patch history.
- getRepoSummary replaces listRepos + getRepoPatches + getRepoTags
with a single readPatches pass. Count and tag positions come from
cheap PatchInfo; extractPatchListing only runs on rows actually
displayed.
- Native Text implementation of esc replaces the HtmlPure.esc String
bridge. Equivalence against the Coq-extracted spec is pinned by
QuickCheck with a biased generator over escaped characters.
- Tree/blob breadcrumb build is now O(depth) (threaded Text prefix)
instead of O(depth^2) (acc ++ [p]).
UX / mobile-first:
- renderPage emits semantic <header>/<main>/<footer> landmarks plus a
favicon <link>. Empty breadcrumb <nav> is skipped on index and 404.
- Index page has a visible <h1> site title.
- repoNavBar is a <nav aria-label="Repository sections"> with
aria-current="page" on the active tab.
- Full-log entries are <article class="log-entry">.
- Tree-icon cells carry aria-hidden="true".
Tests:
- New Properties.Config covers parsePortPure's accepted/rejected set.
- Properties.Clone pins the two-segment clone allowlist and rejects
nested paths (patches/a/b, inventories/a/b, pristine.hashed/a/b).
- Properties.Html pins the native esc equivalence vs HtmlPure.esc
with a biased generator, plus example-based tests for the
breadcrumb escape + percent-encode contracts (including the empty
blob-breadcrumb degenerate case).
Reviews under reviews/ record the pre- and post-change findings from
three Codex + three Claude agents (first pass) and four Codex + four
Claude agents (post-change), plus the reconciliation document that
the user-visible fixes were derived from.
M ./app/Main.hs -69 +91
M ./darcsweb.cabal +1
A ./reviews/
A ./reviews/_post-prompt-bugs.md
A ./reviews/_post-prompt-efficiency.md
A ./reviews/_post-prompt-quality.md
A ./reviews/_post-prompt-webapp.md
A ./reviews/_prompt-readability.md
A ./reviews/_prompt-security.md
A ./reviews/_prompt-webperf.md
A ./reviews/claude-readability.md
A ./reviews/claude-security.md
A ./reviews/claude-webperf.md
A ./reviews/codex-readability.md
A ./reviews/codex-reconciliation.md
A ./reviews/codex-security.md
A ./reviews/codex-webperf.md
A ./reviews/post-claude-bugs.md
A ./reviews/post-claude-efficiency.md
A ./reviews/post-claude-quality.md
A ./reviews/post-claude-webapp.md
A ./reviews/post-codex-bugs.md
A ./reviews/post-codex-efficiency.md
A ./reviews/post-codex-quality.md
A ./reviews/post-codex-webapp.md
M ./src/DarcsWeb/Config.hs -2 +23
M ./src/DarcsWeb/Darcs.hs -54 +203
M ./src/DarcsWeb/Html.hs -163 +267
M ./test/Properties/Clone.hs -1 +77
M ./test/Properties/Html.hs -1 +73
M ./test/Spec.hs -1 +5