Problem/Motivation
RestMenuItemsResource::getElementValue() calls $url->toString(TRUE)->getGeneratedUrl() in four places, discarding the GeneratedUrl wrapper that carries bubbleable cacheability metadata.
When a menu item targets a CSRF-protected route (notably user.logout), the URL's cache metadata is lost. REST menu responses are cached without it, leading to cross-user leakage of session-specific tokens for same-role users.
Steps to reproduce
- Fresh Drupal 11.x install with
rest_menu_items3.0.x and the corerestmodule enabled. - At
/admin/config/services/rest, enable therest_menu_itemresource forGETwithjsonformat and
cookieauthentication. - Grant
access GET on Menu items per menu resourceto any authenticated role. - Create two users (User A, User B) and assign both the same authenticated role — the default
authenticatedrole is enough, since the
accountmenu already exposesuser.logoutto all authenticated users. - In one browser session, log in as User A. Request
GET /api/menu_items/account. In the JSON response, note therelativefield of the
entry whosekeyisuser.logout— it contains?token=<TOKEN_A>. - In a separate browser session (or curl with a different cookie jar), log in as User B. Request
GET /api/menu_items/account. - Compare the
?token=...value in both responses.
Expected: tokens differ — each user has their own session-specific token.
Actual: tokens are identical. User B's response is the cached copy of User A's. Response headers confirm: x-drupal-dynamic-cache: HIT
on User B's request, and x-drupal-cache-contexts lists user.permissions only — no session.
Proposed resolution
Capture the GeneratedUrl wrapper at each call site and merge its bubbleable cacheability into a per-request accumulator; attach the accumulator to the response in get().
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #5 | 3587299-3-propagate-url-bubbleable-metadata-3.0.5.patch | 3.89 KB | petar_basic |
| #3 | 3587299-2-propagate-url-bubbleable-metadata.patch | 4 KB | petar_basic |
Issue fork rest_menu_items-3587299
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 #3
petar_basic commentedMR + patch attached. Captures the GeneratedUrl returned by each ->toString(TRUE) call site and merges its bubbleable cacheability onto the response.
End-to-end fix also needs core issue https://www.drupal.org/project/drupal/issues/3587298 — verified both together in a two-user reproduction on a vanilla install.
Comment #4
petar_basic commentedComment #5
petar_basic commentedPatch against 3.0.5
Comment #6
fagoI reviewed the fix along-side with the core fix. Fix makes sense and works as it should, MR is good also, we should make sure this metadata is preserved.
I've tested it to work correctly also, all ready!
Comment #8
batigolixThanks for helping out