Skip to content

Documentation

Tested Security Features

Comprehensive list of security features covered by automated tests

10/11/2026
21 min read Edit on GitHub

This document lists all security features that are covered by automated tests in SveltyCMS.

Security Policy: SveltyCMS follows a strict no-backdoor policy. All security tests must pass through real security guards without any mocked permission checks or test bypass mechanisms.

Overview

SveltyCMS has comprehensive security test coverage across multiple test suites:

  • Unit Tests: ~180+ tests for middleware and utilities
  • Integration Tests: ~80+ tests for API endpoints
  • E2E Tests: ~40+ tests for user flows

Security Vulnerabilities Fixed

1. Localhost Rate Limit Bypass (Protected March 2026)

Issue: Localhost (127.0.0.1, ::1) was exempt from rate limiting, allowing SSRF attacks to bypass rate limits.

Fix: Restricted localhost exemption to dev mode or TEST_MODE=true integration tests in src/hooks/handle-rate-limit.ts. In production builds, all requests are rate-limited regardless of source IP.

Tests: tests/unit/hooks/rate-limit.test.ts

2. Firewall Bypass (Protected March 2026)

Issue: Setting TEST_MODE=true would completely bypass the firewall security checks.

Fix: Restricted TEST_MODE bypass in src/hooks/handle-security.ts:167 to local requests only and to explicit test environments — if (isLocal && IS_TEST_MODE && !forceSecurity) return resolve(event). A validated x-test-secret alone is not sufficient, so benchmark runs still exercise the full WAF/firewall path. In production, the firewall is always active unless explicitly disabled via settings.

Tests: tests/unit/security/handle-waf-guard.test.ts, tests/unit/hooks/defense-in-depth.test.ts

3. Missing POST-Authentication Authorization Gap (Resolved April 2026)

Issue: Authenticated users could access various /api/ endpoints within their tenant without granular permission checks.

Fix: Implemented a Fail-Closed API Dispatcher in src/routes/api/[...path]/+server.ts using an exhaustive ENDPOINT_PERMISSIONS mapping. Any route not explicitly authorized is denied by default.

Tests: tests/unit/hooks/authorization.test.ts, tests/unit/api/dispatcher-body-limit.test.ts, tests/unit/api/dispatcher-security-matrix.test.ts

UI: Added “Access Denied” feedback logic in user-management.svelte to handle 403 status codes.

4. September 2026 Enterprise Security & Performance Hardening

Assessment Outcome: Enhanced A++ Enterprise Security with constant-time cryptographic normalization, AST-level batch size verification, and reverse-proxy spoof-proofing.

Hardened Defenses & Fixes (September 2026) {#september-2026-hardened-defenses}

  1. Timing-Attack Defense on Login (CWE-208): Neutralized user enumeration by executing verifyDummyPassword() against a pre-computed Argon2id hash (DUMMY_ARGON2_HASH) in AuthNamespace.login whenever a user is not found. Response latency for invalid accounts matches valid-user password hashing (~50–80ms).
  2. Reverse Proxy IP Spoofing Elimination: Enforced getClientIp(event) across all authentication hooks, password reset flows, audit logging, and WebSocket rate limits. Direct access to raw X-Forwarded-For headers is blocked and scanner-enforced via scripts/scan-security-risk.ts.
  3. AST Batch Payload Guard (batch-size-unbounded): Added context-aware AST security analysis in scripts/scan-security-risk.ts using the TypeScript Compiler API. Inspects all API handlers for batch processing and asserts an upper-bound length assertion (<= 100) to prevent memory exhaustion and event loop starvation.
  4. Non-Blocking Redis Key Scanning: Replaced synchronous, blocking redis.keys() in dashboard metrics with non-blocking redis.scanIterator() and bounded counts, protecting Redis event-loop throughput under high load.
  5. Deterministic SSR Hydration & Localization: Replaced render-time crypto.randomUUID() in UI primitives with Svelte 5’s $props.id() rune, and consolidated 52+ unpinned date/number formatting call sites into @utils/format-date with scanner enforcement (slop-scanner.ts).
  6. API Error Handling Uniformity: Migrated all remaining handlers in src/routes/api/[...path]/handlers/ to the canonical raise(status, message, code) pattern.

Tests: tests/unit/auth/timing-defense.test.ts, tests/unit/scripts/scan-security-risk.test.ts, tests/unit/scripts/slop-scanner.test.ts, tests/unit/hooks/ws-ratelimit.test.ts, tests/unit/utils/format-date.test.ts.

5. Native Field-Level Encryption at Rest (September 2026)

Control: Collection fields flagged encrypt: true are stored as AES-256-GCM envelopes (v1:iv:tag:ciphertext) using ENCRYPTION_KEY. Writes fail closed without the key. Tamper / AAD mismatch decrypts that field to null. Filter/sort on encrypted fields returns 400 ENCRYPTED_FIELD_NOT_QUERYABLE.

Tests: tests/unit/utils/field-encryption.test.ts, tests/unit/services/field-encryption-pipeline.test.ts, tests/unit/content/schema-contract.test.ts (encrypt+unique warning).

6. Collection Weak ETags & DB-free 304 (September 2026)

Control: Collection find/entry GET responses carry W/"cv1|{tenant}|{collection}|{epoch}|{reprHash}". Matching If-None-Match returns 304 before the handler (no DB). Epoch bumps on write; repr hash includes user id so publication/FLAC views do not cross-validate. refresh / nocache / bypassCache skip the 304.

Tests: tests/unit/services/collection-etag.test.ts.

7. August 2026 Lean-Code Security Assessment & Audit Remediation

Assessment Outcome: Maintained A++ Security Grade with self-healing isolation.

Hardened Defenses & Fixes:

  1. Multi-Tenant Test Gate (T1): Gated x-tenant-id and x-test-tenant-id header resolution strictly to isTestMode, closing header-based tenant switching in production.
  2. Permission Retention Elimination (T2): Added the invalidateRoleBitset and setRolePermissions helpers in src/databases/auth/permissions.ts to purge cached role bitsets. No role-edit path calls them yet, so automatic bitset invalidation on role edits is not active; wiring is tracked in the roadmap.
  3. Cryptographic Content-ETag (T15): Replaced timestamp weak ETags with content hashes (generateContentEtag), preventing false 304s on content updates and deletions.
  4. Open Redirect Defense (T25): Centralized safeRedirect() engine blocking protocol-relative URLs (//evil.com) and unvalidated domains.
  5. Multi-Variant Session Cookie Purge (T6): Consolidated multi-variant cookie revocation (__Host-, __Secure-, plain) in session-cookie.ts.
  6. SQL LIKE Wildcard Escaping (T24): Added escapeLike helper escaping %, _, and \ with explicit ESCAPE '\\'.
  7. Commerce Security Hardening (T27, T28, T29, T33, T34):
    • Gated variants/apply to admin callers.
    • Bound PaymentIntent ID directly to verified order records.
    • Fail-closed order listing by customer identity.
    • Capped variant matrix generation to 500 combinations to prevent Cartesian DoS attacks.
    • Protocol-validated trackingUrl (only https?:// allowed).

Tests: tests/unit/security/safe-redirect.test.ts, tests/unit/plugins/commerce.test.ts, tests/unit/databases/escape-like.test.ts, tests/unit/auth/sessions/session-cookies.test.ts.

8. April 2026 Security Assessment & Hardening

Assessment Outcome: Achieved A++ Security Grade.

Key Implemented Features:

  • ✅ Robust Path Normalization: Hardened src/routes/files/ against directory traversal using strict path.relative boundary checks.

  • ✅ Fail-Closed Dispatcher Verification: Every unhandled route now strictly returns 403.

  • ✅ Argon2id Memory-Hardness Audit: Confirmed 64MB memory cost for resistance against quantum/ASIC speedup.

  • ✅ SHA-256 Audit Chaining: Implemented tamper-evident chaining for all file-backed security logs.

  • ✅ Middleware Sequence Lockdown: Verified that security headers and firewall scan are executed before any logic layers.

  • ✅ Resource Exhaustion Immunity (Load Shedding): Verified that mutation requests are rejected (503) when memory usage exceeds 90%, protecting read-availability.

  • ✅ Graceful Shutdown: Confirmed clean teardown of DB and Cache connections on SIGTERM.

  • ✅ Timing-Safe Cryptographic Handshake: Verified that x-test-secret comparison uses constant-time logic.

Tests: tests/benchmarks/security-audit.test.ts (added April 2026), tests/unit/security/handle-waf-guard.test.ts.

9. May 2026 Enterprise Security Hardening

Assessment Outcome: Defense-in-depth hardening across all API and page action layers.

Key Implemented Features:

  • ✅ Cookie Prefix Enforcement: __Host- prefixed cookies only accepted on secure connections. Insecure connections (localhost/dev) never accept prefixed cookies, preventing subdomain cookie tossing (src/hooks/handle-authentication.ts).

  • ✅ Setup Completion Gating: All /api/setup endpoints blocked (403) after setup completes. Bootstrap route redirects enforced at middleware level (src/hooks/handle-system-state.ts, src/routes/api/[...path]/handlers/setup.ts).

  • ✅ Handler-Level Admin Verification: handleSettingsRoutes, handleSystemMgmtRoutes, and handleAutomationRoutes in system.ts now verify admin status for all mutating operations.

  • ✅ Media Mutation Permissions: Defense-in-depth RBAC checks added to handleMediaUpload (media:write) and handleMediaPostDelete (media:delete) in media.ts.

  • ✅ Centralized Permission Guards: requireCollectionBuilderPermission() helper eliminates duplicated authorization logic across all Collection Builder actions (src/routes/(app)/config/collectionbuilder/+page.server.ts).

  • ✅ Critical Bug Fixes: Restored missing initSystemFast import in setup seed flow. Fixed adapter disconnect-before-use race condition in setup completion handler.

Files Modified: src/hooks/handle-authentication.ts, src/hooks/handle-system-state.ts, src/routes/api/[...path]/handlers/setup.ts, src/routes/api/[...path]/handlers/system.ts, src/routes/api/[...path]/handlers/media.ts, src/routes/(app)/config/collectionbuilder/+page.server.ts, src/routes/(admin)/admin/tenants/+page.server.ts.

Tests: tests/unit/hooks/authentication.test.ts, tests/unit/hooks/authorization.test.ts, tests/unit/api/dispatcher-security-matrix.test.ts.

10. AI Bot Defense & Crypto Hardening

Assessment Outcome: Enhanced AI crawler defenses, zero-bias tokens, and cross-origin isolation headers.

  • AI Bot Detection: Proactive User-Agent fingerprinting blocks 28 known AI crawler/reconnaissance bot patterns in src/hooks/handle-security.ts.

  • Expanded Honeypot Grid: 45+ decoy routes (WordPress, Drupal, AWS metadata, config files) trigger IP blacklisting.

  • Honeypot IP Flagging + Response Poisoning: Immediate decoy 200 + IP flagging (no socket-holding tarpit) plus fake JSON payloads waste bot resources.

  • Cross-Origin Isolation Headers: Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy, Cross-Origin-Resource-Policy on all API responses in src/hooks/handle-security-headers.ts.

  • Zero-Bias Token Generation: generateRandomToken uses rejection sampling eliminating modulo bias in src/databases/auth/constants.ts.

  • Setup Session Cookie Fix: Consistent __Host- prefix logic in setup handler (src/routes/api/[...path]/handlers/setup.ts).

Files: src/hooks/handle-security.ts, src/hooks/handle-security-headers.ts, src/databases/auth/constants.ts, src/routes/api/[...path]/handlers/setup.ts.

Tests: All 134 security regression tests pass.

11. Comprehensive Security Audit & Hardening

Assessment Outcome: Closed 6 security gaps identified through full-stack codebase audit against dependency stack, security architecture docs, and CVE landscape.

Key Implemented Features:

  • ✅ SVG Polyglot Upload Sanitization: Server-side sanitizeSvg() function strips script tags, foreignObject elements, inline event handlers, javascript:/data: URIs, CDATA blocks, XML PIs, and DOCTYPE declarations from uploaded SVG files before storage (src/utils/media/media-service.server.ts).

  • ✅ SVG large-file streaming bypass fixed: SVGs always take bufferAndSanitizeSvg (never the ≥5MB pure-stream path). MAX_SVG_BYTES rejects oversized SVGs. File server adds CSP script-src 'none' for image/svg+xml responses.

  • ✅ User attribute privilege escalation fixed: handleUpdateUserAttributesRoute requires auth, restricts non-admins to self, and strips role/isAdmin/permissions via stripPrivilegedUserFields (src/utils/security/user-attribute-policy.ts).

  • ✅ User creation privilege escalation fixed: POST /api/user (bare namespace root) no longer inherits the auth path exemption — the dispatcher requires user:write for creates and user:read for listings (GET /api/user), matching the strict POST /api/user/create-user sibling; bare GET /api/auth stays a named self-service read, and public login/2FA/OIDC/SAML actions remain exempt. Non-admin create payloads are stripped of role/roles/isAdmin/roleIds/permissions via sanitizeClientUserAttributePatch (src/routes/api/[...path]/handlers/auth.ts). Coverage: tests/unit/api/rbac-endpoint-integration.test.ts, tests/unit/api/user.test.ts, tests/unit/security/user-attribute-policy.test.ts.

  • ✅ Token namespace positive allowlist: /api/token/* no longer inherits a public-route deny-list — only GET /api/token/validate-token/{value} is public. List/create/resolve/batch and PUT/DELETE require an admin session or the mapped api:token permission (Website Tokens stay admin-only), unauthenticated GET /api/token/{value} returns 401, and PUT /api/token/{id} is field-allowlisted to expires/blocked/consumed (admins additionally email/role; never user_id, type, token, _id, tenantId). Coverage: tests/unit/api/token.test.ts, tests/integration/api/token.test.ts, tests/unit/api/authz-bypass-guard.test.ts.

  • ✅ GraphQL APQ hash binding: extensions.persistedQuery keys must be exactly 64 hex characters and sha256(query) is compared with timingSafeEqual before the query is registered, so a client cannot plant a query under a hash another client will request (PERSISTED_QUERY_HASH_INVALID / PERSISTED_QUERY_HASH_MISMATCH). Coverage: tests/unit/api/graphql-apq.test.ts.

  • ✅ Central page-size ceiling: MAX_PAGE_SIZE = 200 in @utils/api-params clamps REST parsers, the SDK find funnel and the GraphQL fast path + resolver, so a client-supplied limit can no longer reach adapters unbounded. Coverage: tests/unit/utils/api-params.test.ts, tests/unit/core/page-utils.test.ts, tests/unit/graphql/fast-path.test.ts.

  • ✅ Uniform 15 MB body ceiling (chunked included) and incremental media abort: the dispatcher counts Transfer-Encoding: chunked bodies through a zero-copy stream and stops at API_MAX_BODY_SIZE_BYTES (15 MB) exactly like Content-Length bodies (413 PAYLOAD_TOO_LARGE); the media stream route passes the same cap to parseMultipartStream and aborts oversize parts incrementally instead of buffering. Coverage: tests/unit/api/dispatcher-body-limit.test.ts, tests/unit/media/streaming-upload.test.ts.

  • ✅ Authorization bypass guardrail suite: tests/unit/api/authz-bypass-guard.test.ts (27 tests) enumerates every /api/<namespace> root and every public-route exemption against a reasoned contract, so a new authorization bypass fails a unit test instead of shipping.

  • ✅ Remote media upload SSRF fixed: Gallery remote upload uses MediaService.saveRemoteMedia → validateEgressUrl + safeFetch (private IPs, localhost, metadata, DNS rebinding, redirect re-validation).

  • ✅ S3 Endpoint SSRF Prevention: validateS3Endpoint() blocks private IP ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16), IPv6 loopback/link-local, cloud metadata hosts, and non-HTTP protocols before S3Client creation (src/utils/media/storage-adapters.ts:42).

  • ✅ Ollama RAG Prompt Injection Defense: Remote knowledge-base context relocated from system to user message role with <rag_context> XML delimiters. Control characters stripped from RAG results. System prompt hardened with explicit instruction-override rejection (src/services/core/ai-service.ts).

  • ✅ GraphQL Introspection Explicit Blocking: NoSchemaIntrospectionCustomRule added to production validation plugin, preventing unauthenticated schema enumeration (src/routes/api/graphql/+server.ts).

  • ✅ Tiptap Link Protocol Hardening: Link.configure() now enforces protocol allowlist ["http", "https", "mailto", "tel"], blocking javascript: and data: URI injection at editor level (src/widgets/core/rich-text/tiptap.ts).

  • ✅ Rate Limiter Dependency Hygiene: Moved sveltekit-rate-limiter from devDependencies to dependencies to ensure production build inclusion (package.json).

Files Modified: src/utils/media/media-service.server.ts, src/utils/media/storage-adapters.ts, src/services/core/ai-service.ts, src/routes/api/graphql/+server.ts, src/widgets/core/rich-text/tiptap.ts, package.json.

Tests: All 64 defense-in-depth security regression tests pass. 0 lint errors. 0 type errors.

12. Performance Sprint — Security Preservation

Assessment Outcome: 19-file multi-phase performance optimization sprint completed with zero security regressions. All 4 defense-in-depth layers preserved. 70/70 regression tests pass.

Key Security-Preserving Changes:

  • ✅ Fire-and-Forget Audit Logging: Audit context (userId, tenantId, path, method) captured before await resolve(event). Log written in detached Promise.resolve().then(...) — no blocking writes on the mutation hot path. SHA-256 chain integrity preserved (src/hooks/handle-audit-logging.ts).
  • ✅ Turbo Auth Skip Guard: __turboAuth flag prevents duplicate audit on turbo-served mutations without bypassing any auth checks. Turbo path still requires valid session + absolute-expiry auth cache (src/hooks/handle-turbo-get.ts).
  • ✅ Hook Timing Gate: HOOK_TIMING_ENABLED gate disables per-request instrumentation in production. No security hooks are skipped — only timing/tracing overhead is removed (src/hooks.server.ts).

13. August 2026 Security & Cookie Parity Hardening

Assessment Outcome: Full cookie flag alignment, non-fatal transaction isolation, and translation catalog integrity verification.

  • ✅ Logout Cookie Attribute Parity: clearSessionCookies() in src/routes/api/[...path]/handlers/auth.ts explicitly enforces matching path: "/", httpOnly: true, sameSite, and secure deletion flags aligned with setSessionCookie().
  • ✅ Transaction Read Isolation: PostgreSQL and MariaDB transaction-module.ts adapters respect rollback: false, ensuring non-fatal query misses do not abort outer transactions.
  • ✅ Media Stream Abort Teardown: Attached request.signal abort listener to destroyed file streams on HTTP client disconnects (src/routes/api/[...path]/handlers/media.ts).
  • ✅ Translation Catalog Integrity: Automated scripts/check-translations.ts checks locale JSON files in src/messages/ for key parity, empty values, and duplicate catalog keys.

Files Modified: src/routes/api/[...path]/handlers/auth.ts, src/databases/postgresql/transaction-module.ts, src/databases/mariadb/transaction-module.ts, src/routes/api/[...path]/handlers/media.ts, scripts/check-translations.ts.

Tests: tests/unit/routes/cookie-logout.test.ts, tests/unit/databases/transaction-read-isolation.test.ts.

  • ✅ Pre-Compressed Cache: Compression utilities operate on response bodies only — never touch authentication, authorization, or input validation. No bypass paths introduced.
  • ✅ Batch Relational Upserts: setMany + bulkUpdate use parameterized Drizzle ORM queries — no SQL injection risk. Tenant isolation preserved via existing tenantId scoping.
  • ✅ CDN Purge Fast-Path: Cached _cdnActive flag reduces invalidation overhead without changing the purge security model. Fire-and-forget Cloudflare API calls unchanged.
  • ✅ OpenAPI Pre-Warm: generateFullSpec() uses existing admin-gated endpoint. No new API surface. No permission bypass.

Security Tests: All 70 defense-in-depth regression tests pass across 4 files (defense-in-depth.test.ts, authentication.test.ts, authorization.test.ts, role-permission-access.test.ts). 0 regressions from any hook, cache, or adapter change.

Files Modified: src/hooks.server.ts, src/hooks/handle-compression.ts, src/hooks/handle-turbo-get.ts, src/hooks/handle-api-requests.ts, src/hooks/handle-audit-logging.ts, src/hooks/handle-local-context.ts, src/databases/cache/cache-service.ts, src/databases/core/relational-content.ts, src/databases/core/relational-system.ts, src/databases/postgresql/adapter-core.ts.


14. Threat Modeling & Gating Hardening

Assessment Outcome: Resolved critical race conditions in setup gating and corrected CSRF constraints for stateless external API integrations.

  • Setup Wizard DB-Level Fallback Gate: Implemented a deep unseeded database validation check in handleSeedDatabase and handleCompleteSetup (src/routes/api/[...path]/handlers/setup.ts). Before seeding or completion, the handler queries the database to check if any user with the admin role is already registered. If present, it throws a 403 SETUP_ALREADY_COMPLETE error. This blocks setup hijacking attempts even if config/private.ts is deleted or corrupted on the server filesystem.
  • CSRF Bypass for Header-Authenticated APIs: Updated SveltyCMS dispatcher in src/routes/api/[...path]/+server.ts to bypass CSRF token validation if the request is authenticated via user.isApiKey or user.isApiToken. Cookie-based admin dashboard sessions remain fully protected, while decoupled integration clients can execute write operations cleanly.
  • Vite Compilation OOM Protection: Configured chunk-splitting in vite.config.ts to output drizzle-orm in a dedicated bundle, preventing Vite/rollup compiler memory exhaustion on low-memory servers.

Security Tests: Added unit and integration tests confirming deep setup wizard gating and CSRF token bypass on Bearer auth. All 65 defense-in-depth regression tests pass successfully.

Files Modified: src/routes/api/[...path]/handlers/setup.ts, src/routes/api/[...path]/+server.ts, vite.config.ts.


15. Global Security Risk Audit & Pre-Auth Fixes (August 2026)

Assessment Outcome: A global static risk audit now runs on every commit, and the first full-src/ pass found and fixed two pre-authentication vulnerabilities.

New commit gate (.githooks/pre-commit → bun run risk:audit, ~5s):

  • scripts/security/scan-security-risk.ts — global static scan of the entire src/ tree (2917 files): SQL value/concat interpolation across all 4 adapters, MongoDB $where/$function/$accumulator, eval(/new Function( sinks, shell command interpolation, fs path interpolation, SSRF on request-derived URLs, DOM XSS sinks, regex interpolation, SvelteKit CSRF/cookie config. Guard-aware: identifier-validation files, path-guard helpers, escape idioms, and configured endpoints pass.
  • scripts/security/scan-secret-misuse.ts — hardcoded credentials / comparison backdoors.
  • scripts/security/slop-scanner.ts — XSS/RTL/security architecture rules.
  • bun audit — GitHub Advisory DB for the npm tree (SvelteKit/Svelte/Vite advisories included).
  • scripts/security/scan-osv.ts — OSV.dev global database (GHSA + NVD + 20+ feeds) queried from CycloneDX SBOM purls, 24h cache.

Found & fixed (both on the pre-authentication setup wizard):

  • Pre-auth DDL SQL injection — CREATE/DROP DATABASE IF EXISTS \${db.name}` interpolated operator-supplied database names into raw DDL without validation (src/routes/setup/setup.server.ts, setup-service.server.ts). Fixed with an assertSafeSqlIdentifier` allowlist so only plain identifiers can reach DDL.
  • Pre-auth shell interpolation — exec(\${pm} add ${packageName}`)built installer commands via a shell. Fixed withexecFile(pm, [“add”, packageName]) args-array calls (setup/+page.server.ts, setup.remote.ts`) — input can never reach a shell.

Tests: tests/unit/scripts/scan-security-risk.test.ts (SQL, Mongo, eval, shell, path, SSRF, XSS, regex, CSRF/cookie rules), tests/unit/scripts/scan-osv.test.ts (batch + parse), tests/unit/scripts/scan-secret-misuse.test.ts. The scanner reports 0 findings on the current codebase.

Files Modified: scripts/security/scan-security-risk.ts, scripts/security/scan-osv.ts, scripts/security/risk-audit.ts, .githooks/pre-commit, src/routes/setup/setup.server.ts, src/routes/setup/setup-service.server.ts, src/routes/setup/+page.server.ts, src/routes/setup/setup.remote.ts.

16. Pen-Test Hardening Batch (August 2026)

External penetration-test findings closed with regression coverage in tests/unit/security/login-hardening.test.ts, tests/unit/api/auth-2fa.test.ts, tests/unit/auth/totp/totp-encryption.test.ts, and tests/unit/security/egress-guard.test.ts:

  • Passwordless API login blocked (Critical) — AuthNamespace.login requires a password and handleLogin validates the body with loginFormSchema; email-only bodies return 401 without a session.
  • Passwordless 2FA verify closed (High) — verify2FA (form + API) accepts the TOTP code only together with a short-lived signed pending-2FA token minted after a successful password login, plus per-client rate limiting.
  • Blocked users rejected on the form path (High) — Auth.authenticate now checks user.blocked (parity with AuthNamespace.login).
  • API login enforces 2FA (High) — handleLogin returns requires2FA + pending token instead of a session; 2FA-enabled accounts get no session from the password step (AuthNamespace.login returns session: null outside the test harness).
  • GraphQL RBAC parity with REST (High) — users/media resolvers require user:read/media:read instead of any logged-in user.
  • Global-admin requires the admin role (High) — isGlobalAdmin = isAdmin(user) && !user.tenantId (previously any null-tenant user was treated as global admin).
  • Open redirect after login closed (High) — safeRedirect(input.redirect) on the login success path.
  • Trusted-device cookie rebound to IP/UA at skip time (Medium) — the 30-day 2FA-skip cookie is now validated against the presenting device’s fingerprint; this also fixed a latent parsing bug where a colon-bearing fingerprint (ip prefix + UA hash) bypassed the expiry check.
  • SSRF egress-guard coverage completed (Medium) — IPv6 ULA fc00::/7 is fully blocked (both fc00: and fd00: hextets), CGNAT 100.64.0.0/10 added, and bracketed IPv6 literals ([::1]) are now recognized as IPs instead of falling through to DNS.

17. Off-request patches (October 2026)

Helpers covered by unit tests. The write-path ownership check and the scheduler’s execute path are in the source and described in the security index. They do not have their own integration test yet.

  • Guarded filter and sort: stripGuardedFilter and stripGuardedSort drop a name the caller may not read, including a nested $and that becomes empty. Admins and unguarded schemas get the same object back. tests/unit/utils/guarded-query.test.ts.
  • CSV formula prefix: text cells that start with =, +, -, @, tab, or carriage return are prefixed. Numbers and booleans are not. tests/unit/utils/export-encode.test.ts.
  • Status-transition allow-list: acceptStatusTransitionJob keeps collectionId, entryId, targetStatus, and entryPath, overwrites scheduledBy with the authenticated user, and rejects an unknown task or status. tests/unit/services/scheduler-job.test.ts.
  • WebSocket Origin host: websocketOriginAllowed allows an empty Origin, matches the host (default port omitted), and rejects a foreign or malformed Origin. Same file.

No Backdoor Policy

SveltyCMS follows a strict no-backdoor policy with cryptographically-gated test support:

  • ✅ Cryptographically-gated test bypass — The __testBypass flag requires a timing-safe timingSafeEqual() cryptographic handshake against a runtime-generated secret, gated behind TEST_MODE/BENCHMARK environment variables. In production builds without these env vars, the bypass path is unconditionally blocked.
  • ✅ No static test API keys — Test secrets are generated at runtime via CSPRNG and never hardcoded.
  • ✅ No mocked permissions — RBAC tested with real role checks; the bypass only skips middleware when cryptographically verified in test environments.
  • ✅ No shortcuts — Encryption tested with real key derivation; auth paths excluded from turbo bypass, always using real credentials.
  • ✅ CI-enforced backdoor closure — Every PR runs a deploy build (bun run build without COMPILE_ALL_ADAPTERS) and verifies the testing handler is stripped via verify-prod-build-backdoor.ts --mode=deploy (CI task 07-deploy-backdoor). A live probe then confirms /api/testing returns 401/404 with no secret, wrong secret, and hardcoded secret.
  • ✅ Benchmark build gating — Bench builds (COMPILE_ALL_ADAPTERS=true) are verified to include the testing handler via verify:bench-build. The security-audit.ts script includes a dedicated --only=backdoor mode for OWASP A01 testing.
  • ✅ No backdoor in production — Normal bun run build strips /api/testing to a static 404 via testBackdoorStripperPlugin in vite.config.ts. The secret-misuse CI task (06-secret-misuse) scans all source files for hardcoded secrets on every PR.

Authentication & Sessions

Feature Test Location Status
User login tests/unit/hooks/authentication.test.ts ✅
User logout tests/unit/hooks/authentication.test.ts ✅
Session creation tests/integration/databases/auth-session-amr-contract.test.ts ✅
Session validation tests/unit/auth/sessions/session-manager.test.ts ✅
Session deletion tests/unit/auth/sessions/session-manager.test.ts ✅
Session cookie hardening tests/unit/auth/sessions/session-cookies.test.ts ✅
Session timeout handling tests/unit/hooks/authentication.test.ts ✅

| Password hashing (Argon2) | tests/unit/utils/security.test.ts | ✅ | | Password verification | tests/unit/utils/security.test.ts | ✅ | | Timing attack resistance | tests/unit/utils/security.test.ts | ✅ |

| OAuth/Google signup | tests/e2e/oauth-signup-firstuser.spec.ts | ✅ | | SAML authentication | tests/unit/auth/sso/saml.test.ts | ✅ |


Authorization & Access Control

Feature Test Location Status
Role-based access control (RBAC) tests/unit/hooks/authorization.test.ts ✅
Permission checking tests/unit/auth/roles/role-permission-access.test.ts ✅
Public route access tests/unit/hooks/authorization.test.ts ✅
Protected route redirect tests/unit/hooks/authorization.test.ts ✅
Admin-only endpoints tests/integration/api/token.test.ts ✅

| Unauthorized access blocked | tests/integration/api/collections.test.ts | ✅ | | Plugin sandbox isolation | tests/unit/plugins/sandbox.test.ts | ✅ |


Two-Factor Authentication (2FA)

Feature Test Location Status
TOTP setup tests/integration/api/auth-2fa.test.ts ✅
TOTP validation tests/integration/api/auth-2fa.test.ts ✅
Recovery codes tests/integration/api/auth-2fa.test.ts ✅
2FA enforcement for admins tests/integration/api/auth-2fa.test.ts ✅

Security Middleware

Feature Test Location Status
Content Security Policy (CSP) tests/unit/hooks/security-headers.test.ts ✅
HSTS header tests/unit/hooks/security-headers.test.ts ✅
X-Frame-Options tests/unit/hooks/security-headers.test.ts ✅
X-Content-Type-Options tests/unit/hooks/security-headers.test.ts ✅
Referrer-Policy tests/unit/hooks/security-headers.test.ts ✅
CORS configuration tests/unit/hooks/security-headers.test.ts ✅

| Rate limiting (IP-based) | tests/unit/hooks/rate-limit.test.ts | ✅ | | Rate limiting (session-based) | tests/unit/hooks/rate-limit.test.ts | ✅ |


Threat Detection & Prevention

Feature Test Location Status
SQL injection detection tests/unit/security/handle-waf-guard.test.ts ✅
XSS attack detection tests/unit/security/handle-waf-guard.test.ts ✅
Path traversal detection tests/unit/security/handle-waf-guard.test.ts ✅
Prototype pollution detection tests/unit/security/handle-waf-guard.test.ts ✅
Header splitting detection tests/unit/security/handle-waf-guard.test.ts ✅
Credentials in query strings tests/unit/security/threat-scan.test.ts ✅

Input Validation

Feature Test Location Status
Password strength validation tests/integration/routes/login/signup.test.ts ✅
Email format validation tests/integration/routes/login/signup.test.ts ✅
Password mismatch rejection tests/integration/routes/login/signup.test.ts ✅
Invalid OAuth data rejection tests/integration/routes/login/signup.test.ts ✅
Input sanitization docs/reference/security/richtext-security.mdx ✅

API Security

Feature Test Location Status
Fail-Closed Dispatcher src/routes/api/[...path]/+server.ts ✅
TBAC Role Isolation src/hooks/handle-authentication.ts ✅
Informative 403 UI user-management.svelte ✅

| Authentication required for APIs | tests/unit/hooks/api-requests.test.ts | ✅ | | Valid session required | tests/integration/api/collections.test.ts | ✅ | | Admin-only API access | tests/integration/api/token.test.ts | ✅ |

| CSRF protection | tests/integration/api/user.test.ts | ✅ | | Token creation/validation | tests/integration/api/token.test.ts | ✅ | | GraphQL auth enforcement | tests/integration/api/graphql.test.ts | ✅ | | GraphQL password field blocking | tests/integration/api/graphql.test.ts | ✅ |


Widget & Plugin Security

Feature Test Location Status
Widget sandbox isolation tests/unit/plugins/sandbox.test.ts ✅
Protected collection access denied tests/unit/plugins/sandbox.test.ts ✅
File upload restrictions docs/reference/security/widget-security.mdx ✅
XSS prevention in widgets docs/reference/security/widget-security.mdx ✅
Widget permission system tests/unit/hooks/authorization.test.ts ✅

E2E Security Tests

Feature Test Location Status
Login flow tests/e2e/routes/login/login.spec.ts ✅
Role-based access tests/e2e/routes/system/permission-enforcement.spec.ts ✅
User CRUD operations tests/e2e/routes/user/management.spec.ts ✅
Permission changes tests/e2e/routes/system/permissions.spec.ts ✅
OAuth signup flow tests/e2e/routes/login/oauth.spec.ts ✅
Password change tests/e2e/routes/user/profile.spec.ts ✅
Accessibility (axe) tests/e2e/routes/login/accessibility.spec.ts ✅

Test Commands

# Security-focused unit subset (hooks, authz, egress, WAF, SVG, scanners)
bun run test:security

# Middleware pipeline tests
bun run test:unit -- tests/unit/hooks/

# Authentication tests
bun run test:unit -- tests/unit/hooks/authentication.test.ts

# Authorization tests
bun run test:unit -- tests/unit/hooks/authorization.test.ts

# WAF / threat detection tests
bun run test:unit -- tests/unit/security/handle-waf-guard.test.ts tests/unit/security/threat-scan.test.ts

# Security headers tests
bun run test:unit -- tests/unit/hooks/security-headers.test.ts

# Integration API security tests (builds the harness first)
bun run test:integration

# E2E security tests
bun run test:e2e -- --grep="(Authentication)"

# Run all tests (unit + integration)
bun run test:all

Security Test Statistics

Category Test Count
Authentication 50+
Authorization 40+
Input Validation 80+
XSS Prevention 30+
Injection Prevention 40+
Security Headers 20+
File Upload Security 20+
Total 280+

Related Documentation

securitytestingcoverage
Was this page helpful?