Headline
CVE-2022-0218: Changeset 2656984 for wp-html-mail/trunk/includes/class-template-designer.php – WordPress Plugin Repository
The WP HTML Mail WordPress plugin is vulnerable to unauthorized access which allows unauthenticated attackers to retrieve and modify theme settings due to a missing capability check on the /themesettings REST-API endpoint found in the ~/includes/class-template-designer.php file, in versions up to and including 3.0.9. This makes it possible for attackers with no privileges to execute the endpoint and add malicious JavaScript to a vulnerable WordPress site.
wp-html-mail/trunk/includes/class-template-designer.php
r2466288
r2656984
1
<?php if ( ! defined( 'ABSPATH' ) ) exit;
1
<?php if (!defined('ABSPATH')) exit;
2
2
3
class Haet\_TemplateDesigner {
3
class Haet\_TemplateDesigner
4
{
4
5
private $api\_base = 'whm/v3';
5
6
6
function \_\_construct(){
7
add\_action( 'admin\_enqueue\_scripts', \[ $this, 'admin\_page\_scripts\_and\_styles' \] );
8
add\_action( 'rest\_api\_init', \[ $this, 'rest\_api\_init' \] );
7
function \_\_construct()
8
{
9
add\_action('admin\_enqueue\_scripts', \[$this, 'admin\_page\_scripts\_and\_styles'\]);
10
add\_action('rest\_api\_init', \[$this, 'rest\_api\_init'\]);
9
11
10
add\_action( 'admin\_notices', \[ $this, 'showTemplateDesignerUpdateNotice' \] );
12
add\_action('admin\_notices', \[$this, 'showTemplateDesignerUpdateNotice'\]);
11
13
}
12
13
14
15
public function admin\_page\_scripts\_and\_styles($page){
16
if(strpos($page, 'wp-html-mail') && ( !array\_key\_exists( 'tab', $\_GET ) || $\_GET\['tab'\] =="template" ) ){
17
14
15
16
17
public function admin\_page\_scripts\_and\_styles($page)
18
{
19
if (strpos($page, 'wp-html-mail') && (!array\_key\_exists('tab', $\_GET) || $\_GET\['tab'\] == "template")) {
20
18
21
// style our options panel like the block editor
19
wp\_enqueue\_style( 'wp-editor' );
20
wp\_enqueue\_style( 'wp-components' );
21
wp\_enqueue\_style( 'forms' );
22
wp\_enqueue\_style('wp-editor');
23
wp\_enqueue\_style('wp-components');
24
wp\_enqueue\_style('forms');
22
25
23
26
// https://developer.wordpress.org/block-editor/packages/packages-dependency-extraction-webpack-plugin/
24
$script\_path = HAET\_MAIL\_PATH . 'js/template-designer/' . ( $this->isScriptDebug() ? 'dev' : 'dist' ) . '/main.js';
25
$script\_asset\_path = HAET\_MAIL\_PATH . 'js/template-designer/' . ( $this->isScriptDebug() ? 'dev' : 'dist' ) . '/main.asset.php';
26
$script\_asset = file\_exists( $script\_asset\_path )
27
? require( $script\_asset\_path )
28
: array( 'dependencies' => array(), 'version' => filemtime( $script\_path ) );
29
$script\_url = HAET\_MAIL\_URL . 'js/template-designer/' . ( $this->isScriptDebug() ? 'dev' : 'dist' ) . '/main.js';
27
$script\_path = HAET\_MAIL\_PATH . 'js/template-designer/' . ($this->isScriptDebug() ? 'dev' : 'dist') . '/main.js';
28
$script\_asset\_path = HAET\_MAIL\_PATH . 'js/template-designer/' . ($this->isScriptDebug() ? 'dev' : 'dist') . '/main.asset.php';
29
$script\_asset = file\_exists($script\_asset\_path)
30
? require($script\_asset\_path)
31
: array('dependencies' => array(), 'version' => filemtime($script\_path));
32
$script\_url = HAET\_MAIL\_URL . 'js/template-designer/' . ($this->isScriptDebug() ? 'dev' : 'dist') . '/main.js';
30
33
31
34
wp\_enqueue\_script('wp-html-mail-template-designer', $script\_url, $script\_asset\['dependencies'\], $script\_asset\['version'\]);
32
35
wp\_localize\_script('wp-html-mail-template-designer', 'mailTemplateDesigner', \[
33
36
'restUrl' => $this->getRestUrl(),
34
'nonce' => wp\_create\_nonce( 'wp\_rest' ),
37
'nonce' => wp\_create\_nonce('wp\_rest'),
35
38
'fonts' => $this->getAvailableFonts(),
36
39
'templateLibraryUrl' => Haet\_Mail()->get\_tab\_url('template-library'),
37
40
'isMultiLanguageSite' => Haet\_Mail()->multilanguage->is\_multilanguage\_site(),
38
'currentLanguage' => Haet\_Mail()->multilanguage->get\_current\_language()
41
'currentLanguage' => Haet\_Mail()->multilanguage->get\_current\_language(),
42
'nonce' => wp\_create\_nonce('wp\_rest')
39
43
\]);
40
44
wp\_enqueue\_media();
41
45
wp\_enqueue\_editor();
42
}
46
}
43
47
}
44
48
45
49
46
50
47
public function rest\_api\_init() {
48
register\_rest\_route( $this->api\_base, '/themesettings', array(
49
'methods' => 'GET',
50
'callback' => \[ $this, 'getThemeSettings' \],
51
'permission\_callback' => '\_\_return\_true'
51
public function rest\_api\_init()
52
{
53
register\_rest\_route($this->api\_base, '/themesettings', array(
54
'methods' => 'GET',
55
'callback' => \[$this, 'getThemeSettings'\],
56
'permission\_callback' => function () {
57
return current\_user\_can('manage\_options');
58
}
52
59
));
53
54
register\_rest\_route( $this->api\_base, '/themesettings', array(
55
'methods' => 'POST',
56
'callback' => \[ $this, 'saveThemeSettings' \],
57
'permission\_callback' => '\_\_return\_true'
60
61
register\_rest\_route($this->api\_base, '/themesettings', array(
62
'methods' => 'POST',
63
'callback' => \[$this, 'saveThemeSettings'\],
64
'permission\_callback' => function () {
65
return current\_user\_can('manage\_options');
66
}
58
67
));
59
68
}
60
69
61
70
62
public function getThemeSettings(){
71
public function getThemeSettings()
72
{
63
73
$theme\_options = Haet\_Mail()->get\_theme\_options('default');
64
74
65
return new \\WP\_REST\_Response( $theme\_options );
75
return new \\WP\_REST\_Response($theme\_options);
66
76
}
67
68
public function saveThemeSettings( $request ){
69
if( $request->get\_params() ){
77
78
public function saveThemeSettings($request)
79
{
80
if ($request->get\_params()) {
70
81
$theme\_options = $request->get\_params();
71
82
update\_option('haet\_mail\_theme\_options', $theme\_options);
72
83
}
73
84
74
85
$options = Haet\_Mail()->get\_options();
75
86
$plugin\_options = Haet\_Sender\_Plugin::get\_plugin\_options();
76
87
77
$preview = Haet\_Mail()->get\_preview( Haet\_Sender\_Plugin::get\_active\_plugins(), 'template', $options, $plugin\_options, $theme\_options );
78
return new \\WP\_REST\_Response( \['preview' => $preview\] );
88
$preview = Haet\_Mail()->get\_preview(Haet\_Sender\_Plugin::get\_active\_plugins(), 'template', $options, $plugin\_options, $theme\_options);
89
return new \\WP\_REST\_Response(\['preview' => $preview\]);
79
90
}
80
91
81
private function getAvailableFonts(){
92
private function getAvailableFonts()
93
{
82
94
$fonts = Haet\_Mail()->get\_fonts();
83
95
$fonts\_select\_options = \[\];
84
foreach( $fonts as $value => $label ){
96
foreach ($fonts as $value => $label) {
85
97
$fonts\_select\_options\[\] = \[
86
98
'value' => $value,
…
…
88
100
\];
89
101
}
90
return $fonts\_select\_options;
102
return $fonts\_select\_options;
91
103
}
92
104
93
105
94
106
95
private function getRestUrl( $endpoint = '' ) {
96
return get\_rest\_url( null, $this->api\_base . '/' . $endpoint );
107
private function getRestUrl($endpoint = '')
108
{
109
return get\_rest\_url(null, $this->api\_base . '/' . $endpoint);
97
110
}
98
99
111
100
public function isScriptDebug() {
101
return defined('SCRIPT\_DEBUG') && SCRIPT\_DEBUG === true;
112
113
public function isScriptDebug()
114
{
115
return defined('SCRIPT\_DEBUG') && SCRIPT\_DEBUG === true;
102
116
}
103
104
public function isWPVersionCompatible(){
117
118
public function isWPVersionCompatible()
119
{
105
120
// our new JavaScript based editor relies on some WordPress React components available in 5.4
106
return version\_compare( get\_bloginfo( 'version' ), '5.4', '>=' );
121
return version\_compare(get\_bloginfo('version'), '5.4', '>=');
107
122
}
108
123
…
…
112
127
\* either to tell the users to check their settings in the new editor or to tell them to better update WP to see the new editor
113
128
\*/
114
public function showTemplateDesignerUpdateNotice(){
115
if( $this->isWPVersionCompatible() ){
129
public function showTemplateDesignerUpdateNotice()
130
{
131
if ($this->isWPVersionCompatible()) {
116
132
$options = Haet\_Mail()->get\_options();
117
if( !array\_key\_exists( 'user\_checked\_settings\_in\_v3', $options ) || !$options\['user\_checked\_settings\_in\_v3'\] ){
118
?>
133
if (!array\_key\_exists('user\_checked\_settings\_in\_v3', $options) || !$options\['user\_checked\_settings\_in\_v3'\]) {
134
?>
119
135
<div class="notice notice-success">
120
<p><?php echo sprintf( \_\_( 'You successfully upgraded to our <strong>new email editor</strong>! Please <a href="%1$s">review your settings</a> to make sure everything still looks as expected.', 'wp-html-mail' ), get\_admin\_url(null,'options-general.php?page=wp-html-mail') ); ?></p>
136
<p><?php echo sprintf(\_\_('You successfully upgraded to our <strong>new email editor</strong>! Please <a href="%1$s">review your settings</a> to make sure everything still looks as expected.', 'wp-html-mail'), get\_admin\_url(null, 'options-general.php?page=wp-html-mail')); ?></p>
121
137
</div>
122
<?php
138
<?php
123
139
}
124
}elseif( array\_key\_exists( 'page', $\_GET ) && $\_GET\['page'\] == "wp-html-mail" ){
140
} elseif (array\_key\_exists('page', $\_GET) && $\_GET\['page'\] == "wp-html-mail") {
125
141
?>
126
142
<div class="notice notice-warning">
127
<p><?php \_e( 'In order to use our <strong>new email editor</strong> you need to upgrade to WordPress 5.4 or higher. In the meanwhile you can still use our classic settings pages.', 'wp-html-mail' ); ?></p>
143
<p><?php \_e('In order to use our <strong>new email editor</strong> you need to upgrade to WordPress 5.4 or higher. In the meanwhile you can still use our classic settings pages.', 'wp-html-mail'); ?></p>
128
144
</div>
129
<?php
145
<?php
130
146
}
131
147
}