mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
* fix(sync): defense-in-depth vs entityId retarget on encrypted ops SuperSync E2EE (AES-256-GCM) covers only op.payload; metadata fields (entityId, opType, actionType, vectorClock, isPayloadEncrypted, ...) travel as plaintext and are not bound by the auth tag. A malicious/ compromised sync server or MITM could retag an encrypted LWW-update op with a different entityId, redirecting the authenticated changes onto an attacker-chosen entity — convertOpToAction() previously trusted the tampered entityId over the authenticated payload.id (coercing even a missing payload.id) and only warned. At the decrypt boundary (where encryption origin is known) verify that an in-scope LWW op's authenticated payload carries a string id equal to op.entityId; otherwise fail closed via a new OperationIntegrityError, distinct from DecryptError so it does not trigger the enter-password dialog. The gate mirrors convertOpToAction's predicate (alias resolution + singleton exclusion) so the two boundaries cannot drift. Scoped defense-in-depth for GHSA-8pxh-mgc7-gp3g, NOT full integrity. Still open (durable AAD-envelope fix): plaintext-injection downgrade via isPayloadEncrypted=false (needs a download-side mandatory-encryption guard), opType promotion, entityType swap, vectorClock replay. Correct the overstated integrity claim in the encryption architecture doc. * fix(sync): reject plaintext ops when SuperSync encryption is mandatory The isPayloadEncrypted flag is unauthenticated plaintext metadata, so a compromised SuperSync server or MITM could set it to false and inject a fully attacker-authored plaintext op — it would skip decryption AND the payload/metadata integrity check and be applied verbatim (arbitrary op forgery). This is a strictly more powerful bypass than the ciphertext entityId retarget closed previously. assertOpsEncryptedWhenExpected rejects any inbound plaintext op (download + piggyback paths) when encryption is enabled. It gates on config INTENT (isEncryptionMandatory && isEncryptionEnabled()), not key presence, so it also fails closed in the dropped-credential state (a !!encryptKey gate would fail open there). Safe with no legacy-data loss: enabling encryption deletes the server copy and re-uploads everything encrypted, so no legitimate plaintext op remains; a never-encrypted account (isEncryptionEnabled()===false) still accepts plaintext. The SuperSync op-level twin of the file-based GHSA-vrc7 download guard and the GHSA-9544 upload guard. Also give OperationIntegrityError a dedicated sync-wrapper branch: fail closed with a calm translated message instead of the raw GHSA/technical string. Follows up the review of GHSA-8pxh-mgc7-gp3g. |
||
|---|---|---|
| .. | ||
| diagrams | ||
| contributor-sync-model.md | ||
| file-based-sync-flowchart.md | ||
| operation-log-architecture.md | ||
| operation-rules.md | ||
| package-boundaries.md | ||
| README.md | ||
| sqlite-migration-followup.md | ||
| sqlite-migration.md | ||
| supersync-encryption-architecture.md | ||
| supersync-scenarios-flowchart.md | ||
| supersync-scenarios.md | ||
| vector-clocks.md | ||
Operation Log & Sync Documentation
The Operation Log is the single sync system for all providers (SuperSync, WebDAV, Dropbox, LocalFile). It is an event-sourced persistence + sync layer: the log is the source of truth, current state is derived by replaying it, and vector clocks detect concurrent edits.
User Action
│
▼
NgRx Store (runtime source of truth)
│
┌───────────────────┼───────────────────┐
▼ │ ▼
OpLogEffects │ Other Effects
│ │
├──► SUP_OPS ◄───────┘ (local persistence — IndexedDB)
│
└──► Sync Providers
├── SuperSync (operation-based, real-time)
└── WebDAV / Dropbox / LocalFile (file-based, single sync-data.json)
Start here
| You want to… | Read |
|---|---|
| Write an effect/reducer/bulk-dispatch correctly | contributor-sync-model.md — the one invariant, enforced by lint |
| Understand the whole architecture + why it's built this way | operation-log-architecture.md — Parts A–F + rejected alternatives |
| See it visually | diagrams/ — 8 topic diagrams |
Reference docs
| Document | Scope |
|---|---|
| operation-log-architecture.md | Authoritative architecture: Local Persistence (A), File-Based Sync (B), Server Sync (C), Validation & Repair (D), Smart Archive (E), Atomic State Consistency (F), and Why this architecture: rejected alternatives |
| contributor-sync-model.md | The single sync invariant for contributors (one intent = one op; replayed/remote ops must not re-trigger effects) |
| operation-rules.md | Design rules and guidelines for operations |
| package-boundaries.md | Dependency/ownership boundaries for @sp/sync-core, @sp/sync-providers, app wiring |
| vector-clocks.md | Vector clock implementation, pruning, history |
| supersync-encryption-architecture.md | End-to-end encryption (AES-256-GCM + Argon2id) |
| diagrams/ | Mermaid diagrams split by topic |
Scenario catalogs (expected behavior)
| Document | Scope |
|---|---|
| supersync-scenarios.md | Concrete SuperSync scenarios A–G with expected behavior |
| supersync-scenarios-flowchart.md | Visual decision tree for the SuperSync scenarios |
| file-based-sync-flowchart.md | Visual decision tree for file-based providers |
Related
| Location | Content |
|---|---|
| packages/super-sync-server/ | SuperSync server implementation |
| ARCHITECTURE-DECISIONS.md | Load-bearing product/data decisions |
Historical design notes and superseded plans are not kept as docs; they live in git history (reference the relevant commit if you need the rationale).