4

I try to add bookmarklet button to my website with generating link in a controller.

Template part:

    <a id="bookmarklet"
       class="bookmarklet"
       onclick="alert('Drag and drop me to the bookmarks bar');return false;"
       href="{{getCode()}}">
      + Add
    </a>

Controller part:

$scope.getCode = function () {
  var code = 'javascript:(function(){s=document.createElement(\'SCRIPT\');s.type=\'text/javascript\';' +
    's.src=\'http://localhost:9000/scripts/bookmarklet/bookmarklet.js?x=' + ($scope.user.id) + '\';' +
    'document.getElementsByTagName(\'head\')[0].appendChild(s);document.sc_srvurl=\'http://localhost:8080\'})();'  ;
  return code;
};

But I get following after compilation:

<a class="bookmarklet" href="unsafe:javascript:(function(){s=document.createElement('SCRIPT');s.type='text/javascript';s.src='http://localhost:9000/scripts/bookmarklet/bookmarklet.js?x=5517325d40c37bc2bfe20db6';document.getElementsByTagName('head')[0].appendChild(s);document.sc_srvurl='http://localhost:8080'})();">
              + Add
            </a>

Link starts with "unsafe" and I can't get how to tell angular to trust this link.

This answer - Angular changes urls to "unsafe:" in extension page suggests to add protocol to whitelist. I don't want to disable $sce or adding "javascript" to whitelist protocols as I think it's insecure.

May I tell somehow to angularjs to avoid adding prefix "unsafe" by using $sce? Unfortunately documentation is not clear for me and $sce.trustAsJs(code) haven't helped me.

!EDIT Angular version is 1.4.1.

Community
  • 1
  • 1
Ivan Kaplin
  • 315
  • 2
  • 5
  • 11
  • 1
    possible duplicate of [Angular changes urls to "unsafe:" in extension page](http://stackoverflow.com/questions/15606751/angular-changes-urls-to-unsafe-in-extension-page) – plamut Aug 12 '15 at 15:42
  • The solution in http://stackoverflow.com/questions/15606751/angular-changes-urls-to-unsafe-in-extension-page is to add protocol to whitelist, where I am concerned with security in my case. – Ivan Kaplin Aug 12 '15 at 15:53
  • I don't think you have any other options. And you wouldn't add 'javascript', you would add 'chrome-extension' to the whitelist. – Matthew Green Aug 12 '15 at 16:06
  • That's a valid concern indeed. One alternative way I can think of is creating a directive and setting the value of the `href` attribute there (by manipulating it directly on the directive's DOM element). Is that feasible for your use case? – plamut Aug 12 '15 at 16:06
  • @plamut It's not clear for me how workaround with directive can help. I am wondering is there any direct way to tell angular to trust this url? – Ivan Kaplin Aug 12 '15 at 16:19
  • @IvanKaplin Directives work directly with DOM elements and setting an attribute value circumvents Angular's security mechanisms and policies for "valid" links. You can thus set the `href` attribute to anything you want without having to add anything to `$compileProvider`'s whitelist. – plamut Aug 12 '15 at 16:28

1 Answers1

2

Try to circumvent the restrictions by writing a custom directive:

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

app.directive('bookmarklet', function () {
    return {
        restrict: 'A',
        scope: {},
        link: function($scope, element, attrs) {
            if (element[0].tagName !== 'A') {
                return;  // simply do nothing (or raise an error)
            }
            element[0].href = 'javascript:alert("It works in 1.4.1, too!")';
        }
    };
 });

Usage:

<div ng-app="myApp">
    <a href="#foo" bookmarklet>click me</a>
</div>

I also created a fiddle demo to test it: http://jsfiddle.net/t0acdxcL/1/

plamut
  • 3,085
  • 10
  • 29
  • 40
  • I have now fixed the link to the demo. – plamut Aug 12 '15 at 16:31
  • Thank you! I see that your fiddle works and there is clear 'javascript:alert("It works!")' without unsafe. However I tried it in my project and it still has unsafe href="unsafe:javascript:alert('It works!')". Wondering what is the difference. – Ivan Kaplin Aug 12 '15 at 18:50
  • I have updated angular dependency to 1.4.1 and it reproduces. See this fiddle - http://jsfiddle.net/ikaplin/8rm57bwf/2/ – Ivan Kaplin Aug 12 '15 at 19:00
  • I will add to question that angular version is 1.4.1 – Ivan Kaplin Aug 12 '15 at 19:05
  • 1
    Updated my answer and the demo, it now works in 1.4.1, too. Seems like `attrs.$set(...)` behavior was changed at some point, but it's trivial to get around that. Setting the attribute value directly on the element does the trick, i.e. `element[0].href = ...`. – plamut Aug 12 '15 at 22:43