--restore: файлы настроек копии – только печатные ASCII, нулевой байт больше не выключает проверку

Нулевой байт в kit.env, install-result.env или kit-backup.env заставлял GNU grep считать
файл двоичным и молчать: safe_env «проходил», а bash при «.» выкидывал нулевые байты
и выполнял остальное (лишние имена вроде PATH и KIT_CLI_URL).

- проверка архива отклоняет три файла настроек с байтами вне 0x20–0x7E и \n (и больше 64 КиБ),
  3x-ui.txt – не UTF-8 или с управляющими символами (кроме \n и \t), больше 1 МиБ;
  проверяются все записи с таким именем, в том числе повторные;
- safe_env: сначала tr – только печатные ASCII и \n, затем grep -a под LC_ALL=C;
- grep -a там, где kit и установщик читают свои файлы настроек (kit.sh: замена токена API);
- отрицательные тесты: \0, \r, \x1b, \x80, \xff в начале, в середине, в конце, отдельной строкой
  и в значении в каждом из трёх файлов, повторная запись в архиве, 3x-ui.txt.
This commit is contained in:
maxdmitrov
2026-10-06 18:58:38 +03:00
parent 436acffc22
commit 018d9cefa2
6 changed files with 92 additions and 6 deletions
+25 -3
View File
@@ -1703,10 +1703,14 @@ sub_links() {
# Иначе чужой архив подменил бы переменные установщика или kit (RESULT, PATH, KIT_VERSION…).
# Значение каждого имени – тоже по его формату: kit потом подставляет их в sed, nginx и ссылки.
# Кавычки – только пустые (''): так printf %q пишет пустое значение, остальное у нас без кавычек.
# Сначала – только печатные ASCII и перевод строки: файл с нулевым байтом GNU grep считает двоичным и молчит
# (проверка «прошла» бы), а bash при «.» нулевые байты выкидывает и выполняет остальное. Поэтому и grep -a.
safe_env() { # файл имя...
local f=$1 n re; shift
[[ -r $f ]] || die "В копии нет файла ${f##*/}."
grep -qvE "^([A-Z][A-Z0-9_]*=([A-Za-z0-9._:/@%+,=_-]*|''))?$" "$f" \
[[ $(LC_ALL=C tr -d '\n -~' <"$f" | wc -c) -eq 0 ]] \
|| die "Это не резервная копия 3X-UI KIT или она повреждена (файл ${f##*/}). Ничего не менял."
LC_ALL=C grep -aqvE "^([A-Z][A-Z0-9_]*=([A-Za-z0-9._:/@%+,=_-]*|''))?$" "$f" \
&& die "Это не резервная копия 3X-UI KIT или она повреждена (файл ${f##*/}). Ничего не менял."
while IFS= read -r n; do
[[ " $* " == *" $n "* ]] || die "Это не резервная копия 3X-UI KIT или она повреждена (лишнее имя $n в ${f##*/}). Ничего не менял."
@@ -1731,7 +1735,7 @@ safe_env() { # файл имя...
esac
[[ $(env_get "$f" "$n") =~ $re ]] \
|| die "Это не резервная копия 3X-UI KIT или она повреждена (значение $n в ${f##*/}). Ничего не менял."
done < <(grep -oE '^[A-Z][A-Z0-9_]*' "$f")
done < <(LC_ALL=C grep -aoE '^[A-Z][A-Z0-9_]*' "$f")
return 0
}
@@ -1888,6 +1892,24 @@ with tarfile.open(sys.argv[1], "r:gz") as t:
for need in ("etc/x-ui/x-ui.db", "etc/x-ui/install-result.env", "kit-backup.env"):
if need not in names:
sys.exit("нет " + need)
# Файлы настроек потом подключаются от root: в них только печатные ASCII и перевод строки. Нулевой байт,
# \r или не-ASCII ломают построчную проверку (GNU grep считает такой файл двоичным). Данные для входа
# (3x-ui.txt) показываются в терминале: только UTF-8 без управляющих символов, кроме \n и \t.
for m in ms:
if not m.isfile():
continue
if m.name in ("etc/x-ui/install-result.env", "etc/kit/kit.env", "kit-backup.env"):
data = t.extractfile(m).read(65537)
if len(data) > 65536 or data.translate(None, bytes(range(0x20, 0x7f)) + b"\n"):
sys.exit("байты " + m.name)
elif m.name == "root/3x-ui.txt":
data = t.extractfile(m).read(1048577)
try:
text = data.decode("utf-8")
except UnicodeDecodeError:
sys.exit("байты " + m.name)
if len(data) > 1048576 or any((c < " " and c not in "\n\t") or "\x7f" <= c <= "\x9f" for c in text):
sys.exit("байты " + m.name)
# Каталог сертификата есть – в нём оба файла, иначе установка споткнётся на полпути.
files = {m.name for m in ms if m.isfile() and m.size > 0}
for c in ("root/cert/self", "root/cert/custom"):
@@ -2023,7 +2045,7 @@ PY
if [[ -f /etc/kit/kit.env ]]; then
install_kit_file
# Панель по домену: nginx собран заново, значит блок домена нужно вернуть (домен должен указывать на этот сервер).
if grep -q '^PANEL_ON=domain' /etc/kit/kit.env; then
if grep -aq '^PANEL_ON=domain' /etc/kit/kit.env; then
sed -i 's/^PANEL_ON=.*/PANEL_ON=ip/' /etc/kit/kit.env
/usr/local/bin/kit net panel domain >/dev/null 2>&1 || { /usr/local/bin/kit __xver 0 >/dev/null 2>&1 || true; warn "Панель по домену не включилась (домен должен указывать на этот сервер): kit net panel domain"; }
fi