New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable interpolation (mustache-style directives). Vue unusable for apps with user content. #4223

Closed
sebastiaanluca opened this Issue Nov 16, 2016 · 9 comments

Comments

Projects
None yet
7 participants
@sebastiaanluca
Copy link

sebastiaanluca commented Nov 16, 2016

Vue.js version

2.0.5

Reproduction Link

http://codepen.io/anon/pen/pNbQeY

Steps to reproduce

  1. An app built around user content
  2. Vue instance bound to app container
  3. User content contains curly braces ({{ and }})

What is Expected?

Curly braces in non-app, user content are ignored. Especially when encoded.

What is actually happening?

User can crash Vue and thus the application by simply using the curly braces syntax anywhere their input is rendered, i.e. {{ myvalue }} and {{ myvalue }}.

Solutions that did not work

  • Encoding the curly braces; {{ myvalue }}
  • Creating several Vue instances and binding them only to non-user content. Whenever a Vue component needs to render user content, use v-html.
    • This is not a viable solution as it would require a developer to create a Vue instance per Vue component seperately. Each component would then also have to use the v-html syntax, which prevents using third-party, non-Vue code they have no access to.
  • Changing delimiters
    • The problem remains and the user can still crash the app.
  • Disabling delimiters (interpolation)
    • Not supported. Tried delimiters: ['', ''] which causes Vue to render every single element.

Propositions

  • Bring back Vue.config({ interpolate: false }) (#147 (comment)) or a non-global option per Vue instance.
  • A new tag, e.g. v-ignore-delimiters or v-disable-interpolation, to disable {{ myvalue }} rendering inside that HTML tag, but still allow it to be altered using Vue or act as a Vue component.

Related issues

Perhaps I'm completely missing the point, but at the moment, this prevents me from using Vue in my app. I didn't think this would be a major issue, yet it's proven itself to be quite cumbersome to fix or bypass. Not sure how others handle this situation. Would love to get some feedback on this and be able to continue development using Vue.

@qw3n

This comment has been minimized.

Copy link

qw3n commented Nov 16, 2016

If you are using vue why aren't you loading user content through vue. If you place curly braces inside a data property you can output them and there are no problems.

@sebastiaanluca

This comment has been minimized.

Copy link

sebastiaanluca commented Nov 16, 2016

Because all content is loaded and rendered server-side using Laravel and Blade templates. It's not a single page application that's built entirely in JS. I don't even load any data using JS, I'm merely using Vue and its awesome components to optimize the user's experience (client-side).

@yyx990803

This comment has been minimized.

Copy link
Member

yyx990803 commented Nov 16, 2016

@yyx990803 yyx990803 closed this Nov 16, 2016

@sebastiaanluca

This comment has been minimized.

Copy link

sebastiaanluca commented Nov 17, 2016

😱 I feel like such a dumbass now. I've spent hours looking around in those docs. Thank you!

@alexoj

This comment has been minimized.

Copy link

alexoj commented Jan 12, 2017

v-pre also disables attribute parsing though.

i.e. if you have:

<div id="app" v-pre>
<p v-text="2+2"></p>
</div>

It won't show 4.

Since HTML escaping should already be done by all applications a way to disable only moustache interpolation would be nice. I'm currently doing this by setting the delimiters to a long random string but it is sort of hacky.

@IlyaSemenov

This comment has been minimized.

Copy link

IlyaSemenov commented Apr 18, 2017

Clearly, v-pre doesn't really answer the original question (which was "Disable interpolation" and not "Disable parsing").

More importantly, v-pre doesn't allow to use Vue.js on pages where there is user content and Vue.js components at the same time

See, if one uses v-pre on the root element, the entire root element is not parsed, including all Vue.js components, basically making it useless. To allow components to work, v-pre must be used on all user input separately.

But this will not work (in the long run). Template authors will forget to do that. This is a time bomb, very similar to the old times when we used raw PHP3 and had to call htmlspecialchars() on all user input to prevent XSS, but this time it's even worse since we now have "smart" server-side frameworks which emit supposedly "safe" HTML for us (which turns out to be unsafe with Vue.js!)

For example, let's say we use Django forms framework and have Edit User Profile page template:

<form method="post">
  {{ user_form.name }}
  <button>Save</button>
</form>

It will emit <input type="text" value="(escaped user name)">. This works very well until this code runs inside a Vue app.

If a user submits "{{ var }}" (literally) for his name, just for lulz, the HTML code will become <input ... value="{{ var }}">, and the entire page will crash and become empty (with "Interpolation inside attributes has been removed" error).

At the same time, it's very inconvenient to guard each and every surrounding element with v-pre. Sometimes it's not even possible: for example, I might like the button caption to be rendered with Vue depending on the app state.

And of course, if the user's new funny name is saved into the database and then rendered somewhere else with {{ user.name }} (Django code) without explicit v-pre, it will crash Vue.js and show empty page for all other users (with "avoid using JavaScript keyword as property name: "var" error). Bummer!

The real solution is to disable delimiters but keep parsing components

Below is a quick hack that I came up with:

const noDelimiter = {replace: () => '(?!x)x'}; // return a non-matching regexp.

const app = new Vue({
	el: '#vue-app',
	delimiters: [noDelimiter, noDelimiter],
	components: {
		MyComponent,
	},
});

Just in case, pulling app vars will still be possible with v-text.

@alfa-jpn

This comment has been minimized.

Copy link

alfa-jpn commented Jul 25, 2017

@IlyaSemenov

This way is not enough.
Because, it enables interpolation with undefined syntax.

<div id="app">
  undefined message undefined
</div>

<script>
const noDelimiter = {replace: function(){}};

const app = new Vue({
  el: '#app',
  delimiters: [noDelimiter, noDelimiter],
  data: {
    message: 'Hello Vue!'
  }
});
</script>

Result

Hello Vue!

so, I created pr.
#6203

@alfa-jpn alfa-jpn referenced this issue Jul 25, 2017

Closed

fix #4223, add interpolation config. #6203

6 of 13 tasks complete
@IlyaSemenov

This comment has been minimized.

Copy link

IlyaSemenov commented Jul 26, 2017

@alfa-jpn Good point, I didn't pay attention to what happens to the result of the replace call. Since it's simply concatenated to construct a regexp, returning any non-matching regexp should work. I've updated my example above correspondingly.

@devonblzx

This comment has been minimized.

Copy link

devonblzx commented Apr 24, 2018

Is there a specific reason why Vue renders &#123;&#123; myvalue &#125;&#125;? Adding v-pre everywhere may not be the best solution for server-side rendered templates as it's very hard to ensure complete coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment