fix(installer): harden admin smoke and rollback cleanup
This commit is contained in:
@@ -1059,24 +1059,48 @@ EOF
|
||||
|| fail "acceptance: smoke больше не выполняет настоящий вход в панель"
|
||||
code_has orchestrator/src/steps/smoke.ts -F -- 'ADMIN_LOGIN_PATH' \
|
||||
|| fail "acceptance: адрес формы входа перестал браться из профиля"
|
||||
# Успех определяется КОНВЕРТОМ: админка отвечает 200 OK и на отказ тоже.
|
||||
code_has orchestrator/src/steps/smoke.ts -F -- '"code"\s*:\s*20000' \
|
||||
# Успех определяется разобранным КОНВЕРТОМ: админка отвечает 200 OK и на
|
||||
# отказ тоже, а regex по сырому тексту способен принять подстроку в message.
|
||||
code_has orchestrator/src/steps/smoke.ts -F -- 'envelope?.code === 20000' \
|
||||
|| fail "acceptance: успех входа снова определяется кодом HTTP, а не конвертом ответа"
|
||||
# Команда несёт пароль администратора и не имеет права попасть в текст ошибки:
|
||||
# runReadOnly (без Secret) кладёт её туда, а текст уезжает в журнал установки
|
||||
# и в diagnostics-бандл. Проверяется ТЕЛО функции, а не файл: остальной smoke
|
||||
# законно пользуется обоими раннерами.
|
||||
local login_body
|
||||
# Positive и negative probe обязаны пользоваться одной транспортной функцией:
|
||||
# две независимые curl-команды и разошлись в RC3 с production middleware.
|
||||
local login_body login_request_body
|
||||
login_body="$(sed -n '/^async function assertAdminLoginWorks/,/^}/p' orchestrator/src/steps/smoke.ts)" \
|
||||
|| fail "acceptance: не удалось прочитать проверку входа в панель"
|
||||
[ -n "$login_body" ] \
|
||||
|| fail "acceptance: функция проверки входа в панель не найдена"
|
||||
grep -q 'runReadOnlySecret`curl' <<<"$login_body" \
|
||||
[ "$(grep -c 'requestAdminLogin(' <<<"$login_body")" -eq 2 ] \
|
||||
|| fail "acceptance: positive и negative login probe не используют один request helper"
|
||||
! grep -q 'curl ' <<<"$login_body" \
|
||||
|| fail "acceptance: assertAdminLoginWorks снова содержит отдельную curl-команду"
|
||||
|
||||
login_request_body="$(sed -n '/^async function requestAdminLogin/,/^}/p' orchestrator/src/steps/smoke.ts)" \
|
||||
|| fail "acceptance: не удалось прочитать transport helper входа"
|
||||
[ -n "$login_request_body" ] \
|
||||
|| fail "acceptance: общий transport helper входа не найден"
|
||||
grep -q 'runReadOnlySecret`curl' <<<"$login_request_body" \
|
||||
|| fail "acceptance: запрос входа не выполняется секретным раннером"
|
||||
# `runReadOnly`curl` совпадает только с ГОЛОЙ формой: у секретной после
|
||||
# `runReadOnly` идёт `Secret`, а не обратная кавычка.
|
||||
! grep -q 'runReadOnly`curl' <<<"$login_body" \
|
||||
! grep -q 'runReadOnly`curl' <<<"$login_request_body" \
|
||||
|| fail "acceptance: запрос входа выполняется раннером, кладущим команду в текст ошибки"
|
||||
grep -q -- '--user-agent' <<<"$login_request_body" \
|
||||
|| fail "acceptance: login smoke снова наследует блокируемый curl User-Agent"
|
||||
|
||||
# Wire-contract проверяется значением, а не поиском форматирования в исходнике.
|
||||
"$BUN_BIN" -e '
|
||||
const smoke = await import("./orchestrator/src/steps/smoke.ts");
|
||||
const request = smoke.buildAdminLoginRequest("hy2xsadmin", "quote\" slash\\ tail ");
|
||||
const body = JSON.parse(request.body);
|
||||
if (body.username !== "hy2xsadmin" || body.pass !== "quote\" slash\\ tail ") {
|
||||
throw new Error("login helper искажает username/pass");
|
||||
}
|
||||
if (Object.hasOwn(body, "password")) throw new Error("login helper отправляет alias password");
|
||||
if (!request.userAgent || /fofa|shodan|curl|wget/i.test(request.userAgent)) {
|
||||
throw new Error("login helper отправляет scanner-like User-Agent");
|
||||
}
|
||||
' || fail "acceptance: общий login helper нарушает wire-контракт или scanner middleware"
|
||||
|
||||
log_step "Acceptance: the negative login probe proves a rejection"
|
||||
# Проверка `code.trim() === "200"` доказывала ровно одно: что запрос не
|
||||
@@ -1084,6 +1108,8 @@ EOF
|
||||
# поэтому «неверные учётные данные отвергнуты» из неё не следовало.
|
||||
code_has orchestrator/src/steps/smoke.ts -F -- 'isRejectedLogin' \
|
||||
|| fail "acceptance: отрицательная проба снова определяет отказ по коду HTTP"
|
||||
code_has orchestrator/src/steps/smoke.ts -F -- 'JSON.parse(body)' \
|
||||
|| fail "acceptance: login envelope снова проверяется regex по сырому тексту"
|
||||
code_has orchestrator/src/steps/smoke.ts -F -- 'INVALID_CREDENTIALS_CODE = "invalid_credentials"' \
|
||||
|| fail "acceptance: отрицательная проба не сверяет доменный код отказа"
|
||||
# Литерал в исходнике теоретически может оказаться настоящим паролем — тогда
|
||||
@@ -1579,6 +1605,17 @@ run_single_owner_acceptance() {
|
||||
grep -q 'persistFailureState(' "$rollback_command" \
|
||||
|| fail "acceptance: запись состояния отказа в $rollback_command не помечена как best effort"
|
||||
done
|
||||
# reset-failed — cleanup с допустимым исходом «юнит уже выгружен». Ненулевой
|
||||
# код нельзя ни превращать в ложный manual-recovery warning, ни глушить:
|
||||
# решение принимается после наблюдения ActiveState каждого юнита.
|
||||
grep -q 'resetFailedUnit("hysteria-server")' orchestrator/src/commands/install.ts \
|
||||
|| fail "acceptance: rollback install не проверяет postcondition reset-failed для hysteria-server"
|
||||
grep -q 'resetFailedUnit("hy2xs-admin")' orchestrator/src/commands/install.ts \
|
||||
|| fail "acceptance: rollback install не проверяет postcondition reset-failed для hy2xs-admin"
|
||||
code_has orchestrator/src/lib/systemd.ts -F -- 'runMutatingStatus`systemctl reset-failed' \
|
||||
|| fail "acceptance: reset-failed снова бросает до проверки допустимого not-loaded состояния"
|
||||
code_has orchestrator/src/lib/systemd.ts -F -- 'properties.get("ActiveState")' \
|
||||
|| fail "acceptance: успех reset-failed не доказывается наблюдаемым ActiveState"
|
||||
"$BUN_BIN" -e '
|
||||
const fs = require("node:fs");
|
||||
for (const [file, write] of [
|
||||
|
||||
Reference in New Issue
Block a user