Background information

This was originally reported as a private security issue, but has been approved for handling in the public queue by the Drupal Security Team.

Problem/Motivation

If Drupal is installed in a /drupal sub-folder, this is not allowed:

<a class="use-ajax" href="/foo">Click me</a>

But this will work:

<a data-hx-get="/foo">HTMX</a>

That's because Drupal.url.isLocal() uses drupalSettings.path.baseUrl but isLocalLink() does not.

We should prevent Drupal from making AJAX requests to another app in another folder.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3601068

Command icon 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

prudloff created an issue. See original summary.

deruina made their first commit to this issue’s fork.

deruina’s picture

Status: Active » Needs review

Opened an MR. With htmx 4 (after #3635924), Drupal's drupal-assets extension already calls Drupal.url.isLocal() in htmx_config_request, but only to skip adding ajax_page_state. The request was still sent. Now it cancels the request (returning false from the hook cancels it in htmx 4) and reports it with Drupal.throwError(), using the same message as ajax.js.

Cross-origin requests already fail because htmx uses fetch mode same-origin, so the practical change is same-host URLs outside the base path, like /other-app/ when Drupal is in /drupal/.

I checked it in headless Chrome with the real drupal.js, htmx.js and htmx-assets.js and baseUrl /drupal/. Before: hx-get="/other-app/endpoint" and "/drupal/../other-app/x" were both sent and swapped in. After: neither reaches the server, and the local one still works.

Added a Nightwatch case to htmxTest.js. It adds buttons for a local URL, an external URL and, when installed in a sub-folder like on CI, a same-host URL outside Drupal, clicks them, and checks that only the local one fired htmx:before:request. I couldn't run the full Nightwatch suite locally. I ran the same test code in my harness, where it passes with the change and fails without it, so CI will be its first real run.

One behavior change to flag: code that used hx-get with a non-Drupal URL on the same host on purpose is now blocked, same as with ajax.js. drupalSettings.ajaxTrustedUrl isn't checked here; I can add that if it's needed.

smustgrave’s picture

deruina’s picture

Fair point. I missed the AI disclosure here too. It's in the MR description now (more on #3590068). If anything in the change is unclear, I'll explain.

fathershawn’s picture

Issue tags: +HTMX initiative