mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 06:00:20 +08:00
Use PostgreSQL as the only database backend across runtime, schema tooling, installation, Compose, and CI. Update regression tests and reject removed drivers explicitly.
172 lines
7.4 KiB
Bash
Executable File
172 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
|
# shellcheck source=../install.sh
|
|
source "${REPO_ROOT}/install.sh"
|
|
|
|
fail_test() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_generated_key_output() {
|
|
local label="$1"
|
|
local output="$2"
|
|
local key value
|
|
local -a values=()
|
|
|
|
for key in \
|
|
JWT_SECRET_KEY ENCRYPTION_KEY DB_PASSWORD REDIS_PASSWORD; do
|
|
value="$(printf '%s\n' "${output}" | awk -F= -v key="${key}" '
|
|
$1 == key { print substr($0, length(key) + 2); exit }
|
|
')"
|
|
[[ "${value}" =~ ^[A-Za-z0-9_-]{40,}$ ]] \
|
|
|| fail_test "${label} did not generate a strong URL-safe ${key}"
|
|
values+=("${value}")
|
|
done
|
|
[[ "$(printf '%s\n' "${values[@]}" | sort -u | wc -l | tr -d '[:space:]')" == "4" ]] \
|
|
|| fail_test "${label} reused a generated secret"
|
|
}
|
|
|
|
assert_line() {
|
|
local file="$1"
|
|
local expected="$2"
|
|
grep -Fqx -- "${expected}" "${file}" \
|
|
|| fail_test "missing expected line in ${file}: ${expected}"
|
|
}
|
|
|
|
APP_DOCKERFILE="${REPO_ROOT}/Dockerfile.app"
|
|
COMPOSE_FILES=(
|
|
"${REPO_ROOT}/docker-compose.yml"
|
|
"${REPO_ROOT}/docker-compose.single-node.yml"
|
|
)
|
|
|
|
assert_line "${APP_DOCKERFILE}" "USER 65532:65532"
|
|
assert_line "${APP_DOCKERFILE}" " HOME=/tmp/aether-home \\"
|
|
if grep -Eq '^USER[[:space:]]+(root|0)(:0)?[[:space:]]*$' "${APP_DOCKERFILE}"; then
|
|
fail_test "production image still selects a root runtime identity"
|
|
fi
|
|
|
|
for compose_file in "${COMPOSE_FILES[@]}"; do
|
|
assert_line "${compose_file}" \
|
|
' user: "${AETHER_CONTAINER_UID:-65532}:${AETHER_CONTAINER_GID:-65532}"'
|
|
assert_line "${compose_file}" " read_only: true"
|
|
assert_line "${compose_file}" " cap_drop:"
|
|
assert_line "${compose_file}" " - ALL"
|
|
assert_line "${compose_file}" " security_opt:"
|
|
assert_line "${compose_file}" " - no-new-privileges:true"
|
|
assert_line "${compose_file}" " tmpfs:"
|
|
assert_line "${compose_file}" " - /tmp:rw,nosuid,nodev,noexec,mode=1777"
|
|
if grep -Eq '^[[:space:]]+user:[[:space:]]+"?(root|0)(:0)?"?[[:space:]]*$' "${compose_file}"; then
|
|
fail_test "production Compose file still selects a root runtime identity: ${compose_file}"
|
|
fi
|
|
done
|
|
|
|
assert_line "${REPO_ROOT}/.env.example" "DB_PASSWORD="
|
|
assert_line "${REPO_ROOT}/.env.example" "REDIS_PASSWORD="
|
|
assert_line "${REPO_ROOT}/README.md" "chmod 600 .env"
|
|
grep -Fq '${DB_PASSWORD:?set DB_PASSWORD in .env}' "${REPO_ROOT}/docker-compose.yml" \
|
|
|| fail_test "Postgres password is not required by Compose"
|
|
grep -Fq '${REDIS_PASSWORD:?set REDIS_PASSWORD in .env}' "${REPO_ROOT}/docker-compose.yml" \
|
|
|| fail_test "Redis password is not required by Compose"
|
|
if grep -Eq '(DB_PASSWORD|REDIS_PASSWORD):?-?=?aether(_root)?([}"[:space:]]|$)' \
|
|
"${REPO_ROOT}/docker-compose.yml" "${REPO_ROOT}/.env.example"; then
|
|
fail_test "production Compose configuration contains a weak infrastructure password default"
|
|
fi
|
|
|
|
TEST_ROOT="$(mktemp -d)"
|
|
trap 'chmod -R u+rwX "${TEST_ROOT}" 2>/dev/null || true; rm -rf "${TEST_ROOT}"' EXIT
|
|
COMPOSE_DIR="${TEST_ROOT}/compose"
|
|
mkdir -p "${COMPOSE_DIR}"
|
|
fake_bin="${TEST_ROOT}/fake-bin"
|
|
mkdir -p "${fake_bin}"
|
|
printf '#!/usr/bin/env bash\nprintf "false\\n"\n' >"${fake_bin}/docker"
|
|
chmod 0755 "${fake_bin}/docker"
|
|
PATH="${fake_bin}:${PATH}"
|
|
fixture_uid="$(id -u)"
|
|
fixture_gid="$(id -g)"
|
|
[[ "${fixture_uid}" != "0" ]] || fixture_uid="65532"
|
|
[[ "${fixture_gid}" != "0" ]] || fixture_gid="65532"
|
|
cp "${REPO_ROOT}/.env.example" "${COMPOSE_DIR}/.env.example"
|
|
ADMIN_PASSWORD="test-admin-password"
|
|
APP_IMAGE="example.invalid/aether:test"
|
|
AETHER_CONTAINER_UID="${fixture_uid}"
|
|
AETHER_CONTAINER_GID="${fixture_gid}"
|
|
JWT_SECRET_KEY=""
|
|
ENCRYPTION_KEY=""
|
|
generated_env="${TEST_ROOT}/generated.env"
|
|
generate_compose_env "${generated_env}"
|
|
|
|
generated_secrets=()
|
|
for key in \
|
|
JWT_SECRET_KEY ENCRYPTION_KEY DB_PASSWORD REDIS_PASSWORD; do
|
|
value="$(env_file_value "${generated_env}" "${key}")"
|
|
[[ "${value}" =~ ^[A-Za-z0-9_-]{40,}$ ]] \
|
|
|| fail_test "installer did not generate a strong URL-safe ${key}"
|
|
generated_secrets+=("${value}")
|
|
done
|
|
[[ "$(printf '%s\n' "${generated_secrets[@]}" | sort -u | wc -l | tr -d '[:space:]')" == "4" ]] \
|
|
|| fail_test "installer reused a generated secret"
|
|
|
|
generated_single_node_env="${TEST_ROOT}/generated-single-node.env"
|
|
generate_compose_single_node_env "${generated_single_node_env}"
|
|
assert_line "${generated_single_node_env}" "AETHER_DATABASE_DRIVER=postgres"
|
|
assert_line "${generated_single_node_env}" "AETHER_GATEWAY_DEPLOYMENT_TOPOLOGY=single-node"
|
|
assert_generated_key_output "single-node installer" "$(cat "${generated_single_node_env}")"
|
|
|
|
(
|
|
unset AETHER_DATABASE_URL DATABASE_URL AETHER_GATEWAY_DATA_POSTGRES_URL
|
|
native_env="${TEST_ROOT}/native.env"
|
|
if (generate_first_install_env "${native_env}") >/dev/null 2>&1; then
|
|
fail_test "native installer accepted a missing PostgreSQL URL"
|
|
fi
|
|
for database_url in 'mysql://localhost/aether' 'sqlite::memory:' $'postgres://localhost/aether\nINJECTED=true'; do
|
|
if (DATABASE_URL="${database_url}" generate_first_install_env "${native_env}") >/dev/null 2>&1; then
|
|
fail_test "native installer accepted an unsupported or unsafe database URL"
|
|
fi
|
|
done
|
|
[[ ! -e "${native_env}" ]] || fail_test "invalid native database config wrote an env file"
|
|
DATABASE_URL="postgresql://localhost/aether" generate_first_install_env "${native_env}"
|
|
assert_line "${native_env}" "AETHER_DATABASE_DRIVER=postgres"
|
|
assert_line "${native_env}" "AETHER_DATABASE_URL=postgresql://localhost/aether"
|
|
validate_env_file "${native_env}"
|
|
replace_or_append_env "${native_env}" "AETHER_DATABASE_DRIVER" "sqlite"
|
|
if (validate_env_file "${native_env}") >/dev/null 2>&1; then
|
|
fail_test "native env validation accepted a removed driver"
|
|
fi
|
|
replace_or_append_env "${native_env}" "AETHER_DATABASE_DRIVER" "postgres"
|
|
replace_or_append_env "${native_env}" "AETHER_DATABASE_URL" "sqlite::memory:"
|
|
replace_or_append_env "${native_env}" "DATABASE_URL" "sqlite::memory:"
|
|
if (validate_env_file "${native_env}") >/dev/null 2>&1; then
|
|
fail_test "native env validation accepted a removed database URL"
|
|
fi
|
|
)
|
|
|
|
assert_generated_key_output \
|
|
"repository generate_keys.sh" "$("${REPO_ROOT}/generate_keys.sh")"
|
|
generated_key_script="${TEST_ROOT}/generated-keys.sh"
|
|
write_generate_keys_script "${generated_key_script}"
|
|
assert_generated_key_output \
|
|
"installer-generated key script" "$("${generated_key_script}")"
|
|
|
|
injection_env="${TEST_ROOT}/injection.env"
|
|
printf 'SAFE=value\n' >"${injection_env}"
|
|
if (replace_or_append_env "${injection_env}" "SAFE" $'value\nINJECTED=true') >/dev/null 2>&1; then
|
|
fail_test "dotenv CR/LF injection was accepted"
|
|
fi
|
|
grep -Fqx "SAFE=value" "${injection_env}" \
|
|
|| fail_test "rejected dotenv injection still modified the target"
|
|
if grep -Fq "INJECTED" "${injection_env}"; then
|
|
fail_test "rejected dotenv injection added another variable"
|
|
fi
|
|
|
|
literal_backslash_value='literal\nINJECTED=true'
|
|
replace_or_append_env "${injection_env}" "SAFE" "${literal_backslash_value}"
|
|
grep -Fqx "SAFE=${literal_backslash_value}" "${injection_env}" \
|
|
|| fail_test "dotenv replacement interpreted a literal backslash escape"
|
|
[[ "$(wc -l <"${injection_env}" | tr -d '[:space:]')" == "1" ]] \
|
|
|| fail_test "literal backslash escape injected another dotenv line"
|
|
|
|
echo "PASS: production container runtime hardening fixtures"
|