get_var( $wpdb->prepare( "SELECT option_value FROM %i WHERE option_name=%s LIMIT 1", $wpdb->options, 'logged_ai_settings' ) ); if ( '' !== $wpdb->last_error ) { return new \WP_Error( 'storage', 'Request alert retention is unavailable.' );} if ( null === $raw ) { return Settings::defaults()['request_days'] * DAY_IN_SECONDS;} $settings = maybe_unserialize( $raw ); $days = is_array( $settings ) ? ( $settings['request_days'] ?? null ) : null; if ( ! is_int( $days ) || $days < 1 || $days > 3650 ) { return new \WP_Error( 'settings', 'Request alert retention is invalid.' ); }return $days * DAY_IN_SECONDS; } /** * Project retained request findings into minimized alert facts with current eligibility. * * No address, raw query, path, body or source token is selected for alert processing. * * @param array $row Bounded retained request or option row, with null representing absence where supported. * @param int $retention Current validated request-retention duration in seconds. * * @return array Alert reference and finite facts with unavailable evidence explicitly labelled. */ private static function project( array $row, int $retention ): array { $analysis = json_decode( $row['evidence'], true, 8 ); $flags = is_array( $analysis ) ? ( $analysis['quality_flags'] ?? array() ) : array(); $flags = is_array( $flags ) ? array_values( array_filter( self::FLAGS, static fn( string $flag ): bool=>in_array( $flag, $flags, true ) ) ) : array(); $captured = strtotime( $row['captured_at'] . ' UTC' ); $eligible = is_array( $analysis ) && ( $analysis['version'] ?? '' ) === RequestsClassifier::VERSION && in_array( $row['alert_state'], array( 'pending', 'processed' ), true ) && self::qualifies( $row ) && false !== $captured && $captured >= time() - $retention; $status = null; $response = is_string( $row['response_at'] ) ? strtotime( $row['response_at'] . ' UTC' ) : false; if ( false !== $response && false !== $captured && $response >= $captured && $response <= time() && preg_match( '/\A[1-5][0-9]{2}\z/D', (string) $row['status'] ) ) { $status = (int) $row['status'];} return array( 'fact_kind' => 'request', 'id' => $row['id'], 'uid' => $row['request_uid'], 'network_id' => $row['network_id'], 'site_id' => $row['site_id'], 'ingested_at' => $row['ingested_at'], 'alert_revision' => $row['alert_revision'], 'code' => $eligible ? 'request.finding' : 'request.unavailable', 'payload' => wp_json_encode( array( 'source' => 'synthetic_test' === $row['source'] ? 'synthetic_test' : 'wordpress', 'facts' => array( 'family' => $eligible ? $row['family'] : 'unavailable', 'rule_id' => $eligible ? $row['rule_id'] : 'unavailable', 'rule_revision' => $eligible ? (int) $row['rule_revision'] : 0, 'partial' => '0' !== ( (string) $row['partial'] ), 'quality_flags' => $flags, 'status' => $status, 'own_response' => in_array( $row['own_response'], array( 'wordpress_response_issued', 'wordpress_response_unconfirmed' ), true ) ? $row['own_response'] : 'none', 'effect' => 'unknown', ), ) ), ); } /** * Read at most fifty current-scope pending request facts for alert processing. * * Internal worker adapter, always current site/network; pending selection has no high-water mark. * * @param int $network Selected current network identifier. * @param int $site Selected site identifier for the current operation. * * @return array|\WP_Error Minimized pending facts, an empty unavailable-schema result, or a scope/storage error. */ public static function pending( int $network, int $site ): array|\WP_Error { if ( ! self::ready() ) { return array();} if ( Scope::network() !== $network || get_current_blog_id() !== $site ) { return new \WP_Error( 'scope', 'Request alert scope changed.' );} $retention = self::retention(); if ( is_wp_error( $retention ) ) { return $retention;} global $wpdb; // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- COLUMNS is the fixed minimized request-fact projection; requests is a literal owned table kind and both scope IDs are bound. Read current pending request facts from custom storage; cached membership could retain erased evidence. $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT id,request_uid,network_id,site_id,captured_at,ingested_at,response_at,sensor,source,parser_version,family,rule_id,rule_revision,confidence,priority,partial,evidence,status,own_response,effect,alert_revision,alert_state FROM %i WHERE network_id=%d AND site_id=%d AND alert_state=\'pending\' ORDER BY id LIMIT 50', Requests::table( 'requests' ), $network, $site ), ARRAY_A ); if ( '' !== $wpdb->last_error || ! is_array( $rows ) ) { return new \WP_Error( 'storage', 'Pending request alert facts are unavailable.' ); }return array_map( static fn( array $row ): array=>self::project( $row, $retention ), $rows ); } /** * Reload one exact scoped request identity before alert processing. * * @param int $network Selected current network identifier. * @param int $site Selected site identifier for the current operation. * @param int $id Selected stored request or native account identifier for this operation. * @param string $uid Exact retained request UUID paired with its physical row identifier. * * @return array|\WP_Error|null Current minimized fact, null for unavailable membership, or a scope/storage error. */ public static function load( int $network, int $site, int $id, string $uid ): array|\WP_Error|null { if ( ! self::ready() ) { return null;} if ( Scope::network() !== $network || get_current_blog_id() !== $site ) { return new \WP_Error( 'scope', 'Request alert scope changed.' );} $retention = self::retention(); if ( is_wp_error( $retention ) ) { return $retention;} global $wpdb; // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- COLUMNS and the requests table kind are fixed; current scope, physical ID and exact request UID are prepared values. Reload the actual exact request ID and UUID before projecting a delivery fact. $row = $wpdb->get_row( $wpdb->prepare( 'SELECT id,request_uid,network_id,site_id,captured_at,ingested_at,response_at,sensor,source,parser_version,family,rule_id,rule_revision,confidence,priority,partial,evidence,status,own_response,effect,alert_revision,alert_state FROM %i WHERE network_id=%d AND site_id=%d AND id=%d AND request_uid=%s', Requests::table( 'requests' ), $network, $site, $id, $uid ), ARRAY_A ); if ( '' !== $wpdb->last_error ) { return new \WP_Error( 'storage', 'Request alert evidence is unavailable.' ); }return $row ? self::project( $row, $retention ) : null; } /** * Format finite request-alert facts with their coverage limits and authorized review link. * * @param array $event Already minimized request-alert fact returned by the fixed adapter. * * @return string Plain-text alert message without source address, raw target or body contents. */ public static function message( array $event ): string { $facts = json_decode( $event['payload'], true )['facts']; $rule = RequestsClassifier::rules()[ $facts['family'] ] ?? null; $text = 'Observed request finding #' . (int) $event['id'] . ".\nDetection: " . ( $rule ? $rule['reason'] : 'Unavailable' ) . "\nConfidence: " . ( $rule ? $rule['confidence'] : 'unavailable' ) . '; priority: ' . ( $rule ? $rule['priority'] : 'unavailable' ) . ".\nAnalysis: " . ( $facts['partial'] ? 'partial' : 'complete within parser limits' ) . '; quality flags: ' . ( $facts['quality_flags'] ? implode( ', ', $facts['quality_flags'] ) : 'none recorded' ) . ".\nFinal observed HTTP status: " . ( null === $facts['status'] ? 'unknown' : (string) $facts['status'] ) . ".\nLogged.ai response: " . ( 'wordpress_response_issued' === $facts['own_response'] ? 'temporary WordPress-layer response issued' : ( 'wordpress_response_unconfirmed' === $facts['own_response'] ? 'unconfirmed; no verified local issuance receipt' : 'none recorded' ) ) . ".\nApplication effect: unknown.\nThis pattern does not establish intent, identity, exposure or a successful attack. Counts and coverage are limited to admitted WordPress observations.\nReview authorized request evidence: " . admin_url( 'admin.php?page=logged-ai-requests&evidence=' . (int) $event['id'] ) . "\n"; return $text; } }