Skip to content

Commit 95e56cd

Browse files
authored
Add CCSS allowlist, notify (#807)
1 parent e8d1d75 commit 95e56cd

10 files changed

+162
-15
lines changed

data/ccss_whitelist.txt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Predefined list for CCSS whitelist #
2+
# Comment can use `# `(there is a space following), or `##`, can use both as a new line or end of one line
3+
# If you want to predefine new items, please send a Pull Request to https://github.com/litespeedtech/lscache_wp/blob/dev/data/ccss_whitelist.txt We will merge into next plugin release
4+
5+
6+
############# DoBar compatibility #############
7+
.pace-inactive
8+
9+
############# DIVI ################
10+
.et_pb_number_counter.active

data/const.default.json

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"optm-ggfonts_rm": "",
8888
"optm-css_async": "",
8989
"optm-ccss_per_url": "",
90+
"optm-ccss_whitelist": "",
9091
"optm-css_async_inline": "1",
9192
"optm-css_font_display": "",
9293
"optm-js_defer": "",

readme.txt

+4
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro
255255

256256
== Changelog ==
257257

258+
= Unreleased =
259+
* 🌱**Page Optimize** Added allowlist support for CCSS.
260+
* **Cloud** CCSS results are now posted via API notification.
261+
258262
= 7.0 - Mar 25 2025 =
259263
* 🌱**Image Optimization** Added AVIF format.
260264
* **Core** Changed plugin classes auto load to preload all to prevent upgrade problems.

src/base.cls.php

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ class Base extends Root
149149
const O_OPTM_CCSS_PER_URL = 'optm-ccss_per_url';
150150
const O_OPTM_CCSS_SEP_POSTTYPE = 'optm-ccss_sep_posttype';
151151
const O_OPTM_CCSS_SEP_URI = 'optm-ccss_sep_uri';
152+
const O_OPTM_CCSS_SELECTOR_WHITELIST = 'optm-ccss_whitelist';
152153
const O_OPTM_CSS_ASYNC_INLINE = 'optm-css_async_inline';
153154
const O_OPTM_CSS_FONT_DISPLAY = 'optm-css_font_display';
154155
const O_OPTM_JS_DEFER = 'optm-js_defer';
@@ -437,6 +438,7 @@ class Base extends Root
437438
self::O_OPTM_CCSS_PER_URL => false,
438439
self::O_OPTM_CCSS_SEP_POSTTYPE => array(),
439440
self::O_OPTM_CCSS_SEP_URI => array(),
441+
self::O_OPTM_CCSS_SELECTOR_WHITELIST => array(),
440442
self::O_OPTM_CSS_ASYNC_INLINE => false,
441443
self::O_OPTM_CSS_FONT_DISPLAY => false,
442444
self::O_OPTM_JS_DEFER => false,

src/cloud.cls.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ class Cloud extends Base
8383
// No api key needed for these services
8484
private static $_PUB_SVC_SET = array(self::API_NEWS, self::API_REPORT, self::API_VER, self::API_BETA_TEST, self::API_REST_ECHO, self::SVC_D_V3UPGRADE);
8585

86-
private static $_QUEUE_SVC_SET = array(self::SVC_UCSS, self::SVC_VPI);
86+
private static $_QUEUE_SVC_SET = array(self::SVC_CCSS, self::SVC_UCSS, self::SVC_VPI);
8787

8888
public static $SERVICES_LOAD_CHECK = array(
89-
self::SVC_CCSS,
89+
// self::SVC_CCSS,
9090
// self::SVC_UCSS,
9191
// self::SVC_VPI,
9292
self::SVC_LQIP,

src/css.cls.php

+88-13
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ class CSS extends Base
1818
const TYPE_CLEAR_Q_CCSS = 'clear_q_ccss';
1919

2020
protected $_summary;
21+
private $_ccss_whitelist;
2122
private $_queue;
22-
private $_endts;
2323

2424
/**
2525
* Init
@@ -29,6 +29,8 @@ class CSS extends Base
2929
public function __construct()
3030
{
3131
$this->_summary = self::get_summary();
32+
33+
add_filter('litespeed_ccss_whitelist', array($this->cls('Data'), 'load_ccss_whitelist'));
3234
}
3335

3436
/**
@@ -206,22 +208,11 @@ private function _cron_handler($type, $continue)
206208
}
207209

208210
$i = 0;
209-
$timeoutLimit = ini_get('max_execution_time');
210-
$this->_endts = time() + $timeoutLimit;
211211
foreach ($this->_queue as $k => $v) {
212212
if (!empty($v['_status'])) {
213213
continue;
214214
}
215215

216-
if (function_exists('set_time_limit')) {
217-
$this->_endts += 120;
218-
set_time_limit(120);
219-
}
220-
if ($this->_endts - time() < 10) {
221-
// self::debug("🚨 End loop due to timeout limit reached " . $timeoutLimit . "s");
222-
// return;
223-
}
224-
225216
Debug2::debug('[' . $type_tag . '] cron job [tag] ' . $k . ' [url] ' . $v['url'] . ($v['is_mobile'] ? ' 📱 ' : '') . ' [UA] ' . $v['user_agent']);
226217

227218
if ($type == 'ccss' && empty($v['url_tag'])) {
@@ -291,6 +282,8 @@ private function _send_req($request_url, $queue_k, $uid, $user_agent, $vary, $ur
291282
return 'out_of_quota';
292283
}
293284

285+
set_time_limit(120);
286+
294287
// Update css request status
295288
$this->_summary['curr_request_' . $type] = time();
296289
self::save_summary();
@@ -306,7 +299,8 @@ private function _send_req($request_url, $queue_k, $uid, $user_agent, $vary, $ur
306299
list($css, $html) = $this->prepare_css($html, $is_webp);
307300

308301
if (!$css) {
309-
Debug2::debug('[UCSS] ❌ No combined css');
302+
$type_tag = strtoupper($type);
303+
Debug2::debug('[' . $type_tag . '] ❌ No combined css');
310304
return false;
311305
}
312306

@@ -320,6 +314,10 @@ private function _send_req($request_url, $queue_k, $uid, $user_agent, $vary, $ur
320314
'html' => $html,
321315
'css' => $css,
322316
);
317+
if (!isset($this->_ccss_whitelist)) {
318+
$this->_ccss_whitelist = $this->_filter_whitelist();
319+
}
320+
$data['whitelist'] = $this->_ccss_whitelist;
323321

324322
self::debug('Generating: ', $data);
325323

@@ -526,6 +524,83 @@ public function prepare_css($html, $is_webp = false, $dryrun = false)
526524
return array($css, $html);
527525
}
528526

527+
/**
528+
* Filter the comment content, add quotes to selector from whitelist. Return the json
529+
*
530+
* @since 7.1
531+
*/
532+
private function _filter_whitelist()
533+
{
534+
$whitelist = array();
535+
$list = apply_filters('litespeed_ccss_whitelist', $this->conf(self::O_OPTM_CCSS_SELECTOR_WHITELIST));
536+
foreach ($list as $v) {
537+
if (substr($v, 0, 2) === '//') {
538+
continue;
539+
}
540+
$whitelist[] = $v;
541+
}
542+
543+
return $whitelist;
544+
}
545+
546+
/**
547+
* Notify finished from server
548+
* @since 7.1
549+
*/
550+
public function notify()
551+
{
552+
$post_data = \json_decode(file_get_contents('php://input'), true);
553+
if (is_null($post_data)) {
554+
$post_data = $_POST;
555+
}
556+
self::debug('notify() data', $post_data);
557+
558+
$this->_queue = $this->load_queue('ccss');
559+
560+
list($post_data) = $this->cls('Cloud')->extract_msg($post_data, 'ccss');
561+
562+
$notified_data = $post_data['data'];
563+
if (empty($notified_data) || !is_array($notified_data)) {
564+
self::debug('❌ notify exit: no notified data');
565+
return Cloud::err('no notified data');
566+
}
567+
568+
// Check if its in queue or not
569+
$valid_i = 0;
570+
foreach ($notified_data as $v) {
571+
if (empty($v['request_url'])) {
572+
self::debug('❌ notify bypass: no request_url', $v);
573+
continue;
574+
}
575+
if (empty($v['queue_k'])) {
576+
self::debug('❌ notify bypass: no queue_k', $v);
577+
continue;
578+
}
579+
580+
if (empty($this->_queue[$v['queue_k']])) {
581+
self::debug('❌ notify bypass: no this queue [q_k]' . $v['queue_k']);
582+
continue;
583+
}
584+
585+
// Save data
586+
if (!empty($v['data_ccss'])) {
587+
$is_mobile = $this->_queue[$v['queue_k']]['is_mobile'];
588+
$is_webp = $this->_queue[$v['queue_k']]['is_webp'];
589+
$this->_save_con('ccss', $v['data_ccss'], $v['queue_k'], $is_mobile, $is_webp);
590+
591+
$valid_i++;
592+
}
593+
594+
unset($this->_queue[$v['queue_k']]);
595+
self::debug('notify data handled, unset queue [q_k] ' . $v['queue_k']);
596+
}
597+
$this->save_queue('ccss', $this->_queue);
598+
599+
self::debug('notified');
600+
601+
return Cloud::ok(array('count' => $valid_i));
602+
}
603+
529604
/**
530605
* Handle all request actions from main cls
531606
*

src/data.cls.php

+15
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,21 @@ public function load_css_exc($list)
645645
return $list;
646646
}
647647

648+
/**
649+
* Get list from `data/ccss_whitelist.txt`
650+
*
651+
* @since 7.1
652+
*/
653+
public function load_ccss_whitelist($list)
654+
{
655+
$data = $this->_load_per_line('ccss_whitelist.txt');
656+
if ($data) {
657+
$list = array_unique(array_filter(array_merge($list, $data)));
658+
}
659+
660+
return $list;
661+
}
662+
648663
/**
649664
* Get list from `data/ucss_whitelist.txt`
650665
*

src/lang.cls.php

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ public static function title($id)
172172
self::O_OPTM_CCSS_CON => __('Critical CSS Rules', 'litespeed-cache'),
173173
self::O_OPTM_CCSS_SEP_POSTTYPE => __('Separate CCSS Cache Post Types', 'litespeed-cache'),
174174
self::O_OPTM_CCSS_SEP_URI => __('Separate CCSS Cache URIs', 'litespeed-cache'),
175+
self::O_OPTM_CCSS_SELECTOR_WHITELIST => __('CCSS Selector Allowlist', 'litespeed-cache'),
175176
self::O_OPTM_JS_DEFER_EXC => __('JS Deferred / Delayed Excludes', 'litespeed-cache'),
176177
self::O_OPTM_GM_JS_EXC => __('Guest Mode JS Excludes', 'litespeed-cache'),
177178
self::O_OPTM_EMOJI_RM => __('Remove WordPress Emoji', 'litespeed-cache'),

src/rest.cls.php

+15
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ public function rest_api_init()
8888
'permission_callback' => array($this, 'is_from_cloud'),
8989
));
9090

91+
register_rest_route('litespeed/v1', '/notify_ccss', array(
92+
'methods' => 'POST',
93+
'callback' => array($this, 'notify_ccss'),
94+
'permission_callback' => array($this, 'is_from_cloud'),
95+
));
96+
9197
register_rest_route('litespeed/v1', '/notify_ucss', array(
9298
'methods' => 'POST',
9399
'callback' => array($this, 'notify_ucss'),
@@ -199,6 +205,15 @@ public function notify_img()
199205
return Img_Optm::cls()->notify_img();
200206
}
201207

208+
/**
209+
* @since 7.1
210+
*/
211+
public function notify_ccss()
212+
{
213+
self::debug('notify_ccss');
214+
return CSS::cls()->notify();
215+
}
216+
202217
/**
203218
* @since 5.2
204219
*/

tpl/page_optm/settings_tuning_css.tpl.php

+24
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,30 @@
120120
</td>
121121
</tr>
122122

123+
<tr>
124+
<th>
125+
<?php $id = Base::O_OPTM_CCSS_SELECTOR_WHITELIST; ?>
126+
<?php $this->title( $id ); ?>
127+
</th>
128+
<td>
129+
<?php $this->build_textarea( $id ); ?>
130+
<div class="litespeed-desc">
131+
<?php echo __( 'List CSS selectors that should always have their styles contained in critical CSS.', 'litespeed-cache' ); ?>
132+
<?php Doc::learn_more( 'https://docs.litespeedtech.com/lscache/lscwp/pageopt/#ccss-whitelist', __( 'How to choose a CCSS allowlist selector?', 'litespeed-cache' ) ); ?>
133+
<br /><?php echo sprintf( __( 'Wildcard %s supported.', 'litespeed-cache' ), '<code>*</code>' ); ?>
134+
<div class="litespeed-callout notice notice-warning inline">
135+
<h4><?php echo __( 'Note', 'litespeed-cache' ); ?></h4>
136+
<p>
137+
<?php echo __( 'Selectors must exist in the CSS. Parent classes in the HTML will not work.', 'litespeed-cache' ); ?>
138+
</p>
139+
</div>
140+
<font class="litespeed-success">
141+
<?php echo __( 'Predefined list will also be combined w/ the above settings', 'litespeed-cache' ); ?>: <a href="https://github.com/litespeedtech/lscache_wp/blob/dev/data/ccss_whitelist.txt" target="_blank">https://github.com/litespeedtech/lscache_wp/blob/dev/data/ccss_whitelist.txt</a>
142+
</font>
143+
</div>
144+
</td>
145+
</tr>
146+
123147
<tr>
124148
<th>
125149
<?php $id = Base::O_OPTM_CCSS_CON; ?>

0 commit comments

Comments
 (0)