Commit graph

222 commits

Author SHA1 Message Date
Johannes Millan
b05b0400ed feat: add EU data hosting badge with SVG image to login page 2026-01-22 16:40:34 +01:00
Johannes Millan
5be9aea701 feat(supersync): add EU data hosting badge to login page
Add a trust badge displaying EU flag stars and "Data hosted in EU" text to the SuperSync server login/register page. The badge uses official EU flag colors (blue #039 and gold #fc0) with the circle of 12 stars from public domain SVG.
2026-01-22 15:36:57 +01:00
Johannes Millan
5b09b9a88e chore: remove internal GDPR compliance documentation
Internal compliance documentation has been moved to a private location.
These documents contain sensitive operational procedures and security
analysis that should not be public.

Files moved:
- GDPR compliance analysis
- Incident response playbooks
- Data subject request procedures
- DPIA screening decisions
- Records of processing activities
- Infrastructure verification documents
2026-01-22 15:36:57 +01:00
Johannes Millan
36f91a845f docs(compliance): update operational procedures for encryption disclosure
Update incident response, data subject request, and DPIA procedures to
accurately reflect that database encryption at rest is NOT implemented
for non-E2EE users.

Changes:
- INCIDENT-RESPONSE-PLAYBOOK.md: Clarify E2EE is optional throughout,
  add physical server compromise scenarios, update risk assessments to
  differentiate E2EE vs non-E2EE users, document encryption gap in
  prevention measures
- DATA-SUBJECT-REQUEST-PROCEDURES.md: Add encryption status disclosure
  to access responses, clarify data export formats, add security notice
  about unencrypted storage for non-E2EE users
- DPIA-SCREENING-DECISION.md: Document encryption gap as additional
  consideration, update risk level to LOW-MEDIUM, add encryption gap
  to conclusion and re-assessment triggers

All procedures now consistently acknowledge 85% compliance score and
risk variance based on E2EE usage, while maintaining that DPIA is not
required per Art. 35.
2026-01-22 15:36:57 +01:00
Johannes Millan
0bbced2b08 docs(compliance): document encryption risk and update privacy policy
Update GDPR compliance documentation to accurately reflect that database
encryption at rest is NOT implemented for non-E2EE users. This critical
finding required:

- Update compliance score from 92% to 85% (10% deduction for encryption gap)
- Add comprehensive encryption disclosure to privacy policies (German & English)
- Document risk: unencrypted PostgreSQL data on disk
- Update GDPR analysis with compensating controls (optional E2EE)
- Revise Records of Processing Activities with encryption status
- Add context to Alfahosting verification tracker

Changes prioritize GDPR transparency by honestly documenting security
limitations rather than overstating compliance.
2026-01-22 13:34:54 +01:00
Johannes Millan
11d6dc1738 docs(compliance): remove outdated VPS verification files 2026-01-22 13:34:54 +01:00
Johannes Millan
2c429d77b9
Merge pull request #6087 from super-productivity/dependabot/npm_and_yarn/multi-982254d4fd
chore(deps): bump nodemailer and @types/nodemailer
2026-01-21 15:26:53 +01:00
dependabot[bot]
8c64f6099f
chore(deps): bump nodemailer and @types/nodemailer
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) and [@types/nodemailer](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/nodemailer). These dependencies needed to be updated together.

Updates `nodemailer` from 7.0.11 to 7.0.12
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v7.0.11...v7.0.12)

Updates `@types/nodemailer` from 7.0.4 to 7.0.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/nodemailer)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 7.0.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: "@types/nodemailer"
  dependency-version: 7.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-21 13:43:30 +00:00
dependabot[bot]
45265f915a
chore(deps): bump zod from 4.1.13 to 4.3.5
Bumps [zod](https://github.com/colinhacks/zod) from 4.1.13 to 4.3.5.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.1.13...v4.3.5)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.3.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-21 13:41:42 +00:00
Johannes Millan
ae40f0ba2e feat(sync): add comprehensive timeout handling for large operations
Implement coordinated timeout strategy across all layers:

**Client-side (90s total):**
- HTTP requests: 75s timeout with AbortController
- Sync wait timeout: 90s (exceeds all server timeouts)
- Restore service: Retry logic for network errors (2s, 4s backoff)
- Better error messages for timeout scenarios

**Server-side:**
- Caddy proxy: 85s timeout (exceeds Fastify)
- Fastify server: 80s request timeout (exceeds DB timeout)
- Database operations: 60s (unchanged)

**Rationale:**
Each timeout layer exceeds the one below it, allowing inner operations
to complete and report errors properly. The 90s client timeout ensures
large operations (snapshot generation, imports) can complete without
premature abortion.

**Monitoring:**
- Log slow requests >30s for visibility
- Detailed error logging with duration tracking
- Android WebView timeout support via CapacitorHttp
2026-01-20 17:07:24 +01:00
Ivan Kalashnikov
263495b8cd feat: update migration functions to support splitting operations into multiple results 2026-01-19 13:58:27 +07:00
Johannes Millan
2f7a00371a fix(supersync): improve GDPR compliance in legal documents
- Remove placeholder address text from privacy policies (DE/EN)
- Expand HTML privacy policy with full GDPR disclosures:
  - Legal bases (Art. 6), data subject rights (Art. 15-22)
  - Supervisory authority, retention periods, DPA info
  - Cookies/tracking and automated decision-making sections
- Align HTML terms with German ToS:
  - Add proper termination notice periods (2 weeks/good cause)
  - Add 6-week notice for ToS amendments
  - Add consumer withdrawal rights (14 days)
  - Add ODR platform link and jurisdiction info
2026-01-15 12:48:18 +01:00
Johannes Millan
cee12a444a fix(sync): pass op.id to uploadSnapshot to prevent ID mismatch
When uploading BACKUP_IMPORT via uploadSnapshot(), the client's op.id
was not sent to the server. The server would generate its own ID,
causing the client to not recognize the operation when downloaded later.
This led to data loss as the old backup state would be re-applied.

Changes:
- Add opId parameter to uploadSnapshot() interface
- Pass op.id from operation-log-upload.service.ts
- Send opId in SuperSync API request payload
- Server uses client's opId instead of generating new one
- Add E2E test to verify ID matching

The fix is backwards compatible - legacy clients without opId still work
as the server falls back to uuidv7() when opId is not provided.
2026-01-14 21:57:08 +01:00
Johannes Millan
e7e693e78f fix(sync-server): add rate limiting to page endpoints
Add explicit per-route rate limits to /verify-email and /magic-login
endpoints to address CodeQL security alerts. These endpoints previously
relied only on global rate limiting.
2026-01-13 18:26:45 +01:00
Johannes Millan
187bfaf24c fix(docker): upgrade npm to v11 to match lockfile version
npm 10.9.4 bundled with node:22-alpine handles optional peer
dependencies differently than npm 11, causing build failures
when resolving vitest peer dep from @angular-devkit/build-angular.
2026-01-10 19:08:38 +01:00
Johannes Millan
6501950760 fix: resolve build issues and update E2E tests for sync import conflict
- Fix TypeScript error in passkey.ts (Buffer to Uint8Array conversion)
- Update Dockerfile.test to use npm install instead of npm ci for workspace sync
- Update E2E test to not setup sync before import
- Add additional unit tests for sync-wrapper.service

Note: E2E tests for sync import conflict dialog need further work due to
automatic upload of SYNC_IMPORT when sync is enabled. The core dialog
functionality is implemented and unit tested.
2026-01-10 18:48:47 +01:00
Johannes Millan
f9620d4f37 feat(sync): add sync import conflict resolution dialog
When all remote operations are filtered due to a local SYNC_IMPORT
(e.g., user changed sync account), show a dialog offering three options:
- Use My Data: Push local state to server (forceUploadLocalState)
- Use Server Data: Accept server state (forceDownloadRemoteState)
- Cancel: Abort sync for now

Changes:
- Extend SyncImportFilterService to return filteringImport operation
- Extend RemoteOpsProcessingService to return filter metadata
- Create DialogSyncImportConflictComponent with Material Dialog
- Create SyncImportConflictDialogService to open the dialog
- Integrate dialog trigger into OperationLogSyncService
- Add translation keys to en.json
- Add comprehensive unit tests (86 tests passing)
- Add E2E test for dialog flow

Also increases maxKeys threshold in super-sync-server validation
to 500K to handle archives with 300K+ keys.
2026-01-10 18:31:37 +01:00
Johannes Millan
038a722ed8 style: apply prettier formatting 2026-01-10 17:09:14 +01:00
Johannes Millan
db41055feb feat(supersync): add dark mode with WCAG AA compliant colors
Add automatic dark mode support using prefers-color-scheme media query:

- Dark backgrounds: #1a1a1a (page), #2d2d2d (cards)
- Light text: #e5e5e5 (primary), #a3a3a3 (secondary)
- Adjusted primary color for dark: #4db8e8 (7.74:1 contrast)
- Dark variants for warning/success/error message boxes
- Button backgrounds kept darker for white text contrast

Also add consistent link styling for Terms/Privacy links:
- Normal, visited, and hover states use primary color
- Works in both light and dark modes

All colors verified to meet WCAG AA 4.5:1 minimum contrast.
2026-01-06 13:49:50 +01:00
Johannes Millan
e80850fb1e fix(supersync): improve color contrast for WCAG 2.2 compliance #5903
Update CSS color variables to meet WCAG AA 4.5:1 contrast ratio:

- --primary: #0c96e2 → #0077b6 (4.87:1 on white)
- --text-light: #666666#595959 (7.00:1 on white)
- --success: #4caf50 → #2e7d32 (5.13:1 white on bg)
- --error: #f44336 → #c62828 (5.62:1 white on bg)
- .warning-box: #856404 → #6d5200 (6.63:1 on #fff3cd)

All text elements now meet WCAG AA accessibility requirements.
2026-01-06 13:08:46 +01:00
Johannes Millan
b01afbfcd1 fix(sync): address code review findings for operation-logs
Security:
- Add rate limiting (10 req/5min) to GET /snapshot endpoint
- Prevents DoS via CPU-intensive snapshot generation

Consistency:
- Add 30s timeout to download transaction (matches other sync transactions)

Test robustness:
- Fix weak encryption test - always assert error on wrong password
- Update lww-update.meta-reducer tests to use OpLog instead of console

Defensive coding:
- Add entity-not-found warnings in LWW meta-reducer for sync race conditions
- Log when project/tag/parent task deleted before LWW update arrives

Code quality:
- Standardize logging to OpLog in lww-update.meta-reducer
- Document LWW action types as intentionally not in ActionType enum
- Create e2e-constants.ts for centralized E2E test timeouts/delays
- Extract createProjectReliably helper to supersync-helpers.ts (DRY)
2026-01-06 11:42:47 +01:00
Johannes Millan
f0f536671b test(server): fix testing gaps and failing tests
1. Add DELETE /api/sync/data route tests to sync.routes.spec.ts:
   - Test successful deletion returns { success: true }
   - Test 401 without authorization
   - Test uploading new data works after reset

2. Fix passkey.spec.ts failures (4 tests):
   - Add missing passkey.findUnique mock for credential lookup
   - Update test expectations for discoverable credentials
     (no allowCredentials - implementation changed)

3. Fix password-reset-api.spec.ts failures (12 tests):
   - Exclude from vitest - tests routes that don't exist
   - Server uses passkey/magic link auth, not password auth

All 412 tests now pass.
2026-01-05 17:39:17 +01:00
Johannes Millan
e641b89187 test(server): add unit tests for deleteAllUserData (Reset Account)
Add comprehensive unit tests for the reset account functionality:

- Test that deleteAllUserData removes all operations for a user
- Test that new operations can be uploaded after reset
- Test that resetting one user doesn't affect other users' data

Also adds missing userSyncState.deleteMany mock to the test setup.
2026-01-05 15:47:43 +01:00
Johannes Millan
f9635423d6 feat(server): add Account Settings page with reset and delete options
Add a dedicated Account Settings page to the SuperSync server web interface:

- Move Reset Account and Delete Account buttons from token display to
  separate Account Settings page for better UX
- Reset Account clears all synced data but keeps account active
- Delete Account permanently removes account and all data
- Add E2E tests for account reset functionality

Closes #5848
2026-01-05 12:54:53 +01:00
Johannes Millan
cbeecfd39a fix(sync): multi-tab vector clock staleness + shared code extraction
Bug fix:
- Fix vector clock cache staleness in multi-tab scenarios by clearing
  cache when acquiring operation write lock. Each browser tab has its
  own in-memory cache, so Tab B's cache could be stale if Tab A wrote
  while Tab B was waiting for the lock.

Shared code extraction (client/server consistency):
- Extract vector clock comparison to @sp/shared-schema
  - Client wraps shared impl with null handling
  - Server imports directly from shared
- Extract entity types to @sp/shared-schema
  - Single source of truth for ENTITY_TYPES array
  - Removes duplicated "must match" comments

Files:
- packages/shared-schema/src/vector-clock.ts (new)
- packages/shared-schema/src/entity-types.ts (new)
- src/app/op-log/store/operation-log-store.service.ts (cache clear)
- src/app/op-log/capture/operation-log.effects.ts (call cache clear)
2026-01-03 18:05:11 +01:00
Johannes Millan
9ddced5648 feat(sync-server): sync email input across all auth forms
- Email is now shared between login, register, and lost passkey forms
- Email persists to localStorage across page reloads
- Typing in any email field updates all others in real-time
2026-01-03 15:50:15 +01:00
Johannes Millan
868ed71c4a fix(sync-server): add migration script for passkey credential IDs
Existing passkeys were stored with double-encoded credential IDs due to
a bug where SimpleWebAuthn's credentialInfo.id (UTF-8 bytes of base64url
string) was stored directly instead of being decoded to raw bytes first.

This migration script converts existing passkeys from the old format
(ASCII bytes of base64url string) to the correct format (raw credential
ID bytes).

Run with: npx ts-node prisma/migrations/migrate-passkey-credentials.ts
2026-01-03 14:44:18 +01:00
Johannes Millan
a57a197d44 fix(sync-server): fix double encoding of passkey credential ID
SimpleWebAuthn's credentialInfo.id is a Uint8Array containing the
base64url-encoded credential ID as UTF-8 bytes, NOT the raw credential
ID bytes. We were storing these ASCII bytes directly, then encoding
them again as base64url during lookup.

During login, the browser sends the original base64url string, which
we decoded to raw bytes - but this didn't match the double-encoded
value stored in the database.

The fix decodes the base64url string from credentialInfo.id before
storing:
1. Convert Uint8Array to UTF-8 string (the base64url string)
2. Decode base64url to get raw credential ID bytes
3. Store raw bytes in database

Now login correctly looks up by raw credential ID bytes.
2026-01-03 13:32:37 +01:00
Johannes Millan
c07d0a4588 fix(sync-server): look up passkey by credential ID instead of email
With discoverable credentials, the user can select any passkey for
this RP. We need to look up the passkey by the credential ID that
comes back from the browser, not by the email the user entered.

This fixes the 401 error when the user's passkey credential ID
doesn't match what we expected based on email lookup.
2026-01-03 13:20:34 +01:00
Johannes Millan
0f8d2cbc42 chore: code review improvements for operation-logs branch
Phase 1 - Code Quality:
- Add clarifying comment in lww-update.meta-reducer.ts explaining
  Date.now() choice for LWW conflict resolution
- Document multi-instance deployment limitations in README.md
  (passkey challenges, snapshot locks)

Phase 2 - Testing:
- Add TODAY_TAG selector edge case tests (deleted/archived tasks)

Phase 3 - Minor Polish:
- Add createValidationErrorResponse() helper to hide Zod validation
  details in production (sync.routes.ts)
- Add database index @@index([userId, opType]) for faster restore
  point queries
2026-01-03 13:17:42 +01:00
Johannes Millan
374ba9873a fix(sync-server): use discoverable credentials for passkey login
Instead of providing allowCredentials (which requires the browser to
match specific credential IDs), let the browser discover all resident
credentials for this RP automatically.

This matches how passkeys.io works and should fix the "no passkeys
available" issue on mobile.
2026-01-03 13:16:01 +01:00
Johannes Millan
f1eab1d8ca debug(sync-server): add detailed credential ID logging for passkey
Log the full credential ID (both hex and base64url) during:
- Registration: to see exactly what's being stored
- Login: to see exactly what's being retrieved from DB

This will help debug why passkeys aren't being found during login.
2026-01-03 13:01:34 +01:00
Johannes Millan
253e55cd2c fix(sync-server): use properly minified SimpleWebAuthn browser v13.2.2
The previous version was reformatted by prettier into 362 lines.
This version is the original minified bundle with version header.
2026-01-03 12:32:50 +01:00
Johannes Millan
d5e196b82b fix(sync-server): use v13 API format in passkey recovery page
The recovery page was using the old SimpleWebAuthn API format:
  startRegistration(options)

Updated to use the v13 format:
  startRegistration({ optionsJSON: options })

This was causing "nothing happens" when clicking Register New Passkey.
2026-01-03 12:14:47 +01:00
Johannes Millan
1fad43d198 fix(sync-server): update SimpleWebAuthn browser library to v13.2.2
The server was updated to @simplewebauthn/server v13.2.2 but the
browser library was still at pre-v13. The v13 API expects options
wrapped as { optionsJSON: options }, causing allowCredentials to be
lost during login with the old library.

This caused "no passkeys found" errors when users tried to login
after registering.
2026-01-03 12:08:04 +01:00
Johannes Millan
3a7191d5f5 fix(sync-server): require discoverable credentials for passkeys
Change residentKey from 'preferred' to 'required' for passkey registration.
Discoverable credentials (resident keys) are required for synced passkeys
like Google Password Manager to properly store and retrieve the passkey.

Also add detailed logging of registration options for debugging.
2026-01-02 22:07:03 +01:00
Johannes Millan
4cee57db14 fix(sync-server): fix SimpleWebAuthn v13 type imports
Import types from @simplewebauthn/server instead of deprecated
@simplewebauthn/types package which is no longer supported in v13.
2026-01-02 22:00:10 +01:00
Johannes Millan
f143210cd9 fix(sync-server): update SimpleWebAuthn to v13 and add debug logging
- Update @simplewebauthn/server from v11 to v13.2.2 for compatibility
- Add debug logging for WebAuthn config and authentication options
- Browser library also needs to be updated to v13.2.2 (manual step)

The version mismatch between server and browser libraries may have been
causing passkey registration/login issues.
2026-01-02 21:58:49 +01:00
Johannes Millan
4ca44fd3d3 add logs 2 2026-01-02 21:23:35 +01:00
Johannes Millan
92b8aa8178 add logs 2026-01-02 21:16:03 +01:00
Johannes Millan
f09d7c51b4 fix(sync-server): allow passkeys without user verification
The @simplewebauthn/server library defaults to requireUserVerification: true,
but our registration options set userVerification: 'preferred'. This mismatch
caused passkey registration and login to fail with:
"User verification was required, but user could not be verified"

Add requireUserVerification: false to all verification calls to match
the 'preferred' setting in registration/authentication options.
2026-01-02 21:07:37 +01:00
Johannes Millan
4843d2afb7 fix(sync-server): add body to DELETE account request
The deleteAccount fetch request sent Content-Type: application/json
but no body, causing Fastify's JSON parser to fail with 400 Bad Request.

Add empty JSON body to match the pattern used by refreshToken.
2026-01-02 21:02:34 +01:00
Johannes Millan
ab648b2b6d fix(sync-server): add WebAuthn env vars to docker-compose 2026-01-02 20:55:48 +01:00
Johannes Millan
2ec5e08df4 fix(sync): address code review findings for operation log
- Add startup warning in passkey.ts when running with in-memory
  challenge storage in production (multi-instance deployments)
- Use LOCK_NAMES.OPERATION_LOG constant in stale-operation-resolver
  instead of hardcoded 'sp_op_log' string
- Add client-side validation for empty/whitespace entityId to match
  server-side validation and provide earlier feedback
2026-01-02 18:13:22 +01:00
Johannes Millan
3a179df3cc fix(sync): prevent data loss when multiple clients join SuperSync
Fixes critical bug where multiple clients with existing data could each
create a SYNC_IMPORT when enabling SuperSync, causing data loss.

Root cause: When clients had local data (from legacy migration), each
would detect "server needs migration" and create competing SYNC_IMPORTs.
The last one uploaded would invalidate all prior data.

Fixes:
- Server rejects duplicate SYNC_IMPORT with SYNC_IMPORT_EXISTS (409)
- Client double-checks server is empty before creating SYNC_IMPORT
- Client merges all local op clocks into SYNC_IMPORT's vector clock
- hasSyncedOps() excludes MIGRATION/RECOVERY ops from check
- Upload service gracefully handles SYNC_IMPORT_EXISTS rejection

Tests added:
- ServerMigrationService: double-check and clock merging (5 tests)
- OperationLogStoreService: hasSyncedOps() MIGRATION exclusion (7 tests)
- OperationLogUploadService: SYNC_IMPORT_EXISTS handling (5 tests)
- E2E: Multiple clients with existing data merge correctly
2026-01-02 17:51:12 +01:00
Johannes Millan
9c0a728ef4 feat(sync-server): replace password auth with passkey + magic link
Authentication changes:
- Add passkey (WebAuthn) as primary login method
- Add email magic link as fallback for devices without passkey support
- Remove password-based authentication entirely

New features:
- Passkey registration and login via @simplewebauthn/server
- Magic link login with 15-minute expiry tokens
- Passkey recovery via email link
- Self-hosted simplewebauthn-browser.min.js for reliability

Database changes:
- Add Passkey model for WebAuthn credentials
- Add PasskeyChallenge model for registration/auth challenges
- Add loginToken and loginTokenExpiresAt fields for magic links
- Add passkeyRecoveryToken fields for passkey recovery

UI changes:
- Login form: email + "Login with Passkey" + "Send Login Link"
- Register form: email + terms checkbox + "Register with Passkey"
- Consistent token display UI for both passkey and magic link login
- Remove password fields and forgot password flow

Security:
- CSP-compliant magic link redirect using external script
- Rate limiting on all auth endpoints
- Single-use magic link tokens
2026-01-02 16:52:48 +01:00
Johannes Millan
4f2dbcdaa7 feat(sync): handle auth errors for account deletion scenarios
When a SuperSync account is deleted, clients now properly detect
authentication failures and prompt for reconfiguration:

- Add _checkHttpStatus() helper to SuperSyncProvider that throws
  AuthFailSPError on 401/403 responses
- Clear cached credentials on auth failure to allow reconfiguration
- Add DELETE /api/test/user/:userId endpoint for E2E testing
- Add deleteTestUser() helper in supersync-helpers.ts
- Add E2E tests for account deletion and reconfiguration scenarios

The existing SyncWrapperService already handles AuthFailSPError by
showing a snackbar with "Configure" action, so no UI changes needed.
2026-01-02 15:48:17 +01:00
Johannes Millan
4e0f5e8999 fix(e2e): improve app loading detection and fix server TypeScript errors
- e2e/utils/waits.ts: Add proper retry logic and error handling when
  waiting for magic-side-nav to appear. Now throws a clear error if
  app doesn't load within 30s instead of silently continuing.

- packages/super-sync-server/src/auth.ts: Use nullish coalescing for
  passwordHash to handle passkey-only users (who have no password).

- packages/super-sync-server/src/passkey.ts: Add WebAuthn passkey
  authentication support. Fix TypeScript errors by importing types
  from @simplewebauthn/types and converting credential IDs to
  base64url strings.

Fixes flaky "Multiple fresh clients join and sync correctly after
snapshot" test that was failing when app didn't fully load.
2026-01-02 15:11:16 +01:00
Johannes Millan
187cbdafe3 fix(sync-server): reject empty/whitespace-only entityId in validation
The server was accepting empty string entityId values because the validation
only checked type and length, not content. This could allow corrupt operations
to be synced to other clients.

Changes:
- Add check for empty/whitespace-only entityId (returns INVALID_ENTITY_ID)
- Add 3 unit tests for empty, whitespace-only, and tab/newline entityId
- Update existing empty string test to expect INVALID_ENTITY_ID (format error)
  instead of MISSING_ENTITY_ID (null/undefined check)
2026-01-02 13:58:07 +01:00
Johannes Millan
b367c9595b feat(sync): add account deletion to SuperSync web frontend
Add ability for users to delete their SuperSync account from the
web frontend. This addresses GitHub issue #5848.

Server changes:
- Add DELETE /api/account endpoint in api.ts
- Requires JWT authentication
- Uses Prisma cascade delete for user and related data
- Rate limited to 3 requests per 15 minutes
- Logs USER_ACCOUNT_DELETED audit event

Web frontend changes:
- Add "Delete Account" button with danger styling
- Confirmation dialog warns about permanent deletion
- Clears state and returns to login on success
2026-01-02 13:25:40 +01:00