Plugin Directory

Changeset 3391996


Ignore:
Timestamp:
11/08/2025 02:06:55 AM (5 months ago)
Author:
volixta
Message:
  • Improved code compliance: added PHPCS annotations for dynamic SQL clauses to avoid false warnings.
  • Ensured all SQL queries remain fully prepared and secure.
  • Improved SSL detection to recognize valid certificates even when the site still uses HTTP.
  • Added admin notice suggesting HTTPS activation when a valid SSL is detected.
  • Updated UI for clearer SSL and security headers status display.
  • Internal cleanup for plugin review and coding standards validation.
Location:
volixta-ssl-security-headers/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • volixta-ssl-security-headers/trunk/NOTICE.txt

    r3388964 r3391996  
    11Volixta SSL & Security Headers — Trademark & Licensing Notice
    2 Version: 1.0.10
     2Version: 1.1.0
    33Date: 2025-09-15
    44
  • volixta-ssl-security-headers/trunk/includes/helpers.php

    r3371940 r3391996  
    228228function volissam_has_valid_ssl(): bool
    229229{
    230     if (! volissam_request_is_https()) return false;
    231     if (volissam_is_localhost()) return false;
    232230    $host = volissam_current_host();
    233231    if ($host === '') return false;
    234     $ctx  = @stream_context_create(['ssl' => [
    235         'capture_peer_cert' => true,
    236         'SNI_enabled' => true,
    237         'peer_name' => $host,
    238     ]]);
    239     $conn = @stream_socket_client("ssl://" . $host . ":443", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $ctx);
    240     if (! $conn) return true; // best-effort: request is HTTPS, do not block admin flow
    241     $params = stream_context_get_params($conn);
    242     if (empty($params['options']['ssl']['peer_certificate'])) return true;
    243     $parsed = @openssl_x509_parse($params['options']['ssl']['peer_certificate']);
    244     if (! $parsed) return true;
    245     $now  = time();
    246     $from = $parsed['validFrom_time_t'] ?? 0;
    247     $to   = $parsed['validTo_time_t']   ?? 0;
    248     if ($now < $from || $now > $to) return false;
    249     return volissam_hostname_matches_cert($host, $parsed);
    250 }
     232    if (volissam_is_localhost()) return false;
     233
     234    // --- Étape 1 : si la requête actuelle est déjà HTTPS
     235    if (volissam_request_is_https()) {
     236        $ctx  = @stream_context_create(['ssl' => [
     237            'capture_peer_cert' => true,
     238            'SNI_enabled' => true,
     239            'peer_name' => $host,
     240        ]]);
     241        $conn = @stream_socket_client("ssl://{$host}:443", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $ctx);
     242        if (!$conn) return true; // best-effort
     243        $params = stream_context_get_params($conn);
     244        if (empty($params['options']['ssl']['peer_certificate'])) return true;
     245        $parsed = @openssl_x509_parse($params['options']['ssl']['peer_certificate']);
     246        if (!$parsed) return true;
     247        $now  = time();
     248        $from = $parsed['validFrom_time_t'] ?? 0;
     249        $to   = $parsed['validTo_time_t'] ?? 0;
     250        if ($now < $from || $now > $to) return false;
     251        return volissam_hostname_matches_cert($host, $parsed);
     252    }
     253
     254    // --- Étape 2 : le site est encore en HTTP, test HTTPS distant
     255    $url = 'https://' . $host;
     256    $response = wp_remote_get($url, [
     257        'timeout'     => 5,
     258        'redirection' => 2,
     259        'sslverify'   => true, // on veut détecter un vrai certificat public
     260    ]);
     261
     262    if (is_wp_error($response)) {
     263        return false;
     264    }
     265
     266    $code = wp_remote_retrieve_response_code($response);
     267    if ($code >= 200 && $code < 400) {
     268        // SSL répond, même si WP est encore en HTTP
     269        return true;
     270    }
     271
     272    return false;
     273}
     274
    251275
    252276// ======= SSL parsing helpers <<<<<
  • volixta-ssl-security-headers/trunk/includes/mixed-content-fixer.php

    r3371940 r3391996  
    11<?php
    22// Exit if accessed directly.
    3 if (! defined('ABSPATH')) {
    4     exit;
    5 }
     3if ( ! defined( 'ABSPATH' ) ) {
     4    exit;
     5}
     6
     7// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
     8
    69
    710/**
     
    912 * Returns an array of pairs: [['http'=>'http://ex.com','https'=>'https://ex.com'], ...]
    1013 */
    11 function volissam_mcf_base_scheme_candidates(): array
    12 {
    13     $home = (string) get_option('home');
    14     $host = wp_parse_url($home, PHP_URL_HOST);
    15     $port = wp_parse_url($home, PHP_URL_PORT);
    16 
    17     if (! is_string($host) || $host === '') {
    18         if (function_exists('volissam_current_host')) {
    19             $host = volissam_current_host();
    20         } else {
    21             $host = isset($_SERVER['HTTP_HOST'])
    22                 ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST']))
    23                 : '';
    24             $host = strtolower(preg_replace('#:\d+$#', '', trim($host)));
    25         }
    26     }
    27 
    28     $cands = [];
    29     if ($host !== '') {
    30         $cands[] = $host;
    31         if (stripos($host, 'www.') === 0) {
    32             $cands[] = substr($host, 4);
    33         } else {
    34             $cands[] = 'www.' . $host;
    35         }
    36     }
    37     $cands = array_values(array_unique(array_filter($cands)));
    38 
    39     $out = [];
    40     foreach ($cands as $h) {
    41         $hp    = $h . ($port ? ':' . (int) $port : '');
    42         $out[] = ['http' => 'http://' . $hp, 'https' => 'https://' . $hp];
    43     }
    44     return $out ?: [['http' => 'http://', 'https' => 'https://']]; // safe fallback
     14function volissam_mcf_base_scheme_candidates(): array {
     15    $home = (string) get_option( 'home' );
     16    $host = wp_parse_url( $home, PHP_URL_HOST );
     17    $port = wp_parse_url( $home, PHP_URL_PORT );
     18
     19    if ( ! is_string( $host ) || $host === '' ) {
     20        if ( function_exists( 'volissam_current_host' ) ) {
     21            $host = volissam_current_host();
     22        } else {
     23            $host = isset( $_SERVER['HTTP_HOST'] )
     24                ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) )
     25                : '';
     26            $host = strtolower( preg_replace( '#:\d+$#', '', trim( $host ) ) );
     27        }
     28    }
     29
     30    $cands = [];
     31    if ( $host !== '' ) {
     32        $cands[] = $host;
     33        if ( stripos( $host, 'www.' ) === 0 ) {
     34            $cands[] = substr( $host, 4 );
     35        } else {
     36            $cands[] = 'www.' . $host;
     37        }
     38    }
     39    $cands = array_values( array_unique( array_filter( $cands ) ) );
     40
     41    $out = [];
     42    foreach ( $cands as $h ) {
     43        $hp    = $h . ( $port ? ':' . (int) $port : '' );
     44        $out[] = [ 'http' => 'http://' . $hp, 'https' => 'https://' . $hp ];
     45    }
     46    return $out ?: [ [ 'http' => 'http://', 'https' => 'https://' ] ]; // safe fallback
    4547}
    4648
     
    4850 * Recursive, serialization-safe http->https replacement.
    4951 */
    50 function volissam_mcf_recursive_replace($data, string $http, string $https)
    51 {
    52     if (is_string($data)) {
    53         return str_replace($http, $https, $data);
    54     }
    55     if (is_array($data)) {
    56         foreach ($data as $k => $v) {
    57             $data[$k] = volissam_mcf_recursive_replace($v, $http, $https);
    58         }
    59         return $data;
    60     }
    61     if (is_object($data)) {
    62         foreach (get_object_vars($data) as $prop => $v) {
    63             $data->$prop = volissam_mcf_recursive_replace($v, $http, $https);
    64         }
    65         return $data;
    66     }
    67     return $data;
     52function volissam_mcf_recursive_replace( $data, string $http, string $https ) {
     53    if ( is_string( $data ) ) {
     54        return str_replace( $http, $https, $data );
     55    }
     56    if ( is_array( $data ) ) {
     57        foreach ( $data as $k => $v ) {
     58            $data[ $k ] = volissam_mcf_recursive_replace( $v, $http, $https );
     59        }
     60        return $data;
     61    }
     62    if ( is_object( $data ) ) {
     63        foreach ( get_object_vars( $data ) as $prop => $v ) {
     64            $data->$prop = volissam_mcf_recursive_replace( $v, $http, $https );
     65        }
     66        return $data;
     67    }
     68    return $data;
    6869}
    6970
     
    7374 * @return array { sql: string, params: array }
    7475 */
    75 function volissam_mcf_like_or_clause(string $column, array $http_pairs, callable $esc_like): array
    76 {
    77     $likes  = [];
    78     $params = [];
    79     foreach ($http_pairs as $pair) {
    80         $likes[]  = "$column LIKE %s";
    81         $params[] = '%' . $esc_like($pair['http']) . '%';
    82     }
    83     $sql = '(' . implode(' OR ', $likes) . ')';
    84     return ['sql' => $sql, 'params' => $params];
     76function volissam_mcf_like_or_clause( string $column, array $http_pairs, callable $esc_like ): array {
     77    $likes  = [];
     78    $params = [];
     79    foreach ( $http_pairs as $pair ) {
     80        $likes[]  = "$column LIKE %s";
     81        $params[] = '%' . $esc_like( $pair['http'] ) . '%';
     82    }
     83    $sql = '(' . implode( ' OR ', $likes ) . ')';
     84    return [ 'sql' => $sql, 'params' => $params ];
    8585}
    8686
     
    9090 * @return array { sql: string, params: array }
    9191 */
    92 function volissam_mcf_replace_expr(string $column, array $pairs): array
    93 {
    94     $expr   = $column;
    95     $params = [];
    96     foreach ($pairs as $pair) {
    97         $expr     = "REPLACE($expr, %s, %s)";
    98         $params[] = $pair['http'];
    99         $params[] = $pair['https'];
    100     }
    101     return ['sql' => $expr, 'params' => $params];
     92function volissam_mcf_replace_expr( string $column, array $pairs ): array {
     93    $expr   = $column;
     94    $params = [];
     95    foreach ( $pairs as $pair ) {
     96        $expr     = "REPLACE($expr, %s, %s)";
     97        $params[] = $pair['http'];
     98        $params[] = $pair['https'];
     99    }
     100    return [ 'sql' => $expr, 'params' => $params ];
    102101}
    103102
     
    107106 * @return array { posts, postmeta, options, total }
    108107 */
    109 function volissam_mcf_scan(): array
    110 {
    111     global $wpdb;
    112 
    113     if (function_exists('wp_raise_memory_limit')) {
    114         wp_raise_memory_limit('admin');
    115     }
    116     if (function_exists('set_time_limit')) {
    117         @set_time_limit(300); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
    118     }
    119 
    120     $pairs = volissam_mcf_base_scheme_candidates();
    121 
    122     // -------- POSTS --------
    123     $clause = volissam_mcf_like_or_clause('post_content', $pairs, [$wpdb, 'esc_like']);
    124     $cache_key_posts = 'volissam_mcf_scan_posts_' . md5(maybe_serialize($clause['params']));
    125     $posts = wp_cache_get($cache_key_posts, 'volissam_mcf');
    126     if ($posts === false) {
    127         /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
    128         $posts = (int) $wpdb->get_var(
    129             $wpdb->prepare(
    130                 "SELECT COUNT(*) FROM {$wpdb->posts} WHERE {$clause['sql']}",
    131                 ...$clause['params']
    132             )
    133         );
    134         /* phpcs:enable */
    135         wp_cache_set($cache_key_posts, $posts, 'volissam_mcf', 60);
    136     }
    137 
    138     // -------- POSTMETA --------
    139     $clause = volissam_mcf_like_or_clause('meta_value', $pairs, [$wpdb, 'esc_like']);
    140     $cache_key_meta = 'volissam_mcf_scan_postmeta_' . md5(maybe_serialize($clause['params']));
    141     $postmeta = wp_cache_get($cache_key_meta, 'volissam_mcf');
    142     if ($postmeta === false) {
    143         /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
    144         $postmeta = (int) $wpdb->get_var(
    145             $wpdb->prepare(
    146                 "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE {$clause['sql']}",
    147                 ...$clause['params']
    148             )
    149         );
    150         /* phpcs:enable */
    151         wp_cache_set($cache_key_meta, $postmeta, 'volissam_mcf', 60);
    152     }
    153 
    154     // -------- OPTIONS (exclure transients) --------
    155     $clause = volissam_mcf_like_or_clause('option_value', $pairs, [$wpdb, 'esc_like']);
    156     $ex1 = $wpdb->esc_like('_transient_%');
    157     $ex2 = $wpdb->esc_like('_site_transient_%');
    158 
    159     $cache_key_opts = 'volissam_mcf_scan_options_' . md5(maybe_serialize([$ex1, $ex2, $clause['params']]));
    160     $options = wp_cache_get($cache_key_opts, 'volissam_mcf');
    161     if ($options === false) {
    162         /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
    163         $options = (int) $wpdb->get_var(
    164             $wpdb->prepare(
    165                 "SELECT COUNT(*) FROM {$wpdb->options}
    166              WHERE option_name NOT IN ('home','siteurl')
    167                AND option_name NOT LIKE %s
    168                AND option_name NOT LIKE %s
    169                AND {$clause['sql']}",
    170                 $ex1,
    171                 $ex2,
    172                 ...$clause['params']
    173             )
    174         );
    175         /* phpcs:enable */
    176         wp_cache_set($cache_key_opts, $options, 'volissam_mcf', 60);
    177     }
    178 
    179 
    180     $total = $posts + $postmeta + $options;
    181     return compact('posts', 'postmeta', 'options', 'total');
    182 }
    183 
     108function volissam_mcf_scan(): array {
     109    global $wpdb;
     110
     111    if ( function_exists( 'wp_raise_memory_limit' ) ) {
     112        wp_raise_memory_limit( 'admin' );
     113    }
     114    if ( function_exists( 'set_time_limit' ) ) {
     115        @set_time_limit( 300 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
     116    }
     117
     118    $pairs = volissam_mcf_base_scheme_candidates();
     119
     120    // -------- POSTS --------
     121    $clause           = volissam_mcf_like_or_clause( 'post_content', $pairs, [ $wpdb, 'esc_like' ] );
     122    $cache_key_posts  = 'volissam_mcf_scan_posts_' . md5( maybe_serialize( $clause['params'] ) );
     123    $posts = wp_cache_get( $cache_key_posts, 'volissam_mcf' );
     124    if ( $posts === false ) {
     125        /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
     126        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
     127        $posts = (int) $wpdb->get_var(
     128            $wpdb->prepare(
     129                "SELECT COUNT(*) FROM {$wpdb->posts} WHERE {$clause['sql']}",
     130                ...$clause['params']
     131            )
     132        );
     133        wp_cache_set( $cache_key_posts, $posts, 'volissam_mcf', 60 );
     134    }
     135
     136    // -------- POSTMETA --------
     137    $clause            = volissam_mcf_like_or_clause( 'meta_value', $pairs, [ $wpdb, 'esc_like' ] );
     138    $cache_key_meta    = 'volissam_mcf_scan_postmeta_' . md5( maybe_serialize( $clause['params'] ) );
     139    $postmeta = wp_cache_get( $cache_key_meta, 'volissam_mcf' );
     140    if ( $postmeta === false ) {
     141        /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
     142        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
     143        $postmeta = (int) $wpdb->get_var(
     144            $wpdb->prepare(
     145                "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE {$clause['sql']}",
     146                ...$clause['params']
     147            )
     148        );
     149        wp_cache_set( $cache_key_meta, $postmeta, 'volissam_mcf', 60 );
     150    }
     151
     152    // -------- OPTIONS (exclude transients) --------
     153    $clause = volissam_mcf_like_or_clause( 'option_value', $pairs, [ $wpdb, 'esc_like' ] );
     154    $ex1    = $wpdb->esc_like( '_transient_%' );
     155    $ex2    = $wpdb->esc_like( '_site_transient_%' );
     156
     157    $cache_key_opts = 'volissam_mcf_scan_options_' . md5( maybe_serialize( [ $ex1, $ex2, $clause['params'] ] ) );
     158    $options = wp_cache_get( $cache_key_opts, 'volissam_mcf' );
     159    if ( $options === false ) {
     160        /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
     161        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
     162        $options = (int) $wpdb->get_var(
     163            $wpdb->prepare(
     164                "SELECT COUNT(*) FROM {$wpdb->options}
     165                WHERE option_name NOT IN ('home','siteurl')
     166                AND option_name NOT LIKE %s
     167                AND option_name NOT LIKE %s
     168                AND {$clause['sql']}",
     169                $ex1,
     170                $ex2,
     171                ...$clause['params']
     172            )
     173        );
     174        wp_cache_set( $cache_key_opts, $options, 'volissam_mcf', 60 );
     175    }
     176
     177    $total = $posts + $postmeta + $options;
     178    return compact( 'posts', 'postmeta', 'options', 'total' );
     179}
    184180
    185181/**
     
    188184 * @return array { posts, postmeta, options, total }
    189185 */
    190 function volissam_mcf_fix(): array
    191 {
    192     global $wpdb;
    193 
    194     if (function_exists('wp_raise_memory_limit')) {
    195         wp_raise_memory_limit('admin');
    196     }
    197     if (function_exists('set_time_limit')) {
    198         @set_time_limit(300); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
    199     }
    200     if (function_exists('wp_defer_term_counting')) {
    201         wp_defer_term_counting(true);
    202     }
    203     if (function_exists('wp_defer_comment_counting')) {
    204         wp_defer_comment_counting(true);
    205     }
    206 
    207     $pairs         = volissam_mcf_base_scheme_candidates();
    208     $updated_posts = 0;
    209     $updated_meta  = 0;
    210     $updated_opts  = 0;
    211 
    212     // POSTS: single UPDATE with nested REPLACE + WHERE LIKE(OR)
    213     if ($pairs) {
    214         $rep    = volissam_mcf_replace_expr('post_content', $pairs);
    215         $clause = volissam_mcf_like_or_clause('post_content', $pairs, [$wpdb, 'esc_like']);
    216 
    217         // $rep['sql'] and $clause['sql'] are safe fragments composed of placeholders only.
    218         /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */
    219         $updated_posts = (int) $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    220             $wpdb->prepare(
    221                 "UPDATE {$wpdb->posts}
    222          SET post_content = {$rep['sql']}
    223          WHERE {$clause['sql']}",
    224                 ...array_merge($rep['params'], $clause['params'])
    225             )
    226         );
    227         /* phpcs:enable */
    228     }
    229 
    230     // POSTMETA: batch, serialization-safe
    231     $last_id = 0;
    232     do {
    233         $clause = volissam_mcf_like_or_clause('meta_value', $pairs, [$wpdb, 'esc_like']);
    234 
    235         /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber */
    236         $rows = $wpdb->get_results(
    237             $wpdb->prepare(
    238                 "SELECT meta_id, meta_value
    239              FROM {$wpdb->postmeta}
    240              WHERE meta_id > %d AND {$clause['sql']}
    241              ORDER BY meta_id ASC
    242              LIMIT 2000",
    243                 $last_id,
    244                 ...$clause['params']
    245             )
    246         );
    247         /* phpcs:enable */
    248 
    249         if (! $rows) {
    250             break;
    251         }
    252 
    253         foreach ($rows as $row) {
    254             $last_id = (int) $row->meta_id;
    255 
    256             $orig = maybe_unserialize($row->meta_value);
    257             $repl = $orig;
    258 
    259             foreach ($pairs as $p) {
    260                 $repl = volissam_mcf_recursive_replace($repl, $p['http'], $p['https']);
    261             }
    262 
    263             if ($repl !== $orig) {
    264                 // Uses the Metadata API (no direct DB call; handles serialization & cache)
    265                 update_metadata_by_mid('post', (int) $row->meta_id, $repl);
    266                 $updated_meta++;
    267             }
    268         }
    269     } while (true);
    270 
    271     // OPTIONS: batch, serialization-safe (exclude transients)
    272     $ex1     = $wpdb->esc_like('_transient_%');
    273     $ex2     = $wpdb->esc_like('_site_transient_%');
    274     $last_id = 0;
    275 
    276     do {
    277         $clause = volissam_mcf_like_or_clause('option_value', $pairs, [$wpdb, 'esc_like']);
    278 
    279         /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber */
    280         $rows = $wpdb->get_results(
    281             $wpdb->prepare(
    282                 "SELECT option_id, option_name, option_value, autoload
    283          FROM {$wpdb->options}
    284          WHERE option_id > %d
    285            AND option_name NOT IN ('home','siteurl')
    286            AND option_name NOT LIKE %s
    287            AND option_name NOT LIKE %s
    288            AND {$clause['sql']}
    289          ORDER BY option_id ASC
    290          LIMIT 1000",
    291                 $last_id,
    292                 $ex1,
    293                 $ex2,
    294                 ...$clause['params']
    295             )
    296         );
    297         /* phpcs:enable */
    298 
    299 
    300         if (! $rows) {
    301             break;
    302         }
    303 
    304         foreach ($rows as $row) {
    305             $last_id = (int) $row->option_id;
    306 
    307             $name = (string) $row->option_name;
    308             $orig = maybe_unserialize($row->option_value);
    309             $repl = $orig;
    310 
    311             foreach ($pairs as $p) {
    312                 $repl = volissam_mcf_recursive_replace($repl, $p['http'], $p['https']);
    313             }
    314 
    315             if ($repl !== $orig) {
    316                 $autoload_yes = ($row->autoload === 'yes');
    317                 update_option($name, $repl, $autoload_yes);
    318                 $updated_opts++;
    319             }
    320         }
    321     } while (true);
    322 
    323     if (function_exists('wp_defer_term_counting')) {
    324         wp_defer_term_counting(false);
    325     }
    326     if (function_exists('wp_defer_comment_counting')) {
    327         wp_defer_comment_counting(false);
    328     }
    329 
    330     $total = $updated_posts + $updated_meta + $updated_opts;
    331 
    332     return [
    333         'posts'    => $updated_posts,
    334         'postmeta' => $updated_meta,
    335         'options'  => $updated_opts,
    336         'total'    => $total,
    337     ];
    338 }
     186function volissam_mcf_fix(): array {
     187    global $wpdb;
     188
     189    if ( function_exists( 'wp_raise_memory_limit' ) ) {
     190        wp_raise_memory_limit( 'admin' );
     191    }
     192    if ( function_exists( 'set_time_limit' ) ) {
     193        @set_time_limit( 300 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged
     194    }
     195    if ( function_exists( 'wp_defer_term_counting' ) ) {
     196        wp_defer_term_counting( true );
     197    }
     198    if ( function_exists( 'wp_defer_comment_counting' ) ) {
     199        wp_defer_comment_counting( true );
     200    }
     201
     202    $pairs         = volissam_mcf_base_scheme_candidates();
     203    $updated_posts = 0;
     204    $updated_meta  = 0;
     205    $updated_opts  = 0;
     206
     207    // POSTS
     208    if ( $pairs ) {
     209        $rep    = volissam_mcf_replace_expr( 'post_content', $pairs );
     210        $clause = volissam_mcf_like_or_clause( 'post_content', $pairs, [ $wpdb, 'esc_like' ] );
     211
     212        /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
     213        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
     214        $updated_posts = (int) $wpdb->query(
     215            $wpdb->prepare(
     216                "UPDATE {$wpdb->posts}
     217                SET post_content = {$rep['sql']}
     218                WHERE {$clause['sql']}",
     219                ...array_merge( $rep['params'], $clause['params'] )
     220            )
     221        );
     222    }
     223
     224    // POSTMETA
     225    $last_id = 0;
     226    do {
     227        $clause = volissam_mcf_like_or_clause( 'meta_value', $pairs, [ $wpdb, 'esc_like' ] );
     228
     229        /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
     230        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
     231        $rows = $wpdb->get_results(
     232            $wpdb->prepare(
     233                "SELECT meta_id, meta_value
     234                FROM {$wpdb->postmeta}
     235                WHERE meta_id > %d AND {$clause['sql']}
     236                ORDER BY meta_id ASC
     237                LIMIT 2000",
     238                $last_id,
     239                ...$clause['params']
     240            )
     241        );
     242
     243        if ( ! $rows ) {
     244            break;
     245        }
     246
     247        foreach ( $rows as $row ) {
     248            $last_id = (int) $row->meta_id;
     249            $orig    = maybe_unserialize( $row->meta_value );
     250            $repl    = $orig;
     251
     252            foreach ( $pairs as $p ) {
     253                $repl = volissam_mcf_recursive_replace( $repl, $p['http'], $p['https'] );
     254            }
     255
     256            if ( $repl !== $orig ) {
     257                update_metadata_by_mid( 'post', (int) $row->meta_id, $repl );
     258                $updated_meta++;
     259            }
     260        }
     261    } while ( true );
     262
     263    // OPTIONS
     264    $ex1     = $wpdb->esc_like( '_transient_%' );
     265    $ex2     = $wpdb->esc_like( '_site_transient_%' );
     266    $last_id = 0;
     267
     268    do {
     269        $clause = volissam_mcf_like_or_clause( 'option_value', $pairs, [ $wpdb, 'esc_like' ] );
     270
     271        /* phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching */
     272        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter
     273        $rows = $wpdb->get_results(
     274            $wpdb->prepare(
     275                "SELECT option_id, option_name, option_value, autoload
     276                FROM {$wpdb->options}
     277                WHERE option_id > %d
     278                AND option_name NOT IN ('home','siteurl')
     279                AND option_name NOT LIKE %s
     280                AND option_name NOT LIKE %s
     281                AND {$clause['sql']}
     282                ORDER BY option_id ASC
     283                LIMIT 1000",
     284                $last_id,
     285                $ex1,
     286                $ex2,
     287                ...$clause['params']
     288            )
     289        );
     290
     291        if ( ! $rows ) {
     292            break;
     293        }
     294
     295        foreach ( $rows as $row ) {
     296            $last_id = (int) $row->option_id;
     297            $name    = (string) $row->option_name;
     298            $orig    = maybe_unserialize( $row->option_value );
     299            $repl    = $orig;
     300
     301            foreach ( $pairs as $p ) {
     302                $repl = volissam_mcf_recursive_replace( $repl, $p['http'], $p['https'] );
     303            }
     304
     305            if ( $repl !== $orig ) {
     306                $autoload_yes = ( $row->autoload === 'yes' );
     307                update_option( $name, $repl, $autoload_yes );
     308                $updated_opts++;
     309            }
     310        }
     311    } while ( true );
     312
     313    if ( function_exists( 'wp_defer_term_counting' ) ) {
     314        wp_defer_term_counting( false );
     315    }
     316    if ( function_exists( 'wp_defer_comment_counting' ) ) {
     317        wp_defer_comment_counting( false );
     318    }
     319
     320    $total = $updated_posts + $updated_meta + $updated_opts;
     321
     322    return [
     323        'posts'    => $updated_posts,
     324        'postmeta' => $updated_meta,
     325        'options'  => $updated_opts,
     326        'total'    => $total,
     327    ];
     328}
     329// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
  • volixta-ssl-security-headers/trunk/readme.txt

    r3388964 r3391996  
    33Tags: security headers, mixed content, ssl, https
    44Requires at least: 5.8
    5 Tested up to: 6.8.3
    6 Stable tag: 1.0.10
     5Tested up to: 6.8
     6Stable tag: 1.1.0
    77Requires PHP: 7.4
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Easily enable SSL/HTTPS in WordPress, force 301 redirects, fix mixed content, and apply modern security headers (HSTS, CSP, X-Frame-Options). Safe, admin-only toolkit.
     11Add modern security headers, enable SSL/HTTPS, fix mixed content, and force 301 redirects. Fast and safe.
    1212
    1313== Description ==
     
    115115
    116116== Changelog ==
     117= 1.1.0 – 2025-11-08 =
     118- Improved code compliance: added PHPCS annotations for dynamic SQL clauses to avoid false warnings.
     119- Ensured all SQL queries remain fully prepared and secure.
     120- Improved SSL detection to recognize valid certificates even when the site still uses HTTP.
     121- Added admin notice suggesting HTTPS activation when a valid SSL is detected.
     122- Updated UI for clearer SSL and security headers status display.
     123- Internal cleanup for plugin review and coding standards validation.
     124
    117125= 1.0.10 =
    118126* Updated readme.txt
  • volixta-ssl-security-headers/trunk/uninstall.php

    r3371940 r3391996  
    11<?php
    22// Exit if uninstall not called from WordPress.
    3 if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
     3if (! defined('WP_UNINSTALL_PLUGIN')) {
    44    exit;
    55}
    66
    77// Markers
    8 defined( 'VOLISSAM_MARKER' )          || define( 'VOLISSAM_MARKER', 'Volixta Security Headers' );
    9 defined( 'VOLISSAM_REDIRECT_MARKER' ) || define( 'VOLISSAM_REDIRECT_MARKER', 'Volixta HTTPS Redirect' );
     8defined('VOLISSAM_MARKER')          || define('VOLISSAM_MARKER', 'Volixta Security Headers');
     9defined('VOLISSAM_REDIRECT_MARKER') || define('VOLISSAM_REDIRECT_MARKER', 'Volixta HTTPS Redirect');
    1010
    1111// WP includes
    12 if ( ! function_exists( 'trailingslashit' ) ) {
     12if (! function_exists('trailingslashit')) {
    1313    require_once ABSPATH . 'wp-includes/formatting.php';
    1414}
    15 if ( ! function_exists( 'get_home_path' ) ) {
     15if (! function_exists('get_home_path')) {
    1616    require_once ABSPATH . 'wp-admin/includes/file.php';
    1717}
    1818
    1919// --- Helpers
    20 function volissam_uninstall_htaccess_path(): string {
    21     $home_path = function_exists( 'get_home_path' ) ? get_home_path() : ABSPATH;
    22     return trailingslashit( $home_path ) . '.htaccess';
     20function volissam_uninstall_htaccess_path(): string
     21{
     22    $home_path = function_exists('get_home_path') ? get_home_path() : ABSPATH;
     23    return trailingslashit($home_path) . '.htaccess';
    2324}
    2425
     
    2627 * Supprime complètement un bloc .htaccess (y compris # BEGIN / # END).
    2728 */
    28 function volissam_uninstall_remove_block_fully( string $marker ): void {
     29function volissam_uninstall_remove_block_fully(string $marker): void
     30{
    2931    $file = volissam_uninstall_htaccess_path();
    30     if ( ! $file || ! file_exists( $file ) || ! wp_is_writable( $file ) ) {
     32    if (! $file || ! file_exists($file) || ! wp_is_writable($file)) {
    3133        return;
    3234    }
    3335
    34     $contents = file_get_contents( $file );
    35     if ( $contents === false ) {
     36    $contents = file_get_contents($file);
     37    if ($contents === false) {
    3638        return;
    3739    }
     
    4042    $pattern = sprintf(
    4143        '/\n?# BEGIN %1$s.*?# END %1$s\n?/s',
    42         preg_quote( $marker, '/' )
     44        preg_quote($marker, '/')
    4345    );
    44     $new = preg_replace( $pattern, "\n", $contents );
     46    $new = preg_replace($pattern, "\n", $contents);
    4547
    46     if ( $new !== null && $new !== $contents ) {
    47         file_put_contents( $file, $new );
     48    if ($new !== null && $new !== $contents) {
     49        file_put_contents($file, $new);
    4850    }
    4951}
    5052
    51 function volissam_uninstall_delete_all_options_for_blog(): void {
    52     delete_option( 'volissam_php_redirect_enabled' );
    53     delete_option( 'volissam_headers_options' );
    54     delete_option( 'volissam_mcf_last_scan' );   // Mixed Content Fixer last scan
     53function volissam_uninstall_delete_all_options_for_blog(): void
     54{
     55    delete_option('volissam_php_redirect_enabled');
     56    delete_option('volissam_headers_options');
     57    delete_option('volissam_mcf_last_scan');   // Mixed Content Fixer last scan
    5558    // Transients used in admin UI
    56     delete_transient( 'volissam_flash_notice' );
    57     delete_transient( 'volissam_last_error' );
     59    delete_transient('volissam_flash_notice');
     60    delete_transient('volissam_last_error');
    5861}
    5962
    6063// --- Cleanup .htaccess backups
    61 function volissam_uninstall_cleanup_ht_backups(): void {
     64function volissam_uninstall_cleanup_ht_backups(): void
     65{
    6266    $file = volissam_uninstall_htaccess_path();
    63     if ( ! $file ) {
     67    if (! $file) {
    6468        return;
    6569    }
    6670
    6771    $pattern = $file . '.bak-*';
    68     $files   = @glob( $pattern, GLOB_NOSORT ) ?: [];
     72    $files   = @glob($pattern, GLOB_NOSORT) ?: [];
    6973
    70     foreach ( $files as $bak ) {
     74    foreach ($files as $bak) {
    7175        // security: only delete expected pattern in same directory
    72         if ( is_file( $bak ) && strpos( $bak, basename( $file ) . '.bak-' ) !== false ) {
    73             wp_delete_file( $bak );
     76        if (is_file($bak) && strpos($bak, basename($file) . '.bak-') !== false) {
     77            wp_delete_file($bak);
    7478        }
    7579    }
     
    7781
    7882// --- Exécution désinstallation globale (seulement sur site principal en multisite)
    79 if ( ! is_multisite() || is_main_site() ) {
     83if (! is_multisite() || is_main_site()) {
    8084    volissam_uninstall_cleanup_ht_backups();
    81     volissam_uninstall_remove_block_fully( VOLISSAM_MARKER );
    82     volissam_uninstall_remove_block_fully( VOLISSAM_REDIRECT_MARKER );
     85    volissam_uninstall_remove_block_fully(VOLISSAM_MARKER);
     86    volissam_uninstall_remove_block_fully(VOLISSAM_REDIRECT_MARKER);
    8387}
    8488
    8589// --- Remove options (single / multisite)
    86 if ( is_multisite() ) {
    87     $page = 1;
     90if (is_multisite()) {
     91    $volissam_page = 1;
    8892    do {
    89         $sites = get_sites(
    90             [
    91                 'fields' => 'ids',
    92                 'number' => 200,
    93                 'offset' => ( $page - 1 ) * 200,
    94             ]
    95         );
    96         foreach ( $sites as $blog_id ) {
    97             switch_to_blog( $blog_id );
     93        $volissam_sites = get_sites([
     94            'fields' => 'ids',
     95            'number' => 200,
     96            'offset' => ($volissam_page - 1) * 200,
     97        ]);
     98        foreach ($volissam_sites as $blog_id) {
     99            switch_to_blog($blog_id);
    98100            volissam_uninstall_delete_all_options_for_blog();
    99101            restore_current_blog();
    100102        }
    101         $page++;
    102     } while ( ! empty( $sites ) && count( $sites ) === 200 );
     103        $volissam_page++;
     104    } while (! empty($volissam_sites) && count($volissam_sites) === 200);
    103105
    104106    // Cleanup site-wide options
    105     delete_site_option( 'volissam_php_redirect_enabled' );
    106     delete_site_option( 'volissam_headers_options' );
    107     delete_site_option( 'volissam_mcf_last_scan' );
     107    delete_site_option('volissam_php_redirect_enabled');
     108    delete_site_option('volissam_headers_options');
     109    delete_site_option('volissam_mcf_last_scan');
    108110} else {
    109111    volissam_uninstall_delete_all_options_for_blog();
  • volixta-ssl-security-headers/trunk/views/admin-page.php

    r3371940 r3391996  
    22if (! defined('ABSPATH')) exit;
    33/** @var array $state */
    4 $notice = $state['notice'] ?? null;
    5 $cfg    = $state['headersConfig'];
    6 $ms_sub = (is_multisite() && !is_main_site());
     4$volissam_notice = $state['notice'] ?? null;
     5$volissam_cfg    = $state['headersConfig'];
     6$volissam_ms_sub = (is_multisite() && !is_main_site());
    77?>
    88<div class="wrap volissam-wrap" data-volissam-state="<?php echo esc_attr(wp_json_encode($state)); ?>">
    99    <h1>🔒 Volixta SSL & Security Headers</h1>
    1010
    11     <?php if (!empty($notice) && is_array($notice)): ?>
    12         <div class="notice notice-<?php echo esc_attr($notice[0]); ?>">
    13             <p><?php echo esc_html($notice[1]); ?></p>
     11    <?php if (!empty($volissam_notice) && is_array($volissam_notice)): ?>
     12        <div class="notice notice-<?php echo esc_attr($volissam_notice[0]); ?>">
     13            <p><?php echo esc_html($volissam_notice[1]); ?></p>
    1414        </div>
    1515    <?php endif; ?>
     
    4646                    <td><?php echo $state['sslValid'] ? '✅ ' . esc_html__('Yes', 'volixta-ssl-security-headers') : '❌ ' . esc_html__('No', 'volixta-ssl-security-headers'); ?></td>
    4747                </tr>
     48                <?php if (!$state['httpsRequest'] && $state['sslValid']): ?>
     49                    <tr>
     50                        <th><?php echo esc_html__('SSL detected on server', 'volixta-ssl-security-headers'); ?></th>
     51                        <td>🌐 <?php echo esc_html__('Yes, valid certificate found even if site is HTTP', 'volixta-ssl-security-headers'); ?></td>
     52                    </tr>
     53                <?php endif; ?>
     54
    4855                <?php if (!$state['isLocal'] && !empty($state['sslInfo'])):
    49                     $info = $state['sslInfo'];
    50                     $exp  = !empty($info['validTo']) ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), (int)$info['validTo']) : '';
    51                     $days = isset($info['daysRemaining']) ? (int)$info['daysRemaining'] : null;
    52                     $soon = is_int($days) && $days >= 0 && $days <= 30;
     56                    $volissam_notice = $state['sslInfo'];
     57                    $volissam_exp  = !empty($volissam_notice['validTo']) ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), (int)$volissam_notice['validTo']) : '';
     58                    $volissam_days = isset($volissam_notice['daysRemaining']) ? (int)$volissam_notice['daysRemaining'] : null;
     59                    $volissam_soon = is_int($volissam_days) && $volissam_days >= 0 && $volissam_days <= 30;
    5360                ?>
    5461                    <tr>
    5562                        <th><?php echo esc_html__('SSL issuer', 'volixta-ssl-security-headers'); ?></th>
    56                         <td><?php echo $info['issuer'] ? esc_html($info['issuer']) : '<em>' . esc_html__('Unknown', 'volixta-ssl-security-headers') . '</em>'; ?></td>
     63                        <td><?php echo $volissam_notice['issuer'] ? esc_html($volissam_notice['issuer']) : '<em>' . esc_html__('Unknown', 'volixta-ssl-security-headers') . '</em>'; ?></td>
    5764                    </tr>
    5865                    <tr>
    5966                        <th><?php echo esc_html__('SSL expires', 'volixta-ssl-security-headers'); ?></th>
    6067                        <td>
    61                             <?php if ($exp): ?>
    62                                 <span><?php echo esc_html($exp); ?></span>
    63                                 <?php if (is_int($days) && $days >= 0): ?>
    64                                     <span style="margin-left:8px;<?php echo $soon ? 'color:#b71c1c;font-weight:600' : 'opacity:.8'; ?>">
     68                            <?php if ($volissam_exp): ?>
     69                                <span><?php echo esc_html($volissam_exp); ?></span>
     70                                <?php if (is_int($volissam_days) && $volissam_days >= 0): ?>
     71                                    <span style="margin-left:8px;<?php echo $volissam_soon ? 'color:#b71c1c;font-weight:600' : 'opacity:.8'; ?>">
    6572                                        <?php
    66                                         if ($soon) {
     73                                        if ($volissam_soon) {
    6774                                            printf(
    6875                                                /* translators: %d: Number of days until the SSL certificate expires (certificate expires soon) */
    6976                                                esc_html__('%d days remaining (renew soon)', 'volixta-ssl-security-headers'),
    70                                                 esc_html(absint($days))
     77                                                esc_html(absint($volissam_days))
    7178                                            );
    7279                                        } else {
     
    7481                                                /* translators: %d: Number of days until the SSL certificate expires */
    7582                                                esc_html__('%d days remaining', 'volixta-ssl-security-headers'),
    76                                                 esc_html(absint($days))
     83                                                esc_html(absint($volissam_days))
    7784                                            );
    7885                                        }
     
    122129    </section>
    123130
    124     <?php if ($ms_sub): ?>
     131    <?php if ($volissam_ms_sub): ?>
    125132        <div class="notice notice-info">
    126133            <p><?php echo esc_html__('Multisite: .htaccess is shared across the network. Only the main site can modify it.', 'volixta-ssl-security-headers'); ?></p>
     
    137144
    138145    <?php if ($state['isLocal']): ?>
    139         <?php $can_switch_local = ($state['httpsRequest'] && !$state['wpHttps']); ?>
     146        <?php $volissam_can_switch_local = ($state['httpsRequest'] && !$state['wpHttps']); ?>
    140147        <section class="volissam-panel">
    141148            <h2><?php echo esc_html__('Local HTTPS (mkcert)', 'volixta-ssl-security-headers'); ?></h2>
    142             <?php $https_url = preg_replace('/^http:/', 'https:', home_url('/')); ?>
     149            <?php $volissam_https_url = preg_replace('/^http:/', 'https:', home_url('/')); ?>
    143150            <p><?php echo wp_kses_post(__('To get a trusted local certificate (no browser warnings), use <code>mkcert</code> or enable a local SSL certificate in your local tool.', 'volixta-ssl-security-headers')); ?></p>
    144151            <ol style="margin-left:1em">
     
    162169                <?php wp_nonce_field('volissam_action', 'volissam_nonce'); ?>
    163170                <input type="hidden" name="volissam_action" value="activate_wp_ssl" />
    164                 <button class="button button-primary volissam-confirm" type="submit" <?php echo $can_switch_local ? '' : 'disabled'; ?>>
     171                <button class="button button-primary volissam-confirm" type="submit" <?php echo $volissam_can_switch_local ? '' : 'disabled'; ?>>
    165172                    <?php echo esc_html__('Activate HTTPS for WordPress (local)', 'volixta-ssl-security-headers'); ?>
    166173                </button>
     
    196203
    197204                <div class="volissam-row">
    198                     <?php $redirectApplied = ($state['htRedirect'] || $state['phpRedirectOn']); ?>
     205                    <?php $volissam_redirect_applied = ($state['htRedirect'] || $state['phpRedirectOn']); ?>
    199206                    <form method="post" class="volissam-actions" data-volissam-action="enable_https_redirect">
    200207                        <?php wp_nonce_field('volissam_action', 'volissam_nonce'); ?>
    201208                        <input type="hidden" name="volissam_action" value="enable_https_redirect" />
    202209                        <button class="button volissam-confirm" type="submit"
    203                             <?php echo ($redirectApplied || ! $state['sslValid']) ? 'disabled' : ''; ?>>
     210                            <?php echo ($volissam_redirect_applied || ! $state['sslValid']) ? 'disabled' : ''; ?>>
    204211                            🚀 <?php echo esc_html__('Enable HTTPS Redirect (301)', 'volixta-ssl-security-headers'); ?>
    205212                        </button>
     
    210217                        <?php endif; ?>
    211218                    </form>
    212                     <?php if ($redirectApplied): ?>
     219                    <?php if ($volissam_redirect_applied): ?>
    213220                        <form method="post" class="volissam-actions" data-volissam-action="disable_https_redirect">
    214221                            <?php wp_nonce_field('volissam_action', 'volissam_nonce'); ?>
     
    348355                        </thead>
    349356                        <tbody>
    350                             <?php foreach ($cfg as $name => $row):
    351                                 $key     = volissam_field_key($name);
    352                                 $enabled = !empty($row['enabled']);
    353                                 $value   = (string)($row['value'] ?? '');
     357                            <?php foreach ($volissam_cfg as $volissam_name => $volissam_row):
     358                                $volissam_key     = volissam_field_key($volissam_name);
     359                                $volissam_enabled = !empty($volissam_row['enabled']);
     360                                $volissam_value   = (string)($volissam_row['value'] ?? '');
    354361                            ?>
    355362                                <tr>
    356                                     <td><code><?php echo esc_html($name); ?></code></td>
    357                                     <td><input type="checkbox" name="enable_<?php echo esc_attr($key); ?>" <?php checked($enabled); ?> /></td>
    358                                     <td><input type="text" name="value_<?php echo esc_attr($key); ?>" value="<?php echo esc_attr($value); ?>" /></td>
     363                                    <td><code><?php echo esc_html($volissam_name); ?></code></td>
     364                                    <td><input type="checkbox" name="enable_<?php echo esc_attr($volissam_key); ?>" <?php checked($volissam_enabled); ?> /></td>
     365                                    <td><input type="text" name="value_<?php echo esc_attr($volissam_key); ?>" value="<?php echo esc_attr($volissam_value); ?>" /></td>
    359366                                </tr>
    360367                            <?php endforeach; ?>
  • volixta-ssl-security-headers/trunk/volixta-ssl-security-headers.php

    r3388964 r3391996  
    44 * Plugin Name: Volixta SSL & Security Headers
    55 * Description: Activate SSL/HTTPS, apply modern Security Headers (incl. HSTS), fix mixed content, file-permissions audit, — simple & safe.
    6  * Version: 1.0.10
     6 * Version: 1.1.0
    77 * Author: HELLO SITE LLC
    88 * Author URI: https://www.agence-hello-site.com/
     
    1212 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1313 * Requires at least: 5.8
    14  * Tested up to: 6.8.3
     14 * Tested up to: 6.8
    1515 * Requires PHP: 7.4
    1616 */
     
    1818if (! defined('ABSPATH')) exit;
    1919
    20 define('VOLISSAM_VERSION', '1.0.10');
     20define('VOLISSAM_VERSION', '1.1.0');
    2121define('VOLISSAM_MARKER', 'Volixta Security Headers');
    2222define('VOLISSAM_REDIRECT_MARKER', 'Volixta HTTPS Redirect');
     
    3131
    3232
    33 // Load Mixed Content Fixer only in admin.
     33// Load Mixed Content Fixer only in admin area.
    3434if (is_admin()) {
    35     $mcf = plugin_dir_path(__FILE__) . 'includes/mixed-content-fixer.php';
    36     if (file_exists($mcf)) {
    37         require_once $mcf;
    38     }
    39 }
     35    $volissam_mcf_file = plugin_dir_path(__FILE__) . 'includes/mixed-content-fixer.php';
     36
     37    if (file_exists($volissam_mcf_file)) {
     38        require_once $volissam_mcf_file; // Safe inclusion with plugin prefix.
     39    }
     40}
     41
    4042
    4143/**
Note: See TracChangeset for help on using the changeset viewer.