最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Replace n with <br> tag on a model variable that I use $sce.trustAsHtml() on - Stack Overflow

programmeradmin1浏览0评论

I use the bination of the 2 following filters to first strip all the HTML from the user input from obvious reasons(preventing attacks), and than replace all the \n in
tags.

<span data-ng-bind-html-unsafe="model.userInput | noHTML | newlines"></span>

  filters.filter('newlines', function () {
      return function(text) {
        console.log(text)
          return text.replace(/\n/g, '<br/>');
      }
  })
  filters.filter('noHTML', function () {
      return function(text) {
          return Boolean(text) ? text
                  .replace(/&/g, '&amp;')
                  .replace(/>/g, '&gt;')
                  .replace(/</g, '&lt;') : '';
      }
  });

the problem is that in angular 1.2.2 bind-html-unsafe is deprecated, and you must use $sce (strict contextual escaping) to 'trust the html', which returns a function, on which I obviously cannot apply those filters.

new code:

ctrls.controllers('someCtrl', function($scope, $sce){
$scope.trusterInput = $sce.trustAsHtml($scope.userInput);
});

Error: TypeError: Object [object Object] has no method 'replace' at Scope.<anonymous> (http://localhost:8000/js/filters.js:20:20) at fnInvoke (.2.2/angular.js:9756:21) at OPERATORS.| (.2.2/angular.js:9271:59) at extend.constant (.2.2/angular.js:9701:14) at OPERATORS.| (.2.2/angular.js:9271:74) at extend.constant (.2.2/angular.js:9701:14) at Object.getStringValue (.2.2/angular.js:16990:41) at Scope.$digest (.2.2/angular.js:11494:47) at Scope.$apply (.2.2/angular.js:11740:24) at .2.2/angular.js:13265:36

Obviously becasue $sce returns a function and not a primitive(value of 'text'): TrustedValueHolderType {$$unwrapTrustedValue: function, $$unwrapTrustedValue: function, valueOf: function, toString: function, valueOf: function…} $$unwrapTrustedValue: function () { arguments: (...) get arguments: function ThrowTypeError() { [native code] } set arguments: function ThrowTypeError() { [native code] } caller: (...) get caller: function ThrowTypeError() { [native code] } set caller: function ThrowTypeError() { [native code] } length: 0 name: "" prototype: Object __proto__: function Empty() {} <function scope> __proto__: TrustedValueHolderType

Any ide on how to solve this, beside calling my filters programmatically before the $sce, which is hardly the Angular way of doing things

I use the bination of the 2 following filters to first strip all the HTML from the user input from obvious reasons(preventing attacks), and than replace all the \n in
tags.

<span data-ng-bind-html-unsafe="model.userInput | noHTML | newlines"></span>

  filters.filter('newlines', function () {
      return function(text) {
        console.log(text)
          return text.replace(/\n/g, '<br/>');
      }
  })
  filters.filter('noHTML', function () {
      return function(text) {
          return Boolean(text) ? text
                  .replace(/&/g, '&amp;')
                  .replace(/>/g, '&gt;')
                  .replace(/</g, '&lt;') : '';
      }
  });

the problem is that in angular 1.2.2 bind-html-unsafe is deprecated, and you must use $sce (strict contextual escaping) to 'trust the html', which returns a function, on which I obviously cannot apply those filters.

new code:

ctrls.controllers('someCtrl', function($scope, $sce){
$scope.trusterInput = $sce.trustAsHtml($scope.userInput);
});

Error: TypeError: Object [object Object] has no method 'replace' at Scope.<anonymous> (http://localhost:8000/js/filters.js:20:20) at fnInvoke (http://code.angularjs/1.2.2/angular.js:9756:21) at OPERATORS.| (http://code.angularjs/1.2.2/angular.js:9271:59) at extend.constant (http://code.angularjs/1.2.2/angular.js:9701:14) at OPERATORS.| (http://code.angularjs/1.2.2/angular.js:9271:74) at extend.constant (http://code.angularjs/1.2.2/angular.js:9701:14) at Object.getStringValue (http://code.angularjs/1.2.2/angular.js:16990:41) at Scope.$digest (http://code.angularjs/1.2.2/angular.js:11494:47) at Scope.$apply (http://code.angularjs/1.2.2/angular.js:11740:24) at http://code.angularjs/1.2.2/angular.js:13265:36

Obviously becasue $sce returns a function and not a primitive(value of 'text'): TrustedValueHolderType {$$unwrapTrustedValue: function, $$unwrapTrustedValue: function, valueOf: function, toString: function, valueOf: function…} $$unwrapTrustedValue: function () { arguments: (...) get arguments: function ThrowTypeError() { [native code] } set arguments: function ThrowTypeError() { [native code] } caller: (...) get caller: function ThrowTypeError() { [native code] } set caller: function ThrowTypeError() { [native code] } length: 0 name: "" prototype: Object __proto__: function Empty() {} <function scope> __proto__: TrustedValueHolderType

Any ide on how to solve this, beside calling my filters programmatically before the $sce, which is hardly the Angular way of doing things

Share Improve this question edited Apr 27, 2014 at 14:47 Emissary 10.1k9 gold badges56 silver badges67 bronze badges asked Dec 2, 2013 at 14:11 Oleg BelousovOleg Belousov 10.1k14 gold badges74 silver badges128 bronze badges 3
  • 1 If it doesn't enhance user experience in some way, escaping and removing HTML on the client side has no benefit, and certainly doesn't improve security, as that is something you do on the serverside. – adeneo Commented Dec 2, 2013 at 14:14
  • We would do it normally, but, we also have mobile clients, which don't phrase HTML, thus the server plainly returns what it gets(minus special chars). The idea is to replace new lines with <br> tags within angular, and to do it in the DOM context with $sce, not to achieve security – Oleg Belousov Commented Dec 2, 2013 at 14:20
  • /\n/g is a poor way to match newlines. You may end up with \r<br> on some platforms, which may copy/paste poorly. Perhaps /\r?\n/g. – Mike Samuel Commented Dec 2, 2013 at 14:58
Add a ment  | 

4 Answers 4

Reset to default 2

Instead of writing your own HTML escaper, just let the auto-escaper do it for you, and instead of replacing newlines with <br> just use the CSS property white-space: pre.

TypeError: Object [object Object] has no method 'replace'
at Scope.<anonymous> (http://localhost:8000/js/filters.js:20:20)

occurs because text is not a string -- the contextual auto-escaper has already done the first step for you, and converted the string to a privileged SCE safe HTML value.


$sce.trustAsHtml($scope.userInput)

is risky. Echoing a naïve user's input back to them as HTML can be dangerous if they are being socially engineered to copy/paste text that really es from an attacker.

I ran into a similar problem when trying to test the return value of $sce.trustAsHtml. In my case I wanted to verify the string as part of a unit test.

I discovered that you can use $sce.getTrusted which will return the value originally passed to $sce.trustAsHtml, in this case a string containing HTML, which you can then apply a filter to in the usual way.

Therefore the filter could look something like this:

.filter('noHTML', function ($sce) {
  return function(text) {
      text = $sce.getTrusted($sce.HTML, text); // this gets the original (unsafe) HTML as a string which can then be filtered
      var result =  Boolean(text) ? text
              .replace(/&/g, '&amp;')
              .replace(/>/g, '&gt;')
              .replace(/</g, '&lt;') : '';
      return $sce.trustAsHtml(result); // you need to do this again before returning otherwise you will get a 'Attempting to use an unsafe value in a safe context.' error
  }
});

See this fiddle I made demonstrating it: http://jsfiddle/GeTMJ/

In this way you can just apply the filter as usual and you don't have to worry about calling the filter programatically, which, as you say, is not such an 'Angular' way to do it.

I used this function to allow saving an escaped html with only newlines enabled to a scope variable that will be used in the DOM context, obviously the user input ng-model, has to point to another variable, and then when saving, applying the changes or loading the text from the backend at the first place , I am calling this function again.

$scope.layout.allowSpaces = function(input){
    return $sce.trustAsHtml($filter('newlines')($filter('noHTML')(input)));
}

I'm using this to enable HTML content in ng-bind:

app.config(function ($sceProvider) {
    $sceProvider.enabled(false);
})

It pletely disables SCE, so use it for demonstration purposes only! Do not use in new projects.

发布评论

评论列表(0)

  1. 暂无评论