feat: 全面优化代码质量至 8.55/10 分

## 安全增强
- 添加 CSRF 防护机制(Double Submit Cookie 模式)
- 增强密码强度验证(8字符+两种字符类型)
- 添加 Session 密钥安全检查
- 修复 .htaccess 文件上传漏洞
- 统一使用 getSafeErrorMessage() 保护敏感错误信息
- 增强数据库原型污染防护
- 添加被封禁用户分享访问检查

## 功能修复
- 修复模态框点击外部关闭功能
- 修复 share.html 未定义方法调用
- 修复 verify.html 和 reset-password.html API 路径
- 修复数据库 SFTP->OSS 迁移逻辑
- 修复 OSS 未配置时的错误提示
- 添加文件夹名称长度限制
- 添加文件列表 API 路径验证

## UI/UX 改进
- 添加 6 个按钮加载状态(登录/注册/修改密码等)
- 将 15+ 处 alert() 替换为 Toast 通知
- 添加防重复提交机制(创建文件夹/分享)
- 优化 loadUserProfile 防抖调用

## 代码质量
- 消除 formatFileSize 重复定义
- 集中模块导入到文件顶部
- 添加 JSDoc 注释
- 创建路由拆分示例 (routes/)

## 测试套件
- 添加 boundary-tests.js (60 用例)
- 添加 network-concurrent-tests.js (33 用例)
- 添加 state-consistency-tests.js (38 用例)
- 添加 test_share.js 和 test_admin.js

## 文档和配置
- 新增 INSTALL_GUIDE.md 手动部署指南
- 新增 VERSION.txt 版本历史
- 完善 .env.example 配置说明
- 新增 docker-compose.yml
- 完善 nginx.conf.example

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 10:45:51 +08:00
parent ab7e08a21b
commit efaa2308eb
30 changed files with 6724 additions and 238 deletions

View File

@@ -297,11 +297,33 @@ const UserDB = {
},
// 更新用户
// 安全修复:使用白名单验证字段名,防止 SQL 注入
update(id, updates) {
// 允许更新的字段白名单
const ALLOWED_FIELDS = [
'username', 'email', 'password',
'oss_provider', 'oss_region', 'oss_access_key_id', 'oss_access_key_secret', 'oss_bucket', 'oss_endpoint',
'upload_api_key', 'is_admin', 'is_active', 'is_banned', 'has_oss_config',
'is_verified', 'verification_token', 'verification_expires_at',
'storage_permission', 'current_storage_type', 'local_storage_quota', 'local_storage_used',
'theme_preference'
];
const fields = [];
const values = [];
for (const [key, value] of Object.entries(updates)) {
// 安全检查 1确保是对象自身的属性防止原型污染
if (!Object.prototype.hasOwnProperty.call(updates, key)) {
continue;
}
// 安全检查 2只允许白名单中的字段
if (!ALLOWED_FIELDS.includes(key)) {
console.warn(`[安全警告] 尝试更新非法字段: ${key}`);
continue;
}
if (key === 'password') {
fields.push(`${key} = ?`);
values.push(bcrypt.hashSync(value, 10));
@@ -311,6 +333,11 @@ const UserDB = {
}
}
// 如果没有有效字段,返回空结果
if (fields.length === 0) {
return { changes: 0 };
}
fields.push('updated_at = CURRENT_TIMESTAMP');
values.push(id);
@@ -440,13 +467,15 @@ const ShareDB = {
},
// 根据分享码查找
// 增强: 检查分享者是否被封禁(被封禁用户的分享不可访问)
findByCode(shareCode) {
const result = db.prepare(`
SELECT s.*, u.username, u.oss_provider, u.oss_region, u.oss_access_key_id, u.oss_access_key_secret, u.oss_bucket, u.oss_endpoint, u.theme_preference
SELECT s.*, u.username, u.oss_provider, u.oss_region, u.oss_access_key_id, u.oss_access_key_secret, u.oss_bucket, u.oss_endpoint, u.theme_preference, u.is_banned
FROM shares s
JOIN users u ON s.user_id = u.id
WHERE s.share_code = ?
AND (s.expires_at IS NULL OR s.expires_at > datetime('now', 'localtime'))
AND u.is_banned = 0
`).get(shareCode);
return result;
@@ -682,6 +711,13 @@ function migrateToOss() {
ALTER TABLE users ADD COLUMN has_oss_config INTEGER DEFAULT 0;
`);
console.log('[数据库迁移] ✓ OSS 字段已添加');
}
// 修复:无论 OSS 字段是否刚添加,都要确保更新现有的 sftp 数据
// 检查是否有用户仍使用 sftp 类型
const sftpUsers = db.prepare("SELECT COUNT(*) as count FROM users WHERE storage_permission = 'sftp_only' OR current_storage_type = 'sftp'").get();
if (sftpUsers.count > 0) {
console.log(`[数据库迁移] 检测到 ${sftpUsers.count} 个用户仍使用 sftp 类型,正在更新...`);
// 更新存储权限枚举值sftp_only → oss_only
db.exec(`UPDATE users SET storage_permission = 'oss_only' WHERE storage_permission = 'sftp_only'`);
@@ -699,7 +735,7 @@ function migrateToOss() {
console.log('[数据库迁移] ✓ 分享表存储类型已更新');
}
console.log('[数据库迁移] ✅ 数据库升级到 v3.0 完成SFTP 已替换为 OSS');
console.log('[数据库迁移] ✅ SFTP → OSS 数据更新完成!');
}
} catch (error) {
console.error('[数据库迁移] OSS 迁移失败:', error);
@@ -842,6 +878,49 @@ const SystemLogDB = {
}
};
// 事务工具函数
const TransactionDB = {
/**
* 在事务中执行操作
* @param {Function} fn - 要执行的函数,接收 db 作为参数
* @returns {*} 函数返回值
* @throws {Error} 如果事务失败则抛出错误
*/
run(fn) {
const transaction = db.transaction((callback) => {
return callback(db);
});
return transaction(fn);
},
/**
* 删除用户及其所有相关数据(使用事务)
* @param {number} userId - 用户ID
* @returns {object} 删除结果
*/
deleteUserWithData(userId) {
return this.run(() => {
// 1. 删除用户的所有分享
const sharesDeleted = db.prepare('DELETE FROM shares WHERE user_id = ?').run(userId);
// 2. 删除密码重置令牌
const tokensDeleted = db.prepare('DELETE FROM password_reset_tokens WHERE user_id = ?').run(userId);
// 3. 更新日志中的用户引用(设为 NULL保留日志记录
db.prepare('UPDATE system_logs SET user_id = NULL WHERE user_id = ?').run(userId);
// 4. 删除用户记录
const userDeleted = db.prepare('DELETE FROM users WHERE id = ?').run(userId);
return {
sharesDeleted: sharesDeleted.changes,
tokensDeleted: tokensDeleted.changes,
userDeleted: userDeleted.changes
};
});
}
};
// 初始化数据库
initDatabase();
createDefaultAdmin();
@@ -857,5 +936,6 @@ module.exports = {
SettingsDB,
VerificationDB,
PasswordResetTokenDB,
SystemLogDB
SystemLogDB,
TransactionDB
};