Skip to content

Fix isPublic plugin/prefix scope leak and hasRole type juggling#165

Merged
dereuromark merged 1 commit into
masterfrom
fix-isPublic-and-hasRole
May 2, 2026
Merged

Fix isPublic plugin/prefix scope leak and hasRole type juggling#165
dereuromark merged 1 commit into
masterfrom
fix-isPublic-and-hasRole

Conversation

@dereuromark
Copy link
Copy Markdown
Owner

Summary

Two authorization-correctness fixes in security-critical code paths.

AclTrait::_isPublic() — asymmetric scope guard

src/Auth/AclTrait.php:317 used:

if ($params['plugin'] && $params['plugin'] !== $rule['plugin']) {
    continue;
}
if (!empty($params['prefix']) && $params['prefix'] !== $rule['prefix']) {
    continue;
}

The first conjunct short-circuits to false when the request has no plugin/prefix, so the rule is not skipped even though it was authored for a specific plugin or prefix. A rule defined for Foo.Tags::index could mark a non-plugin request to Tags::index as public — the canonical AllowTrait::_getAllowRule() (src/Auth/AllowTrait.php:33-50) does not have this asymmetry. The fix mirrors that symmetric guard so a rule is skipped whenever either side has a scope and they do not match.

AuthUserTrait::hasRole() — loose in_array and stray auto-int keys

src/Auth/AuthUserTrait.php:122 called in_array($expectedRole, $roles) without strict mode, so hasRole(0, ['admin', 'moderator']) returned true (loose 0 == 'admin'). The array_key_exists() branch additionally treated auto-int array keys as alias matches, so hasRole(0, [0 => 'admin']) was also a false positive.

The fix coerces both sides to string and compares strictly. Numeric-string / int equivalence (the common DB-ints-vs-Configure-strings case) is preserved on purpose; only string keys count as alias matches; non-scalar inputs return false early.

Tests

  • AuthenticationComponentTest::testIsPublicDoesNotMatchPluginRuleForNonPluginRequest and testIsPublicDoesNotMatchPrefixRuleForNonPrefixRequest lock down the symmetric scope guard.
  • AuthUserComponentTest::testHasRoleRejectsLooseTypeJugglingMatches covers hasRole(0, ['admin']), hasRole('1abc', [1, 2, 3]), null/bool inputs, and confirms numeric-string / int equivalence still works.

phpunit, composer stan, and phpcs src/ tests/ are all clean locally.

AclTrait::_isPublic() (src/Auth/AclTrait.php:317) used asymmetric guards
that short-circuited to false when the request had no plugin or prefix,
so a plugin-scoped or prefix-scoped allow rule would match a request
without one. Mirror the symmetric check from AllowTrait::_getAllowRule()
so a rule is skipped whenever either side has a scope and they do not
match.

AuthUserTrait::hasRole() (src/Auth/AuthUserTrait.php:122) used a loose
in_array() that matched 0 against any non-numeric string and treated
auto-int array keys as alias matches. Coerce both sides to string and
compare strictly while still treating numeric-string and int as the
same role id; only string array keys count as alias matches. Reject
non-scalar inputs early.

Adds regression tests for both code paths.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 2, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 60.00000% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.59%. Comparing base (fae4a8b) to head (bd6c991).

Files with missing lines Patch % Lines
src/Auth/AclTrait.php 40.00% 6 Missing ⚠️
src/Auth/AuthUserTrait.php 80.00% 2 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #165      +/-   ##
============================================
- Coverage     67.68%   67.59%   -0.10%     
- Complexity      621      627       +6     
============================================
  Files            29       29              
  Lines          1600     1614      +14     
============================================
+ Hits           1083     1091       +8     
- Misses          517      523       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dereuromark dereuromark merged commit 919f2d9 into master May 2, 2026
16 checks passed
@dereuromark dereuromark deleted the fix-isPublic-and-hasRole branch May 2, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants