Skip to content

Commit e70fd87

Browse files
authored
release: fixes
- Refactored Permissions-Policy and Accept-CH headers. The hints value is now dynamically generated based on the current Retina, Smart Scaling, and Network Optimization settings, ensuring that only relevant device-specific optimizations are applied. - Enhance security.
2 parents e75e011 + 16a5472 commit e70fd87

6 files changed

Lines changed: 263 additions & 16 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@
5959
"php": ">=7.4",
6060
"codeinwp/themeisle-sdk": "^3.3",
6161
"codeinwp/optimole-sdk": "^1.2",
62-
"enshrined/svg-sanitize": "^0.21.0"
62+
"enshrined/svg-sanitize": "^0.22.0"
6363
}
6464
}

composer.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

inc/admin.php

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,33 @@ public function add_permission_policy() {
131131
if ( headers_sent() ) {
132132
return;
133133
}
134-
$policy = 'ch-viewport-width=(self "%1$s")';
134+
$policy = $this->get_permissions_policy();
135+
if ( empty( $policy ) ) {
136+
return;
137+
}
138+
header( sprintf( 'Permissions-Policy: %s', $policy ), false );
139+
}
140+
141+
/**
142+
* Build the Permissions-Policy header value based on active settings.
143+
*
144+
* @return string Comma-separated policy directives, or empty string when none apply.
145+
*/
146+
public function get_permissions_policy(): string {
147+
$parts = [];
148+
$service_url = esc_url( Optml_Config::$service_url );
149+
150+
if ( $this->settings->is_scale_enabled() ) {
151+
$parts[] = sprintf( 'ch-viewport-width=(self "%s")', $service_url );
152+
}
135153
if ( $this->settings->get( 'network_optimization' ) === 'enabled' ) {
136-
$policy .= ', ch-ect=(self "%1$s")';
154+
$parts[] = sprintf( 'ch-ect=(self "%s")', $service_url );
155+
}
156+
if ( $this->settings->get( 'retina_images' ) === 'enabled' ) {
157+
$parts[] = sprintf( 'ch-dpr=(self "%s")', $service_url );
137158
}
138-
header( sprintf( 'Permissions-Policy: %s', sprintf( $policy, esc_url( Optml_Config::$service_url ) ) ), false );
159+
160+
return implode( ', ', $parts );
139161
}
140162
/**
141163
* Function that purges the image cache for a specific file.
@@ -1071,13 +1093,37 @@ public function generator() {
10711093
return;
10721094
}
10731095

1074-
$hints = 'Viewport-Width';
1075-
if ( $this->settings->get( 'network_optimization' ) === 'enabled' ) {
1076-
$hints .= ', ECT';
1096+
$hints = $this->get_accept_ch_hints();
1097+
if ( empty( $hints ) ) {
1098+
return;
10771099
}
10781100
echo sprintf( '<meta http-equiv="Accept-CH" content="%s" />', esc_attr( $hints ) );
10791101
}
10801102

1103+
/**
1104+
* Build the Accept-CH meta content value based on active settings.
1105+
*
1106+
* Mirrors the directives used in get_permissions_policy() so both
1107+
* the Permissions-Policy header and the Accept-CH meta stay in sync.
1108+
*
1109+
* @return string Comma-separated hint tokens, or empty string when none apply.
1110+
*/
1111+
public function get_accept_ch_hints(): string {
1112+
$hints = [];
1113+
1114+
if ( $this->settings->is_scale_enabled() ) {
1115+
$hints[] = 'Viewport-Width';
1116+
}
1117+
if ( $this->settings->get( 'network_optimization' ) === 'enabled' ) {
1118+
$hints[] = 'ECT';
1119+
}
1120+
if ( $this->settings->get( 'retina_images' ) === 'enabled' ) {
1121+
$hints[] = 'DPR';
1122+
}
1123+
1124+
return implode( ', ', $hints );
1125+
}
1126+
10811127
/**
10821128
* Update daily the quota routine.
10831129
*/

inc/settings.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class Optml_Settings {
5959
'cdn' => 'disabled',
6060
'admin_bar_item' => 'enabled',
6161
'lazyload' => 'disabled',
62-
'scale' => 'disabled',
62+
'scale' => 'disabled', // Due to legacy reasons the disabled state means that the scale is enabled and the enabled state means that the scale is disabled.
6363
'network_optimization' => 'enabled',
6464
'lazyload_placeholder' => 'enabled',
6565
'bg_replacer' => 'enabled',
@@ -662,6 +662,14 @@ public function is_smart_cropping() {
662662
public function is_best_format() {
663663
return $this->get( 'best_format' ) === 'enabled';
664664
}
665+
/**
666+
* Check if scale is enabled.
667+
*
668+
* @return bool Scale enabled
669+
*/
670+
public function is_scale_enabled() {
671+
return $this->get( 'scale' ) === 'disabled'; // Due to legacy reasons the disabled state means that the scale is enabled and the enabled state means that the scale is disabled.
672+
}
665673

666674
/**
667675
* Check if offload limit was reached.

inc/tag_replacer.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,11 @@ public function add_missing_srcset_attributes( $tag, $missing_srcsets, $new_url,
504504
$optimized_url = $this->change_url_for_size( $new_url, $width, $height, $dpr );
505505

506506
if ( $optimized_url ) {
507-
$new_srcset_entries[] = $optimized_url . ' ' . $descriptor;
507+
$escaped_url = esc_url( $optimized_url );
508+
if ( empty( $escaped_url ) ) {
509+
continue;
510+
}
511+
$new_srcset_entries[] = $escaped_url . ' ' . esc_attr( $descriptor );
508512

509513
// Add sizes attribute entry for responsive breakpoints
510514
if ( $breakpoint > 0 ) {

tests/test-admin.php

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,195 @@ public function test_get_bf_notices_banner_dismissal_values() {
311311
}
312312
}
313313

314+
// -------------------------------------------------------------------------
315+
// get_permissions_policy() tests
316+
// -------------------------------------------------------------------------
317+
318+
/**
319+
* Helper: set scale, network_optimization and retina_images settings, then
320+
* return a fresh Optml_Admin instance that reads those values.
321+
*
322+
* @param string $scale 'enabled' or 'disabled'.
323+
* @param string $network_opt 'enabled' or 'disabled'.
324+
* @param string $retina 'enabled' or 'disabled'.
325+
* @return Optml_Admin
326+
*/
327+
private function make_admin_with_policy_settings( string $scale, string $network_opt, string $retina ): Optml_Admin {
328+
$settings = new Optml_Settings();
329+
$settings->update( 'scale', $scale );
330+
$settings->update( 'network_optimization', $network_opt );
331+
$settings->update( 'retina_images', $retina );
332+
Optml_Config::$service_url = 'https://test123.i.optimole.com';
333+
return new Optml_Admin();
334+
}
335+
336+
/**
337+
* All three settings off → empty string.
338+
*/
339+
public function test_permissions_policy_empty_when_all_off() {
340+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'disabled' );
341+
$this->assertSame( '', $admin->get_permissions_policy() );
342+
}
343+
344+
/**
345+
* Scale ON → ch-viewport-width present; ch-dpr and ch-ect absent.
346+
*/
347+
public function test_permissions_policy_ch_viewport_width_when_scale_on() {
348+
$admin = $this->make_admin_with_policy_settings( 'disabled', 'disabled', 'disabled' );
349+
$policy = $admin->get_permissions_policy();
350+
$this->assertStringContainsString( 'ch-viewport-width', $policy );
351+
$this->assertStringNotContainsString( 'ch-dpr', $policy );
352+
$this->assertStringNotContainsString( 'ch-ect', $policy );
353+
}
354+
355+
/**
356+
* Scale OFF → ch-viewport-width absent.
357+
*/
358+
public function test_permissions_policy_no_ch_viewport_width_when_scale_off() {
359+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'disabled' );
360+
$this->assertStringNotContainsString( 'ch-viewport-width', $admin->get_permissions_policy() );
361+
}
362+
363+
/**
364+
* Retina ON → ch-dpr present; ch-viewport-width absent.
365+
*/
366+
public function test_permissions_policy_ch_dpr_when_retina_on() {
367+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'enabled' );
368+
$policy = $admin->get_permissions_policy();
369+
$this->assertStringContainsString( 'ch-dpr', $policy );
370+
$this->assertStringNotContainsString( 'ch-viewport-width', $policy );
371+
}
372+
373+
/**
374+
* Retina OFF → ch-dpr absent.
375+
*/
376+
public function test_permissions_policy_no_ch_dpr_when_retina_off() {
377+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'disabled' );
378+
$this->assertStringNotContainsString( 'ch-dpr', $admin->get_permissions_policy() );
379+
}
380+
381+
/**
382+
* Network optimization ON → ch-ect present.
383+
*/
384+
public function test_permissions_policy_ch_ect_when_network_optimization_on() {
385+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'enabled', 'disabled' );
386+
$policy = $admin->get_permissions_policy();
387+
$this->assertStringContainsString( 'ch-ect', $policy );
388+
}
389+
390+
/**
391+
* All three ON → all three directives present in a single string.
392+
*/
393+
public function test_permissions_policy_all_features_on() {
394+
$admin = $this->make_admin_with_policy_settings( 'disabled', 'enabled', 'enabled' );
395+
$policy = $admin->get_permissions_policy();
396+
$this->assertStringContainsString( 'ch-viewport-width', $policy );
397+
$this->assertStringContainsString( 'ch-ect', $policy );
398+
$this->assertStringContainsString( 'ch-dpr', $policy );
399+
}
400+
401+
/**
402+
* Service URL is embedded in the policy when any feature is ON.
403+
*/
404+
public function test_permissions_policy_service_url_embedded() {
405+
$admin = $this->make_admin_with_policy_settings( 'disabled', 'disabled', 'disabled' );
406+
$policy = $admin->get_permissions_policy();
407+
$this->assertStringContainsString( 'test123.i.optimole.com', $policy );
408+
}
409+
410+
// -------------------------------------------------------------------------
411+
// get_accept_ch_hints() tests
412+
// -------------------------------------------------------------------------
413+
414+
/**
415+
* All three settings off → empty string.
416+
* Note: for the scale setting, 'enabled' = scale IS off (legacy inversion).
417+
*/
418+
public function test_accept_ch_hints_empty_when_all_off() {
419+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'disabled' );
420+
$this->assertSame( '', $admin->get_accept_ch_hints() );
421+
}
422+
423+
/**
424+
* Scale ON → Viewport-Width present; DPR and ECT absent.
425+
* Note: 'disabled' raw value = scale IS on (legacy inversion).
426+
*/
427+
public function test_accept_ch_hints_viewport_width_when_scale_on() {
428+
$admin = $this->make_admin_with_policy_settings( 'disabled', 'disabled', 'disabled' );
429+
$hints = $admin->get_accept_ch_hints();
430+
$this->assertStringContainsString( 'Viewport-Width', $hints );
431+
$this->assertStringNotContainsString( 'DPR', $hints );
432+
$this->assertStringNotContainsString( 'ECT', $hints );
433+
}
434+
435+
/**
436+
* Scale OFF → Viewport-Width absent.
437+
* Note: 'enabled' raw value = scale IS off (legacy inversion).
438+
*/
439+
public function test_accept_ch_hints_no_viewport_width_when_scale_off() {
440+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'disabled' );
441+
$this->assertStringNotContainsString( 'Viewport-Width', $admin->get_accept_ch_hints() );
442+
}
443+
444+
/**
445+
* Retina ON → DPR present; Viewport-Width absent (scale kept off).
446+
*/
447+
public function test_accept_ch_hints_dpr_when_retina_on() {
448+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'enabled' );
449+
$hints = $admin->get_accept_ch_hints();
450+
$this->assertStringContainsString( 'DPR', $hints );
451+
$this->assertStringNotContainsString( 'Viewport-Width', $hints );
452+
}
453+
454+
/**
455+
* Retina OFF → DPR absent.
456+
*/
457+
public function test_accept_ch_hints_no_dpr_when_retina_off() {
458+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'disabled', 'disabled' );
459+
$this->assertStringNotContainsString( 'DPR', $admin->get_accept_ch_hints() );
460+
}
461+
462+
/**
463+
* Network optimization ON → ECT present.
464+
*/
465+
public function test_accept_ch_hints_ect_when_network_optimization_on() {
466+
$admin = $this->make_admin_with_policy_settings( 'enabled', 'enabled', 'disabled' );
467+
$this->assertStringContainsString( 'ECT', $admin->get_accept_ch_hints() );
468+
}
469+
470+
/**
471+
* All three ON → all three tokens present.
472+
* Note: 'disabled' raw value = scale IS on (legacy inversion).
473+
*/
474+
public function test_accept_ch_hints_all_features_on() {
475+
$admin = $this->make_admin_with_policy_settings( 'disabled', 'enabled', 'enabled' );
476+
$hints = $admin->get_accept_ch_hints();
477+
$this->assertStringContainsString( 'Viewport-Width', $hints );
478+
$this->assertStringContainsString( 'ECT', $hints );
479+
$this->assertStringContainsString( 'DPR', $hints );
480+
}
481+
482+
/**
483+
* Hints and policy directives stay in sync: each setting contributes
484+
* to both get_accept_ch_hints() and get_permissions_policy().
485+
* Note: scale uses legacy inversion — 'disabled' raw = scale ON.
486+
*/
487+
public function test_accept_ch_and_policy_are_in_sync() {
488+
foreach ( [ 'scale', 'network_optimization', 'retina_images' ] as $setting ) {
489+
// Legacy: 'disabled' = scale ON, 'enabled' = scale OFF.
490+
$scale = $setting === 'scale' ? 'disabled' : 'enabled';
491+
$net_opt = $setting === 'network_optimization' ? 'enabled' : 'disabled';
492+
$retina = $setting === 'retina_images' ? 'enabled' : 'disabled';
493+
494+
$admin = $this->make_admin_with_policy_settings( $scale, $net_opt, $retina );
495+
$hints = $admin->get_accept_ch_hints();
496+
$policy = $admin->get_permissions_policy();
497+
498+
$this->assertNotEmpty( $hints, "Expected non-empty hints for setting: $setting" );
499+
$this->assertNotEmpty( $policy, "Expected non-empty policy for setting: $setting" );
500+
}
501+
}
502+
314503
/**
315504
* Test get_bf_notices at exact boundary conditions.
316505
*/

0 commit comments

Comments
 (0)