PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.14
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.14
3.1.14 3.1.13 3.1.12 3.1.11 3.1.10 3.1.9 3.1.8 3.1.7 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 All 123 releases
← All changes | Inc/Core/Controllers/BoxImport.php +78 -11 3.1.8 → 3.1.14 View file →
@@ -1,8 +1,8 @@
1 1 <?php
2 2 /**
3 3 * @package FireBox
4 - * @version 3.1.8 Free
4 + * @version 3.1.14 Free
5 5 *
6 6 * @author FirePlugins <[email protected]>
7 7 * @link https://www.fireplugins.com
8 8 * @copyright Copyright © 2026 FirePlugins All Rights Reserved
@@ -55,8 +55,20 @@
55 55 {
56 56 return; // get out if we didn't click the Activate button
57 57 }
58 58
59 + /**
60 + * Importing creates campaigns from a file the user supplies, so it needs an
61 + * explicit capability check of its own. Today options.php also enforces
62 + * manage_options for this option group, but this method must not depend on
63 + * how it happens to be reached.
64 + */
65 + if (!current_user_can('edit_fireboxes'))
66 + {
67 + \FPFramework\Libs\AdminNotice::displayError(fpframework()->_('FPF_CANNOT_VERIFY_REQUEST'));
68 + return;
69 + }
70 +
59 71 if (!isset($_FILES['file']))
60 72 {
61 73 return;
62 74 }
@@ -91,9 +103,12 @@
91 103 \FPFramework\Libs\AdminNotice::displayError(fpframework()->_('FPF_FILE_EMPTY'));
92 104 return;
93 105 }
94 106
95 - if (!$items = json_decode($data, true))
107 + $items = json_decode($data, true);
108 +
109 + // Scalar JSON (e.g. "123") is valid JSON but not an export payload
110 + if (!$items || !is_array($items))
96 111 {
97 112 \FPFramework\Libs\AdminNotice::displayError(firebox()->_('FB_CAMPAIGN_IMPORT_CONTENTS_ERROR'));
98 113 return;
99 114 }
@@ -132,14 +147,16 @@
132 147 $success = true;
133 148
134 149 foreach ($items as $item)
135 150 {
136 - if (!isset($item['meta']))
151 + // The .fbox contents are user-supplied; validate the shape before
152 + // using it or a hand-edited file fatals instead of erroring.
153 + if (!is_array($item) || !isset($item['meta']))
137 154 {
138 155 $success = false;
139 156 break;
140 157 }
141 -
158 +
142 159 // get meta
143 160 $meta = $item['meta'];
144 161
145 162 // remote meta from item
@@ -144,21 +161,58 @@
144 161
145 162 // remote meta from item
146 163 unset($item['meta']);
147 164
148 - if (!isset($item['box']))
165 + // Campaign settings are free-form, but they are always a map — never a scalar.
166 + if (!is_array($meta) && !is_object($meta))
149 167 {
150 168 $success = false;
151 169 break;
152 170 }
153 171
172 + if (!isset($item['box']) || !is_array($item['box']))
173 + {
174 + $success = false;
175 + break;
176 + }
177 +
154 178 $box = $item['box'];
155 179
156 - // remove ID
157 - $box['ID'] = '';
180 + /**
181 + * The .fbox file carries a full wp_posts row, and every key in it used to be
182 + * written straight to the database. Keep only the columns that actually
183 + * describe a campaign; everything else (post_type, post_author, guid,
184 + * post_parent, ...) is either forced below or dropped.
185 + */
186 + $box = array_intersect_key($box, array_flip([
187 + 'post_title',
188 + 'post_content',
189 + 'post_excerpt',
190 + 'post_name',
191 + 'menu_order'
192 + ]));
158 193
194 + if (!isset($box['post_content']) || !is_string($box['post_content']))
195 + {
196 + $box['post_content'] = '';
197 + }
198 +
199 + $box['post_title'] = isset($box['post_title']) && is_string($box['post_title'])
200 + ? sanitize_text_field($box['post_title'])
201 + : '';
202 +
203 + $box['post_excerpt'] = isset($box['post_excerpt']) && is_string($box['post_excerpt'])
204 + ? sanitize_textarea_field($box['post_excerpt'])
205 + : '';
206 +
207 + $box['post_name'] = isset($box['post_name']) && is_string($box['post_name'])
208 + ? sanitize_title($box['post_name'])
209 + : '';
210 +
211 + $box['menu_order'] = isset($box['menu_order']) ? (int) $box['menu_order'] : 0;
212 +
159 213 $factory = new \FPFramework\Base\Factory();
160 -
214 +
161 215 $tz = wp_timezone();
162 216 $date_without_tz = $factory->getDate();
163 217 $date_with_tz = $factory->getDate()->setTimezone($tz);
164 218
@@ -164,8 +218,12 @@
164 218
165 219 $box['post_date'] = $date_with_tz->format('Y-m-d H:i:s');
166 220 $box['post_date_gmt'] = $date_without_tz->format('Y-m-d H:i:s');
167 221
222 + // These are ours to decide, never the file's.
223 + $box['post_type'] = 'firebox';
224 + $box['post_author'] = get_current_user_id();
225 +
168 226 \FireBox\Core\Helpers\Form\Form::ensureUniqueFormIDs($box['post_content']);
169 227
170 228 // set publish status
171 229 if (in_array($publish_all, [0, 1]))
@@ -171,11 +229,20 @@
171 229 if (in_array($publish_all, [0, 1]))
172 230 {
173 231 $box['post_status'] = ($publish_all == 0) ? 'draft' : 'publish';
174 232 }
175 -
176 - // insert new box
177 - if (!$new_box_id = firebox()->tables->box->insert((object) $box))
233 + else
234 + {
235 + $box['post_status'] = 'draft';
236 + }
237 +
238 + /**
239 + * Insert through wp_insert_post() rather than the raw table layer so that
240 + * kses, slug uniqueness and the usual save_post hooks all apply.
241 + */
242 + $new_box_id = wp_insert_post(wp_slash($box), true);
243 +
244 + if (is_wp_error($new_box_id) || !$new_box_id)
178 245 {
179 246 $success = false;
180 247 break;
181 248 }