PluginProbe
Tracking Code Manager / 2.5.0
Tracking Code Manager v2.5.0
2.8.0 2.7.0 trunk 1.11.8 1.11.9 1.12.0 1.12.1 1.12.2 1.12.3 1.4 1.5 2.0.0 2.0.1 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.2.0 All 29 releases
← All changes | includes/classes/utils/Options.php +26 -57 trunk2.5.0 View file →
@@ -97,65 +97,39 @@
97 97 $key = $this->get_key( $key );
98 98 $wp_session[ $key ] = $value;
99 99 }
100 100
101 - // Request-scoped state.
102 - //
103 - // These values are the plugin's own per-request scratch space: the post being
104 - // rendered, the ids of snippets already written, the pending admin notices.
105 - // Every one of them is produced by this plugin during the request and read
106 - // back by it later in the same request. None is ever legitimately supplied by
107 - // the client.
108 - //
109 - // getRequest() used to fall back to $_POST['TCM_' . $key] whenever a key was
110 - // absent from the in-memory store, which turned all of that internal state
111 - // into an unauthenticated input surface. That is CVE-2026-15180: a POST of
112 - // TCM_ErrorMessages[0] landed in the admin notice box on the next page render.
113 - // The fallback was already vestigial — nothing in the plugin has written to
114 - // $_POST['TCM_*'] since 2.5.0 (RDU-1630) — so it is removed outright rather
115 - // than gated behind a nonce, which would have kept an attacker-influenced
116 - // path alive for no caller's benefit.
117 - //
118 - // Consequence for removeRequest(): unsetting the POST field is no longer a
119 - // way to clear a key, so it now clears the in-memory entry. That also fixes
120 - // the clean-up it was always meant to perform — writeMessages( clean: true )
121 - // previously only ever removed a POST field that the plugin itself never set,
122 - // so an internally queued notice survived its own render and could be emitted
123 - // a second time by the next writeMessages() call in the same request
124 - // (includes/admin/editor.php does exactly that).
101 + //$_REQUEST
102 + //However WP enforces its own logic - during load process wp_magic_quotes() processes variables to emulate magic quotes setting and enforces $_REQUEST to contain combination of $_GET and $_POST, no matter what PHP configuration says.
125 103 private function removeRequest( $key ) {
126 - unset( $this->request_data[ $key ] );
104 + $key = $this->get_key( $key );
105 + if ( isset( $_POST[ $key ] ) ) {
106 + unset( $_POST[ $key ] );
107 + }
127 108 }
128 109 private function getRequest( $key, $default = false ) {
129 110 $result = $default;
130 - if ( isset( $this->request_data[ $key ] ) ) {
131 - if ( is_object( $this->request_data[ $key ] ) ) {
132 - $result = clone $this->request_data[ $key ];
133 - } else {
134 - $result = $this->request_data[ $key ];
111 + if ( isset($this->request_data[ $key ]) ) {
112 + if ( is_object( $this->request_data[ $key ] ) ) {
113 + $result = clone $this->request_data[ $key ];
114 + } else {
115 + $result = $this->request_data[ $key ];
116 + }
117 + $result = $this->recursive_wp_kses( $result );
118 + } else {
119 + $key = $this->get_key( $key );
120 + if ( isset( $_POST[ $key ] ) ) {
121 + if ( is_object( $_POST[ $key ] ) ) {
122 + $result = clone $_POST[ $key ];
123 + } else {
124 + $result = $_POST[ $key ];
125 + }
126 + $result = $this->recursive_wp_kses( $result );
135 127 }
136 - $result = $this->recursive_wp_kses( $result );
137 128 }
138 129 return $result;
139 130 }
140 131
141 - // Sanitize a request-scoped value tree.
142 - //
143 - // The permissive whitelist in tcmp_free_wp_kses_tags_attrs.php exists for
144 - // exactly one kind of value: a snippet body. Those are admin-authored
145 - // tracking snippets and legitimately contain <script>, <iframe> and onload —
146 - // that is the plugin's purpose. Applying the same whitelist to every other
147 - // string, as this method used to, extended "may contain executable markup" to
148 - // values that are never tracking code: notice text, names, ids.
149 - //
150 - // So the 'code' field keeps the permissive pass (still opt-out-able via Skip
151 - // Code Sanitization, since a snippet may need markup the whitelist omits) and
152 - // everything else gets wp_kses_post(), which keeps the formatting notices
153 - // legitimately embed — <a href>, <strong>, <span style> — while dropping
154 - // script/iframe/style and every on* handler.
155 - //
156 - // This mirrors the split TCMP_Utils::sanitize_post_or_get() already applies on
157 - // the save path (F-06): permissive for 'code', plain text for the rest.
158 132 public function recursive_wp_kses( $array ) {
159 133 global $tcmp_allowed_html_tags;
160 134 foreach ( $array as $key => &$value ) {
161 135 if ( is_array( $value ) ) {
@@ -164,9 +138,9 @@
164 138 if ( ! $this->getSkipCodeSanitization() ) {
165 139 $value = wp_kses( $value, $tcmp_allowed_html_tags );
166 140 }
167 141 } elseif ( is_string( $value ) ) {
168 - $value = wp_kses_post( $value );
142 + $value = wp_kses( $value, $tcmp_allowed_html_tags );
169 143 } else {
170 144 // do nothing ... could be a video or graphics object
171 145 }
172 146 }
@@ -388,19 +362,14 @@
388 362 private function writeGenericMessages( $type, $clean = true ) {
389 363 $type = sanitize_text_field( $type );
390 364 $result = false;
391 365 $array = $this->getRequest( $type . 'Messages', array() );
366 + // These messages are built by the plugin and have been already sanitized.
367 + // Trying to sanitize them again will break plugin functionality since some of the messages contain html.
392 368 if ( is_array( $array ) && count( $array ) > 0 ) {
393 369 $result = true;
394 - // Escape at the sink so safety no longer depends on every caller
395 - // pre-escaping its message arguments (see F-04). wp_kses_post keeps
396 - // the intentional markup (links, styled spans) some notices embed
397 - // while neutralising anything dangerous. It now wraps the wpautop()
398 - // result rather than each message, so the escaping is applied to
399 - // everything that is actually echoed.
400 - $html = wpautop( implode( "\n", $array ) );
401 370 ?>
402 - <div class="tcmp-box-<?php echo esc_attr( strtolower( $type ) ); ?>"><?php echo wp_kses_post( $html ); ?></div>
371 + <div class="tcmp-box-<?php echo strtolower( $type ); ?>"><?php echo wpautop( implode( "\n", $array ) ); ?></div>
403 372 <?php
404 373 }
405 374 if ( $clean ) {
406 375 $this->removeRequest( $type . 'Messages' );