fix(GOV-012): 修复 Schema 定义安全与错误语义

This commit is contained in:
2026-08-18 02:46:50 +08:00
parent 3d12c83621
commit 874796ab65
3 changed files with 79 additions and 13 deletions
@@ -494,6 +494,20 @@ test('定义规范化只移除账号绑定和当前自增值', () => {
);
});
test('规范化保留例程中的自增文本并拒绝主机信息', () => {
const routine = normalizeCreateDefinition(
"CREATE PROCEDURE `sample`() SELECT ' AUTO_INCREMENT=123 DEFAULT CHARSET=utf8mb4'",
);
assert.equal(
routine,
"CREATE PROCEDURE `sample`() SELECT ' AUTO_INCREMENT=123 DEFAULT CHARSET=utf8mb4'",
);
assert.throws(
() => normalizeCreateDefinition('CREATE TABLE `sample` (`id` int) /* Host: prod-db.internal */'),
(error) => error.code === 'UNSAFE_DEFINITION',
);
});
test('完整导出双重读取一致并始终关闭连接', async () => {
const connection = createExportConnection();
let receivedOptions;
@@ -592,6 +606,29 @@ test('SHOW CREATE 拒绝空结果、错误形状和过大定义', async () => {
);
});
test('直接元数据和 SHOW CREATE 查询错误统一脱敏且保留既有业务错误', async () => {
const rawFailure = new Error('host=prod-db.internal password=not-for-logs');
await assert.rejects(
() => readMysql57ServerMetadata({ query: async () => { throw rawFailure; } }, 'a_share_analysis_dev'),
(error) =>
error.code === 'DATABASE_QUERY_FAILED' &&
!error.message.includes('prod-db.internal') &&
!error.message.includes('not-for-logs'),
);
await assert.rejects(
() => readDefinitions({ query: async () => { throw rawFailure; } }, completeCatalog),
(error) =>
error.code === 'DATABASE_QUERY_FAILED' &&
!error.message.includes('prod-db.internal') &&
!error.message.includes('not-for-logs'),
);
const timeout = new SchemaExportError('EXPORT_TIMEOUT', 'Schema 导出超过总时长限制');
await assert.rejects(
() => readDefinitions({ query: async () => { throw timeout; } }, completeCatalog),
(error) => error === timeout,
);
});
test('查询失败和总超时只返回脱敏错误并关闭连接', async () => {
const failed = createExportConnection({
queryError: new Error('connect 127.0.0.1 password=not-for-logs'),