bigskysoftware / htmx

</> htmx - high power tools for HTML
https://htmx.org
Other
37.39k stars 1.26k forks source link

Data URLs do not work when selfRequestsOnly is enabled. #2828

Open nedaras opened 1 month ago

nedaras commented 1 month ago

Code below will throw htmx:invalidPath when htmx.config.selfRequestsOnly is set to true.

<button
  hx-get="data:text/plain,"
  hx-swap="outerHTML"
>
  Delete me.
</button>
MichaelWest22 commented 1 month ago

to allow custom control of the url validation see https://htmx.org/events/#htmx:validateUrl

You can for example set the selfRequestsOnly to false which will allow any url to be accessed and not just local server requests only. And then you can add the below event listener script to run on the validate url htmx event and lock your requests down to just the URL's you want to keep your site more secure.

  document.body.addEventListener('htmx:validateUrl', function (evt) {
    // only allow requests to the current server or data:*
    if (!evt.detail.sameHost && evt.detail.url.href.indexOf('data:') !== 0) {
      evt.preventDefault();
    }
  });

If they wanted to fix this in htmx and make all data:* urls treated as sameHost internally then the change required would be to add this:

 if (startsWith(path, 'data:')) {
        sameHost = true
      }

To the verifyPath function:

    function verifyPath(elt, path, requestConfig) {
      let sameHost
      let url
      if (typeof URL === 'function') {
        url = new URL(path, document.location.href)
        const origin = document.location.origin
        sameHost = origin === url.origin
      } else {
      // IE11 doesn't support URL
        url = path
        sameHost = startsWith(path, document.location.origin)
      }
      if (startsWith(path, 'data:')) {
        sameHost = true
      }

      if (htmx.config.selfRequestsOnly) {
        if (!sameHost) {
          return false
        }
      }
      return triggerEvent(elt, 'htmx:validateUrl', mergeObjects({ url, sameHost }, requestConfig))
    }
nedaras commented 3 weeks ago

That is nice.

MichaelWest22 commented 3 weeks ago

Sorry looks like I made a mistake in my example handler as I had === 0 when it should have been !==0

The correct script would be

  document.body.addEventListener('htmx:validateUrl', function (evt) {
    // only allow requests to the current server or data:*
    if (!evt.detail.sameHost && evt.detail.url.href.indexOf('data:') !== 0) {
      evt.preventDefault();
    }
  });