#!/bin/bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=scripts/init-security-env.sh . "${SCRIPT_DIR}/init-security-env.sh" TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT fail() { echo "FAIL: $*" >&2 exit 1 } write_env() { local file="$1" shift printf '%s\n' "$@" > "$file" } assert_min_length() { local file="$1" local key="$2" local minimum="$3" local value value="$(read_env_value "$file" "$key")" [ "${#value}" -ge "$minimum" ] || fail "$key must contain at least $minimum characters" } assert_value() { local file="$1" local key="$2" local expected="$3" local actual actual="$(read_env_value "$file" "$key")" [ "$actual" = "$expected" ] || fail "$key expected '$expected', got '$actual'" } assert_single_key() { local file="$1" local key="$2" local count count="$(grep -c "^${key}=" "$file" || true)" [ "$count" -eq 1 ] || fail "$key must appear exactly once" } file_sha256() { local file="$1" if command -v sha256sum >/dev/null 2>&1; then sha256sum "$file" | awk '{print $1}' else shasum -a 256 "$file" | awk '{print $1}' fi } upgrade_env="$TMP_DIR/upgrade.env" upgrade_log="$TMP_DIR/upgrade.log" write_env "$upgrade_env" \ "CONFIG_JWT_SECRET='this is a very secret secret'" \ "CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY=" \ "WS_AUTH_WS_TICKET_SECRET=" \ "WS_AUTH_METHODS=none,bearer" init_security_env "$upgrade_env" upgrade > "$upgrade_log" assert_min_length "$upgrade_env" "CONFIG_JWT_SECRET" 32 assert_min_length "$upgrade_env" "WS_AUTH_WS_TICKET_SECRET" 32 assert_value "$upgrade_env" "CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY" "" assert_value "$upgrade_env" "WS_AUTH_METHODS" "none,bearer,ws_ticket" assert_single_key "$upgrade_env" "WS_AUTH_WS_TICKET_SECRET" assert_single_key "$upgrade_env" "WS_AUTH_METHODS" upgrade_jwt="$(read_env_value "$upgrade_env" "CONFIG_JWT_SECRET")" upgrade_ws="$(read_env_value "$upgrade_env" "WS_AUTH_WS_TICKET_SECRET")" if grep -Fq "$upgrade_jwt" "$upgrade_log" || grep -Fq "$upgrade_ws" "$upgrade_log"; then fail "security initialization output must not reveal generated secrets" fi before_repeat="$(file_sha256 "$upgrade_env")" init_security_env "$upgrade_env" upgrade > "$TMP_DIR/repeat.log" after_repeat="$(file_sha256 "$upgrade_env")" [ "$before_repeat" = "$after_repeat" ] || fail "repeated upgrade must preserve generated values byte-for-byte" preserved_secret="existing-ws-ticket-secret-that-is-long-enough" preserved_env="$TMP_DIR/preserved.env" write_env "$preserved_env" \ "CONFIG_JWT_SECRET=existing-jwt-secret-that-is-long-enough" \ "WS_AUTH_WS_TICKET_SECRET=$preserved_secret" \ "WS_AUTH_METHODS=bearer,ws_ticket" init_security_env "$preserved_env" upgrade > "$TMP_DIR/preserved.log" assert_value "$preserved_env" "WS_AUTH_WS_TICKET_SECRET" "$preserved_secret" assert_value "$preserved_env" "WS_AUTH_METHODS" "bearer,ws_ticket" new_env="$TMP_DIR/new.env" write_env "$new_env" \ "CONFIG_JWT_SECRET=" \ "CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY=" \ "CONFIG_OTP_HASH_SECRET=" \ "CONFIG_AUDIT_HASH_PEPPER=" \ "CONFIG_AUDIT_INTEGRITY_KEY=" \ "CONFIG_INITIAL_ADMIN_PASSWORD=" \ "WS_AUTH_WS_TICKET_SECRET=" \ "WS_AUTH_METHODS=" init_security_env "$new_env" new > "$TMP_DIR/new.log" for key in \ CONFIG_JWT_SECRET \ CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY \ CONFIG_OTP_HASH_SECRET \ CONFIG_AUDIT_HASH_PEPPER \ CONFIG_AUDIT_INTEGRITY_KEY \ WS_AUTH_WS_TICKET_SECRET; do assert_min_length "$new_env" "$key" 32 done assert_min_length "$new_env" "CONFIG_INITIAL_ADMIN_PASSWORD" 12 assert_value "$new_env" "WS_AUTH_METHODS" "none,bearer,ws_ticket" mode="$(stat -c '%a' "$new_env" 2>/dev/null || stat -f '%Lp' "$new_env")" [ "$mode" = "600" ] || fail "generated environment file mode must be 600, got $mode" echo "Security environment shell tests passed"