Problem/Motivation
The Routing Access report (RoutingAccessComparison::compare() /
RoutingAccessForm) only evaluates routes gated by
_permission, _role, or
_user_is_logged_in. Any route without one of those three
requirements is silently dropped from the report entirely, including:
- Publicly accessible routes using
_access: 'TRUE'(a
statically knowable case that should be reported as accessible to both
roles), and - Routes with genuinely dynamic access checks such as
_entity_accessor_custom_access, which
can't be resolved without a request context.
In an audit tool, silently omitting routes is misleading — an administrator
comparing two roles has no way to know the report is incomplete, or by how
much (on a default Standard install, roughly 169 of the site's routes fall
into this category).
Investigating this also surfaced two related bugs in the same report:
-
Unescaped HTML in
#markup.
RoutingAccessFormandPermissionsFormbuild list
items by concatenating route/permission machine names, paths, and
provider/group names directly into#markupstrings with no
escaping. This is user-visible, not just theoretical: several of Drupal
core's routes have</>in their names
(see next point), and browsers silently swallow the resulting
unrecognized tags, making the affected rows render with a blank title. -
Core's angle-bracket pseudo-routes are compared as if they were
real pages.system.routing.ymldefines
<front>,<none>,
<nolink>,<button>, and
<current>as internal link-generation placeholders, not
navigable pages. Before this fix they were included in the comparison —
combined with the escaping bug above, four of them rendered as blank,
indistinguishable "/" rows in the "shared" column, and
<current>(which has no access requirement at all)
inflated the "not analyzable" count.
Proposed resolution
In RoutingAccessComparison:
- Added
isStaticallyAnalyzable(), checked before evaluating
a route. Routes with none of_permission,_role,
_user_is_logged_in, or_accessare collected into
a newnot_analyzable_routesresult key (route name → title /
path / requirements) instead of being dropped;not_analyzable_count
is derived from it. checkAccess()now resolves_access: 'TRUE'as
accessible to both roles directly, androute_info['requirements']
includes_accesswhen present.compare()now skips any route name starting with
<up front, excluding core's reserved pseudo-routes
(<front>,<none>,
<nolink>,<button>,
<current>) from every part of the comparison.
In RoutingAccessForm:
- The not-analyzable count is merged into the existing disclaimer
message at the top of the report. - A collapsed (
<details>) "N routes not statically
analyzable" section, grouped by module and respecting the module filter,
lists the skipped routes below the three result columns (placed as a
sibling of the flex-based results container, not inside it, so it can't
distort the three-column layout). - All dynamic values (route name, path, requirements string,
provider/group name) are now wrapped inHtml::escape()before
being concatenated into#markup.
In PermissionsForm, the same Html::escape()
treatment was applied to the provider/group name and permission machine
name (permission titles/descriptions were left untouched since they're
already-safe TranslatableMarkup from trusted
hook_permission() implementations).
Test coverage: kernel tests added to
tests/src/Kernel/RoutingAccessComparisonTest.php —
testPublicAccessRoute, testDynamicAccessRouteIsCountedNotDropped,
and testPseudoRoutesAreExcluded — using new synthetic routes
(role_audit_test.public_route,
role_audit_test.dynamic_access) in the
role_audit_test fixture module.
User interface changes
The Routing Access report's disclaimer now states how many routes could not
be statically analyzed, and a new collapsed "N routes not statically
analyzable" section appears below the three comparison columns, listing
those routes grouped by module.
API changes
RoutingAccessComparison::compare()'s return array gains two new
keys: not_analyzable_count (int) and
not_analyzable_routes (array, same shape as
route_info). No existing keys changed shape.
Issue fork role_audit-3608956
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
gedur commented