# Security & Code Quality Audit — homepage **Date:** 2026-09-18 **Auditor:** pi agent (read-only audit; no fixes applied at audit time) **Method:** manual source review (all files) + tooling: `git log`, `git grep` secret scan, `ssh-keygen` key validation, `curl` live-header probe. ## Disposition (2026-09-18, same session as the audit) - **M-1, M-2, M-4 → scheduled as phases** under the phased-execution protocol: `.agents/phases/todo/01_fix_history_xss/`, `02_simulate_fetch_commands/`, `03_nginx_security_headers/` (see `.agents/PLAN.md` §7). Execute with the `phased-execution` skill (`auto-phase.sh`). - **M-3 → DISMISSED (false positive per project owner, 2026-09-18).** The host firewall already restricts access to the container port and the site is fronted by the Caddy TLS edge; the README's `0.0.0.0:8080` binding is accepted as-is. - **Code-quality findings (Q-1…Q-12, plus the vim `dd` dead path) → executed directly in-session** (explicit owner instruction to run them outside the phase pipeline): module split (`terminal.js` 1353 → 652 lines + `terminal-commands.js` / `terminal-vim.js` / `terminal-achievements.js`), dead-code removal, expand/collapse helpers, rack-decoration CSS classes, `hidden`-attribute reveal, a11y label, dynamic `date`, vim line-deletion fix. A 44-test Playwright E2E regression suite (`tests/e2e/`, run with `./build.sh && npm test`) was created and is green. - Remaining Low items (L-1, L-3, L-5, L-7, L-8) stay unphased; L-1 is folded into Phase 03's verification notes, L-2 into Phase 03 task 01. ## Scope & Stack - **Stack:** static site (vanilla HTML/CSS/JS, no framework, no build deps) → hashed via `build.sh` (md5 cache-bust + `sed` rewrites) → served by `nginx:alpine` on `:8080` (plain HTTP, TLS expected at an external Caddy edge) → Docker/Podman image pushed to `gitea.reeseapps.com/services/homepage` via Gitea Actions. - **Core functionality:** personal portfolio with an interactive fake terminal (easter-egg commands, vim simulator, achievements in `localStorage`). - **Data sensitivity:** none at rest. Public PII only (name, email, GPG public keys). No auth, no backend, no forms. - **Assumptions:** TLS termination + HSTS happen at the Caddy reverse proxy in front of this container (container only listens on 8080 HTTP). Live site headers could not be verified from this network. **Overall:** low-risk attack surface (static, no input persistence, no backend). No Critical/High findings. One Medium XSS-pattern, two Medium deployment issues, several Low hardening items, and a number of code-quality debts (dead code, duplication, untested build). ## Findings (severity-ranked) --- ### M-1. XSS sink: terminal command history recalled via `innerHTML` - **Severity:** Medium (High if ever served without the CSP below) - **Type:** CWE-79 (DOM-based XSS via innerHTML) - **Location:** `src/terminal.js`, ArrowUp/ArrowDown handlers in the terminal `keydown` listener (~lines 330–370) - **Description:** The Enter handler stores raw user input into `commandHistory` (captured via `textContent`, so typing is safe). But ArrowUp/ArrowDown recall it with: ```js lastLine.innerHTML = '$ ' + cmdLine + ' '; ``` `cmdLine` is untrusted input injected into `innerHTML`. - **PoC:** In the site's terminal type: ``` ``` press Enter, then press ↑. The payload is injected as HTML. Mitigated *today* by the deployed CSP `script-src 'self'` (inline handlers and inline `