Skip to content

Force strict equivalence in mongo role checks#834

Merged
abnegate merged 1 commit intomainfrom
fix-mongo-perms
Mar 12, 2026
Merged

Force strict equivalence in mongo role checks#834
abnegate merged 1 commit intomainfrom
fix-mongo-perms

Conversation

@abnegate
Copy link
Member

@abnegate abnegate commented Mar 12, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Refined MongoDB database permission filtering to enforce stricter authorization validation checks in find, count, and sum operations. The update applies more restrictive permission matching criteria, potentially affecting which documents are accessible in database queries. Users with specific permission configurations may notice changes in query result sets.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

The MongoDB adapter's permission-filtering methods undergo regex pattern refinement. The find, count, and sum methods transition from a flexible wildcard-based role-matching pattern to a more restrictive exact-match pattern, changing the authorization filter expressions accordingly.

Changes

Cohort / File(s) Summary
MongoDB Permission Filter Tightening
src/Database/Adapter/Mongo.php
Refines role-based permission regex in three methods (find, count, sum) from read(".*(?:roles).*") to read("(?:roles)"), implementing stricter authorization matching logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop, hop, the filters grow tight,
Roles matched with precision, not loose and light,
From wildcard ways to exact, precise sight,
Security strengthens—permissions done right! 🔐

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Force strict equivalence in mongo role checks' accurately describes the main change: tightening Mongo permission filtering by replacing flexible regex matching with exact role matching.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-mongo-perms

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Database/Adapter/Mongo.php`:
- Line 2029: The permission regex is built from raw role strings so
metacharacters in roles can break matching; update the code that builds $roles
(used in new Regex("{$forPermission}\\(\"(?:{$roles})\"\\)", 'i')) to escape
each role with preg_quote (specifying the regex delimiter, e.g., '/') before
imploding with '|' (e.g., $roles = implode('|', array_map(fn($r) =>
preg_quote($r, '/'), $this->authorization->getRoles()))), and apply the same
change in the count() and sum() branches where the identical Regex construction
occurs so all three usages (find/count/sum) use escaped role values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fe44fb97-3b41-4dfa-9118-e8a23ca61ff2

📥 Commits

Reviewing files that changed from the base of the PR and between 4920bb6 and 6d50ac1.

📒 Files selected for processing (1)
  • src/Database/Adapter/Mongo.php

if ($this->authorization->getStatus()) {
$roles = \implode('|', $this->authorization->getRoles());
$filters['_permissions']['$in'] = [new Regex("{$forPermission}\\(\".*(?:{$roles}).*\"\\)", 'i')];
$filters['_permissions']['$in'] = [new Regex("{$forPermission}\\(\"(?:{$roles})\"\\)", 'i')];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check the exact code at the three mentioned lines
sed -n '2025,2035p' src/Database/Adapter/Mongo.php

Repository: utopia-php/database

Length of output: 1882


🏁 Script executed:

# Check lines 2290 and 2380
sed -n '2285,2295p' src/Database/Adapter/Mongo.php
sed -n '2375,2385p' src/Database/Adapter/Mongo.php

Repository: utopia-php/database

Length of output: 1882


🏁 Script executed:

# Check the Permission and Role helper classes to understand toString() behavior
cat src/Database/Helpers/Permission.php

Repository: utopia-php/database

Length of output: 7107


🏁 Script executed:

# Check Role.php
cat src/Database/Helpers/Role.php

Repository: utopia-php/database

Length of output: 4142


🏁 Script executed:

# Search for how getRoles() is implemented to understand what values it returns
rg -A 5 "function getRoles" src/

Repository: utopia-php/database

Length of output: 409


Escape role values before interpolating them into these permission regexes.

This is not a strict-equivalence check. Permission::toString() stores permission("role") strings where the role contains identifiers and dimensions appended verbatim (e.g., user:identifier/dimension). Since getRoles() returns raw role strings and the regex is built via implode('|', $this->authorization->getRoles()) without escaping, any regex metacharacters in identifiers or dimensions will be interpreted as regex syntax rather than literal characters. This causes over-matching or under-matching in find(), count(), and sum().

🔧 Suggested fix
-            $roles = \implode('|', $this->authorization->getRoles());
-            $filters['_permissions']['$in'] = [new Regex("{$forPermission}\\(\"(?:{$roles})\"\\)", 'i')];
+            $roles = \implode('|', \array_map(
+                static fn (string $role): string => \preg_quote($role, '/'),
+                $this->authorization->getRoles()
+            ));
+            $permission = \preg_quote($forPermission, '/');
+            $filters['_permissions']['$in'] = [new Regex("{$permission}\\(\"(?:{$roles})\"\\)", 'i')];

Apply the same escaping in the count() and sum() branches as well.

Also applies to: 2290–2290, 2380–2380

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Database/Adapter/Mongo.php` at line 2029, The permission regex is built
from raw role strings so metacharacters in roles can break matching; update the
code that builds $roles (used in new
Regex("{$forPermission}\\(\"(?:{$roles})\"\\)", 'i')) to escape each role with
preg_quote (specifying the regex delimiter, e.g., '/') before imploding with '|'
(e.g., $roles = implode('|', array_map(fn($r) => preg_quote($r, '/'),
$this->authorization->getRoles()))), and apply the same change in the count()
and sum() branches where the identical Regex construction occurs so all three
usages (find/count/sum) use escaped role values.

@abnegate abnegate merged commit 503f13d into main Mar 12, 2026
18 checks passed
@abnegate abnegate deleted the fix-mongo-perms branch March 12, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant