Skip to content

Commit 0ef6336

Browse files
fix: scheduled CSV refresh runs without a logged-in user
Background schedule/refresh invokes the upload handler with no logged-in user and VISUALIZER_DO_NOT_DIE set. Since Feb 2026 uploadData() also requires edit_posts/edit_post, so the scheduled import silently returned and charts stopped auto-updating. Gate the two capability checks on $can_die so they apply to real web requests only; trusted internal calls (the only context that defines VISUALIZER_DO_NOT_DIE, never from request input) proceed. Web path and the nonce check are unchanged. Fixes Codeinwp/visualizer-pro#590 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent aa36ff4 commit 0ef6336

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

classes/Visualizer/Module/Chart.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,12 +1222,13 @@ public function uploadData() {
12221222
// if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
12231223
// otherwise, assume this is a normal web request.
12241224
$can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1225+
// $can_die also gates the capability checks below, so VISUALIZER_DO_NOT_DIE must stay internal-only (never set from request input or globally).
12251226

1226-
// validate nonce
1227+
// validate nonce; capability check applies to web requests only, not trusted internal calls.
12271228
if (
12281229
! isset( $_GET['nonce'] ) ||
12291230
! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1230-
! current_user_can( 'edit_posts' )
1231+
( $can_die && ! current_user_can( 'edit_posts' ) )
12311232
) {
12321233
if ( ! $can_die ) {
12331234
return;
@@ -1244,7 +1245,7 @@ public function uploadData() {
12441245
! $chart_id ||
12451246
! $chart ||
12461247
$chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ||
1247-
! current_user_can( 'edit_post', $chart_id )
1248+
( $can_die && ! current_user_can( 'edit_post', $chart_id ) )
12481249
) {
12491250
if ( ! $can_die ) {
12501251
return;

tests/test-schedule.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* Tests for background/scheduled import behavior.
4+
*
5+
* @package visualizer
6+
* @subpackage Tests
7+
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8+
*/
9+
10+
/**
11+
* Scheduled/background refresh must still import with no logged-in user (regression for visualizer-pro#590).
12+
*
13+
* ponytail: defines VISUALIZER_DO_NOT_DIE process-wide; isolate this test if one ever needs the exit/die path.
14+
*/
15+
class Test_Visualizer_Schedule extends WP_UnitTestCase {
16+
17+
/**
18+
* Internal upload (no user, VISUALIZER_DO_NOT_DIE set) must still import and persist chart data.
19+
*/
20+
public function test_internal_upload_imports_without_logged_in_user() {
21+
wp_set_current_user( 0 );
22+
$this->assertFalse( current_user_can( 'edit_posts' ), 'precondition: background context has no editing user' );
23+
24+
$chart_id = self::factory()->post->create(
25+
array(
26+
'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
27+
'post_status' => 'publish',
28+
'post_content' => 'OLD-CONTENT',
29+
)
30+
);
31+
32+
// mimic Pro's internal invocation; clean superglobals so prior tests don't reroute uploadData().
33+
$_GET = $_POST = $_FILES = array();
34+
define( 'VISUALIZER_DO_NOT_DIE', true );
35+
$_GET['nonce'] = wp_create_nonce( 'visualizer-upload-data' );
36+
$_GET['chart'] = $chart_id;
37+
$_POST['editor-type'] = 'text';
38+
$_POST['chart_data'] = "Name,Value\nstring,number\nAlpha,111\nBeta,222";
39+
40+
do_action( 'wp_ajax_' . Visualizer_Plugin::ACTION_UPLOAD_DATA );
41+
42+
$content = get_post_field( 'post_content', $chart_id );
43+
$this->assertStringContainsString( 'Alpha', $content, 'scheduled/background import must update chart data without a logged-in user' );
44+
}
45+
}

0 commit comments

Comments
 (0)