修复 BgFilter 内部 Token 单段校验

统一校验 BgFilter 内部 Token 并限制启动角色

部署与 provision 预检拒绝内部空白和多行 Token

补充定向测试、运维门禁与架构文档
This commit is contained in:
2026-07-22 14:15:13 +00:00
parent f5db8f4c1e
commit 835fe20c63
7 changed files with 181 additions and 29 deletions
+41 -1
View File
@@ -61,6 +61,8 @@ function main() {
assertDeployRejectsInlineBgFilterInternalToken();
assertDeployRejectsEmptyBgFilterInternalToken();
assertDeployRejectsWhitespaceBgFilterInternalToken();
assertDeployRejectsInternalWhitespaceBgFilterInternalToken();
assertDeployRejectsMultilineBgFilterInternalToken();
assertReadinessFailureKeepsMaintenanceAfterCurrentSwitch();
assertMissingReleaseManifestFails();
assertReleaseManifestMissingApiArtifactFails();
@@ -933,7 +935,7 @@ function assertDeployRejectsWhitespaceBgFilterInternalToken() {
}
assertIncludes(
result.stderr,
'BgFilter 内部 Token 文件必须至少包含一个非空白字符',
'BgFilter 内部 Token 文件必须为不含空白字符的单段值',
'纯空白 Token 文件预检必须给出明确错误。',
);
assertBgFilterPreflightFailedBeforeSwitch(
@@ -942,6 +944,44 @@ function assertDeployRejectsWhitespaceBgFilterInternalToken() {
);
}
function assertDeployRejectsInternalWhitespaceBgFilterInternalToken() {
const fixture = prepareFixture('internal-whitespace-bgfilter-internal-token');
writeFileSync(fixture.bgfilterTokenFile, 'fixture bgfilter-token\n', 'utf8');
const result = runDeploy(fixture);
if (result.status === 0) {
failures.push('BgFilter 内部 Token 含内部空格时部署必须失败。');
}
assertIncludes(
result.stderr,
'BgFilter 内部 Token 文件必须为不含空白字符的单段值',
'含内部空格的 Token 文件预检必须给出明确错误。',
);
assertBgFilterPreflightFailedBeforeSwitch(
fixture,
'BgFilter 含内部空格 Token 文件预检失败',
);
}
function assertDeployRejectsMultilineBgFilterInternalToken() {
const fixture = prepareFixture('multiline-bgfilter-internal-token');
writeFileSync(fixture.bgfilterTokenFile, 'fixture-token\nsecond-token\n', 'utf8');
const result = runDeploy(fixture);
if (result.status === 0) {
failures.push('BgFilter 内部 Token 包含多个非空行时部署必须失败。');
}
assertIncludes(
result.stderr,
'BgFilter 内部 Token 文件必须为不含空白字符的单段值',
'多行 Token 文件预检必须给出明确错误。',
);
assertBgFilterPreflightFailedBeforeSwitch(
fixture,
'BgFilter 多行 Token 文件预检失败',
);
}
function assertDeployRejectsInlineBgFilterInternalToken() {
const cases = [
['api-env', 'apiEnvFile'],
+2 -2
View File
@@ -1681,9 +1681,9 @@ const checks = [
},
{
file: 'scripts/jenkins-server-provision.sh',
includes: 'BgFilter 内部 Token 文件不得为空或只包含空白字符',
includes: 'BgFilter 内部 Token 文件必须为不含空白字符的单段值',
reason:
'Server-Provision 必须拒绝仅含空白字符的 BgFilter 内部 Token 文件。',
'Server-Provision 必须拒绝纯空白、含内部空白或包含多个非空行的 BgFilter 内部 Token 文件。',
},
{
file: 'scripts/jenkins-server-provision.sh',
+18 -2
View File
@@ -654,6 +654,22 @@ validate_no_bgfilter_internal_token_plaintext() {
done
}
bgfilter_internal_token_file_is_single_segment() {
local token_file="$1"
run_privileged awk '
/[^[:space:]]/ {
non_empty_lines += 1
if ($0 !~ /^[[:space:]]*[^[:space:]]+[[:space:]]*$/) {
invalid = 1
}
}
END {
exit !(non_empty_lines == 1 && invalid == 0)
}
' "${token_file}"
}
validate_bgfilter_internal_token_file() {
local api_env_file="$1"
local token_file token_metadata
@@ -667,8 +683,8 @@ validate_bgfilter_internal_token_file() {
echo "[production-api-deploy] BgFilter 内部 Token 必须是非空普通文件且不能是符号链接: ${token_file}" >&2
return 1
fi
if ! run_privileged grep -q '[^[:space:]]' -- "${token_file}"; then
echo "[production-api-deploy] BgFilter 内部 Token 文件必须至少包含一个非空白字符: ${token_file}" >&2
if ! bgfilter_internal_token_file_is_single_segment "${token_file}"; then
echo "[production-api-deploy] BgFilter 内部 Token 文件必须为不含空白字符的单段值: ${token_file}" >&2
return 1
fi
+20 -4
View File
@@ -809,6 +809,22 @@ ensure_bgfilter_worker_runtime_env_defaults() {
ensure_env_value "${BGFILTER_WORKER_ENV_FILE}" "GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_COOLDOWN_SECONDS" "300"
}
bgfilter_internal_token_file_is_single_segment() {
local token_file="$1"
awk '
/[^[:space:]]/ {
non_empty_lines += 1
if ($0 !~ /^[[:space:]]*[^[:space:]]+[[:space:]]*$/) {
invalid = 1
}
}
END {
exit !(non_empty_lines == 1 && invalid == 0)
}
' "${token_file}"
}
ensure_bgfilter_internal_token_file() {
local token_file="/etc/genarrative/secrets/bgfilter-worker.token"
local token_dir="/etc/genarrative/secrets"
@@ -840,9 +856,9 @@ ensure_bgfilter_internal_token_file() {
echo "[server-provision] 生成 BgFilter 内部 Token 失败。" >&2
exit 1
fi
if ! grep -q '[^[:space:]]' -- "${temporary_file}"; then
if ! bgfilter_internal_token_file_is_single_segment "${temporary_file}"; then
rm -f "${temporary_file}"
echo "[server-provision] 生成的 BgFilter 内部 Token 不得为空或只包含空白字符。" >&2
echo "[server-provision] 生成的 BgFilter 内部 Token 必须为不含空白字符的单段值。" >&2
exit 1
fi
chown root:genarrative "${temporary_file}"
@@ -850,8 +866,8 @@ ensure_bgfilter_internal_token_file() {
mv -T "${temporary_file}" "${token_file}"
echo "[server-provision] 已生成 BgFilter 内部 Token 文件: ${token_file}"
else
if ! grep -q '[^[:space:]]' -- "${token_file}"; then
echo "[server-provision] BgFilter 内部 Token 文件不得为空或只包含空白字符: ${token_file}" >&2
if ! bgfilter_internal_token_file_is_single_segment "${token_file}"; then
echo "[server-provision] BgFilter 内部 Token 文件必须为不含空白字符的单段值: ${token_file}" >&2
exit 1
fi
chown root:genarrative "${token_file}"