security: 修复公开配置接口泄露密钥(map_location_config/security_words)

- /api/system/configs 无需登录,此前原样下发 map_location_config(腾讯位置服务 SecretKey)与 security_words(风控词库)
- getPlatformConfigs 改为:排除 security_words/map_location_config,并递归剔除 secret/private/apiv3/token/pem/cert/mchid 等敏感字段
- wechat_pay_config 仍仅返回脱敏字段(mode/appId/mchIdConfigured)
- 新增回归护栏 public-config-privacy.spec.ts(89 套件/644 用例全绿);冒烟 5/5
这个提交包含在:
xcbyy
2026-10-19 13:39:04 +08:00
父节点 699d2bd951
当前提交 a61051fa96
共有 2 个文件被更改,包括 40 次插入 和 1 次删除

查看文件

@@ -2700,8 +2700,24 @@ export class MysqlRepository {
FROM platform_configs
WHERE status = 1
`);
// 移动端公开配置:严禁下发任何密钥/风控内部数据(该接口无需登录)
const PUBLIC_EXCLUDED_KEYS = new Set(['security_words', 'map_location_config']);
const SENSITIVE_FIELD = /(secret|private|apiv3|api_v3|password|passwd|token|pem|cert|mchid)/i;
const stripSensitive = (val: any): any => {
if (Array.isArray(val)) return val.map(stripSensitive);
if (val && typeof val === 'object') {
const out: { [k: string]: any } = {};
for (const [k, v] of Object.entries(val)) {
if (SENSITIVE_FIELD.test(k)) continue;
out[k] = stripSensitive(v);
}
return out;
}
return val;
};
const result: { [key: string]: any } = {};
for (const r of rows) {
if (PUBLIC_EXCLUDED_KEYS.has(r.configKey)) continue;
let val = r.valueJson;
if (typeof val === 'string') {
try {
@@ -2715,7 +2731,7 @@ export class MysqlRepository {
mchIdConfigured: Boolean(val.mchId && val.apiV3Key && val.privateKey)
};
} else {
result[r.configKey] = val;
result[r.configKey] = stripSensitive(val);
}
}
return result;

查看文件

@@ -0,0 +1,23 @@
// 已读取AGENTS.md
import * as fs from 'fs';
import * as path from 'path';
/**
* 第七十三批回归闸门:移动端公开配置不得泄露密钥/风控数据。
* 背景:/api/system/configs 无需登录,曾原样下发 map_location_config(含 SecretKey) 与 security_words(风控词库)。
*/
describe('第七十三批公开配置隐私闸门', () => {
const root = path.resolve(__dirname, '../../');
const repo = fs.readFileSync(path.join(root, 'server/src/db/repository.ts'), 'utf-8');
it('公开配置排除风控词库与地图密钥配置', () => {
expect(repo).toContain("const PUBLIC_EXCLUDED_KEYS = new Set(['security_words', 'map_location_config'])");
expect(repo).toContain('if (PUBLIC_EXCLUDED_KEYS.has(r.configKey)) continue;');
});
it('公开配置递归剔除敏感字段', () => {
expect(repo).toContain('const stripSensitive = (val: any)');
expect(repo).toContain('if (SENSITIVE_FIELD.test(k)) continue;');
expect(repo).toContain('result[r.configKey] = stripSensitive(val);');
});
});