fix(security): close runtime XSS and object authorization gaps - #6098
Conversation
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — systematic runtime-XSS and object-authorization hardening across admin/member/registry/perspective/feed/Addie surfaces.
What I checked:
- Scope is server/UI only — no
static/schemas/source/**,docs/reference/**, ordist/**touched, so no schema↔docs coherence, oneOf discriminator, or immutable-artifact concerns apply, and the omitted changeset is correct per the app/admin/Addie out-of-scope rule. gated_paths: false; no no-auto-approve team match; first review of this PR.high_risk: truebut all reasons are(modified)underserver/src/addie/**andserver/src/routes/**— no(deleted)entries, and no Medium-or-higher finding lands on any modified high-risk file, so rows 3 and 5 do not fire.- Centralized URL validators (HTTPS-only member-profile-url, perspective-url) replace ad-hoc parsing at persistence/render boundaries; feedback writes bound to (thread_id, message_id); portrait serving and admin routes gated to owner/global-admin; curator model output crosses a fail-closed, retryable validation boundary. Substantial accompanying test coverage.
Findings: Only one Low finding (content-curator.ts:1257 — fail-closed retryable boundary, verified safe). No critical/high/medium findings survive review.
Decision table: rows 1–8 do not fire (no critical/high, gate not applicable, no deletions, no medium on modified high-risk file, no prior escalation, no team gate, fewer than three medium findings). Falls through to row 9 → approve.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean diff, no blocking or medium findings.
The delta since the prior clean pass is two files: an explicit window.getSafePerspectiveNavigationUrl export at perspective-url.js:12-14 and a new unit test covering it. The export is redundant in the browser (classic-script top-level functions already bind to window) but load-bearing for the test's new Function('window', source) harness, which scopes declarations locally. Validator logic is unchanged, no wire surface is touched, and the test assertion is correct.
No protocol surface (static/schemas/source/**, docs/reference/**, dist/**) is modified, so schema↔docs coherence, changeset, oneOf-discriminator, and released-artifact-immutability gates do not apply. The PR is app/server/site-scoped.
high_risk is true only via (modified) matches on server/src/addie/** and server/src/routes/**, but the reviewer surfaced no medium-or-higher concerns on those files, so the modification is presumed safe (per high-risk-flag guidance — do not escalate on the flag alone). gated_paths is false and no no-auto-approve team match. Prior decision was already approve.
Decision table: rows 1–8 do not fire (no critical/high, no medium, gate false, no deletions, no team gate). Falls through to row 9 → approve.
Summary
Root cause
Untrusted stored strings were reused across multiple HTML and navigation sinks without one canonical validation boundary. Several resource IDs, organization IDs, and lifecycle states were also trusted without consistently binding them to the authenticated owner or required global-admin role.
Validation
No changeset is required because these are server/UI operational fixes with no protocol-package changes.