Skip to content

Commit

Permalink
Security #257: Gadget chain / unserialization follow-up to add test c…
Browse files Browse the repository at this point in the history
…overage.
  • Loading branch information
quicksketch committed Jan 8, 2025
1 parent 769c349 commit 18cd49c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
80 changes: 80 additions & 0 deletions core/modules/simpletest/tests/database_test.test
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ class FakeRecord {

}

/**
* Dummy class that contains a construct method.
*/
class FakeRecordWithConstructor {

/**
* @var string
*/
public $name;

/**
* Constructor for FakeRecordWithConstructor.
*/
public function __construct() {}
}

/**
* Base test class for databases.
*
Expand Down Expand Up @@ -403,6 +419,26 @@ class DatabaseFetchTestCase extends DatabaseTestCase {
$this->assertIdentical(count($records), 1, 'There is only one record.');
}

/**
* Test passing custom constructor_args for a FETCH_CLASS.
*
* @see FakeRecordWithConstructor
*/
public function testQueryFetchClassCustomConstructorArgs() {
$records = array();
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
// @see EntityPlusController::query (in the contrib entity_plus module).
$result->setFetchMode(PDO::FETCH_CLASS, 'FakeRecordWithConstructor', array('foo'));
foreach ($result as $record) {
$records[] = $record;
if ($this->assertTrue($record instanceof FakeRecordWithConstructor, 'Record is an object of class FakeRecordWithConstructor.')) {
$this->assertIdentical($record->name, 'John', '25 year old is John.');
}
}

$this->assertIdentical(count($records), 1, 'There is only one record.');
}

/**
* Confirm that we can fetch a record into a new instance of a custom class.
*
Expand Down Expand Up @@ -4125,3 +4161,47 @@ class ConnectionUnitTest extends BackdropUnitTestCase {
}

}

/**
* Tests database statements against deserialization attacks.
*
* PHPGGC (PHP Generic Gadget Chain) is a tool for creating serialized attack
* strings.
*/
class DatabaseStatementPrefetchGadgetChainTestCase extends BackdropWebTestCase {
/**
* Tests unserialization within the theme registry.
*/
public function testThemeRegistryGadgetChain() {
if (version_compare(PHP_VERSION, '7.4', '<')) {
$this->assert('pass', 'Test skipped for older PHP versions.');
return;
}
$payload = 'O:13:"ThemeRegistry":1:{s:13:"keysToPersist";O:25:"DatabaseStatementPrefetch":3:{s:10:"currentRow";a:0:{}s:10:"fetchStyle";i:8;s:12:"fetchOptions";a:2:{s:5:"class";s:10:"FakeRecord";s:16:"constructor_args";a:1:{i:0;a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}}}}}';
try {
// Do not assign the return value of unserialize as we want the objects
// to be destructed immediately.
unserialize($payload);
} catch (Exception $e) {
$this->assertEqual(get_class($e), 'UnexpectedValueException', get_class($e) . ' thrown when unserializing payload.');
}
}

/**
* Tests unserialization within the UpdateQuery class.
*/
public function testUpdateQueryGadgetChain() {
if (version_compare(PHP_VERSION, '7.4', '<')) {
$this->assert('pass', 'Test skipped for older PHP versions.');
return;
}
$payload = 'O:11:"UpdateQuery":2:{s:10:"connection";O:24:"DatabaseConnection_mysql":0:{}s:6:"fields";O:25:"DatabaseStatementPrefetch":3:{s:10:"currentRow";a:0:{}s:10:"fetchStyle";i:8;s:12:"fetchOptions";a:2:{s:5:"class";s:10:"FakeRecord";s:16:"constructor_args";a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}}}}';
try {
// In this case we can assign the return value; casting it to a string
// invokes the relevant __toString magic method.
$result = (string) unserialize($payload);
} catch (Exception $e) {
$this->assertEqual(get_class($e), 'UnexpectedValueException', get_class($e) . ' thrown when unserializing payload.');
}
}
}
6 changes: 6 additions & 0 deletions core/modules/simpletest/tests/simpletest.tests.info
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,12 @@ description = Tests management of database connections.
group = Database
file = database_test.test

[DatabaseStatementPrefetchGadgetChainTestCase]
name = Database Statement Prefetch Gadget Chain Test
description = Tests protection against unserialization attacks.
group = Database
file = database_test.test

[BackdropErrorHandlerUnitTest]
name = Backdrop error handlers
description = Performs tests on the Backdrop error and exception handler.
Expand Down

0 comments on commit 18cd49c

Please sign in to comment.