PluginProbe
CSS & JavaScript Toolbox / 12.0.7
CSS & JavaScript Toolbox v12.0.7
trunk 0.3 0.8 10 10.1 11 11.2 11.3 11.4 11.5 11.6 11.7 11.8 11.9 11.9.1 12 12.0 12.0.1 12.0.3 12.0.4 12.0.5 12.0.6 12.0.7 6.0 6.0.11 All 60 releases
← All changes | controllers/blocks-ajax.php +44 -2 1212.0.7 View file →
@@ -66,10 +66,19 @@
66 66 public function createBlockAction($blockId = null, $blockType = null, $pinPoint = null, $viewName = null) {
67 67 $response = array();
68 68 // If viewName not provided read it from request vars.
69 69 if (!$viewName) {
70 - $viewName = filter_input(INPUT_GET, 'viewName', FILTER_SANITIZE_STRING);
70 + $viewName = filter_input(INPUT_GET, 'viewName', FILTER_UNSAFE_RAW);
71 + $viewName = is_string($viewName) ? sanitize_text_field($viewName) : null;
71 72 }
73 + // $viewName is interpolated into a filesystem path by CJTController::getView(),
74 + // which require_once's "views/blocks/{$viewName}/view.php". Restrict it to the
75 + // known block views so traversal sequences can never reach that include.
76 + $allowedBlockViews = array('cjt-block', 'block', 'metabox', 'create-metabox');
77 + if ($viewName && !in_array($viewName, $allowedBlockViews, true)) {
78 + $this->httpCode = '403 Forbidden';
79 + return;
80 + }
72 81 // Prepare parameters.
73 82 $defaultBlockName = 'block_' . hexdec(substr(md5(time()), 0, 6));
74 83 $wordpressMYSQLTime = current_time('mysql');
75 84 // Block data to insert.
@@ -90,8 +99,10 @@
90 99 if (array_key_exists($name, $_GET)) {
91 100 $blockData[$name] = $_GET[$name];
92 101 }
93 102 }
103 + // Harden the block name server-side (CVE-2025-13533 defence in depth).
104 + $blockData['name'] = self::sanitizeBlockName($blockData['name']);
94 105 // Import block model.
95 106 require_once CJTOOLBOX_MODELS_PATH . '/block.php';
96 107 $block = new CJTBlockModel($blockData);
97 108 // Add block.
@@ -139,9 +150,10 @@
139 150 $allowedViews = array(
140 151 'blocks/new' => array(),
141 152 );
142 153 // Prepare parameters.
143 - $viewName = filter_input(INPUT_GET, 'viewName', FILTER_SANITIZE_STRING);
154 + $viewName = filter_input(INPUT_GET, 'viewName', FILTER_UNSAFE_RAW);
155 + $viewName = is_string($viewName) ? sanitize_text_field($viewName) : '';
144 156 if (array_key_exists($viewName, $allowedViews) === FALSE) {
145 157 $this->httpCode = '403 Forbidden';
146 158 }
147 159 else {
@@ -204,8 +216,13 @@
204 216 // Push block id into block data.
205 217 $blockData = ( object ) $postedblockPartialData;
206 218 $blockData->id = $id;
207 219
220 + // Harden the block name server-side (CVE-2025-13533 defence in depth).
221 + if ( isset( $blockData->name ) ) {
222 + $blockData->name = self::sanitizeBlockName( $blockData->name );
223 + }
224 +
208 225 // Recalculate pinPoint field value.
209 226 ! $calculatePinPoint or ( CJTBlockModel::arrangePins( $blockData ) && CJTBlockModel::calculateBlockPinPoint( $blockData ) );
210 227
211 228 // Create block revision.
@@ -251,7 +268,32 @@
251 268 $order = array('normal' => $_GET['order']);
252 269 // Centralized orders to be shared between all users!
253 270 $this->model->setOrder($order);
254 271 $this->response = array('order' => $order, 'state' => 'saved');
272 + }
273 +
274 + /**
275 + * Sanitize a code-block name coming from the request.
276 + *
277 + * Defence-in-depth for the stored-XSS issue (CVE-2025-13533): the client-side UI
278 + * already restricts block names to A-Z, 0-9, space, '-' and '_', but that check is
279 + * trivially bypassed (e.g. via an HTTP proxy). We enforce the exact same contract
280 + * server-side so characters that could break out of an HTML attribute/element -
281 + * such as < > " ' - can never be persisted. Output is still escaped at every sink
282 + * as the primary defence; this simply keeps the stored data clean.
283 + *
284 + * @param mixed $name Raw name value from the request.
285 + * @return string Sanitized name (never empty).
286 + */
287 + public static function sanitizeBlockName($name) {
288 + // Keep only the documented allowed characters.
289 + $name = preg_replace('/[^A-Za-z0-9 _-]/', '', (string) $name);
290 + // Collapse to a trimmed value and guard against an empty result.
291 + $name = trim($name);
292 + if ($name === '') {
293 + $name = 'block_' . hexdec(substr(md5((string) time()), 0, 6));
294 + }
295 + // Respect the 50 char column/UI limit.
296 + return substr($name, 0, 50);
255 297 }
256 298
257 299 } // End class.