fix(utf8): закрыть lossy-границы конфигурации и API

This commit is contained in:
2026-09-07 17:25:27 +05:00
parent ab788725cf
commit af9f476658
38 changed files with 759 additions and 159 deletions
+8 -19
View File
@@ -131,10 +131,9 @@ func IsValidAdminUsername(username string) bool {
// же класс дефекта, ради уничтожения которого контракт и существует, только на
// один слой ниже.
//
// U+FEFF в этот список НЕ входит: `0xFEFF & 0xFFFE` — это `0xFEFE`, и systemd
// такое значение принимает (комментарий `/* BOM */` в его исходнике относится к
// U+xFFFE и является неточностью самого systemd). Запрет U+FEFF — отдельное
// продуктовое решение, см. hasForbiddenRune.
// Публичная документация systemd также запрещает U+FEFF. Реализация v257.13
// случайно пропускает его из-за маски в `unichar_is_valid`; продукт следует
// документированному контракту, а не этому расхождению реализации.
func isUnicodeNoncharacter(r rune) bool {
if r >= 0xFDD0 && r <= 0xFDEF {
return true
@@ -144,8 +143,7 @@ func isUnicodeNoncharacter(r rune) bool {
// IsEnvTransportableText сообщает, переживёт ли значение файл окружения.
//
// Это ДОМЕН systemd, а не политика HY2XS: здесь перечислено ровно то, что
// `utf8_is_valid` отвергает, и ничего сверх того. Продуктовые ограничения
// Это документированный ДОМЕН systemd, а не политика HY2XS. Продуктовые ограничения
// (границы длины, запрет управляющих символов) живут отдельно — иначе
// невозможно объяснить, какое из двух правил нарушено и чем это грозит.
func IsEnvTransportableText(value string) bool {
@@ -159,7 +157,7 @@ func IsEnvTransportableText(value string) bool {
for _, r := range value {
// NUL — валидный UTF-8, но границей строки в C; systemd отвергает его
// отдельной веткой (`embedded NUL`).
if r == 0 {
if r == 0 || r == 0xFEFF {
return false
}
if isUnicodeNoncharacter(r) {
@@ -178,7 +176,7 @@ func IsEnvTransportableText(value string) bool {
// (IsEnvTransportableText);
// 6-64 code points — граница, которую видит оператор;
// <= 72 UTF-8 bytes — граница, которую ставит bcrypt;
// Cc и U+FEFF — продуктовая политика, см. hasForbiddenRune.
// Cc — продуктовая политика, см. hasForbiddenRune.
//
// Длина в СИМВОЛАХ, а не в байтах, потому что именно так её считает
// go-playground/validator (`utf8.RuneCountInString` в baked_in.go) и так её
@@ -226,22 +224,13 @@ func IsValidAdminPassword(password string) bool {
// символов» — то есть документация была шире кода ровно на C1. Проверяется
// теперь то, что обещано.
//
// И отдельно U+FEFF. Формат его несёт: `0xFEFF & 0xFFFE` — это `0xFEFE`, и
// `unichar_is_valid` такое значение ПРИНИМАЕТ (комментарий `/* BOM */` в
// исходнике systemd относится к U+xFFFE и является его собственной
// неточностью). Запрет здесь — решение продукта: невидимый символ нулевой
// ширины в пароле нельзя ни увидеть в поле ввода, ни повторить при следующем
// входе, то есть он умеет ровно одно — запереть оператора снаружи. Ровно тем же
// обоснованием запрещены и управляющие символы: перевод строки невозможно
// ввести в однострочное поле формы входа.
// U+FEFF здесь уже не проверяется: он принадлежит документированному домену
// EnvironmentFile и отвергается IsEnvTransportableText.
func hasForbiddenRune(value string) bool {
for _, r := range value {
if r < 0x20 || (r >= 0x7F && r <= 0x9F) {
return true
}
if r == 0xFEFF {
return true
}
}
return false
}