PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / trunk
JetFormBuilder — Dynamic Blocks Form Builder vtrunk
3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / modules / validation / handlers / validation-handler.php
jetformbuilder / modules / validation / handlers Last commit date
ajax-validation-handler.php 1 year ago self-validation-handler.php 1 year ago validation-handler.php 1 week ago
validation-handler.php
177 lines
1 <?php
2 namespace JFB_Modules\Validation\Handlers;
3
4 use Jet_Form_Builder\Blocks\Block_Helper;
5 use JFB_Modules\Validation\Rest_Api\Rest_Validation_Endpoint;
6 use Jet_Form_Builder\Exceptions\Repository_Exception;
7 use Jet_Form_Builder\Request\Exceptions\Plain_Value_Exception;
8 use Jet_Form_Builder\Classes\Arrayable\Array_Tools;
9
10 class Validation_Handler {
11
12 const MAIN_SIGNATURES_KEY = '_jfb_validation_sigs';
13
14 /**
15 * Validate that the given ID belongs to a published JetFormBuilder form.
16 *
17 * @since 3.5.6.2
18 *
19 * @param int $form_id The form ID to validate.
20 *
21 * @return bool True if valid, false otherwise.
22 */
23 public static function validate_form_post_type( int $form_id ): bool {
24 return Block_Helper::is_valid_form_post( $form_id );
25 }
26
27 /**
28 * Validate the signature from request body.
29 *
30 * @since 3.5.6.2
31 *
32 * @param array $body Request body parameters.
33 *
34 * @return bool True if signature is valid, false otherwise.
35 */
36 public static function validate_signature( array $body ): bool {
37 $form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 );
38 $field_path = $body[ Rest_Validation_Endpoint::FIELD_KEY ] ?? '';
39 $rule_index = absint( $body[ Rest_Validation_Endpoint::RULE_INDEX_KEY ] ?? 0 );
40 $signature = sanitize_text_field( $body[ Rest_Validation_Endpoint::SIGNATURE_KEY ] ?? '' );
41
42 if ( empty( $signature ) || empty( $form_id ) ) {
43 return false;
44 }
45
46 // Normalize the field path for signature verification.
47 // Client sends full path like ['repeater_name', '0', 'field_name']
48 // but signature is generated without row indexes: ['repeater_name', 'field_name']
49 $normalized_path = self::normalize_field_path( $field_path );
50
51 $expected = Rest_Validation_Endpoint::generate_signature( $form_id, $normalized_path, $rule_index );
52
53 return hash_equals( $expected, $signature );
54 }
55
56 /**
57 * Normalize field path by removing numeric row indexes.
58 * Converts ['repeater', '0', 'field'] to ['repeater', 'field']
59 * and sanitizes all path segments.
60 *
61 * @since 3.5.6.2
62 *
63 * @param string|array $field_path The field path from request.
64 *
65 * @return string|array Normalized path without row indexes.
66 */
67 public static function normalize_field_path( $field_path ) {
68 if ( ! is_array( $field_path ) ) {
69 return sanitize_text_field( (string) $field_path );
70 }
71
72 $normalized = array();
73 foreach ( $field_path as $segment ) {
74 $segment = sanitize_text_field( $segment );
75 // Skip numeric segments (row indexes in repeaters)
76 if ( ! is_numeric( $segment ) ) {
77 $normalized[] = $segment;
78 }
79 }
80
81 return $normalized;
82 }
83
84 public static function get_signature_key( $field_path, int $rule_index ): string {
85 $normalized = self::normalize_field_path( $field_path );
86 $path_string = is_array( $normalized ) ? implode( '.', $normalized ) : (string) $normalized;
87
88 return md5( $path_string . '|' . $rule_index );
89 }
90
91 public static function validate_main_signature( array $body, int $form_id, $field_path, int $rule_index ): bool {
92 $signatures = $body[ self::MAIN_SIGNATURES_KEY ] ?? array();
93
94 if ( ! is_array( $signatures ) ) {
95 return false;
96 }
97
98 $key = self::get_signature_key( $field_path, $rule_index );
99 $signature = sanitize_text_field( $signatures[ $key ] ?? '' );
100
101 if ( empty( $signature ) || empty( $form_id ) ) {
102 return false;
103 }
104
105 $expected = Rest_Validation_Endpoint::generate_signature(
106 $form_id,
107 self::normalize_field_path( $field_path ),
108 $rule_index
109 );
110
111 return hash_equals( $expected, $signature );
112 }
113
114 public static function validate( $body ) {
115 remove_all_actions( 'jet-form-builder/validate-field' );
116
117 // Security: Validate form post type
118 $form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 );
119
120 if ( ! self::validate_form_post_type( $form_id ) ) {
121 return array(
122 'result' => false,
123 'message' => __( 'Invalid form ID', 'jet-form-builder' ),
124 );
125 }
126
127 // Security: Validate signature
128 if ( ! self::validate_signature( $body ) ) {
129 return array(
130 'result' => false,
131 'message' => __( 'Invalid security signature', 'jet-form-builder' ),
132 );
133 }
134
135 try {
136 $request = new \WP_REST_Request();
137 $request->set_body_params( $body );
138 $parser = ( new Rest_Validation_Endpoint() )->get_parser_public( $request );
139 } catch ( Plain_Value_Exception $exception ) {
140 return array(
141 'result' => false,
142 'message' => __( 'Unresolved parser for field', 'jet-form-builder' ),
143 );
144 } catch ( Repository_Exception $exception ) {
145 return array(
146 'result' => false,
147 'message' => __( 'Unresolved parser for field', 'jet-form-builder' ),
148 );
149 }
150
151 $validation = $parser->get_setting( 'validation' );
152 $ssr_attrs = Array_Tools::get(
153 $validation,
154 array( 'rules', $body[ Rest_Validation_Endpoint::RULE_INDEX_KEY ] )
155 );
156
157 if ( null === $parser->get_value() || '' === $parser->get_value() || empty( $ssr_attrs['value'] ) ) {
158 return array(
159 'result' => false,
160 'message' => __( 'Field value or callback is empty', 'jet-form-builder' ),
161 );
162 }
163
164 /** @var Module $module */
165 $module = jet_form_builder()->module( 'validation' );
166 $ssr_rule = $module->get_rules()->get_ssr();
167
168 $ssr_rule->set_settings( $ssr_attrs );
169 $ssr_rule->validate_field( $parser );
170
171 return array(
172 'result' => empty( $parser->get_errors() ),
173 'message' => empty( $parser->get_errors() ) ? '' : __( 'Validation failed', 'jet-form-builder' ),
174 );
175 }
176 }
177