fix(utf8): закрыть lossy-границы конфигурации и API
This commit is contained in:
@@ -875,7 +875,7 @@ run_admin_login_acceptance() {
|
||||
|| fail "acceptance: модуль формата env-файлов отсутствует"
|
||||
code_has orchestrator/src/config/env.ts -F -- 'parseEnvFile(content)' \
|
||||
|| fail "acceptance: разбор hy2xs.env снова расходится с systemd"
|
||||
code_has orchestrator/src/config/env.ts -F -- 'renderEnvFile(entries)' \
|
||||
code_has orchestrator/src/config/env.ts -F -- 'renderEnvFile(runtimeEnvEntries(config))' \
|
||||
|| fail "acceptance: запись hy2xs.env снова идёт интерполяцией"
|
||||
code_has orchestrator/src/steps/env.ts -F -- 'renderEnvFile(' \
|
||||
|| fail "acceptance: bootstrap-admin.secret снова пишется интерполяцией"
|
||||
@@ -908,6 +908,8 @@ run_admin_login_acceptance() {
|
||||
|| fail "acceptance: $unicode_domain_owner не знает диапазон noncharacters U+FDD0..U+FDEF"
|
||||
code_has "$unicode_domain_owner" -E -- '0[xX][fF][fF][fF][eE]' \
|
||||
|| fail "acceptance: $unicode_domain_owner не знает noncharacters вида *FFFE/*FFFF"
|
||||
code_has "$unicode_domain_owner" -E -- '0[xX][fF][eE][fF][fF]' \
|
||||
|| fail "acceptance: $unicode_domain_owner не отвергает документированный U+FEFF"
|
||||
done
|
||||
# Одиночный суррогат в JS-строке TextEncoder молча заменяет на U+FFFD: без
|
||||
# проверки это была бы не ошибка, а тихая подмена секрета.
|
||||
@@ -918,6 +920,24 @@ run_admin_login_acceptance() {
|
||||
|| fail "acceptance: $surrogate_owner не отвергает одиночные суррогаты"
|
||||
done
|
||||
|
||||
log_step "Acceptance: text files use fatal UTF-8 decoding"
|
||||
code_has orchestrator/src/lib/fs.ts -F -- '.bytes()' \
|
||||
|| fail "acceptance: readText снова не читает исходные байты"
|
||||
code_has orchestrator/src/lib/fs.ts -F -- 'fatal: true' \
|
||||
|| fail "acceptance: readText снова допускает replacement decoding"
|
||||
code_has orchestrator/src/lib/fs.ts -F -- 'ignoreBOM: true' \
|
||||
|| fail "acceptance: reader снова съедает BOM до проверки U+FEFF"
|
||||
"$BUN_BIN" -e '
|
||||
const source = require("node:fs").readFileSync("orchestrator/src/lib/fs.ts", "utf8");
|
||||
const start = source.indexOf("export async function readText");
|
||||
const end = source.indexOf("\nexport async function writeText", start);
|
||||
const body = source.slice(start, end);
|
||||
if (body.includes(".text()")) throw new Error("readText использует replacement decoding");
|
||||
' || fail "acceptance: строгий файловый reader заменён на Blob.text()"
|
||||
! grep -R -F --include='*.ts' 'Bun.file(' orchestrator/tools \
|
||||
| grep -F '.text()' >/dev/null \
|
||||
|| fail "acceptance: build-time tool обходит строгий файловый reader"
|
||||
|
||||
# Порт автомата обязан повторять upstream буквально: в состоянии
|
||||
# DOUBLE_QUOTE_VALUE_ESCAPE systemd пишет `c != '\n'`, а не проверку на любой
|
||||
# перевод строки, - иначе `\<CR>` мы прочитали бы иначе, чем systemd.
|
||||
@@ -935,11 +955,36 @@ run_admin_login_acceptance() {
|
||||
const start = source.indexOf("export function parseRuntimeEnv");
|
||||
if (start < 0) throw new Error("parseRuntimeEnv отсутствует");
|
||||
const body = source.slice(start, source.indexOf("\nexport function validateRuntimeConfig"));
|
||||
const rawValidation = body.indexOf("assertEnvTransportable");
|
||||
const parsing = body.indexOf("parseEnv(content)");
|
||||
if (rawValidation < 0 || parsing < 0 || rawValidation > parsing) {
|
||||
throw new Error("исходный EnvironmentFile не проверяется до разбора");
|
||||
}
|
||||
if (!body.includes("validateRuntimeEnvTransport(config)")) {
|
||||
throw new Error("parseRuntimeEnv не проверяет транспорт: отказ придёт уже после мутаций хоста");
|
||||
}
|
||||
' || fail "acceptance: проверка транспорта не входит в разбор конфигурации"
|
||||
|
||||
log_step "Acceptance: JSON is strict before encoding/json"
|
||||
code_has apps/controller/json_body.go -F -- 'utf8.Valid(raw)' \
|
||||
|| fail "acceptance: HTTP JSON снова допускает повреждённый UTF-8"
|
||||
code_has apps/controller/json_body.go -F -- 'validateStrictJSONEncoding(raw)' \
|
||||
|| fail "acceptance: strict JSON binder не проверяет Unicode escapes"
|
||||
code_has apps/controller/validator.go -F -- 'strictBindJSON(c, &field)' \
|
||||
|| fail "acceptance: DTO снова разбираются напрямую через Gin/encoding/json"
|
||||
code_has apps/controller/hysteria2.go -F -- 'strictBindJSON(c, &req)' \
|
||||
|| fail "acceptance: Hysteria auth обходит строгую JSON-границу"
|
||||
code_has apps/controller/peer.go -F -- 'validateStrictJSONEncoding(content)' \
|
||||
|| fail "acceptance: импорт пиров обходит строгую JSON-границу"
|
||||
|
||||
log_step "Acceptance: diagnostics never stages raw secrets"
|
||||
! code_has orchestrator/src/commands/diagnostics.ts -E -- 'cp -a .*(hy2xs\.env|post-install\.env|config\.yaml)' \
|
||||
|| fail "acceptance: diagnostics снова копирует секретный конфиг в staging до редакции"
|
||||
code_has orchestrator/src/commands/diagnostics.ts -F -- 'prepareDiagnosticFile(source, sanitize)' \
|
||||
|| fail "acceptance: diagnostics не проводит файл через строгую подготовку до записи"
|
||||
! code_has orchestrator/src/commands/diagnostics.ts -F -- 'Bun.write' \
|
||||
|| fail "acceptance: diagnostics снова пишет сырой промежуточный файл напрямую"
|
||||
|
||||
log_step "Acceptance: env files are read as data, never executed"
|
||||
# Файл научился нести произвольные значения, и это сделало `source` опасным:
|
||||
# для systemd `HY2XS_ADMIN_INITIAL_PASSWORD="$(...)"` - буквальное значение
|
||||
|
||||
Reference in New Issue
Block a user