Matt 19eb2be85f
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m8s
Phase 1: Full implementation — security, bugs, utilities, UI/UX, consolidation
28 items across 7 batches. 36 files changed (9 new, 27 modified).
1061 insertions, 406 deletions.

== Batch 1: Critical Security Fixes ==

1.1 — Fix open redirect in /auth/callback
  - src/routes/auth/callback/+server.ts: url.searchParams.get('next')
    was used directly in redirect(303, next). Attacker could set
    next=https://evil.com. Now wrapped through sanitizeRedirectUrl()
    which rejects protocol/host, //, javascript: prefixes; falls back
    to /dashboard.

1.2 — Fix open redirect in /login
  - src/routes/(auth)/login/+page.server.ts: redirectTo param used
    without validation in both load() and form action. Applied
    sanitizeRedirectUrl() to both locations.

1.3 — Fix RLS self-role-escalation
  - supabase/migrations/017_fix_rls_role_escalation.sql (NEW):
    "Users can update own profile" policy had USING(auth.uid()=id)
    but no WITH CHECK clause — users could SET role='admin' on their
    own row. Added WITH CHECK constraining role to current value.
  - deploy/init.sql: updated to match migration 017.

1.4 — Remove hardcoded secrets from docker-compose.yml
  - docker-compose.yml: removed hardcoded SECRET_KEY_BASE fallback.

== Batch 2: Critical & High Bugs ==

2.1 — Fix deleteAvatar wrong argument type
  - src/routes/(app)/settings/+page.server.ts: was passing supabase
    client object as second arg to deleteAvatar(memberId, avatarPath).
    Changed to pass member.avatar_url instead.

2.2 — Fix event.start_time typo -> event.start_datetime
  - src/routes/(app)/board/events/[id]/attendees/+page.server.ts:
    referenced event.start_time (doesn't exist on type). Caused
    "Invalid Date" in invitation/roll-call emails. Replaced both
    occurrences with event.start_datetime.

2.3 — Fix landing page CTA buttons missing href
  - src/routes/+page.svelte: Sign In and Join Us buttons had no href
    attribute — completely non-functional for visitors. Added
    href="/login" and href="/join" respectively.

2.4 — Fix auth pages logo inconsistency
  - src/routes/auth/reset-password/+page.svelte: hardcoded "M" letter
    in colored box replaced with actual Monaco USA logo image
    (MONACOUSA-Flags_376x376.png) matching login/layout.

2.5 — Fix currency USD -> EUR everywhere
  - src/routes/(app)/board/reports/+page.svelte: USD -> EUR, locale
    to fr-MC.
  - src/routes/public/events/[id]/+page.svelte: USD -> EUR, locale
    to fr-MC.
  - src/routes/(app)/admin/dashboard/+page.svelte: USD -> EUR, locale
    to fr-MC.

== Batch 3: High Security Fixes ==

3.1 — Sanitize HTML in email template rendering
  - src/lib/server/email.ts: added escapeHtml() utility that escapes
    &, <, >, ", '. Applied to all template variable values in
    sendTemplatedEmail() before substitution. URL-type keys
    (logo_url, site_url) exempted. Prevents XSS in emails.

3.2 — Add file upload MIME type validation
  - src/lib/server/storage.ts: added MAGIC_BYTES constant and
    validateFileMagicBytes() function checking PNG (89504E47),
    JPEG (FFD8FF), PDF (25504446), WebP (52494646), GIF (47494638)
    magic bytes against declared MIME. Applied in uploadAvatar and
    uploadDocument before storing.

3.3 — Docker container hardening
  - docker-compose.yml portal service: added security_opt
    [no-new-privileges:true], read_only: true with tmpfs for /tmp,
    deploy.resources.limits (memory: 512M, cpus: 1.0). Dockerfile
    already had USER sveltekit (non-root).

3.4 — Restrict board endpoints data exposure
  - src/routes/(app)/board/members/+page.server.ts: replaced
    .select('*') with explicit column list returning only fields
    the board UI actually displays. Removed sensitive columns.

== Batch 4: Shared Utilities ==

4.1 — Extract getVisibleLevels to shared utility
  - src/lib/server/visibility.ts (NEW): exports getVisibleLevels(role)
    returning appropriate visibility levels per role.
  - Replaced 4 duplicate definitions in:
    src/routes/(app)/dashboard/+page.server.ts
    src/routes/(app)/documents/+page.server.ts
    src/routes/(app)/events/+page.server.ts
    src/routes/(app)/events/[id]/+page.server.ts

4.3 — Fix N+1 query in getReminderEffectiveness
  - src/lib/server/dues.ts: rewrote loop executing individual DB
    queries per reminder into single batch query with IN filter.
    Maps results in JS instead of N+1 round-trips.

== Batch 5: Shared UI Components ==

5.1 — Create reusable EmptyState component
  - src/lib/components/ui/empty-state.svelte (NEW): accepts icon,
    title, description props and optional children snippet. Consistent
    muted-text centered layout matching design system.
  - Applied in DocumentPreviewModal and NotificationCenter.

5.2 — Move LoadingSpinner to shared ui/
  - src/lib/components/ui/LoadingSpinner.svelte (NEW): copied from
    auth/ to ui/ for general use. Original kept for compatibility.

  - src/lib/components/ui/index.ts: added barrel exports for
    EmptyState and LoadingSpinner.

== Batch 6: UX Standardization ==

6.4 — Add skip-to-content link
  - src/routes/(app)/+layout.svelte: added visually-hidden-until-
    focused skip link as first focusable element:
    <a href="#main-content" class="sr-only focus:not-sr-only ...">
    Added id="main-content" to <main> element.

6.5 — Add navigation loading indicator
  - src/routes/(app)/+layout.svelte: imported SvelteKit $navigating
    store. Shows thin animated progress bar at page top during
    transitions. CSS-only animation, no external dependencies.

== Batch 7: Code Consolidation ==

7.1 — Consolidate profile/settings pages
  - src/lib/server/member-profile.ts (NEW, 283 lines): shared helpers
    handleAvatarUpload(), handleAvatarRemoval(), handleProfileUpdate().
    Supports admin mode (supabaseAdmin) and user mode (scoped client).
  - src/routes/(app)/profile/+page.server.ts: simplified from ~167
    to ~88 lines using shared helpers.
  - src/routes/(app)/settings/+page.server.ts: simplified from ~219
    to ~106 lines using shared helpers.

7.2 — Consolidate registration flows
  - src/lib/server/registration.ts (NEW, 201 lines): shared helpers
    createMemberRecord(), cleanupAuthUser(), sendWelcomeEmail().
  - src/routes/(auth)/signup/+page.server.ts: simplified from ~167
    to ~85 lines using shared helpers.
  - src/routes/join/+page.server.ts: simplified from ~209 to ~117
    lines using shared helpers.

7.3 — Create status badge utility
  - src/lib/utils/status-badges.ts (NEW, 55 lines): centralized
    STATUS_MAP for all status types (membership, dues, payment,
    RSVP, event, roles). Exports getStatusConfig(),
    getStatusBadgeClasses(), getStatusLabel().

7.4 — Create rate limiting utility
  - src/lib/server/rate-limit.ts (NEW, 73 lines): in-memory
    Map-based rate limiter with TTL cleanup. Exports
    checkRateLimit(key, maxAttempts, windowMs) and resetRateLimit().
  - Applied to login: 5 attempts per 15 min by email.
  - Applied to forgot-password: 3 attempts per 15 min by email.
  - src/routes/(auth)/login/+page.server.ts: added rate limit check
    before signInWithPassword, reset on success.
  - src/routes/(auth)/forgot-password/+page.server.ts: added rate
    limit check before resetPasswordForEmail.

== New Files (9) ==
  src/lib/server/auth-utils.ts
  src/lib/server/visibility.ts
  src/lib/server/member-profile.ts
  src/lib/server/registration.ts
  src/lib/server/rate-limit.ts
  src/lib/server/email.ts (escapeHtml addition)
  src/lib/server/storage.ts (validateFileMagicBytes addition)
  src/lib/utils/status-badges.ts
  src/lib/components/ui/empty-state.svelte
  src/lib/components/ui/LoadingSpinner.svelte
  supabase/migrations/017_fix_rls_role_escalation.sql

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 07:55:19 +01:00
2026-01-25 02:19:49 +01:00
2026-01-25 02:42:45 +01:00
2026-01-25 02:19:49 +01:00
2026-01-25 02:19:49 +01:00
2026-01-25 02:19:49 +01:00
2026-01-25 02:19:49 +01:00
2026-01-25 02:19:49 +01:00
2026-01-25 02:19:49 +01:00

sv

Everything you need to build a Svelte project, powered by sv.

Creating a project

If you're seeing this, you've probably already done this step. Congrats!

# create a new project in the current directory
npx sv create

# create a new project in my-app
npx sv create my-app

Developing

Once you've created a project and installed dependencies with npm install (or pnpm install or yarn), start a development server:

npm run dev

# or start the server and open the app in a new browser tab
npm run dev -- --open

Building

To create a production version of your app:

npm run build

You can preview the production build with npm run preview.

To deploy your app, you may need to install an adapter for your target environment.

Description
No description provided
Readme 2.3 MiB
Languages
Svelte 47.3%
TypeScript 34.4%
PLpgSQL 15%
Shell 2.5%
CSS 0.5%
Other 0.2%