bigskysoftware/htmx

Data URLs do not work when selfRequestsOnly is enabled.

nedaras opened this issue · 3 comments

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>

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))
    }

That is nice.

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();
    }
  });