summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorcarson <carson@leaddyno.com>2021-01-04 10:36:56 -0700
committercarson <carson@leaddyno.com>2021-01-04 10:36:56 -0700
commit58f373d1e5a0d10b134d9c2f7cc4d49838a7bcc7 (patch)
tree80adb36e420e52cfe96aa8f2afcf24b77f7c2482
parent3c7dde7e1f4aee87d23bd40cb227a007cabc064c (diff)
downloadhtmx-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.js29
-rw-r--r--test/core/api.js17
-rw-r--r--test/core/tokenizer.js2
-rw-r--r--www/api.md1
-rw-r--r--www/docs.md1
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);
diff --git a/www/api.md b/www/api.md
index 4b6430c2..fbf8acdc 100644
--- a/www/api.md
+++ b/www/api.md
@@ -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>