fix: 修复第二轮代码审计发现的全部 15 个问题

严重 Bug(6 个):
- C-01: settingsRepository.save() 改为先 load 再 merge 再 put,避免数据丢失
- C-02: 原子写入临时文件写到目标同目录(避免跨盘 rename EXDEV 失败)
- C-03: registerIpcHandlers 移到 initWindow 外部,macOS activate 不重复注册
- C-04: rehypeFixImages 拒绝包含 .. 的路径(路径遍历防护)
- C-05: initWindow 开头重置 isClosing/closeTimeout
- C-06: 右键菜单添加边界修正(Math.min 防溢出屏幕)

中等问题(5 个):
- M-01: Sidebar 路径比较归一化(norm = p.replace(/\\/g, '/'))
- M-02: 自动展开 while 循环添加 prev 防护无限循环
- M-03: 单标签时隐藏关闭其他标签菜单项
- M-04: Banner .banner-btn:hover 添加暗色主题覆盖
- M-05: closeTab 优先从 MRU 栈取最近使用的标签

低级问题(4 个):
- L-01: 删除死代码 useSearch hook(useFileWatch.ts)
- L-02: file-watcher error 事件显式调用 watcher.close()
- L-03: scrollSync 插值算法保持原样(O(N²) 仅影响超大文件)
- L-04: modified 标签关闭按钮始终可见

TypeScript 检查零错误,构建成功。
This commit is contained in:
thzxx
2026-05-27 21:35:09 +08:00
parent d23d209522
commit 70dff0cf01
10 changed files with 76 additions and 78 deletions
+11 -6
View File
@@ -30,7 +30,6 @@ function openFileInTab(filePath: string): void {
})
}
// 单实例锁
const lockOk = setupSingleInstanceLock((filePath) => {
if (mainWindow && !mainWindow.isDestroyed()) {
if (mainWindow.isMinimized()) mainWindow.restore()
@@ -72,11 +71,15 @@ if (!lockOk) {
})
}
// B-07: 完整的窗口初始化逻辑(activate 复用)
// C-05: 窗口重建时重置状态
function initWindow(): void {
mainWindow = createWindow()
state.isClosing = false
if (state.closeTimeout) {
clearTimeout(state.closeTimeout)
state.closeTimeout = null
}
registerIpcHandlers(() => mainWindow, fileWatcher, sidebarWatcher, state)
mainWindow = createWindow()
if (process.env.ELECTRON_RENDERER_URL) {
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
@@ -112,9 +115,11 @@ if (!lockOk) {
}
})
// C-03: IPC 处理器只注册一次
registerIpcHandlers(() => mainWindow, fileWatcher, sidebarWatcher, state)
initWindow()
// 命令行文件
const cmdFile = getFilePathFromArgs(process.argv)
if (cmdFile) {
state.pendingFilePath = cmdFile
@@ -125,7 +130,7 @@ if (!lockOk) {
if (process.platform !== 'darwin') app.quit()
})
// B-07: macOS activate 完整重建窗口
// C-03: activate 重建窗口,不重复注册 IPC
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
initWindow()