$a[0] <=> $b[0] ); return hash( 'sha256', wp_json_encode( $canonical ) ); } /** * Validate supported automatic target structure, finite times and authenticated policy fields. * * @param array $policy Stored or newly compiled automatic-policy fields. * * @return bool Whether the policy satisfies the fixed automatic format and HMAC. */ public static function valid( array $policy ): bool { if ( ( $policy['parser_version'] ?? null ) !== self::VERSION || 'automatic' !== ( $policy['origin'] ?? null ) || 'ip' !== ( $policy['target_kind'] ?? null ) || ! in_array( $policy['target_erased'] ?? null, array( 0, '0' ), true ) || 'GET,HEAD' !== ( $policy['methods'] ?? null ) || 'automatic_source_condition' !== ( $policy['safe_path'] ?? null ) || ! RequestsAutomaticState::generation( $policy['id'] ?? null ) || ! RequestsAutomaticState::generation( $policy['auto_generation'] ?? null ) || str_repeat( '0', 32 ) === $policy['auto_generation'] || ! RequestsAutomaticState::generation( $policy['target_source_generation'] ?? null ) ) { return false; } $numbers = array(); foreach ( array( 'network_id', 'site_id', 'creator', 'revision', 'ttl', 'target_expires_at', 'target_captured_at', 'target_settings_revision', 'evidence_id', 'created_at', 'starts_at', 'expires_at' ) as $key ) { $numbers[ $key ] = RequestsAutomaticState::integer( $policy[ $key ] ?? null, 1 ); if ( null === $numbers[ $key ] ) { return false; } } if ( 1 !== $numbers['revision'] || ! in_array( $numbers['ttl'], RequestsAutomaticState::TTLS, true ) || $numbers['created_at'] !== $numbers['starts_at'] || $numbers['expires_at'] - $numbers['starts_at'] !== $numbers['ttl'] || $numbers['target_expires_at'] < $numbers['expires_at'] || $numbers['target_captured_at'] > $numbers['created_at'] || $numbers['target_captured_at'] <= $numbers['created_at'] - 60 ) { return false; } $json = $policy['target_json'] ?? null; $target = is_string( $json ) && strlen( $json ) <= 2048 ? json_decode( $json, true, 5 ) : null; if ( ! is_array( $target ) || array_keys( $target ) !== array( 'version', 'kind', 'ip', 'predicate', 'routes', 'proof' ) || 1 !== $target['version'] || 'ip' !== $target['kind'] || RequestsAutomaticSchema::PREDICATE !== $target['predicate'] || RequestsAutomaticSchema::ROUTES !== $target['routes'] || ! is_string( $target['ip'] ) || RequestsSource::address( $target['ip'] ) !== $target['ip'] || ! is_string( $target['proof'] ) || ! preg_match( '/\A[a-f0-9]{64}\z/D', $target['proof'] ) || ! is_string( $policy['source_request_uid'] ?? null ) || ! preg_match( '/\A[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}\z/D', $policy['source_request_uid'] ) || ! is_string( $policy['compiled_hash'] ?? null ) ) { return false; } return hash_equals( self::hash( $policy ), $policy['compiled_hash'] ); } /** * Present saved automatic policies as disabled when current dependencies are unavailable. * * Projection never calls a saved policy active when its runtime dependency is currently unavailable. * * @param array $rows Bounded native rows already selected for the method's operation. * * @return array Policy rows with runtime-unavailable automatic state labelled disabled. */ public static function project_states( array $rows ): array { if ( ! array_filter( $rows, static fn( array $row ): bool => 'automatic' === ( $row['origin'] ?? null ) && 'active' === ( $row['state'] ?? null ) ) ) { return $rows; } $db = RequestsAutomaticState::connection(); $available = false; $state = null; if ( ! is_wp_error( $db ) ) { try { $state = RequestsAutomaticState::read( $db ); $context = RequestsAutomaticState::context( $db ); $available = ! is_wp_error( $state ) && ! is_wp_error( $context ) && 'active' === $state['mode'] && RequestsAutomaticState::bound( $state, $context ) && $context['response_enabled'] && ! Requests::recovery_disabled() && BackgroundAuthority::allowed( $state['operator_id'], $state['network_id'], $state['site_id'], 'automatic_operator' ); } finally { $db->close(); } } foreach ( $rows as &$row ) { if ( 'automatic' === ( $row['origin'] ?? null ) && 'active' === ( $row['state'] ?? null ) && ( ! $available || $row['auto_generation'] !== $state['generation'] ) ) { $row['state'] = 'disabled'; $row['availability_reason'] = 'automatic_context_unavailable'; } } unset( $row ); return $rows; } }