Javascript JSON.parse string onclick in-line - javascript

I'm using jquery to append HTML to an ID; specifically, I am having trouble placing a JSON-formatted string into the onclick parameter of an element. I intentionally want the JSON to be parsed onClick and am perfectly okay with the JSON living in the DOM for this specific effort.
I think the problem is related to escaping the quotes properly (because a Chrome Inspect Elements debug reveals that each JSON element is being treated as an HTML tag as opposed to a string--see https://ibb.co/eHOgq8 for screenshot of how the DOM is being interpreted)
var str = '{"artist":"So and So","title":"Not Relevant"}';
$("#d").append('<ul><li><a onclick="JSON.parse(\'' + str + '\')" title="NA">Link</a></li></ul>')
I've tried using numerous escaping mechanism and quote types as well as some native Javascript escaping and gotten no where. After 2 hours, I have to turn SO for help. Thank you so much in advance.

Try this :
var str = '{"artist":"So and So","title":"Not Relevant"}';
var jsonObj = JSON.parse(str);
$("#d").append('<ul><li><a onclick="alert(JSON.stringify(jsonObj))" title="NA">Link</a></li></ul>');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="d"></div>

Don't try to assign handlers with inline attributes - that's as bad as eval, and as you're experiencing, can lead to confusing issues with escape characters. Instead, attach handlers properly using Javascript instead, for example:
var str = '{"artist":"So and So","title":"Not Relevant"}';
$('#div').append('<ul><li><a title="etc">link</a></li></ul>');
$('div a').on('click', () => console.log(JSON.parse(str)));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div"></div>

Related

Using variables with jQuery's replaceWith() method

Ok this one seems pretty simple (and it probably is). I am trying to use jQuery's replace with method but I don't feel like putting all of the html that will be replacing the html on the page into the method itself (its like 60 lines of HTML). So I want to put the html that will be the replacement in a variable named qOneSmall like so
var qOneSmall = qOneSmall.html('..........all the html');
but when I try this I get this error back
Uncaught SyntaxError: Unexpected token ILLEGAL
I don't see any reserved words in there..? Any help would be appreciated.
I think the solution is to only grab the element on the page you're interested in. You say you have like 60 lines. If you know exactly what you want to replace..place just that text in a div with an id='mySpecialText'. Then use jQuery to find and replace just that.
var replacementText = "....all the HTML";
$("#mySpecialText").text(replacementText);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mySpecialText">Foo</div>
If you're only looking to replace text then jaj.laney's .text() approach can be used. However, that will not render the string as HTML.
The reason the way you're using .html() is likely illegal is that qSmallOne is not a JQuery object. The method cannot be performed on arbitrary variables. You can set the HTML string to a variable and pass that string to the .html() function like this:
var htmlstring = '<em>emphasis</em> and <strong>strong</strong>';
$('#target').html(htmlstring);
To see the difference between using .html() and .text() you can check out this short fiddle.
Edit after seeing the HTML
So there is a lot going on here. I'm just going to group these things into a list of issues
The HTML Strings
So I actually learned something here. Using the carriage return and tab keys in the HTML string is breaking the string. The illegal-ness is coming from the fact the string is never properly terminated since it thinks it ends at the first line. Strip out the white space in your strings and they're perfectly valid.
Variable Names
Minor thing, you've got a typo in qSmallOne. Be sure to check your spelling especially when working with these giant variables. A little diligence up front will save a bunch of headache later.
Selecting the Right Target
Your targets for the change in content are IDs that are in the strings in your variables and not in the actual DOM. While it looks like you're handling this, I found it rather confusing. I would use one containing element with a static ID and target that instead (that way you don't have to remember why you're handling multiple IDs for one container in the future).
Using replaceWith() and html()
.replaceWith() is used to replace an element with something else. This includes the element that is being targeted, so you need to be very aware of what you're wanting to replace. .html() may be a better way to go since it replaces the content within the target, not including the target itself.
I've made these updates and forked your fiddle here.

Is there a direct way of appending text to a DOM element with jQuery?

Is there a more direct way of writing the following in jQuery?
var $b = $('b');
$b.text($b.text() + ', World!!');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<b>Hello</b>
This seams like something jQuery would have existing functionality for, as vanilla JavaScript can do it by direct access to the property.
document.querySelector('b').innerText += ', World!!';
I looked into the .append() method, however it appears that it isn't designed for appending text, even though it works:
$('b').append(', World!!');
Also the Additional Notes section warns of XSS vulnerabilities when using .append(), as it can potentially execute code.
No. As you have already pointed out, the cleanest way is by modifying the property directly.
To do that in jQuery you can get the DOM element reference from within the object:
var $b = $('b');
$b[0].innerText += ', World!!';
JSFiddle
Or,
You could pass a function to .text(), which isn't any 'cleaner' but can be very helpful if you want to use the context:
var $b = $('b');
$b.text(function(_,v){
return v += ', World!!';
});
JSFiddle
Or,
If it really bugs you, introduce your own jQuery method:
jQuery.fn.appendText = function(a){
return this.each(function(){
$(this).text(function(_,v){
return v += a;
});
});
};
For use like so:
$b.appendText(', World!!');
JSFiddle
'This seams like something jQuery would have existing functionality for, as vanilla JavaScript can do it by direct access to the property.'
Is probably exactly why jQuery doesn't implement its own method to do so. Why waste valuable bytes with a method that will carry out something that is so easily done with vanilla JavaScript?
use text()
$('b').text("Hello");
We need to be aware that this method escapes the string provided as
necessary so that it will render correctly in HTML. To do so, it calls
the DOM method .createTextNode(), which replaces special characters
with their HTML entity equivalents (such as < for <)
When you use .text() jQuery uses createTextNode internally, which escapes all special characters.

Escaping apostrophe in jQuery

I'm trying to escape out of some apostrophe's in my JSON. I know it's not ideal but i append the data to the DOM and call it later in the code. At the moment i get an error whenever there is an ' in the data. I've tried using replace and encodeURI but just doesnt seem to work.
My code basically looks like:
var addItem = function (item, target){
var obj = $(item).data('obj');
var obj_string = JSON.stringify(obj);
target.append("<div data-obj='" + obj_string + "'> Added </div>
}
When i inspect the element it breaks when it gets to the apostrophe:
{"publisher":"EA","games":[{"game":"Dragon's"}]}
Looks like this in the element inspector:
{"publisher":"EA","games":[{"game":"Dragon" s"}]}
And everything that follows is broken. Any ideas how i escape it?
I've found lots of ways if it was pureply jquery but with it being in the html it seems to not work.
You can avoid the escaping and stringification© using .data() to set the obj to the element directly.
$('<div/>').data('obj', obj).text('Added').appendTo(target);
Just keep in mind with this method you won't get a data attribute on the actual element itself but the data will be there when you request it with .data.
You are concatenating your content with a string that already uses single quotes. The concatenated result will most likely be <div data-obj='Dragon's'> which is not what you want.
Either escape the single quote when concatenating it (not sure the entity won't be interpreted):
.append("<div data-obj='" + obj_string.replace("'", "‚") + "'> Added </div>");
Or safer, you can build your nodes with jQuery which will give you a native escaping for some performance penalty:
.append($("<div>Added</div>").data("obj", obj_string));
Rather than leaving yourself open to XSS, try:
var div = document.createElement('div');
div.appendChild(document.createTextNode("Added"));
div.setAttribute("data-obj",obj_string);
target.append(div);

Better Way to Sanitize HTML for Insertion

In a recent review by the AMO editors, my Thunderbird addon's version was rejected because it "creates HTML from strings containing unsanitized data" - which "is a major security risk".
I think I understand why. Now, my problem is about how to solve that issue.
This thread gave me some clues, but it's not quite what I need.
My addon needs to paste the contents of the clipboard as a hyperlink, by using the clipboard contents as the link text, and inserting html around it like this: `" + clipboardtext + "".
Now, if I am inserting the clipboard contents as HTML, I need to "sanitize" it first. Here is what I came up with. Now, I haven't written in the regex part yet, because I don't think this is the best way to do this, although I think it will work:
function makeSafeHTML(whathtml){
var parser = Cc["#mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
var sanitizedHTML = parser.sanitize(whathtml, 01);
//now remove the extratags added by the sanitization method, perhaps via regex
//"<html><head></head><body>"
//"</body></html>"
return sanitizedHTML;
}
My intent is to do this with the resulting "sanitized" string - this will paste the string as the href value of a hyperlink:
var html_editor = editor.QueryInterface(Components.interfaces.nsIHTMLEditor);
html_editor.insertHTML("<a href='"+whathref+"'>"+whattext+"</a>");
So I am looking for a better way to get sanitized HTML into a simple string variable. Would any of you do it this way?
It seems that you simply want to insert clipboard contents into HTML code as pure text - you don't need any complicated escaping approach then, it's enough to make sure all "dangerous" characters are replaced by HTML entities:
var sanitizedText = text.replace(/&/g, "&").replace(/</g, "<")
.replace(/>/g, ">").replace(/"/g, """);
It's not clear from your question what you do with the generated HTML code. If you add it to a DOM document via something like innerHTML then you can do better - add the HTML code first and manipulate the text in the document then:
document.getElementById("text-container").textContent = text;
Using Node.textContent to set text in a document is always safe, no escaping needs to be performed.

How to decode HTML entities using jQuery?

How do I use jQuery to decode HTML entities in a string?
Security note: using this answer (preserved in its original form below) may introduce an XSS vulnerability into your application. You should not use this answer. Read lucascaro's answer for an explanation of the vulnerabilities in this answer, and use the approach from either that answer or Mark Amery's answer instead.
Actually, try
var encodedStr = "This is fun & stuff";
var decoded = $("<div/>").html(encodedStr).text();
console.log(decoded);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div/>
Without any jQuery:
function decodeEntities(encodedString) {
var textArea = document.createElement('textarea');
textArea.innerHTML = encodedString;
return textArea.value;
}
console.log(decodeEntities('1 & 2')); // '1 & 2'
This works similarly to the accepted answer, but is safe to use with untrusted user input.
Security issues in similar approaches
As noted by Mike Samuel, doing this with a <div> instead of a <textarea> with untrusted user input is an XSS vulnerability, even if the <div> is never added to the DOM:
function decodeEntities(encodedString) {
var div = document.createElement('div');
div.innerHTML = encodedString;
return div.textContent;
}
// Shows an alert
decodeEntities('<img src="nonexistent_image" onerror="alert(1337)">')
However, this attack is not possible against a <textarea> because there are no HTML elements that are permitted content of a <textarea>. Consequently, any HTML tags still present in the 'encoded' string will be automatically entity-encoded by the browser.
function decodeEntities(encodedString) {
var textArea = document.createElement('textarea');
textArea.innerHTML = encodedString;
return textArea.value;
}
// Safe, and returns the correct answer
console.log(decodeEntities('<img src="nonexistent_image" onerror="alert(1337)">'))
Warning: Doing this using jQuery's .html() and .val() methods instead of using .innerHTML and .value is also insecure* for some versions of jQuery, even when using a textarea. This is because older versions of jQuery would deliberately and explicitly evaluate scripts contained in the string passed to .html(). Hence code like this shows an alert in jQuery 1.8:
//<!-- CDATA
// Shows alert
$("<textarea>")
.html("<script>alert(1337);</script>")
.text();
//-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
* Thanks to Eru Penkman for catching this vulnerability.
Like Mike Samuel said, don't use jQuery.html().text() to decode html entities as it's unsafe.
Instead, use a template renderer like Mustache.js or decodeEntities from #VyvIT's comment.
Underscore.js utility-belt library comes with escape and unescape methods, but they are not safe for user input:
_.escape(string)
_.unescape(string)
I think you're confusing the text and HTML methods. Look at this example, if you use an element's inner HTML as text, you'll get decoded HTML tags (second button). But if you use them as HTML, you'll get the HTML formatted view (first button).
<div id="myDiv">
here is a <b>HTML</b> content.
</div>
<br />
<input value="Write as HTML" type="button" onclick="javascript:$('#resultDiv').html($('#myDiv').html());" />
<input value="Write as Text" type="button" onclick="javascript:$('#resultDiv').text($('#myDiv').html());" />
<br /><br />
<div id="resultDiv">
Results here !
</div>
First button writes : here is a HTML content.
Second button writes : here is a <B>HTML</B> content.
By the way, you can see a plug-in that I found in jQuery plugin - HTML decode and encode that encodes and decodes HTML strings.
The question is limited by 'with jQuery' but it might help some to know that the jQuery code given in the best answer here does the following underneath...this works with or without jQuery:
function decodeEntities(input) {
var y = document.createElement('textarea');
y.innerHTML = input;
return y.value;
}
You can use the he library, available from https://github.com/mathiasbynens/he
Example:
console.log(he.decode("Jörg &amp Jürgen rocked to & fro "));
// Logs "Jörg & Jürgen rocked to & fro"
I challenged the library's author on the question of whether there was any reason to use this library in clientside code in favour of the <textarea> hack provided in other answers here and elsewhere. He provided a few possible justifications:
If you're using node.js serverside, using a library for HTML encoding/decoding gives you a single solution that works both clientside and serverside.
Some browsers' entity decoding algorithms have bugs or are missing support for some named character references. For example, Internet Explorer will both decode and render non-breaking spaces ( ) correctly but report them as ordinary spaces instead of non-breaking ones via a DOM element's innerText property, breaking the <textarea> hack (albeit only in a minor way). Additionally, IE 8 and 9 simply don't support any of the new named character references added in HTML 5. The author of he also hosts a test of named character reference support at http://mathias.html5.org/tests/html/named-character-references/. In IE 8, it reports over one thousand errors.
If you want to be insulated from browser bugs related to entity decoding and/or be able to handle the full range of named character references, you can't get away with the <textarea> hack; you'll need a library like he.
He just darn well feels like doing things this way is less hacky.
encode:
$("<textarea/>").html('<a>').html(); // return '<a&gt'
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea/>
decode:
$("<textarea/>").html('<a&gt').val() // return '<a>'
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea/>
Try this :
var htmlEntities = "<script>alert('hello');</script>";
var htmlDecode =$.parseHTML(htmlEntities)[0]['wholeText'];
console.log(htmlDecode);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
parseHTML is a Function in Jquery library and it will return an array that includes some details about the given String..
in some cases the String is being big, so the function will separate the content to many indexes..
and to get all the indexes data you should go to any index, then access to the index called "wholeText".
I chose index 0 because it's will work in all cases (small String or big string).
Use
myString = myString.replace( /\&/g, '&' );
It is easiest to do it on the server side because apparently JavaScript has no native library for handling entities, nor did I find any near the top of search results for the various frameworks that extend JavaScript.
Search for "JavaScript HTML entities", and you might find a few libraries for just that purpose, but they'll probably all be built around the above logic - replace, entity by entity.
I just had to have an HTML entity charater (⇓) as a value for a HTML button. The HTML code looks good from the beginning in the browser:
<input type="button" value="Embed & Share ⇓" id="share_button" />
Now I was adding a toggle that should also display the charater. This is my solution
$("#share_button").toggle(
function(){
$("#share").slideDown();
$(this).attr("value", "Embed & Share " + $("<div>").html("⇑").text());
}
This displays ⇓ again in the button. I hope this might help someone.
You have to make custom function for html entities:
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g,'>').replace(/"/g, '"');
}
Suppose you have below String.
Our Deluxe cabins are warm, cozy & comfortable
var str = $("p").text(); // get the text from <p> tag
$('p').html(str).text(); // Now,decode html entities in your variable i.e
str and assign back to tag.
that's it.
For ExtJS users, if you already have the encoded string, for example when the returned value of a library function is the innerHTML content, consider this ExtJS function:
Ext.util.Format.htmlDecode(innerHtmlContent)
Extend a String class:
String::decode = ->
$('<textarea />').html(this).text()
and use as method:
"<img src='myimage.jpg'>".decode()
You don't need jQuery to solve this problem, as it creates a bit of overhead and dependency.
I know there are a lot of good answers here, but since I have implemented a bit different approach, I thought to share.
This code is a perfectly safe security-wise approach, as the escaping handler depends on the browser, instead on the function. So, if some vulnerability will be discovered in the future, this solution is covered.
const decodeHTMLEntities = text => {
// Create a new element or use one from cache, to save some element creation overhead
const el = decodeHTMLEntities.__cache_data_element
= decodeHTMLEntities.__cache_data_element
|| document.createElement('div');
const enc = text
// Prevent any mixup of existing pattern in text
.replace(/⪪/g, '⪪#')
// Encode entities in special format. This will prevent native element encoder to replace any amp characters
.replace(/&([a-z1-8]{2,31}|#x[0-9a-f]+|#\d+);/gi, '⪪$1⪫');
// Encode any HTML tags in the text to prevent script injection
el.textContent = enc;
// Decode entities from special format, back to their original HTML entities format
el.innerHTML = el.innerHTML
.replace(/⪪([a-z1-8]{2,31}|#x[0-9a-f]+|#\d+)⪫/gi, '&$1;')
.replace(/⪪#/g, '⪪');
// Get the decoded HTML entities
const dec = el.textContent;
// Clear the element content, in order to preserve a bit of memory (in case the text is big)
el.textContent = '';
return dec;
}
// Example
console.log(decodeHTMLEntities("<script>alert('&awconint;&CounterClockwiseContourIntegral;∳∳⪪#x02233⪫');</script>"));
// Prints: <script>alert('∳∳∳∳⪪#x02233⪫');</script>
By the way, I have chosen to use the characters ⪪ and ⪫, because they are rarely used, so the chance of impacting the performance by matching them is significantly lower.
Here are still one problem:
Escaped string does not look readable when assigned to input value
var string = _.escape("<img src=fake onerror=alert('boo!')>");
$('input').val(string);
Exapmle: https://jsfiddle.net/kjpdwmqa/3/
Alternatively, theres also a library for it..
here, https://cdnjs.com/libraries/he
npm install he //using node.js
<script src="js/he.js"></script> //or from your javascript directory
The usage is as follows...
//to encode text
he.encode('© Ande & Nonso® Company LImited 2018');
//to decode the
he.decode('© Ande & Nonso® Company Limited 2018');
cheers.
To decode HTML Entities with jQuery, just use this function:
function html_entity_decode(txt){
var randomID = Math.floor((Math.random()*100000)+1);
$('body').append('<div id="random'+randomID+'"></div>');
$('#random'+randomID).html(txt);
var entity_decoded = $('#random'+randomID).html();
$('#random'+randomID).remove();
return entity_decoded;
}
How to use:
Javascript:
var txtEncoded = "á é í ó ú";
$('#some-id').val(html_entity_decode(txtEncoded));
HTML:
<input id="some-id" type="text" />
The easiest way is to set a class selector to your elements an then use following code:
$(function(){
$('.classSelector').each(function(a, b){
$(b).html($(b).text());
});
});
Nothing any more needed!
I had this problem and found this clear solution and it works fine.
I think that is the exact opposite of the solution chosen.
var decoded = $("<div/>").text(encodedStr).html();

Categories

Resources