7

Attribute values need to be encoded. If I'm building a jQuery object like so:

$('<div data-value="' + value + '">');

Really, value must be attribute encoded like so:

$('<div data-value="' + HtmlAttributeEncode(value) + '">');

I cannot find such a native function. Some suggest it's simply a matter of replacing double quotes with &quot;, but Microsoft's HttpEncoder.HtmlAttributeEncode method encodes these four characters & < " '. I've seem implementations such as quoteattr here: https://stackoverflow.com/a/9756789/88409, but that is horribly inefficient, calling replace to iterate over the string multiple times. Likewise, I need a native function for encoding a javascript string (e.g. $('<div onclick="var s =\'' + HtmlAttributeEncode(JavaScriptStringEncode(value)) + '\';alert(s);"></div>).appendTo(body); << contrived example for illustration only)

Is there a native equivalent of this functionality?

Note: Please don't mention escape (which is now deprecated in favor of encodeURI and encodeURIComponent) all of which have nothing to do with attribute encoding.

Community
  • 1
  • 1
Triynko
  • 18,766
  • 21
  • 107
  • 173

4 Answers4

3

No.

But you don't need them since you can build elements using DOM methods (or jQuery's wrappers around them) which bypass the need for escaping since you are dealing with a DOM instead of HTML.

$('<div />', { "data-value" : value });

or

var div = document.createElement('div');
div.setAttribute('data-value', value);

If you really want to get the escaped HTML, you can take a DOM and generate HTML from it:

var html = $('<div />').append(
    $('<div />', { "data-value" : value })
).html();
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Well that defeats the purpose of being able to use JQuery to construct complex nested elements from a string, such as a modal dialog, e.g. (`$('');` as part of a function that generates such dialogs. If I cannot encode the variable in the string, then I have to separately locate the generated elements with inefficient 'find' calls, and set attribute methods. I'm well aware of "alternatives", I want native functionality, not "don't do it" answers. – Triynko Aug 13 '15 at 18:46
  • 1
    That isn't a bad thing. Smashing together strings is the most error prone and hard to debug way to generating any data formats. http://jsbin.com/golahutasa/1/edit?html,js,output is much easier to maintain and debug. – Quentin Aug 13 '15 at 18:51
  • There is no native functionality (other then the approach I pointed out in the last code sample of the answer). – Quentin Aug 13 '15 at 18:51
  • It just seems like such basic functionality: "attribute encode" or "string encode", should have native support like URI encoding does. The browser clearly already implements the methods, why not expose them. This doesn't touch on JavaScript string encoding either. One of the reasons people flaunt JavaScript is for it's ability to parse smashed strings like that in a totally non-object-oriented way, e.g. eval. The jsbin post highly departs from that and is much longer. While I like the code, it could become far too complex in a larger scenario. – Triynko Aug 13 '15 at 18:56
  • 1
    In any case, that's an excellent example of how to build DOM structures with jQuery in a highly object-oriented fashion, but that's just not the kind of raw native string-encoding functionality I'm looking for. I'm imagining scenarios where a web app would want to generate HTML strings for unrelated reasons... and I'd unfortunately have to go searching for a library to perform those operations... which again, the browser already implements. It's like re-inventing the wheel, not to mention going backwards a decade switching from AS3 back to JS. – Triynko Aug 13 '15 at 19:04
  • If you want this DOM approach as a one-liner, this _seems_ to work for extracting the encoded attribute: `$('

    ').append($('

    ', {"a": value})).html().split(/"/)[1]`. I really hope things have improved in 7 years, but I still can't find `escapeHtmlAttribute` on MDN.

    – seanf Sep 29 '22 at 14:27
  • @seanf but again, that's *jQuery* which the original question explicitly asks not for – Michael Feb 25 '23 at 04:42
2

This is quite an old question, but I think more interesting with the javascript templating capabilities:

html = `<table title="${ myTitleVar }"><thead><tr>
</tr></thead></table>`

No longer is this error prone and stringing together hundreds of jquery functions is just impractical and unportable.

So, there is a little trick for encoding attributes. Should work fine. I want to make sure i don't have quotes or some nonsense in myTitleVar, so:

    var $h=$('<span>');
    function encodeAttr(t) {
        return $h.attr('title',t).prop('outerHTML').match(/title="(.*)"/)[1];
    }

    html = `<table title="${ encodeAttr(myTitleVar) }"><thead><tr>
       </tr></thead></table>`


I haven't tested in all browsers. It's possible some generate the html using ' instead of ", which would make it a little more challenging.

https://jsfiddle.net/p359ux01/

Garr Godfrey
  • 8,257
  • 2
  • 25
  • 23
  • This doesn't seem to handle quotes, eg with `myTitleVar = '"> – seanf Sep 29 '22 at 14:00
  • 1
    @seanf you are right. I don't remember what I was using this for, but it worked for something. I'm editing for a working solution. – Garr Godfrey Oct 08 '22 at 23:20
  • The question asks for native Javascript but this seems to require jQuery? – Michael Feb 25 '23 at 04:39
  • it's not what OP asked for, but what they want. It's obvious from the question they are using JQuery. – Garr Godfrey Mar 01 '23 at 15:38
1

Here is a vanilla JS version (no jQuery), inspired mostly by Garr Godfrey's answer, based mostly on Quentin's answer. It should handle browsers which choose to wrap attributes in single quotes for whatever reason. Use at your own risk, etc etc.

// Implementation:
/**
 * Encodes an HTML attribute using the browser's DOM methods
 */
function encodeAttr(text) {
  const elem = document.createElement('p');
  elem.setAttribute('title', text);
  const elemHtml = elem.outerHTML; // <p title="encodedText"> or maybe <p title='encodedText'>
  // Find out whether the browser used single or double quotes before encodedText
  const quote = elemHtml[elemHtml.search(/['"]/)];
  // Split up the generated HTML using the quote character; take item 1
  return elemHtml.split(new RegExp(quote))[1];
}

// Demo:
const untrustedAttribute = '"><iframe src="javascript:alert(\'XSS\');">.jpg';
document.getElementById('results').innerHTML = 
  `<span title="${encodeAttr(untrustedAttribute)}"> My content </span>`;

https://jsfiddle.net/sys2061/vorm8Ldp/16/

seanf
  • 6,504
  • 3
  • 42
  • 52
-2

Found an npm package that will get your job done!

https://www.npmjs.com/package/escape-html