Skip to content

Commit b8e9d8d

Browse files
release: fixes
### Fixes - **Updated internal dependencies:** Enhanced performance and security.
2 parents 8ff25c6 + 7410f89 commit b8e9d8d

File tree

5 files changed

+128
-9
lines changed

5 files changed

+128
-9
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ cypress.env.json
1111
.DS_Store
1212
/cypress/videos/
1313
/cypress/screenshots/
14-
artifacts
14+
artifacts
15+
.phpunit.result.cache

classes/Visualizer/Module/Chart.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1421,7 +1421,7 @@ private function _handleDataPage() {
14211421
public function getQueryData() {
14221422
check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
14231423

1424-
if ( ! current_user_can( 'edit_posts' ) ) {
1424+
if ( ! current_user_can( 'administrator' ) ) {
14251425
wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
14261426
}
14271427

classes/Visualizer/Source/Query.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,43 @@ public function fetch( $as_html = false, $results_as_numeric_array = false, $raw
8080
}
8181

8282
// only select queries allowed.
83-
if ( preg_match( '/\s*(\binsert\b|\bdelete\b|\bupdate\b|\breplace\b|\bcreate\b|\balter\b|\bdrop\b|\btruncate\b)\s/i', $this->_query ) ) {
83+
if ( ! preg_match( '/\s*(\bselect\b)\s/i', $this->_query ) ) {
84+
$this->_error = __( 'Only SELECT queries are allowed', 'visualizer' );
85+
return false;
86+
}
87+
88+
// if previous check passed, check for disallowed query parts to prevent subqueries and other harmful queries.
89+
$disallow_query_parts = array(
90+
'INSERT',
91+
'UPDATE',
92+
'DELETE',
93+
'RENAME',
94+
'DROP',
95+
'CREATE',
96+
'TRUNCATE',
97+
'ALTER',
98+
'COMMIT',
99+
'ROLLBACK',
100+
'MERGE',
101+
'CALL',
102+
'EXPLAIN',
103+
'LOCK',
104+
'GRANT',
105+
'REVOKE',
106+
'SAVEPOINT',
107+
'TRANSACTION',
108+
'SET',
109+
);
110+
$disallow_regex = implode(
111+
'|',
112+
array_map(
113+
function ( $value ) {
114+
return '\b' . $value . '\b';
115+
}, $disallow_query_parts
116+
)
117+
);
118+
119+
if ( preg_match( '/(' . $disallow_regex . ')/i', $this->_query) !== 0 ) {
84120
$this->_error = __( 'Only SELECT queries are allowed', 'visualizer' );
85121
return false;
86122
}

composer.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test-ajax.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ class Test_Visualizer_Ajax extends WP_Ajax_UnitTestCase {
2020
*/
2121
private $admin_user_id;
2222

23+
/**
24+
* Contributor user ID.
25+
*
26+
* @var int
27+
*/
28+
private $contibutor_user_id;
29+
30+
/**
31+
* Subscriber user ID.
32+
*
33+
* @var int
34+
*/
35+
private $subscriber_user_id;
36+
2337
/**
2438
* Set up.
2539
*/
@@ -32,6 +46,18 @@ public function setUp() {
3246
);
3347
wp_set_current_user( $this->admin_user_id );
3448

49+
$this->contibutor_user_id = $this->factory->user->create(
50+
array(
51+
'role' => 'contributor',
52+
)
53+
);
54+
55+
$this->subscriber_user_id = $this->factory->user->create(
56+
array(
57+
'role' => 'subscriber',
58+
)
59+
);
60+
3561
}
3662

3763
/**
@@ -113,10 +139,39 @@ public function test_ajax_response_get_query_data_valid_query_with_filtered_colu
113139
$this->assertTrue( $response->success );
114140
}
115141

142+
/**
143+
* Test the AJAX response for fetching the database data with user capability.
144+
*/
145+
public function test_ajax_response_get_query_data_contributor_dissallow() {
146+
wp_set_current_user( $this->contibutor_user_id );
147+
$this->_setRole( 'contributor' );
148+
149+
$_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION );
150+
151+
$_POST['params'] = array(
152+
'query' => "/**/UPDATE wp_options SET option_value='administrator' WHERE option_name='default_role' --",
153+
'chart_id' => 1,
154+
);
155+
try {
156+
// Trigger the AJAX action
157+
$this->_handleAjax( Visualizer_Plugin::ACTION_FETCH_DB_DATA );
158+
} catch ( WPAjaxDieContinueException $e ) {
159+
// We expected this, do nothing.
160+
}
161+
162+
$response = json_decode( $this->_last_response );
163+
$this->assertIsObject( $response );
164+
$this->assertObjectHasAttribute( 'success', $response );
165+
$this->assertObjectHasAttribute( 'data', $response );
166+
$this->assertEquals( 'Action not allowed for this user.', $response->data->msg );
167+
$this->assertFalse( $response->success );
168+
}
169+
116170
/**
117171
* Test the AJAX response for fetching the database data with user capability.
118172
*/
119173
public function test_ajax_response_get_query_data_subcriber_dissallow() {
174+
wp_set_current_user( $this->subscriber_user_id );
120175
$this->_setRole( 'subscriber' );
121176

122177
$_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION );
@@ -139,4 +194,31 @@ public function test_ajax_response_get_query_data_subcriber_dissallow() {
139194
$this->assertEquals( 'Action not allowed for this user.', $response->data->msg );
140195
$this->assertFalse( $response->success );
141196
}
197+
198+
/**
199+
* Test the AJAX response for fetching the database data with invalid query.
200+
*/
201+
public function test_ajax_response_get_query_data_invalid_query_subquery() {
202+
$this->_setRole( 'administrator' );
203+
204+
$_GET['security'] = wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION );
205+
206+
$_POST['params'] = array(
207+
'query' => "UPDATE wp_options SET option_value = ( SELECT role_name FROM role_configurations WHERE condition = 'specific_condition' LIMIT 1 )WHERE option_name = 'default_role';",
208+
'chart_id' => 1,
209+
);
210+
try {
211+
// Trigger the AJAX action
212+
$this->_handleAjax( Visualizer_Plugin::ACTION_FETCH_DB_DATA );
213+
} catch ( WPAjaxDieContinueException $e ) {
214+
// We expected this, do nothing.
215+
}
216+
217+
$response = json_decode( $this->_last_response );
218+
$this->assertIsObject( $response );
219+
$this->assertObjectHasAttribute( 'success', $response );
220+
$this->assertObjectHasAttribute( 'data', $response );
221+
$this->assertEquals( 'Only SELECT queries are allowed', $response->data->msg );
222+
$this->assertFalse( $response->success );
223+
}
142224
}

0 commit comments

Comments
 (0)