Ref: SECBUG-4408.
Summary
MongoLock::acquire() builds an aggregation pipeline update where the caller-supplied owner string is embedded raw into $eq and $cond expressions (src/Cache/MongoLock.php:52 and :63). Any owner value starting with $ is evaluated by MongoDB as a field-path expression. Passing owner = "$owner" turns the ownership check into $eq: ['$owner', '$owner'], a tautology, so the pipeline overwrites owner and expires_at on a lock the caller does not own.
Impact
An application that plumbs untrusted input into the owner parameter of Cache::lock($name, $ttl, $owner) or restoreLock($name, $owner) lets an attacker hijack or force-expire arbitrary locks in the tenant's own cache-lock collection. Breaks mutual exclusion; no RCE, no cross-tenant, no arbitrary reads.
Fix
Prefer wrapping the owner value inside
{ $literal: <owner> }in the pipeline stages built by acquire(), so a $-prefixed string is stored and compared as a literal instead of being evaluated. Alternatively, or in addition, reject $-prefixed values in the constructor to fail fast.
Locations:
- src/Cache/MongoLock.php:52 ($eq ownership check)
- src/Cache/MongoLock.php:63 ($cond then-value for owner)
- src/Cache/MongoLock.php:31 (constructor accepts any ?string unchecked)
- src/Cache/MongoStore.php:59,74 (forward caller-supplied owner)
Add regression tests covering owner strings starting with $ (e.g. "$owner", "$literal"), including the acquire, restoreLock, and getCurrentOwner paths. Backport to supported branches.