# DashCaddy Full Sweep — 2026-10-27 Branch: `dc/DC-168-calendar-widget` @ 6eaee81 · Suite: 3704/3705 passed (1 failure = date-expired fixture, fixed this sweep) · Lint: 12 errors (11 test-only), syntax clean. **46 unique findings: 8 high · 16 medium · 18 low.** Method: 3 parallel audit agents (backend, frontend, product consistency) + jest + eslint; the top auth finding was **verified live** (sandbox boot + real minted API key). Cross-agent duplicates merged. --- ## P0 — Broken functionality (verified) ### 1. TOTP gate blocks ALL JWT/API-key auth when TOTP is enabled — HIGH (live-verified) `src/utilities/middleware.js:531-598` — `totpAuthMiddleware` registers before `jwtApiKeyAuthMiddleware` and only checks session cookies; the JWT/API-key verifier never runs. Agent booted the real app with TOTP enabled, minted a valid `dk_` key, and `GET /api/v1/mcp/info` returned **401** with the TOTP gate's message. MCP-over-HTTP ("Connect your AI"), fleet endpoints, and every machine-credential integration are dead on any TOTP-protected install (i.e., production). **Fix:** in the TOTP gate, attempt `verifyJWT`/`verifyAPIKey` before rejecting (or register the JWT/API-key gate first and let the TOTP gate pass through when `req.auth` is set). Tests missed it because `mcp.routes.test.js` injects `req.auth` directly. ### 2. Calendar widget writes all fail with 403 CSRF — HIGH `status/js/calendar-widget.js:269-390` — all four mutations (discover / save / toggle / delete) use raw `fetch` with no `X-CSRF-Token`; backend `csrf-protection.js` rejects non-GETs with `[DC-100]`. Test Connection, Save, toggle, remove → all 403. The DC-168 feature we just shipped is write-broken. **Fix:** route the calls through the existing `secureFetch` helper (injects token + auto-retries DC-100/101). ### 3. CSP silently kills inline onclick handlers — HIGH `status/index.html:223,1051` — the meta CSP allows no `unsafe-inline`/`unsafe-hashes`, so `onclick=` attributes are blocked: **Disk Safety button, AI-chat FAB, and the injected Disk modal's Save/Clean-Up buttons do nothing** in all modern browsers. **Fix:** wire `addEventListener` in `disk-settings.js` / `ai-chat.js` init; drop the inline attributes. ### 4. Workflows UI broken — double-prefix route bug still live; the fix exists on an unmerged branch — HIGH `routes/workflows.js:37-78` declares `/workflows/...` while mounted at `/workflows` → `/api/v1/workflows/workflows/...`. Dashboard calls single-prefix → **every workflow toggle and Run Now 404s**. Premium-gated feature broken for paying customers. `openapi.yaml` documents the doubled paths (doc faithfully describing the bug). **Root cause:** commit e524d16 (DC-166: workflows fix + MCP 17→28 tools) lives only on `dc/DC-166-mcp-completion` — **never merged into this branch** (verified via merge-base). **Fix:** merge DC-166 into this branch, resolve mcp-core.js conflicts, re-run MCP suites. ### 5. Six MCP tools map to nonexistent routes — 404 at call time — HIGH `src/mcp/mcp-core.js:268-300` — `get_service` (→GET /services/:serviceId ✗), `check_health` both modes (→/services/:id/health ✗, /health/all ✗), `list_containers` (→GET /containers ✗ no list route), `container_action remove` (→DELETE /containers/:id/remove ✗), `list_dns` (→GET /dns ✗). tools/list still advertises all of them to AI clients. **Fix:** repoint to real routes (add `GET /containers` list route), and extend the route-existence test to EVERY tool (currently only the 11 DC-166 tools are covered — that's how this survived). ### 6. Calendar error path calls a nonexistent method — HIGH `status/js/calendar-widget.js:329` — catch block calls `errorHandler.log()` which doesn't exist (`window.ErrorHandler` only has `logError`, and args are reversed anyway). The "Calendar offline" chip state is unreachable; every failed poll throws an unhandled rejection instead. **Fix:** `errorHandler.logError('calendar-widget', e)`. ### 7. Calendar modal unusable on mobile — HIGH `status/js/calendar-widget.js:18` — inline `min-width:560px` on the modal beats the ≤700px media query (inline styles win), so it overflows horizontally on phones. Codebase already needed `!important` for this exact pattern elsewhere. **Fix:** size via a CSS class, drop the inline min-width. ### 8. `dashcaddy_deploy_app` MCP tool lies about deploying — MEDIUM (borderline high) `src/mcp/mcp-core.js:318-358` — returns `deployed:true` + "Access it at https://plex.sami" but never pulls an image, creates a container, or writes a Caddyfile — only generates text and adds a dashboard entry; its own nextSteps tell the user to run docker manually. Also **hardcodes the owner's private `.sami` TLD into what would be customer deployments** (same hardcode in `app-selector.js:42`, `dns-template-selector.js:37,161`). **Fix:** route through the real `POST /api/v1/apps/deploy`, or re-contract as a "plan deployment" helper; read the base domain from site config. --- ## P1 — Reliability & security 9. **MEDIUM — DNS check-update/update handlers hang.** `routes/dns.js:669-800`: bare `return`s, empty else/catch branches → request never answered until client timeout; non-JSON upstream dies in `JSON.parse` inside the swallowing catch. Fix: every path responds or throws typed. 10. **MEDIUM — Seven reversed-arg `errorResponse(res, '', )` callsites** (`dns.js:624,735,817`, `errorlogs.js:219`, `ssl-monitor.js:95,99`, `disk-space.js:133`) hit the DC-062 guard → reachable paths 500 instead of intended 4xx/50x. This is the exact class DC-063 claimed fixed. Fix: reorder + grep gate to prevent regressions. 11. **MEDIUM — Calendar ICS fetch timeout silently ignored** — `routes/calendar.js:177` passes `opts.timeout`, which `src/utils/http.js` explicitly strips (undici); effective deadline 5s not 15s. Fix: `fetchT(url, {}, 15000)`. 12. **MEDIUM — StateManager writes are non-atomic.** `state-manager.js:85,138` truncate-then-write; crash mid-write corrupts services.json/config.json with no recovery (read only special-cases ENOENT). share-store already does tmp+rename correctly — reuse it. 13. **MEDIUM — Tailscale tailnet gate fails open** on daemon errors (`middleware.js:139-184`) — a tailscaled outage silently disables the IP allowlist while config claims enforcement. Fix: fail closed or make it loud. 14. **MEDIUM — WS auth fallback fails open.** `dashboard-ws.js:75-86` defaults to "any non-empty session cookie" when the real verifier isn't injected — the exact DC-061 footgun. Fix: default deny + loud log. 15. **MEDIUM — MCP `generate_caddyfile` auth flag is a silent no-op** (`mcp-core.js:375-383`) — "Enable SSO auth gate" produces a Caddyfile with NO auth gate, no error. Security-relevant for AI-generated configs. 16. **MEDIUM — SW offline fallback is dead code** (`status/sw.js:62-64`): `caches.match(...) || caches.match('/index.html')` — a Promise is always truthy, fallback never runs; plus 5xx responses get cached and later served as the app shell (no `response.ok` checks); plus install is all-or-nothing `addAll` of 36 URLs — one miss keeps the stale SW in control indefinitely. 17. **LOW — Host ingest keys HMAC'd with hardcoded fallback pepper** (`host-registry.js:97-103`) when the pepper file is missing — and no generation/persistence path exists at all. 18. **LOW — Calendar source update wipes the other credential type** (updating pass nulls token and vice versa) `routes/calendar.js:144-148`. 19. **LOW — Authenticated SSRF surface**: calendar sources + /discover accept localhost http + arbitrary https, status differentials allow internal port-scanning; no link-local/private-CIDR guard (169.254.169.254 over https). 20. **LOW — Public GET /api/v1/config returns raw config.json verbatim and 500s if corrupt** (`routes/config/settings.js:26-33`, route is in PUBLIC_ROUTES) — pairs badly with finding 12. 21. **LOW — MCP search_catalog drops the category filter when a query is present** (`mcp-core.js:304-311`). 22. **LOW — system_health/system_metrics tools bypass apiCall** — no timeout/size-cap/error envelope, unlike every other tool. 23. **LOW — Fleet tool 403s for free licenses with no premium hint in tools/list.** ## P2 — Frontend polish & races 24. **MEDIUM — In-flight guard silently drops forced refreshes** (`calendar-widget.js:284`) — save/toggle followed by a mid-flight poll = stale chip for up to 5 min. 25. **MEDIUM — openModal issues two overlapping fetch sequences** (no in-flight guard; last-response-wins can repaint with stale data). 26. **MEDIUM — Source toggle/delete handlers: unguarded fetch, no resp.ok check, optimistic re-render** — failures re-render as success. 27. **MEDIUM — `--cal-dot` theme overrides are dead CSS** — variables scoped to elements that are never ancestors of `.cal-dot` (`dashboard.css:4316,4347-4358`); the WCAG-tuned DC-168 dot colors never render. 28. **MEDIUM — Calendar chip keyboard-inaccessible; modal lacks Escape/click-outside/focus management** (`role=button` with no keydown; `display` set directly, bypassing wireModal). 29. **LOW — Calendar GETs bypass secureFetch** — no 15s timeout; one hung request wedges `refresh()` forever (pairs with 24). 30. **LOW — Polling ignores `document.hidden`** — 5-min calendar polls + 1s health polls run 24/7 in background tabs. 31. **LOW — log-insights.js still uses blocking `alert()` 6×** despite the toast convention. 32. **LOW — Windows path `C:\caddy\Caddyfile` prefilled on a Linux deployment** (`globals.js:39`). 33. **LOW — One malformed event start (null/invalid) crashes the cache merge** and flips the whole widget offline instead of skipping the event (`calendar-cache.js:8`). ## P3 — Professional/docs (the "polish" tier) 34. **HIGH (credibility) — DC-166 branch never merged** (see P0-4): capabilities manifest and changelog advertise a toolset this branch doesn't ship. 35. **MEDIUM — Changelog malformed**: two `[Unreleased]` sections; `[1.17.0]` before older entries; `[1.5.0]` after `[1.13.4]`; no `[1.16.0]` though package.json is 1.17.0; **internal judge grades + UMP URNs sitting in customer-facing copy**; DC-166 and DC-169 have no entries at all. 36. **MEDIUM — Three-way version drift**: package.json 1.17.0 vs openapi.yaml 1.15.0. ~~VERSION file 1.15.0~~ CORRECTION (fix round): dashcaddy-api/VERSION is a BUILD STAMP (start.sh writes the deployed commit SHA at deploy time), not a semver source — it legitimately read 70e252c; setting it to a semver broke SelfUpdater commit resolution and was reverted. Real drift = openapi.yaml only (fixed to 1.17.0). 37. **MEDIUM — README quickstart endpoints omit `/v1` — every example 404s as written** (`README.md:283-300`). 38. **LOW — README says "50+ templates"; the measured catalog is 78** (DC-165 fixed marketing but not the repo README). 39. **LOW — API-SURFACE.md stale (2026-07-13, 285 vs 290 routes)** and documents the broken workflows paths as canonical; wrong PUBLIC_ROUTES line refs. 40. **MEDIUM — MCP mapping verifier covers only the 11 new tools** — extend the EXPECTED table to every tool and fail on any unresolved (method,path): turns tool↔route drift into a CI gate. 41. **LOW — Test-suite time bomb** (fixed this sweep): `calendar.routes.test.js` hardcoded `DTSTART:20261007` which silently expired out of the default window Oct 14 — fixture now builds tomorrow's date at runtime. Worth a repo-wide grep for hardcoded future dates in fixtures. ## Also this sweep - Fixed + verified: calendar test fixture (15/15 green) — `__tests__/routes/calendar.routes.test.js`, uncommitted. - Uncommitted in tree (pre-existing, not mine): `status/dist/features.js`, `status/sw.js`. - Lint: 12 errors = 10× `no-eval` + 1× `no-new-func` in test files, 0 production errors remaining (credentials.js control-regex self-resolved in the response-helper refactor). 1027 warnings mostly unused-vars noise — a `warnings → 0` pass would be a cheap professionalism win. ## Recommended execution order 1. **Merge `dc/DC-166-mcp-completion`** into this branch (fixes P0-4, shrinks P0-5, restores toolset truth) → re-run MCP suites. 2. **Calendar widget fix batch**: secureFetch (CSRF+timeout), `errorHandler.logError`, modal CSS class, race/coalesce fixes, ARIA/Escape (P0-2, P0-6, P0-7, 24-30). 3. **TOTP/JWT gate ordering** (P0-1) + a chain-level integration test that exercises the real middleware chain, not injected `req.auth`. 4. **CSP event-handler wiring** (P0-3). 5. **MCP truth batch**: repoint dead tools, deploy_app re-contract + TLD config, auth-flag fix, full route-existence gate (P0-5, P0-8, 15, 21-23, 40). 6. **Reliability batch**: DNS hang, errorResponse arg order + grep gate, ICS timeout, atomic state writes, WS/tailscale fail-closed (9-14, 20). 7. **Docs/polish batch**: changelog rebuild, version single-sourcing, README/API-SURFACE regen, alert() → toast, Windows path (35-39, 31-32). Every fix batch lands judged (Codex min B) + full suite + live-curl verification before commit, per standing policy.