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
这个提交包含在:
@@ -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);');
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户