diff --git a/src/htmx.js b/src/htmx.js
index 005da3d7..8f592893 100644
--- a/src/htmx.js
+++ b/src/htmx.js
@@ -73,6 +73,7 @@ return (function () {
getCacheBusterParam: false,
globalViewTransitions: false,
methodsThatUseUrlParams: ["get"],
+ selfRequestsOnly: false
},
parseInterval:parseInterval,
_:internalEval,
@@ -2844,6 +2845,18 @@ return (function () {
return arr;
}
+ function verifyPath(elt, path, requestConfig) {
+ var url = new URL(path, document.location.href);
+ var hostname = document.location.hostname;
+ var sameHost = hostname !== url.hostname;
+ if (htmx.config.selfRequestsOnly) {
+ if (sameHost) {
+ return false;
+ }
+ }
+ return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig));
+ }
+
function issueAjaxRequest(verb, path, elt, event, etc, confirmed) {
var resolve = null;
var reject = null;
@@ -3072,6 +3085,11 @@ return (function () {
}
}
+ if (!verifyPath(elt, finalPath, requestConfig)) {
+ triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig)
+ return;
+ };
+
xhr.open(verb.toUpperCase(), finalPath, true);
xhr.overrideMimeType("text/html");
xhr.withCredentials = requestConfig.withCredentials;
diff --git a/test/core/security.js b/test/core/security.js
index d28da83f..3c95ddf9 100644
--- a/test/core/security.js
+++ b/test/core/security.js
@@ -6,7 +6,6 @@ describe("security options", function() {
});
afterEach(function() {
this.server.restore();
- clearWorkArea();
});
it("can disable a single elt", function(){
@@ -105,4 +104,50 @@ describe("security options", function() {
this.server.respond();
btn.innerHTML.should.equal("Clicked a second time");
})
+
+ it("can make egress cross site requests when htmx.config.selfRequestsOnly is enabled", function(done){
+ htmx.logAll()
+ // should trigger send error, rather than reject
+ var listener = htmx.on("htmx:sendError", function (){
+ htmx.off("htmx:sendError", listener);
+ done();
+ });
+ this.server.restore(); // use real xhrs
+ // will 404, but should respond
+ var btn = make('')
+ btn.click();
+ })
+
+ it("can't make egress cross site requests when htmx.config.selfRequestsOnly is enabled", function(done){
+ htmx.logAll()
+ // should trigger send error, rather than reject
+ htmx.config.selfRequestsOnly = true;
+ var listener = htmx.on("htmx:invalidPath", function (){
+ htmx.config.selfRequestsOnly = false;
+ htmx.off("htmx:invalidPath", listener);
+ done();
+ })
+ this.server.restore(); // use real xhrs
+ // will 404, but should respond
+ var btn = make('')
+ btn.click();
+ })
+
+ it("can cancel egress request based on htmx:validateUrl event", function(done){
+ htmx.logAll()
+ // should trigger send error, rather than reject
+ var pathVerifier = htmx.on("htmx:validateUrl", function (evt){
+ evt.preventDefault();
+ htmx.off("htmx:validateUrl", pathVerifier);
+ })
+ var listener = htmx.on("htmx:invalidPath", function (){
+ htmx.config.selfRequestsOnly = false;
+ htmx.off("htmx:invalidPath", listener);
+ done();
+ })
+ this.server.restore(); // use real xhrs
+ // will 404, but should respond
+ var btn = make('')
+ btn.click();
+ })
});
\ No newline at end of file
diff --git a/www/content/docs.md b/www/content/docs.md
index 52a461ff..4ca408b0 100644
--- a/www/content/docs.md
+++ b/www/content/docs.md
@@ -1462,30 +1462,101 @@ to generate a different `ETag` for each content.
## Security
-htmx allows you to define logic directly in your DOM. This has a number of advantages, the
-largest being [Locality of Behavior](@/essays/locality-of-behaviour.md) making your system
-more coherent.
+htmx allows you to define logic directly in your DOM. This has a number of advantages, the largest being
+[Locality of Behavior](@/essays/locality-of-behaviour.md), making your system more coherent.
-One concern with this approach, however, is security. This is especially the case if you are injecting user-created
-content into your site without any sort of HTML escaping discipline.
+One concern with this approach, however, is security: since htmx increases the expressiveness of HTML, if a malicious
+user is able to inject HTML into your application they can leverage this expressiveness.
-You should, of course, escape all 3rd party untrusted content that is injected into your site to prevent, among other issues, [XSS attacks](https://en.wikipedia.org/wiki/Cross-site_scripting). Attributes starting with `hx-` and `data-hx`, as well as inline `