fix(GOV-007): 收紧 Schema 基线安全门禁

This commit is contained in:
2026-08-13 13:12:58 +08:00
parent da056681b6
commit d827bfadb1
3 changed files with 107 additions and 6 deletions
+31 -4
View File
@@ -130,9 +130,12 @@ function assertSchemaHasNoSensitiveOrDataStatements(sql) {
const sqlWithoutStringLiterals = sql.replace(/'(?:''|\\.|[^'\\])*'/gs, "''");
const prohibitedPatterns = [
[sqlWithoutStringLiterals, /\bDEFINER\s*=/i, 'DEFINER'],
[sql, /^--\s*MySQL dump\b/im, 'MySQL dump 头'],
[sql, /--\s*Dump completed on/i, '导出完成时间'],
[sql, /--\s*Host:/i, '连接主机注释'],
[sql, /\bpassword\s*=/i, '疑似密码'],
[sql, /\b(?:password|passwd|pwd|api[_-]?key|api[_-]?token|access[_-]?token|client[_-]?secret|secret)\s*[:=]/i, '疑似凭据'],
[sql, /\b(?:mysql|mariadb):\/\/[^\s'"`]+/i, '数据库连接串'],
[sql, /\b(?:production|prod)[-_\s]*(?:host|db|database)?\s*[:=]\s*[^\s'"`]+/i, '疑似生产主机'],
];
for (const [content, pattern, label] of prohibitedPatterns) {
if (pattern.test(content)) {
@@ -156,11 +159,16 @@ function assertSchemaHasNoSensitiveOrDataStatements(sql) {
);
}
const dataStatement = candidates.find((candidate) =>
/^(?:INSERT\s+INTO|REPLACE\s+INTO)\b/i.test(candidate),
/^(?:INSERT\s+INTO|REPLACE\s+INTO|UPDATE\b|DELETE\s+FROM\b|TRUNCATE(?:\s+TABLE)?\b|LOAD\s+(?:DATA|XML)\b)/i.test(
candidate,
),
);
if (dataStatement) {
const operation = dataStatement.match(
/^(?:INSERT\s+INTO|REPLACE\s+INTO|UPDATE|DELETE\s+FROM|TRUNCATE(?:\s+TABLE)?|LOAD\s+(?:DATA|XML))/i,
)[0];
throw new Error(
`Schema SQL 包含禁止的数据写入:${dataStatement.match(/^(?:INSERT\s+INTO|REPLACE\s+INTO)/i)[0]}`,
`Schema SQL 包含禁止的数据操作:${operation}`,
);
}
}
@@ -629,13 +637,31 @@ export function getCurrentReleasedVersion(repositoryRoot) {
};
}
/**
* not_applicable 清单只记录生成当时 MySQL 未启用,不能永久替代真实结构基线。
* 每次校验都重新读取固定开发环境配置;一旦开发库启用,旧清单立即失效,强制
* 下一版本开发开始前先从开发库生成 captured 基线。
*/
export function assertNotApplicableMatchesDevelopmentConfiguration(repositoryRoot, result) {
if (result.status !== 'not_applicable') return result;
const config = assertSafeDevelopmentConfiguration(
parseEnvironmentFile(readFileSync(resolveFixedDevelopmentEnv(repositoryRoot), 'utf8')),
);
if (config.MYSQL_ENABLED) {
throw new Error('当前开发环境已启用 MySQL,不能继续使用 not_applicable Schema 基线');
}
return result;
}
export function assertCurrentReleasedBaseline(repositoryRoot) {
const { applicationVersion, gitCommit } = getCurrentReleasedVersion(repositoryRoot);
return verifyBaselineDirectory(
const result = verifyBaselineDirectory(
path.join(repositoryRoot, 'database', 'schema-baselines', applicationVersion),
applicationVersion,
gitCommit,
);
return assertNotApplicableMatchesDevelopmentConfiguration(repositoryRoot, result);
}
function runGitLines(repositoryRoot, args) {
@@ -660,6 +686,7 @@ export function assertCurrentReleasedBaselinePublished(repositoryRoot) {
release.applicationVersion,
release.gitCommit,
);
assertNotApplicableMatchesDevelopmentConfiguration(repositoryRoot, result);
const trackedFiles = new Set(
runGitLines(repositoryRoot, ['ls-files', '--', relativeDirectory]),
);