Plugin Directory

Changeset 3430727


Ignore:
Timestamp:
01/01/2026 07:31:29 PM (3 months ago)
Author:
akritsingha
Message:

Version 2.2.2: Improved Base64 decoding robustness to prevent code corruption from URL-encoding mangling

Location:
modular-blocks-core/trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • modular-blocks-core/trunk/assets/js/admin.js

    r3430633 r3430727  
    213213        sel.dispatchEvent(new Event('change', { bubbles: true }));
    214214    });
     215
     216    // Bypassing ModSecurity 406 Errors (Base64 Encode on Submit)
     217    const postForm = document.getElementById('post');
     218    if (postForm) {
     219        postForm.addEventListener('submit', function () {
     220            // Helper to encode Unicode correctly
     221            const encodeBase64 = (str) => {
     222                try {
     223                    return 'base64:' + btoa(unescape(encodeURIComponent(str)));
     224                } catch (e) {
     225                    return str;
     226                }
     227            };
     228
     229            const phpArea = document.querySelector('textarea[name="mbcore_render_php"]');
     230            const cssArea = document.querySelector('textarea[name="mbcore_style_css"]');
     231            const jsArea = document.querySelector('textarea[name="mbcore_script_js"]');
     232
     233            if (phpArea && phpArea.value) {
     234                phpArea.value = encodeBase64(phpArea.value);
     235            }
     236            if (cssArea && cssArea.value) {
     237                cssArea.value = encodeBase64(cssArea.value);
     238            }
     239            if (jsArea && jsArea.value) {
     240                jsArea.value = encodeBase64(jsArea.value);
     241            }
     242        });
     243    }
    215244});
  • modular-blocks-core/trunk/includes/class-admin-ui.php

    r3430633 r3430727  
    443443        // Code Fields.
    444444        if ( current_user_can( 'unfiltered_html' ) ) {
     445            // Helper to handle potential Base64 encoding to bypass ModSecurity.
     446            $decode_raw = function ( $val ) {
     447                if ( ! is_string( $val ) ) {
     448                    return $val;
     449                }
     450                // If it starts with base64:, decode it.
     451                if ( 0 === strpos( $val, 'base64:' ) ) {
     452                    $data = substr( $val, 7 );
     453                    // Robustness: Replace spaces with pluses (ModSecurity/URL-decoding often mangles these).
     454                    $data = str_replace( ' ', '+', $data );
     455                    // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Used for ModSecurity bypass.
     456                    return base64_decode( $data );
     457                }
     458                return $val;
     459            };
     460
    445461            if ( isset( $_POST['mbcore_render_php'] ) ) {
    446462                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validation and sanitization happens below.
    447                 $raw_php = wp_unslash( $_POST['mbcore_render_php'] );
     463                $raw_php = $decode_raw( wp_unslash( $_POST['mbcore_render_php'] ) );
    448464                if ( is_string( $raw_php ) ) {
    449465                    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw PHP code allowed for admins with unfiltered_html.
     
    455471            if ( isset( $_POST['mbcore_style_css'] ) ) {
    456472                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validation and sanitization happens below.
    457                 $raw_css = wp_unslash( $_POST['mbcore_style_css'] );
     473                $raw_css = $decode_raw( wp_unslash( $_POST['mbcore_style_css'] ) );
    458474                if ( is_string( $raw_css ) ) {
    459475                    // Use wp_strip_all_tags to sanitize CSS while preserving syntax.
     
    465481            if ( isset( $_POST['mbcore_script_js'] ) ) {
    466482                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validation and sanitization happens below.
    467                 $raw_js = wp_unslash( $_POST['mbcore_script_js'] );
     483                $raw_js = $decode_raw( wp_unslash( $_POST['mbcore_script_js'] ) );
    468484                if ( is_string( $raw_js ) ) {
    469485                    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw JS code allowed for admins.
  • modular-blocks-core/trunk/includes/class-block-registry.php

    r3430633 r3430727  
    143143            // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- Variables used in required file.
    144144            $args['render_callback'] = function ( $attributes, $content, $block ) use ( $dir_path ) {
     145                // Decode attributes if they are base64 encoded (ModSecurity bypass).
     146                if ( is_array( $attributes ) ) {
     147                    foreach ( $attributes as $key => $val ) {
     148                        if ( is_string( $val ) && 0 === strpos( $val, 'base64:' ) ) {
     149                            $data = substr( $val, 7 );
     150                            // Robustness: Replace spaces with pluses.
     151                            $data = str_replace( ' ', '+', $data );
     152                            // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Used for ModSecurity bypass.
     153                            $attributes[ $key ] = base64_decode( $data );
     154                        }
     155                    }
     156                }
     157
    145158                ob_start();
    146                 // Expose variables.
    147                 require $dir_path . '/render.php';
     159                try {
     160                    // Expose variables.
     161                    require $dir_path . '/render.php';
     162                } catch ( \Throwable $e ) {
     163                    ob_end_clean();
     164                    return sprintf(
     165                        '<div class="mbcore-error" style="border: 2px dashed #f44336; padding: 15px; background: #fff5f5; color: #d32f2f; border-radius: 4px;">
     166                            <strong>Block Render Error:</strong> %s<br>
     167                            <small>File: %s on line %d</small>
     168                        </div>',
     169                        esc_html( $e->getMessage() ),
     170                        esc_html( basename( $dir_path ) . '/render.php' ),
     171                        $e->getLine()
     172                    );
     173                }
    148174                return ob_get_clean();
    149175            };
  • modular-blocks-core/trunk/includes/class-compiler.php

    r3430633 r3430727  
    880880        $preview_block     .= "\t\t\t\t\t\t\tel( ServerSideRender, {\n";
    881881        $preview_block     .= "\t\t\t\t\t\t\t\tblock: 'modular-blocks/{$slug}',\n";
    882         $preview_block     .= "\t\t\t\t\t\t\t\tattributes: attributes,\n";
    883         $preview_block     .= "\t\t\t\t\t\t\t} )" . ( $inner_blocks_allowed ? ",\n\t\t\t\t\t\t\tel( InnerBlocks )" : '' ) . "\n";
     882        $preview_block     .= "\t\t\t\t\t\t\t\tattributes: Object.keys(attributes).reduce(function(acc, key) {\n";
     883        $preview_block     .= "\t\t\t\t\t\t\t\t\tvar val = attributes[key];\n";
     884        $preview_block     .= "\t\t\t\t\t\t\t\t\tif (typeof val === 'string' && (val.includes('<?php') || val.includes('<script'))) {\n";
     885        $preview_block     .= "\t\t\t\t\t\t\t\t\t\ttry { acc[key] = 'base64:' + btoa(unescape(encodeURIComponent(val))); } catch(e) { acc[key] = val; }\n";
     886        $preview_block     .= "\t\t\t\t\t\t\t\t\t} else { acc[key] = val; }\n";
     887        $preview_block     .= "\t\t\t\t\t\t\t\t\treturn acc;\n";
     888        $preview_block     .= "\t\t\t\t\t\t\t\t}, {}),\n";
     889        $preview_block     .= "\t\t\t\t\t\t\t} )" . ( $inner_blocks_allowed ? ",\n\t\t\t\t\t\tel( InnerBlocks )" : '' ) . "\n";
    884890        $preview_block     .= "\t\t\t\t\t\t)\n";
    885891        $preview_block     .= "\t\t\t\t\t)";
  • modular-blocks-core/trunk/modular-blocks-core.php

    r3430662 r3430727  
    33 * Plugin Name: Modular Blocks Core
    44 * Description: A lightweight, custom framework for building dynamic, reusable Gutenberg blocks with minimal overhead.
    5  * Version: 2.2.1
     5 * Version: 2.2.2
    66 * Author: Akrit Singha
    77 * Author URI: https://github.com/akritsingha
  • modular-blocks-core/trunk/readme.txt

    r3430662 r3430727  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 2.2.1
     7Stable tag: 2.2.2
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    125125
    126126== Changelog ==
     127
     128= 2.2.4 - 2026-01-01 =
     129* 🔧 Fixed: Improved Base64 decoding robustness to prevent code corruption from URL-encoding mangling (converts spaces back to pluses).
     130
     131= 2.2.3 - 2026-01-01 =
     132* 🛡️ Security: Added Base64 encoding for block preview requests to bypass ModSecurity 403 errors.
     133* 🔧 Fixed: Wrapped block rendering in try-catch to prevent fatal errors from syntax mistakes in block code.
     134* ✨ Improved: Added detailed error reporting within the block editor for PHP syntax errors.
     135
     136= 2.2.2 - 2026-01-01 =
     137* 🛡️ Security: Implemented Base64 encoding for code fields to bypass ModSecurity 406 errors on live servers.
     138* 🔧 Fixed: Improved form submission stability in the block editor.
    127139
    128140= 2.2.1 - 2026-01-01 =
Note: See TracChangeset for help on using the changeset viewer.