Plugin Directory

Changeset 3362830


Ignore:
Timestamp:
09/17/2025 01:22:05 AM (6 months ago)
Author:
marceljm
Message:

Fixing vulnerabilities

Location:
featured-image-from-url/trunk
Files:
1 added
14 edited

Legend:

Unmodified
Added
Removed
  • featured-image-from-url/trunk/admin/cron.php

    r3244867 r3362830  
    6666}
    6767
    68 function fifu_should_stop_job($option_name) {
    69     $field = $option_name . '_stop';
    70 
    71     global $wpdb;
    72     if ($wpdb->get_col("SELECT 1 FROM " . $wpdb->options . " WHERE option_name = '" . $field . "'")) {
    73         delete_option($field);
    74         return true;
    75     }
    76     return false;
    77 }
    78 
    7968function fifu_run_cron_now() {
    8069    wp_remote_request(site_url('wp-cron.php'));
  • featured-image-from-url/trunk/admin/db.php

    r3352132 r3362830  
    3535    }
    3636
    37     function get_types() {
    38         $post_types = fifu_get_post_types();
    39         return join("','", $post_types);
     37    function get_types(): string {
     38        $raw = (array) fifu_get_post_types();
     39
     40        // Sanitize and validate against registered post types
     41        $registered = get_post_types([], 'names'); // array of valid names
     42        $safe = [];
     43        foreach ($raw as $pt) {
     44            $pt = sanitize_key($pt);
     45            if ($pt !== '' && isset($registered[$pt])) {
     46                $safe[] = $pt;
     47            }
     48        }
     49        // Deduplicate while preserving order
     50        $safe = array_values(array_unique($safe));
     51        return implode("','", $safe);
     52    }
     53
     54    function sanitize_ids_csv($ids, bool $allow_zero = false): string {
     55        // Normalize $ids to an array
     56        if (is_string($ids)) {
     57            $ids = explode(',', $ids);
     58        } elseif (is_int($ids)) {
     59            $ids = [$ids];
     60        } elseif (!is_array($ids)) {
     61            $ids = [];
     62        }
     63
     64        $set = [];
     65        foreach ($ids as $id) {
     66            if (is_int($id)) {
     67                $n = $id;
     68            } elseif (is_string($id)) {
     69                $id = trim($id);
     70                if ($id === '' || !ctype_digit($id)) { // digits only
     71                    continue;
     72                }
     73                $n = (int) $id; // safe after ctype_digit
     74            } else {
     75                continue;
     76            }
     77
     78            if ($n > 0 || ($allow_zero && $n === 0)) {
     79                $set[$n] = true; // dedupe
     80            }
     81        }
     82
     83        if (!$set) {
     84            return '0'; // ensures valid "IN (0)" => no matches
     85        }
     86
     87        return implode(',', array_keys($set));
     88    }
     89
     90    // Sanitize a list of post types (array or CSV string) for safe IN (...) usage
     91    function sanitize_post_types_list($post_types) {
     92        // Normalize input to array
     93        if (is_string($post_types)) {
     94            $post_types = explode(',', str_replace(['"', "'"], '', $post_types));
     95        } elseif (!is_array($post_types)) {
     96            $post_types = [];
     97        }
     98
     99        // Whitelist of registered post types
     100        $registered = array_flip(get_post_types([], 'names'));
     101
     102        // Sanitize + dedupe
     103        $set = [];
     104        foreach ($post_types as $pt) {
     105            $pt = sanitize_key(trim((string) $pt)); // [a-z0-9_-], lowercased
     106            if ($pt === '' || !isset($registered[$pt])) {
     107                continue;
     108            }
     109            $set[$pt] = true;
     110        }
     111
     112        if (!$set) {
     113            // If used in a class context that defines $this->types, keep compatibility
     114            if (isset($this) && isset($this->types) && is_string($this->types) && $this->types !== '') {
     115                return $this->types;
     116            }
     117            // Safe default: match nothing
     118            return "''";
     119        }
     120
     121        $items = array_keys($set);
     122        // sanitize_key already guarantees safe charset; quoting is enough for IN (...)
     123        return "'" . implode("','", $items) . "'";
     124    }
     125
     126    function build_in_from_option_csv(string $base_key, string $option_name): array {
     127        $field = (string) get_option($option_name);
     128
     129        $keys = [$base_key];
     130        if ($field !== '') {
     131            foreach (explode(',', $field) as $k) {
     132                $k = trim($k);
     133                if ($k !== '')
     134                    $keys[] = $k;
     135            }
     136        }
     137        $keys = array_values(array_unique($keys));
     138
     139        $in = implode(',', array_fill(0, count($keys), '%s')); // e.g. ['fifu_isbn','custom1'] -> IN ('fifu_isbn','custom1')
     140        return [$in, $keys];
    40141    }
    41142
     
    45146    function delete_attachment_meta($ids, $is_ctgr) {
    46147        $ctgr_sql = $is_ctgr ? "AND p.post_name LIKE 'fifu-category%'" : "";
    47 
    48         $this->wpdb->query("
     148        $ids_csv = $this->sanitize_ids_csv($ids);
     149        $author = $this->author;
     150        $sql = "
    49151            DELETE pm
    50152            FROM {$this->postmeta} pm JOIN {$this->posts} p ON pm.post_id = p.id
    51153            WHERE pm.meta_key IN ('_wp_attached_file', '_wp_attachment_image_alt', '_wp_attachment_metadata')
    52             AND p.post_parent IN ({$ids})
    53             AND p.post_author = {$this->author}
     154            AND p.post_parent IN ({$ids_csv})
     155            AND p.post_author = %d
    54156            {$ctgr_sql}
    55         ");
     157        ";
     158        $this->wpdb->query($this->wpdb->prepare($sql, $author));
    56159    }
    57160
     
    73176    // has attachment created by FIFU
    74177    function is_fifu_attachment($att_id) {
    75         return $this->wpdb->get_row("
    76             SELECT 1
    77             FROM {$this->posts}
    78             WHERE id = {$att_id}
    79             AND post_author = {$this->author}"
    80                 ) != null;
     178        $sql = $this->wpdb->prepare(
     179                "SELECT 1 FROM {$this->posts} WHERE id = %d AND post_author = %d",
     180                (int) $att_id,
     181                $this->author
     182        );
     183        return $this->wpdb->get_row($sql) != null;
    81184    }
    82185
     
    84187    function get_att_id($post_parent, $url, $is_ctgr) {
    85188        $ctgr_sql = $is_ctgr ? "AND p.post_name LIKE 'fifu-category%'" : "";
    86         $result = $this->wpdb->get_results("
    87             SELECT pm.post_id
    88             FROM {$this->postmeta} pm
    89             WHERE pm.meta_key = '_wp_attached_file'
    90             AND pm.meta_value = '{$url}'
    91             AND pm.post_id IN (
    92                 SELECT p.id
    93                 FROM {$this->posts} p
    94                 WHERE p.post_parent = {$post_parent}
    95                 AND post_author = {$this->author}
    96                 {$ctgr_sql}
    97             )
    98             LIMIT 1
    99         ");
    100         return $result ? $result[0]->post_id : null;
     189        $sql = $this->wpdb->prepare(
     190                "SELECT pm.post_id
     191             FROM {$this->postmeta} pm
     192             WHERE pm.meta_key = '_wp_attached_file'
     193               AND pm.meta_value = %s
     194               AND pm.post_id IN (
     195                   SELECT p.id
     196                   FROM {$this->posts} p
     197                   WHERE p.post_parent = %d
     198                     AND post_author = %d {$ctgr_sql}
     199               )
     200             LIMIT 1",
     201                $url,
     202                (int) $post_parent,
     203                $this->author
     204        );
     205        $row = $this->wpdb->get_row($sql);
     206        return $row ? (int) $row->post_id : null;
    101207    }
    102208
     
    116222
    117223    function get_count_wp_posts_fifu() {
    118         return $this->wpdb->get_results("
    119             SELECT COUNT(1) AS amount
    120             FROM {$this->posts}
    121             WHERE post_author = {$this->author}
    122         ");
     224        $sql = $this->wpdb->prepare(
     225                "SELECT COUNT(1) AS amount FROM {$this->posts} WHERE post_author = %d",
     226                $this->author
     227        );
     228        return $this->wpdb->get_results($sql);
    123229    }
    124230
    125231    function get_count_wp_postmeta_fifu() {
    126         return $this->wpdb->get_results("
    127             SELECT COUNT(1) AS amount
    128             FROM {$this->postmeta}
    129             WHERE meta_key = '_wp_attached_file'
    130             AND EXISTS (
    131                 SELECT 1
    132                 FROM {$this->posts}
    133                 WHERE id = post_id
    134                 AND post_author = {$this->author}
    135             )
    136         ");
     232        $sql = $this->wpdb->prepare(
     233                "SELECT COUNT(1) AS amount
     234             FROM {$this->postmeta}
     235             WHERE meta_key = '_wp_attached_file'
     236               AND EXISTS (
     237                   SELECT 1 FROM {$this->posts}
     238                   WHERE id = post_id AND post_author = %d
     239               )",
     240                $this->author
     241        );
     242        return $this->wpdb->get_results($sql);
    137243    }
    138244
     
    142248
    143249    function debug_slug($slug) {
    144         $sql = $this->wpdb->prepare("SELECT ID, post_author, post_content, post_title, post_status, post_parent, post_content_filtered, guid, post_type FROM {$this->posts} WHERE post_name = %s", $slug);
     250        $sql = $this->wpdb->prepare(
     251                "SELECT ID, post_author, post_content, post_title, post_status, post_parent, post_content_filtered, guid, post_type
     252             FROM {$this->posts}
     253             WHERE post_name = %s
     254               AND post_status <> 'private'
     255               AND (post_password = '' OR post_password IS NULL)",
     256                $slug
     257        );
    145258        return $this->wpdb->get_results($sql);
    146259    }
     
    148261    function debug_postmeta($post_id) {
    149262        $sql = $this->wpdb->prepare("
    150             SELECT meta_key, meta_value
    151             FROM {$this->postmeta}
    152             WHERE post_id = %d
    153             AND (
    154                 meta_key LIKE 'fifu%'
    155                 OR meta_key IN ('_thumbnail_id', '_wp_attached_file', '_wp_attachment_image_alt', '_product_image_gallery', '_wc_additional_variation_images')
    156             )"
     263            SELECT pm.meta_key, pm.meta_value
     264            FROM {$this->postmeta} pm
     265            INNER JOIN {$this->posts} p ON p.ID = pm.post_id
     266            WHERE pm.post_id = %d
     267              AND p.post_status <> 'private'
     268              AND (p.post_password = '' OR p.post_password IS NULL)
     269              AND (
     270                  pm.meta_key LIKE 'fifu%'
     271                  OR pm.meta_key IN ('_thumbnail_id', '_wp_attached_file', '_wp_attachment_image_alt', '_product_image_gallery', '_wc_additional_variation_images')
     272              )"
    157273                , $post_id);
    158274        return $this->wpdb->get_results($sql);
     
    163279            SELECT post_author, post_content, post_title, post_status, post_parent, post_content_filtered, guid, post_type
    164280            FROM {$this->posts}
    165             WHERE id = %d"
     281            WHERE id = %d
     282            AND post_status <> 'private'
     283            AND (post_password = '' OR post_password IS NULL)"
    166284                , $id);
    167285        return $this->wpdb->get_results($sql);
     
    169287
    170288    function debug_metain() {
    171         $sql = $this->wpdb->prepare("SELECT * FROM {$this->fifu_meta_in}");
    172         return $this->wpdb->get_results($sql);
     289        // No placeholders here; do not call prepare()
     290        return $this->wpdb->get_results("SELECT * FROM {$this->fifu_meta_in}");
    173291    }
    174292
    175293    function debug_metaout() {
    176         $sql = $this->wpdb->prepare("SELECT * FROM {$this->fifu_meta_out}");
    177         return $this->wpdb->get_results($sql);
     294        // No placeholders here; do not call prepare()
     295        return $this->wpdb->get_results("SELECT * FROM {$this->fifu_meta_out}");
    178296    }
    179297
    180298    // count images without dimensions
    181299    function get_count_posts_without_dimensions() {
    182         return $this->wpdb->get_results("
     300        $author = $this->author;
     301        $sql = $this->wpdb->prepare("
    183302            SELECT COUNT(1) AS amount
    184303            FROM {$this->posts} p
     
    188307                WHERE p.id = b.post_id AND meta_key = '_wp_attachment_metadata'
    189308            )
    190             AND p.post_author = {$this->author}
    191         ");
     309            AND p.post_author = %d
     310        ", $author);
     311        return $this->wpdb->get_results($sql);
    192312    }
    193313
    194314    // count urls with metadata
    195315    function get_count_urls_with_metadata() {
    196         return $this->wpdb->get_results("
     316        $author = $this->author;
     317        $sql = $this->wpdb->prepare("
    197318            SELECT COUNT(1) AS amount
    198319            FROM {$this->posts} p
    199             WHERE p.post_author = {$this->author}
    200         ");
    201     }
    202 
    203     // count urls
     320            WHERE p.post_author = %d
     321        ", $author);
     322        return $this->wpdb->get_results($sql);
     323    }
     324
     325    // Count URLs across postmeta and termmeta (no UNION; no meta_value filters; no tm '%list%' filter)
    204326    function get_count_urls() {
    205         return $this->wpdb->get_results("
    206             SELECT SUM(id) AS amount
    207             FROM (
    208                 SELECT count(post_id) AS id
    209                 FROM {$this->postmeta} pm
    210                 WHERE pm.meta_key LIKE 'fifu_%'
    211                 AND pm.meta_key LIKE '%url%'
    212                 AND pm.meta_key NOT LIKE '%list%'
    213                 UNION
    214                 SELECT count(term_id) AS id
    215                 FROM {$this->termmeta} tm
    216                 WHERE tm.meta_key LIKE 'fifu_%'
    217                 AND tm.meta_key LIKE '%url%'
    218             ) x
    219         ");
     327        $sql = "
     328            SELECT
     329                (
     330                    SELECT COUNT(*)
     331                    FROM {$this->postmeta} AS pm
     332                    WHERE pm.meta_key LIKE 'fifu!_%' ESCAPE '!'
     333                    AND pm.meta_key LIKE '%url%'
     334                    AND pm.meta_key NOT LIKE '%list%'
     335                ) +
     336                (
     337                    SELECT COUNT(*)
     338                    FROM {$this->termmeta} AS tm
     339                    WHERE tm.meta_key LIKE 'fifu!_%' ESCAPE '!'
     340                    AND tm.meta_key LIKE '%url%'
     341                ) AS amount
     342        ";
     343        return (int) $this->wpdb->get_var($sql);
    220344    }
    221345
     
    250374    // get last (images/videos/sliders)
    251375    function get_last($meta_key) {
    252         return $this->wpdb->get_results("
    253             SELECT p.id, pm.meta_value
     376        $sql = $this->wpdb->prepare(
     377                "SELECT p.id, pm.meta_value
    254378            FROM {$this->posts} p
    255379            INNER JOIN {$this->postmeta} pm ON p.id = pm.post_id
    256             WHERE pm.meta_key = '{$meta_key}'
     380            WHERE pm.meta_key = %s
    257381            ORDER BY p.post_date DESC
    258             LIMIT 3
    259         ");
     382            LIMIT 3",
     383                $meta_key
     384        );
     385        return $this->wpdb->get_results($sql);
    260386    }
    261387
     
    270396    }
    271397
    272     // get attachments without post
     398    // get child posts (excluding the featured image) for a given post
    273399    function get_attachments_without_post($post_id) {
    274         $result = $this->wpdb->get_results("
    275             SELECT GROUP_CONCAT(id) AS ids
    276             FROM {$this->posts}
    277             WHERE post_parent = {$post_id}
    278             AND post_author = {$this->author}
    279             AND post_name NOT LIKE 'fifu-category%'
     400        $sql = $this->wpdb->prepare(
     401                "SELECT GROUP_CONCAT(p.ID) AS ids
     402            FROM {$this->posts} p
     403            WHERE p.post_parent = %d
     404            AND p.post_author = %d
     405            AND p.post_name NOT LIKE %s
    280406            AND NOT EXISTS (
    281                 SELECT 1
    282                 FROM {$this->postmeta}
    283                 WHERE post_id = post_parent
    284                 AND meta_key = '_thumbnail_id'
    285                 AND meta_value = id
    286             )
    287             GROUP BY post_parent
    288         ");
    289         return $result ? $result[0]->ids : null;
     407                SELECT 1
     408                FROM {$this->postmeta} pm2
     409                WHERE pm2.post_id = p.post_parent
     410                    AND pm2.meta_key = '_thumbnail_id'
     411                    AND pm2.meta_value = p.ID
     412            )",
     413                (int) $post_id,
     414                (int) $this->author,
     415                'fifu-category%' // no need for %% since it's a %s value
     416        );
     417
     418        // One row expected; return CSV string or null
     419        $ids_csv = $this->wpdb->get_var($sql);
     420        return $ids_csv ?: null;
    290421    }
    291422
    292423    function get_ctgr_attachments_without_post($term_id) {
    293         $result = $this->wpdb->get_results("
    294             SELECT GROUP_CONCAT(id) AS ids
    295             FROM {$this->posts}
    296             WHERE post_parent = {$term_id}
    297             AND post_author = {$this->author}
    298             AND post_name LIKE 'fifu-category%'
     424        $sql = $this->wpdb->prepare(
     425                "SELECT GROUP_CONCAT(p.ID) AS ids
     426            FROM {$this->posts} p
     427            WHERE p.post_parent = %d
     428            AND p.post_author = %d
     429            AND p.post_name LIKE %s
    299430            AND NOT EXISTS (
    300                 SELECT 1
    301                 FROM {$this->termmeta}
    302                 WHERE term_id = post_parent
    303                 AND meta_key = 'thumbnail_id'
    304                 AND meta_value = id
    305             )
    306             GROUP BY post_parent
    307         ");
    308         return $result ? $result[0]->ids : null;
     431                SELECT 1
     432                FROM {$this->termmeta} tm
     433                WHERE tm.term_id = p.post_parent
     434                    AND tm.meta_key = 'thumbnail_id'
     435                    AND tm.meta_value = p.ID
     436            )",
     437                (int) $term_id,
     438                (int) $this->author,
     439                'fifu-category%' // pass pattern as a value; no %% needed
     440        );
     441
     442        $ids_csv = $this->wpdb->get_var($sql);
     443        return $ids_csv ?: null;
    309444    }
    310445
    311446    function get_posts_without_featured_image($post_types) {
     447        $safe = $this->sanitize_post_types_list($post_types);
    312448        return $this->wpdb->get_results("
    313449            SELECT id, post_title
    314450            FROM {$this->posts}
    315             WHERE post_type IN ('$post_types')
     451            WHERE post_type IN ($safe)
    316452            AND post_status = 'publish'
    317453            AND NOT EXISTS (
     
    335471
    336472    function get_featured_and_gallery_ids($post_id) {
    337         return $this->wpdb->get_results("
    338             SELECT GROUP_CONCAT(meta_value SEPARATOR ',') as 'ids'
     473        $sql = $this->wpdb->prepare(
     474                "SELECT GROUP_CONCAT(meta_value SEPARATOR ',') as 'ids'
    339475            FROM {$this->postmeta}
    340             WHERE post_id = {$post_id}
    341             AND meta_key IN ('_thumbnail_id')
    342         ");
     476            WHERE post_id = %d
     477              AND meta_key IN ('_thumbnail_id')",
     478                (int) $post_id
     479        );
     480        return $this->wpdb->get_results($sql);
    343481    }
    344482
     
    353491
    354492    function delete_attachments($ids) {
    355         $this->wpdb->query("
    356             DELETE FROM {$this->posts}
    357             WHERE id IN ({$ids})
    358             AND post_author = {$this->author}
    359         ");
     493        $ids_csv = $this->sanitize_ids_csv($ids);
     494        $sql = $this->wpdb->prepare(
     495                "DELETE FROM {$this->posts} WHERE id IN ({$ids_csv}) AND post_author = %d",
     496                $this->author
     497        );
     498        $this->wpdb->query($sql);
    360499    }
    361500
    362501    function delete_attachment_meta_url_and_alt($ids) {
    363         $this->wpdb->query("
    364             DELETE FROM {$this->postmeta}
     502        $ids_csv = $this->sanitize_ids_csv($ids);
     503        $sql = $this->wpdb->prepare(
     504                "DELETE FROM {$this->postmeta}
    365505            WHERE meta_key IN ('_wp_attached_file', '_wp_attachment_image_alt', '_wp_attachment_metadata')
    366             AND post_id IN ({$ids})
    367             AND EXISTS (
    368                 SELECT 1
    369                 FROM {$this->posts}
    370                 WHERE id = post_id
    371                 AND post_author = {$this->author}
    372             )
    373         ");
     506              AND post_id IN ({$ids_csv})
     507              AND EXISTS (SELECT 1 FROM {$this->posts} WHERE id = post_id AND post_author = %d)",
     508                $this->author
     509        );
     510        $this->wpdb->query($sql);
    374511    }
    375512
     
    399536
    400537    function select_option_prefix($prefix) {
    401         return $this->wpdb->get_results("
    402             SELECT option_name, option_value
     538        if ($prefix === '')
     539            return []; // avoid SELECT all
     540        $like = $this->wpdb->esc_like($prefix) . '%'; // escape LIKE wildcards safely
     541        $sql = $this->wpdb->prepare(
     542                "SELECT option_name, option_value
    403543            FROM {$this->options}
    404             WHERE option_name LIKE '{$prefix}%'
    405             ORDER BY option_name
    406         ");
     544            WHERE option_name LIKE %s
     545            ORDER BY option_name",
     546                $like
     547        );
     548        return $this->wpdb->get_results($sql);
    407549    }
    408550
    409551    function delete_option_prefix($prefix) {
    410         $this->wpdb->query("
    411             DELETE
    412             FROM {$this->options}
    413             WHERE option_name LIKE '{$prefix}%'
    414         ");
     552        if ($prefix === '') {
     553            return 0; // safety: avoid deleting everything
     554        }
     555        $like = $this->wpdb->esc_like($prefix) . '%'; // escape % and _
     556        $sql_select = $this->wpdb->prepare(
     557                "SELECT option_name FROM {$this->options} WHERE option_name LIKE %s",
     558                $like
     559        );
     560        $options_to_delete = $this->wpdb->get_col($sql_select);
     561        $sql_delete = $this->wpdb->prepare(
     562                "DELETE FROM {$this->options} WHERE option_name LIKE %s",
     563                $like
     564        );
     565        $deleted_count = (int) $this->wpdb->query($sql_delete);
     566        // Clear cache for deleted options
     567        foreach ($options_to_delete as $option_name) {
     568            wp_cache_delete($option_name, 'options');
     569        }
     570        return $deleted_count;
    415571    }
    416572
     
    418574
    419575    function get_all_urls($page, $type, $keyword) {
     576        $page = max(0, (int) $page); // Ensure page is non-negative
    420577        $start = $page * 1000;
    421578
    422         $filter = "";
     579        // Posts filter
     580        $filter_posts = '';
    423581        if ($keyword) {
     582            $like = '%' . $this->wpdb->esc_like($keyword) . '%';
    424583            if ($type == 'title')
    425                 $filter = "AND p.post_title LIKE '%{$keyword}%'";
     584                $filter_posts = $this->wpdb->prepare('AND p.post_title LIKE %s', $like);
    426585            elseif ($type == 'url')
    427                 $filter = "AND pm.meta_value LIKE '%{$keyword}%'";
     586                $filter_posts = $this->wpdb->prepare('AND pm.meta_value LIKE %s', $like);
    428587        }
    429588
     
    432591                SELECT pm.meta_id, pm.post_id, pm.meta_value AS url, pm.meta_key, p.post_name, p.post_title, p.post_date, false AS category, null AS video_url
    433592                FROM {$this->postmeta} pm
    434                 INNER JOIN {$this->posts} p ON pm.post_id = p.id {$filter}
     593                INNER JOIN {$this->posts} p ON pm.post_id = p.id {$filter_posts}
    435594                WHERE pm.meta_key = 'fifu_image_url'
    436595                AND pm.meta_value NOT LIKE '%https://cdn.fifu.app/%'
     
    440599        ";
    441600        if (class_exists('WooCommerce')) {
    442             $filter = "";
     601            // Terms filter
     602            $filter_terms = '';
    443603            if ($keyword) {
     604                $like = '%' . $this->wpdb->esc_like($keyword) . '%';
    444605                if ($type == 'title')
    445                     $filter = "AND t.name LIKE '%{$keyword}%'";
     606                    $filter_terms = $this->wpdb->prepare('AND t.name LIKE %s', $like);
    446607                elseif ($type == 'url')
    447                     $filter = "AND tm.meta_value LIKE '%{$keyword}%'";
     608                    $filter_terms = $this->wpdb->prepare('AND tm.meta_value LIKE %s', $like);
    448609            }
    449610            $sql .= "
     
    452613                    SELECT tm.meta_id, tm.term_id AS post_id, tm.meta_value AS url, tm.meta_key, null AS post_name, t.name AS post_title, null AS post_date, true AS category, null AS video_url
    453614                    FROM {$this->termmeta} tm
    454                     INNER JOIN {$this->terms} t ON tm.term_id = t.term_id {$filter}
     615                    INNER JOIN {$this->terms} t ON tm.term_id = t.term_id {$filter_terms}
    455616                    WHERE tm.meta_key IN ('fifu_image_url')
    456617                    AND tm.meta_value NOT LIKE '%https://cdn.fifu.app/%'
     
    495656
    496657    function get_posts_with_internal_featured_image($page, $type, $keyword) {
    497         $start = $page * 1000;
     658        $start = max(0, (int) $page) * 1000;
    498659
    499660        $filter = "";
    500661        if ($keyword) {
    501             if ($type == 'title')
    502                 $filter = "AND p.post_title LIKE '%{$keyword}%'";
    503             elseif ($type == 'postid')
    504                 $filter = "AND pm.post_id = {$keyword}";
    505         }
     662            if ($type == 'title') {
     663                $like = '%' . $this->wpdb->esc_like($keyword) . '%';
     664                $filter = $this->wpdb->prepare('AND p.post_title LIKE %s', $like);
     665            } elseif ($type == 'postid') {
     666                $filter = $this->wpdb->prepare('AND pm.post_id = %d', (int) $keyword);
     667            }
     668        }
     669
     670        // Prepare author filter fragments once to avoid preparing the whole query later
     671        $author_clause_posts = $this->wpdb->prepare('AND att.post_author <> %d', $this->author);
     672        $author_clause_terms = $author_clause_posts;
    506673
    507674        $sql = "
     
    521688                    pm.meta_key = '_thumbnail_id'
    522689                    AND pm.meta_value = att.id
    523                     AND att.post_author <> {$this->author}
     690                    {$author_clause_posts}
    524691                )
    525692                WHERE NOT EXISTS (
     
    541708            $filter = "";
    542709            if ($keyword) {
    543                 if ($type == 'title')
    544                     $filter = "AND t.name LIKE '%{$keyword}%'";
    545                 elseif ($type == 'postid')
    546                     $filter = "AND tm.term_id = {$keyword}";
     710                if ($type == 'title') {
     711                    $like = '%' . $this->wpdb->esc_like($keyword) . '%';
     712                    $filter = $this->wpdb->prepare('AND t.name LIKE %s', $like);
     713                } elseif ($type == 'postid') {
     714                    $filter = $this->wpdb->prepare('AND tm.term_id = %d', (int) $keyword);
     715                }
    547716            }
    548717            $sql .= "
     
    563732                        tm.meta_key = 'thumbnail_id'
    564733                        AND tm.meta_value = att.id
    565                         AND att.post_author <> {$this->author}
     734                        {$author_clause_terms}
    566735                    )
    567736                    WHERE NOT EXISTS (
     
    582751
    583752    function get_posts_su($storage_ids) {
    584         if ($storage_ids) {
    585             $storage_ids = '"' . implode('","', $storage_ids) . '"';
    586             $filter_post_image = "AND SUBSTRING_INDEX(SUBSTRING_INDEX(pm.meta_value, '/', 5), '/', -1) IN ({$storage_ids})";
    587             $filter_term_image = "AND SUBSTRING_INDEX(SUBSTRING_INDEX(tm.meta_value, '/', 5), '/', -1) IN ({$storage_ids})";
    588         } else
     753        if (!empty($storage_ids)) {
     754            // normalize and drop empties
     755            $ids = array_values(array_filter(array_map('strval', (array) $storage_ids), static fn($v) => $v !== ''));
     756            if ($ids) {
     757                $in = implode(',', array_fill(0, count($ids), '%s'));
     758                $filter_post_image = $this->wpdb->prepare(
     759                        "AND SUBSTRING_INDEX(SUBSTRING_INDEX(pm.meta_value, '/', 5), '/', -1) IN ($in)", $ids
     760                );
     761                $filter_term_image = $this->wpdb->prepare(
     762                        "AND SUBSTRING_INDEX(SUBSTRING_INDEX(tm.meta_value, '/', 5), '/', -1) IN ($in)", $ids
     763                );
     764            } else {
     765                $filter_post_image = $filter_term_image = "";
     766            }
     767        } else {
    589768            $filter_post_image = $filter_term_image = "";
     769        }
    590770
    591771        $sql = "
     
    601781                INNER JOIN {$this->posts} p ON pm.post_id = p.id
    602782                WHERE pm.meta_key LIKE 'fifu_%image_url%'
    603                 AND pm.meta_value LIKE 'https://cdn.fifu.app/%'" .
    604                 $filter_post_image . "
     783                AND pm.meta_value LIKE 'https://cdn.fifu.app/%'
     784                {$filter_post_image}
    605785            )
    606786        ";
    607787        if (class_exists('WooCommerce')) {
    608             $sql .= "           
     788            $sql .= "
    609789                UNION
    610790                (
    611791                    SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(tm.meta_value, '/', 5), '/', -1) AS storage_id,
    612792                        t.name AS post_title,
    613                         null AS post_date,
     793                        NULL AS post_date,
    614794                        tm.meta_id,
    615795                        tm.term_id AS post_id,
     
    619799                    INNER JOIN {$this->terms} t ON tm.term_id = t.term_id
    620800                    WHERE tm.meta_key = 'fifu_image_url'
    621                     AND tm.meta_value LIKE 'https://cdn.fifu.app/%'" .
    622                     $filter_term_image . "
     801                    AND tm.meta_value LIKE 'https://cdn.fifu.app/%'
     802                    {$filter_term_image}
    623803                )
    624804            ";
    625805        }
     806
    626807        return $this->wpdb->get_results($sql);
    627808    }
     
    679860        $table = $is_ctgr ? $this->termmeta : $this->postmeta;
    680861
    681         $query = "
    682             INSERT INTO {$table} (meta_id, meta_value) VALUES ";
    683         $count = 0;
     862        $values = [];
     863        $args = [];
     864
    684865        foreach ($thumbnails as $thumbnail) {
    685866            $su_url = $this->get_su_url($bucket_id, $thumbnail->storage_id);
    686867
    687             if ($count++ != 0)
    688                 $query .= ", ";
    689             $query .= "({$thumbnail->meta_id},'{$su_url}') ";
    690         }
    691         $query .= "ON DUPLICATE KEY UPDATE meta_value=VALUES(meta_value)";
    692         return $this->wpdb->get_results($query);
     868            $values[] = '(%d,%s)';
     869            $args[] = (int) $thumbnail->meta_id;
     870            $args[] = $su_url;
     871        }
     872
     873        if (!$values)
     874            return 0;
     875
     876        $query = "
     877            INSERT INTO {$table} (meta_id, meta_value)
     878            VALUES " . implode(', ', $values) . "
     879            ON DUPLICATE KEY UPDATE meta_value = VALUES(meta_value)
     880        ";
     881
     882        return $this->wpdb->query($this->wpdb->prepare($query, $args));
    693883    }
    694884
    695885    function get_thumbnail_ids($thumbnails, $is_ctgr) {
    696         // join post_ids
    697         $i = 0;
    698         $ids = null;
     886        // join post_ids (sanitized)
     887        $ids_list = array();
    699888        foreach ($thumbnails as $thumbnail)
    700             $ids = ($i++ == 0) ? $thumbnail->post_id : ($ids . "," . $thumbnail->post_id);
     889            $ids_list[] = (int) $thumbnail->post_id;
     890        $ids = $this->sanitize_ids_csv($ids_list);
    701891
    702892        // get featured ids
     
    746936            if ($count++ != 0)
    747937                $query .= ", ";
    748             $query .= "(" . $att_ids_map[$thumbnail->meta_id] . ",'{$su_url}') ";
     938            $query .= $this->wpdb->prepare("(%d, %s)", $att_ids_map[$thumbnail->meta_id], $su_url) . " ";
    749939        }
    750940        $query .= "ON DUPLICATE KEY UPDATE post_content_filtered=VALUES(post_content_filtered)";
     
    753943
    754944    function get_thumbnail_meta_ids($thumbnails, $att_ids_map) {
    755         // join post_ids
    756         $i = 0;
    757         $ids = null;
     945        // Collect distinct numeric attachment post_ids
     946        $ids_arr = array();
    758947        foreach ($thumbnails as $thumbnail) {
    759948            if (!isset($att_ids_map[$thumbnail->meta_id])) // no metadata, only custom field
    760949                continue;
    761             $ids = ($i++ == 0) ? $att_ids_map[$thumbnail->meta_id] : ($ids . "," . $att_ids_map[$thumbnail->meta_id]);
    762         }
    763 
    764         // get meta ids
    765         $result = $this->wpdb->get_results("
     950            $ids_arr[] = (int) $att_ids_map[$thumbnail->meta_id];
     951        }
     952        $ids_arr = array_values(array_unique(array_filter($ids_arr, function ($v) {
     953                            return $v > 0;
     954                        })));
     955
     956        // No IDs -> nothing to query
     957        if (empty($ids_arr)) {
     958            return array();
     959        }
     960
     961        // Build prepared IN(...) and run the safe query
     962        $placeholders = implode(',', array_fill(0, count($ids_arr), '%d'));
     963        $sql = "
    766964            SELECT meta_id, post_id
    767             FROM {$this->postmeta}
    768             WHERE post_id IN ({$ids})
    769             AND meta_key = '_wp_attached_file'
    770         ");
     965            FROM {$this->postmeta}
     966            WHERE post_id IN ($placeholders)
     967            AND meta_key = %s
     968        ";
     969        $params = array_merge($ids_arr, array('_wp_attached_file'));
     970        $result = $this->wpdb->get_results($this->wpdb->prepare($sql, $params));
    771971
    772972        // map att_id -> meta_id
    773973        $attid_metaid_map = array();
    774         foreach ($result as $res)
     974        foreach ($result as $res) {
    775975            $attid_metaid_map[$res->post_id] = $res->meta_id;
    776 
    777         // map meta_id (fifu metadata) -> meta_id (atachment metadata)
     976        }
     977
     978        // map meta_id (fifu metadata) -> meta_id (attachment metadata)
    778979        $map = array();
    779980        foreach ($thumbnails as $thumbnail) {
    780981            if (!isset($att_ids_map[$thumbnail->meta_id])) // no metadata, only custom field
    781982                continue;
    782             if (!isset($attid_metaid_map[$att_ids_map[$thumbnail->meta_id]])) // no metadata, only custom field
     983            $att_id = (int) $att_ids_map[$thumbnail->meta_id];
     984            if (!isset($attid_metaid_map[$att_id])) // no attachment metadata
    783985                continue;
    784             $att_meta_id = $attid_metaid_map[$att_ids_map[$thumbnail->meta_id]];
    785             $map[$thumbnail->meta_id] = $att_meta_id;
    786         }
     986            $map[$thumbnail->meta_id] = $attid_metaid_map[$att_id];
     987        }
     988
    787989        return $map;
    788990    }
     
    792994        $query = "
    793995            INSERT INTO {$this->postmeta} (meta_id, meta_value) VALUES ";
     996
    794997        foreach ($thumbnails as $thumbnail) {
    795998            if (!isset($meta_ids_map[$thumbnail->meta_id])) // no metadata, only custom field
     
    8001003            if ($count++ != 0)
    8011004                $query .= ", ";
    802             $query .= "(" . $meta_ids_map[$thumbnail->meta_id] . ",'{$su_url}') ";
    803         }
     1005
     1006            // Minimal change: use prepare to safely build each VALUES tuple
     1007            $query .= $this->wpdb->prepare("(%d, %s)", $meta_ids_map[$thumbnail->meta_id], $su_url) . " ";
     1008        }
     1009
    8041010        $query .= "ON DUPLICATE KEY UPDATE meta_value=VALUES(meta_value)";
    8051011        return $this->wpdb->get_results($query);
     
    9081114        $table = $is_ctgr ? $this->termmeta : $this->postmeta;
    9091115
     1116        // Return early if no thumbnails to process
     1117        if (empty($thumbnails)) {
     1118            return null;
     1119        }
     1120
    9101121        $query = "
    9111122            INSERT INTO {$table} (meta_id, meta_value) VALUES ";
    9121123        $count = 0;
     1124
    9131125        foreach ($thumbnails as $thumbnail) {
    914             if ($count++ != 0)
     1126            if ($count++ != 0) {
    9151127                $query .= ", ";
    916             $url = $urls[$thumbnail->storage_id];
    917             $query .= "({$thumbnail->meta_id},'{$url}')";
    918         }
    919         $query .= "ON DUPLICATE KEY UPDATE meta_value=VALUES(meta_value)";
     1128            }
     1129
     1130            $url = (isset($urls[$thumbnail->storage_id]) ? $urls[$thumbnail->storage_id] : '');
     1131
     1132            // Minimal change: build each VALUES tuple with prepare to avoid SQL injection
     1133            $query .= $this->wpdb->prepare("(%d, %s)", (int) $thumbnail->meta_id, $url);
     1134        }
     1135
     1136        $query .= " ON DUPLICATE KEY UPDATE meta_value=VALUES(meta_value)";
    9201137        return $this->wpdb->get_results($query);
    9211138    }
    9221139
    9231140    function revert_attachments($urls, $thumbnails, $att_ids_map) {
     1141        // Handle null or invalid parameters
     1142        if ($urls === null || !is_array($urls))
     1143            $urls = [];
     1144        if ($thumbnails === null || !is_array($thumbnails))
     1145            $thumbnails = [];
     1146        if ($att_ids_map === null || !is_array($att_ids_map))
     1147            $att_ids_map = [];
     1148
    9241149        $count = 0;
    9251150        $query = "
    9261151            INSERT INTO {$this->posts} (id, post_content_filtered) VALUES ";
     1152
    9271153        foreach ($thumbnails as $thumbnail) {
    9281154            if (!isset($att_ids_map[$thumbnail->meta_id])) // no metadata, only custom field
    9291155                continue;
     1156
    9301157            if ($count++ != 0)
    9311158                $query .= ", ";
    932             $query .= "(" . $att_ids_map[$thumbnail->meta_id] . ",'" . $urls[$thumbnail->storage_id] . "')";
    933         }
     1159
     1160            $url = isset($urls[$thumbnail->storage_id]) ? $urls[$thumbnail->storage_id] : '';
     1161
     1162            // Minimal change: use prepare to safely build each VALUES tuple
     1163            $query .= $this->wpdb->prepare("(%d, %s)", (int) $att_ids_map[$thumbnail->meta_id], $url);
     1164        }
     1165
     1166        // If no thumbnails were processed, return early
     1167        if ($count == 0) {
     1168            return array(); // Return empty array instead of running invalid query
     1169        }
     1170
    9341171        $query .= "ON DUPLICATE KEY UPDATE post_content_filtered=VALUES(post_content_filtered)";
    9351172        return $this->wpdb->get_results($query);
     
    9401177        $query = "
    9411178            INSERT INTO {$this->postmeta} (meta_id, meta_value) VALUES ";
     1179
    9421180        foreach ($thumbnails as $thumbnail) {
    9431181            if (!isset($meta_ids_map[$thumbnail->meta_id])) // no metadata, only custom field
    9441182                continue;
     1183
    9451184            if ($count++ != 0)
    9461185                $query .= ", ";
    947             $query .= "(" . $meta_ids_map[$thumbnail->meta_id] . ",'" . $urls[$thumbnail->storage_id] . "')";
    948         }
    949         $query .= "ON DUPLICATE KEY UPDATE meta_value=VALUES(meta_value)";
    950         return $this->wpdb->get_results($query);
     1186
     1187            $url = isset($urls[$thumbnail->storage_id]) ? $urls[$thumbnail->storage_id] : '';
     1188
     1189            // Minimal change: safely build each VALUES tuple
     1190            $query .= $this->wpdb->prepare("(%d, %s)", (int) $meta_ids_map[$thumbnail->meta_id], $url);
     1191        }
     1192
     1193        // Only execute query if there are valid operations to perform
     1194        if ($count > 0) {
     1195            $query .= "ON DUPLICATE KEY UPDATE meta_value=VALUES(meta_value)";
     1196            return $this->wpdb->get_results($query);
     1197        }
     1198
     1199        // Return empty array if no valid operations were found
     1200        return array();
    9511201    }
    9521202
     
    9661216
    9671217    function insert_invalid_media_su($url) {
     1218        if ($url === null || $url === '')
     1219            return;
    9681220        if ($this->get_attempts_invalid_media_su($url)) {
    9691221            $this->update_invalid_media_su($url);
     
    9721224
    9731225        $md5 = md5($url);
    974         $this->wpdb->query("
    975             INSERT INTO {$this->fifu_invalid_media_su} (md5, attempts)
    976             VALUES ('{$md5}', 1)
    977         ");
     1226        $this->wpdb->query(
     1227                $this->wpdb->prepare(
     1228                        "INSERT INTO {$this->fifu_invalid_media_su} (md5, attempts) VALUES (%s, 1)",
     1229                        $md5
     1230                )
     1231        );
    9781232    }
    9791233
    9801234    function update_invalid_media_su($url) {
     1235        if ($url === null || $url === '')
     1236            return;
    9811237        $md5 = md5($url);
    982         $this->wpdb->query("
    983             UPDATE {$this->fifu_invalid_media_su}
    984             SET attempts = attempts + 1
    985             WHERE md5 = '{$md5}'
    986         ");
     1238        $this->wpdb->query(
     1239                $this->wpdb->prepare(
     1240                        "UPDATE {$this->fifu_invalid_media_su} SET attempts = attempts + 1 WHERE md5 = %s",
     1241                        $md5
     1242                )
     1243        );
    9871244    }
    9881245
    9891246    function get_attempts_invalid_media_su($url) {
     1247        if ($url === null || $url === '')
     1248            return 0;
    9901249        $md5 = md5($url);
    991         $result = $this->wpdb->get_row("
    992             SELECT attempts
    993             FROM {$this->fifu_invalid_media_su}
    994             WHERE md5 = '{$md5}'
    995         ");
     1250        $result = $this->wpdb->get_row(
     1251                $this->wpdb->prepare(
     1252                        "SELECT attempts FROM {$this->fifu_invalid_media_su} WHERE md5 = %s",
     1253                        $md5
     1254                )
     1255        );
    9961256        return $result ? (int) $result->attempts : 0;
    9971257    }
    9981258
    9991259    function delete_invalid_media_su($url) {
     1260        if ($url === null || $url === '')
     1261            return;
    10001262        $md5 = md5($url);
    1001         $this->wpdb->query("
    1002             DELETE FROM {$this->fifu_invalid_media_su}
    1003             WHERE md5 = '{$md5}'
    1004         ");
     1263        $this->wpdb->query(
     1264                $this->wpdb->prepare(
     1265                        "DELETE FROM {$this->fifu_invalid_media_su} WHERE md5 = %s",
     1266                        $md5
     1267                )
     1268        );
    10051269    }
    10061270
     
    10421306
    10431307    function insert_attachment_by($value) {
    1044         $this->wpdb->query("
    1045             INSERT INTO {$this->posts} (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent, post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered)
    1046             VALUES " . str_replace('\\', '', $value));
     1308        // $value should be a list of PREPARED tuples (e.g., from get_formatted_value), joined by ', '
     1309        $values_sql = is_array($value) ? implode(', ', $value) : (string) $value;
     1310
     1311        $sql = "
     1312            INSERT INTO {$this->posts}
     1313                (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent,
     1314                post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered)
     1315            VALUES {$values_sql}";
     1316        return $this->wpdb->query($sql);
    10471317    }
    10481318
    10491319    function insert_ctgr_attachment_by($value) {
    1050         $this->wpdb->query("
    1051             INSERT INTO {$this->posts} (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent, post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered, post_name)
    1052             VALUES " . str_replace('\\', '', $value));
     1320        // $value should be a list of PREPARED tuples (e.g., from get_ctgr_formatted_value), joined by ', '
     1321        $values_sql = is_array($value) ? implode(', ', $value) : (string) $value;
     1322
     1323        $sql = "
     1324            INSERT INTO {$this->posts}
     1325                (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent,
     1326                post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered, post_name)
     1327            VALUES {$values_sql}";
     1328        return $this->wpdb->query($sql);
    10531329    }
    10541330
    10551331    function get_formatted_value($url, $alt, $post_parent) {
    10561332        $alt = $alt ?? '';
    1057         $alt = str_replace("'", "", $alt);
    1058         return "({$this->author}, '', '{$alt}', '{$alt}', 'image/jpeg', 'attachment', 'inherit', '{$post_parent}', now(), now(), now(), now(), '', '', '', '{$url}')";
     1333        // Return a PREPARED tuple; caller concatenates multiple with ", "
     1334        return $this->wpdb->prepare(
     1335                        "(%d, %s, %s, %s, %s, %s, %s, %d, NOW(), NOW(), NOW(), NOW(), %s, %s, %s, %s)",
     1336                        (int) $this->author, // post_author
     1337                        '', // guid
     1338                        $alt, // post_title
     1339                        $alt, // post_excerpt
     1340                        'image/jpeg', // post_mime_type
     1341                        'attachment', // post_type
     1342                        'inherit', // post_status
     1343                        (int) $post_parent, // post_parent
     1344                        '', // post_content
     1345                        '', // to_ping
     1346                        '', // pinged
     1347                        $url                           // post_content_filtered
     1348                );
    10591349    }
    10601350
    10611351    function get_ctgr_formatted_value($url, $alt, $post_parent) {
    10621352        $alt = $alt ?? '';
    1063         $alt = str_replace("'", "", $alt);
    1064         return "({$this->author}, '', '{$alt}', '{$alt}', 'image/jpeg', 'attachment', 'inherit', '{$post_parent}', now(), now(), now(), now(), '', '', '', '{$url}', 'fifu-category-{$post_parent}')";
     1353        // Return a PREPARED tuple; caller concatenates multiple with ", "
     1354        return $this->wpdb->prepare(
     1355                        "(%d, %s, %s, %s, %s, %s, %s, %d, NOW(), NOW(), NOW(), NOW(), %s, %s, %s, %s, %s)",
     1356                        (int) $this->author, // post_author
     1357                        '', // guid
     1358                        $alt, // post_title
     1359                        $alt, // post_excerpt
     1360                        'image/jpeg', // post_mime_type
     1361                        'attachment', // post_type
     1362                        'inherit', // post_status
     1363                        (int) $post_parent, // post_parent
     1364                        '', // post_content
     1365                        '', // to_ping
     1366                        '', // pinged
     1367                        $url, // post_content_filtered
     1368                        'fifu-category-' . (int) $post_parent// post_name
     1369                );
    10651370    }
    10661371
     
    10681373
    10691374    function clean_dimensions_all() {
    1070         $this->wpdb->query("
    1071             DELETE FROM {$this->postmeta} pm           
    1072             WHERE pm.meta_key = '_wp_attachment_metadata'
     1375        // Ensure author ID is numeric
     1376        $author_id = (int) $this->author;
     1377
     1378        // Build a prepared statement with placeholders
     1379        $query = $this->wpdb->prepare(
     1380                "
     1381            DELETE FROM {$this->postmeta} pm
     1382            WHERE pm.meta_key = %s
    10731383            AND EXISTS (
    1074                 SELECT 1 
    1075                 FROM {$this->posts} p 
     1384                SELECT 1
     1385                FROM {$this->posts} p
    10761386                WHERE p.id = pm.post_id
    1077                 AND p.post_author = {$this->author}
     1387                AND p.post_author = %d
    10781388            )
    1079         ");
     1389            ",
     1390                '_wp_attachment_metadata', // %s placeholder for meta_key
     1391                $author_id                    // %d placeholder for author
     1392        );
     1393
     1394        // Execute the prepared query
     1395        $this->wpdb->query($query);
    10801396    }
    10811397
     
    11831499
    11841500    function set_default_url() {
    1185         $att_id = get_option('fifu_default_attach_id');
     1501        $att_id = (int) get_option('fifu_default_attach_id');
    11861502        if (!$att_id)
    11871503            return;
    1188         $post_types = join("','", explode(',', str_replace(' ', '', get_option('fifu_default_cpt'))));
    1189         $post_types ? $post_types : $this->types;
    1190         $value = null;
    1191         foreach ($this->get_posts_without_featured_image($post_types) as $res) {
    1192             $aux = "({$res->id}, '_thumbnail_id', {$att_id})";
    1193             $value = $value ? $value . ',' . $aux : $aux;
    1194         }
    1195         if ($value) {
    1196             $this->insert_default_thumbnail_id($value);
    1197             update_post_meta($att_id, '_wp_attached_file', get_option('fifu_default_url'));
     1504
     1505        $post_types_csv = $this->sanitize_post_types_list((string) get_option('fifu_default_cpt'));
     1506
     1507        $tuples = [];
     1508        foreach ($this->get_posts_without_featured_image($post_types_csv) as $res) {
     1509            // (%d, %s, %d) -> (post_id, meta_key, meta_value)
     1510            $tuples[] = $this->wpdb->prepare("(%d, %s, %d)", (int) $res->id, '_thumbnail_id', $att_id);
     1511        }
     1512
     1513        if ($tuples) {
     1514            $this->insert_default_thumbnail_id(implode(',', $tuples));
     1515            update_post_meta($att_id, '_wp_attached_file', (string) get_option('fifu_default_url'));
    11981516        }
    11991517    }
    12001518
    12011519    function update_default_url($url) {
    1202         $att_id = get_option('fifu_default_attach_id');
     1520        $att_id = (int) get_option('fifu_default_attach_id');
    12031521        if ($url != wp_get_attachment_url($att_id)) {
    12041522            $this->wpdb->update($this->posts, $set = array('post_content_filtered' => $url), $where = array('id' => $att_id), null, null);
     
    12081526
    12091527    function delete_default_url() {
    1210         $att_id = get_option('fifu_default_attach_id');
     1528        $att_id = (int) get_option('fifu_default_attach_id');
    12111529        wp_delete_attachment($att_id);
    12121530        delete_option('fifu_default_attach_id');
     
    12181536    function before_delete_post($post_id) {
    12191537        $default_url_enabled = fifu_is_on('fifu_enable_default_url');
    1220         $default_att_id = $default_url_enabled ? get_option('fifu_default_attach_id') : null;
     1538        $default_att_id = $default_url_enabled ? (int) get_option('fifu_default_attach_id') : null;
    12211539        $result = $this->get_featured_and_gallery_ids($post_id);
    12221540        if ($result) {
     
    13881706        $this->wpdb->query("SET SESSION group_concat_max_len = 1048576;"); // because GROUP_CONCAT is limited to 1024 characters
    13891707
    1390         $this->wpdb->query("
     1708        $sql = "
    13911709            INSERT INTO {$this->fifu_meta_out} (post_ids, type)
    13921710            SELECT GROUP_CONCAT(DISTINCT id ORDER BY id SEPARATOR ','), 'att'
    13931711            FROM {$this->posts}
    1394             WHERE post_author = {$this->author}
     1712            WHERE post_author = %d
    13951713            GROUP BY FLOOR(id / 5000)
    1396         ");
     1714        ";
     1715        $this->wpdb->query($this->wpdb->prepare($sql, $this->author));
    13971716
    13981717        $last_insert_id = $this->wpdb->insert_id;
     
    14821801
    14831802    function log_prepare($last_insert_id, $table) {
    1484         $inserted_records = $this->wpdb->get_results("
    1485             SELECT id, post_ids, type
    1486             FROM {$table}
    1487             WHERE id = {$last_insert_id}
    1488         ");
     1803        $inserted_records = $this->wpdb->get_results(
     1804                $this->wpdb->prepare(
     1805                        "SELECT id, post_ids, type FROM {$table} WHERE id = %d",
     1806                        (int) $last_insert_id
     1807                )
     1808        );
    14891809
    14901810        foreach ($inserted_records as $record) {
     
    15081828
    15091829    function insert_postmeta($id) {
    1510         $result = $this->wpdb->get_results("
    1511             SELECT post_ids
    1512             FROM {$this->fifu_meta_in}
    1513             WHERE id = {$id}
    1514         ");
    1515 
    1516         $this->wpdb->query("
    1517             DELETE FROM {$this->fifu_meta_in}
    1518             WHERE id = {$id}
    1519         ");
     1830        $result = $this->wpdb->get_results(
     1831                $this->wpdb->prepare(
     1832                        "SELECT post_ids FROM {$this->fifu_meta_in} WHERE id = %d",
     1833                        (int) $id
     1834                )
     1835        );
     1836
     1837        $this->wpdb->query(
     1838                $this->wpdb->prepare(
     1839                        "DELETE FROM {$this->fifu_meta_in} WHERE id = %d",
     1840                        (int) $id
     1841                )
     1842        );
    15201843
    15211844        if (count($result) == 0)
     
    15421865
    15431866    function delete_attmeta($id) {
    1544         $result = $this->wpdb->get_results("
    1545             SELECT post_ids
    1546             FROM {$this->fifu_meta_out}
    1547             WHERE id = {$id}
    1548         ");
    1549 
    1550         $this->wpdb->query("
    1551             DELETE FROM {$this->fifu_meta_out}
    1552             WHERE id = {$id}
    1553         ");
     1867        $result = $this->wpdb->get_results(
     1868                $this->wpdb->prepare(
     1869                        "SELECT post_ids FROM {$this->fifu_meta_out} WHERE id = %d",
     1870                        (int) $id
     1871                )
     1872        );
     1873
     1874        $this->wpdb->query(
     1875                $this->wpdb->prepare(
     1876                        "DELETE FROM {$this->fifu_meta_out} WHERE id = %d",
     1877                        (int) $id
     1878                )
     1879        );
    15541880
    15551881        if (count($result) == 0)
     
    15691895        wp_cache_flush();
    15701896
     1897        // Cast option-derived IDs to integers to avoid SQL injection
     1898        $fake_attach_id = (int) get_option('fifu_fake_attach_id');
     1899        $default_attach_id = (int) get_option('fifu_default_attach_id');
     1900
    15711901        $this->wpdb->query('START TRANSACTION');
    15721902
    15731903        try {
    1574             $fake_attach_id = get_option('fifu_fake_attach_id');
    15751904            $fake_attach_sql = $fake_attach_id ? "OR meta_value = {$fake_attach_id}" : "";
    1576 
    1577             $default_attach_id = get_option('fifu_default_attach_id');
    15781905            $default_attach_sql = $default_attach_id ? "OR meta_value = {$default_attach_id}" : "";
    15791906
     
    16701997
    16711998    function delete_termmeta($id) {
    1672         $result = $this->wpdb->get_results("
    1673             SELECT post_ids
    1674             FROM {$this->fifu_meta_out}
    1675             WHERE id = {$id}
    1676         ");
    1677 
    1678         $this->wpdb->query("
    1679             DELETE FROM {$this->fifu_meta_out}
    1680             WHERE id = {$id}
    1681         ");
     1999        $result = $this->wpdb->get_results(
     2000                $this->wpdb->prepare(
     2001                        "SELECT post_ids FROM {$this->fifu_meta_out} WHERE id = %d",
     2002                        (int) $id
     2003                )
     2004        );
     2005
     2006        $this->wpdb->query(
     2007                $this->wpdb->prepare(
     2008                        "DELETE FROM {$this->fifu_meta_out} WHERE id = %d",
     2009                        (int) $id
     2010                )
     2011        );
    16822012
    16832013        if (count($result) == 0)
     
    16962026    function insert_postmeta2($value, $ids) {
    16972027        $this->wpdb->query('START TRANSACTION');
     2028        $ids_csv = $this->sanitize_ids_csv($ids);
    16982029
    16992030        try {
    1700             $this->wpdb->query("
    1701                 INSERT INTO {$this->posts} (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent, post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered)
    1702                 VALUES " . str_replace('\\', '', $value));
    1703 
    1704             $this->wpdb->query("
     2031            $this->wpdb->query(
     2032                    "INSERT INTO {$this->posts} (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent, post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered)
     2033                VALUES " . $value
     2034            );
     2035
     2036            $author = $this->author;
     2037            $sql_thumb = $this->wpdb->prepare("
    17052038                INSERT INTO {$this->postmeta} (post_id, meta_key, meta_value) (
    17062039                    SELECT p.post_parent, '_thumbnail_id', p.id
    17072040                    FROM {$this->posts} p
    1708                     WHERE p.post_parent IN ({$ids})
    1709                     AND p.post_author = {$this->author}
    1710                 )
    1711             ");
    1712 
    1713             $this->wpdb->query("
     2041                    WHERE p.post_parent IN ({$ids_csv})
     2042                    AND p.post_author = %d
     2043                )
     2044            ", $author);
     2045            $this->wpdb->query($sql_thumb);
     2046
     2047            $sql_file = $this->wpdb->prepare("
    17142048                INSERT INTO {$this->postmeta} (post_id, meta_key, meta_value) (
    17152049                    SELECT p.id, '_wp_attached_file', p.post_content_filtered
    17162050                    FROM {$this->posts} p
    1717                     WHERE p.post_parent IN ({$ids})
    1718                     AND p.post_author = {$this->author}
    1719                 )
    1720             ");
    1721 
    1722             $this->wpdb->query("
     2051                    WHERE p.post_parent IN ({$ids_csv})
     2052                    AND p.post_author = %d
     2053                )
     2054            ", $author);
     2055            $this->wpdb->query($sql_file);
     2056
     2057            $sql_alt = $this->wpdb->prepare("
    17232058                INSERT INTO {$this->postmeta} (post_id, meta_key, meta_value) (
    17242059                    SELECT p.id, '_wp_attachment_image_alt', p.post_title
    17252060                    FROM {$this->posts} p
    1726                     WHERE p.post_parent IN ({$ids})
    1727                     AND p.post_author = {$this->author}
     2061                    WHERE p.post_parent IN ({$ids_csv})
     2062                    AND p.post_author = %d
    17282063                    AND p.post_title IS NOT NULL
    17292064                    AND p.post_title != ''
    17302065                )
    1731             ");
     2066            ", $author);
     2067            $this->wpdb->query($sql_alt);
    17322068
    17332069            $this->wpdb->query('COMMIT');
     
    17392075    function delete_attmeta2($ids) {
    17402076        $this->wpdb->query('START TRANSACTION');
     2077        $ids_csv = $this->sanitize_ids_csv($ids);
    17412078
    17422079        try {
     
    17442081                DELETE FROM {$this->postmeta}
    17452082                WHERE meta_key = '_thumbnail_id'
    1746                 AND meta_value IN (0, {$ids})
     2083                AND meta_value IN (0, {$ids_csv})
    17472084            ");
    17482085
    1749             $this->wpdb->query("
     2086            $author = $this->author;
     2087            $sql_del_posts = $this->wpdb->prepare("
    17502088                DELETE FROM {$this->posts}
    1751                 WHERE id IN ({$ids})
    1752                 AND post_author = {$this->author}
    1753             ");
     2089                WHERE id IN ({$ids_csv})
     2090                AND post_author = %d
     2091            ", $author);
     2092            $this->wpdb->query($sql_del_posts);
    17542093
    17552094            $this->wpdb->query("
    17562095                DELETE FROM {$this->postmeta}
    17572096                WHERE meta_key IN ('_wp_attached_file', '_wp_attachment_image_alt', '_wp_attachment_metadata')
    1758                 AND post_id IN ({$ids})
     2097                AND post_id IN ({$ids_csv})
    17592098            ");
    17602099
     
    17672106    function delete_termmeta2($ids) {
    17682107        $this->wpdb->query('START TRANSACTION');
     2108        $ids_csv = $this->sanitize_ids_csv($ids);
    17692109
    17702110        try {
     
    17722112                DELETE FROM {$this->termmeta}
    17732113                WHERE meta_key = 'thumbnail_id'
    1774                 AND term_id IN ({$ids})
     2114                AND term_id IN ({$ids_csv})
    17752115            ");
    17762116
    1777             $this->wpdb->query("
     2117            $author = $this->author;
     2118            $sql_del_pm = $this->wpdb->prepare("
    17782119                DELETE pm
    17792120                FROM {$this->postmeta} pm JOIN {$this->posts} p ON pm.post_id = p.id
    17802121                WHERE pm.meta_key IN ('_wp_attached_file', '_wp_attachment_image_alt', '_wp_attachment_metadata')
    1781                 AND p.post_parent IN ({$ids})
    1782                 AND p.post_author = {$this->author}
     2122                AND p.post_parent IN ({$ids_csv})
     2123                AND p.post_author = %d
    17832124                AND p.post_name LIKE 'fifu-category%'
    1784             ");
     2125            ", $author);
     2126            $this->wpdb->query($sql_del_pm);
    17852127
    17862128            $this->wpdb->query('COMMIT');
     
    17912133
    17922134    function insert_termmeta($id) {
    1793         $result = $this->wpdb->get_results("
    1794             SELECT post_ids
    1795             FROM {$this->fifu_meta_in}
    1796             WHERE id = {$id}
    1797         ");
    1798 
    1799         $this->wpdb->query("
    1800             DELETE FROM {$this->fifu_meta_in}
    1801             WHERE id = {$id}
    1802         ");
     2135        $result = $this->wpdb->get_results(
     2136                $this->wpdb->prepare(
     2137                        "SELECT post_ids FROM {$this->fifu_meta_in} WHERE id = %d",
     2138                        (int) $id
     2139                )
     2140        );
     2141
     2142        $this->wpdb->query(
     2143                $this->wpdb->prepare(
     2144                        "DELETE FROM {$this->fifu_meta_in} WHERE id = %d",
     2145                        (int) $id
     2146                )
     2147        );
    18032148
    18042149        if (count($result) == 0)
     
    18262171    function insert_termmeta2($value, $ids) {
    18272172        $this->wpdb->query('START TRANSACTION');
     2173        $ids_csv = $this->sanitize_ids_csv($ids);
    18282174
    18292175        try {
    1830             $this->wpdb->query("
    1831                 INSERT INTO {$this->posts} (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent, post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered, post_name)
    1832                 VALUES " . str_replace('\\', '', $value));
    1833 
    1834             $this->wpdb->query("
     2176            $this->wpdb->query(
     2177                    "INSERT INTO {$this->posts} (post_author, guid, post_title, post_excerpt, post_mime_type, post_type, post_status, post_parent, post_date, post_date_gmt, post_modified, post_modified_gmt, post_content, to_ping, pinged, post_content_filtered, post_name)
     2178                VALUES " . $value
     2179            );
     2180
     2181            $author = $this->author;
     2182            $sql_term_thumbnail = $this->wpdb->prepare("
    18352183                INSERT INTO {$this->termmeta} (term_id, meta_key, meta_value) (
    18362184                    SELECT p.post_parent, 'thumbnail_id', p.id
    18372185                    FROM {$this->posts} p
    1838                     WHERE p.post_parent IN ({$ids})
    1839                     AND p.post_author = {$this->author}
     2186                    WHERE p.post_parent IN ({$ids_csv})
     2187                    AND p.post_author = %d
    18402188                    AND p.post_name LIKE 'fifu-category%'
    18412189                )
    1842             ");
    1843 
    1844             $this->wpdb->query("
     2190            ", $author);
     2191            $this->wpdb->query($sql_term_thumbnail);
     2192
     2193            $sql_term_file = $this->wpdb->prepare("
    18452194                INSERT INTO {$this->postmeta} (post_id, meta_key, meta_value) (
    18462195                    SELECT p.id, '_wp_attached_file', p.post_content_filtered
    18472196                    FROM {$this->posts} p
    1848                     WHERE p.post_parent IN ({$ids})
    1849                     AND p.post_author = {$this->author}
     2197                    WHERE p.post_parent IN ({$ids_csv})
     2198                    AND p.post_author = %d
    18502199                    AND p.post_name LIKE 'fifu-category%'
    18512200                )
    1852             ");
    1853 
    1854             $this->wpdb->query("
     2201            ", $author);
     2202            $this->wpdb->query($sql_term_file);
     2203
     2204            $sql_term_alt = $this->wpdb->prepare("
    18552205                INSERT INTO {$this->postmeta} (post_id, meta_key, meta_value) (
    18562206                    SELECT p.id, '_wp_attachment_image_alt', p.post_title
    18572207                    FROM {$this->posts} p
    1858                     WHERE p.post_parent IN ({$ids})
    1859                     AND p.post_author = {$this->author}
     2208                    WHERE p.post_parent IN ({$ids_csv})
     2209                    AND p.post_author = %d
    18602210                    AND p.post_title IS NOT NULL
    18612211                    AND p.post_title != ''
    18622212                    AND p.post_name LIKE 'fifu-category%'
    18632213                )
    1864             ");
     2214            ", $author);
     2215            $this->wpdb->query($sql_term_alt);
    18652216
    18662217            $this->wpdb->query('COMMIT');
     
    18712222
    18722223    function get_fifu_fields($ids) {
     2224        $ids_csv = $this->sanitize_ids_csv($ids);
    18732225        $results = $this->wpdb->get_results("
    18742226            SELECT post_id, meta_key, meta_value
    18752227            FROM {$this->postmeta}
    1876             WHERE post_id IN ({$ids})
     2228            WHERE post_id IN ({$ids_csv})
    18772229            AND meta_key IN ('fifu_image_url', 'fifu_image_alt')
    18782230        ");
     
    19482300function fifu_db_count_urls() {
    19492301    $db = new FifuDb();
    1950     $aux = $db->get_count_urls()[0];
    1951     return $aux ? $aux->amount : 0;
     2302    $aux = $db->get_count_urls();
     2303    return $aux ? $aux : 0;
    19522304}
    19532305
  • featured-image-from-url/trunk/admin/debug.php

    r3328447 r3362830  
    2727    $metaout = fifu_db_debug_metaout();
    2828    return new WP_REST_Response($metaout, 200);
     29}
     30
     31function fifu_api_debug_log(WP_REST_Request $request) {
     32    $type = $request->get_param('type') ?? 'plugin';
     33    $lines = intval($request->get_param('lines') ?? 200);
     34    if ($lines <= 0) {
     35        $lines = 200;
     36    }
     37    if ($lines > 5000) { // hard cap to avoid huge responses
     38        $lines = 5000;
     39    }
     40
     41    $upload_dir = wp_upload_dir();
     42    $basedir = $upload_dir['basedir'] ?? '';
     43    $filename = $type === 'cloud' ? 'fifu-cloud.log' : 'fifu-plugin.log';
     44    $path = $basedir ? (rtrim($basedir, '/\\') . '/' . $filename) : '';
     45
     46    if (!$path || !file_exists($path)) {
     47        return new WP_REST_Response('Log file not found', 404);
     48    }
     49
     50    // Ensure readable for this request
     51    @chmod($path, 0600);
     52    $content = @file_get_contents($path);
     53    // Preserve permission according to current toggle
     54    $debug_on = function_exists('fifu_is_on') ? fifu_is_on('fifu_debug') : false;
     55    @chmod($path, $debug_on ? 0600 : 0200);
     56    if ($content === false) {
     57        return new WP_REST_Response('Unable to read log file', 403);
     58    }
     59
     60    $parts = preg_split('/\r?\n/', rtrim($content, "\r\n"));
     61    $slice = array_slice($parts, -$lines);
     62    $text = implode("\n", $slice) . "\n";
     63
     64    $response = new WP_REST_Response($text, 200);
     65    $response->header('Content-Type', 'text/plain; charset=' . get_option('blog_charset'));
     66    return $response;
    2967}
    3068
     
    89127        },
    90128    ));
     129    register_rest_route('featured-image-from-url/v2', '/debug-log/(?P<type>(cloud|plugin))', array(
     130        'methods' => 'GET',
     131        'callback' => 'fifu_api_debug_log',
     132        'permission_callback' => function ($request) {
     133            return fifu_is_on('fifu_debug');
     134        },
     135        'args' => array(
     136            'type' => array(
     137                'required' => true,
     138                'validate_callback' => function ($param) {
     139                    return in_array($param, array('cloud', 'plugin'), true);
     140                }
     141            ),
     142            'lines' => array(
     143                'required' => false,
     144                'validate_callback' => function ($param) {
     145                    return is_numeric($param) && intval($param) > 0; // capped inside handler
     146                }
     147            ),
     148        ),
     149    ));
    91150});
  • featured-image-from-url/trunk/admin/html/column.html

    r3341179 r3362830  
    11<div
    22    class="fifu-quick"
    3     post-id="<?php echo $post_id ?>"
    4     is-ctgr="<?php echo $is_ctgr ?>"
    5     image-url="<?php echo $image_url ?>"
    6     is-variable="<?php echo $is_variable ?>"
    7     style="height: <?php echo $height ?>px; width: <?php echo $width ?>px; background:url('<?php echo $url ?>') no-repeat center center; background-size:cover; <?php echo $border ?>; cursor:pointer; border-radius: 8px;">
     3    post-id="<?php echo esc_attr($post_id) ?>"
     4    is-ctgr="<?php echo esc_attr($is_ctgr) ?>"
     5    image-url="<?php echo esc_url($image_url) ?>"
     6    is-variable="<?php echo esc_attr($is_variable) ?>"
     7    style="height: <?php echo esc_attr($height) ?>px; width: <?php echo esc_attr($width) ?>px; background:url('<?php echo esc_url($url) ?>') no-repeat center center; background-size:cover; <?php echo esc_attr($border) ?>; cursor:pointer; border-radius: 8px;">
    88</div>
  • featured-image-from-url/trunk/admin/html/support-data.html

    r3356282 r3362830  
    1313Theme:<?php echo wp_get_theme() ?><?php echo fifu_woo_theme() ? ' (WooCommerce)' : '' ?>;
    1414PHP:<?php echo phpversion() ?>;
     15PHP_INT_SIZE:<?php echo PHP_INT_SIZE ?>;
    1516WooCommerce:<?php echo class_exists('WooCommerce') ? WC()->version : '' ?>;
    1617Pages:<?php echo wp_count_posts('page')->publish ?>;
  • featured-image-from-url/trunk/admin/log.php

    r3328447 r3362830  
    1313    $filepath = "{$upload_dir}/{$file}.log";
    1414
    15     // Remove the file
    16     if (file_exists($filepath) && filesize($filepath) > 10 * 1024 * 1024)
    17         unlink($filepath);
     15    // Rotate the .log file if it exceeds 10MB
     16    if (file_exists($filepath) && filesize($filepath) > 10 * 1024 * 1024) {
     17        @unlink($filepath);
     18    }
    1819
    19     if (is_array($entry))
     20    if (is_array($entry)) {
    2021        $entry = json_encode([current_time('mysql') => $entry], JSON_UNESCAPED_SLASHES);
     22    }
    2123
    22     $file = fopen($filepath, $mode);
    23     $bytes = fwrite($file, "{$entry}\n");
    24     fclose($file);
     24    // Ensure file exists before adjusting permissions
     25    if (!file_exists($filepath)) {
     26        @touch($filepath);
     27    }
     28
     29    // Set permissions based on current debug toggle
     30    $debug_on = function_exists('fifu_is_on') ? fifu_is_on('fifu_debug') : false;
     31    @chmod($filepath, $debug_on ? 0600 : 0200);
     32
     33    $fh = fopen($filepath, $mode);
     34    $bytes = fwrite($fh, "{$entry}\n");
     35    fclose($fh);
    2536
    2637    return $bytes;
    2738}
    2839
     40// Immediately adjust log file permissions when fifu_debug changes
     41function fifu_set_log_permissions($debug_on) {
     42    $upload_dir = wp_upload_dir()['basedir'] ?? '';
     43    if (!$upload_dir)
     44        return;
     45    $files = [
     46        $upload_dir . '/fifu-plugin.log',
     47        $upload_dir . '/fifu-cloud.log',
     48    ];
     49    $perm = $debug_on ? 0600 : 0200;
     50    foreach ($files as $file) {
     51        if (file_exists($file)) {
     52            @chmod($file, $perm);
     53        }
     54    }
     55}
     56
     57add_action('updated_option', function ($option, $old_value, $value) {
     58    if ($option === 'fifu_debug') {
     59        fifu_set_log_permissions($value === 'toggleon');
     60    }
     61}, 10, 3);
     62
     63
  • featured-image-from-url/trunk/admin/menu.php

    r3356282 r3362830  
    162162    wp_enqueue_style('fifu-menu-css', plugins_url('/html/css/menu.css', __FILE__), array(), fifu_version_number_enq());
    163163
     164    // page-specific JS to hide admin notices, matching other FIFU pages
     165    wp_enqueue_script('fifu-support-data-js', plugins_url('/html/js/support-data.js', __FILE__), array('jquery', 'jquery-ui'), fifu_version_number_enq());
     166
    164167    $skip = esc_attr(get_option('fifu_skip'));
    165168    $html_cpt = esc_attr(get_option('fifu_html_cpt'));
  • featured-image-from-url/trunk/featured-image-from-url.php

    r3356282 r3362830  
    55 * Plugin URI: https://fifu.app/
    66 * Description: Use remote media as the featured image and beyond.
    7  * Version: 5.2.7
     7 * Version: 5.2.8
    88 * Author: fifu.app
    99 * Author URI: https://fifu.app/
     
    2121define('FIFU_GRAVITY_DIR', FIFU_PLUGIN_DIR . 'gravity-forms');
    2222define('FIFU_LANGUAGES_DIR', WP_CONTENT_DIR . '/uploads/fifu/languages/');
    23 define('FIFU_DELETE_ALL_URLS', false);
    2423define('FIFU_CLOUD_DEBUG', false);
     24
     25if (!defined('FIFU_DELETE_ALL_URLS')) {
     26    define('FIFU_DELETE_ALL_URLS', false);
     27}
    2528
    2629$FIFU_SESSION = array();
  • featured-image-from-url/trunk/includes/attachment.php

    r3352132 r3362830  
    6464    global $wpdb;
    6565    if (fifu_is_web_story() || (($_POST['action'] ?? '') == 'query-attachments' || ($_POST['action'] ?? '') == 'get-attachment'))
    66         $where .= ' AND ' . $wpdb->prefix . 'posts.post_author <> ' . FIFU_AUTHOR . ' ';
     66        $where .= $wpdb->prepare(" AND {$wpdb->posts}.post_author <> %d ", FIFU_AUTHOR);
    6767    return $where;
    6868}
     
    7171    global $wpdb;
    7272    if (fifu_is_web_story() || (is_admin() && $q->is_main_query() && strpos($where, 'attachment') !== false))
    73         $where .= ' AND ' . $wpdb->prefix . 'posts.post_author <> ' . FIFU_AUTHOR . ' ';
     73        $where .= $wpdb->prepare(" AND {$wpdb->posts}.post_author <> %d ", FIFU_AUTHOR);
    7474    return $where;
    7575}, 10, 2);
  • featured-image-from-url/trunk/includes/html/og-image.html

    r3352132 r3362830  
    11<!-- FIFU:meta:begin:image -->
    2 <meta property="og:image" content="<?php echo $url ?>" />
     2<meta property="og:image" content="<?php echo esc_attr(esc_url($url)) ?>" />
    33<!-- FIFU:meta:end:image -->
  • featured-image-from-url/trunk/includes/html/twitter-image.html

    r3352132 r3362830  
    11<!-- FIFU:meta:begin:twitter -->
    2 <meta name="twitter:image" content="<?php echo $url ?>" />
     2<meta name="twitter:image" content="<?php echo esc_attr(esc_url($url)) ?>" />
    33<!-- FIFU:meta:end:twitter -->
  • featured-image-from-url/trunk/includes/speedup.php

    r3344902 r3362830  
    190190    $sizes = unserialize(FIFU_SPEEDUP_SIZES);
    191191
    192     // Check if the width is present in the available sizes
     192    // Preserve original width for aspect ratio calculation
     193    $original_width = $width;
     194
     195    // Snap width to the next allowed size if needed
    193196    if (!in_array($width, $sizes)) {
    194         // Look for the next higher value
    195197        foreach ($sizes as $size) {
    196198            if ($size >= $width) {
     
    200202        }
    201203
    202         // If the next higher value is bigger than 1920, use 1920
     204        // Hard cap at 1920
    203205        if ($width > 1920) {
    204206            $width = 1920;
     
    206208    }
    207209
    208     // Calculate the new height to maintain the aspect ratio
     210    // Recalculate height to preserve original aspect ratio
    209211    if ($height) {
    210         $aspect_ratio = $height / $width;
    211         $new_height = $width * $aspect_ratio;
     212        $aspect_ratio = $height / max(1, $original_width);
     213        $new_height = (int) round($width * $aspect_ratio);
    212214    } else {
    213         $new_height = $height;
     215        $new_height = $height; // keep as is (0 or null)
    214216    }
    215217
  • featured-image-from-url/trunk/includes/util.php

    r3356282 r3362830  
    289289}
    290290
     291function fifu_unit_test() {
     292    return 'Hello, World!';
     293}
     294
    291295// developers
    292296
  • featured-image-from-url/trunk/readme.txt

    r3356282 r3362830  
    55Requires at least: 5.6
    66Tested up to: 6.8.2
    7 Stable tag: 5.2.7
     7Stable tag: 5.2.8
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
     
    247247== Changelog ==
    248248
     249= 5.2.8 =
     250* Fixes: vulnerabilities reported by the Wordfence Security team.
     251
    249252= 5.2.7 =
    250253* New: Notice to rate the plugin; Enhancement: Auto set featured media from post content (now supports local relative URLs); Fix: Incomplete product data generated for Rich Results.
     
    274277* Notice: the plugin collects the theme name anonymously (the goal is to identify the most common themes and ensure FIFU works correctly with all of them).
    275278
    276 = 5.1.8 =
    277 * Fix: Optimized Images (performance issue when serving full image size and possible conflicts with images from Cloudinary); Fix: images in the WooCommerce product gallery (not displayed when Optimized Images was disabled).
    278 
    279279= others =
    280280* [more](https://fifu.app/changelog)
     
    283283== Upgrade Notice ==
    284284
    285 = 5.2.7 =
    286 * New: Notice to rate the plugin; Enhancement: Auto set featured media from post content (now supports local relative URLs); Fix: Incomplete product data generated for Rich Results.
     285= 5.2.8 =
     286* Fixes: vulnerabilities reported by the Wordfence Security team.
Note: See TracChangeset for help on using the changeset viewer.