Skip to content

测试分支 - #8

Merged
ReasonW6 merged 7 commits into
mainfrom
测试分支
Mar 25, 2026

Hidden character warning

The head ref may contain hidden characters: "\u6d4b\u8bd5\u5206\u652f"
Merged

测试分支#8
ReasonW6 merged 7 commits into
mainfrom
测试分支

Conversation

@ReasonW6

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings March 25, 2026 05:07
@ReasonW6
ReasonW6 merged commit b09c3e9 into main Mar 25, 2026
2 of 4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a Vitest-based test suite (frontend + server), refactors the Express server into a shared core to reuse between browser and Electron modes, and adds several security/performance improvements (XSS hardening, resource cleanup, scroll handler throttling), plus an Electron custom titlebar and local vendoring of some libraries.

Changes:

  • Add Vitest + Supertest + JSDOM testing infrastructure with new unit/integration tests and CI workflow.
  • Refactor server logic into shared/server-core.js and update browser/Electron entrypoints to reuse it.
  • Apply multiple security/perf fixes across UI rendering, bookmark storage, PDF/TXT rendering, and Electron window controls.

Reviewed changes

Copilot reviewed 29 out of 34 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
vitest.config.js Adds Vitest configuration (timeouts, include globs, environment mapping).
tests/server/utils.test.js Adds unit tests for server path/extension utilities and security behaviors.
tests/server/api.test.js Adds Supertest integration tests for server APIs (books/config/fonts/cover).
tests/frontend/utils.test.js Adds unit tests for core frontend utilities.
tests/frontend/configManager.test.js Adds regression test around safe bookmark rendering during config import.
tests/frontend/config.test.js Adds unit tests for CONFIG constants and getFileKey.
tests/frontend/bookmarkStorage.test.js Adds tests for canonical + legacy bookmark storage merging/migration.
tests/frontend/addBooksModal.test.js Adds XSS safety test for add-books modal filename rendering.
src/vendor/jszip.min.js Vendors JSZip locally for offline/packaged usage.
src/js/modules/txtReader.js Escapes TXT chapter title before injecting into HTML to prevent XSS.
src/js/modules/pdfReader.js Switches PDF.js loading to local vendor path; improves lifecycle reset for observers/binding.
src/js/modules/fileManager.js Hardens bookshelf rendering against XSS by using DOM APIs; formatting cleanup.
src/js/modules/configManager.js Migrates bookmark import/export to canonical storage helpers; URL-encodes download filename.
src/js/modules/bookmarkStorage.js Introduces canonical bookmark map storage + legacy key migration helpers.
src/js/modules/bookmarkManager.js Uses canonical bookmark storage, adds numeric timestamps, and applies HTML escaping in render.
src/js/modules/addBooksModal.js Reworks file list rendering to DOM APIs to avoid HTML injection and improve safety.
src/js/bookshelfApp.js Revokes evicted/replaced cover ObjectURLs to prevent memory leaks.
src/js/app.js Refactors save logic, improves scroll progress handling (rAF throttling + DOM caching), improves unload persistence.
src/css/titlebar.css Adds Electron-only custom titlebar styling and layout offsets.
shared/server-core.js New shared Express core for browser + Electron modes, including security path checks and APIs.
server.js Refactors to use shared server core; adds static caching and exports for tests.
scripts/launch-electron.js Adds a cross-platform Electron launcher that clears problematic env vars.
reader.html Uses locally vendored libs; adds Electron custom titlebar markup/logic.
preload.js Exposes window control APIs and maximize state events to renderer.
package.json Adds vitest/jsdom/supertest, updates scripts for testing and Electron launch.
index.html Adds Electron custom titlebar markup/logic and CSS include.
electron-main.js Uses shared server core, switches to frameless window, adds IPC for window controls + maximize events.
_test_electron.js Adds a minimal Electron environment diagnostic script.
README.md Documents the new test setup, structure, and commands.
.github/workflows/ci.yml Adds CI workflow running npm test on Node 18/20.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread shared/server-core.js
Comment on lines +502 to +519
app.delete('/api/book', (req, res) => {
try {
const relPath = req.query.path;
if (!relPath) {
return res.status(400).json({ error: '缺少书籍路径' });
}
const absolutePath = utils.resolveBookPath(relPath);
if (!fs.existsSync(absolutePath)) {
return res.status(404).json({ error: '书籍不存在' });
}
fs.unlinkSync(absolutePath);
utils.cleanupEmptyFolders(absolutePath);
coverCache.delete(absolutePath);
res.json({ success: true });
} catch (error) {
console.error('Error deleting book:', error);
res.status(500).json({ error: '删除书籍失败: ' + error.message });
}

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DELETE /api/bookresolveBookPath 抛错(例如路径穿越/非法绝对路径)时会被外层 catch 捕获并返回 500,同时把 error.message 拼进响应。对非法输入更合适的响应是 403/400,并且不应在响应体里回显内部错误细节。建议在调用 resolveBookPath 的地方单独捕获并返回 403(或复用与 GET /api/book 一致的 Forbidden 行为)。

Copilot uses AI. Check for mistakes.
Comment thread README.md
npm test

# 运行测试并显示覆盖率
npm run test:coverage

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README 中给出了 npm run test:coverage 的运行方式,但 package.json 的 scripts 里并没有 test:coverage,会导致文档指引无法执行。建议补充对应脚本(例如 vitest run --coverage)或移除/改写该段说明。

Suggested change
npm run test:coverage
npx vitest run --coverage

Copilot uses AI. Check for mistakes.
Comment thread tests/server/api.test.js
Comment on lines +45 to +48
describe('POST /api/upload — 上传书籍', () => {
const testFilePath = path.join(__dirname, 'test-upload.txt');

beforeEach(() => {

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此测试文件使用 ESM(顶部 import ... + createRequire(import.meta.url)),但同时在多处使用了 __dirname(例如构造 test-upload.txt / test.xyz 路径)。在 ESM 环境下 __dirname 未定义,会导致用例直接崩溃。建议改为用 fileURLToPath(import.meta.url)/path.dirname(...) 生成当前目录路径,或改成 CommonJS 写法保持一致。

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +145
// BUG-4: HTML 转义函数,防止 XSS
const escHtml = (str) => String(str || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');

sortedBookmarks.forEach(bookmark => {
const el = document.createElement('div');
const lvl = bookmark.level || 1;
el.className = 'bookmark-item level-' + lvl;
el.style.paddingLeft = ((lvl - 1) * 12) + 'px';
el.innerHTML = `
<div class="bookmark-title"><span style="display:inline-block;min-width:28px;padding:2px 6px;margin-right:6px;border-radius:10px;font-size:12px;opacity:.7;background:rgba(127,127,127,.15);">Lv${lvl}</span>${bookmark.title}</div>
<div class="bookmark-location">${bookmark.location.chapterTitle}</div>
<div class="bookmark-time">${bookmark.createdAt}</div>
<button class="bookmark-delete" onclick="window.removeBookmark('${bookmark.id}')" title="删除书签">×</button>
<div class="bookmark-title"><span style="display:inline-block;min-width:28px;padding:2px 6px;margin-right:6px;border-radius:10px;font-size:12px;opacity:.7;background:rgba(127,127,127,.15);">Lv${lvl}</span>${escHtml(bookmark.title)}</div>
<div class="bookmark-location">${escHtml(bookmark.location.chapterTitle)}</div>
<div class="bookmark-time">${escHtml(bookmark.createdAt)}</div>
<button class="bookmark-delete" onclick="window.removeBookmark('${escHtml(bookmark.id)}')" title="删除书签">×</button>

Copilot AI Mar 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里用 innerHTML + 内联 onclick="window.removeBookmark('...')" 拼接 bookmark.id。当前 escHtml 只转义了 & < > ",没有处理单引号 ',因此导入的书签 id 若包含 ' 会破坏属性边界并形成 XSS 注入面。建议避免内联事件(改为 addEventListener + data-*),或至少补全对 ' 的转义。

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants