Plugin Directory

Changeset 3407779


Ignore:
Timestamp:
12/02/2025 10:04:07 AM (4 months ago)
Author:
sverde1
Message:

Using WP input escaping

Location:
watermark-reloaded
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • watermark-reloaded/tags/1.4.0/class-watermark-reloaded-admin.php

    r3406555 r3407779  
    7272
    7373        // Add filter for watermarking images.
    74         add_filter( 'wp_generate_attachment_metadata', array( $this, 'apply_watermark' ), 10, 2 );
     74        add_filter( 'wp_generate_attachment_metadata', array( $this, 'attachment_metadata' ), 10, 2 );
    7575    }
    7676
     
    130130        $plugin_page = add_options_page(
    131131            esc_html__( 'Watermark Reloaded Settings', 'watermark-reloaded' ),
    132             esc_html__( 'Watermark Reloaded', 'watermark-reloaded' ),
     132            esc_html__( 'Watermark RELOADED', 'watermark-reloaded' ),
    133133            'manage_options',
    134134            WR_SETTINGS_PAGE,
     
    263263        }
    264264
     265        if ( ! $this->gd_exists() ) {
     266            wp_die(
     267                esc_html__(
     268                    'Watermark RELOADED requires the PHP extension GD to process images. Please enable GD to configure the plugin.',
     269                    'watermark-reloaded'
     270                )
     271            );
     272        }
     273
     274        if ( ! $this->has_freetype() ) {
     275            wp_die(
     276                esc_html__( 'Text watermarking requires FreeType Library.', 'watermark-reloaded' )
     277            );
     278        }
     279
    265280        check_admin_referer( 'wr_preview', 'wr_preview_nonce' );
    266281
    267         $watermark_text = filter_input( INPUT_GET, 'watermark_text', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY );
    268         if ( ! empty( $watermark_text ) && is_array( $watermark_text ) ) {
    269             $watermark_text = $this->filter_option_value( 'watermark_text', $watermark_text );
    270         }
    271 
    272         $this->create_preview( array( 'watermark_text' => $watermark_text ) );
     282        $get            = wp_unslash( $_GET );
     283        $watermark_text = ! empty( $get['watermark_text'] ) && is_array( $get['watermark_text'] )
     284            ? $get['watermark_text']
     285            : array();
     286
     287        $this->create_preview(
     288            array(
     289                'watermark_text' => $this->sanitize_option_value( 'watermark_text', $watermark_text ),
     290            )
     291        );
     292
     293        wp_die();
     294    }
     295
     296    /**
     297     * Process attachment metadata during upload.
     298     *
     299     * @param array $data          Attachment metadata.
     300     * @param int   $attachment_id Attachment ID.
     301     * @return array
     302     */
     303    public function attachment_metadata( $data, $attachment_id ) {
     304        // If GD doesn't exist don't watermark.
     305        if ( ! $this->gd_exists() || ! $this->has_freetype() ) {
     306            return $data;
     307        }
     308
     309        return $this->apply_watermark( $data, $attachment_id );
    273310    }
    274311
     
    400437        }
    401438
    402         // Collect and sanitize POST payload.
    403         $post = filter_input_array( INPUT_POST, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
     439        // Collect raw POST payload (will be sanitized per-field).
     440        $post = wp_unslash( $_POST );
    404441
    405442        // Check if our form submit button was clicked.
     
    436473        foreach ( $this->options as $option => $value ) {
    437474            if ( isset( $post[ $option ] ) ) {
    438                 update_option( $option, $this->filter_option_value( $option, $post[ $option ] ) );
     475                update_option( $option, $this->sanitize_option_value( $option, $post[ $option ] ) );
    439476            }
    440477        }
     
    653690     * @return mixed
    654691     */
    655     private function filter_option_value( $option, $value ) {
     692    private function sanitize_option_value( $option, $value ) {
    656693        switch ( $option ) {
    657694            case 'watermark_on':
     
    660697                }
    661698
    662                 // Filter out any intruding image sizes and make sure value is 1.
    663                 $filtered = array_intersect_key( $value, $this->image_size_list() );
    664                 return array_fill_keys( array_keys( $filtered ), 1 );
     699                $sanitized     = array();
     700                $allowed_sizes = array_keys( $this->image_size_list() );
     701                foreach ( $value as $image_size => $enabled ) {
     702                    // Allow only registered image sizes.
     703                    if ( in_array( $image_size, $allowed_sizes, true ) ) {
     704                        $sanitized[ sanitize_key( $image_size ) ] = 1;
     705                    }
     706                }
     707
     708                return $sanitized;
    665709
    666710            case 'watermark_position':
     711                $value     = sanitize_key( $value );
    667712                $positions = explode( '_', $value );
    668713
     
    678723                }
    679724
    680                 // Allow only x and y axis.
    681                 $filtered = array_intersect_key( $value, array_flip( array_keys( $this->watermark_positions ) ) );
    682 
    683                 return array_map( 'intval', $filtered );
     725                $offset   = array();
     726                $defaults = $this->get_option( $option );
     727                foreach ( array( 'x', 'y' ) as $axis ) {
     728                    $offset[ $axis ] = isset( $value[ $axis ] ) ? absint( $value[ $axis ] ) : $defaults[ $axis ];
     729                }
     730
     731                return $offset;
    684732
    685733            case 'watermark_text':
    686734                $defaults = $this->get_option( $option );
    687735
     736                // Ensure font whitelist is populated.
     737                if ( empty( $this->fonts ) ) {
     738                    $this->get_fonts();
     739                }
     740
     741                // Font field validation.
     742                $font = isset( $value['font'] ) ? sanitize_file_name( $value['font'] ) : $defaults['font'];
     743                $font = array_key_exists( $font, $this->fonts ) ? $font : $defaults['font'];
     744
     745                // Color field validation.
     746                $color = isset( $value['color'] ) ? sanitize_hex_color_no_hash( $value['color'] ) : $defaults['color'];
     747                $color = null !== $color && strlen( $color ) === 6 ? $color : $defaults['color'];
     748
    688749                return array(
    689750                    'value' => isset( $value['value'] ) ? sanitize_text_field( $value['value'] ) : $defaults['value'],
    690                     'font'  => isset( $value['font'] ) ? sanitize_file_name( $value['font'] ) : $defaults['font'],
    691                     'size'  => isset( $value['size'] ) ? intval( $value['size'] ) : $defaults['size'],
    692                     'color' => isset( $value['color'] ) && preg_match( '/^\#[a-f0-9]{6}$/si', $value['color'] )
    693                         ? str_replace( '#', '', $value['color'] )
    694                         : $defaults['color'],
     751                    'size'  => isset( $value['size'] ) ? absint( $value['size'] ) : $defaults['size'],
     752                    'font'  => $font,
     753                    'color' => $color,
    695754                );
    696755
     
    716775        }
    717776
    718         $gd_info = gd_info();
    719         if ( ! $gd_info['FreeType Support'] ) {
     777        if ( ! $this->has_freetype() ) {
    720778            $this->missing_dependencies[] = 'freetype';
    721779            $this->messages['error'][]    = esc_html__( 'Text watermarking requires FreeType Library.', 'watermark-reloaded' );
     
    734792    private function gd_exists() {
    735793        return extension_loaded( 'gd' ) || function_exists( 'gd_info' );
     794    }
     795
     796    /**
     797     * Check if FreeType is supported within GD extension.
     798     *
     799     * @return bool
     800     */
     801    private function has_freetype() {
     802        $gd_info = gd_info();
     803        return (bool) $gd_info['FreeType Support'];
    736804    }
    737805
  • watermark-reloaded/tags/1.4.0/watermark-loader.php

    r3406555 r3407779  
    1818 */
    1919
    20 define( 'WR_MIN_PHP_VERSION', '5.1' );
     20define( 'WR_MIN_PHP_VERSION', '5.3' );
    2121
    2222/**
     
    4949
    5050/**
     51 * Load plugin textdomain.
     52 *
     53 * @return void
     54 */
     55function watermark_reloaded_load_textdomain() {
     56    load_plugin_textdomain(
     57        'watermark-reloaded',
     58        false,
     59        dirname( plugin_basename( __FILE__ ) ) . '/languages/'
     60    );
     61}
     62
     63// Use init or plugins_loaded (both are fine, and safe for WP 6.7+).
     64add_action( 'init', 'watermark_reloaded_load_textdomain' );
     65
     66/**
    5167 * Handle WordPress error scraping when PHP version is too low.
    5268 *
     
    5773function watermark_reloaded_error_scrape() {
    5874    if ( version_compare( PHP_VERSION, WR_MIN_PHP_VERSION, '<' ) ) {
    59         $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
     75        // WordPress core calls this endpoint without a nonce during fatal error scraping.
     76        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     77        $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : '';
    6078        if ( 'error_scrape' === $action ) {
    6179            $message = sprintf(
     
    7694}
    7795
    78 watermark_reloaded_error_scrape();
    79 
     96add_action( 'init', 'watermark_reloaded_error_scrape' );
    8097
    8198// Load Watermark RELOADED plugin core only in admin.
  • watermark-reloaded/trunk/class-watermark-reloaded-admin.php

    r3406555 r3407779  
    7272
    7373        // Add filter for watermarking images.
    74         add_filter( 'wp_generate_attachment_metadata', array( $this, 'apply_watermark' ), 10, 2 );
     74        add_filter( 'wp_generate_attachment_metadata', array( $this, 'attachment_metadata' ), 10, 2 );
    7575    }
    7676
     
    130130        $plugin_page = add_options_page(
    131131            esc_html__( 'Watermark Reloaded Settings', 'watermark-reloaded' ),
    132             esc_html__( 'Watermark Reloaded', 'watermark-reloaded' ),
     132            esc_html__( 'Watermark RELOADED', 'watermark-reloaded' ),
    133133            'manage_options',
    134134            WR_SETTINGS_PAGE,
     
    263263        }
    264264
     265        if ( ! $this->gd_exists() ) {
     266            wp_die(
     267                esc_html__(
     268                    'Watermark RELOADED requires the PHP extension GD to process images. Please enable GD to configure the plugin.',
     269                    'watermark-reloaded'
     270                )
     271            );
     272        }
     273
     274        if ( ! $this->has_freetype() ) {
     275            wp_die(
     276                esc_html__( 'Text watermarking requires FreeType Library.', 'watermark-reloaded' )
     277            );
     278        }
     279
    265280        check_admin_referer( 'wr_preview', 'wr_preview_nonce' );
    266281
    267         $watermark_text = filter_input( INPUT_GET, 'watermark_text', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY );
    268         if ( ! empty( $watermark_text ) && is_array( $watermark_text ) ) {
    269             $watermark_text = $this->filter_option_value( 'watermark_text', $watermark_text );
    270         }
    271 
    272         $this->create_preview( array( 'watermark_text' => $watermark_text ) );
     282        $get            = wp_unslash( $_GET );
     283        $watermark_text = ! empty( $get['watermark_text'] ) && is_array( $get['watermark_text'] )
     284            ? $get['watermark_text']
     285            : array();
     286
     287        $this->create_preview(
     288            array(
     289                'watermark_text' => $this->sanitize_option_value( 'watermark_text', $watermark_text ),
     290            )
     291        );
     292
     293        wp_die();
     294    }
     295
     296    /**
     297     * Process attachment metadata during upload.
     298     *
     299     * @param array $data          Attachment metadata.
     300     * @param int   $attachment_id Attachment ID.
     301     * @return array
     302     */
     303    public function attachment_metadata( $data, $attachment_id ) {
     304        // If GD doesn't exist don't watermark.
     305        if ( ! $this->gd_exists() || ! $this->has_freetype() ) {
     306            return $data;
     307        }
     308
     309        return $this->apply_watermark( $data, $attachment_id );
    273310    }
    274311
     
    400437        }
    401438
    402         // Collect and sanitize POST payload.
    403         $post = filter_input_array( INPUT_POST, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
     439        // Collect raw POST payload (will be sanitized per-field).
     440        $post = wp_unslash( $_POST );
    404441
    405442        // Check if our form submit button was clicked.
     
    436473        foreach ( $this->options as $option => $value ) {
    437474            if ( isset( $post[ $option ] ) ) {
    438                 update_option( $option, $this->filter_option_value( $option, $post[ $option ] ) );
     475                update_option( $option, $this->sanitize_option_value( $option, $post[ $option ] ) );
    439476            }
    440477        }
     
    653690     * @return mixed
    654691     */
    655     private function filter_option_value( $option, $value ) {
     692    private function sanitize_option_value( $option, $value ) {
    656693        switch ( $option ) {
    657694            case 'watermark_on':
     
    660697                }
    661698
    662                 // Filter out any intruding image sizes and make sure value is 1.
    663                 $filtered = array_intersect_key( $value, $this->image_size_list() );
    664                 return array_fill_keys( array_keys( $filtered ), 1 );
     699                $sanitized     = array();
     700                $allowed_sizes = array_keys( $this->image_size_list() );
     701                foreach ( $value as $image_size => $enabled ) {
     702                    // Allow only registered image sizes.
     703                    if ( in_array( $image_size, $allowed_sizes, true ) ) {
     704                        $sanitized[ sanitize_key( $image_size ) ] = 1;
     705                    }
     706                }
     707
     708                return $sanitized;
    665709
    666710            case 'watermark_position':
     711                $value     = sanitize_key( $value );
    667712                $positions = explode( '_', $value );
    668713
     
    678723                }
    679724
    680                 // Allow only x and y axis.
    681                 $filtered = array_intersect_key( $value, array_flip( array_keys( $this->watermark_positions ) ) );
    682 
    683                 return array_map( 'intval', $filtered );
     725                $offset   = array();
     726                $defaults = $this->get_option( $option );
     727                foreach ( array( 'x', 'y' ) as $axis ) {
     728                    $offset[ $axis ] = isset( $value[ $axis ] ) ? absint( $value[ $axis ] ) : $defaults[ $axis ];
     729                }
     730
     731                return $offset;
    684732
    685733            case 'watermark_text':
    686734                $defaults = $this->get_option( $option );
    687735
     736                // Ensure font whitelist is populated.
     737                if ( empty( $this->fonts ) ) {
     738                    $this->get_fonts();
     739                }
     740
     741                // Font field validation.
     742                $font = isset( $value['font'] ) ? sanitize_file_name( $value['font'] ) : $defaults['font'];
     743                $font = array_key_exists( $font, $this->fonts ) ? $font : $defaults['font'];
     744
     745                // Color field validation.
     746                $color = isset( $value['color'] ) ? sanitize_hex_color_no_hash( $value['color'] ) : $defaults['color'];
     747                $color = null !== $color && strlen( $color ) === 6 ? $color : $defaults['color'];
     748
    688749                return array(
    689750                    'value' => isset( $value['value'] ) ? sanitize_text_field( $value['value'] ) : $defaults['value'],
    690                     'font'  => isset( $value['font'] ) ? sanitize_file_name( $value['font'] ) : $defaults['font'],
    691                     'size'  => isset( $value['size'] ) ? intval( $value['size'] ) : $defaults['size'],
    692                     'color' => isset( $value['color'] ) && preg_match( '/^\#[a-f0-9]{6}$/si', $value['color'] )
    693                         ? str_replace( '#', '', $value['color'] )
    694                         : $defaults['color'],
     751                    'size'  => isset( $value['size'] ) ? absint( $value['size'] ) : $defaults['size'],
     752                    'font'  => $font,
     753                    'color' => $color,
    695754                );
    696755
     
    716775        }
    717776
    718         $gd_info = gd_info();
    719         if ( ! $gd_info['FreeType Support'] ) {
     777        if ( ! $this->has_freetype() ) {
    720778            $this->missing_dependencies[] = 'freetype';
    721779            $this->messages['error'][]    = esc_html__( 'Text watermarking requires FreeType Library.', 'watermark-reloaded' );
     
    734792    private function gd_exists() {
    735793        return extension_loaded( 'gd' ) || function_exists( 'gd_info' );
     794    }
     795
     796    /**
     797     * Check if FreeType is supported within GD extension.
     798     *
     799     * @return bool
     800     */
     801    private function has_freetype() {
     802        $gd_info = gd_info();
     803        return (bool) $gd_info['FreeType Support'];
    736804    }
    737805
  • watermark-reloaded/trunk/watermark-loader.php

    r3406555 r3407779  
    1818 */
    1919
    20 define( 'WR_MIN_PHP_VERSION', '5.1' );
     20define( 'WR_MIN_PHP_VERSION', '5.3' );
    2121
    2222/**
     
    4949
    5050/**
     51 * Load plugin textdomain.
     52 *
     53 * @return void
     54 */
     55function watermark_reloaded_load_textdomain() {
     56    load_plugin_textdomain(
     57        'watermark-reloaded',
     58        false,
     59        dirname( plugin_basename( __FILE__ ) ) . '/languages/'
     60    );
     61}
     62
     63// Use init or plugins_loaded (both are fine, and safe for WP 6.7+).
     64add_action( 'init', 'watermark_reloaded_load_textdomain' );
     65
     66/**
    5167 * Handle WordPress error scraping when PHP version is too low.
    5268 *
     
    5773function watermark_reloaded_error_scrape() {
    5874    if ( version_compare( PHP_VERSION, WR_MIN_PHP_VERSION, '<' ) ) {
    59         $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
     75        // WordPress core calls this endpoint without a nonce during fatal error scraping.
     76        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     77        $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : '';
    6078        if ( 'error_scrape' === $action ) {
    6179            $message = sprintf(
     
    7694}
    7795
    78 watermark_reloaded_error_scrape();
    79 
     96add_action( 'init', 'watermark_reloaded_error_scrape' );
    8097
    8198// Load Watermark RELOADED plugin core only in admin.
Note: See TracChangeset for help on using the changeset viewer.