PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.4.1
JetFormBuilder — Dynamic Blocks Form Builder v3.6.4.1
3.6.4.1 3.6.4 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 / security / honeypot / module.php
jetformbuilder / modules / security / honeypot Last commit date
module.php 3 days ago
module.php
270 lines
1 <?php
2
3
4 namespace JFB_Modules\Security\Honeypot;
5
6 use Jet_Form_Builder\Live_Form;
7 use JFB_Components\Module\Base_Module_It;
8 use JFB_Modules\Security\Exceptions\Spam_Exception;
9
10 // If this file is called directly, abort.
11 if ( ! defined( 'WPINC' ) ) {
12 die;
13 }
14
15 class Module implements Base_Module_It {
16
17
18 const FIELD_NAME = 'name';
19 const FIELD_EMAIL = 'email';
20 const FIELD_PHONE = 'phone';
21 const FIELD_COMPANY = 'company';
22
23 const SPAM_EXCEPTION = 'honeypot';
24
25 const HONEYPOT_STYLE_HANDLE = 'jet-form-builder-honeypot';
26
27 public function __construct() {
28 add_filter( 'jet-form-builder/security/spam-statuses', array( $this, 'add_spam_statuses' ) );
29 }
30
31 public function rep_item_id() {
32 return 'honeypot';
33 }
34
35 public function condition(): bool {
36 return true;
37 }
38
39 public function add_spam_statuses( $statuses ) {
40 $statuses[] = self::SPAM_EXCEPTION;
41
42 return $statuses;
43 }
44
45 public function init_hooks() {
46 add_filter(
47 'jet-form-builder/before-render-field',
48 array( $this, 'on_render_field' ),
49 10,
50 3
51 );
52
53 add_filter(
54 'jet-form-builder/request-handler/request',
55 array( $this, 'handle_request' )
56 );
57
58 add_filter(
59 'jet-form-builder/message-types',
60 array( $this, 'handle_global_messages' )
61 );
62
63 add_action(
64 'wp_enqueue_scripts',
65 array( $this, 'enqueue_styles' ),
66 20
67 );
68 }
69
70 public function remove_hooks() {
71 remove_filter(
72 'jet-form-builder/before-render-field',
73 array( $this, 'on_render_field' )
74 );
75
76 remove_filter(
77 'jet-form-builder/request-handler/request',
78 array( $this, 'handle_request' )
79 );
80
81 remove_filter(
82 'jet-form-builder/message-types',
83 array( $this, 'handle_global_messages' )
84 );
85
86 remove_action(
87 'wp_enqueue_scripts',
88 array( $this, 'enqueue_styles' ),
89 20
90 );
91 }
92
93 public function enqueue_styles() {
94 wp_register_style(
95 self::HONEYPOT_STYLE_HANDLE,
96 false,
97 array(),
98 jet_form_builder()->get_version()
99 );
100 wp_enqueue_style( self::HONEYPOT_STYLE_HANDLE );
101 wp_add_inline_style(
102 self::HONEYPOT_STYLE_HANDLE,
103 '
104 .jfb-user-info,
105 .jfb-user-info input {
106 position: absolute !important;
107 top: 0 !important;
108 left: 0 !important;
109 width: 1px !important;
110 height: 1px !important;
111 overflow: hidden !important;
112 clip-path: inset(50%) !important;
113 white-space: nowrap !important;
114 user-select: none !important;
115 -webkit-user-select: none !important;
116 pointer-events: none !important;
117 }
118 '
119 );
120 }
121
122 private function get_honeypot_base_names(): array {
123 return array(
124 self::FIELD_NAME,
125 self::FIELD_EMAIL,
126 self::FIELD_PHONE,
127 self::FIELD_COMPANY,
128 );
129 }
130
131 private function get_honeypot_field_names( array $blocks ): array {
132 $existing_names = array_fill_keys(
133 $this->get_existing_field_names( $blocks ),
134 true
135 );
136
137 $honeypot_names = array();
138
139 foreach ( $this->get_honeypot_base_names() as $base_name ) {
140 $honeypot_names[] = $this->resolve_honeypot_field_name(
141 $base_name,
142 $existing_names
143 );
144
145 $existing_names[ end( $honeypot_names ) ] = true;
146 }
147
148 return $honeypot_names;
149 }
150
151 private function resolve_honeypot_field_name( string $base_name, array $existing_names ): string {
152 $candidates = array(
153 $base_name,
154 '_' . $base_name,
155 sprintf( '_jfb_%s_hp_', $base_name ),
156 );
157
158 foreach ( $candidates as $candidate ) {
159 if ( empty( $existing_names[ $candidate ] ) ) {
160 return $candidate;
161 }
162 }
163
164 $index = 2;
165
166 do {
167 $candidate = sprintf( '_jfb_%s_hp_%d', $base_name, $index );
168 ++$index;
169 } while ( ! empty( $existing_names[ $candidate ] ) );
170
171 return $candidate;
172 }
173
174 private function get_existing_field_names( array $blocks ): array {
175 $result = array();
176
177 $this->walk_blocks_for_field_names( $blocks, $result );
178
179 return array_values( array_unique( $result ) );
180 }
181
182 private function walk_blocks_for_field_names( array $blocks, array &$result ) {
183 foreach ( $blocks as $block ) {
184 if ( ! is_array( $block ) ) {
185 continue;
186 }
187
188 if ( isset( $block['attrs']['name'] ) && is_string( $block['attrs']['name'] ) ) {
189 $result[] = $block['attrs']['name'];
190 }
191
192 if ( isset( $block['name'] ) && is_string( $block['name'] ) ) {
193 $result[] = $block['name'];
194 }
195
196 if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
197 $this->walk_blocks_for_field_names( $block['innerBlocks'], $result );
198 }
199 }
200 }
201
202 public function on_render_field( string $content, string $field_name, array $attrs ): string {
203 $type = $attrs['action_type'] ?? '';
204
205 if ( 'submit-field' !== $field_name || 'submit' !== $type ) {
206 return $content;
207 }
208
209 $args = jet_form_builder()->post_type->get_args();
210
211 if ( empty( $args['use_honeypot'] ) ) {
212 return $content;
213 }
214
215 $fields = '';
216
217 foreach ( $this->get_honeypot_field_names( Live_Form::instance()->blocks ) as $name ) {
218 $fields .= sprintf(
219 '<input type="text" name="%s" autocomplete="one-time-code">',
220 esc_attr( $name )
221 );
222 }
223
224 $content .= sprintf(
225 '<div class="jfb-user-info" inert>%s</div>',
226 $fields
227 );
228
229 return $content;
230 }
231
232 /**
233 * @param array $request
234 *
235 * @return array
236 * @throws Spam_Exception
237 */
238 public function handle_request( array $request ): array {
239 $args = jet_form_builder()->post_type->get_args();
240
241 if ( empty( $args['use_honeypot'] ) ) {
242 return $request;
243 }
244
245 $honeypot_names = $this->get_honeypot_field_names(
246 jet_form_builder()->form_handler->request_handler->_fields
247 );
248
249 foreach ( $honeypot_names as $honeypot_name ) {
250 if ( ! empty( $request[ $honeypot_name ] ) ) {
251 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
252 throw new Spam_Exception( self::SPAM_EXCEPTION );
253 }
254
255 unset( $request[ $honeypot_name ] );
256 }
257
258 return $request;
259 }
260
261 public function handle_global_messages( array $types ): array {
262 $types[ self::SPAM_EXCEPTION ] = array(
263 'label' => __( 'Honeypot validation failed', 'jet-form-builder' ),
264 'value' => __( 'You are not allowed to fill in the honeypot field', 'jet-form-builder' ),
265 );
266
267 return $types;
268 }
269 }
270