fix(auth/security): harden against missing or null policy entries (#3076)#3077
fix(auth/security): harden against missing or null policy entries (#3076)#3077WhoamiI00 wants to merge 2 commits into
Conversation
The /auth/security page assumes every policy in the listPolicies()
response is non-null and has a $id, and that the policies it indexes
into the result map always exist. After a 1.8.1 -> 1.9.0 upgrade,
listPolicies may return entries that are absent for policies not yet
present in the migrated project, which causes:
- +page.ts: TypeError when .map((policy) => [policy.$id, policy])
encounters a null entry.
- passwordPolicies.svelte: TypeError accessing .total / .enabled
on an undefined policy in onMount and in the hasChanges derived.
Changes:
- +page.ts: filter out null entries before building policiesById.
- passwordPolicies.svelte: type the three password-policy props as
| undefined to match the (already-possible) runtime shape, and
use optional chaining + nullish-coalescing fallbacks everywhere
the policies are read.
Related to appwrite#3076. This addresses the same class of null-safety bug in
the same load path; the exact failing call site reported in appwrite#3076
points into minified JS without source maps, so this is defensive
hardening rather than a confirmed root-cause fix.
Greptile SummaryThis PR hardens all seven Auth → Security components against
Confidence Score: 5/5Safe to merge — all crash paths identified in the previous review round are now guarded, and no new error paths are introduced. The changes are narrowly scoped: null filters in the load function and optional-chaining wrappers in each component. Every direct property read on a potentially-absent policy is guarded, and the disabled-button logic in each component ensures users cannot accidentally persist a default-fallback value. updateSessionsLimit.svelte — minor cosmetic edge case when sessions policy is absent. Important Files Changed
Reviews (2): Last reviewed commit: "fix(auth/security): extend null-safety t..." | Re-trigger Greptile |
Addresses Greptile review on PR appwrite#3077: the prior commit only hardened +page.ts and passwordPolicies.svelte, leaving five sibling components that read policy fields in top-level $state / $derived initializers which crash before render when a policy is absent. Changes: - +page.ts: widen the eight cast-as policy return types to `... | undefined` so downstream components see the real runtime shape. The three email-policy return values keep their fallback defaults and remain non-nullable. - sessionSecurity.svelte, updateUsersLimit.svelte, updateSessionLength.svelte, updateSessionsLimit.svelte, updateMembershipPrivacy.svelte: type the `policy` prop as `... | undefined`, replace direct field reads with optional chaining + nullish-coalescing defaults (false for booleans, 0 for numeric totals/durations). This makes the entire /auth/security page tolerant of a partially migrated 1.8.1 -> 1.9.0 backend that returns only a subset of policies. Verification: `bun run check` 0 errors, `bun run lint` clean on the security folder, `bun run test:unit` 235/235 pass. Related to appwrite#3076.
|
Good catch, you're right — the first commit only fixed two files and left the others reading policy fields in top-level Pushed c9bfd4d. The five sibling components ( Whole page should survive a partial migration now. Check/lint/tests still clean. |
What
Hardens the load function and
passwordPoliciescomponent for the/auth/securityroute againstnullor missing policy entries returned byproject.listPolicies().Why
After upgrading a self-hosted instance from
1.8.1to1.9.0, opening Auth → Security crashes with:— reported in #3076.
The current code makes two unsafe assumptions:
src/routes/.../auth/security/+page.tscalls.map((policy) => [policy.$id, policy])on thepoliciesarray, so a singlenullentry throws before the page can render.passwordPolicies.svelteaccesseshistoryPolicy.total,dictionaryPolicy.enabled, andpersonalDataPolicy.enableddirectly, but+page.tsalready casts the lookup result as the policy type without verifying it is defined. After a migration from1.8.1→1.9.0it is possible for some policies not to be present yet, in which case those reads throw too.What changed
+page.ts—.filter((policy) => policy != null && policy.$id != null)before.map.passwordPolicies.svelte— typed the three policy props as… | undefined(to match the runtime reality), used optional chaining + nullish-coalescing defaults inonMountand in thehasChangesderived.Verification
bun run check— 0 errors, no new warnings.bun run lint— 0 errors.bun run test:unit— 235/235 pass.bun run dev) loads with no regression.Caveats
The original stack trace in #3076 (
null.groupinsideArray.filter) does not directly map to either site changed here — it points into minified JS without source maps. None of the policy models in@appwrite.io/consoleexpose agroupfield, so the exact failing call site is somewhere I could not identify from the source alone (likely a code path I missed, or inside a dependency).This PR fixes a same-class null-safety bug in the same load path that is reachable from the same user action. It is defensive hardening rather than a confirmed root-cause fix. If maintainers can share an unminified stack from a reproducing environment, I'm happy to follow up with a targeted patch for the precise failing site.
Related to: #3076