code; } public function get_error_message(): string { return $this->message; } } function is_wp_error($value): bool { return $value instanceof WP_Error; } function __(string $text, string $domain = ''): string { return $text; } function sanitize_key($value): string { return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) ?? ''; } function sanitize_text_field($value): string { return trim(strip_tags((string) $value)); } function sanitize_textarea_field($value): string { return sanitize_text_field($value); } function wp_kses_post($value): string { return strip_tags((string) $value, '


'); } function esc_url_raw($value): string { return trim((string) $value); } function wp_unslash($value) { return $value; } function get_option(string $key, $default = false) { return array_key_exists($key, $GLOBALS['aksrt_test_options']) ? $GLOBALS['aksrt_test_options'][$key] : $default; } function update_option(string $key, $value): bool { $GLOBALS['aksrt_test_options'][$key] = $value; return true; } function add_option(string $key, $value, string $deprecated = '', bool $autoload = true): bool { if (array_key_exists($key, $GLOBALS['aksrt_test_options'])) return false; $GLOBALS['aksrt_test_options'][$key] = $value; return true; } function delete_option(string $key): bool { if (!array_key_exists($key, $GLOBALS['aksrt_test_options'])) return false; unset($GLOBALS['aksrt_test_options'][$key]); return true; } function maybe_serialize($value): string { return serialize($value); } function maybe_unserialize($value) { $decoded = @unserialize((string) $value); return $decoded === false && $value !== serialize(false) ? $value : $decoded; } function wp_cache_delete(string $key, string $group = ''): void { unset($GLOBALS['wp_cache'][$group][$key]); } function wp_salt(string $scheme = 'auth'): string { return 'captcha-harness-' . $scheme; } function is_ssl(): bool { return true; } function wp_rand(int $min, int $max): int { return $max; } function add_action(...$args): void {} function add_filter(...$args): void {} function current_user_can(string $capability): bool { return false; } function get_transient(string $key) { return false; } function set_transient(string $key, $value, int $ttl): bool { return true; } function get_current_user_id(): int { return 1; } function wp_parse_url(string $url, int $component = -1) { return parse_url($url, $component); } function home_url(string $path = ''): string { return 'https://example.test/' . ltrim($path, '/'); } function wp_json_encode($value): string { return json_encode($value, JSON_UNESCAPED_SLASHES); } function wp_generate_uuid4(): string { return '00000000-0000-4000-8000-000000000000'; } function wp_remote_post(string $url, array $args = []) { return new WP_Error('unexpected_http', 'unexpected HTTP request'); } function wp_remote_retrieve_response_code($response): int { return 500; } function wp_remote_retrieve_body($response): string { return ''; } function esc_html($value): string { return (string) $value; } function wp_unique_id(string $prefix = ''): string { return $prefix . '1'; } } namespace AKSRT\Settings { final class Settings { public static function flush_settings_cache(): void {} public static function sync_native_site_icon(string $value): void {} } } namespace { require dirname(__DIR__, 2) . '/inc/modules/captcha.php'; require dirname(__DIR__, 2) . '/src/Attachment/UploadLimits.php'; require dirname(__DIR__, 2) . '/src/Admin/Schema/SchemaPermissions.php'; require dirname(__DIR__, 2) . '/src/Admin/SchemaProvider.php'; function check(bool $condition, string $message): void { if (!$condition) { throw new RuntimeException($message); } } check(aksrt_captcha_default_config()['enabled_scenarios'] === [], 'captcha scenarios are not opt-in'); check(!in_array('domain', array_keys(aksrt_captcha_provider_defaults('cloudflare')), true), 'Cloudflare domain remains allowed'); $GLOBALS['aksrt_test_options']['aksrt_captcha_config'] = [ 'enabled' => true, 'active_provider' => 'cloudflare', 'enabled_scenarios' => ['login'], 'cloudflare' => [ 'site_key' => 'site-key', 'secret_key' => 'stored-secret', 'theme' => 'dark', 'size' => 'compact', 'domain' => 'should-be-dropped.test', ], ]; aksrt_captcha_config_reset(); $provider = \AKSRT\Admin\SchemaProvider::get_instance(); $read = $provider->read_settings('permissions', 'login'); check(($read['cloudflare']['secret_key'] ?? '') === '***', 'nested secret was not masked'); check(!isset($read['cloudflare']['domain']), 'unknown provider key was exposed'); $provider->write_settings('permissions', [ 'cloudflare' => [ 'site_key' => 'new-site-key', 'secret_key' => '***', 'theme' => 'light', 'size' => 'normal', 'domain' => 'ignored.test', ], ], 'login'); $stored = $GLOBALS['aksrt_test_options']['aksrt_captcha_config']['cloudflare']; check($stored['secret_key'] === 'stored-secret', 'masked secret did not preserve stored value'); check($stored['site_key'] === 'new-site-key', 'public provider field was not updated'); check(!isset($stored['domain']), 'unknown provider key was persisted'); $provider->write_settings('permissions', [ 'cloudflare' => ['secret_key' => ''], ], 'login'); check($GLOBALS['aksrt_test_options']['aksrt_captcha_config']['cloudflare']['secret_key'] === 'stored-secret', 'empty secret did not preserve stored value'); $provider->write_settings('permissions', [ '__clear_secrets' => ['cloudflare.secret_key'], ], 'login'); check($GLOBALS['aksrt_test_options']['aksrt_captcha_config']['cloudflare']['secret_key'] === '', 'explicit nested secret clear failed'); $GLOBALS['aksrt_test_options']['aksrt_captcha_config'] = [ 'enabled' => true, 'active_provider' => '', 'enabled_scenarios' => ['login'], ]; aksrt_captcha_config_reset(); $failed_closed = aksrt_require_captcha('login', []); check(is_wp_error($failed_closed), 'enabled scene with missing provider failed open'); check($failed_closed->get_error_code() === 'aksrt_captcha_config', 'fail-closed error code mismatch'); $GLOBALS['aksrt_test_options']['aksrt_captcha_config'] = [ 'enabled' => true, 'active_provider' => 'cloudflare', 'enabled_scenarios' => ['login', 'comment'], 'cloudflare' => ['site_key' => 'site', 'secret_key' => 'secret', 'theme' => 'auto', 'size' => 'normal'], ]; aksrt_captcha_config_reset(); $_COOKIE[AKSRT_CAPTCHA_BROWSER_COOKIE] = str_repeat('A', 43); $challenge = aksrt_captcha_issue_challenge('login', 'cloudflare'); check(aksrt_captcha_validate_challenge($challenge, 'login', 'cloudflare', false), 'valid scene challenge failed'); check(!aksrt_captcha_validate_challenge($challenge, 'comment', 'cloudflare', false), 'cross-scene challenge accepted'); check(!aksrt_captcha_validate_challenge($challenge, 'login', 'geetest', false), 'cross-provider challenge accepted'); $_COOKIE[AKSRT_CAPTCHA_BROWSER_COOKIE] = str_repeat('B', 43); check(!aksrt_captcha_validate_challenge($challenge, 'login', 'cloudflare', false), 'cross-browser challenge accepted'); $_COOKIE[AKSRT_CAPTCHA_BROWSER_COOKIE] = str_repeat('A', 43); check(aksrt_captcha_validate_challenge($challenge, 'login', 'cloudflare', true), 'valid challenge consume failed'); check(!aksrt_captcha_validate_challenge($challenge, 'login', 'cloudflare', true), 'challenge replay accepted'); $proof = aksrt_captcha_issue_proof('login', 'cloudflare'); check(!aksrt_captcha_consume_proof($proof, 'comment', 'cloudflare'), 'cross-scene proof accepted'); check(aksrt_captcha_consume_proof($proof, 'login', 'cloudflare'), 'valid proof consume failed'); check(!aksrt_captcha_consume_proof($proof, 'login', 'cloudflare'), 'proof replay accepted'); $headers = aksrt_tencent_tc3_sign('id', 'key', 'captcha', 1700000000, '2023-11-14', 'DescribeCaptchaResult', '2019-07-22', '{}'); check(str_starts_with($headers['Authorization'], 'TC3-HMAC-SHA256 Credential=id/2023-11-14/captcha/tc3_request'), 'TC3 authorization scope mismatch'); echo "Captcha security harness passed.\n"; }