Synchro's avatar

Escaping JSON data in Blade

I have this blade snippet that generates some data for chart.js:

labels: [
    @foreach ($locations as $location)
        "{{ $location->name }}",
    @endforeach
],

The problem is that if the name property contains HTML special chars, they are HTML escaped, so for example & is turned into &. This is as expected, however, the value is displayed as-is by chart.js and the encoded entities are visible.

I can make it work by disabling escaping, but that opens up the opportunity for XSS through the name property. I've tried various combinations of htmlentities and json_encode, but they all run into one problem or another.

How should I apply escaping that's appropriate for a JS context?

0 likes
10 replies
Tray2's avatar

You can use {!! $lacation->name !!} but only if you know you can trust the data retrieved.

Synchro's avatar

I know, that's what I meant by "disabling escaping". The point is that I don't want to have to trust the data; The main thing is that I'm not sure what escaping rules apply in the JS context - unescaped & is clearly fine there, but < would not be.

Synchro's avatar
Synchro
OP
Best Answer
Level 2

I've made a very ugly workaround, but it works:

{!! str_replace('&amp;', '&', htmlspecialchars($location->name)) !!}

I'd still like to hear if anyone has a better idea!

Sinnbeck's avatar

If it's json then laravel has a helper

@json($location->name) 
Synchro's avatar

@Sinnbeck It is effectively JSON in that a simple string is valid JSON. Unfortunately the @json helper passes the data through htmlspecialchars, and so results in the same inappropriate escaping.

1 like
Synchro's avatar

OK, I got something wrong here; using the json helper adds quotes around the string, and I was already inside a quoted string, so it was resulting in invalid data. I fixed it by doing all the assembly of the target string inside the json helper, and that way I could make use of its quoting instead o doing it myself. To be clear, I was doing this:

"@json($location->name) more text",

and now I'm doing:

@json($location->name . ' more text'),

Thanks for getting me to check it again :)

Sinnbeck's avatar

Can you try this?

labels: @json($locations->pluck('name'))
//or
labels: @json($locations->pluck('name')->toArray()) 

Please or to participate in this conversation.