Skip to content
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

Where is Ember.Handlebars.Utils.escapeExpression in the @ember module system? #16817

Closed
peabnuts123 opened this issue Jul 12, 2018 · 21 comments
Closed

Comments

@peabnuts123
Copy link

Similar to the last comment in #9874, how does one access Ember.Handlebars.Utils.escapeExpression in a modern way, without importing all of ember? I need to escape HTML in my app (because I am decorating some user-input with markup) before marking it as htmlSafe() and can't find any way of doing so without writing my own escaping function or importing some kind of addon.

@pixelhandler
Copy link
Contributor

@peabnuts123 Have you found an answer yet? Did asking this on the ember community slack or forum help you get any answers to your question?

I found some dialog on this post, https://medium.com/@bradleyscollins/how-does-one-access-ember-handlebars-utils-escapeexpression-ebbe33093186

@peabnuts123
Copy link
Author

No, I never did find a satisfactory answer, though I did not post in the Slack or Forum.

I currently have the following in my component

import Ember from 'ember';
const htmlEscape = Ember.Handlebars.Utils.escapeExpression;

so that in the future I may import htmlEscape properly and remove these lines

@karthiicksiva
Copy link
Contributor

karthiicksiva commented Aug 27, 2018

@peabnuts123
Copy link
Author

Yea, nice links - I had found the same thread in my travels. I think the idea for the future is to import it from Handlebars directly e.g.

import { htmlEscape } from "Handlebars";

or similar. This has the unfortunate downside of relying on Handlebars to export these functionalities themselves via the module API, which I think we're just kind of "waiting on" at this point

@panthony
Copy link

panthony commented Dec 31, 2018

Trying to upgrade an addon to a more recent version of Ember I stumbled upon this issue.

Since we cannot rely on global Ember variable and Handlebars is not a valid module, how are we supposed to migrate this particular piece of code?

import Ember from 'ember';
const htmlEscape = Ember.Handlebars.Utils.escapeExpression;

@Redsandro
Copy link

We need this.

Right now you need to either/or:

@rwjblue said:

Closing this for now. If we need to add these API's to HTMLBars in the future, it will be simple.

What about now? Something possible that I should know about?

@jherdman
Copy link
Contributor

jherdman commented May 9, 2019

Is there a resolution on this? It feels like a quintessential util for people who need to process text.

@Alonski
Copy link
Member

Alonski commented Dec 29, 2019

@rwjblue Any thoughts? :)

@MelSumner
Copy link
Contributor

Folks please see the comment that closed the related PR:

After discussing with the core team today- we've decided to not move forward with this PR because Ember doesn't use it internally, and hasn't for quite some time now- it was always an artifact from Handlebars itself.

The work for the module RFC removed non-Ember re-exports, and authors were advised to import what they need directly from any particular non-Ember module (e.g., rsvp or jQuery). This is still our position.

If you feel like you still have a use case, please submit an RFC.

@peabnuts123
Copy link
Author

I am happy with this position / this has been my understanding throughout this issue.

If people find this issue in the future still wanting to access this function - you will need access it from the Handlebars package directly. However, I think they still have not structured their package as a module i.e. you may need to import all of Handlebars and then use Handlebars.Utils.escapeExpression.

import Handlebars from 'handlebars';
import { htmlSafe } from '@ember/template';

let userInput = getUserInput(); // Something unsafe, e.g. user-controlled text
let escapedInput = Handlebars.Utils.escapeExpression(userInput); // Escape any HTML in this string so that it will render as <, & etc.
let alteredHtml = escapedInput.replace(/hello/g, '<span class="highlight">hello</span>'); // Add some HTML to the text
let safeHtml = htmlSafe(alteredHtml);

// safeHtml is now safe to render in the DOM

@Redsandro
Copy link

@MelSumner

Ember doesn't use it internally

So I understand this issue is 100% upstream. Out of curiosity, is there something similar'ish that can be done using only Ember utils?

@sandstrom
Copy link
Contributor

sandstrom commented Mar 9, 2020

@Redsandro

Something like this should work:

// your-app/utils/misc.js

import { isHTMLSafe } from '@ember/template';

const ESCAPE = {
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  "'": '&#x27;',
  '`': '&#x60;',
  '=': '&#x3D;',
};

const BAD_CHARS = /[&<>"'`=]/g;
const POSSIBLE = /[&<>"'`=]/;

let escapeChar = function(chr) {
  return ESCAPE[chr];
};

// borrowed from Handlebars.utils.escapeexpression
// https://github.com/emberjs/ember.js/pull/18791
let escapeExpression = function(string) {
  if (typeof string !== 'string') {
    // don't escape SafeStrings, since they're already safe
    if (string && isHTMLSafe(string)) {
      return string.toHTML(); // private API, since SafeString isn't public
    } else if (string === null) {
      return '';
    } else if (!string) {
      return String(string);
    }

    // Force a string conversion as this will be done by the append regardless and
    // the regex test will do this transparently behind the scenes, causing issues if
    // an object's to string has escaped characters in it.
    string = String(string);
  }

  if (!POSSIBLE.test(string)) {
    return string;
  }

  return string.replace(BAD_CHARS, escapeChar);
};

export { escapeExpression as escape };

@kategengler
Copy link
Member

What about using a dedicated package for this?

  • sanitize-html supports escaping disallowed tags

  • dompurify (very similar to sanitize-html) allows you to keep some tags or attributes, or strip out undesired html while leaving the content in place, among many other things. I use dompurify in emberobserver to only allow certain tags to be rendered in addon READMEs and to ensure all links have the correct rel applied.

@sandstrom
Copy link
Contributor

sandstrom commented Mar 12, 2020

@kategengler Those are also good suggestions, but I don't think they interop with SafeString in Ember. Not needed for all use-cases though.

@pzuraq
Copy link
Contributor

pzuraq commented Mar 12, 2020

SafeString is a string marked as safe by the user, it doesn’t do any escaping itself, and it doesn’t have any expectations of what has been escaped, if anything. You can use any escaping library you like. As @MelSumner said, Ember doesn’t use any escaping functions internally anymore.

@sandstrom
Copy link
Contributor

@pzuraq Yeah, but wouldn't the escaping library need to know if a string is a safe-string or not? To know whether it should, or should not, escape them?

And it seems to me like the SafeString class itself, plus the method toHTML are private.

This isn't a big problem + I don't mind using private APIs (or perhaps them not being private is my misunderstanding), but it seems to me like making them public would be helpful in the aforementioned regard.

@pzuraq
Copy link
Contributor

pzuraq commented Mar 12, 2020

@sandstrom I don't think that the htmlSafe API is meant to be unwrapped ever, and definitely not mutated. My understanding was that it was meant to only be used at the edge of your state, so once you're actually ready to put a string into the DOM somewhere.

I'll double check to see about that. FWIW, there have also been discussions of having a new htmlSafe API, one that has some other benefits (for instance, you probably want to be able to do (eq '' someHtmlSafeString) type checks), so maybe the ability to unwrap and rewrap strings could be added in the future if I'm correct about the current API.

@sandstrom
Copy link
Contributor

sandstrom commented Mar 13, 2020

@pzuraq Alright, I'm happy with that! 😄

This isn't by any means a big issue in our application, so it's all good.

Have a great weekend & thanks for all the awsome work you (and others) are doing on Ember! 🏅

(for reference, here is my PR about making escapeExpression public #18791)

@cincodenada
Copy link
Contributor

cincodenada commented Sep 22, 2021

sanitize-html supports escaping disallowed tags

dompurify (very similar to sanitize-html) allows you to keep some tags or attributes, or strip out undesired html while leaving the content in place, among many other things. I use dompurify in emberobserver to only allow certain tags to be rendered in addon READMEs and to ensure all links have the correct rel applied.

I'm sure OP knows this but for those reading along casually, I wouldn't recommend just swapping in sanitization libraries in place of escaping. Sanitization and escaping are two very different things that are often confused, because their end goal is similar (protecting against XSS and the like).

Escaping should always be the first tool in your toolbox, because it is the safest - it simply prevents any HTML from being rendered, so if you're only trying to display text, escaping protects you from any injected HTML altogether.

Sanitization is useful if you want to render some HTML but still do so relatively safely. It attempts to determine what HTML is "safe" and what is "not", and strip out the unsafe stuff. This is a much harder problem, and existing libraries are good at it, but if you don't need to render HTML at all, it's much simpler and safer to just escape all HTML and not worry about it.

@kategengler
Copy link
Member

In spite of the name, sanitize-html provides options for escaping instead of sanitizing.

@cincodenada
Copy link
Contributor

Thanks, yeah I noticed that - it still only escapes disallowed tags, although as mentioned in the docs just above that you can set it to disallow all tags and effectively escape everything. But my main goal was to try to avoid folks skimming through, see "oh yay sanitize that sounds great" and drop those libraries into their project as a replacement, without understanding that it's not the same thing, and comes with different tradeoffs.

chriskrycho added a commit that referenced this issue Jan 25, 2023
This is unused elsewhere in the framework itself and is not public API,
so it should be safe to remove. Additionally, since this is just a copy
of the API from within Handlebars itself, users who want to use it
should directly use Handlebars' (or some other library's) escaping
library instead. See [the discussion at #16817][16817] for background.

#16817
chriskrycho added a commit that referenced this issue Jan 25, 2023
This is unused elsewhere in the framework itself and is not public API,
so it should be safe to remove. Additionally, since this is just a copy
of the API from within Handlebars itself, users who want to use it
should directly use Handlebars' (or some other library's) escaping
library instead. See [the discussion at #16817][16817] for background.

[16817]: #16817
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.