Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 1.2: ng-bind-html removes src attribute on img

I'm working on a CMS driven website using Angular (1.2 RC2) on the client side. As the content comes from a CMS I'm unfortunately forced to use receive HTML in JSON Strings. Most of the HTML gets injected and rendered just fine, but image tags have their src stripped away. From what I've read the src should have been prefixed with "unsafe:", not removed entirely if this was a XSS security issue in Angular .. but I might be mistaken.

I've been at this problem for a while now and feel I've tried everything from plausible to downright stupid. Whitelisting my CMS url, whitelisting everything, disabling $sce, forcing html trough $sce.trustAsHtml() and well ... So far, no luck. As the site is CMS driven I'm unfortunatly not able to create a plunker / fiddle, but I'm hoping a kind soul will try to help out anyway.

Update: Forgot to mention that I've also tried ng-src, same effect. Update II: If I use a HTTPS the src attribute remains and the image is shown. This is acceptable as it will run on HTTPS in production, but I still would like to know why disabling $sce doesn't work.

HTML as seen by the browser (content of slide.body)

<div class="row">
    <div class="col-md-6 visible-md visible-lg">
        <img alt="none" class="img-responsive">
    </div>
    <div class="col-xs-12 col-md-6">
        <div class="itx-article-header">
             <h1>Sulvat Quis 1</h1>

             <h2>– Nullam dictum ac enim</h2>

        </div>
        <p>Proin quis justo vel felis varius sodales sit amet ut diam.</p>
    </div>
</div>

JADE (HTML)

.my-carousel(ng-controller='CarouselCtrl')
    carousel(interval='myInterval')
        slide(ng-repeat='slide in slides', active='slide.active')
            .my-carousel-item(ng-bind-html='slide.body')

Angular Controller

.controller('CarouselCtrl', ['$sce', 'Article', '$scope',
    function($sce, Article, $scope) {
        $scope.myInterval = -1;
        $scope.slides = Article.query(
            {category: 'carousel'},
            function(data){
                for (var i = 0; i < data.length; i++) {
                    $scope.slides[i].body = $sce.trustAsHtml(data[i].body);                     
                }
            },
            function() {
                // Fail
            });
    }])

Example JSON Response (Slide) Sorry about the wide box, didn't manage to format it.

{
"cmarId": 16,
"corp": {
    "corpId": 2,
    "guiSelected": false
},
"createdAt": "Sep 27, 2013",
"articleTag": "slide-1",
"headline": "Slide 1",
"highlighted": false,
  "body": "\u003cdiv class\u003d\"container my-carousel-container-small\"\u003e\r\n\u003cdiv class\u003d\"row\"\u003e\r\n\u003cdiv class\u003d\"col-md-6 visible-md visible-lg\"\u003e\u003cimg ng-src\u003d\"img/illustrative/laptop.jpg\" alt\u003d\"none\" class\u003d\"img-responsive\" /\u003e\u003c/div\u003e\r\n\u003cdiv class\u003d\"col-xs-12 col-md-6\"\u003e\r\n\u003cdiv class\u003d\"my-article-header\"\u003e\r\n\u003ch1\u003eSulvat Quis 1\u003c/h1\u003e\r\n\u003ch2\u003e– Nullam dictum ac enim\u003c/h2\u003e\r\n\u003c/div\u003e\r\n\u003cp\u003eProin quis justo vel felis varius sodales sit amet ut diam. Fusce auctor sapien nec purus sagittis, in venenatis turpis luctus. Nullam dictum ac enim sed commodo. Vivamus et placerat sapien.\u003c/p\u003e\r\n\u003c/div\u003e\r\n\u003c/div\u003e\r\n\u003c/div\u003e",
"articlePriority": 0,
"category": {
    "cmcaId": 9,
    "corp": {
        "corpId": 2,
        "guiSelected": false
    },
    "name": "carousel",
    "visibleInMenu": false
},
"published": true

}

like image 972
Index Avatar asked Sep 27 '13 09:09

Index


Video Answer


3 Answers

The piece you're missing here is trusting the image url via $sce.trustAsResourceURL();. See here for the relevant documentation.

EDIT: Also, it appears you're not wrapping the ng-src value in quotes (as well as the required quotes for it being an HTML attribute). That won't work--ng-src expects a javascript string as the final result of the expression, and you're providing it with an invalid javascript literal.

like image 126
Jeff Hubbard Avatar answered Sep 22 '22 10:09

Jeff Hubbard


See this comment on the AngularUI discussion: https://github.com/angular-ui/bootstrap/issues/813#issuecomment-25760432

It is a directive which reimplements the old bind-html-unsafe feature which was present in AngularJS pre 1.2. In the end this will do the same as suggested above but with a minimum of hassle. So you could just use ng-bind-html-unsafe and not make your code less readable by having to add the $sce bypass in your main code.

See also this Stackoverflow question: How do you use $sce.trustAsHtml(string) to replicate ng-bind-html-unsafe in Angular 1.2+

like image 42
Juliane Holzt Avatar answered Sep 24 '22 10:09

Juliane Holzt


Reference: The AngularJS documentation for $sanitize has a demo. In this demo, if you use src instead of ng-src and ng-bind-html to a function (which returns an object from $sce.trustAsHtml()), the image appears as expected.


Suggested edits: I would first replace ng-src with src.

Then I would change

for (var i = 0; i < data.length; i++) {
    $scope.slides[i].body = $sce.trustAsHtml(data[i].body);                     
}

to

for (var i = 0; i < data.length; i++) {
    $scope.slides[i].body = function() {
        return $sce.trustAsHtml(data[i].body);
    };
}

Finally, this

.my-carousel-item(ng-bind-html='slide.body')

would change to

.my-carousel-item(ng-bind-html='slide.body()')

I have not tested this myself, but please let me know if this works.


Edit (2013-11-02): Typo fix — I added return to the function example. The code should work as expected now.

like image 23
Tyler Eich Avatar answered Sep 26 '22 10:09

Tyler Eich