Plugin Directory


Ignore:
Timestamp:
10/27/2026 04:42:52 AM (23 hours ago)
Author:
softminal
Message:

Release 1.1.1: multisite security fixes for the CDN address and the shared Cloudflare token

File:
1 edited

Legend:

Unmodified
Added
Removed
  • softminal-cache/trunk/app/Edge/Edge.php

    r3767725 r3767752  
    8484    public static function connect(string $token): array
    8585    {
     86        if (!self::mayManage()) {
     87            return ['ok' => false, 'error' => self::networkOnly()];
     88        }
    8689        $fromConstant = self::fromConstant();
    8790        $token        = $fromConstant ? (string) self::token() : trim($token);
    … …  
    103106
    104107        return ['ok' => true];
     108    }
     109
     110    /**
     111     * Whether the current user may connect, disconnect, switch purging or clear the whole zone. A token
     112     * set in wp-config.php is shared by every site of a network, so there only a network admin may
     113     * (WP-CLI and cron count as the server; an anonymous web request does not).
     114     */
     115    public static function mayManage(): bool
     116    {
     117        return !self::fromConstant() || !is_multisite() || self::serverContext() || current_user_can('manage_network_options');
     118    }
     119
     120    /** WP-CLI or a cron run: the server acting, not a visitor. An anonymous web request is not one. */
     121    public static function serverContext(): bool
     122    {
     123        return (defined('WP_CLI') && WP_CLI) || wp_doing_cron();
     124    }
     125
     126    public static function networkOnly(): string
     127    {
     128        return __('On a network, only a network admin can use the Cloudflare token set in wp-config.php.', 'softminal-cache');
    105129    }
    106130
    … …  
    136160        }
    137161        if (array_key_exists('cloudflare_purge', $input)) {
    138             $data['cloudflare']['purge'] = (bool) filter_var($input['cloudflare_purge'], FILTER_VALIDATE_BOOLEAN);
     162            $purge = (bool) filter_var($input['cloudflare_purge'], FILTER_VALIDATE_BOOLEAN);
     163            if ($purge !== $data['cloudflare']['purge'] && !self::mayManage()) {
     164                return ['ok' => false, 'error' => self::networkOnly()];
     165            }
     166            $data['cloudflare']['purge'] = $purge;
    139167        }
    140168        self::put($data);
    … …  
    158186                'unreadable'   => $data['cloudflare']['zone_id'] !== '' && self::token() === null,
    159187                'constant'     => self::fromConstant(),
     188                'canManage'    => self::mayManage(),
    160189                'zone'         => $data['cloudflare']['zone_name'],
    161190                'purge'        => $data['cloudflare']['purge'],
    … …  
    202231            $token = (string) self::token();
    203232            $zone  = $data['cloudflare']['zone_id'];
    204             $reply = $all ? Cloudflare::purgeAll($token, $zone) : Cloudflare::purgeUrls($token, $zone, $urls);
    205             $data['cloudflare']['last'] = self::result($reply, $all, count($urls));
     233            // A shared (network) token clears the whole zone only for a network admin; for anyone else
     234            // "everything" means this site's homepage plus what changed, never the other sites' pages.
     235            $zoneWide = $all && self::mayManage();
     236            $files    = $all && !$zoneWide ? array_values(array_unique(array_merge([home_url('/')], $urls))) : $urls;
     237            $reply    = $zoneWide ? Cloudflare::purgeAll($token, $zone) : Cloudflare::purgeUrls($token, $zone, $files);
     238            $data['cloudflare']['last'] = self::result($reply, $zoneWide, count($files));
    206239        }
    207240        if (self::varnishOn()) {
Note: See TracChangeset for help on using the changeset viewer.