$maximum * 2 ) { return ''; } $value = sanitize_text_field( wp_unslash( $values[ $key ] ) ); return strlen( $value ) <= $maximum && $value === wp_unslash( $values[ $key ] ) ? $value : ''; } /** Preserve credential bytes; accept only the provider's bounded visible-ASCII token. */ private static function feedToken( array $values ): string { if ( ! isset( $values['feed_token'] ) || ! is_string( $values['feed_token'] ) || strlen( $values['feed_token'] ) > 1024 ) { return ''; } $raw = trim( wp_unslash( $values['feed_token'] ) ); $token = preg_replace( '/[^\x21-\x7e]/', '', $raw ); return is_string( $token ) && $token === $raw && strlen( $token ) >= 8 && strlen( $token ) <= 512 ? $token : ''; } private static function begin( string $operation ): void { echo '
' . esc_html( $id ) . ' — ' . esc_html( $label ) . '' . esc_html__( 'If a legitimate request is blocked, use your host’s file manager to set ANTIVIRUS_AI_DISABLE_FIREWALL to true in wp-config.php. Review the cause before restoring protection. Activity records are sampled and exclude request values and IP addresses.', 'shieldlume-security' ) . '
' . esc_html__( 'Exceptions apply to one rule, exact decoded path, method and parameter for seven days. Use query.name, form.name or json.name; nested fields use dots (for example json.address.city). Overlong or ambiguous field names cannot be excepted. A request matching another rule can still be blocked. Saved paths and reasons remain local; avoid personal data.', 'shieldlume-security' ) . '
'; foreach ( $config['exceptions'] as $exception ) { if ( ! is_array( $exception ) ) { continue; } echo '' . esc_html( ( $exception['method'] ?? '' ) . ' ' . ( $exception['path'] ?? '' ) ) . ' · ' . esc_html( ( $exception['rule'] ?? '' ) . ' · ' . ( $exception['parameter'] ?? '' ) . ' · expires ' . gmdate( 'Y-m-d H:i', (int) ( $exception['expires'] ?? 0 ) ) . ' UTC' ) . '
'; self::begin( 'exception_remove' ); echo ''; submit_button( __( 'Remove exception', 'shieldlume-security' ), 'secondary', 'submit', false ); echo ''; } self::begin( 'exception_add' ); echo ''; foreach ( array( 'path' => array( 'Exact path', '/contact/', 200 ), 'parameter' => array( 'Parameter scope (or @path)', 'form.message', 128 ), 'reason' => array( 'Reason for this exception', '', 300 ) ) as $key => [$label, $placeholder, $max] ) { echo ''; } submit_button( __( 'Add seven-day exception', 'shieldlume-security' ), 'secondary', 'submit', false ); echo '' . esc_html__( 'A test uses the recipient and independent email permission already saved in Settings. It includes no finding or source content. Check your inbox yourself; an accepted attempt only confirms the mail service accepted it. Requests are limited to one every five minutes.', 'shieldlume-security' ) . '
'; self::begin( 'notification_test' ); submit_button( __( 'Queue a test notification', 'shieldlume-security' ), 'secondary', 'submit', false ); echo '' . esc_html__( 'Manage notification permission', 'shieldlume-security' ) . '
' . esc_html__( 'The bundled original structural checks update with the plugin. Additional signed SHA-256 indicators require an operator-trusted publisher and a reviewed bundle. A signature authenticates its publisher; detection quality depends on that publisher’s evidence and maintenance.', 'shieldlume-security' ) . '
' . esc_html__( 'Additional indicators:', 'shieldlume-security' ) . ' ' . esc_html( str_replace( '_', ' ', $rules['status'] ) ) . ' · ' . (int) $rules['indicator_count'] . '
' . esc_html( $rules['limitation'] ) . '
'; if ( RuleBundle::trustedKeys() ) { self::begin( 'rules_import' ); echo ''; submit_button( __( 'Verify and install bundle', 'shieldlume-security' ), 'secondary', 'submit', false ); echo ''; } else { echo '' . esc_html__( 'No external indicator publisher is configured. Local structural checks remain available; no maintained malware hash corpus is included in this release.', 'shieldlume-security' ) . '
'; } self::ruleConnection(); echo '' . esc_html__( 'Connection:', 'shieldlume-security' ) . ' ' . esc_html( str_replace( '_', ' ', $state['status'] ) ) . '
' . esc_html( $state['limitation'] ) . '
'; if ( $state['configured'] ) { echo '' . esc_html__( 'Configured destination:', 'shieldlume-security' ) . ' ' . esc_html( $state['destination'] ) . ' · ' . esc_html__( 'Publisher privacy information', 'shieldlume-security' ) . '
' . esc_html__( 'Your host must configure a reviewed publisher and trusted public keys before this connection can be enabled. A configured connection is not evidence of an effective or maintained malware corpus.', 'shieldlume-security' ) . '
'; } // Keep withdrawal available even if the publisher configuration becomes invalid. self::begin( 'rules_disable' ); submit_button( __( 'Stop indicator downloads', 'shieldlume-security' ), 'secondary', 'submit', false ); echo ''; if ( $state['last_success'] ) { echo '' . esc_html__( 'Last verified download:', 'shieldlume-security' ) . ' ' . esc_html( $state['last_success'] ) . '
'; } if ( $state['missed_schedule'] ) { echo '' . esc_html__( 'The update check is overdue. Review the host scheduler and connection; saved indicators may expire.', 'shieldlume-security' ) . '
'; } echo '' . esc_html__( 'Connect your permitted Wordfence Intelligence token here. It is encrypted with this site’s WordPress authentication salts and never displayed again. Changing those salts requires reconnecting. On an authorized refresh, Wordfence receives the token and server network address; the full feed is downloaded and installed-component matching happens locally. No source code or customer records are sent.', 'shieldlume-security' ) . '
' . esc_html__( 'Connection:', 'shieldlume-security' ) . ' ' . esc_html( str_replace( '_', ' ', FeedConnection::status() ) ) . '
'; if ( is_string( getenv( 'ANTIVIRUS_AI_WORDFENCE_KEY' ) ) && '' !== getenv( 'ANTIVIRUS_AI_WORDFENCE_KEY' ) ) { echo '' . esc_html__( 'Feed access is managed by your host. Its token takes precedence; ask the host to change it.', 'shieldlume-security' ) . '
'; } $cache = get_option( 'antivirus_ai_vulnerability_cache', array() ); if ( ! empty( $cache['fetched'] ) ) { echo '' . esc_html__( 'Last successful feed download:', 'shieldlume-security' ) . ' ' . esc_html( gmdate( 'Y-m-d H:i', (int) $cache['fetched'] ) . ' UTC' ) . ' · ' . esc_html( time() - (int) $cache['fetched'] < 86400 ? 'within the daily cache window; inventory coverage is shown in scan results' : 'stale; request a refresh' ) . '
'; } echo '' . esc_html__( 'Get feed access and review provider terms', 'shieldlume-security' ) . '
'; self::begin( 'feed_save' ); echo ''; submit_button( __( 'Save encrypted token', 'shieldlume-security' ), 'primary', 'submit', false ); echo ''; self::begin( 'feed_refresh' ); submit_button( __( 'Check permitted feed now', 'shieldlume-security' ), 'secondary', 'submit', false ); echo ''; self::begin( 'feed_remove' ); submit_button( __( 'Disconnect and remove saved feed data', 'shieldlume-security' ), 'secondary', 'submit', false ); echo '' . esc_html__( 'Manage scan permissions and repository identities', 'shieldlume-security' ) . '