PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.5.6.3
JetFormBuilder — Dynamic Blocks Form Builder v3.5.6.3
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 4 months ago
validation-handler.php
185 lines
1 <?php
2 namespace JFB_Modules\Validation\Handlers;
3
4 use JFB_Modules\Validation\Rest_Api\Rest_Validation_Endpoint;
5 use Jet_Form_Builder\Exceptions\Repository_Exception;
6 use Jet_Form_Builder\Request\Exceptions\Plain_Value_Exception;
7 use Jet_Form_Builder\Classes\Arrayable\Array_Tools;
8
9 class Validation_Handler {
10
11 /**
12 * Form post type constant for validation.
13 *
14 * @since 3.5.6.2
15 */
16 const FORM_POST_TYPE = 'jet-form-builder';
17
18 /**
19 * Validate that the given ID belongs to a published JetFormBuilder form.
20 * Supports revision IDs by resolving them to their parent form.
21 *
22 * @since 3.5.6.2
23 *
24 * @param int $form_id The form ID to validate (can be a revision ID).
25 *
26 * @return bool True if valid, false otherwise.
27 */
28 public static function validate_form_post_type( int $form_id ): bool {
29 if ( ! $form_id ) {
30 return false;
31 }
32
33 $post = get_post( $form_id );
34
35 if ( ! $post ) {
36 return false;
37 }
38
39 // Handle revisions: resolve to parent form and validate the parent
40 if ( 'revision' === $post->post_type ) {
41 $parent_id = wp_get_post_parent_id( $form_id );
42 if ( ! $parent_id ) {
43 return false;
44 }
45
46 $post = get_post( $parent_id );
47 if ( ! $post ) {
48 return false;
49 }
50 }
51
52 // Must be a JetFormBuilder form
53 if ( self::FORM_POST_TYPE !== $post->post_type ) {
54 return false;
55 }
56
57 // Only allow published forms
58 if ( 'publish' !== $post->post_status ) {
59 return false;
60 }
61
62 return true;
63 }
64
65 /**
66 * Validate the signature from request body.
67 *
68 * @since 3.5.6.2
69 *
70 * @param array $body Request body parameters.
71 *
72 * @return bool True if signature is valid, false otherwise.
73 */
74 public static function validate_signature( array $body ): bool {
75 $form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 );
76 $field_path = $body[ Rest_Validation_Endpoint::FIELD_KEY ] ?? '';
77 $rule_index = absint( $body[ Rest_Validation_Endpoint::RULE_INDEX_KEY ] ?? 0 );
78 $signature = sanitize_text_field( $body[ Rest_Validation_Endpoint::SIGNATURE_KEY ] ?? '' );
79
80 if ( empty( $signature ) || empty( $form_id ) ) {
81 return false;
82 }
83
84 // Normalize the field path for signature verification.
85 // Client sends full path like ['repeater_name', '0', 'field_name']
86 // but signature is generated without row indexes: ['repeater_name', 'field_name']
87 $normalized_path = self::normalize_field_path( $field_path );
88
89 $expected = Rest_Validation_Endpoint::generate_signature( $form_id, $normalized_path, $rule_index );
90
91 return hash_equals( $expected, $signature );
92 }
93
94 /**
95 * Normalize field path by removing numeric row indexes.
96 * Converts ['repeater', '0', 'field'] to ['repeater', 'field']
97 * and sanitizes all path segments.
98 *
99 * @since 3.5.6.2
100 *
101 * @param string|array $field_path The field path from request.
102 *
103 * @return string|array Normalized path without row indexes.
104 */
105 protected static function normalize_field_path( $field_path ) {
106 if ( ! is_array( $field_path ) ) {
107 return sanitize_text_field( (string) $field_path );
108 }
109
110 $normalized = array();
111 foreach ( $field_path as $segment ) {
112 $segment = sanitize_text_field( $segment );
113 // Skip numeric segments (row indexes in repeaters)
114 if ( ! is_numeric( $segment ) ) {
115 $normalized[] = $segment;
116 }
117 }
118
119 return $normalized;
120 }
121
122 public static function validate( $body ) {
123 remove_all_actions( 'jet-form-builder/validate-field' );
124
125 // Security: Validate form post type
126 $form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 );
127
128 if ( ! self::validate_form_post_type( $form_id ) ) {
129 return array(
130 'result' => false,
131 'message' => __( 'Invalid form ID', 'jet-form-builder' ),
132 );
133 }
134
135 // Security: Validate signature
136 if ( ! self::validate_signature( $body ) ) {
137 return array(
138 'result' => false,
139 'message' => __( 'Invalid security signature', 'jet-form-builder' ),
140 );
141 }
142
143 try {
144 $request = new \WP_REST_Request();
145 $request->set_body_params( $body );
146 $parser = ( new Rest_Validation_Endpoint() )->get_parser_public( $request );
147 } catch ( Plain_Value_Exception $exception ) {
148 return array(
149 'result' => false,
150 'message' => __( 'Unresolved parser for field', 'jet-form-builder' ),
151 );
152 } catch ( Repository_Exception $exception ) {
153 return array(
154 'result' => false,
155 'message' => __( 'Unresolved parser for field', 'jet-form-builder' ),
156 );
157 }
158
159 $validation = $parser->get_setting( 'validation' );
160 $ssr_attrs = Array_Tools::get(
161 $validation,
162 array( 'rules', $body[ Rest_Validation_Endpoint::RULE_INDEX_KEY ] )
163 );
164
165 if ( null === $parser->get_value() || '' === $parser->get_value() || empty( $ssr_attrs['value'] ) ) {
166 return array(
167 'result' => false,
168 'message' => __( 'Field value or callback is empty', 'jet-form-builder' ),
169 );
170 }
171
172 /** @var Module $module */
173 $module = jet_form_builder()->module( 'validation' );
174 $ssr_rule = $module->get_rules()->get_ssr();
175
176 $ssr_rule->set_settings( $ssr_attrs );
177 $ssr_rule->validate_field( $parser );
178
179 return array(
180 'result' => empty( $parser->get_errors() ),
181 'message' => empty( $parser->get_errors() ) ? '' : __( 'Validation failed', 'jet-form-builder' ),
182 );
183 }
184 }
185