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

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.

Like This?

If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can also subscribe to the email feed to get notified of new posts.

See Also