# Риски, бенчмарки, полный список файлов ## Риски | # | Риск | Вероятность | Импакт | Итерация | Митигация | |---|------|------------|--------|----------|-----------| | 1 | Path traversal через IPC | Средняя | Критический | 1+ | `validateFilePath()` на КАЖДОМ handler + module-level projectRoot | | 2 | Symlink escape из projectRoot | Высокая | Критический | 1 | `fs.realpath()` + re-check на каждом entry в readDir | | 3 | node_modules/огромные директории -- OOM | Высокая | Высокий | 1 | IGNORED_DIRS фильтр + MAX_DIR_ENTRIES=500 + виртуализация (итерация 4) | | 4 | CM6 тормозит на файлах >2MB | Низкая | Средний | 1 | Hard limit 2MB + тиерная стратегия + external editor fallback | | 5 | TOCTOU race condition при save | Высокая | Высокий | 2 | Atomic write (tmp + rename) + post-read verify | | 6 | Race condition: агент и пользователь редактируют один файл | Высокая | Высокий | 5 | mtime check + conflict dialog (overwrite / cancel / diff) | | 7 | Unsaved data loss при crash | Средняя | Средний | 2 | Возможен autosave в localStorage/IndexedDB (P2 фича) | | 8 | Device file DoS (/dev/zero) | Средняя | Высокий | 1 | `fs.lstat()` + `isFile()` + block /dev/ /proc/ /sys/ | | 9 | Credential leakage (.env, .key) | Высокая | Высокий | 1 | `validateFilePath()` + визуальная пометка + блокировка чтения | | 10 | ReDoS в searchInFiles | Средняя | Средний | 4 | Только literal search + timeout через AbortController | | 11 | Memory leak: 20+ EditorView | Высокая | Критический | 2 | EditorState pooling + LRU eviction | | 12 | Zustand keystroke storm | Высокая | Высокий | 2 | Content вне store + debounced dirty flag | | 13 | XSS через имена файлов | Низкая | Средний | 1 | React JSX + validateFileName() при создании | | 14 | Запись в .git/ | Средняя | Высокий | 2 | `isGitInternalPath()` блокирует write | | 15 | ~~review.ts без валидации пути~~ | ~~Существует~~ | ~~Критический~~ | **ИСПРАВЛЕНО** | `validateFilePath()` добавлен в handleSaveEditedFile (hotfix применён) | --- ## Benchmarks ``` Benchmark 1: EditorView memory Открыть 25 файлов x 200KB Измерить: performance.memory.usedJSHeapSize Порог: < 150MB Benchmark 2: Tab switch latency Переключить таб (500KB файл с syntax highlighting) Измерить: time from click to contentful paint Порог: < 50ms Benchmark 3: File tree render 5000+ файлов, все папки раскрыты (с виртуализацией) Измерить: FPS при скролле Порог: >= 55fps Benchmark 4: readDir latency Директория с 5000 файлами Измерить: time from click to tree displayed Порог: < 200ms Benchmark 5: Keystroke re-renders React DevTools Profiler при наборе текста Порог: FileTreePanel и TabBar рендерятся 0 раз при наборе ``` --- ## Полный список файлов ### Новые файлы (~30) | # | Файл | Итерация | Описание | |---|------|----------|----------| | 1 | `src/shared/types/editor.ts` | 1 | Все типы editor | | 2 | `src/main/services/editor/ProjectFileService.ts` | 1 | Stateless файловый сервис | | 3 | `src/main/services/editor/index.ts` | 1 | Barrel export: `{ ProjectFileService }` (расширяется в итерациях 4-5) | | 4 | `src/main/services/editor/FileSearchService.ts` | 4 | Search in files | | 5 | `src/main/services/editor/GitStatusService.ts` | 5 | git status --porcelain | | 6 | `src/main/services/editor/EditorFileWatcher.ts` | 5 | FileWatcher (~250-300 LOC, burst coalescing + ENOSPC fallback) | | 7 | `src/main/services/editor/conflictDetection.ts` | 5 | Утилита mtime check: сравнение mtime до/после save, conflict resolution (~40 LOC) | | 8 | `src/main/ipc/editor.ts` | 1 | IPC handlers | | 9 | `src/main/ipc/ipcWrapper.ts` | 1 | Общий `createIpcWrapper()` | | 10 | `src/main/utils/atomicWrite.ts` | 2 | Перемещение `atomicWriteAsync()` из `team/atomicWrite.ts` (randomUUID, fsync, EXDEV fallback) | | 11 | `src/renderer/utils/fileTreeBuilder.ts` | 1 | buildTree (рефакторинг) | | 12 | `src/renderer/utils/codemirrorLanguages.ts` | 1 | Языковой маппинг (рефакторинг) | | 13 | `src/renderer/utils/codemirrorTheme.ts` | 1 | Базовая тема CM (рефакторинг) | | 14 | `src/renderer/utils/tabLabelDisambiguation.ts` | 3 | Disambiguation дублей | | 15 | `src/renderer/store/slices/editorSlice.ts` | 1 | Zustand slice (Группа 1: tree), расширяется в итерации 2-3 | | 16 | `src/renderer/hooks/useEditorKeyboardShortcuts.ts` | 4 | Горячие клавиши | | 17 | `src/renderer/components/common/FileTree.tsx` | 1 | Generic FileTree с render-props | | 18 | `src/renderer/components/team/editor/ProjectEditorOverlay.tsx` | 1 | Full-screen overlay | | 19 | `src/renderer/components/team/editor/EditorFileTree.tsx` | 1 | Обёртка над FileTree | | 20 | `src/renderer/components/team/editor/CodeMirrorEditor.tsx` | 1 | CM6 wrapper | | 21 | `src/renderer/components/team/editor/EditorTabBar.tsx` | 2 | Панель вкладок | | 22 | `src/renderer/components/team/editor/EditorToolbar.tsx` | 2 | Toolbar | | 23 | `src/renderer/components/team/editor/EditorStatusBar.tsx` | 2 | Status bar | | 24 | `src/renderer/components/team/editor/EditorEmptyState.tsx` | 1 | Empty state | | 25 | `src/renderer/components/team/editor/EditorBinaryState.tsx` | 1 | Binary файлы | | 26 | `src/renderer/components/team/editor/EditorErrorState.tsx` | 1 | Ошибки чтения | | 27 | `src/renderer/components/team/editor/EditorContextMenu.tsx` | 3 | Context menu | | 28 | `src/renderer/components/team/editor/NewFileDialog.tsx` | 3 | Inline-input | | 29 | `src/renderer/components/team/editor/QuickOpenDialog.tsx` | 4 | Cmd+P dialog | | 30 | `src/renderer/components/team/editor/SearchInFilesPanel.tsx` | 4 | Cmd+Shift+F | | 31 | `src/renderer/components/team/editor/EditorBreadcrumb.tsx` | 4 | Breadcrumb | | 32 | `src/renderer/components/team/editor/EditorShortcutsHelp.tsx` | 4 | Shortcuts modal | | 33 | `src/renderer/components/team/editor/fileIcons.ts` | 4 | Иконки файлов | | 34 | `src/renderer/components/team/editor/GitStatusBadge.tsx` | 5 | M/U/A/C(conflict) бейджи | | 35 | `src/renderer/utils/editorBridge.ts` | 2 | Module-level singleton: Store ↔ CM6 refs bridge (R3) | ### Модификации существующих файлов (~17) | # | Файл | Итерация | Изменение | |---|------|----------|-----------| | 1 | `src/preload/constants/ipcChannels.ts` | 1-5 | +12 констант EDITOR_* (включая EDITOR_CLOSE) | | 2 | `src/preload/index.ts` | 1-5 | Секция `editor: { ... }` | | 3 | `src/shared/types/api.ts` | 1-5 | `EditorAPI` interface | | 4 | `src/main/ipc/review.ts` | 1 | Замена wrapReviewHandler на import из ipcWrapper | | 5 | `src/main/utils/pathValidation.ts` | 1 | +validateFileName, +isDevicePath, +isGitInternalPath | | 6 | `src/renderer/store/types.ts` | 1 | +EditorSlice в AppState | | 7 | `src/renderer/store/index.ts` | 1 | +createEditorSlice | | 8 | `src/renderer/components/team/TeamDetailView.tsx` | 1 | Кнопка "Open in Editor" + overlay state | | 9 | `src/renderer/components/team/review/ReviewFileTree.tsx` | 1 | Рефакторинг: generic FileTree + fileTreeBuilder | | 10 | `src/renderer/components/team/review/CodeMirrorDiffView.tsx` | 1 | Рефакторинг: импорт из codemirrorLanguages/Theme | | 11 | `src/main/ipc/handlers.ts` | 1 | +initializeEditorHandlers() + registerEditorHandlers(ipcMain) + removeEditorHandlers(ipcMain) | | 12 | `src/renderer/api/httpClient.ts` | 1 | Stub для editor: EditorAPI (throw "not available in browser mode") | | 13 | `src/main/ipc/teams.ts` | follow-up | Миграция wrapTeamHandler → createIpcWrapper (40+ замен, отдельный PR) | | 14 | `src/shared/types/index.ts` | 1 | +`export type * from './editor'` (barrel re-export, паттерн как team/review/terminal) | | 15 | `src/main/index.ts` | 5 | `mainWindow.on('closed')` → `cleanupEditorState()`. `shutdownServices()` → `cleanupEditorState()` | | 16 | `src/renderer/index.css` | 2 | +editor CSS-переменные | | 17 | `src/renderer/hooks/useKeyboardShortcuts.ts` | 4 | Guard `editorOpen` для 6 конфликтующих shortcuts (R1) | ### Тесты (новые, ~15) | # | Файл | Итерация | |---|------|----------| | 1 | `test/main/services/editor/ProjectFileService.test.ts` | 1 | | 2 | `test/main/ipc/editor.test.ts` | 1 | | 3 | `test/main/ipc/ipcWrapper.test.ts` | 1 | | 4 | `test/main/utils/atomicWrite.test.ts` | 2 | | 5 | `test/renderer/utils/fileTreeBuilder.test.ts` | 1 | | 6 | `test/renderer/utils/codemirrorLanguages.test.ts` | 1 | | 7 | `test/renderer/store/editorSlice.test.ts` | 1 (расширяется в 2-3) | | 8 | `test/renderer/utils/tabLabelDisambiguation.test.ts` | 3 | | 9 | `test/renderer/components/team/editor/EditorContextMenu.test.ts` | 3 | | 10 | `test/main/services/editor/FileSearchService.test.ts` | 4 | | 11 | `test/renderer/hooks/useEditorKeyboardShortcuts.test.ts` | 4 | | 12 | `test/renderer/components/team/editor/fileIcons.test.ts` | 4 | | 13 | `test/main/services/editor/GitStatusService.test.ts` | 5 | | 14 | `test/main/services/editor/EditorFileWatcher.test.ts` | 5 | | 15 | `test/main/services/editor/conflictDetection.test.ts` | 5 |