diff --git a/source/changelog.txt b/source/changelog.txt index 4e52f4a..6125179 100644 --- a/source/changelog.txt +++ b/source/changelog.txt @@ -9,6 +9,7 @@ #### Removed #### Fixed #### Security +- Escape translated strings to prevent HTML injections ### [1.4.0] #### Changed diff --git a/source/front/blocks/events-list/edit.js b/source/front/blocks/events-list/edit.js index e1c28e9..d46b5ae 100644 --- a/source/front/blocks/events-list/edit.js +++ b/source/front/blocks/events-list/edit.js @@ -7,6 +7,7 @@ import { hideErrorMessages, showLoadingIndicator, } from '../../events-displayer.js' +import Formatter from '../../formatter.js' const { InspectorControls, useBlockProps } = wp.blockEditor const { Panel, PanelBody } = wp.components @@ -73,12 +74,18 @@ export default ({ attributes, setAttributes }) => { return [ - ')}> + '), + )} + > { className="components-base-control__label" htmlFor={NAME + '_group-name'} > - {__('Group name (optional)', '')} + {Formatter.escapeHTML( + __('Group name (optional)', ''), + )} { ,
- {__('The events could not be loaded!', '')} + {Formatter.escapeHTML( + __('The events could not be loaded!', ''), + )}
- {__('The group could not be found!', '')} + {Formatter.escapeHTML( + __('The group could not be found!', ''), + )}
- {__('Loading...', '')} + {Formatter.escapeHTML(__('Loading...', ''))}
{ target="_blank" style={{ display: 'inline-block', 'margin-top': '20px;' }} > - {__('Show more events', '')} + {Formatter.escapeHTML(__('Show more events', ''))}
, ] diff --git a/source/front/formatter-test.js b/source/front/formatter-test.js index d34dd60..b810343 100644 --- a/source/front/formatter-test.js +++ b/source/front/formatter-test.js @@ -1,6 +1,18 @@ import test from 'ava' +import { JSDOM } from 'jsdom' + import Formatter from './formatter.js' +test.beforeEach(() => { + const dom = new JSDOM() + global.document = dom.window.document +}) + +test('#escapeHTML', (t) => { + const escaped = Formatter.escapeHTML('a') + t.is(escaped, '<b>a</b>') +}) + test('#formatDate one date', (t) => { const date = Formatter.formatDate({ start: '2021-04-15T10:30:00Z', diff --git a/source/front/formatter.js b/source/front/formatter.js index 614db12..5deda7c 100644 --- a/source/front/formatter.js +++ b/source/front/formatter.js @@ -1,6 +1,12 @@ import DateTimeWrapper from './date-time-wrapper.js' export default class Formatter { + static escapeHTML(input) { + const div = document.createElement('div') + div.appendChild(document.createTextNode(input)) + return div.innerHTML + } + static formatDate({ locale, timeZone, start, end, isShortOffsetNameShown }) { const startDateTime = new DateTimeWrapper({ locale, diff --git a/source/includes/EventsListBlock.php b/source/includes/EventsListBlock.php index 3bdd25b..b376fa4 100644 --- a/source/includes/EventsListBlock.php +++ b/source/includes/EventsListBlock.php @@ -13,8 +13,8 @@ class EventsListBlock { ], '', array('in_footer' => true)); register_block_type(NAME . '/events-list', [ 'api_version' => 2, - 'title' => __('Events List', 'connector-mobilizon'), - 'description' => __('A list of the upcoming events of the connected Mobilizon instance.', 'connector-mobilizon'), + 'title' => esc_html__('Events List', 'connector-mobilizon'), + 'description' => esc_html__('A list of the upcoming events of the connected Mobilizon instance.', 'connector-mobilizon'), 'category' => 'widgets', 'icon' => 'list-view', 'supports' => [ diff --git a/source/includes/EventsListWidget.php b/source/includes/EventsListWidget.php index 0726a7b..bfe0a62 100644 --- a/source/includes/EventsListWidget.php +++ b/source/includes/EventsListWidget.php @@ -6,9 +6,9 @@ class EventsListWidget extends \WP_Widget { public function __construct() { parent::__construct( NAME . '-events-list', - NICE_NAME . ' ' . __('Events List', 'connector-mobilizon'), + NICE_NAME . ' ' . esc_html__('Events List', 'connector-mobilizon'), array( - 'description' => __('A list of the upcoming events of the connected Mobilizon instance.', 'connector-mobilizon'), + 'description' => esc_html__('A list of the upcoming events of the connected Mobilizon instance.', 'connector-mobilizon'), ), ); } @@ -49,7 +49,7 @@ class EventsListWidget extends \WP_Widget { } public function form($options) { - $title = !empty($options['title']) ? $options['title'] : __('Events', 'connector-mobilizon'); + $title = !empty($options['title']) ? $options['title'] : esc_html__('Events', 'connector-mobilizon'); $eventsCount = !empty($options['eventsCount']) ? $options['eventsCount'] : DEFAULT_EVENTS_COUNT; $groupName = !empty($options['groupName']) ? $options['groupName'] : ''; diff --git a/source/includes/Settings.php b/source/includes/Settings.php index 9b5feac..de33123 100644 --- a/source/includes/Settings.php +++ b/source/includes/Settings.php @@ -31,14 +31,14 @@ class Settings { add_settings_section( self::$SETTINGS_SECTION_NAME, - __('General Settings', 'connector-mobilizon'), + esc_html__('General Settings', 'connector-mobilizon'), '', self::$PAGE_NAME ); add_settings_field( self::$SETTING_FIELD_NAME_URL, - __('URL', 'connector-mobilizon'), + esc_html__('URL', 'connector-mobilizon'), 'MobilizonConnector\Settings::output_field_url', self::$PAGE_NAME, self::$SETTINGS_SECTION_NAME, @@ -48,7 +48,7 @@ class Settings { ); add_settings_field( self::$SETTING_FIELD_NAME_IS_SHORT_OFFSET_NAME_SHOWN, - __('Display named offset', 'connector-mobilizon'), + esc_html__('Display named offset', 'connector-mobilizon'), 'MobilizonConnector\Settings::output_field_is_short_offset_name_shown', self::$PAGE_NAME, self::$SETTINGS_SECTION_NAME, @@ -74,7 +74,7 @@ class Settings { add_settings_error( self::$OPTION_NAME_URL, 'wordpress_mobilizon_field_url_error', - __('The URL is invalid.', 'connector-mobilizon'), + esc_html__('The URL is invalid.', 'connector-mobilizon'), 'error' ); } @@ -86,7 +86,7 @@ class Settings { public static function register_settings_page() { add_options_page( - NICE_NAME . ' ' . __('Settings', 'connector-mobilizon'), + NICE_NAME . ' ' . esc_html__('Settings', 'connector-mobilizon'), NICE_NAME, 'manage_options', NAME . '-settings',