Skip to content

Commit cf56488

Browse files
Merge branch 'development' into bugfix/pro/593
2 parents 99d5107 + 9a7fee5 commit cf56488

11 files changed

Lines changed: 1236 additions & 127 deletions

File tree

classes/Visualizer/Module/AIBuilder.php

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ private function _verify_create_nonce(): void {
9494
}
9595
}
9696

97+
/**
98+
* Verify that the current user can edit a chart.
99+
*
100+
* @param int $chart_id Chart ID.
101+
*/
102+
private function _verify_chart_access( $chart_id ): void {
103+
if ( ! current_user_can( 'edit_post', $chart_id ) ) {
104+
wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'visualizer' ) ), 403 );
105+
}
106+
}
107+
97108
/**
98109
* Persist chart data + series from a source.
99110
*
@@ -159,6 +170,7 @@ public function getChartNonce(): void {
159170
if ( ! $chart_id || ! get_post( $chart_id ) ) {
160171
wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
161172
}
173+
$this->_verify_chart_access( $chart_id );
162174
wp_send_json_success(
163175
array(
164176
'upload_nonce' => wp_create_nonce( 'visualizer-ai-upload-' . $chart_id ),
@@ -180,6 +192,7 @@ public function fetchChart(): void {
180192
if ( ! $chart || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
181193
wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
182194
}
195+
$this->_verify_chart_access( $chart_id );
183196

184197
$series = get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true );
185198
$data = Visualizer_Module::get_chart_data( $chart, '', false );
@@ -206,7 +219,7 @@ public function fetchChart(): void {
206219
/**
207220
* Determines whether a remote URL serves an XLSX file.
208221
*
209-
* Uses wp_safe_remote_get() and checks ZIP magic number (PK\x03\x04).
222+
* Uses the shared remote-fetch policy and checks ZIP magic number (PK\x03\x04).
210223
*
211224
* @access private
212225
* @param string $url The remote URL to probe.
@@ -218,14 +231,15 @@ private static function _url_is_xlsx( $url ) {
218231
return false;
219232
}
220233

221-
$response = wp_safe_remote_get(
234+
$response = Visualizer_Remote_Fetch::request(
222235
$url,
223236
array(
224-
'timeout' => 15,
225-
'redirection' => 5,
226-
'stream' => true,
227-
'filename' => $tmpfile,
228-
'headers' => array( 'Range' => 'bytes=0-3' ),
237+
'timeout' => 15,
238+
'redirection' => 5,
239+
'stream' => true,
240+
'filename' => $tmpfile,
241+
'headers' => array( 'Range' => 'bytes=0-3' ),
242+
'limit_response_size' => 4,
229243
)
230244
);
231245

@@ -258,6 +272,7 @@ public function uploadData(): void {
258272
if ( ! get_post( $chart_id ) ) {
259273
wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
260274
}
275+
$this->_verify_chart_access( $chart_id );
261276

262277
$source_type = isset( $_POST['source_type'] ) ? sanitize_key( $_POST['source_type'] ) : 'csv_string';
263278
$source = null;
@@ -297,17 +312,6 @@ public function uploadData(): void {
297312
}
298313
$url = wp_unslash( $_POST['file_url'] );
299314

300-
// Allow local absolute paths in dev (same CSVs used by Classic).
301-
if ( is_string( $url ) && file_exists( $url ) && is_readable( $url ) ) {
302-
$ext = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
303-
if ( 'xlsx' === $ext && class_exists( 'Visualizer_Source_Xlsx' ) ) {
304-
$source = new Visualizer_Source_Xlsx( $url );
305-
} else {
306-
$source = new Visualizer_Source_Csv( $url );
307-
}
308-
break;
309-
}
310-
311315
if ( function_exists( 'wp_http_validate_url' ) ) {
312316
$validated_url = wp_http_validate_url( (string) $url );
313317
$url = false === $validated_url ? false : (string) $validated_url;
@@ -435,6 +439,7 @@ public function generateChart(): void {
435439
if ( ! $chart_id || ! get_post( $chart_id ) ) {
436440
wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
437441
}
442+
$this->_verify_chart_access( $chart_id );
438443

439444
$prompt = isset( $_POST['prompt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['prompt'] ) ) : '';
440445
$series = isset( $_POST['series'] ) ? wp_unslash( $_POST['series'] ) : '';
@@ -560,6 +565,7 @@ public function saveChart(): void {
560565
if ( ! $chart_id || ! get_post( $chart_id ) ) {
561566
wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
562567
}
568+
$this->_verify_chart_access( $chart_id );
563569
if ( empty( $code ) ) {
564570
wp_send_json_error( array( 'message' => __( 'No chart code found. Generate a chart first.', 'visualizer' ) ) );
565571
}

classes/Visualizer/Module/Chart.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ public function getJsonData() {
204204

205205
$chart_id = $params['chart'];
206206

207-
if ( empty( $chart_id ) ) {
207+
$chart = $chart_id ? get_post( $chart_id ) : null;
208+
if ( ! $chart || Visualizer_Plugin::CPT_VISUALIZER !== $chart->post_type || ! current_user_can( 'edit_post', $chart_id ) ) {
208209
wp_die();
209210
}
210211

@@ -1063,7 +1064,7 @@ public function renderFlattrScript() {
10631064
* Used as a fallback when the URL path has no recognisable file extension
10641065
* (e.g. SharePoint, signed S3 URLs, or "download?id=…" endpoints).
10651066
*
1066-
* Uses wp_safe_remote_get() to block requests to private/loopback addresses,
1067+
* Uses the shared remote-fetch policy to block non-public destinations,
10671068
* and streams the response to a temp file so no body data is held in memory
10681069
* regardless of whether the server honours the Range header.
10691070
*
@@ -1082,14 +1083,15 @@ private static function _url_is_xlsx( $url ) {
10821083
return false;
10831084
}
10841085

1085-
$response = wp_safe_remote_get(
1086+
$response = Visualizer_Remote_Fetch::request(
10861087
$url,
10871088
array(
1088-
'timeout' => 10,
1089-
'user-agent' => 'WordPress/' . get_bloginfo( 'version' ),
1090-
'headers' => array( 'Range' => 'bytes=0-3' ),
1091-
'stream' => true,
1092-
'filename' => $tmpfile,
1089+
'timeout' => 10,
1090+
'user-agent' => 'WordPress/' . get_bloginfo( 'version' ),
1091+
'headers' => array( 'Range' => 'bytes=0-3' ),
1092+
'stream' => true,
1093+
'filename' => $tmpfile,
1094+
'limit_response_size' => 4,
10931095
)
10941096
);
10951097

0 commit comments

Comments
 (0)