diff --git a/modules/website/wordpress/wordpress-cron-manager.sh b/modules/website/wordpress/wordpress-cron-manager.sh index 7d3c6f6..e5edda5 100755 --- a/modules/website/wordpress/wordpress-cron-manager.sh +++ b/modules/website/wordpress/wordpress-cron-manager.sh @@ -27,7 +27,12 @@ if [ "$EUID" -ne 0 ]; then fi # Lock file to prevent concurrent execution (ephemeral, removed on exit) -LOCK_FILE="/tmp/wordpress-cron-manager.lock" +# SECURITY: Use mktemp to prevent symlink attacks (script runs as root!) +LOCK_FILE=$(mktemp -t wordpress-cron-manager.lock.XXXXXX) || { + echo "ERROR: Cannot create secure lock file" >&2 + exit 1 +} +chmod 600 "$LOCK_FILE" exec 9>"$LOCK_FILE" if ! flock -n 9; then print_error "Another instance of this script is already running" @@ -216,7 +221,10 @@ declare -g SYSTEM_DETECTION_LAZY=0 # Instead of running find 23 times, run once and reuse results declare -g WP_SITES_CACHE="" declare -g WP_CACHE_INITIALIZED=0 -declare -g WP_CACHE_FILE="/tmp/wp-sites-cache" # Persistent across invocations (no $$) +# SECURITY: Use /var/cache instead of /tmp to prevent symlink attacks (script runs as root!) +# Falls back to /tmp with symlink detection if /var/cache unavailable +declare -g WP_CACHE_DIR="${WP_CACHE_DIR:-/var/cache/wordpress-toolkit}" +declare -g WP_CACHE_FILE="${WP_CACHE_DIR}/wp-sites-cache" declare -g WP_CACHE_TTL=3600 # Cache valid for 1 hour (3600 seconds) # Lazy-initialize system detection only when first needed @@ -329,11 +337,39 @@ get_home_path() { esac } +# SECURITY: Safely initialize cache directory, prevent symlink attacks +# Must be called before first cache access +initialize_cache_directory() { + # Create cache directory if needed (with secure permissions) + if [ ! -d "$WP_CACHE_DIR" ]; then + mkdir -p "$WP_CACHE_DIR" 2>/dev/null || return 1 + chmod 700 "$WP_CACHE_DIR" || return 1 + fi + + # SECURITY: Check for symlink attack - refuse to use symlinked cache file + if [ -L "$WP_CACHE_FILE" ]; then + print_warning "Cache file is a symlink (potential security issue), removing" + rm -f "$WP_CACHE_FILE" + return 0 + fi + + # Verify directory is not writable by others (prevent TOCTOU) + local dir_perms=$(stat -c %a "$WP_CACHE_DIR" 2>/dev/null || echo "000") + if [ "$dir_perms" != "700" ]; then + chmod 700 "$WP_CACHE_DIR" 2>/dev/null || return 1 + fi + + return 0 +} + # Function to initialize and cache all WordPress installations # Runs once at script startup, results used by all subsequent functions initialize_wp_cache() { local panel="$SYS_CONTROL_PANEL" + # SECURITY: Initialize cache directory with protection + initialize_cache_directory || return 1 + # Check if cache file exists and is still fresh (within TTL) if [ -f "$WP_CACHE_FILE" ]; then local cache_age=$(($(date +%s) - $(stat -c %Y "$WP_CACHE_FILE" 2>/dev/null || echo 0)))