Make WordPress Core

Changeset 57868


Ignore:
Timestamp:
03/22/2024 10:59:01 PM (2 months ago)
Author:
peterwilsoncc
Message:

Editor: Prevent font folder naive filtering causing infinite loops.

This modifies the font directory API to more closely reflect the upload directory API to help account for naive filtering when uploading fonts.

This moves the protection of infinite loops to the new function _wp_filter_font_directory() to allow developers extending and maintaining the font library to apply the filter without the need for a closure.

These changes also ensure both the upload_dir and font_dir filter are applied consistently when both creating and deleting fonts faces. Prior to this commit the upload_dir filter was only fired when creating fonts faces via the REST API.

Applying the font directory filter to the upload_dir filter is now done by adding the _wp_filter_font_directory function rather than wp_get_font_dir(). Developers who have previously modified the font upload directory using the font_dir filter will NOT need to upload their code.

Extenders wishing to upload files to the font directory can do so via the code:

<?php
add_filter( 'upload_dir', '_wp_filter_font_directory' );
// Your code to upload or sideload a font file.
remove_filter( 'upload_dir', '_wp_filter_font_directory' );

Introduces:

  • wp_font_dir(): Attempt to create and retrieve the font upload directory. The equivalent to wp_upload_dir().
  • _wp_filter_font_directory(): To run on the upload_dir filter, this sets the default destination of the fonts directory and fires the font_dir filter.

wp_get_font_dir() has been modified to be a lightweight getter for the font directory. It returns the location without attempting to create it. The equivalent to wp_get_upload_dir().

Follow up to [57740].

Props peterwilsoncc, mukesh27, mikachan, costdev, mmaattiiaass, swissspidy, youknowriad, dd32, grantmkin.
Fixes #60652.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/fonts.php

    r57740 r57868  
    9393
    9494/**
     95 * Retrieves font uploads directory information.
     96 *
     97 * Same as wp_font_dir() but "light weight" as it doesn't attempt to create the font uploads directory.
     98 * Intended for use in themes, when only 'basedir' and 'baseurl' are needed, generally in all cases
     99 * when not uploading files.
     100 *
     101 * @since 6.5.0
     102 *
     103 * @see wp_font_dir()
     104 *
     105 * @return array See wp_font_dir() for description.
     106 */
     107function wp_get_font_dir() {
     108    return wp_font_dir( false );
     109}
     110
     111/**
    95112 * Returns an array containing the current fonts upload directory's path and URL.
    96113 *
    97114 * @since 6.5.0
    98115 *
    99  * @return array $defaults {
    100  *     Array of information about the upload directory.
     116 * @param bool $create_dir Optional. Whether to check and create the font uploads directory. Default true.
     117 * @return array {
     118 *     Array of information about the font upload directory.
    101119 *
    102120 *     @type string       $path    Base directory and subdirectory or full path to the fonts upload directory.
     
    108126 * }
    109127 */
    110 function wp_get_font_dir() {
     128function wp_font_dir( $create_dir = true ) {
     129    /*
     130     * Allow extenders to manipulate the font directory consistently.
     131     *
     132     * Ensures the upload_dir filter is fired both when calling this function
     133     * directly and when the upload directory is filtered in the Font Face
     134     * REST API endpoint.
     135     */
     136    add_filter( 'upload_dir', '_wp_filter_font_directory' );
     137    $font_dir = wp_upload_dir( null, $create_dir, false );
     138    remove_filter( 'upload_dir', '_wp_filter_font_directory' );
     139    return $font_dir;
     140}
     141
     142/**
     143 * Returns the font directory for use by the font library.
     144 *
     145 * This function is a callback for the {@see 'upload_dir'} filter. It is not
     146 * intended to be called directly. Use wp_get_font_dir() instead.
     147 *
     148 * The function can be used when extending the font library to modify the upload
     149 * destination for font files via the upload_dir filter. The recommended way to
     150 * do this is:
     151 *
     152 * ```php
     153 * add_filter( 'upload_dir', '_wp_filter_font_directory' );
     154 * // Your code to upload or sideload a font file.
     155 * remove_filter( 'upload_dir', '_wp_filter_font_directory' );
     156 * ```
     157 *
     158 * @since 6.5.0
     159 * @access private
     160 *
     161 * @param string $font_dir The font directory.
     162 * @return string The modified font directory.
     163 */
     164function _wp_filter_font_directory( $font_dir ) {
     165    if ( doing_filter( 'font_dir' ) ) {
     166        // Avoid an infinite loop.
     167        return $font_dir;
     168    }
     169
    111170    $site_path = '';
    112171    if ( is_multisite() && ! ( is_main_network() && is_main_site() ) ) {
     
    114173    }
    115174
    116     $defaults = array(
     175    $font_dir = array(
    117176        'path'    => path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path,
    118177        'url'     => untrailingslashit( content_url( 'fonts' ) ) . $site_path,
     
    130189     * @since 6.5.0
    131190     *
    132      * @param array $defaults The original fonts directory data.
     191     * @param array $font_dir {
     192     *     Array of information about the font upload directory.
     193     *
     194     *     @type string       $path    Base directory and subdirectory or full path to the fonts upload directory.
     195     *     @type string       $url     Base URL and subdirectory or absolute URL to the fonts upload directory.
     196     *     @type string       $subdir  Subdirectory
     197     *     @type string       $basedir Path without subdir.
     198     *     @type string       $baseurl URL path without subdir.
     199     *     @type string|false $error   False or error message.
     200     * }
    133201     */
    134     return apply_filters( 'font_dir', $defaults );
     202    return apply_filters( 'font_dir', $font_dir );
    135203}
    136204
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-font-faces-controller.php

    r57804 r57868  
    857857    protected function handle_font_file_upload( $file ) {
    858858        add_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
    859 
    860         /*
    861          * Set the upload directory to the fonts directory.
    862          *
    863          * wp_get_font_dir() contains the 'font_dir' hook, whose callbacks are
    864          * likely to call wp_get_upload_dir().
    865          *
    866          * To avoid an infinite loop, don't hook wp_get_font_dir() to 'upload_dir'.
    867          * Instead, just pass its return value to the 'upload_dir' callback.
    868          */
    869         $font_dir       = wp_get_font_dir();
    870         $set_upload_dir = function () use ( $font_dir ) {
    871             return $font_dir;
    872         };
    873         add_filter( 'upload_dir', $set_upload_dir );
     859        // Filter the upload directory to return the fonts directory.
     860        add_filter( 'upload_dir', '_wp_filter_font_directory' );
    874861
    875862        $overrides = array(
     
    888875        $uploaded_file = wp_handle_upload( $file, $overrides );
    889876
    890         remove_filter( 'upload_dir', $set_upload_dir );
     877        remove_filter( 'upload_dir', '_wp_filter_font_directory' );
    891878        remove_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
    892879
  • trunk/tests/phpunit/tests/fonts/font-library/fontLibraryHooks.php

    r57539 r57868  
    7474
    7575        add_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
    76         add_filter( 'upload_dir', 'wp_get_font_dir' );
     76        add_filter( 'upload_dir', '_wp_filter_font_directory' );
    7777        $font_file = wp_upload_bits(
    7878            $font_filename,
     
    8080            file_get_contents( $font_file_path )
    8181        );
    82         remove_filter( 'upload_dir', 'wp_get_font_dir' );
     82        remove_filter( 'upload_dir', '_wp_filter_font_directory' );
    8383        remove_filter( 'upload_mimes', array( 'WP_Font_Utils', 'get_allowed_font_mime_types' ) );
    8484
  • trunk/tests/phpunit/tests/fonts/font-library/wpFontsDir.php

    r57539 r57868  
    7070        $this->assertSame( static::$dir_defaults, $font_dir, 'The wp_get_font_dir() method should return the default values.' );
    7171    }
     72
     73    /**
     74     * @ticket 60652
     75     */
     76    public function test_fonts_dir_filters_do_not_trigger_infinite_loop() {
     77        /*
     78         * Naive filtering of uploads directory to return font directory.
     79         *
     80         * This emulates the approach a plugin developer may take to
     81         * add the filter when extending the font library functionality.
     82         */
     83        add_filter( 'upload_dir', '_wp_filter_font_directory' );
     84
     85        add_filter(
     86            'upload_dir',
     87            function ( $upload_dir ) {
     88                static $count = 0;
     89                ++$count;
     90                // The filter may be applied a couple of times, at five iterations assume an infinite loop.
     91                if ( $count >= 5 ) {
     92                    $this->fail( 'Filtering the uploads directory triggered an infinite loop.' );
     93                }
     94                return $upload_dir;
     95            },
     96            5
     97        );
     98
     99        /*
     100         * Filter the font directory to return the uploads directory.
     101         *
     102         * This emulates moving font files back to the uploads directory due
     103         * to file system structure.
     104         */
     105        add_filter( 'font_dir', 'wp_get_upload_dir' );
     106
     107        wp_get_upload_dir();
     108
     109        // This will never be hit if an infinite loop is triggered.
     110        $this->assertTrue( true );
     111    }
    72112}
Note: See TracChangeset for help on using the changeset viewer.