Skip to content

Documentation

Threat Model: Admin Pages

STRIDE review of admin page loads, page guards, layout data and the administration landing route.

11/5/2026
3 min read Edit on GitHub
On this page

Scope: src/routes/(app)/+layout.server.ts, +layout.ts, every page below src/routes/(app)/config/**, (app)/dashboard/**, (app)/plugin/**, src/routes/(admin)/** and the shared guards in src/utils/page-guards.server.ts. The admin layout owns the session-aware page payload; the authorization hook (src/hooks/handle-authorization.ts) decides whether the request reaches it.

STRIDE

Threat Surface Existing control (file reference) Residual risk
Spoofing — unauthenticated visitor opens an admin page src/utils/page-guards.server.ts getAuthenticatedUser() throws a redirect to the login page when locals.user is absent; requirePagePermission() fails closed on a missing permission. A page that reads locals.user directly instead of the guard can render a partial shell; code review must reject that pattern.
Spoofing — tenant-bound admin reaches a system-only page src/routes/(admin)/admin/+page.server.ts The landing route requires locals.isAdmin and rejects users with a tenantId via a redirect. The same distinction must be repeated in any new system-only route; this model covers the existing route only.
Tampering — admin writes bypass the API authorization path src/routes/(app)/+layout.server.ts, src/hooks/handle-authorization.ts Page loads are read-only; mutations go through /api handlers that run _checkEndpointPermission(). A form action that writes directly to an adapter would bypass the endpoint mapping and must be rejected in review.
Repudiation — admin change without an audit entry src/hooks/handle-audit-logging.ts The audit hook wraps authenticated admin mutations in the server pipeline. Page loads themselves are not mutations and are not audited.
Information disclosure — layout data leaks another tenant’s config src/routes/(app)/+layout.server.ts Layout data is resolved per request from the authenticated user’s tenant scope. Cached settings must be keyed by tenant; a global cache key is a defect this model expects tests to catch.
Denial of service — expensive dashboard widgets src/routes/(app)/dashboard/+page.server.ts Widget data is loaded server-side; expensive system metrics are served by dedicated API handlers with their own rate lane. A dashboard with many widgets still issues several queries per load; pool sizing is operational.
Elevation of privilege — plugin admin view renders privileged actions src/routes/(app)/plugin/[...path]/+page.server.ts The plugin page resolves the plugin and the current user before rendering; API calls from the view are still gated by the dispatcher. Plugin view code is third-party and can render any UI; the API gate remains the enforcement point.

Assumptions

  • The (app) layout is the only place that assembles the admin page payload; individual pages add data, not identity.
  • The authorization hook runs before every (app) and (admin) request, including client-side navigations that hit a server load.
  • Security headers (CSP, HSTS, X-Frame-Options) are applied globally by src/hooks/handle-security-headers.ts.
securitythreat-modeladmin
Was this page helpful?