env -> encrypted DB -> fallback public static function hmac_secret(): string { // 1. check wp-config constant override if (defined('FINLYZER_WORKER_HMAC_SECRET') && is_string(FINLYZER_WORKER_HMAC_SECRET) && FINLYZER_WORKER_HMAC_SECRET !== '') { return FINLYZER_WORKER_HMAC_SECRET; } // 2. check server environment variable $server_val = getenv('FINLYZER_WORKER_HMAC_SECRET'); if (is_string($server_val) && $server_val !== '') { return $server_val; } // 3. check legacy constant override if (defined('FXLI_WORKER_HMAC_SECRET') && is_string(FXLI_WORKER_HMAC_SECRET) && FXLI_WORKER_HMAC_SECRET !== '') { return FXLI_WORKER_HMAC_SECRET; } // 4. check build-time baked constant if (defined('FINLYZER_BAKED_WORKER_HMAC_SECRET') && is_string(FINLYZER_BAKED_WORKER_HMAC_SECRET) && FINLYZER_BAKED_WORKER_HMAC_SECRET !== '') { return FINLYZER_BAKED_WORKER_HMAC_SECRET; } // 5. check authenticated encrypted database storage (2026 enterprise standard) if (class_exists('FXLI_Crypto')) { $stored = FXLI_Crypto::get_stored_secret(); if (is_string($stored) && $stored !== '') { return $stored; } } elseif (function_exists('get_option')) { $opt = (string) get_option('finlyzer_worker_hmac_secret', ''); if ($opt !== '') { return $opt; } } // 6. check active .env file $env_val = self::read_env_value('FINLYZER_WORKER_HMAC_SECRET'); if ($env_val !== null && $env_val !== '') { return $env_val; } // 7. development fallback if (self::is_development()) { return 'dev-ephemeral-secret-32-byte-hex-token'; } return ''; } // identify active source of HMAC secret for administrator diagnostics public static function hmac_secret_source(): string { if (defined('FINLYZER_WORKER_HMAC_SECRET') && is_string(FINLYZER_WORKER_HMAC_SECRET) && FINLYZER_WORKER_HMAC_SECRET !== '') { return 'constant'; } $server_val = getenv('FINLYZER_WORKER_HMAC_SECRET'); if (is_string($server_val) && $server_val !== '') { return 'env'; } if (defined('FXLI_WORKER_HMAC_SECRET') && is_string(FXLI_WORKER_HMAC_SECRET) && FXLI_WORKER_HMAC_SECRET !== '') { return 'constant'; } if (defined('FINLYZER_BAKED_WORKER_HMAC_SECRET') && is_string(FINLYZER_BAKED_WORKER_HMAC_SECRET) && FINLYZER_BAKED_WORKER_HMAC_SECRET !== '') { return 'baked'; } if (class_exists('FXLI_Crypto')) { $stored = FXLI_Crypto::get_stored_secret(); if (is_string($stored) && $stored !== '') { return 'database'; } } elseif (function_exists('get_option') && get_option('finlyzer_worker_hmac_secret', '') !== '') { return 'database'; } $env_val = self::read_env_value('FINLYZER_WORKER_HMAC_SECRET'); if ($env_val !== null && $env_val !== '') { return 'dotenv'; } if (self::is_development()) { return 'dev_fallback'; } return 'none'; } // check if HMAC secret is locked by server-level configuration (constant or env) public static function is_hmac_secret_locked(): bool { $src = self::hmac_secret_source(); return $src === 'constant' || $src === 'env' || $src === 'baked'; } // retrieve masked version of active HMAC secret public static function get_masked_hmac_secret(): string { $secret = self::hmac_secret(); if ($secret === '' || $secret === 'dev-ephemeral-secret-32-byte-hex-token') { return ''; } return class_exists('FXLI_Crypto') ? FXLI_Crypto::mask_secret($secret) : substr($secret, 0, 4) . '••••••••' . substr($secret, -4); } // strictly validate endpoint URL against SSRF and protocol policies public static function validate_endpoint_url(string $url): bool|WP_Error { if ($url === '') { return new WP_Error('empty_endpoint', 'Worker endpoint URL is required.'); } $parts = wp_parse_url($url); if (!is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) { return new WP_Error('malformed_endpoint', 'Worker endpoint URL is malformed.'); } $scheme = strtolower($parts['scheme']); $host = strtolower($parts['host']); // in production, strictly enforce HTTPS protocol if (self::is_production() && $scheme !== 'https') { return new WP_Error('insecure_protocol', 'Production API endpoints MUST use HTTPS encryption (TLS 1.2+).'); } // SSRF defense: block loopback and private IP ranges in production if (self::is_production()) { // block explicit localhost name if ($host === 'localhost' || str_ends_with($host, '.localhost') || str_ends_with($host, '.local')) { return new WP_Error('ssrf_blocked_host', 'Loopback hosts are prohibited in production mode.'); } // resolve IP address $ip = filter_var($host, FILTER_VALIDATE_IP) ? $host : gethostbyname($host); if (filter_var($ip, FILTER_VALIDATE_IP)) { // check private, reserved, and loopback ranges $flags = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; if (!filter_var($ip, FILTER_VALIDATE_IP, $flags)) { return new WP_Error('ssrf_blocked_ip', "Target IP {$ip} is within a prohibited private or loopback range."); } // check AWS metadata service (169.254.169.254) if ($ip === '169.254.169.254') { return new WP_Error('ssrf_blocked_metadata', 'Cloud metadata service access is strictly blocked.'); } } } return true; } // read key from cached active .env file private static function read_env_value(string $key): ?string { if (self::$parsed_env_cache === null) { self::$parsed_env_cache = self::load_active_env_file(); } return self::$parsed_env_cache[$key] ?? null; } // load active environment file according to determined mode private static function load_active_env_file(): array { $target_file = self::is_production() ? FINLYZER_PLUGIN_DIR . '.env.production' : FINLYZER_PLUGIN_DIR . '.env.development'; if (!file_exists($target_file)) { // fallback check for generic .env $generic = FINLYZER_PLUGIN_DIR . '.env'; if (file_exists($generic)) { $target_file = $generic; } else { return []; } } $lines = file($target_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if ($lines === false) { return []; } $env = []; foreach ($lines as $line) { $trimmed = trim($line); // skip comments if ($trimmed === '' || str_starts_with($trimmed, '#')) { continue; } $parts = explode('=', $trimmed, 2); if (count($parts) === 2) { $k = trim($parts[0]); $v = trim($parts[1]); // unquote if wrapped in quotes if ((str_starts_with($v, '"') && str_ends_with($v, '"')) || (str_starts_with($v, "'") && str_ends_with($v, "'"))) { $v = substr($v, 1, -1); } $env[$k] = $v; } } return $env; } } // ------------------------------------------------------------- // BUILD-TIME BAKED CONFIGURATION (PRODUCTION PROFILE) // ------------------------------------------------------------- if (!defined('FINLYZER_BAKED_ENV')) { define('FINLYZER_BAKED_ENV', 'production'); } if (!defined('FINLYZER_BAKED_WORKER_ENDPOINT')) { define('FINLYZER_BAKED_WORKER_ENDPOINT', 'https://finlyzer-worker-prod.ebi1055lol.workers.dev/insight'); } if (!defined('FINLYZER_BAKED_WORKER_ANALYZE_ENDPOINT')) { define('FINLYZER_BAKED_WORKER_ANALYZE_ENDPOINT', 'https://finlyzer-worker-prod.ebi1055lol.workers.dev/api/v1/analyze'); } if (!defined('FINLYZER_BAKED_WORKER_HMAC_SECRET') && '') { define('FINLYZER_BAKED_WORKER_HMAC_SECRET', ''); } if (!defined('FINLYZER_ENABLE_DEV_TOOLS')) { define('FINLYZER_ENABLE_DEV_TOOLS', false); } if (!defined('FINLYZER_STRICT_SSL')) { define('FINLYZER_STRICT_SSL', true); } if (!defined('FINLYZER_FORCE_API_CALCULATION')) { define('FINLYZER_FORCE_API_CALCULATION', false); }