PluginProbe
SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking / trunk
SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking vtrunk
1.5.0 1.4.0 1.3.0 1.3.1 trunk 0.0.0-alpha.1 0.0.0-alpha.2 0.0.0-alpha.3 0.0.1-beta.1 0.0.1-beta.2 0.0.1-beta.3 0.0.1-beta.4 1.0.0 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4
surecookie / inc / modules / script-blocking / blocking-surface.php

blocking-surface.php in SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking trunk, at inc/modules/script-blocking/blocking-surface.php

135 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * What the blocker can actually reach.
4 *
5 * The admin screens used to read "there is a row for it" as "it is blocked
6 * until consent". It is not the same claim: the passes rewrite `<script>`,
7 * `<iframe>`, `<embed>`, `<object>` and `<link>` and nothing else, and every
8 * pass returns the tag untouched when the resource resolves to a skippable
9 * category. An image host therefore shows as a managed, categorised,
10 * apparently-blocked resource that no pass can ever act on.
11 *
12 * One answer, computed the way the passes decide, so every surface reports the
13 * same thing and adding a pass flips them all at once.
14 *
15 * @package SureCookie\Inc\Modules\ScriptBlocking
16 * @since 1.5.0
17 */
18
19 namespace SureCookie\Inc\Modules\ScriptBlocking;
20
21 use SureCookie\Inc\Modules\Services\Pattern_Kinds;
22
23 if ( ! defined( 'ABSPATH' ) ) {
24 exit; // Exit if accessed directly.
25 }
26
27 /**
28 * Blocking_Surface
29 *
30 * @since 1.5.0
31 */
32 final class Blocking_Surface {
33 /**
34 * The resource does not arrive on a tag any pass rewrites.
35 */
36 public const UNREACHABLE_TAG = 'unreachable_tag';
37
38 /**
39 * The resource's category is released without consent, so every pass
40 * returns its tag untouched.
41 */
42 public const SKIPPABLE_CATEGORY = 'skippable_category';
43
44 /**
45 * Why the blocker will not gate this resource, or '' when it will.
46 *
47 * Reachability is decided first: a resource no pass can rewrite stays
48 * unblocked whatever category it is put in, so reporting the category would
49 * imply that recategorising it fixes something.
50 *
51 * @since 1.5.0
52 * @param string $domain Resource domain, as stored on the row.
53 * @param string $category Category the resource is actually gated under.
54 * @return string One of the class constants, or ''.
55 */
56 public static function unenforced_reason( string $domain, string $category ): string {
57 if ( ! self::is_reachable( $domain ) ) {
58 return self::UNREACHABLE_TAG;
59 }
60
61 if ( self::is_skippable_category( $category ) ) {
62 return self::SKIPPABLE_CATEGORY;
63 }
64
65 return '';
66 }
67
68 /**
69 * Categories every pass releases without consent.
70 *
71 * The single reader of the filter: the tag passes, the browser guard, the
72 * placeholder trait and this verdict all have to agree on it, and four
73 * copies of one `apply_filters` call is how they stop agreeing.
74 *
75 * @since 1.5.0
76 * @return array<int, string>
77 */
78 public static function skippable_categories(): array {
79 /**
80 * Filter the categories released without consent.
81 *
82 * A resource resolving to one of these is never parked: every tag pass
83 * hands its tag back untouched and the browser guard lets it through.
84 *
85 * @since 0.0.1
86 * @param array<int, string> $categories Category ids. Default `['essential']`.
87 */
88 $categories = apply_filters( 'surecookie_skippable_categories', [ 'essential' ] );
89
90 return is_array( $categories ) ? array_values( array_filter( $categories, 'is_string' ) ) : [ 'essential' ];
91 }
92
93 /**
94 * Whether a category is released without consent.
95 *
96 * @since 1.5.0
97 * @param string $category Resolved category.
98 * @return bool
99 */
100 public static function is_skippable_category( string $category ): bool {
101 return in_array( $category, self::skippable_categories(), true );
102 }
103
104 /**
105 * Whether some pass can rewrite the tag this host's resources arrive on.
106 *
107 * The catalog bucket is the only evidence used. The URL a scan happened to
108 * sample is NOT: `Scan_Scripts::merge_resource()` files every unknown
109 * scanned domain as a `scripts` pattern, so the script pass reaches the
110 * host whatever that one URL pointed at. Reading `.../logo.png` off a row
111 * and calling the host out of reach contradicted the pass that was parking
112 * `.../tracker.js` on it a moment later.
113 *
114 * @since 1.5.0
115 * @param string $domain Resource domain.
116 * @return bool
117 */
118 private static function is_reachable( string $domain ): bool {
119 $buckets = Resource_Categories::catalog_buckets( $domain );
120
121 // Unknown to the catalog means scan-merged, and that lands in `scripts`.
122 if ( $buckets === [] ) {
123 return true;
124 }
125
126 foreach ( array_keys( $buckets ) as $bucket ) {
127 if ( Pattern_Kinds::is_enforced( $bucket ) ) {
128 return true;
129 }
130 }
131
132 return false;
133 }
134 }
135