Files
familysync/.planning/phases/17-ui-optimization-polish/17-SECURITY.md
T
Lucas Berger b8d4a69b73
CI / changes (pull_request) Successful in 4s
CI / fast-checks (pull_request) Failing after 1m41s
CI / api (pull_request) Successful in 2m2s
CI / security (pull_request) Successful in 54s
CI / gate (pull_request) Has been cancelled
CI / harness (pull_request) Has been cancelled
docs(phase-17): add security threat verification (9 closed, 0 open)
2026-06-18 14:33:10 -04:00

6.1 KiB

phase, slug, status, threats_open, asvs_level, created
phase slug status threats_open asvs_level created
17 ui-optimization-polish verified 0 1 2026-06-18

Phase 17 — Security

Per-phase security contract: threat register, accepted risks, and audit trail.

Phase 17 is a UI optimization & polish phase. Every plan carried a plan-time <threat_model> block (register_authored_at_plan_time: true). The work is client-side CSS/layout, static brand-asset wiring, and presentation-only React state — no new endpoints, no new authorization logic, no new runtime data flow. The single non-accept threat (logout wiring) reuses an endpoint already verified live in Phase 19.


Trust Boundaries

Boundary Description Data Crossing
Build tooling → repo (17-02) @vite-pwa/assets-generator (+ sharp, sharp-ico) runs at design time and writes static images into public/. New devDependency = supply-chain surface. Static image bytes; no secrets/PII
Client UI → existing logout endpoint (17-05) Sign out control calls the already-implemented, Phase-19-verified POST /api/auth/local/logout via fetchLocalLogout(). No new endpoint, no new auth logic. Session cookie (cleared server-side)
(none new) — 17-01, 17-03, 17-04, 17-06 CSS-only restructure/offsets, static asset references, and presentation-only local useState (tab/toast). Server-side admin 403 enforcement unchanged. None

Threat Register

Threat ID Category Component Disposition Mitigation Status
T-17-01-01 Tampering tokens.css selector restructure accept CSS custom properties carry no executable content and no user input; selector change cannot introduce injection. closed
T-17-02-SC Tampering npm devDependency install (@vite-pwa/assets-generator, sharp, sharp-ico) accept RESEARCH Package Legitimacy Audit rates all three Approved (official vite-pwa, 13-yr sharp, sharp-ico); no [SLOP]/unverified packages. devDependencies only; generated output is static images. closed
T-17-02-02 Information disclosure generated brand assets accept Assets are public-by-design brand images; no secrets or PII. closed
T-17-03-01 Tampering FAB/content CSS offsets accept Pure layout geometry via existing CSS custom property; no executable content, no input. closed
T-17-04-01 Tampering BrandSlot img / index.html links accept Logo img is decorative with empty alt; no dangerouslySetInnerHTML (T-05-24 invariant maintained); favicon/manifest entries point at committed static files. Verified live: BrandSlot renders <img src="/logo.svg" alt="" aria-hidden="true">, no dangerouslySetInnerHTML in source. closed
T-17-05-01 Elevation of Privilege logout control (D-07) mitigate fetchLocalLogout() clears the local-session cookie via the existing Phase-19-verified endpoint; client navigates to /login regardless of success/failure so a stale-cookie-with-logged-out-UI state cannot persist. Verified: SettingsSheet.tsx:143-151try { await fetchLocalLogout(); } catch {} onClose(); void navigate('/login');. closed
T-17-05-02 Tampering sheet centering CSS (D-09) accept Position-only CSS branch; no input, no executable content. closed
T-17-06-01 Tampering toast message content (D-08) accept Toast copy is hardcoded JSX string constants ("Member added." / "Password reset."); no user-controlled content; no dangerouslySetInnerHTML. Verified live: toast rendered "Member added." from a role=status node on member creation. closed
T-17-06-02 Elevation of Privilege admin two-tab nav (D-10) accept Tab strip is presentation-only local useState; isAdmin nav visibility is UX-only — the real boundary is server-side 403 on /api/admin/* (unchanged). closed

Status: open · closed Disposition: mitigate (implementation required) · accept (documented risk) · transfer (third-party)


Accepted Risks Log

Risk ID Threat Ref Rationale Accepted By Date
AR-17-01 T-17-01-01 Static stylesheet selector restructure; zero runtime data flow. Lucas Berger 2026-06-18
AR-17-02 T-17-02-SC All new devDependencies Approved by RESEARCH package-legitimacy audit; design-time only. Lucas Berger 2026-06-18
AR-17-03 T-17-02-02 Brand assets are public-by-design; no secrets/PII. Lucas Berger 2026-06-18
AR-17-04 T-17-03-01 Pure CSS layout geometry; no input surface. Lucas Berger 2026-06-18
AR-17-05 T-17-04-01 Decorative img with empty alt; no dangerouslySetInnerHTML; committed static assets. Lucas Berger 2026-06-18
AR-17-06 T-17-05-02 Position-only CSS branch; no input/executable content. Lucas Berger 2026-06-18
AR-17-07 T-17-06-01 Hardcoded toast string constants; no user-controlled content. Lucas Berger 2026-06-18
AR-17-08 T-17-06-02 Presentation-only tab state; authorization enforced server-side (unchanged). Lucas Berger 2026-06-18

Accepted risks do not resurface in future audit runs.


Security Audit Trail

Audit Date Threats Total Closed Open Run By
2026-06-18 9 9 0 /gsd-secure-phase (orchestrator, plan-time register verification)

Verification method: all 6 plans carried plan-time <threat_model> blocks (register_authored_at_plan_time: true). 8 accept-disposition threats are documented accepted risks; the 1 mitigate threat (T-17-05-01) had its mitigation verified present in SettingsSheet.tsx. Several dispositions were additionally corroborated at runtime during the Phase 17 UAT (playwright-cli): BrandSlot decorative img, hardcoded success toast, admin tab presentation-only state. threats_open: 0 — short-circuit per workflow Step 3.


Sign-Off

  • All threats have a disposition (mitigate / accept / transfer)
  • Accepted risks documented in Accepted Risks Log
  • threats_open: 0 confirmed
  • status: verified set in frontmatter

Approval: verified 2026-06-18