diff options
author | carson <carson@leaddyno.com> | 2021-01-04 10:36:56 -0700 |
---|---|---|
committer | carson <carson@leaddyno.com> | 2021-01-04 10:36:56 -0700 |
commit | 58f373d1e5a0d10b134d9c2f7cc4d49838a7bcc7 (patch) | |
tree | 80adb36e420e52cfe96aa8f2afcf24b77f7c2482 | |
parent | 3c7dde7e1f4aee87d23bd40cb227a007cabc064c (diff) | |
download | htmx-58f373d1e5a0d10b134d9c2f7cc4d49838a7bcc7.tar.gz htmx-58f373d1e5a0d10b134d9c2f7cc4d49838a7bcc7.zip |
config that disallows `eval` in htmx implementation
addressed https://github.com/bigskysoftware/htmx/issues/305
-rw-r--r-- | src/htmx.js | 29 | ||||
-rw-r--r-- | test/core/api.js | 17 | ||||
-rw-r--r-- | test/core/tokenizer.js | 2 | ||||
-rw-r--r-- | www/api.md | 1 | ||||
-rw-r--r-- | www/docs.md | 1 |
5 files changed, 42 insertions, 8 deletions
diff --git a/src/htmx.js b/src/htmx.js index 56e3495b..55a64ab9 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -42,6 +42,7 @@ return (function () { requestClass:'htmx-request', settlingClass:'htmx-settling', swappingClass:'htmx-swapping', + allowEval:true, attributesToSettle:["class", "style", "width", "height"] }, parseInterval:parseInterval, @@ -248,7 +249,9 @@ return (function () { //========================================================================================== function internalEval(str){ - return eval(str); + return maybeEval(getDocument().body, function () { + return eval(str); + }); } function onLoadHelper(callback) { @@ -735,7 +738,7 @@ return (function () { last !== "."; } - function maybeGenerateConditional(tokens, paramName) { + function maybeGenerateConditional(elt, tokens, paramName) { if (tokens[0] === '[') { tokens.shift(); var bracketCount = 1; @@ -752,7 +755,10 @@ return (function () { tokens.shift(); conditionalSource += ")})"; try { - var conditionFunction = Function(conditionalSource)(); + var conditionFunction = maybeEval(elt,function () { + return Function(conditionalSource)(); + }, + function(){return true}) conditionFunction.source = conditionalSource; return conditionFunction; } catch (e) { @@ -800,7 +806,7 @@ return (function () { triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)}); } else { var triggerSpec = {trigger: trigger}; - var eventFilter = maybeGenerateConditional(tokens, "event"); + var eventFilter = maybeGenerateConditional(elt, tokens, "event"); if (eventFilter) { triggerSpec.eventFilter = eventFilter; } @@ -1215,7 +1221,9 @@ return (function () { function evalScript(script) { if (script.type === "text/javascript" || script.type === "") { try { - Function(script.innerText)(); + maybeEval(script, function () { + Function(script.innerText)() + }); } catch (e) { logError(e); } @@ -1803,9 +1811,18 @@ return (function () { return getValuesForElement(parentElt(elt), attr, strToValues, expressionVars); } + function maybeEval(elt, toEval, defaultVal) { + if (htmx.config.allowEval) { + return toEval(); + } else { + triggerErrorEvent(elt, 'htmx:evalDisallowedError'); + return defaultVal; + } + } + function getHXVarsForElement(elt, expressionVars) { return getValuesForElement(elt, "hx-vars", function(valueStr){ - return Function("return (" + valueStr + ")")() + return maybeEval(elt,function () {return Function("return (" + valueStr + ")")();}, {}); }, expressionVars); } diff --git a/test/core/api.js b/test/core/api.js index bebb0b7a..856a7ac1 100644 --- a/test/core/api.js +++ b/test/core/api.js @@ -246,5 +246,20 @@ describe("Core htmx API test", function(){ } }); + it('eval can be suppressed', function () { + var calledEvent = false; + var handler = htmx.on("htmx:evalDisallowedError", function(){ + calledEvent = true; + }); + try { + htmx.config.allowEval = false; + should.equal(htmx._("tokenizeString"), undefined); + } finally { + htmx.config.allowEval = true; + htmx.off("htmx:evalDisallowedError", handler); + } + calledEvent.should.equal(true); + }); + - }) +}) diff --git a/test/core/tokenizer.js b/test/core/tokenizer.js index 30dce2fb..3071ad9b 100644 --- a/test/core/tokenizer.js +++ b/test/core/tokenizer.js @@ -34,7 +34,7 @@ describe("Core htmx tokenizer tests", function(){ it('generates conditionals property', function() { var tokens = tokenize("[code==4||(code==5&&foo==true)]"); - var conditional = htmx._("maybeGenerateConditional")(tokens); + var conditional = htmx._("maybeGenerateConditional")(null, tokens); var func = eval(conditional); func({code: 5, foo: true}).should.equal(true); func({code: 5, foo: false}).should.equal(false); @@ -95,6 +95,7 @@ Note that using a [meta tag](/docs/#config) is the preferred mechanism for setti * `requestClass:'htmx-request'` - string: the class to place on triggering elements when a request is in flight * `settlingClass:'htmx-settling'` - string: the class to place on target elements when htmx is in the settling phase * `swappingClass:'htmx-swapping'` - string: the class to place on target elements when htmx is in the swapping phase +* `allowEval:true` - boolean: allows the use of eval-like functionality in htmx, to enable `hx-vars`, trigger conditions & script tag evaluation. Can be set to `false` for CSP compatibility ##### Example diff --git a/www/docs.md b/www/docs.md index 6541f121..d9ad2c2e 100644 --- a/www/docs.md +++ b/www/docs.md @@ -817,6 +817,7 @@ Htmx allows you to configure a few defaults: | `htmx.config.requestClass` | defaults to `htmx-request` | `htmx.config.settlingClass` | defaults to `htmx-settling` | `htmx.config.swappingClass` | defaults to `htmx-swapping` +| `htmx.config.allowEval` | defaults to `true` </div> |