Skip to content
This repository was archived by the owner on Jun 2, 2025. It is now read-only.

Commit b48f210

Browse files
committed
Make infromation schema tables readonly
1 parent 99c0350 commit b48f210

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

tests/WP_SQLite_Driver_Tests.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3931,4 +3931,49 @@ public function getReservedPrefixTestData(): array {
39313931
),
39323932
);
39333933
}
3934+
3935+
/**
3936+
* @dataProvider getInformationSchemaIsReadonlyTestData
3937+
*/
3938+
public function testInformationSchemaIsReadonly( string $query ): void {
3939+
$this->assertQuery( 'CREATE TABLE t1 (id INT)' );
3940+
$this->expectException( WP_SQLite_Driver_Exception::class );
3941+
$this->expectExceptionMessage( "Access denied for user 'sqlite'@'%' to database 'information_schema'" );
3942+
$this->assertQuery( $query );
3943+
}
3944+
3945+
public function getInformationSchemaIsReadonlyTestData(): array {
3946+
return array(
3947+
array( 'INSERT INTO information_schema.tables (table_name) VALUES ("t")' ),
3948+
array( 'UPDATE information_schema.tables SET table_name = "new_t" WHERE table_name = "t"' ),
3949+
array( 'DELETE FROM information_schema.tables WHERE table_name = "t"' ),
3950+
array( 'CREATE TABLE information_schema.new_table (id INT)' ),
3951+
array( 'ALTER TABLE information_schema.tables ADD COLUMN new_column INT' ),
3952+
array( 'DROP TABLE information_schema.tables' ),
3953+
array( 'TRUNCATE information_schema.tables' ),
3954+
);
3955+
}
3956+
3957+
/**
3958+
* @dataProvider getInformationSchemaIsReadonlyWithUseTestData
3959+
*/
3960+
public function testInformationSchemaIsReadonlyWithUse( string $query ): void {
3961+
$this->assertQuery( 'CREATE TABLE t1 (id INT)' );
3962+
$this->expectException( WP_SQLite_Driver_Exception::class );
3963+
$this->expectExceptionMessage( "Access denied for user 'sqlite'@'%' to database 'information_schema'" );
3964+
$this->assertQuery( 'USE information_schema' );
3965+
$this->assertQuery( $query );
3966+
}
3967+
3968+
public function getInformationSchemaIsReadonlyWithUseTestData(): array {
3969+
return array(
3970+
array( 'INSERT INTO tables (table_name) VALUES ("t")' ),
3971+
array( 'UPDATE tables SET table_name = "new_t" WHERE table_name = "t"' ),
3972+
array( 'DELETE FROM tables WHERE table_name = "t"' ),
3973+
array( 'CREATE TABLE new_table (id INT)' ),
3974+
array( 'ALTER TABLE tables ADD COLUMN new_column INT' ),
3975+
array( 'DROP TABLE tables' ),
3976+
array( 'TRUNCATE tables' ),
3977+
);
3978+
}
39343979
}

wp-includes/sqlite-ast/class-wp-sqlite-driver.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ class WP_SQLite_Driver {
306306
*/
307307
private $last_sql_calc_found_rows = null;
308308

309+
/**
310+
* Whether the current MySQL query is read-only.
311+
*
312+
* @var bool
313+
*/
314+
private $is_readonly;
315+
309316
/**
310317
* Transaction nesting level of the executed SQLite queries.
311318
*
@@ -703,6 +710,7 @@ private function execute_mysql_query( WP_Parser_Node $node ): void {
703710
$node = $children[0]->get_first_child_node();
704711
switch ( $node->rule_name ) {
705712
case 'selectStatement':
713+
$this->is_readonly = true;
706714
$this->execute_select_statement( $node );
707715
break;
708716
case 'insertStatement':
@@ -778,12 +786,14 @@ private function execute_mysql_query( WP_Parser_Node $node ): void {
778786
$this->last_result = 0;
779787
break;
780788
case 'showStatement':
789+
$this->is_readonly = true;
781790
$this->execute_show_statement( $node );
782791
break;
783792
case 'utilityStatement':
784793
$subtree = $node->get_first_child_node();
785794
switch ( $subtree->rule_name ) {
786795
case 'describeStatement':
796+
$this->is_readonly = true;
787797
$this->execute_describe_statement( $subtree );
788798
break;
789799
case 'useCommand':
@@ -2084,6 +2094,24 @@ private function translate_qualified_identifier(
20842094
}
20852095
}
20862096

2097+
/*
2098+
* Make the 'information_schema' database read-only.
2099+
*
2100+
* This basic approach is rather restrictive, as it blocks the usage
2101+
* of information schema tables in all data-modifying statements.
2102+
*
2103+
* Some of these statements can be valid, when the schema is only read:
2104+
* DELETE t FROM t JOIN information_schema.columns c ON ...
2105+
*
2106+
* If needed, a more granular approach can be implemented in the future.
2107+
*/
2108+
if ( true === $is_information_schema && false === $this->is_readonly ) {
2109+
throw $this->new_driver_exception(
2110+
"Access denied for user 'sqlite'@'%' to database 'information_schema'",
2111+
'42000'
2112+
);
2113+
}
2114+
20872115
// Database-level object name (table, view, procedure, trigger, etc.).
20882116
if ( null !== $object_node ) {
20892117
if ( $is_information_schema ) {
@@ -2937,6 +2965,7 @@ private function flush(): void {
29372965
$this->last_sqlite_queries = array();
29382966
$this->last_result = null;
29392967
$this->last_return_value = null;
2968+
$this->is_readonly = false;
29402969
}
29412970

29422971
/**

0 commit comments

Comments
 (0)