Using Angular and a Content Security Policy? Watch out for this...

This post is more than 2 years old.

Edit on July 6, 2015: Kevin H, in the comments below, pointed out that the docs for ngShow actually talk about this! I missed this completely. The solution the docs recommend, including an additional CSS file, worked fine for me, and feels like a "better" solution than mine, so I recommend following the docs lead.

I've been working on a Cordova app that uses AngularJS. It has been working fine for a while now but I needed to do a bit of cleanup. After I pushed up my changes, my client noticed something odd. We have a button that only shows up under certain situations. Basically, if some scope variable is true, we display it.

<button ng-show="featureEnabled">Feature</button>

All of a sudden, the button began showing up in all cases, even when the variable was false. I began to debug. I double checked to ensure my variable wasn't being set true when it shouldn't be. That wasn't the problem. I then added some simple tests to my view.


first test, <span ng-show="!barcodeAllowed.status">DONT SHOW</span><br/>
second test, <span ng-show="barcodeAllowed.status == false">DONT SHOW</span><br/>
test -{{barcodeAllowed.status}}-end -{{!barcodeAllowed.status}}- -{{barcodeAllowed | json}}-<br/>
test if <span ng-if="barcodeAllowed.status"> if was true</span><br/>
test opp if <span ng-if="!barcodeAllowed.status"> will see it</span>

As you can see, I checked both the negation of the variable an the variable itself. I also output it, and the negation, as well as the JSON version of the variable. Finally I did two tests using ng-if.

Here is where things got freaky. Both of the first two ng-show tests showed up! Even though the value in the third line was exactly what I expected - false.

And to make things even more weird - the ng-if's worked perfectly! At this point, I was just considering switching to ng-if, but then I punted and asked over on StackOverflow. I had a long "conversation" with user thsorens and he reminded me that ng-show/ng-hide will add/remove CSS classes based on the condition.

Then I paused and thought a bit. When I did my 'cleanup' of the app, I made a few different changes. For example, I had been using a remote Angular JS library and I switched it to a local one. I double checked to ensure it wasn't a version issue. I also made use of a Content Security Policy. I talked about this on my blog a few months ago. As a reminder, it is a way to lock down the resources your Cordova app, or any web app, can make use of. On a whim, I renamed the meta tag that defined the CSP and bammo - things worked!

I didn't want to just remove the CSP, so I dug a bit deeper. It occurred to me that JavaScript was being used to create and manipulate CSS, so I looked at the script-src I had defined:

script-src 'self'

I then though... I bet Angular is using eval. So I added this:

script-src 'self' 'unsafe-eval'

And that did it. Here is a code sample that demonstrates the issue. First, the HTML:

<!DOCTYPE html>
<html ng-app="plunker">
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta http-equiv="Content-Security-Policy" 
			content="default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; script-src 'self' https://cdnjs.cloudflare.com 'unsafe-inline' 'unsafe-eval';">

		<title>App</title>
		<meta name="description" content="">
		<meta name="viewport" content="width=device-width">

		<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.1/angular.js"></script>

		<script src="test.js"></script>
	</head>
	<body ng-controller="MainCtrl">

	    barcodeAllowed=<span ng-show="barcodeAllowed">barcode Allowed</span><br/>
	    true=<span ng-show="true">true</span><br/>
	    false=<span ng-show="false">false</span>

	</body>
</html>

And here is the JavaScript:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.barcodeAllowed = false;
});

If you remove 'unsafe-eval' from the meta tag, it stops working. If you would rather just run a live demo of this, view this Plunker I created: http://plnkr.co/edit/Hqo4G2NqwwAQU3Tu001J?p=preview. You can only see this in Chrome as it appears Firefox doesn't support CSP yet. The issue shows up in Safari as well.

Upon running the Plunker, you will see the ng-show's failing to correctly work. Just go into the meta tag and add 'unsafe-eval' to the script-src area and it will work correctly.

At the end, a very understandable issue I suppose. The lesson here is that while CSPs are a powerful tool to lock down your web app, you're going to need to look out for side effects like this.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Jim Cummins posted on 7/6/2015 at 2:18 PM

Good to see you figured it out.

Comment 2 by Pete Freitag posted on 7/6/2015 at 4:16 PM

FYI FireFox does indeed support CSP via the Content-Security-Policy header, but it does not appear to support it via the meta tag yet, bug: https://bugzilla.mozilla.or...

Comment 3 (In reply to #2) by Raymond Camden posted on 7/6/2015 at 4:30 PM

Ah, thanks Pete, I didn't know.

Comment 4 by Kevin Hakanson posted on 7/6/2015 at 4:54 PM

Hidden in the ngShow docs is "For CSP mode please add angular-csp.css to your html file (see ngCsp)." https://docs.angularjs.org/...

I forked the Plunker (http://plnkr.co/edit/q3tsI1... and added a link to angular-csp.css and it appears to work as designed now.

Does that fix your example?

Comment 5 (In reply to #4) by Raymond Camden posted on 7/6/2015 at 5:20 PM

It sure does. I wouldn't call it hidden really - but honestly - I never would have thought to have associated one with the other, know what I mean? I'm going to update the blog entry to point this out more clearly since people don't always read the comments.

Comment 6 by Woodwolf posted on 1/11/2018 at 6:22 PM

This is a few years later, and I'm working on something similar. For those that stumble on this... just adding 'unsafe-eval' here may work, but it really weakens your security and is not a recommended resolution. Our use was caught in a security review (Enterprise stuff).

Angular CSP mode (see above comments) may help out, but if you are using full JQuery or some other bad practice it may not be sufficient. Better to keep doing some more research on how to properly code JS for security, without using features that require eval or new Function... there are workarounds in most cases. hth.

Comment 7 (In reply to #6) by Raymond Camden posted on 1/11/2018 at 6:35 PM

Thanks Woodwolf. But what do you mean by "if you are using full jQuery or some other bad practice" - using jQuery is bad practice?

Comment 8 (In reply to #7) by Woodwolf posted on 1/17/2018 at 6:33 AM

Hi Raymond.. JQuery is notorious for security holes when you start looking at security from the level of Content Security Policy. By just adding 'unsafe-eval' you make the errors go away, but clever hackers can use JQuery's use of eval against you, because you have opened the doors. There are ways to disable this, function in JQuery (just look up JQuery CSP eval and you'll find them), and there are recommended ways to code JS to avoid the security holes that force you to use 'unsafe-eval' or 'unsafe-inline' (that's the 'some other bad practice' to which I was referring). It's a really huge subject so interested readers will just have to keep googling.

Comment 9 (In reply to #8) by Raymond Camden posted on 1/17/2018 at 2:34 PM

Ah - ok - thanks for the clarification!