Skip to content

Commit e615d23

Browse files
authored
Account for special '$user' variable appearing as first schema in search_path (#35567)
Given that the default search_path value in a PostgreSQL installation is '"$user", public', or perhaps just '"$user"', as may be the case if hardened against CVE-2018-1058, it is preferable to account for the possibility that an end-user may wish to configure a PostgreSQL database connection in Laravel to mimic said default. Now, if '$user' is the first schema in the search_path, the PostgresBuilder will resolve that schema name to the username defined on the database connection whenever appropriate, e.g., in the hasTable() and getColumnListing() methods.
1 parent da38178 commit e615d23

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/Illuminate/Database/Schema/PostgresBuilder.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ protected function parseSchemaAndTable($reference)
181181
// We will use the default schema unless the schema has been specified in the
182182
// query. If the schema has been specified in the query then we can use it
183183
// instead of a default schema configured in the connection search path.
184-
$schema = $searchPath[0];
184+
$schema = $searchPath[0] === '$user'
185+
? $this->connection->getConfig('username')
186+
: $searchPath[0];
185187

186188
if (count($parts) === 2) {
187189
$schema = $parts[0];

tests/Database/DatabasePostgresBuilderTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,29 @@ public function testWhenSearchPathNotEmptyHasTableWithUnqualifiedSchemaReference
5858
$builder->hasTable('foo');
5959
}
6060

61+
/**
62+
* Ensure that when the reference is unqualified (i.e., does not contain a
63+
* database name or a schema), and the first schema in the search_path is
64+
* the special variable '$user', the database specified on the connection is
65+
* used, the first schema in the search_path is used, and the variable
66+
* resolves to the username specified on the connection.
67+
*/
68+
public function testWhenFirstSchemaInSearchPathIsVariableHasTableWithUnqualifiedSchemaReferenceIsCorrect()
69+
{
70+
$connection = $this->getConnection();
71+
$connection->shouldReceive('getConfig')->with('username')->andReturn('foouser');
72+
$connection->shouldReceive('getConfig')->with('search_path')->andReturn('$user');
73+
$grammar = m::mock(PostgresGrammar::class);
74+
$connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar);
75+
$grammar->shouldReceive('compileTableExists')->andReturn("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'");
76+
$connection->shouldReceive('select')->with("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'", ['laravel', 'foouser', 'foo'])->andReturn(['countable_result']);
77+
$connection->shouldReceive('getTablePrefix');
78+
$connection->shouldReceive('getConfig')->with('database')->andReturn('laravel');
79+
$builder = $this->getBuilder($connection);
80+
81+
$builder->hasTable('foo');
82+
}
83+
6184
/**
6285
* Ensure that when the reference is qualified only with a schema, that
6386
* the database specified on the connection is used, and the specified
@@ -146,6 +169,32 @@ public function testWhenSearchPathNotEmptyGetColumnListingWithUnqualifiedSchemaR
146169
$builder->getColumnListing('foo');
147170
}
148171

172+
/**
173+
* Ensure that when the reference is unqualified (i.e., does not contain a
174+
* database name or a schema), and the first schema in the search_path is
175+
* the special variable '$user', the database specified on the connection is
176+
* used, the first schema in the search_path is used, and the variable
177+
* resolves to the username specified on the connection.
178+
*/
179+
public function testWhenFirstSchemaInSearchPathIsVariableGetColumnListingWithUnqualifiedSchemaReferenceIsCorrect()
180+
{
181+
$connection = $this->getConnection();
182+
$connection->shouldReceive('getConfig')->with('username')->andReturn('foouser');
183+
$connection->shouldReceive('getConfig')->with('search_path')->andReturn('$user');
184+
$grammar = m::mock(PostgresGrammar::class);
185+
$connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar);
186+
$grammar->shouldReceive('compileColumnListing')->andReturn('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?');
187+
$connection->shouldReceive('select')->with('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', ['laravel', 'foouser', 'foo'])->andReturn(['countable_result']);
188+
$connection->shouldReceive('getTablePrefix');
189+
$connection->shouldReceive('getConfig')->with('database')->andReturn('laravel');
190+
$processor = m::mock(PostgresProcessor::class);
191+
$connection->shouldReceive('getPostProcessor')->andReturn($processor);
192+
$processor->shouldReceive('processColumnListing')->andReturn(['some_column']);
193+
$builder = $this->getBuilder($connection);
194+
195+
$builder->getColumnListing('foo');
196+
}
197+
149198
/**
150199
* Ensure that when the reference is qualified only with a schema, that
151200
* the database specified on the connection is used, and the specified

0 commit comments

Comments
 (0)