Skip to content

Commit c9d203a

Browse files
authored
Merge branch 'develop' into ari/detect-domain-changes
2 parents 62a1173 + 32c4d29 commit c9d203a

20 files changed

Lines changed: 256 additions & 30 deletions

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ phpstan.neon.dist export-ignore
1313
phpunit.xml.dist export-ignore
1414
README.md export-ignore
1515
.wordpress-org/* export-ignore
16+
.vscode/* export-ignore

.vscode/settings.json

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{
2+
"intelephense.stubs": [
3+
"apache",
4+
"bcmath",
5+
"bz2",
6+
"calendar",
7+
"com_dotnet",
8+
"Core",
9+
"ctype",
10+
"curl",
11+
"date",
12+
"dba",
13+
"dom",
14+
"enchant",
15+
"exif",
16+
"FFI",
17+
"fileinfo",
18+
"filter",
19+
"fpm",
20+
"ftp",
21+
"gd",
22+
"gettext",
23+
"gmp",
24+
"hash",
25+
"iconv",
26+
"imap",
27+
"intl",
28+
"json",
29+
"ldap",
30+
"libxml",
31+
"mbstring",
32+
"meta",
33+
"mysqli",
34+
"oci8",
35+
"odbc",
36+
"openssl",
37+
"pcntl",
38+
"pcre",
39+
"PDO",
40+
"pdo_ibm",
41+
"pdo_mysql",
42+
"pdo_pgsql",
43+
"pdo_sqlite",
44+
"pgsql",
45+
"Phar",
46+
"posix",
47+
"pspell",
48+
"readline",
49+
"Reflection",
50+
"session",
51+
"shmop",
52+
"SimpleXML",
53+
"snmp",
54+
"soap",
55+
"sockets",
56+
"sodium",
57+
"SPL",
58+
"sqlite3",
59+
"standard",
60+
"superglobals",
61+
"sysvmsg",
62+
"sysvsem",
63+
"sysvshm",
64+
"tidy",
65+
"tokenizer",
66+
"xml",
67+
"xmlreader",
68+
"xmlrpc",
69+
"xmlwriter",
70+
"xsl",
71+
"Zend OPcache",
72+
"zip",
73+
"zlib",
74+
"wordpress",
75+
"wordpress-globals",
76+
"wp-cli",
77+
"genesis",
78+
"polylang"
79+
],
80+
"intelephense.environment.includePaths": [
81+
"./vendor/yoast/wp-test-utils",
82+
"./vendor/php-stubs/wordpress-stubs"
83+
]
84+
}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Bugs we fixed:
99
* Fix missing content and streak badges after they are all completed.
1010
* Fix issue when point is sometimes not awarded for completing "Remove term without posts" task.
1111

12+
= 1.8.1 =
13+
14+
- Security fix: Privilege escalation via an AJAX call where authenticated users could update arbitrary site options.
15+
- Added capability checks.
16+
- Thanks to [NumeX](https://github.com/NumeXx) for responsibly disclosing via the Patchstack Bug Bounty Program.
1217

1318
= 1.8.0 =
1419

assets/js/suggested-task.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ prplSuggestedTask = {
267267
'trash' === newStatus ? 'complete' : 'pending'
268268
);
269269
const eventPoints = parseInt(
270-
postData?.meta?.prpl_points
270+
postData?.prpl_points
271271
);
272272

273273
// Task is trashed, check if we need to celebrate.

assets/js/widgets/todo.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const prplTodoWidget = {
5454
detail: {
5555
item,
5656
insertPosition:
57-
1 === item?.meta?.prpl_points
57+
1 === item?.prpl_points
5858
? 'afterbegin' // Add golden task to the start of the list.
5959
: 'beforeend',
6060
listId:
@@ -89,7 +89,7 @@ const prplTodoWidget = {
8989
detail: {
9090
item,
9191
insertPosition:
92-
1 === item?.meta?.prpl_points
92+
1 === item?.prpl_points
9393
? 'afterbegin' // Add golden task to the start of the list.
9494
: 'beforeend',
9595
listId:
@@ -137,13 +137,13 @@ const prplTodoWidget = {
137137
const newTask = {
138138
...response,
139139
meta: {
140-
prpl_points: 0,
141140
prpl_url: '',
142141
...( response.meta || {} ),
143142
},
144143
provider: 'user',
145144
category: 'user',
146145
order: prplTodoWidget.getHighestItemOrder() + 1,
146+
prpl_points: 0,
147147
};
148148

149149
// Inject the new task into the DOM.

classes/admin/class-page-settings.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public function should_show_setting( $page_type ) {
128128
* @return void
129129
*/
130130
public function store_settings_form_options() {
131+
132+
if ( ! \current_user_can( 'manage_options' ) ) {
133+
\wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to update settings.', 'progress-planner' ) ] );
134+
}
135+
131136
// Check the nonce.
132137
\check_admin_referer( 'progress_planner' );
133138

classes/class-suggested-tasks-db.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,10 @@ public function update_recommendation( $id, $data ) {
175175
}
176176

177177
$update_data = [ 'ID' => $id ];
178-
$update_meta = [];
179178
$update_terms = [];
180179
$update_results = [];
181180
foreach ( $data as $key => $value ) {
182181
switch ( $key ) {
183-
case 'points':
184-
case 'prpl_points':
185-
$update_meta[ 'prpl_' . \str_replace( 'prpl_', '', (string) $key ) ] = $value;
186-
break;
187-
188182
case 'category':
189183
case 'provider':
190184
$update_terms[ "prpl_recommendations_$key" ] = $value;
@@ -200,12 +194,6 @@ public function update_recommendation( $id, $data ) {
200194
$update_results[] = (bool) \wp_update_post( $update_data );
201195
}
202196

203-
if ( ! empty( $update_meta ) ) {
204-
foreach ( $update_meta as $key => $value ) {
205-
$update_results[] = (bool) \update_post_meta( $id, $key, $value );
206-
}
207-
}
208-
209197
if ( ! empty( $update_terms ) ) {
210198
foreach ( $update_terms as $taxonomy => $term ) {
211199
$update_results[] = (bool) \wp_set_object_terms( $id, $term->slug, $taxonomy ); // @phpstan-ignore-line property.nonObject

classes/class-suggested-tasks.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ public function suggested_task_action() {
235235
\wp_send_json_error( [ 'message' => \esc_html__( 'Task not found.', 'progress-planner' ) ] );
236236
}
237237

238+
$provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $task->get_provider_id() );
239+
240+
if ( ! $provider ) {
241+
\wp_send_json_error( [ 'message' => \esc_html__( 'Provider not found.', 'progress-planner' ) ] );
242+
}
243+
244+
if ( ! $provider->capability_required() ) {
245+
\wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to complete this task.', 'progress-planner' ) ] );
246+
}
247+
238248
$updated = false;
239249

240250
switch ( $action ) {
@@ -281,7 +291,7 @@ public function register_post_type() {
281291
'show_in_admin_bar' => \apply_filters( 'progress_planner_tasks_show_ui', false ),
282292
'show_in_rest' => true,
283293
'rest_controller_class' => \Progress_Planner\Rest\Recommendations_Controller::class,
284-
'supports' => [ 'title', 'editor', 'author', 'custom-fields', 'page-attributes' ],
294+
'supports' => [ 'title', 'excerpt', 'editor', 'author', 'custom-fields', 'page-attributes' ],
285295
'rewrite' => false,
286296
'menu_icon' => 'dashicons-admin-tools',
287297
'menu_position' => 5,
@@ -291,11 +301,6 @@ public function register_post_type() {
291301
);
292302

293303
$rest_meta_fields = [
294-
'prpl_points' => [
295-
'type' => 'number',
296-
'single' => true,
297-
'show_in_rest' => true,
298-
],
299304
'prpl_task_id' => [
300305
'type' => 'string',
301306
'single' => true,
@@ -442,13 +447,14 @@ public function rest_prepare_recommendation( $response, $post ) {
442447

443448
// This has to be the last item to be added because actions use data from previous items.
444449
$response->data['prpl_task_actions'] = $provider->get_task_actions( $response->data );
450+
$response->data['prpl_points'] = $provider->get_points();
445451

446452
/*
447453
* Check if task was completed before - for example, comments were disabled and then re-enabled, and remove points if so.
448454
* Those are tasks which are completed by toggling an option, so non repetitive & not user tasks.
449455
*/
450456
if ( ! \has_term( 'user', 'prpl_recommendations_provider', $post->ID ) && ! $provider->is_repetitive() && $provider->task_has_activity( $response->data['meta']['prpl_task_id'] ) ) {
451-
$response->data['meta']['prpl_points'] = 0;
457+
$response->data['prpl_points'] = 0;
452458
}
453459
}
454460

@@ -513,7 +519,15 @@ public function get_tasks_in_rest_format( array $args = [] ) {
513519
}
514520
}
515521

516-
return $tasks;
522+
/**
523+
* Allow other classes to modify the tasks in REST format.
524+
*
525+
* @param array $tasks The tasks.
526+
* @param array $args The arguments.
527+
*
528+
* @return array
529+
*/
530+
return \apply_filters( 'progress_planner_suggested_tasks_in_rest_format', $tasks, $args );
517531
}
518532

519533
/**

classes/class-todo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function maybe_change_first_item_points_on_monday() {
5757
foreach ( $pending_items as $task ) {
5858
\progress_planner()->get_suggested_tasks_db()->update_recommendation(
5959
$task->ID,
60-
[ 'points' => $task->ID === $pending_items[0]->ID ? 1 : 0 ]
60+
[ 'post_excerpt' => $task->ID === $pending_items[0]->ID ? 'GOLDEN' : '' ]
6161
);
6262
}
6363

classes/suggested-tasks/providers/class-email-sending.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ protected function is_there_sending_email_override() {
258258
* @return void
259259
*/
260260
public function ajax_test_email_sending() {
261+
262+
if ( ! $this->capability_required() ) {
263+
\wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to test email sending.', 'progress-planner' ) ] );
264+
}
265+
261266
// Check the nonce.
262267
\check_admin_referer( 'progress_planner' );
263268

0 commit comments

Comments
 (0)