Skip to main content

Bug Tracker

Side navigation

#14228 closed feature (migrated)

Opened August 06, 2013 08:37PM UTC

Closed October 21, 2014 12:14AM UTC

Extension point for intercepting HTML before DOM insertion

Reported by: nicholas@nczconsulting.com Owned by:
Priority: low Milestone: 1.12/2.2
Component: manipulation Version: 1.10.2
Keywords: Cc:
Blocked by: Blocking:
Description

At my company, there's a desire to filter HTML strings as they go through methods like html() and append() to prevent XSS. Our code base is quite large and there are thousands of references to these types of methods that accept an HTML string for DOM insertion, so it's not feasible to replace each instance with our own method call.

As far as I can tell, there's no current way to intercept HTML strings before insertion. What I'm suggesting is adding some kind of extension point to jQuery Core that would allow someone to register a function that would receive the HTML before insertion and allow someone to change that HTML that would actually be inserted. This has the following potential uses:

  • custom XSS filters
  • auto linking filters (automatically turn an email address into a link, for example)
  • stripping out personal data

Just to be clear, I'm not suggesting including anything other than an extension point that would allow such filters to be written. Without this, I need to basically overwrite html(), append(), prepend(), and all the others to manfully check any string arguments.

Attachments (0)
Change History (10)

Changed August 06, 2013 09:02PM UTC by dmethvin comment:1

component: unfiledmanipulation
status: newopen

A lot of us on the team consider the fact that we allow scripts to run by injecting them to be a bug that we can't remove for back-compat reasons, so we're sympathetic. I'll leave the ticket open for discussion to see if there's any way we can fix this.

Changed August 06, 2013 10:37PM UTC by rwaldron comment:2

What is preventing something like this:

// Beginning of IIFE or AMD or whatever...

var html = jQuery.fn.html;
var append = jQuery.fn.append;

jQuery.fn.html = function() {
   // do the filtering
   return html.call(this, filtered/safe stuff);
};

jQuery.fn.append = function() {
   // do the filtering
   return append.call(this, filtered/safe stuff);
};
  

// End of IIFE or AMD or whatever...

Changed August 06, 2013 10:43PM UTC by nicholas@nczconsulting.com comment:3

Nothing is preventing it, however, it is inefficient and risky. There are probably many paths to inserting HTML through jQuery, and without tracking them all down, the risk remains.

This is essentially what I'm doing right now, but I'm afraid I might have missed some corner case (and of course, methods change between versions, I don't want to have to keep updating this functionality). It would be much more efficient and safe to have one place to insert this functionality.

Changed August 19, 2013 05:44PM UTC by nicholas@nczconsulting.com comment:4

I just wrote up a blog post on everything we had to do to secure jQuery:

http://tech.blog.box.com/2013/08/securing-jquery-against-unintended-xss/

I would love there to be a formal HTML filter capability, and it seems like the injection points for that functionality are fairly straightforward (see the blog post).

Changed December 25, 2013 07:29PM UTC by m_gol comment:5

I agree this would be nice to have.

Changed December 25, 2013 11:45PM UTC by gibson042 comment:6

Related: #14370; #14464

I don't know that we'd want to add ''another'' function for all input to pass through, but could definitely see exposing the arguments of our currently-existing .replace( rxhtmlTag, "<$1></$2>" ) as properties on jQuery. "Simple" extensions like no-innerHTML contents could still use a string replacement, but more complex ones like what this ticket is requesting could get wholesale filtering with something like the following (apologies for atrocious names):

var origPreMatch = jQuery.htmlPreMatch,
    origPreReplace = jQuery.htmlPreReplace;

jQuery.htmlPreMatch = /[\\w\\W]+/;
jQuery.htmlPreReplace = function( html ) {
    return extraProcess( html ).replace( origPreMatch, origPreReplace );
};

Changed March 04, 2014 02:33PM UTC by dmethvin comment:7

milestone: None1.12/2.2
priority: undecidedlow

Changed May 05, 2014 04:55PM UTC by m_gol comment:8

Replying to [comment:6 gibson042]:

Related: #14370; #14464 I don't know that we'd want to add ''another'' function for all input to pass through

If we passed it through $.escapeHTML only if it's a function then we're saved from one additional function invocation on each manip operation and the only overhead is one additional if. Is that a lot?

I feel regex might not be enough in some cases and we really should make it possible to secure our manip methods.

Changed May 06, 2014 08:38PM UTC by gibson042 comment:9

Replying to [comment:8 m_gol]:

If we passed it through $.escapeHTML only if it's a function then we're saved from one additional function invocation on each manip operation and the only overhead is one additional if. Is that a lot?

Well, it's still guaranteed to do at least two passes over the input (once for custom escaping and once to fix up self-closing tags) when someone sets up preprocessing, even if they're only trying to fix our mangling of script/textarea content (#14370) or attribute values (#14464). Come to think of it, that brings up the related question of whether preprocessing should precede, follow, or replace the currently-existing .replace( rxhtmlTag, "<$1></$2>" ).

I feel regex might not be enough in some cases and we really should make it possible to secure our manip methods.

str = str.replace( /[\\w\\W]+/, fn ) is equivalent to str = fn( str ) for any string-returning single-parameter fn, and therefore guaranteed to support the same use cases. This is purely a question of API surface area.

Changed October 21, 2014 12:14AM UTC by m_gol comment:10

resolution: → migrated
status: openclosed