diff --git a/BAUPLAN.md b/BAUPLAN.md index 9eda9c8..fb242af 100644 --- a/BAUPLAN.md +++ b/BAUPLAN.md @@ -5,6 +5,16 @@ Basierend auf: Pflichtenheft v4 (633 Zeilen) + UI-Mockup Iteration 9 --- +## ⚠️ Audit-Stand 2026-06-30 + +**Dieses Projekt wurde am 2026-06-29 einem brutal-honest Code-Audit unterzogen.** +**Ergebnis**: 50 Probleme identifiziert (8 CRITICAL, 13 HIGH, 15 MEDIUM, 14 LOW). +**Stand**: 11 von 50 Issues gefixt (alle CRITICAL + 3 HIGH). +**Verbleibend**: 39 Issues — siehe `docs/FIXPLAN.md` für Details. +**HEAD**: `4a5377c` (main, origin/main). + +--- + ## Technologie-Stack | Schicht | Technologie | Lizenz | @@ -702,3 +712,58 @@ web-cad-neu/ └── specs/ └── requirements.md # Kopie des Pflichtenhefts ``` + +## Phase 22: Audit & Hardening Phase 1 (2026-06-29) ✅ + +**Trigger**: Codebase-Audit am 2026-06-29 durch Codebase Explorer (brutally-honest mode). +**Quelle**: `CODE_ANALYSIS.md` (Audit-Report) + `docs/FIXPLAN.md` (50-Issue-Liste mit Status). +**Verdict**: Funktionaler Prototyp mit kritischen Security-Lücken, gebrochener Collab-Sync und systemischer Architektur-Schuld — **NICHT production-ready**. + +### Issues-Übersicht + +| Schweregrad | Total | Fixed | Open | +|---|---|---|---| +| CRITICAL | 8 | 8 ✅ | 0 | +| HIGH | 13 | 3 ✅ | 10 | +| MEDIUM | 15 | 0 | 15 | +| LOW | 14 | 0 | 14 | +| **Total** | **50** | **11** | **39** | + +### Fixes Phase 1 (alle 8 CRITICAL + 3 HIGH) + +| Commit | Issues | Inhalt | +|---|---|---| +| `2e9dfdf` | #1, #3 | Auth middleware auf allen CRUD Routes + CORS restrict | +| `cf62a77` | #2, #4, #5 | WebSocket auth + Yjs persistence + doc retention | +| `8493138` | #6 | CRDT sync groups + bgConfig via Yjs | +| `275b863` | #7, #8 | loadFromState race + plugin stale closure | +| `baba0cb` | #9, #10, #11 | Remote sync + save all elements + block drop sync | +| `4a5377c` | #9, #10, #11 | Quality-Review-Fixes (local-first merge) | + +### Verifikation + +- ✅ Backend `tsc --noEmit` clean +- ✅ Frontend `tsc --noEmit` clean +- ✅ Backend `npm run build` erfolgreich +- ✅ Frontend `vite build` erfolgreich (341 modules) +- ✅ Server startet ohne Fehler +- ✅ Health endpoint `GET /api/health` → 200 +- ✅ WS ohne Token → Close code 4001 "Authentication required" +- ✅ WS mit invalid Token → Close code 4001 "Invalid or expired session" +- ✅ WS mit valid Token → accepted, initial state sync +- ✅ Register + Login funktional + +### Verbleibende Brocken (für Phase 23+) + +- **App.tsx Refactoring** (Issue #22, 1207-Zeilen-Monolith in Module) +- **Block-Persistence** (Issues #16, #17 — Rename + Duplicate) +- **Layer-Persistence** (Issue #15) +- **GROUP Command** (Issue #18 — No-Op) +- **Image Insert / Paste / SVG-Import** (Issues #28, #32, #34) +- **listProjects Owner-Filterung** (Issue #21) +- **Input Validation** auf allen Backend-Routes (Issue #33) +- **Cursor-Storage** in Y.Doc (Issue #19) +- Diverse MEDIUM + LOW Cleanups + +**Detaillierter Fixplan**: siehe `docs/FIXPLAN.md` (alle 50 Issues mit Severity, Status, Test-Coverage). + diff --git a/docs/FIXPLAN.md b/docs/FIXPLAN.md new file mode 100644 index 0000000..b1ec2d2 --- /dev/null +++ b/docs/FIXPLAN.md @@ -0,0 +1,339 @@ +# Web-CAD Fixplan — 50-Issue Audit + +**Audit-Datum**: 2026-06-29 +**Audit-Quelle**: `CODE_ANALYSIS.md` (Codebase Explorer, brutally-honest mode) +**Stand**: 2026-06-30 +**HEAD**: `4a5377c` (main, origin/main) +**Verdict**: Funktionaler Prototyp mit kritischen Security-Lücken, gebrochener Collab-Sync und systemischer Architektur-Schuld — **NICHT production-ready**. + +--- + +## Zusammenfassung + +| Schweregrad | Total | Fixed | Open | +|---|---|---|---| +| CRITICAL | 8 | 8 ✅ | 0 | +| HIGH | 13 | 3 ✅ | 10 | +| MEDIUM | 15 | 0 ✅ | 15 | +| LOW | 14 | 0 ✅ | 14 | +| **Total** | **50** | **11** | **39** | + +## Fix-Commits (Audit-Phase 1) + +- `2e9dfdf` — Issues #1, #3 (auth middleware + CORS) +- `cf62a77` — Issues #2, #4, #5 (WebSocket auth + Yjs persistence + doc retention) +- `8493138` — Issue #6 (CRDT sync groups + bgConfig) +- `275b863` — Issues #7, #8 (loadFromState race + plugin stale closure) +- `baba0cb` — Issues #9, #10, #11 (remote sync, save all, block drop) +- `4a5377c` — Quality-Review-Fixes (Issues #9, #10, #11 local-first merge) + +## Test-Coverage-Schema + +- ✅ **Verified**: Smoke-Test im `test_report.md` beweist das Verhalten +- ⚠️ **Build-only**: `tsc --noEmit` clean + `vite build` erfolgreich, aber kein funktionaler Test +- ❌ **Untested**: Kein Test-Beleg vorhanden + +--- + +## Issues + +### #1: [backend/src/routes/*.ts] NO Authentication on All CRUD Routes +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ✅ Verified +- **Notes**: 2e9dfdf: auth middleware on all CRUD routes; smoke-test: no token → 401 (projects.ts tested) + +### #2: [backend/src/websocket/yjsServer.ts:84] WebSocket Collaboration Has No Authentication +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ✅ Verified +- **Notes**: cf62a77: WS token validation; 3 tests: no token→4001, invalid token→4001, valid token→accepted + +### #3: [backend/src/server.ts:26] CORS Allows All Origins +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: 2e9dfdf: CORS restricted to env allowlist; no test in test_report.md (manual CORS-header check needed) + +### #4: [backend/src/websocket/yjsServer.ts:43] Yjs Persistence NEVER Loads +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: cf62a77: persistenceReady flag set; server start log shows '/tmp/yjs-documents initialized', but no restart-roundtrip test + +### #5: [backend/src/websocket/yjsServer.ts:125] In-Memory Doc Deleted When Last Client Disconnects +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: cf62a77: docs retained after last disconnect; no specific test in test_report.md + +### #6: [frontend/src/App.tsx:280-319] CRDT Sync: Groups and bgConfig NOT Synced via Yjs +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: 8493138: groups + bgConfig synced via Yjs; no E2E-collab-test + +### #7: [frontend/src/App.tsx:267-269] loadFromState Called Before Data Is Loaded — Race Condition +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: 275b863: race condition resolved; no specific race-condition test + +### #8: [frontend/src/App.tsx:128-142] Plugin Context Has Stale Closure — Captures Empty State +- **Severity**: CRITICAL +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: 275b863: plugin context stale closure fixed; no plugin-context test + +### #9: [frontend/src/App.tsx:306-317] Band-Aid Guards for Empty Remote Arrays +- **Severity**: HIGH +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: baba0cb + 4a5377c (quality review): band-aid guards replaced with local-first merge; no test + +### #10: [frontend/src/App.tsx:588] Save Action Only Saves Elements Starting with 'el-' +- **Severity**: HIGH +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: baba0cb + 4a5377c (quality review): save all elements; no test + +### #11: [frontend/src/App.tsx:509-517] Block Drop Doesn't Save to Backend or Yjs +- **Severity**: HIGH +- **Status**: ✅ FIXED +- **Test-Coverage**: ⚠️ Build-only +- **Notes**: baba0cb + 4a5377c (quality review): block drop backend sync; no test + +### #12: [frontend/src/App.tsx:528-550] Import Doesn't Save to Backend or Yjs +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #13: [frontend/src/App.tsx:628] Copy/Paste Uses Non-Existent `selected` Property +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #14: [frontend/src/App.tsx:1021] activeLayerName Hardcoded to 'layer-0' +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #15: [frontend/src/App.tsx:754-789] Layer Operations Don't Persist to Backend +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #16: [frontend/src/App.tsx:436-438] Block Rename Doesn't Persist to Backend +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #17: [frontend/src/App.tsx:440-454] Block Duplicate Doesn't Persist to Backend +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #18: [frontend/src/App.tsx:906-912] GROUP Command Doesn't Actually Create Groups +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #19: [frontend/src/crdt/AwarenessManager.ts:38-39] Cursor Data Stored in Shared Y.Doc +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #20: [frontend/src/crdt/WebSocketProvider.ts:62-65] Client Sends Local State on Connect — Conflicts with Server State +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #21: [backend/src/routes/projects.ts:9-11] listProjects Returns ALL Projects — No Owner Filtering +- **Severity**: HIGH +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #22: [frontend/src/App.tsx:1207 lines] Monolithic Component — God Component Anti-Pattern +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #23: [frontend/src/App.tsx:380] Stale `elements` in handleElementsDeleted Dependencies +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #24: [frontend/src/App.tsx:607-621] Export Uses NW.js-Specific `nwsave` Attribute +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #25: [frontend/src/services/api.ts:354-388] Project Load Cache Race Condition +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #26: [frontend/src/App.tsx:66] Global Mutable Set for StrictMode Dedup — Module-Level State +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #27: [frontend/src/App.tsx:170-173] SeatingService Instantiated on Every Elements Change +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #28: [frontend/src/App.tsx:467-472] SVG Import Doesn't Persist to Backend +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #29: [frontend/src/App.tsx:474-488] Save Group as Block Only Uses Single Selected Element +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #30: [backend/src/database/SqliteAdapter.ts:267] Session Expiry Comparison Uses Wrong Format +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #31: [frontend/src/App.tsx:421-428] Text Edit Uses window.prompt — Blocking UX +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #32: [frontend/src/App.tsx:657-680] Image Insert Doesn't Save to Backend or Yjs +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #33: [backend/src/routes/*.ts] No Input Validation on Any Route +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #34: [frontend/src/App.tsx:637-642] Paste Doesn't Save to Backend or Yjs +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #35: [frontend/src/crdt/useYjsBinding.ts:110] useEffect Dependencies Include Values That Don't Change +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #36: [frontend/src/App.tsx:145] Online Count Includes Self Even When Disconnected +- **Severity**: MEDIUM +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #37: [frontend/src/App.tsx:50-63] Mock Data Used as Initial State +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #38: [frontend/src/App.tsx:40-46] Initial Layers Hardcoded — Not from Backend +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #39: [frontend/src/services/api.ts:34,44,54] Return Type `any` for Auth Functions +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #40: [frontend/src/App.tsx:667-671] Multiple `as any` Casts for Image Element +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #41: [frontend/src/history/HistoryManager.ts] generateId() Method Never Used +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #42: [frontend/src/App.tsx:501-503] handleBlockSearch Is a No-Op +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #43: [frontend/src/App.tsx:691-695] Zoom Fit/100 Are No-Ops +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #44: [frontend/src/App.tsx:683-686] Format Actions Are No-Ops +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #45: [frontend/src/App.tsx:706-708] Search Action Is a No-Op +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #46: [backend/src/database/SqliteAdapter.ts:93] Indentation Inconsistency +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #47: [frontend/src/App.tsx:159] BackgroundService Instantiated in useRef — Never Cleaned Up +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #48: [frontend/src/App.tsx:349-352] handleToggleElementVisible Calls API Inside setElements +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #49: [frontend/src/App.tsx:951-960] Plugin Context Duplicated in handleCommand +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed + +### #50: [frontend/src/App.tsx:799] handleZoomIn/Out/Fit Only Log Messages +- **Severity**: LOW +- **Status**: ⏳ OPEN +- **Test-Coverage**: ❌ Untested +- **Notes**: Not fixed +