Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/private/Command/CallableJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

class CallableJob extends QueuedJob {
protected function run($serializedCallable) {
$callable = \unserialize($serializedCallable);
// Restrict to prevent PHP Object Injection; arbitrary PHP objects cannot be callables.
$callable = \unserialize($serializedCallable, ['allowed_classes' => false]);
if (\is_callable($callable)) {
$callable();
} else {
Expand Down
6 changes: 5 additions & 1 deletion lib/private/Command/ClosureJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@

class ClosureJob extends QueuedJob {
protected function run($serializedCallable) {
$serializedClosure = \unserialize($serializedCallable);
// Use allowed_classes => true: SerializableClosure's serialized form nests internal
// classes (e.g. Laravel\SerializableClosure\Serializers\Native) that would be blocked
// by a strict allow-list, silently producing __PHP_Incomplete_Class. The existing
// method_exists('getClosure') guard below prevents execution of any non-closure object.
$serializedClosure = \unserialize($serializedCallable, ['allowed_classes' => true]);
if (\method_exists($serializedClosure, 'getClosure')) {
$callable = $serializedClosure->getClosure();
if (\is_callable($callable)) {
Expand Down
7 changes: 6 additions & 1 deletion lib/private/Command/CommandJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
*/
class CommandJob extends QueuedJob {
protected function run($serializedCommand) {
$command = \unserialize($serializedCommand);
// allowed_classes => true: PHP's allowed_classes performs exact class-name
// matching and does not resolve interface hierarchies. Passing ICommand::class
// (an interface) would silently produce __PHP_Incomplete_Class for every real
// payload. The existing instanceof check below still ensures only valid
// ICommand objects are handled.
$command = \unserialize($serializedCommand, ['allowed_classes' => true]);
if ($command instanceof ICommand) {
$command->handle();
} else {
Expand Down
7 changes: 6 additions & 1 deletion lib/private/Command/QueueBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ private function runCommand($command) {
if (\strlen($serialized) > 4000) {
throw new \InvalidArgumentException('Trying to push a command which serialized form can not be stored in the database (>4000 character)');
}
$unserialized = \unserialize($serialized);
// allowed_classes => true: PHP's allowed_classes does not resolve interface
// hierarchies, so passing ICommand::class (an interface) would produce
// __PHP_Incomplete_Class. The serialized value was just produced from
// $command above in this same method — it is a pure in-memory round-trip
// and not an external attack surface.
$unserialized = \unserialize($serialized, ['allowed_classes' => true]);
$unserialized->handle();
} else {
$command();
Expand Down