VYPR
High severityNVD Advisory· Published Jul 14, 2023· Updated Oct 18, 2024

Limited code execution in zenstruck/collections

CVE-2023-37473

Description

A callable string injection vulnerability in zenstruck/collections allows arbitrary function execution via user input passed to EntityRepository::find() or query().

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A callable string injection vulnerability in zenstruck/collections allows arbitrary function execution via user input passed to EntityRepository::find() or query().

Vulnerability

Overview

The zenstruck/collections library, a set of helpers for iterating, paginating, and filtering collections, contains a callable string injection vulnerability. The EntityRepository::find() and query() methods accept a $specification parameter that can be a callable string (e.g., "system"). When such a string is passed, the library executes it as a PHP callable, leading to arbitrary code execution [1][4].

Exploitation

An attacker can exploit this by providing a callable string as user input to the affected methods. The commit f4b1c48820 shows that the fix adds a check to ensure the callable is an object, preventing string-based callables from being executed [2]. The vulnerability is triggered when user-supplied data reaches find() or query() without proper sanitization [4].

Impact

Successful exploitation allows an attacker to execute arbitrary PHP functions, such as system, which can lead to remote code execution and full compromise of the application server [1][4].

Mitigation

The issue is fixed in version 0.2.1 of the library [3]. Users are advised to upgrade immediately. As a workaround, ensure that user input is never passed to EntityRepository::find() or query() [1].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
zenstruck/collectionPackagist
< 0.2.10.2.1

Affected products

2

Patches

1
f4b1c488206e

security(orm): prevent passing callable strings to `find()`/`query()`

https://github.com/zenstruck/collectionKevin BondJul 14, 2023via ghsa
3 files changed · +29 6
  • src/Collection/Doctrine/ORM/EntityRepositoryBridge.php+2 2 modified
    @@ -25,7 +25,7 @@ trait EntityRepositoryBridge
         private EntityRepository $collectionRepo;
     
         /**
    -     * @param mixed|Criteria|array<string,mixed>|callable(QueryBuilder):void $specification
    +     * @param mixed|Criteria|array<string,mixed>|(object&callable(QueryBuilder):void) $specification
          */
         public function find($specification, $lockMode = null, $lockVersion = null): ?object
         {
    @@ -38,7 +38,7 @@ public function find($specification, $lockMode = null, $lockVersion = null): ?ob
         }
     
         /**
    -     * @param Criteria|array<string,mixed>|callable(QueryBuilder):void $specification
    +     * @param Criteria|array<string,mixed>|(object&callable(QueryBuilder):void) $specification
          *
          * @return EntityResult<V>
          */
    
  • src/Collection/Doctrine/ORM/EntityRepository.php+4 4 modified
    @@ -33,7 +33,7 @@ public function __construct(private EntityManagerInterface $em, private string $
         }
     
         /**
    -     * @param mixed|Criteria|array<string,mixed>|callable(QueryBuilder):void $specification
    +     * @param mixed|Criteria|array<string,mixed>|(object&callable(QueryBuilder):void) $specification
          */
         public function find(mixed $specification): ?object
         {
    @@ -46,7 +46,7 @@ public function find(mixed $specification): ?object
                     return $this->em()->getUnitOfWork()->getEntityPersister($this->class)->load($specification, limit: 1); // @phpstan-ignore-line
                 }
     
    -            if (\is_callable($specification)) {
    +            if (\is_callable($specification) && \is_object($specification)) {
                     $specification($qb = $this->qb(), 'e');
     
                     return $qb->getQuery()->getSingleResult();
    @@ -59,7 +59,7 @@ public function find(mixed $specification): ?object
         }
     
         /**
    -     * @param Criteria|array<string,mixed>|callable(QueryBuilder):void $specification
    +     * @param Criteria|array<string,mixed>|(object&callable(QueryBuilder):void) $specification
          *
          * @return EntityResult<V>
          */
    @@ -71,7 +71,7 @@ public function query(mixed $specification): EntityResult
                 return $qb->addCriteria($specification)->result();
             }
     
    -        if (\is_callable($specification)) {
    +        if (\is_callable($specification) && \is_object($specification)) {
                 $specification($qb, 'e');
     
                 return $qb->result();
    
  • tests/Doctrine/ORM/EntityRepositoryTest.php+23 0 modified
    @@ -155,6 +155,29 @@ public function can_filter_with_callable(): void
             $this->assertSame('value 2', \iterator_to_array($objects)[0]->value);
         }
     
    +    /**
    +     * @test
    +     */
    +    public function cannot_find_with_callable_strings(): void
    +    {
    +        $this->assertIsCallable('system');
    +        $this->assertNull($this->repo()->find('system'));
    +    }
    +
    +    /**
    +     * @test
    +     */
    +    public function cannot_query_with_callable_strings(): void
    +    {
    +        $this->assertIsCallable('system');
    +
    +        $repo = $this->repo();
    +
    +        $this->expectException(\InvalidArgumentException::class);
    +
    +        $repo->query('system');
    +    }
    +
         /**
          * @test
          */
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.