Files
competition-website/scripts/_db.cjs
qubiaobiao a2e24aba2a feat(auth): 口令强度策略(弱口令黑名单 / 账号派生值 / 长度上限)
背景(R61 实测):浏览器密码管理器在「设置密码」页预填 123456,考生若直接提交,该口令即成为其账号真实口令;而后端此前仅由 validate 校验「长度 >= 6」,弱口令完全合法。R62 已从前端修掉预填,本次补齐后端这道闸门。

- app/lib/passwordPolicy.js + password_blacklist.json:口令策略唯一事实来源。黑名单刻意做成纯 JSON,供 Node 运行时与运维脚本共用,避免两份清单漂移。

- controller/auth.js:activate() 与 resetPassword() 接入策略(错误码 2001 / 7001),位置在 validate 之后、短信校验之前(纯本地计算、无副作用、快速失败)。

- validate/index.js:补 max:20,与前端 el-form 规则对齐(此前只有前端限制长度上限)。

- scripts/_db.cjs:DB 配置解析唯一入口;scan_weak_password_accounts.cjs:存量弱口令只读扫描器;verify_password_policy_api.cjs:真接口验证(含「强口令通过」正向对照臂,防假绿)。
2026-09-30 11:56:35 +08:00

62 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/**
* 后端运维脚本共用:数据库连接配置解析(**唯一入口**)
* ============================================================
*
* 为什么单独成文件
* ------------------------------------------------------------
* 任何"需要连库"的运维脚本(弱口令扫描 / 口令策略接口验证 / 后续新增)都必须
* 用同一套优先级与同一条凭据纪律。各自实现必然漂移 —— 例如某处"缺口令时兜底成
* 空串"就能静默连上无口令的库,而另一处明确失败,两者行为不一致却都"看起来正常"。
*
* 读取优先级(与 scripts/_creds.py 完全同源)
* ------------------------------------------------------------
* 1. 环境变量 DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME(CI / 服务器注入优先)
* 2. 仓库根 `scripts/.creds.local.json`(本机开发,已被 .gitignore 排除)
* 3. 明确失败 —— ⛔ **刻意不提供明文兜底**:口令为空时直接抛错,
* 绝不静默尝试空口令连库(那会掩盖"凭据没注入"这一真实故障)。
*
* ⚠️ Python 侧对应实现见 `scripts/_creds.py`,两侧口径必须一致;
* 如需增删键,**两边同时改**。
*
* @returns {{host: string, port: number, user: string, password: string, database: string}}
*/
const fs = require('fs');
const path = require('path');
function loadDbConfig() {
const env = process.env;
let local = {};
// backend/scripts/ → 仓库根/scripts/.creds.local.json
const localFile = path.join(__dirname, '..', '..', 'scripts', '.creds.local.json');
try {
local = JSON.parse(fs.readFileSync(localFile, 'utf8'));
} catch (e) {
// 文件不存在是允许的(走环境变量);但**损坏**必须报错,不静默降级
if (e.code !== 'ENOENT') {
throw new Error(`凭据文件解析失败 ${localFile}: ${e.message}`);
}
}
const cfg = {
host: env.DB_HOST || local.db_host || '127.0.0.1',
port: parseInt(env.DB_PORT || local.db_port || 3306, 10),
user: env.DB_USER || local.db_user || 'root',
password: env.DB_PASSWORD || local.db_password || '',
database: env.DB_NAME || local.db_name || 'competition_db',
};
if (!cfg.password) {
throw new Error(
'未取到数据库口令。请任选其一:\n' +
' ① 设置环境变量 DB_PASSWORD\n' +
' ② 创建 scripts/.creds.local.json(形如 {"db_password":"..."})\n' +
'⛔ 本模块刻意不提供明文兜底:缺凭据时明确失败,不静默连库。'
);
}
return cfg;
}
module.exports = { loadDbConfig };