| @@ -55,9 +55,12 @@ | ||
| 55 | 55 | $id = $data['attributes']['id']; |
| 56 | 56 | |
| 57 | 57 | $ariaLabel = esc_html__(' Use arrow keys to navigate dates. Press enter to select a date.', 'fluentform'); |
| 58 | 58 | $label = ArrayHelper::get($data, 'settings.label'); |
| 59 | - $elMarkup = "<input aria-label='" . $label . $ariaLabel . "' aria-haspopup='dialog' data-type-datepicker data-format='" . esc_attr($dateFormat) . "' " . $atts . " aria-invalid='false' aria-required={$ariaRequired}>"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $atts is escaped before being passed in. | |
| 59 | + // SECURITY (FINDING-12): esc_attr the settings.label before interpolating it into the | |
| 60 | + // single-quoted aria-label; the save-time wp_kses does not encode quotes, so an unescaped | |
| 61 | + // label allows an attribute breakout (stored XSS). | |
| 62 | + $elMarkup = "<input aria-label='" . esc_attr($label) . $ariaLabel . "' aria-haspopup='dialog' data-type-datepicker data-format='" . esc_attr($dateFormat) . "' " . $atts . " aria-invalid='false' aria-required={$ariaRequired}>"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $atts is escaped before being passed in. | |
| 60 | 63 | $config = $this->getDateFormatConfigJSON($data['settings'], $form); |
| 61 | 64 | $customConfig = $this->getCustomConfig($data['settings'], $form); |
| 62 | 65 | $this->loadToFooter($config, $customConfig, $form, $id); |
| 63 | 66 | $html = $this->buildElementMarkup($elMarkup, $data, $form); |
| @@ -137,18 +140,20 @@ | ||
| 137 | 140 | } |
| 138 | 141 | |
| 139 | 142 | public function getCustomConfig($settings, $form = null) |
| 140 | 143 | { |
| 141 | - $customConfigObject = fluentform_sanitize_json_object( | |
| 142 | - (string) ArrayHelper::get($settings, 'date_config') | |
| 143 | - ); | |
| 144 | + $customConfigObject = trim((string) ArrayHelper::get($settings, 'date_config')); | |
| 144 | 145 | |
| 145 | - $customConfigObject = '' !== $customConfigObject ? $customConfigObject : '{}'; | |
| 146 | + if ( | |
| 147 | + !$customConfigObject || | |
| 148 | + '{' !== substr($customConfigObject, 0, 1) || | |
| 149 | + '}' !== substr($customConfigObject, -1) | |
| 150 | + ) { | |
| 151 | + $customConfigObject = '{}'; | |
| 152 | + } else { | |
| 153 | + $customConfigObject = str_ireplace('</script', '<\\/script', $customConfigObject); | |
| 154 | + } | |
| 146 | 155 | |
| 147 | - // The stored field value is always sanitised to a data-only JSON object | |
| 148 | - // (functions/expressions are stripped — they are the XSS vector for | |
| 149 | - // lower-privilege editors). Developers who need flatpickr callbacks | |
| 150 | - // supply them here from trusted server-side code, never via the setting. | |
| 151 | 156 | return apply_filters('fluentform/date_time_custom_config', $customConfigObject, $settings, $form); |
| 152 | 157 | } |
| 153 | 158 | |
| 154 | 159 | private function loadToFooter($config, $customConfigObject, $form, $id) |