squirrellyjs / squirrelly

Semi-embedded JS template engine that supports helpers, filters, partials, and template inheritance. 4KB minzipped, written in TypeScript ⛺
https://squirrelly.js.org
MIT License
571 stars 81 forks source link

Values on Pipes get stringified before passed to filter function #189

Closed jase88 closed 4 years ago

jase88 commented 4 years ago

Describe the bug data types like Arrays get stringified, before passed to the corresponding filter function.

example:

filters.define('length', (array) => {
  return Array.isArray(array) ? array.length : 0;
});

render('{{it.names | length}} names', { names: ['A', 'B', 'C'] });

To Reproduce Steps to reproduce the behavior:

  1. create filter with the name 'length' and set a breakpoint at it
  2. call render function with the given filter 'length' on an array
  3. Look at the data type on the filter function

Expected behavior Plain value is passed through to the filter function, in this case the original array

Screenshots image

Package & Environment Details

nebrelbug commented 4 years ago

Hi @kerosin! The problem here is that, by default, Squirrelly will first pass the value through a filter that XML-escapes it. By default, that filter converts its values to strings.

So, in pseudo-code, {{it.names | length}} would be parsed roughly into length(autoEscape(it.names))

One simple way to solve this problem would be to disable auto-escaping on that value. Examples: {{* it.names | length}} or {{it.names | safe | length}}.

Ideally, Squirrelly would only auto-escape strings. However, JavaScript displays arrays as strings, so displaying an array would leave you vulnerable to XSS.

nebrelbug commented 4 years ago

After explaining this, though, I realized I should probably just make autoEscape the last filter -- so {{it.names | length}} would be parsed into autoEscape(length(it.names)).

I'll need to do a bit more testing to make sure this wouldn't break anything. In the meantime, either of the solutions above will hopefully work :)

Thanks for the questions!

jase88 commented 4 years ago

Thanks for your explanation 😃 I think it makes sense to escape as early as possible - that should be fine.

{{it.names | safe | length}} worked for me

Thank you for your great template engine! I will close this on