--- a/microtango/microtango-init.php
+++ b/microtango/microtango-init.php
@@ -6,228 +6,268 @@
add_shortcode('mt_video', 'microtango_shortcode_video');
add_shortcode('mt_form', 'microtango_shortcode_form');
+/**
+ * Keep shortcode attribute handling safe.
+ *
+ * Shortcode attributes can be stored in post content. Values must be sanitized and
+ * then JSON-encoded before being embedded into inline scripts.
+ */
+function microtango_sanitize_restkey($value)
+{
+ $value = sanitize_text_field((string)$value);
+ // Microtango rest keys are expected to be simple token-like values.
+ return preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
+}
+
+function microtango_sanitize_text($value)
+{
+ return sanitize_text_field((string)$value);
+}
+
+function microtango_sanitize_url($value)
+{
+ return esc_url_raw((string)$value);
+}
+
+function microtango_sanitize_bool_string($value)
+{
+ // The JS library expects strings like "true" / "false".
+ return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
+}
+
+function microtango_json_flags()
+{
+ // Force safe embedding in HTML (prevents breaking out of <script> via quotes/tags).
+ return JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT;
+}
+
function microtango_enqueue_files()
{
- if ( is_preview() || current_user_can('manage_options') ) {
- wp_enqueue_script('microtango', 'https://api.microtango.de/scripts/mtrest-3.0.0.min.js', null);
- } else {
- wp_enqueue_script('microtango', 'https://cdn.microtango.de/scripts/mtrest-3.0.0.min.js', null);
- }
+ // WordPress.org review: avoid loading executable code from external CDNs.
+ $src = plugins_url('scripts/mtrest-3.0.0.min.js', __FILE__);
+ wp_enqueue_script('microtango', $src, array(), null, false);
}
// Use the shortcode: [mt_courses webcategory=""]
function microtango_shortcode_courses($atts, $content = "")
{
- global $microtango_settings;
- $settings = get_option($microtango_settings['settings']);
-
- if ($settings['disabled'] ?? False && !is_preview() && !current_user_can('manage_options'))
+ global $microtango_settings;
+ $settings = get_option($microtango_settings['settings']);
+
+ if (($settings['disabled'] ?? false) && !is_preview() && !current_user_can('manage_options')) {
return;
-
- $rnd = rand(10000000, 99999999);
+ }
+
+ $rnd = rand(10000000, 99999999);
- // Attributes
- $atts = shortcode_atts(
- array(
- 'restkey' => $settings['restkey'] ?? "00000000",
- 'attendurl' => "",
- 'mtattendform' => "",
- 'category' => "",
- 'webcategory' => "",
+ // Attributes
+ $atts = shortcode_atts(
+ array(
+ 'restkey' => $settings['restkey'] ?? "00000000",
+ 'attendurl' => "",
+ 'mtattendform' => "",
+ 'category' => "",
+ 'webcategory' => "",
'orderby' => "",
- 'attendtext' => $settings['attendtext'] ?? "",
- 'coursenotfoundtext' => $settings['coursenotfoundtext'] ?? "",
- 'pleasewaittext' => $settings['pleasewaittext'] ?? "",
- 'fullybookedtext' => $settings['fullybookedtext'] ?? "",
- 'nearlybookedtext' => $settings['nearlybookedtext'] ?? "",
- 'loadcss' => $settings['loadcss'] ?? False,
- 'loadtemplate' => $settings['loadtemplate'] ?? False,
- 'templateid' => 'mtuserdefined' . ($atts['template'] ?? "") . $rnd,
- ),
- $atts,
- 'mt_courses'
- );
-
- if (empty($atts['mtattendform'])) {
- $atts['mtattendform'] = 'popup';
- }
-
- if (empty($content)) {
- $content = $settings['defaultrowtemplate'] ?? "";
- }
-
- if (empty($content)) {
- $content = "|{{ScheduleInfo}}#Kurs|{{Subject}}#Start|{{StartDateText}}#Von|{{Timespan}} Uhr#Stunden|{{RepeatCount}}#|{{AttendButton}}";
- }
-
- $columns = """ . str_replace("#", "", "", $content) . """;
- $additionalrowtemplate1 = """ . str_replace("#", "", "", $settings['additionalrowtemplate1'] ?? "") . """;
- $additionalrowtemplate2 = """ . str_replace("#", "", "", $settings['additionalrowtemplate2'] ?? "") . """;
- $additionalrowtemplate3 = """ . str_replace("#", "", "", $settings['additionalrowtemplate3'] ?? "") . """;
- $additionalrowtemplate4 = """ . str_replace("#", "", "", $settings['additionalrowtemplate4'] ?? "") . """;
- $additionalrowtemplate5 = """ . str_replace("#", "", "", $settings['additionalrowtemplate5'] ?? "") . """;
- $additionalrowtemplate6 = """ . str_replace("#", "", "", $settings['additionalrowtemplate6'] ?? "") . """;
- $additionalrowtemplate7 = """ . str_replace("#", "", "", $settings['additionalrowtemplate7'] ?? "") . """;
- $additionalrowtemplate8 = """ . str_replace("#", "", "", $settings['additionalrowtemplate8'] ?? "") . """;
- $additionalrowtemplate9 = """ . str_replace("#", "", "", $settings['additionalrowtemplate9'] ?? "") . """;
-
- // Code
- $return = <<<EOT
- <script>MicrotangoCMSHelper.add(
- {
- "restKey": "{$atts['restkey']}",
- "attendURL": "{$atts['attendurl']}",
- "useMTAttendForm": "{$atts['mtattendform']}",
- "attendText": "{$atts['attendtext']}",
- "courseNotFoundText": "{$atts['coursenotfoundtext']}",
- "pleaseWaitText": "{$atts['pleasewaittext']}",
- "fullyBookedText": "{$atts['fullybookedtext']}",
- "nearlyBookedText": "{$atts['nearlybookedtext']}",
- "loadCSS": "{$atts['loadcss']}",
- "loadTemplate": "{$atts['loadtemplate']}",
- "templates": [{ "id": "mtuserdefined$rnd", "columns": [{$columns}] },
- { "id": "mtuserdefined1$rnd", "columns": [{$additionalrowtemplate1}] },
- { "id": "mtuserdefined2$rnd", "columns": [{$additionalrowtemplate2}] },
- { "id": "mtuserdefined3$rnd", "columns": [{$additionalrowtemplate3}] },
- { "id": "mtuserdefined4$rnd", "columns": [{$additionalrowtemplate4}] },
- { "id": "mtuserdefined5$rnd", "columns": [{$additionalrowtemplate5}] },
- { "id": "mtuserdefined6$rnd", "columns": [{$additionalrowtemplate6}] },
- { "id": "mtuserdefined7$rnd", "columns": [{$additionalrowtemplate7}] },
- { "id": "mtuserdefined8$rnd", "columns": [{$additionalrowtemplate8}] },
- { "id": "mtuserdefined9$rnd", "columns": [{$additionalrowtemplate9}] }],
- "update": [{ "action": "course", "category": "{$atts['category']}", "webCategory": "{$atts['webcategory']}", "orderBy": "{$atts['orderby']}", "templateId": "{$atts['templateid']}" }],
- });
- </script>
-EOT;
+ 'attendtext' => $settings['attendtext'] ?? "",
+ 'coursenotfoundtext' => $settings['coursenotfoundtext'] ?? "",
+ 'pleasewaittext' => $settings['pleasewaittext'] ?? "",
+ 'fullybookedtext' => $settings['fullybookedtext'] ?? "",
+ 'nearlybookedtext' => $settings['nearlybookedtext'] ?? "",
+ 'loadcss' => $settings['loadcss'] ?? false,
+ 'loadtemplate' => $settings['loadtemplate'] ?? false,
+ 'templateid' => 'mtuserdefined' . ($atts['template'] ?? "") . $rnd,
+ ),
+ $atts,
+ 'mt_courses'
+ );
- return htmlspecialchars_decode($return);
+ if (empty($atts['mtattendform'])) {
+ $atts['mtattendform'] = 'popup';
+ }
+
+ if (empty($content)) {
+ $content = $settings['defaultrowtemplate'] ?? "";
+ }
+
+ if (empty($content)) {
+ $content = "|{{ScheduleInfo}}#Kurs|{{Subject}}#Start|{{StartDateText}}#Von|{{Timespan}} Uhr#Stunden|{{RepeatCount}}#|{{AttendButton}}";
+ }
+
+ $columns = array_map('microtango_sanitize_text', explode('#', (string)$content));
+ $additionalrowtemplate1 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate1'] ?? "")));
+ $additionalrowtemplate2 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate2'] ?? "")));
+ $additionalrowtemplate3 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate3'] ?? "")));
+ $additionalrowtemplate4 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate4'] ?? "")));
+ $additionalrowtemplate5 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate5'] ?? "")));
+ $additionalrowtemplate6 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate6'] ?? "")));
+ $additionalrowtemplate7 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate7'] ?? "")));
+ $additionalrowtemplate8 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate8'] ?? "")));
+ $additionalrowtemplate9 = array_map('microtango_sanitize_text', explode('#', (string)($settings['additionalrowtemplate9'] ?? "")));
+
+ $config = array(
+ 'restKey' => microtango_sanitize_restkey($atts['restkey']),
+ 'attendURL' => microtango_sanitize_url($atts['attendurl']),
+ 'useMTAttendForm' => microtango_sanitize_text($atts['mtattendform']),
+ 'attendText' => microtango_sanitize_text($atts['attendtext']),
+ 'courseNotFoundText' => microtango_sanitize_text($atts['coursenotfoundtext']),
+ 'pleaseWaitText' => microtango_sanitize_text($atts['pleasewaittext']),
+ 'fullyBookedText' => microtango_sanitize_text($atts['fullybookedtext']),
+ 'nearlyBookedText' => microtango_sanitize_text($atts['nearlybookedtext']),
+ 'loadCSS' => microtango_sanitize_bool_string($atts['loadcss']),
+ 'loadTemplate' => microtango_sanitize_bool_string($atts['loadtemplate']),
+ 'templates' => array(
+ array('id' => "mtuserdefined{$rnd}", 'columns' => $columns),
+ array('id' => "mtuserdefined1{$rnd}", 'columns' => $additionalrowtemplate1),
+ array('id' => "mtuserdefined2{$rnd}", 'columns' => $additionalrowtemplate2),
+ array('id' => "mtuserdefined3{$rnd}", 'columns' => $additionalrowtemplate3),
+ array('id' => "mtuserdefined4{$rnd}", 'columns' => $additionalrowtemplate4),
+ array('id' => "mtuserdefined5{$rnd}", 'columns' => $additionalrowtemplate5),
+ array('id' => "mtuserdefined6{$rnd}", 'columns' => $additionalrowtemplate6),
+ array('id' => "mtuserdefined7{$rnd}", 'columns' => $additionalrowtemplate7),
+ array('id' => "mtuserdefined8{$rnd}", 'columns' => $additionalrowtemplate8),
+ array('id' => "mtuserdefined9{$rnd}", 'columns' => $additionalrowtemplate9),
+ ),
+ 'update' => array(
+ array(
+ 'action' => 'course',
+ 'category' => microtango_sanitize_text($atts['category']),
+ 'webCategory' => microtango_sanitize_text($atts['webcategory']),
+ 'orderBy' => microtango_sanitize_text($atts['orderby']),
+ 'templateId' => microtango_sanitize_text($atts['templateid']),
+ ),
+ ),
+ );
+
+ $json = wp_json_encode($config, microtango_json_flags());
+
+ return '<script>MicrotangoCMSHelper.add(' . $json . ');</script>';
}
function microtango_shortcode_reservation($atts, $content = "")
{
- global $microtango_settings;
- $settings = get_option($microtango_settings['settings']);
-
- if ($settings['disabled'] ?? False && !is_preview() && !current_user_can('manage_options'))
+ global $microtango_settings;
+ $settings = get_option($microtango_settings['settings']);
+
+ if (($settings['disabled'] ?? false) && !is_preview() && !current_user_can('manage_options')) {
return;
+ }
+
+ // Attributes
+ $atts = shortcode_atts(
+ array(
+ 'restkey' => $settings['restkey'] ?? "00000000",
+ 'reservationtext' => $settings['reservationtext'] ?? "",
+ 'loadcss' => $settings['loadcss'] ?? false,
+ ),
+ $atts,
+ 'mt_reservation'
+ );
+
+ $config = array(
+ 'restKey' => microtango_sanitize_restkey($atts['restkey']),
+ 'reservationText' => microtango_sanitize_text($atts['reservationtext']),
+ 'loadCSS' => microtango_sanitize_bool_string($atts['loadcss']),
+ 'update' => array(),
+ );
- // Attributes
- $atts = shortcode_atts(
- array(
- 'restkey' => $settings['restkey'] ?? "00000000",
- 'reservationtext' => $settings['reservationtext'] ?? "",
- 'loadcss' => $settings['loadcss'] ?? False,
- ),
- $atts,
- 'mt_reservation'
- );
-
- // Code
- $return = <<<EOT
- <script>MicrotangoCMSHelper.addReservation(
- {
- "restKey": "{$atts['restkey']}",
- "reservationText": "{$atts['reservationtext']}",
- "loadCSS": "{$atts['loadcss']}",
- "update": [],
- });
- </script>
-EOT;
+ $json = wp_json_encode($config, microtango_json_flags());
- return htmlspecialchars_decode($return);
+ return '<script>MicrotangoCMSHelper.addReservation(' . $json . ');</script>';
}
function microtango_shortcode_video($atts, $content = "")
{
- global $microtango_settings;
- $settings = get_option($microtango_settings['settings']);
-
- if ($settings['disabled'] ?? False && !is_preview() && !current_user_can('manage_options'))
+ global $microtango_settings;
+ $settings = get_option($microtango_settings['settings']);
+
+ if (($settings['disabled'] ?? false) && !is_preview() && !current_user_can('manage_options')) {
return;
-
- $rnd = rand(10000000, 99999999);
+ }
+
+ $rnd = rand(10000000, 99999999);
- // Attributes
- $atts = shortcode_atts(
- array(
- 'videogroup' => "",
- 'restkey' => $settings['restkey'] ?? "00000000",
- 'showvideotext' => $settings['showvideotext'] ?? "",
- 'videonotfoundtext' => $settings['videonotfoundtext'] ?? "",
- 'logintext' => $settings['logintext'] ?? "",
- 'loadcss' => $settings['loadcss'] ?? False,
- 'loadtemplate' => $settings['loadtemplate'] ?? False,
- 'templateid' => 'mtuserdefined' . $rnd,
- ),
- $atts,
- 'mt_video'
- );
-
- if (empty($content)) {
- $content = $settings['defaultvideorowtemplate'] ?? "";
- }
-
- if (empty($content)) {
- $content = "|{{VideoThumbnail}}#Name|{{Name}}#Beschreibung|{{Description}}#Länge|{{Length}}#|{{ShowVideo}}";
- }
-
- $columns = """ . str_replace("#", "", "", $content) . """;
-
- // Code
- $return = <<<EOT
- <script>MicrotangoCMSHelper.addVideo(
- {
- "restKey": "{$atts['restkey']}",
- "showVideoText": "{$atts['showvideotext']}",
- "videoNotFoundText": "{$atts['videonotfoundtext']}",
- "loginText": "{$atts['logintext']}",
- "loadCSS": "{$atts['loadcss']}",
- "templates": { "id": "mtuserdefined$rnd", "columns": [{$columns}] },
- "update": [{"videoGroup": "{$atts['videogroup']}", "templateId": "{$atts['templateid']}"}],
- });
- </script>
-EOT;
+ // Attributes
+ $atts = shortcode_atts(
+ array(
+ 'videogroup' => "",
+ 'restkey' => $settings['restkey'] ?? "00000000",
+ 'showvideotext' => $settings['showvideotext'] ?? "",
+ 'videonotfoundtext' => $settings['videonotfoundtext'] ?? "",
+ 'logintext' => $settings['logintext'] ?? "",
+ 'loadcss' => $settings['loadcss'] ?? false,
+ 'loadtemplate' => $settings['loadtemplate'] ?? false,
+ 'templateid' => 'mtuserdefined' . $rnd,
+ ),
+ $atts,
+ 'mt_video'
+ );
- return htmlspecialchars_decode($return);
+ if (empty($content)) {
+ $content = $settings['defaultvideorowtemplate'] ?? "";
+ }
+
+ if (empty($content)) {
+ $content = "|{{VideoThumbnail}}#Name|{{Name}}#Beschreibung|{{Description}}#Länge|{{Length}}#|{{ShowVideo}}";
+ }
+
+ $columns = array_map('microtango_sanitize_text', explode('#', (string)$content));
+
+ $config = array(
+ 'restKey' => microtango_sanitize_restkey($atts['restkey']),
+ 'showVideoText' => microtango_sanitize_text($atts['showvideotext']),
+ 'videoNotFoundText' => microtango_sanitize_text($atts['videonotfoundtext']),
+ 'loginText' => microtango_sanitize_text($atts['logintext']),
+ 'loadCSS' => microtango_sanitize_bool_string($atts['loadcss']),
+ 'templates' => array('id' => "mtuserdefined{$rnd}", 'columns' => $columns),
+ 'update' => array(
+ array(
+ 'videoGroup' => microtango_sanitize_text($atts['videogroup']),
+ 'templateId' => microtango_sanitize_text($atts['templateid']),
+ ),
+ ),
+ );
+
+ $json = wp_json_encode($config, microtango_json_flags());
+
+ return '<script>MicrotangoCMSHelper.addVideo(' . $json . ');</script>';
}
function microtango_shortcode_form($atts, $content = "")
{
- global $microtango_settings;
- $settings = get_option($microtango_settings['settings']);
-
- if ($settings['disabled'] ?? False && !is_preview() && !current_user_can('manage_options'))
+ global $microtango_settings;
+ $settings = get_option($microtango_settings['settings']);
+
+ if (($settings['disabled'] ?? false) && !is_preview() && !current_user_can('manage_options')) {
return;
+ }
+
+ // Attributes
+ $atts = shortcode_atts(
+ array(
+ 'restkey' => $settings['restkey'] ?? "00000000",
+ 'formid' => "",
+ 'redirecturl' => "",
+ 'renameinput' => "",
+ 'testmode' => "",
+ ),
+ $atts,
+ 'mt_form'
+ );
+
+ $inputs = array_map('microtango_sanitize_text', explode('#', (string)$content));
+
+ $config = array(
+ 'restKey' => microtango_sanitize_restkey($atts['restkey']),
+ 'formUpdate' => 'true',
+ 'formId' => microtango_sanitize_text($atts['formid']),
+ 'formRedirectURL' => microtango_sanitize_url($atts['redirecturl']),
+ 'formRenameInput' => $inputs,
+ 'formTestMode' => microtango_sanitize_text($atts['testmode']),
+ 'update' => array(),
+ );
- // Attributes
- $atts = shortcode_atts(
- array(
- 'restkey' => $settings['restkey'] ?? "00000000",
- 'formid' => "",
- 'redirecturl' => "",
- 'renameinput' => "",
- 'testmode' => "",
- ),
- $atts,
- 'mt_form'
- );
-
- $inputs = """ . str_replace("#", "", "", $content) . """;
-
- // Code
- $return = <<<EOT
- <script>MicrotangoCMSHelper.addForm(
- {
- "restKey": "{$atts['restkey']}",
- "formUpdate": "true",
- "formId": "{$atts['formid']}",
- "formRedirectURL": "{$atts['redirecturl']}",
- "formRenameInput": [{$inputs}],
- "formTestMode": "{$atts['testmode']}",
- "update": [],
- });
- </script>
-EOT;
+ $json = wp_json_encode($config, microtango_json_flags());
- return htmlspecialchars_decode($return);
+ return '<script>MicrotangoCMSHelper.addForm(' . $json . ');</script>';
}
--- a/microtango/microtango.php
+++ b/microtango/microtango.php
@@ -3,14 +3,14 @@
Plugin Name: Microtango
Plugin URI: https://microtango.de/
Description: Microtango WP integration. Requires subscription from DMS. Will include the Microtango REST API to show your cloud data.
-Version: 0.9.29
+Version: 0.9.31
Author: microtango
Author URI: https://profiles.wordpress.org/microtango/
License: MIT License
License URI: http://opensource.org/licenses/MIT
Text Domain: microtango
-Copyright 2019-2025 DMS Computer Contor
+Copyright 2019-2026 DMS Computer Contor
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
@@ -29,7 +29,7 @@
"settings_page" => "Microtango",
"settings_page_title" => "Microtango Einstellungen",
"settings" => "microtango_settings",
- "version" => "0.9.29"
+ "version" => "0.9.31"
);
require_once "microtango-settings-init.php";