Right way to escape JSON data from django template - javascript

I want to pass a dictionary from django view to a javascript file. The dictionary is built from a database populated by site users. What's the difference between these 2 methods in terms of security?
var mydata = JSON.parse("{{mydata|escapejs}}");
var mydata = {{ mydata|safe }};
Further, the doc at django says this for escapejs : This does not make the string safe for use in HTML. Could you show me an example of how it's unsafe & how can I make it safe.

For anyone coming across this in 2019, Django now provides a third option with the |json_script template filter. This filter takes care of properly serializing and escaping your Python object for use in HTML.
From the docs, using example data with unsafe characters my_data = {'hello': 'world</script>&'}:
{{ my_data|json_script:"my-data" }}
renders to
<script id="my-data" type="application/json">
{"hello": "world\\u003C/script\\u003E\\u0026amp;"}
</script>
You can then access this data in Javascript via
var value = JSON.parse(document.getElementById('my-data').textContent);

The following dictionary can break your page without proper escaping:
{'x':'</script><b>HELLO</b>'}
Inside tags, you can json.dumps it in your view and then use escapejs to stay safe.
(I believe the explanation means that if you want to show the output of json.dumps in HTML, let's say in a <pre> tag, just make sure it is escaped by not using safe or escapejs.)

Related

Summernote set text through passed parameter in laravel

My laravel controller passes parameters to the view which I can access by $note in {{ }}. All right, it works. I can get note's title. However, when I want to set the HTML for summernote, it somehow doesn't work. This is what I tried:
<script type="text/javascript">
$('#editor').summernote('code', '{{ $note->html }}');
as well as unescaped using {!! $note->html !!}}. The result is a summernote editor which looks like a normal text input. However, when manually setting [...]('code', '<html><h1>some html for example</h1></html>)[...] it works like a charm.
It is obvious that it's related to the way blade is putting out data using {{ }} but I can't currently figure out why. Using unescaped html had been the first approach that popped up in my mind.
When outputting PHP data for JavaScript to use, I like to use json_encode on the variable. This guarantees variables get output in a JavaScript-friendly version - it'll wrap strings in ' marks, leave integers alone, and output complex arrays/objects as proper JSON.
$('#editor').summernote('code', {!! json_encode($note->html) !!});

How to read Python list in Javascript [in a Django template]

I'm programming in oTree (which is a Django based environment for social experiments) and I have the following problem. I defined some lists in Python and I'd like to import them and use them in an HTML template. If I print them in HTML I manage to see them without any problem, however, once I need to use them in Javascript, the program fails to read them and the single quotes of the elements of the list are converted in '.
The list is imported like this var filtered_elements = {{ array }};.
I think the problem is exactly here, as JS cannot work with them. Do you have any suggestion on how to do that? I considered using JSON, but since I'm quite new to programming, I cannot understand if it's just a waste of time or there is a simpler way out.
Thanks for your answers!
It sounds like your data is already JSON, otherwise you would be getting single quotes and u prefixes. So the only issue is Django autoescaping; you can disable it with the safe filter:
var filtered_elements = {{ array|safe }};
Your data should be JSON, instead of putting the Python list into the contact directly, put "array": json.dumps(array) in the context dictionary.
The JSON string doesn't need HTML escaping inside a tag, but it does need JS escaping! Otherwise some string may include something like </script><script>absolutely anything goes here... to run arbitrary JavaScript, if the JSON contains user data.
So use |escapejs:
var filtered_elements = {{ array|escapejs}};

More efficient way to interpret data from route in view with Angular over Swig?

I have an array of JSON objects being passed from my Node route into its respective view. For example:
res.render("path/to/view", { data: result, data2: result2 })
where both result and result2 are arrays of JSON objects. In my view, I am using them in an ng-init function like so: ( {{}} designates swig, where I have changed [[]] to designate Angular, so in the below example, we are using swig before using my defined Angular init function)
<div ng-init="init( {{ data|json }} )"> </div>
I should say that the above example works fine, but when "data" becomes a very large dataset, SO much time is spend on the swig part - that is converting it to JSON (again..?). Like I said, the "data" is already in the form of JSON, but when I remove the |json from the swig part above, I get a [$parse:syntax] error, and it give me the above line but evaluated:
<div ng-init="init( [object Object],[object Object] )"> </div>
I have tried variations of using ng-init="init( [[ JSON.parse( {{ data }} ) ]] )" so that I evaluate the output to JSON (even though the output already is..?) but cant get anything to work.
Any ideas? Maybe I have the syntax wrong? I don't understand because the "data" is JSON when I pass it to the view, but I can't pass it directly into my init function without getting that syntax error.
Take whatever you're calling with ng-init out of your view and put it in a controller or service.
There are tons of reasons not to use ng-init, so much so that the Angular team basically recommends you don't use it: https://docs.angularjs.org/api/ng/directive/ngInit
Update
I finally think I see what you're trying to do, and it's called bootstrapping. You can embed data into your view server-side like so:
<script>
angular.module("app").constant("myBootstrap", {{ data.stringified }});
</script>
Where data.stringified is your stringified data. Then in Angular you can inject into any controllers you want as a constant with myBootstrap, same as you would for $rootScope, etc., and the data will be available.
So while I am not entirely sure what was causing it to go so slow, I have found a solution, although 'hack' might more accurately describe it. The problem I was having was with Swig taking ages to template my data before passing it into the init function (which I now know is bad practice; that is, to use ng-init to call a function).
I should note for future readers that I am using Node.js, Swig for templating, and Angular for handling the the MVC side of things.
See the above question to see what my code was like before (slow version). Below is my solution:
Routing side
var rawSQLData = makeSQLCall();
var parsedAsJsonSQLData = parseData(rawSQLData);
var parsedDataAsString = JSON.stringify(parsedAsJsonSQLData);
res.render("path/to/view", { data: parsedDataAsString });
HTML
<div ng-init=" init( {{ data|json }} )"> </div>
Angular Controller
$scope.init = function(data){
$scope.dataInScope = JSON.parse(data);
}
As you can see, the only thing I changed was stringifying the JSON before shooting it to the HTML and then parsing it once it gets to the controller.
The reason I think this works is because I am essentially doing all of the JSON processing in parts other than the templating engine (Swig). I tried using no filter, but Swig defaults to converting the data to JSON which was breaking my HTML. The reason that I want to call this a hack rather than a solution is because we stringify the data, and then use the json filter for Swig, which, according to the documentation:
Return a string representation of an JavaScript object.
If I had to guess, I would say that by using the json filter on a string, Swig decides it has nothing to do as the object is already a string and just passes it along (just what I wanted all along!). It is incredible how much faster it is when Swig doesn't touch the data. The JSON stringify and parse are really quick for the size of data, while Swig was taking up to 40 seconds

Passing HTML in JSON as data in Assemble

I am working on a project that requires multilanguage support. I decided to utilize Assemble (more specifically grunt-assemble) since it was already part of the project toolbox so my current setup uses JSON file(s) as data/text input for handlebar templates.
The site is responsive and there is a requirement to have certain level of control over text using break lines <br /> or non-breaking spaces to avoid orphaned words. Some sentences require mentioned tag or html entity to be included in the string otherwise I'd be forced to split sentence word by word and combine hardcoded html with json data reference. Imagine something like this:
<p>{{word_1}}<br />{{word_2}}</p>
This approach is also not very translation friendly, since a different language might not require line break at all.
To avoid this I've tried to pass html via JSON like this:
{ "sentence" : "word<br />word" }
Assemble output, however, is literal, so instead or of functional tag I get its string version and page literally displays word<br />word. Same for
What is (if any) proper notation for passing html tags or entities from JSON to handlebar templates via Assemble?
Handlebars escapes HTML by default, but you can avoid escaping with the triple-stash format {{{ }}}. Take a look at the following .hbs page:
---
title: Test
htmlData: This is some <br/> html in data
---
<p>double-stash: {{htmlData}}</p>
<p>triple-stash: {{{htmlData}}}</p>
results in:
double-stash: This is some <br/> html in data
triple-stash: This is some
html in data

how to pass the string from django to javascript

I used to pass the variables in terms of dictionary to HTML template in Django without any problem. Now I plan to add some javascript into the HTML file, and use the same way "{{ }}" to obtain the object from dictionary in javascript. All the objects in the dictionary are actually strings.
In Django, the code is:
dataDict = {}
dataDict["time"] = json.dumps(",".join(timeList))
return render_to_response("xxxx.html", dataDict,\
context_instance=RequestContext(request))
And in HTML page and Javscript, I just want use a string variable to receive it and then resolve the information I want, and our code is:
var test = {{ time }};
But it seems Django cannot pass the data to the string variable in Javascript. So I have two questions:
Is there some special type of variable to pass the data from the Django to Javascript. Does JSON be possible? Or is it a must to convert string to JSON string in Django first before passing to Javascript?
How to use the delivered string in Javascript? Apparently just use var xx = xxxx; doesn't work. We think we can pass data but it seems cannot be processed.
Does anybody have some idea about it?
Thanks!
Maybe you just need to double quote it?
var test = "{{time}}";
Let's assume that dataDict['time'] = "1:30 PM, 2:30 PM". When your template is rendered it creates this text (verify it yourself w/ view source):
var test = 1:30 PM, 2:30 PM;
As you can see, this isn't valid JavaScript. When you double quote it it becomes this:
var test = "1:30 PM, 2:30 PM";
Similarly, you'll want to double quote your DOM elements with interpolated attributes, e.g., ... The Django builtin template filter docs have numerous examples of this.
It's important to keep in mind the difference between template evaluation / rendering time in your Python environment and JavaScript browser execution / evaluation time, especially when you try to pass data between the two.
Converting to JSON is recommended in order to prevent things such as spurious </script>-containing strings from causing issues with JavaScript. Assigning should be enough, since JSON strings look like JavaScript literals.

Categories