'textarea' ) ); $scope = Scope::current( 'logged_ai_manage_settings' ); if ( is_wp_error( $scope ) || 'POST' !== \LoggedAI\AdminInput::method() ) { wp_die( esc_html__( 'Request-source operation denied.', 'logged-ai' ), '', array( 'response' => 403 ) ); } check_admin_referer( 'logged_ai_source' ); if ( 'recover' === ( $request_form['operation'] ?? '' ) ) { $saved = RequestsSource::reset_corrupt( $scope, '1' === ( $request_form['confirmed'] ?? '' ) ); if ( is_wp_error( $saved ) ) { wp_die( esc_html( PresentationTranslations::text( $saved->get_error_message() ) ), '', array( 'response' => 400 ) ); } wp_safe_redirect( admin_url( 'admin.php?page=logged-ai-request-source&saved=1' ) ); exit; } if ( 'selftest' === ( $request_form['operation'] ?? '' ) ) { $url = RequestsSelfTest::begin( $scope ); if ( is_wp_error( $url ) ) { wp_die( esc_html( PresentationTranslations::text( $url->get_error_message() ) ), '', array( 'response' => 503 ) ); } wp_safe_redirect( $url ); exit; } $header = $request_form['header'] ?? null; $text = $request_form['trusted'] ?? null; $revision = $request_form['revision'] ?? null; $generation = $request_form['generation'] ?? null; if ( ! is_string( $generation ) || ! preg_match( '/\A[a-f0-9]{32}\z/D', $generation ) || ! is_string( $header ) || ! is_string( $text ) || strlen( $text ) > 4096 || ! is_string( $revision ) || ! preg_match( '/\A[0-9]{1,10}\z/D', $revision ) ) { wp_die( esc_html__( 'Invalid request-source form.', 'logged-ai' ), '', array( 'response' => 400 ) ); } $lines = '' === trim( $text ) ? array() : preg_split( '/\r\n|\r|\n/', trim( $text ), RequestsSource::MAX_PROXIES + 1 ); $saved = RequestsSource::save( $scope, $header, $lines, (int) $revision, '1' === ( $request_form['confirmed'] ?? '' ), $generation ); if ( is_wp_error( $saved ) ) { wp_die( esc_html( PresentationTranslations::text( $saved->get_error_message() ) ), '', array( 'response' => 400, 'back_link' => true, ) ); } wp_safe_redirect( admin_url( 'admin.php?page=logged-ai-request-source&saved=1' ) ); exit; } /** * Render current source settings, sensitive-context permissions and bounded observer-test status. * * @return void */ public static function page(): void { $request_query = \LoggedAI\AdminInput::query(); // Resolve translation callbacks before reading authorized request evidence. $display_text = array( 0 => __( 'Request-source settings denied.', 'logged-ai' ), 1 => __( 'The display timezone context changed. Reload this page.', 'logged-ai' ), 2 => __( 'Request source', 'logged-ai' ), 3 => __( 'The connection peer is the default. Enable forwarding only when your server reliably supplies the configured header through the exact proxy boundary you control. This does not configure a server or discover a CDN.', 'logged-ai' ), /* translators: Values or formatting elements shown in this message. */ 4 => __( 'Display timezone: %1$s', 'logged-ai' ), 5 => __( ' Reset invalid configuration to the connection peer and invalidate earlier exact-source targets.', 'logged-ai' ), 6 => __( 'Recover source configuration', 'logged-ai' ), 7 => __( 'Client-source configuration saved. Exact-source policies require evidence and a preview from the current configuration.', 'logged-ai' ), /* translators: Values or formatting elements shown in this message. */ 8 => __( 'Trusted proxy addresses are hidden. Sensitive-context permission is required to view or edit this configuration. Current source: %1$s; configured boundaries: %2$s.', 'logged-ai' ), 9 => __( 'Address source', 'logged-ai' ), 10 => __( 'Actual connection peer', 'logged-ai' ), 11 => __( 'Explicit trusted proxies and X-Forwarded-For', 'logged-ai' ), 12 => __( 'Request-source authority changed.', 'logged-ai' ), 13 => __( 'Trusted proxy addresses or CIDRs — one per line, maximum 32', 'logged-ai' ), 14 => __( 'Leave the proxy set empty for connection-peer mode. Forwarding is read only when the immediate peer matches this set. The chain is evaluated from the connection side; the first untrusted hop is the derived source. Earlier supplied entries cannot replace it. Maximum 16 supplied hops and 4096 bytes; malformed trusted-side input makes the exact source unavailable. Other forwarding headers are ignored.', 'logged-ai' ), 15 => __( 'The selected IP privacy mode applies before storage. A derived address is connection context, not a person. Changing this trust configuration immediately invalidates dependent exact-source targets; their physical cleanup is bounded and may need background retries. Path and query policies keep their own matching rules.', 'logged-ai' ), 16 => __( ' I control this proxy boundary and understand that a wrong trust set can misattribute client addresses.', 'logged-ai' ), 17 => __( 'Save request-source configuration', 'logged-ai' ), 18 => __( 'Observer self-test', 'logged-ai' ), 19 => __( 'This opens one harmless, authenticated URL on this site through the browser. A successful result establishes only that this routed request reached the normal plugin observer and its minimized synthetic evidence was admitted. It does not test static files, cache hits, CDN decisions or every path. Temporary policies still apply.', 'logged-ai' ), 20 => __( 'Not recorded', 'logged-ai' ), 21 => __( 'Run this routed observer test', 'logged-ai' ), ); $display_labels = PresentationTranslations::snapshot( array( 'Current test', 'Last successful observer test (display timezone)', 'Address derivation', 'Address quality', 'Manual policy response decision', 'A deliberately configured manual condition matched; this is a response decision, not an attack finding.', 'Ordinary benign requests can match a manual condition. No attack classification is inferred from this response.', 'Configuration-resource probe pattern', 'Exact reserved configuration filename or recognized backup variant.', 'An authorized scanner or broken integration can request these resources.', 'Source-control or diagnostic discovery', 'Exact source-control metadata or diagnostic resource template.', 'Authorized diagnostics can deliberately request these resources.', 'Traversal-shaped resource request', 'Two parent-directory segments precede a recognized configuration/system-resource suffix.', 'Malformed links and authorized scanners are possible; target application normalization is unknown.', 'SQL-shaped numeric parameter', 'A numeric-identifier parameter contains a compound boolean or UNION projection with a comment delimiter.', 'A test or code example can contain the same structure; no execution or intent is established.', 'Executable-markup-shaped parameter', 'A numeric/navigation parameter contains paired script markup, an event-handler tag, or an executable URL scheme.', 'Authorized testing can contain the same syntax. Editing/search content parameters are not classified.', 'Component metadata enumeration', 'A plugin readme or theme metadata path was requested; enumeration is not exploitation.', 'Documentation tools and legitimate theme stylesheets can use related routes; theme style.css is excluded.', 'Unusual method at a non-API resource', 'TRACE/TRACK/CONNECT or a write method was used on a reserved metadata/configuration resource.', 'Protocol/security tests may intentionally exercise methods; ordinary API writes and OPTIONS are excluded.', 'Partial analysis', 'Application effect', 'Authorized scanners, diagnostic tools and unrelated visitors can produce this pattern. A direct network peer may represent many people.', 'Repeated distinct reserved-resource requests', 'At least 10 complete admitted GET/HEAD findings cover at least three distinct safe reserved-resource templates in one fixed UTC minute.', 'Known configuration/source/diagnostic templates; complete parser result; this authorized site and source label. Status can be unknown.', 'Repeated distinct reserved resources with observed 404s', 'At least 10 complete admitted GET/HEAD findings have a finalized observed 404 across at least three distinct safe reserved-resource templates in one fixed UTC minute.', 'Request-sequence access is unavailable.', 'Request-sequence authority changed.', 'Current retained admitted findings only. Counts are lower bounds; missing telemetry is not quantifiable. Fixed UTC-minute boundaries can split a sequence. Site and exact-peer views overlap and must not be summed. No attacker identity, historical alert, exposure, prevention or automated-response eligibility is established.', 'Admitted / complete eligible', 'First / last (display timezone)', 'Safe templates', 'Known finalized 404s', 'Recorded local responses', 'Unknown final status', 'Window still open', 'Sample observation IDs', ) ); $scope = Scope::current( 'logged_ai_manage_settings' ); if ( is_wp_error( $scope ) ) { wp_die( esc_html( $display_text[ 0 ] ), '', array( 'response' => 403 ) ); } try { $time = TimeDisplay::site(); } catch ( \Throwable $error ) { wp_die( esc_html( $display_text[ 1 ] ), '', array( 'response' => 403 ) ); } nocache_headers(); $config = RequestsSource::view( $scope ); $test = RequestsSelfTest::status( $scope ); echo ( '
' . '

' . esc_html( $display_text[ 2 ] ) . '

' . '

' . esc_html( $display_text[ 3 ] ) . '

' ); echo ( '

' . sprintf( /* translators: 1: Value or formatting element shown in this message. */ esc_html( $display_text[ 4 ] ), esc_html( $time->zone() ) ) . '

' ); if ( is_wp_error( $config ) ) { echo ( '

' . ( esc_html( PresentationTranslations::text( $config->get_error_message() ) ) ) . '

' ); if ( 'source_config' === $config->get_error_code() ) { echo '
'; wp_nonce_field( 'logged_ai_source' ); echo ( '

' . '' . '

' . '' . '
' ); } echo '
'; return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This query flag displays only a fixed saved notice on an authorized read-only settings page. if ( isset( $request_query['saved'] ) ) { echo ( '
' . '

' . esc_html( $display_text[ 7 ] ) . '

' . '
' ); } if ( ! $scope->valid_for( 'logged_ai_view_sensitive_context' ) ) { echo ( '

' . sprintf( /* translators: 1: Value or formatting element shown in this message. 2: Value or formatting element shown in this message. */ esc_html( $display_text[ 8 ] ), esc_html( $config['header'] ), esc_html( (string) $config['trusted_count'] ) ) . '

' ); } else { echo '
'; wp_nonce_field( 'logged_ai_source' ); echo ( '

' . '' . '
' . '' . '

' ); $config = RequestsSource::view( $scope ); if ( is_wp_error( $config ) ) { wp_die( esc_html( $display_text[ 12 ] ), '', array( 'response' => 403 ) ); } echo ( '

' . '' . '
' . '' . '

' . '

' . esc_html( $display_text[ 14 ] ) . '

' ); echo ( '

' . esc_html( $display_text[ 15 ] ) . '

' . '

' . '' . '

' . '' . '
' ); } echo ( '

' . esc_html( $display_text[ 18 ] ) . '

' . '

' . esc_html( $display_text[ 19 ] ) . '

' ); if ( is_wp_error( $test ) ) { echo ( '

' . ( esc_html( PresentationTranslations::text( $test->get_error_message() ) ) ) . '

' ); } else { echo '
'; foreach ( array( 'state' => 'Current test', 'last_success_at' => 'Last successful observer test (display timezone)', 'source_derivation' => 'Address derivation', 'source_quality' => 'Address quality', ) as $key => $label ) { $value = $test[ $key ] ?? null; if ( 'last_success_at' === $key && is_int( $value ) ) { $value = $time->text( $value ); } echo ( '
' . ( esc_html( PresentationTranslations::lookup( $display_labels, $label ) ) ) . '
' . '
' . ( esc_html( ( null === $value ? $display_text[ 20 ] : str_replace( '_', ' ', (string) $value ) ) ) ) . '
' ); } echo '
'; } echo '
'; wp_nonce_field( 'logged_ai_source' ); echo ( '' . '
' . '' ); } }