lightweight-server/tests/test-download-security.sh
ochenstarik-ui 9f06756cef
Some checks failed
Bash syntax / syntax (push) Failing after 11s
Bash syntax / syntax (pull_request) Failing after 2s
fix: close installer exposure and pin verified payloads
2026-10-23 12:33:19 +07:00

132 lines
5.4 KiB
Bash
Executable file

#!/usr/bin/env bash
set -Eeuo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
tmp="$(mktemp -d)"
trap 'rm -rf -- "$tmp"' EXIT
mkdir -p "$tmp/bin"
# install-xui allocates the shim with mktemp -d before populating it. Exercise
# that real lifecycle, then both the cached and rejected download paths.
source <(awk '/^write_xui_curl_shim\(\)/ { copying=1 } copying { print } copying && /^}/ { exit }' \
ochenstarik-server-supply-chain.sh)
mkdir -m 700 "$tmp/shim" "$tmp/vendor"
printf 'verified-fixture\n' > "$tmp/vendor/x-ui.sh"
write_xui_curl_shim "$tmp/vendor" "$tmp/shim"
SUPPLY_DIR="$tmp/vendor" "$tmp/shim/curl" -fLRo "$tmp/copied" \
https://raw.githubusercontent.com/MHSanaei/3x-ui/v3.10.2/x-ui.sh
cmp "$tmp/vendor/x-ui.sh" "$tmp/copied"
if SUPPLY_DIR="$tmp/vendor" "$tmp/shim/curl" -o "$tmp/unknown" \
https://untrusted.example.test/installer.sh >/dev/null 2>&1; then
printf 'Shim accepted an unpinned installer request.\n' >&2
exit 1
fi
[[ ! -e "$tmp/unknown" ]]
# Load only the production URL fetcher so this test never calls the network or
# runs the VPN installer.
source <(awk '/^fetch_subscription\(\)/ { copying=1 } copying { print } copying && /^}/ { exit }' \
ochenstarik-server-vpn-5.sh)
die() { printf '%s\n' "$*" >&2; exit 1; }
source <(awk '/^choose_xray_source\(\)/ { copying=1 } copying { print } copying && /^}/ { exit }' \
ochenstarik-server-vpn-5.sh)
choose_xray_source <<< $'2\nhttps://git.example.test/project/releases/download/vendor-20261023'
[[ "$XRAY_SOURCE" == forgejo \
&& "$XRAY_MIRROR_BASE" == 'https://git.example.test/project/releases/download/vendor-20261023' ]]
cat > "$tmp/bin/curl" <<'CURL_STUB'
#!/usr/bin/env bash
set -Eeuo pipefail
printf '%s\n' "$*" > "$CURL_ARGS_LOG"
cat > "$CURL_CONFIG_LOG"
while (($#)); do
if [[ "$1" == -o ]]; then printf 'test payload\n' > "$2"; exit 0; fi
shift
done
exit 1
CURL_STUB
chmod +x "$tmp/bin/curl"
export PATH="$tmp/bin:$PATH"
export CURL_ARGS_LOG="$tmp/curl-args" CURL_CONFIG_LOG="$tmp/curl-config"
literal_url='https://subscriptions.example.test/path/a"b\c?key=literal-marker'
fetch_subscription "$literal_url" "$tmp/subscription"
[[ "$(cat "$tmp/subscription")" == 'test payload' ]]
grep -Fqx -- 'proto = "=https"' "$CURL_CONFIG_LOG"
grep -Fqx -- 'tlsv1.2' "$CURL_CONFIG_LOG"
grep -Fqx -- 'location' "$CURL_CONFIG_LOG"
expected_url="${literal_url//\\/\\\\}"
expected_url="${expected_url//\"/\\\"}"
grep -Fqx -- "url = \"$expected_url\"" "$CURL_CONFIG_LOG"
if grep -Fq 'literal-marker' "$CURL_ARGS_LOG"; then
printf 'Subscription URL leaked into curl arguments.\n' >&2
exit 1
fi
if (fetch_subscription $'https://subscriptions.example.test/good\nurl = "http://bad.example"' "$tmp/blocked") \
>/dev/null 2>&1; then
printf 'Control-character URL was accepted.\n' >&2
exit 1
fi
# A large value must not bypass rejection through grep -q / SIGPIPE under
# pipefail. Bash validates the entire value before curl sees any config.
printf -v padding '%131072s' ''
if (fetch_subscription $'https://subscriptions.example.test/\v'"${padding// /x}" "$tmp/blocked-large") \
>/dev/null 2>&1; then
printf 'Large control-character URL was accepted.\n' >&2
exit 1
fi
# S8 already carries these flags; lock the redirect policy in as a regression.
grep -Fq -- "--proto '=https' --tlsv1.2" ochenstarik-server-ai-agents-8.sh
# A failed Forgejo mirror request must stop without trying GitHub.
cat > "$tmp/bin/curl" <<'CURL_FAIL_STUB'
#!/usr/bin/env bash
set -Eeuo pipefail
printf '%s\n' "$*" >> "$CURL_ATTEMPTS"
exit 22
CURL_FAIL_STUB
chmod +x "$tmp/bin/curl"
export CURL_ATTEMPTS="$tmp/attempts"
if bash ./ochenstarik-server-supply-chain.sh prepare forgejo "$tmp/prepared" \
'https://git.example.test/project/releases/download/vendor-20261023' \
>/dev/null 2>&1; then
printf 'Failed Forgejo fetch unexpectedly succeeded.\n' >&2
exit 1
fi
[[ "$(wc -l < "$CURL_ATTEMPTS")" == 1 ]]
grep -Fq 'https://git.example.test/project/releases/download/vendor-20261023/xui-install.sh' "$CURL_ATTEMPTS"
if grep -Eq 'github\.com|raw\.githubusercontent\.com' "$CURL_ATTEMPTS"; then
printf 'Forgejo failure fell back to GitHub.\n' >&2
exit 1
fi
if bash ./ochenstarik-server-supply-chain.sh prepare-xray forgejo "$tmp/xray-prepared" \
'https://git.example.test/project/releases/download/vendor-20261023' \
>/dev/null 2>&1; then
printf 'Failed Forgejo Xray fetch unexpectedly succeeded.\n' >&2
exit 1
fi
[[ "$(wc -l < "$CURL_ATTEMPTS")" == 2 ]]
grep -Fq 'https://git.example.test/project/releases/download/vendor-20261023/xray-install.sh' "$CURL_ATTEMPTS"
if grep -Eq 'github\.com|raw\.githubusercontent\.com' "$CURL_ATTEMPTS"; then
printf 'Xray Forgejo failure fell back to GitHub.\n' >&2
exit 1
fi
# Both installer steps must reach their pinned helper paths and avoid mutable
# upstream main installer URLs.
grep -Fq 'prepare-xray github' ochenstarik-server-vpn-5.sh
grep -Fq 'prepare-xray forgejo' ochenstarik-server-vpn-5.sh
grep -Fq 'install-xray "$XRAY_SUPPLY_DIR"' ochenstarik-server-vpn-5.sh
grep -Fq 'prepare github' ochenstarik-server-panel-warp-6.sh
grep -Fq 'prepare forgejo' ochenstarik-server-panel-warp-6.sh
grep -Fq 'install-xui "${TMP_DIR}/vendor"' ochenstarik-server-panel-warp-6.sh
if grep -Eq 'XTLS/Xray-install/main|MHSanaei/3x-ui/main' \
ochenstarik-server-vpn-5.sh ochenstarik-server-panel-warp-6.sh; then
printf 'An installer step still references a mutable upstream main URL.\n' >&2
exit 1
fi
printf 'Download security tests passed.\n'