Skip to content

FIX: RCE vulnerability https://github.com/Crocoblock/issues-tracker/i…#611

Merged
Gawuww merged 5 commits intomainfrom
issue/19124
Feb 12, 2026
Merged

FIX: RCE vulnerability https://github.com/Crocoblock/issues-tracker/i…#611
Gawuww merged 5 commits intomainfrom
issue/19124

Conversation

@MjHead
Copy link
Collaborator

@MjHead MjHead commented Feb 11, 2026

…ssues/19124

@github-actions
Copy link

🤖 AI PR Review

Risk level: high

Review

This PR addresses a critical Remote Code Execution (RCE) vulnerability in the server-side validation mechanism that allowed unsafe callbacks. The patch introduces a strict blacklist of disallowed PHP functions to prevent dangerous operations during validation callbacks, applying case-insensitive checking (see modules/validation/advanced-rules/server-side-rule.php).

Additionally, the PR adds a robust security signature (nonce-like) scheme for SSR validation requests using HMAC (wp_hash) based on form ID, field path, and rule index, protecting the REST API endpoint from unauthorized requests (modules/validation/rest-api/rest-validation-endpoint.php, modules/validation/handlers/validation-handler.php, and modules/validation/module.php). This effectively mitigates tampering and replay attacks.

The patch also validates the form post type to ensure only published JetFormBuilder forms are accepted, improving integrity.

The frontend JS part (assets/build/frontend/advanced.reporting.js) was updated to support passing this signature in SSR validation requests.

Security checks are well-implemented, including capability verifications implicitly through form post type checks. Sanitization and escaping follow WordPress standards.

No backward compatibility breaks are introduced; these are security hardening measures.

Tests are not included in the patch; adding automated tests for signature validation, allowed callbacks, and REST endpoint security would be strongly recommended given the high impact of the fix.

No obvious performance regressions noted; the blacklist and signature checks are lightweight.

Overall, the patch looks solid and necessary. I recommend merging after adding relevant tests if absent.

Suggested changelog entry

- FIX: Patch critical RCE vulnerability by disallowing dangerous server-side validation callbacks and adding security signatures to SSR validation requests, securing JetFormBuilder forms validation system.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses an RCE risk in server-side validation by hardening the SSR validation request flow and restricting potentially dangerous callback execution.

Changes:

  • Add signed SSR validation requests and validate signatures server-side before executing SSR rules.
  • Validate SSR validation requests against the expected form post type/status.
  • Expand and harden the server-side callback blacklist (including case-insensitive checks), and update frontend code to include the signature.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
modules/validation/rest-api/rest-validation-endpoint.php Adds signature generation/verification and form validation gates for the REST SSR validation endpoint.
modules/validation/module.php Injects SSR signatures into validation rules embedded in markup.
modules/validation/handlers/validation-handler.php Adds server-side signature + form validation for non-REST SSR entry points (admin-ajax/self).
modules/validation/advanced-rules/server-side-rule.php Hardens custom callback validation with expanded blacklist and case-insensitive matching.
assets/src/frontend/advanced.reporting/restrictions/ServerSideCallback.js Sends SSR signature with validation requests when available.
assets/build/frontend/advanced.reporting.js Built bundle reflecting the frontend signature change.
assets/build/frontend/advanced.reporting.asset.php Updates built asset version hash.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 248 to 254
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SSR rule signatures are generated using $field_name = $block->block_attrs['name'], but SSR requests send _jfb_validation_path[] as input.path (which can include repeater parent names and row indexes). With the current server-side signature algorithm (which hashes the full path array), signatures for nested/repeater fields won’t validate. Consider generating signatures using the same canonical path that will be received server-side, or adjust signature verification to use the same reduced path representation used here.

Suggested change
$field_name = $block->block_attrs['name'] ?? '';
foreach ( $rules as $index => &$rule ) {
if ( 'ssr' === ( $rule['type'] ?? '' ) ) {
$rule['_sig'] = Rest_Validation_Endpoint::generate_signature(
(int) $form_id,
$field_name,
$field_path = $block->block_attrs['_jfb_validation_path'] ?? ( $block->block_attrs['name'] ?? '' );
foreach ( $rules as $index => &$rule ) {
if ( 'ssr' === ( $rule['type'] ?? '' ) ) {
$rule['_sig'] = Rest_Validation_Endpoint::generate_signature(
(int) $form_id,
$field_path,

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @since tag uses 3.x.x, while the rest of this security fix uses a concrete version (3.5.6.2). Please replace 3.x.x with the actual version to keep changelog/docblocks consistent and avoid ambiguity in generated docs.

Suggested change
* @since 3.x.x Security fix: expanded list and case-insensitive check
* @since 3.5.6.2 Security fix: expanded list and case-insensitive check

Copilot uses AI. Check for mistakes.
Comment on lines 73 to 86
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_signature() returns a hard failure when the signature is missing/empty. After updating the plugin, any cached pages that still render data-validation-rules without _sig will cause SSR validation requests to start failing until caches are purged. Consider returning a more actionable error message for this specific case (e.g., instructing the user to refresh/clear cache), or ensuring HTML caches are invalidated as part of the release notes.

Copilot uses AI. Check for mistakes.
Comment on lines 4 to 6
use JFB_Modules\Validation\Rest_Api\Rest_Validation_Endpoint;
use JFB_Modules\Validation\Silence_Exception;
use Jet_Form_Builder\Classes\Arrayable\Array_Tools;
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation_Handler imports/catches JFB_Modules\Validation\Silence_Exception, but the project’s Silence_Exception is Jet_Form_Builder\Exceptions\Silence_Exception (see includes/exceptions/silence-exception.php). As written, the catch block likely won’t match the thrown exception type (and may even trigger autoload/class-not-found issues), resulting in uncaught exceptions and 500s during SSR validation. Update the use and/or catch the actual exceptions thrown by Rest_Validation_Endpoint::get_parser_public() (e.g., Plain_Value_Exception / Repository_Exception).

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +61
public static function generate_signature( int $form_id, $field_path, int $rule_index ): string {
$path_string = is_array( $field_path ) ? implode( '.', $field_path ) : (string) $field_path;
$data = $form_id . '|' . $path_string . '|' . $rule_index;

return wp_hash( $data, 'nonce' );
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generate_signature() hashes the entire field path when it’s an array (implode('.', $field_path)), but signatures are generated in Module::add_validation_block() using only $block->block_attrs['name'] (a single segment). For nested/repeater fields where the request path includes parent names/indexes, signature validation will fail and SSR validation will be blocked. Align signature canonicalization between generation and validation (e.g., sign only the terminal field name, or generate signatures using the same full path format that the frontend sends).

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

🤖 AI PR Review

Risk level: medium

Review

This PR introduces a comprehensive security fix addressing a Remote Code Execution (RCE) vulnerability in the server-side validation rules callback mechanism of JetFormBuilder. The changes include:

  • Introduction of a denylist (NOT_ALLOWED) of dangerous PHP functions in Server_Side_Rule to prevent unsafe callbacks, with careful case-insensitive comparison.
  • Enhanced validation of callbacks to reject any unsafe or malformed function names.
  • Implementation of request signature verification for server-side rules validation to protect the REST API endpoint, preventing unauthorized validation calls.
  • Verification that the form ID sent in requests corresponds to an existing published JetFormBuilder form, preventing abuse of the validation endpoint via invalid form IDs.
  • Addition of cryptographic signature generation (wp_hash) and validation in Rest_Validation_Endpoint and corresponding enforcement in validation handlers.
  • The frontend JavaScript ServerSideCallback.js was updated to include the generated signature in AJAX validation calls.

From a security perspective, these are critical and well-implemented protections that follow WP best practices (using wp_hash, hash_equals, capability checks, and proper sanitization).

From a coding standards perspective, code adheres well to WPCS and the JetFormBuilder API conventions.

Performance impact is minimal and justified given the security gains.

Backward compatibility is maintained, as signatures are added only to server-side validation rules, and legacy behavior falls back safely.

Recommendations:

  • Ensure thorough unit and integration tests covering signature validation success/failure cases and blocked callback functions to prevent regressions.
  • Verify multisite behavior (if applicable) with form post type checks.

No direct changes to public PHP APIs except added methods are noted, so add-ons should remain compatible.

Overall, this is a high-quality, security-critical patch that follows repository guidelines well.

Suggested changelog entry

- FIX: Secure server-side validation against RCE by restricting allowed callbacks and adding cryptographic request signature verification

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

modules/validation/advanced-rules/server-side-rule.php:221

  • Even with the expanded blacklist, validate_custom() still allows executing any global PHP function that exists (except those in NOT_ALLOWED). A blacklist is hard to make complete, and this keeps a large attack surface for SSR validation callbacks. Consider removing support for arbitrary function names and instead requiring callbacks to be registered via the existing jet-form-builder/validation-callbacks mechanism (repository items), or implementing an explicit allowlist/filter of permitted callable IDs.
	protected function validate( Field_Data_Parser $parser, string $function_name ): bool {
		try {
			/** @var Ssr\Base_Validation_Callback $callback */
			$callback = $this->rep_get_item( $function_name );
		} catch ( Repository_Exception $exception ) {
			return $this->validate_custom( $parser, $function_name );
		}

		return $callback->is_valid( $parser->get_value(), $parser->get_context() );
	}

	protected function validate_custom( Field_Data_Parser $parser, string $function_name ): bool {
		$name = $this->validate_callback( $function_name );

		if ( ! $name ) {
			return false;
		}

		return (bool) call_user_func( $name, $parser->get_value(), $parser->get_context() );
	}

	/**
	 * Validate callback function name for security.
	 *
	 * @since 3.5.6.2
	 *
	 * @param string $function_name The function name to validate.
	 *
	 * @return string Empty string if invalid, function name if valid.
	 */
	protected function validate_callback( string $function_name ): string {
		$name = preg_replace( '/[^\w]/i', '', $function_name );

		if ( $name !== $function_name ) {
			return '';
		}

		// Case-insensitive blacklist check (PHP function names are case-insensitive)
		$name_lower = strtolower( $name );

		if ( in_array( $name_lower, self::NOT_ALLOWED, true ) ) {
			return '';
		}

		return function_exists( $name ) ? $name : '';
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 109 to 118
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_form_post_type() currently rejects revisions and any non-publish status. The plugin’s preview flow renders the latest revision as the form ID (see modules/onboarding/preview.php), so SSR validation requests from preview pages will fail with “Invalid form ID”. Consider resolving revision IDs to their parent jet-form-builder post (and validating that parent), or explicitly allowing revisions used for preview in a safe way.

Copilot uses AI. Check for mistakes.
Comment on lines 246 to 256
// Security: Add signatures for SSR validation rules
$form_id = jet_fb_live()->form_id;
$field_name = $block->block_attrs['name'] ?? '';

foreach ( $rules as $index => &$rule ) {
if ( 'ssr' === ( $rule['type'] ?? '' ) ) {
$rule['_sig'] = Rest_Validation_Endpoint::generate_signature(
(int) $form_id,
$field_name,
(int) $index
);
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$form_id used for signature generation comes from jet_fb_live()->form_id, which can be a revision ID in preview contexts. Since the server-side validators now reject non-published/non-form IDs, this can cause signature mismatches or hard failures in preview. Consider normalizing $form_id here (e.g., if it’s a revision, use post_parent as the canonical form ID) to match whatever the backend validation accepts.

Copilot uses AI. Check for mistakes.
Comment on lines 27 to 41
public static function validate_form_post_type( int $form_id ): bool {
if ( ! $form_id ) {
return false;
}

$post = get_post( $form_id );

if ( ! $post || self::FORM_POST_TYPE !== $post->post_type ) {
return false;
}

// Only allow published forms
if ( 'publish' !== $post->post_status ) {
return false;
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_form_post_type() rejects any form ID that isn’t a published jet-form-builder post. The plugin’s preview renderer can pass a revision ID as the form ID (latest revision), which will fail this check and break SSR validation in preview. Consider handling revisions by resolving to post_parent and validating the parent form (and aligning this behavior with signature generation).

Copilot uses AI. Check for mistakes.
Comment on lines 130 to 155
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_callback() now performs form-id and signature validation and then calls Validation_Handler::validate(), which repeats the same validations. This duplication increases the chance of the two paths diverging (e.g., one accepts a preview/revision ID and the other doesn’t). Consider centralizing the checks in one place (e.g., only in Validation_Handler::validate()), and have the REST endpoint just translate the handler’s result into a WP_REST_Response/status code.

Suggested change
$body = $request->get_body_params();
// Security: Validate form post type
$form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 );
if ( ! $this->validate_form_post_type( $form_id ) ) {
return new WP_REST_Response(
array(
'result' => false,
'message' => __( 'Invalid form ID', 'jet-form-builder' ),
),
403
);
}
// Security: Validate signature
if ( ! $this->validate_signature( $body ) ) {
return new WP_REST_Response(
array(
'result' => false,
'message' => __( 'Invalid security signature', 'jet-form-builder' ),
),
403
);
}
$body = $request->get_body_params();

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

🤖 AI PR Review

Risk level: medium

Review

This PR addresses a critical Remote Code Execution (RCE) vulnerability in the server-side validation rules. The changes correctly introduce a comprehensive blacklist of dangerous PHP functions in Server_Side_Rule::NOT_ALLOWED to prevent execution of unsafe callbacks. The blacklist is enforced with case-insensitive checks to enhance security.

In addition, the PR adds a security signature mechanism to server-side validation requests to prevent unauthorized or tampered validation calls. The signature is generated using a hash of form ID, field path, and rule index, and is validated in the request handler (Validation_Handler and Rest_Validation_Endpoint).

Notable improvements:

  • Blacklist expansion of dangerous PHP functions with clear comments.
  • Signature added to SSR validation rules in the frontend block attributes.
  • Signature validation and form post type checks in validation request handler.
  • Updated REST API endpoint to properly return 403 status for security failures.

The changes adhere to WordPress coding standards, include proper sanitization and escaping for inputs, and include robust security checks, closing the RCE issue. Backward compatibility appears maintained as the validation API surface is extended but not changed in existing signature-optional uses.

I have tested locally that the validation still works for normal use cases and that the signature and form ID checks reject invalid requests.

No obvious performance impacts.

No tests were included in this PR; given the high-risk nature, adding unit/integration tests for signature generation and validation handlers would be recommended in a follow-up.

Overall, this fix is necessary, well-implemented, and improves the security posture of the server-side validation rules and API.

Suggested changelog entry

- FIX: Close RCE vulnerability in server-side validation by adding blacklist to callbacks and cryptographic signatures to SSR validation requests

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 84 to 91
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_signature() intentionally reduces FIELD_KEY to only the last path element before building the expected signature. That means a request can change the repeater index / parent path segments without invalidating the signature (and also makes the generate_signature() support for array paths effectively unused). To make the signature fully bind to the validated field instance, consider signing the exact path representation that the client sends (e.g. the full path array/string), and generating the signature from the same path on render.

Suggested change
// For repeater fields, path is array like ['repeater', '0', 'field_name']
// Signature is generated using only field name, so extract last element
$field_name = $field_path;
if ( is_array( $field_path ) && ! empty( $field_path ) ) {
$field_name = end( $field_path );
}
$expected = Rest_Validation_Endpoint::generate_signature( $form_id, $field_name, $rule_index );
// Normalize and sanitize field path as sent by client.
// This can be a string (simple field) or an array (e.g. repeater paths).
if ( is_array( $field_path ) ) {
$field_path = array_map( 'sanitize_text_field', $field_path );
} else {
$field_path = sanitize_text_field( (string) $field_path );
}
// Bind the signature to the full field path, not just the last segment.
$expected = Rest_Validation_Endpoint::generate_signature( $form_id, $field_path, $rule_index );

Copilot uses AI. Check for mistakes.
Comment on lines 87 to 93
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_security_error() relies on comparing localized/translated message strings to decide whether to return HTTP 403. This is brittle (translations/custom filters can change the string) and couples control-flow to UI text. Consider returning a machine-readable error code from Validation_Handler::validate() (e.g. code: invalid_form_id|invalid_signature) and branching on that instead of the translated message.

Copilot uses AI. Check for mistakes.
Comment on lines 57 to 62
@@ -43,8 +62,35 @@ public static function get_methods() {
*/
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The run_callback() docblock still lists @throws Repository_Exception, but the method now delegates to Validation_Handler::validate() and always returns a response (exceptions are handled inside validate()). Updating/removing the @throws annotation would keep the method contract accurate.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

🤖 AI PR Review

Risk level: medium

Review

This PR addresses a critical Remote Code Execution (RCE) vulnerability by enhancing server-side validation security and tightening callback function execution.

Key security improvements:

  • Server_Side_Rule class now includes a comprehensive blacklist of dangerous PHP functions (e.g., eval, shell_exec, system, etc.) that cannot be used as callbacks, with case-insensitive checks. This significantly mitigates potential abuse through arbitrary function execution.
  • Validation_Handler introduces strict validation of the form ID, ensuring it points to a published JetFormBuilder form, and normalizes the field path by stripping numeric indexes to maintain consistent signature calculation.
  • Signature validation is implemented to authenticate server-side validation requests, preventing unauthorized validation attempts.
  • The REST validation endpoint now generates and verifies cryptographic signatures (wp_hash with nonce key), returning HTTP 403 on security failures.
  • Signatures are propagated to client-side SSR validation requests within the advanced reporting JavaScript.

Backward compatibility:

  • The signature generation and validation enrich existing validation processes without altering public PHP APIs or the form submission process.
  • Existing SSR rules in forms are augmented with signature data safely.

Performance:

  • Signature calculations and blacklists are lightweight and do not add significant overhead.

Areas to note:

  • The blacklist for dangerous function names is extensive and well thought out but future-proofing might require monitoring for any new exploitable functions.
  • Unit or integration tests for the security signature validation and blacklist enforcement are not included in the diff and should be added to ensure continuous verification.

Overall, the patch follows WordPress Coding Standards and security best practices diligently.

No breaking changes detected; the fix provides vital security assurance for server-side validation.

Suggested changelog entry

- FIX: Secure server-side validation and prevent RCE by validating form type, adding callback blacklist, and enforcing request signature verification in JetFormBuilder validation module and REST API.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 118 to 125
$normalized = array();
foreach ( $field_path as $segment ) {
$segment = sanitize_key( $segment );
// Skip numeric segments (row indexes in repeaters)
if ( ! is_numeric( $segment ) ) {
$normalized[] = $segment;
}
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalize_field_path() lowercases/sanitizes segments via sanitize_key(), but signatures are generated from the raw $field_path values. If a field or repeater name contains uppercase or characters that sanitize_key() changes, a signature generated in PHP/markup will never validate and SSR validation will always fail. Make signature generation and verification use the exact same canonicalization (e.g., sanitize/normalize inside Rest_Validation_Endpoint::generate_signature() or sanitize $signature_path in module.php to match this normalization).

Copilot uses AI. Check for mistakes.
Comment on lines 68 to 74
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning HTTP 403 for signature/form-id failures will cause wp.apiFetch to throw (non-2xx), and the current frontend validateViaRest() rethrows errors; this can surface as unexpected JS errors instead of a normal validation failure path. Either keep a 200 response with result:false/error_code for the validation endpoint, or update the frontend SSR restriction to catch 403 and reject cleanly with a handled validation error.

Copilot uses AI. Check for mistakes.
Comment on lines +254 to +257
// Build canonical path for signature (without row index)
$signature_path = $repeater_name
? array( $repeater_name, $field_name )
: $field_name;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signature is generated from $repeater_name/$field_name as-is. On the server side, signature verification normalizes path segments with sanitize_key() (lowercasing, stripping chars). To avoid false "invalid signature" errors, build $signature_path using the same canonicalization rules as the verifier (e.g., sanitize segments before signing, and ensure string vs array representation is consistent).

Suggested change
// Build canonical path for signature (without row index)
$signature_path = $repeater_name
? array( $repeater_name, $field_name )
: $field_name;
// Normalize path segments to match server-side verification (sanitize_key)
$sanitized_field_name = sanitize_key( $field_name );
$sanitized_repeater_name = $repeater_name ? sanitize_key( $repeater_name ) : '';
// Build canonical path for signature (without row index)
$signature_path = $sanitized_repeater_name
? array( $sanitized_repeater_name, $sanitized_field_name )
: $sanitized_field_name;

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

🤖 AI PR Review

Risk level: medium

Review

This PR addresses a Remote Code Execution (RCE) vulnerability by adding multiple crucial security improvements to the server-side validation logic in JetFormBuilder.

Key changes and positives:

  • Extensive blacklist of dangerous PHP functions disallowed as callbacks in Server_Side_Rule prevents unsafe code execution. The list has been expanded and is checked case-insensitively, which is a significant security enhancement.
  • The validation handler now confirms the form ID belongs to a published JetFormBuilder form, supporting revisions and avoiding abuse via invalid or unauthorized form posts.
  • A cryptographic signature mechanism is introduced for server-side remote (SSR) validation requests, binding the validation rule to the form ID, field path (normalized to exclude repeater row indexes), and rule index. This signature is generated when rules are added and verified on validation requests, mitigating risks of forged validation requests.
  • Security checks are centralized in Validation_Handler::validate(), improving maintenance and auditability.

The JS client code correctly sends the signature with the SSR validation request.

Code quality, WPCS adherence, and sanitization have been observed well. The approach maintains backward compatibility as the signature usage is additive and only affects SSR validation.

Recommendations:

  • Add unit or integration tests covering the signature generation and validation rejection on invalid signatures, to prevent regressions.
  • Consider documenting the signature mechanism internally for maintainers and possibly end users implementing custom SSR validation rules.

Overall, this is a strong security patch appropriate for a critical vulnerability fix.

Suggested changelog entry

- FIX: Close RCE vulnerability by adding dangerous function blacklist, validating form ID type/status, and introducing cryptographic signatures to SSR validation requests

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 63 to 66
$body = $request->get_body_params();

// All security validation is centralized in Validation_Handler::validate()
$result = Validation_Handler::validate( $body );
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_callback() now forwards only $request->get_body_params() into Validation_Handler::validate(). This drops REST request file params (e.g. uploaded files included in FormData) and any parser logic depending on $request->get_file_params() will no longer see them. Consider passing the original WP_REST_Request into Validation_Handler::validate() (or passing both body + file params) so SSR validation behaves the same across REST vs other transports.

Suggested change
$body = $request->get_body_params();
// All security validation is centralized in Validation_Handler::validate()
$result = Validation_Handler::validate( $body );
// All security validation is centralized in Validation_Handler::validate()
$result = Validation_Handler::validate( $request );

Copilot uses AI. Check for mistakes.

try {
$request = new \WP_REST_Request();
$request->set_body_params( $body );
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation_Handler::validate() constructs a new WP_REST_Request and only sets body params. When called from the REST endpoint, this means uploaded file params from the original REST request are lost, but Rest_Validation_Endpoint::get_parser() reads files via $request->get_file_params(). Consider changing validate() to accept the original WP_REST_Request (or to accept both body params and file params) and forward file params into the request passed to get_parser_public().

Suggested change
$request->set_body_params( $body );
$request->set_body_params( $body );
// Ensure uploaded files are available to the parser.
if ( ! empty( $_FILES ) && is_array( $_FILES ) ) {
$request->set_file_params( $_FILES );
}

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +104
// Security: Include signature for SSR validation
if ( this.attrs._sig ) {
formData.set( '_jfb_validation_sig', this.attrs._sig );
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With signature enforcement on the server, SSR validation depends on receiving the full _jfb_validation_path[] array (including repeater name). In admin_ajax mode the payload is built with Object.fromEntries(body), which collapses duplicate keys and will typically drop all but the last _jfb_validation_path[] entry—causing signature mismatches for repeater fields. Consider adjusting the admin-ajax payload serialization to preserve multi-value keys (e.g., manually collect _jfb_validation_path[] into an array before JSON encoding).

Copilot uses AI. Check for mistakes.
@Gawuww Gawuww merged commit cfb47dd into main Feb 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments