GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (3063)

angularjs/angular-1.6.5/angular-sanitize.js (4 issues)

1
/**
2
 * @license AngularJS v1.6.5
3
 * (c) 2010-2017 Google, Inc. http://angularjs.org
4
 * License: MIT
5
 */
6
(function(window, angular) {'use strict';
7
8
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9
 *     Any commits to this file should be reviewed with security in mind.  *
10
 *   Changes to this file can potentially create security vulnerabilities. *
11
 *          An approval from 2 Core members with history of modifying      *
12
 *                         this file is required.                          *
13
 *                                                                         *
14
 *  Does the change somehow allow for arbitrary javascript to be executed? *
15
 *    Or allows for someone to change the prototype of built-in objects?   *
16
 *     Or gives undesired access to variables likes document or window?    *
17
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
18
19
var $sanitizeMinErr = angular.$$minErr('$sanitize');
20
var bind;
21
var extend;
22
var forEach;
23
var isDefined;
24
var lowercase;
25
var noop;
26
var nodeContains;
27
var htmlParser;
28
var htmlSanitizeWriter;
29
30
/**
31
 * @ngdoc module
32
 * @name ngSanitize
33
 * @description
34
 *
35
 * # ngSanitize
36
 *
37
 * The `ngSanitize` module provides functionality to sanitize HTML.
38
 *
39
 *
40
 * <div doc-module-components="ngSanitize"></div>
41
 *
42
 * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
43
 */
44
45
/**
46
 * @ngdoc service
47
 * @name $sanitize
48
 * @kind function
49
 *
50
 * @description
51
 *   Sanitizes an html string by stripping all potentially dangerous tokens.
52
 *
53
 *   The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
54
 *   then serialized back to properly escaped html string. This means that no unsafe input can make
55
 *   it into the returned string.
56
 *
57
 *   The whitelist for URL sanitization of attribute values is configured using the functions
58
 *   `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider
59
 *   `$compileProvider`}.
60
 *
61
 *   The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}.
62
 *
63
 * @param {string} html HTML input.
64
 * @returns {string} Sanitized HTML.
65
 *
66
 * @example
67
   <example module="sanitizeExample" deps="angular-sanitize.js" name="sanitize-service">
68
   <file name="index.html">
69
     <script>
70
         angular.module('sanitizeExample', ['ngSanitize'])
71
           .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
72
             $scope.snippet =
73
               '<p style="color:blue">an html\n' +
74
               '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
75
               'snippet</p>';
76
             $scope.deliberatelyTrustDangerousSnippet = function() {
77
               return $sce.trustAsHtml($scope.snippet);
78
             };
79
           }]);
80
     </script>
81
     <div ng-controller="ExampleController">
82
        Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
83
       <table>
84
         <tr>
85
           <td>Directive</td>
86
           <td>How</td>
87
           <td>Source</td>
88
           <td>Rendered</td>
89
         </tr>
90
         <tr id="bind-html-with-sanitize">
91
           <td>ng-bind-html</td>
92
           <td>Automatically uses $sanitize</td>
93
           <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
94
           <td><div ng-bind-html="snippet"></div></td>
95
         </tr>
96
         <tr id="bind-html-with-trust">
97
           <td>ng-bind-html</td>
98
           <td>Bypass $sanitize by explicitly trusting the dangerous value</td>
99
           <td>
100
           <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
101
&lt;/div&gt;</pre>
102
           </td>
103
           <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
104
         </tr>
105
         <tr id="bind-default">
106
           <td>ng-bind</td>
107
           <td>Automatically escapes</td>
108
           <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
109
           <td><div ng-bind="snippet"></div></td>
110
         </tr>
111
       </table>
112
       </div>
113
   </file>
114
   <file name="protractor.js" type="protractor">
115
     it('should sanitize the html snippet by default', function() {
116
       expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).
117
         toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
118
     });
119
120
     it('should inline raw snippet if bound to a trusted value', function() {
121
       expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).
122
         toBe("<p style=\"color:blue\">an html\n" +
123
              "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
124
              "snippet</p>");
125
     });
126
127
     it('should escape snippet without any filter', function() {
128
       expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).
129
         toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
130
              "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
131
              "snippet&lt;/p&gt;");
132
     });
133
134
     it('should update', function() {
135
       element(by.model('snippet')).clear();
136
       element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
137
       expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')).
138
         toBe('new <b>text</b>');
139
       expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe(
140
         'new <b onclick="alert(1)">text</b>');
141
       expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe(
142
         "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
143
     });
144
   </file>
145
   </example>
146
 */
147
148
149
/**
150
 * @ngdoc provider
151
 * @name $sanitizeProvider
152
 * @this
153
 *
154
 * @description
155
 * Creates and configures {@link $sanitize} instance.
156
 */
157
function $SanitizeProvider() {
158
  var svgEnabled = false;
159
160
  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
161
    if (svgEnabled) {
162
      extend(validElements, svgElements);
163
    }
164
    return function(html) {
165
      var buf = [];
166
      htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
167
        return !/^unsafe:/.test($$sanitizeUri(uri, isImage));
168
      }));
169
      return buf.join('');
170
    };
171
  }];
172
173
174
  /**
175
   * @ngdoc method
176
   * @name $sanitizeProvider#enableSvg
177
   * @kind function
178
   *
179
   * @description
180
   * Enables a subset of svg to be supported by the sanitizer.
181
   *
182
   * <div class="alert alert-warning">
183
   *   <p>By enabling this setting without taking other precautions, you might expose your
184
   *   application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned
185
   *   outside of the containing element and be rendered over other elements on the page (e.g. a login
186
   *   link). Such behavior can then result in phishing incidents.</p>
187
   *
188
   *   <p>To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
189
   *   tags within the sanitized content:</p>
190
   *
191
   *   <br>
192
   *
193
   *   <pre><code>
194
   *   .rootOfTheIncludedContent svg {
195
   *     overflow: hidden !important;
196
   *   }
197
   *   </code></pre>
198
   * </div>
199
   *
200
   * @param {boolean=} flag Enable or disable SVG support in the sanitizer.
201
   * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called
202
   *    without an argument or self for chaining otherwise.
203
   */
204
  this.enableSvg = function(enableSvg) {
205
    if (isDefined(enableSvg)) {
206
      svgEnabled = enableSvg;
207
      return this;
208
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
209
      return svgEnabled;
210
    }
211
  };
212
213
  //////////////////////////////////////////////////////////////////////////////////////////////////
214
  // Private stuff
215
  //////////////////////////////////////////////////////////////////////////////////////////////////
216
217
  bind = angular.bind;
218
  extend = angular.extend;
219
  forEach = angular.forEach;
220
  isDefined = angular.isDefined;
221
  lowercase = angular.lowercase;
222
  noop = angular.noop;
223
224
  htmlParser = htmlParserImpl;
225
  htmlSanitizeWriter = htmlSanitizeWriterImpl;
226
227
  nodeContains = window.Node.prototype.contains || /** @this */ function(arg) {
228
    // eslint-disable-next-line no-bitwise
229
    return !!(this.compareDocumentPosition(arg) & 16);
230
  };
231
232
  // Regular Expressions for parsing tags and attributes
233
  var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
234
    // Match everything outside of normal chars and " (quote character)
235
    NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g;
236
237
238
  // Good source of info about elements and attributes
239
  // http://dev.w3.org/html5/spec/Overview.html#semantics
240
  // http://simon.html5.org/html-elements
241
242
  // Safe Void Elements - HTML5
243
  // http://dev.w3.org/html5/spec/Overview.html#void-elements
244
  var voidElements = toMap('area,br,col,hr,img,wbr');
245
246
  // Elements that you can, intentionally, leave open (and which close themselves)
247
  // http://dev.w3.org/html5/spec/Overview.html#optional-tags
248
  var optionalEndTagBlockElements = toMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'),
249
      optionalEndTagInlineElements = toMap('rp,rt'),
250
      optionalEndTagElements = extend({},
251
                                              optionalEndTagInlineElements,
252
                                              optionalEndTagBlockElements);
253
254
  // Safe Block Elements - HTML5
255
  var blockElements = extend({}, optionalEndTagBlockElements, toMap('address,article,' +
256
          'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
257
          'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul'));
258
259
  // Inline Elements - HTML5
260
  var inlineElements = extend({}, optionalEndTagInlineElements, toMap('a,abbr,acronym,b,' +
261
          'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' +
262
          'samp,small,span,strike,strong,sub,sup,time,tt,u,var'));
263
264
  // SVG Elements
265
  // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
266
  // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
267
  // They can potentially allow for arbitrary javascript to be executed. See #11290
268
  var svgElements = toMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' +
269
          'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' +
270
          'radialGradient,rect,stop,svg,switch,text,title,tspan');
271
272
  // Blocked Elements (will be stripped)
273
  var blockedElements = toMap('script,style');
274
275
  var validElements = extend({},
276
                                     voidElements,
277
                                     blockElements,
278
                                     inlineElements,
279
                                     optionalEndTagElements);
280
281
  //Attributes that have href and hence need to be sanitized
282
  var uriAttrs = toMap('background,cite,href,longdesc,src,xlink:href');
283
284
  var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
285
      'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
286
      'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
287
      'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
288
      'valign,value,vspace,width');
289
290
  // SVG attributes (without "id" and "name" attributes)
291
  // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
292
  var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
293
      'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
294
      'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
295
      'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
296
      'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
297
      'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
298
      'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
299
      'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
300
      'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
301
      'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
302
      'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
303
      'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
304
      'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
305
      'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
306
      'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
307
308
  var validAttrs = extend({},
309
                                  uriAttrs,
310
                                  svgAttrs,
311
                                  htmlAttrs);
312
313
  function toMap(str, lowercaseKeys) {
314
    var obj = {}, items = str.split(','), i;
315
    for (i = 0; i < items.length; i++) {
316
      obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
317
    }
318
    return obj;
319
  }
320
321
  /**
322
   * Create an inert document that contains the dirty HTML that needs sanitizing
323
   * Depending upon browser support we use one of three strategies for doing this.
324
   * Support: Safari 10.x -> XHR strategy
325
   * Support: Firefox -> DomParser strategy
326
   */
327
  var getInertBodyElement /* function(html: string): HTMLBodyElement */ = (function(window, document) {
328
    var inertDocument;
329
    if (document && document.implementation) {
330
      inertDocument = document.implementation.createHTMLDocument('inert');
331
    } else {
332
      throw $sanitizeMinErr('noinert', 'Can\'t create an inert html document');
333
    }
334
    var inertBodyElement = (inertDocument.documentElement || inertDocument.getDocumentElement()).querySelector('body');
335
336
    // Check for the Safari 10.1 bug - which allows JS to run inside the SVG G element
337
    inertBodyElement.innerHTML = '<svg><g onload="this.parentNode.remove()"></g></svg>';
338
    if (!inertBodyElement.querySelector('svg')) {
339
      return getInertBodyElement_XHR;
340
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
341
      // Check for the Firefox bug - which prevents the inner img JS from being sanitized
342
      inertBodyElement.innerHTML = '<svg><p><style><img src="</style><img src=x onerror=alert(1)//">';
343
      if (inertBodyElement.querySelector('svg img')) {
344
        return getInertBodyElement_DOMParser;
345
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
346
        return getInertBodyElement_InertDocument;
347
      }
348
    }
349
350
    function getInertBodyElement_XHR(html) {
351
      // We add this dummy element to ensure that the rest of the content is parsed as expected
352
      // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the `<head>` tag.
353
      html = '<remove></remove>' + html;
354
      try {
355
        html = encodeURI(html);
356
      } catch (e) {
357
        return undefined;
358
      }
359
      var xhr = new window.XMLHttpRequest();
360
      xhr.responseType = 'document';
361
      xhr.open('GET', 'data:text/html;charset=utf-8,' + html, false);
362
      xhr.send(null);
363
      var body = xhr.response.body;
364
      body.firstChild.remove();
365
      return body;
366
    }
367
368
    function getInertBodyElement_DOMParser(html) {
369
      // We add this dummy element to ensure that the rest of the content is parsed as expected
370
      // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the `<head>` tag.
371
      html = '<remove></remove>' + html;
372
      try {
373
        var body = new window.DOMParser().parseFromString(html, 'text/html').body;
374
        body.firstChild.remove();
375
        return body;
376
      } catch (e) {
377
        return undefined;
378
      }
379
    }
380
381
    function getInertBodyElement_InertDocument(html) {
382
      inertBodyElement.innerHTML = html;
383
384
      // Support: IE 9-11 only
385
      // strip custom-namespaced attributes on IE<=11
386
      if (document.documentMode) {
387
        stripCustomNsAttrs(inertBodyElement);
388
      }
389
390
      return inertBodyElement;
391
    }
392
  })(window, window.document);
393
394
  /**
395
   * @example
396
   * htmlParser(htmlString, {
397
   *     start: function(tag, attrs) {},
398
   *     end: function(tag) {},
399
   *     chars: function(text) {},
400
   *     comment: function(text) {}
401
   * });
402
   *
403
   * @param {string} html string
404
   * @param {object} handler
405
   */
406
  function htmlParserImpl(html, handler) {
407
    if (html === null || html === undefined) {
408
      html = '';
409
    } else if (typeof html !== 'string') {
410
      html = '' + html;
411
    }
412
413
    var inertBodyElement = getInertBodyElement(html);
414
    if (!inertBodyElement) return '';
415
416
    //mXSS protection
417
    var mXSSAttempts = 5;
418
    do {
419
      if (mXSSAttempts === 0) {
420
        throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable');
421
      }
422
      mXSSAttempts--;
423
424
      // trigger mXSS if it is going to happen by reading and writing the innerHTML
425
      html = inertBodyElement.innerHTML;
426
      inertBodyElement = getInertBodyElement(html);
427
    } while (html !== inertBodyElement.innerHTML);
428
429
    var node = inertBodyElement.firstChild;
430 View Code Duplication
    while (node) {
431
      switch (node.nodeType) {
432
        case 1: // ELEMENT_NODE
433
          handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
434
          break;
435
        case 3: // TEXT NODE
436
          handler.chars(node.textContent);
437
          break;
438
      }
439
440
      var nextNode;
441
      if (!(nextNode = node.firstChild)) {
442
        if (node.nodeType === 1) {
443
          handler.end(node.nodeName.toLowerCase());
444
        }
445
        nextNode = getNonDescendant('nextSibling', node);
446
        if (!nextNode) {
447
          while (nextNode == null) {
448
            node = getNonDescendant('parentNode', node);
449
            if (node === inertBodyElement) break;
450
            nextNode = getNonDescendant('nextSibling', node);
451
            if (node.nodeType === 1) {
452
              handler.end(node.nodeName.toLowerCase());
453
            }
454
          }
455
        }
456
      }
457
      node = nextNode;
458
    }
459
460
    while ((node = inertBodyElement.firstChild)) {
461
      inertBodyElement.removeChild(node);
462
    }
463
  }
464
465
  function attrToMap(attrs) {
466
    var map = {};
467
    for (var i = 0, ii = attrs.length; i < ii; i++) {
468
      var attr = attrs[i];
469
      map[attr.name] = attr.value;
470
    }
471
    return map;
472
  }
473
474
475
  /**
476
   * Escapes all potentially dangerous characters, so that the
477
   * resulting string can be safely inserted into attribute or
478
   * element text.
479
   * @param value
480
   * @returns {string} escaped text
481
   */
482 View Code Duplication
  function encodeEntities(value) {
483
    return value.
484
      replace(/&/g, '&amp;').
485
      replace(SURROGATE_PAIR_REGEXP, function(value) {
486
        var hi = value.charCodeAt(0);
487
        var low = value.charCodeAt(1);
488
        return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
489
      }).
490
      replace(NON_ALPHANUMERIC_REGEXP, function(value) {
491
        return '&#' + value.charCodeAt(0) + ';';
492
      }).
493
      replace(/</g, '&lt;').
494
      replace(/>/g, '&gt;');
495
  }
496
497
  /**
498
   * create an HTML/XML writer which writes to buffer
499
   * @param {Array} buf use buf.join('') to get out sanitized html string
500
   * @returns {object} in the form of {
501
   *     start: function(tag, attrs) {},
502
   *     end: function(tag) {},
503
   *     chars: function(text) {},
504
   *     comment: function(text) {}
505
   * }
506
   */
507 View Code Duplication
  function htmlSanitizeWriterImpl(buf, uriValidator) {
508
    var ignoreCurrentElement = false;
509
    var out = bind(buf, buf.push);
510
    return {
511
      start: function(tag, attrs) {
512
        tag = lowercase(tag);
513
        if (!ignoreCurrentElement && blockedElements[tag]) {
514
          ignoreCurrentElement = tag;
515
        }
516
        if (!ignoreCurrentElement && validElements[tag] === true) {
517
          out('<');
518
          out(tag);
519
          forEach(attrs, function(value, key) {
520
            var lkey = lowercase(key);
521
            var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
522
            if (validAttrs[lkey] === true &&
523
              (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
524
              out(' ');
525
              out(key);
526
              out('="');
527
              out(encodeEntities(value));
528
              out('"');
529
            }
530
          });
531
          out('>');
532
        }
533
      },
534
      end: function(tag) {
535
        tag = lowercase(tag);
536
        if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
537
          out('</');
538
          out(tag);
539
          out('>');
540
        }
541
        // eslint-disable-next-line eqeqeq
542
        if (tag == ignoreCurrentElement) {
543
          ignoreCurrentElement = false;
544
        }
545
      },
546
      chars: function(chars) {
547
        if (!ignoreCurrentElement) {
548
          out(encodeEntities(chars));
549
        }
550
      }
551
    };
552
  }
553
554
555
  /**
556
   * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
557
   * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
558
   * to allow any of these custom attributes. This method strips them all.
559
   *
560
   * @param node Root element to process
561
   */
562 View Code Duplication
  function stripCustomNsAttrs(node) {
563
    while (node) {
564
      if (node.nodeType === window.Node.ELEMENT_NODE) {
565
        var attrs = node.attributes;
566
        for (var i = 0, l = attrs.length; i < l; i++) {
567
          var attrNode = attrs[i];
568
          var attrName = attrNode.name.toLowerCase();
569
          if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
570
            node.removeAttributeNode(attrNode);
571
            i--;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
572
            l--;
573
          }
574
        }
575
      }
576
577
      var nextNode = node.firstChild;
578
      if (nextNode) {
579
        stripCustomNsAttrs(nextNode);
580
      }
581
582
      node = getNonDescendant('nextSibling', node);
583
    }
584
  }
585
586
  function getNonDescendant(propName, node) {
587
    // An element is clobbered if its `propName` property points to one of its descendants
588
    var nextNode = node[propName];
589
    if (nextNode && nodeContains.call(node, nextNode)) {
590
      throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText);
591
    }
592
    return nextNode;
593
  }
594
}
595
596
function sanitizeText(chars) {
597
  var buf = [];
598
  var writer = htmlSanitizeWriter(buf, noop);
599
  writer.chars(chars);
600
  return buf.join('');
601
}
602
603
604
// define ngSanitize module and register $sanitize service
605
angular.module('ngSanitize', [])
606
  .provider('$sanitize', $SanitizeProvider)
607
  .info({ angularVersion: '1.6.5' });
608
609
/**
610
 * @ngdoc filter
611
 * @name linky
612
 * @kind function
613
 *
614
 * @description
615
 * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and
616
 * plain email address links.
617
 *
618
 * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
619
 *
620
 * @param {string} text Input text.
621
 * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in.
622
 * @param {object|function(url)} [attributes] Add custom attributes to the link element.
623
 *
624
 *    Can be one of:
625
 *
626
 *    - `object`: A map of attributes
627
 *    - `function`: Takes the url as a parameter and returns a map of attributes
628
 *
629
 *    If the map of attributes contains a value for `target`, it overrides the value of
630
 *    the target parameter.
631
 *
632
 *
633
 * @returns {string} Html-linkified and {@link $sanitize sanitized} text.
634
 *
635
 * @usage
636
   <span ng-bind-html="linky_expression | linky"></span>
637
 *
638
 * @example
639
   <example module="linkyExample" deps="angular-sanitize.js" name="linky-filter">
640
     <file name="index.html">
641
       <div ng-controller="ExampleController">
642
       Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
643
       <table>
644
         <tr>
645
           <th>Filter</th>
646
           <th>Source</th>
647
           <th>Rendered</th>
648
         </tr>
649
         <tr id="linky-filter">
650
           <td>linky filter</td>
651
           <td>
652
             <pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
653
           </td>
654
           <td>
655
             <div ng-bind-html="snippet | linky"></div>
656
           </td>
657
         </tr>
658
         <tr id="linky-target">
659
          <td>linky target</td>
660
          <td>
661
            <pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
662
          </td>
663
          <td>
664
            <div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div>
665
          </td>
666
         </tr>
667
         <tr id="linky-custom-attributes">
668
          <td>linky custom attributes</td>
669
          <td>
670
            <pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"&gt;<br>&lt;/div&gt;</pre>
671
          </td>
672
          <td>
673
            <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"></div>
674
          </td>
675
         </tr>
676
         <tr id="escaped-html">
677
           <td>no filter</td>
678
           <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
679
           <td><div ng-bind="snippet"></div></td>
680
         </tr>
681
       </table>
682
     </file>
683
     <file name="script.js">
684
       angular.module('linkyExample', ['ngSanitize'])
685
         .controller('ExampleController', ['$scope', function($scope) {
686
           $scope.snippet =
687
             'Pretty text with some links:\n' +
688
             'http://angularjs.org/,\n' +
689
             'mailto:[email protected],\n' +
690
             '[email protected],\n' +
691
             'and one more: ftp://127.0.0.1/.';
692
           $scope.snippetWithSingleURL = 'http://angularjs.org/';
693
         }]);
694
     </file>
695
     <file name="protractor.js" type="protractor">
696
       it('should linkify the snippet with urls', function() {
697
         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
698
             toBe('Pretty text with some links: http://angularjs.org/, [email protected], ' +
699
                  '[email protected], and one more: ftp://127.0.0.1/.');
700
         expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
701
       });
702
703
       it('should not linkify snippet without the linky filter', function() {
704
         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
705
             toBe('Pretty text with some links: http://angularjs.org/, mailto:[email protected], ' +
706
                  '[email protected], and one more: ftp://127.0.0.1/.');
707
         expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
708
       });
709
710
       it('should update', function() {
711
         element(by.model('snippet')).clear();
712
         element(by.model('snippet')).sendKeys('new http://link.');
713
         expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
714
             toBe('new http://link.');
715
         expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
716
         expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
717
             .toBe('new http://link.');
718
       });
719
720
       it('should work with the target property', function() {
721
        expect(element(by.id('linky-target')).
722
            element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
723
            toBe('http://angularjs.org/');
724
        expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
725
       });
726
727
       it('should optionally add custom attributes', function() {
728
        expect(element(by.id('linky-custom-attributes')).
729
            element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
730
            toBe('http://angularjs.org/');
731
        expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
732
       });
733
     </file>
734
   </example>
735
 */
736 View Code Duplication
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
737
  var LINKY_URL_REGEXP =
738
        /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
739
      MAILTO_REGEXP = /^mailto:/i;
740
741
  var linkyMinErr = angular.$$minErr('linky');
742
  var isDefined = angular.isDefined;
743
  var isFunction = angular.isFunction;
744
  var isObject = angular.isObject;
745
  var isString = angular.isString;
746
747
  return function(text, target, attributes) {
748
    if (text == null || text === '') return text;
749
    if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
750
751
    var attributesFn =
752
      isFunction(attributes) ? attributes :
753
      isObject(attributes) ? function getAttributesObject() {return attributes;} :
754
      function getEmptyAttributesObject() {return {};};
755
756
    var match;
757
    var raw = text;
758
    var html = [];
759
    var url;
760
    var i;
761
    while ((match = raw.match(LINKY_URL_REGEXP))) {
762
      // We can not end in these as they are sometimes found at the end of the sentence
763
      url = match[0];
764
      // if we did not match ftp/http/www/mailto then assume mailto
765
      if (!match[2] && !match[4]) {
766
        url = (match[3] ? 'http://' : 'mailto:') + url;
767
      }
768
      i = match.index;
769
      addText(raw.substr(0, i));
770
      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
771
      raw = raw.substring(i + match[0].length);
772
    }
773
    addText(raw);
774
    return $sanitize(html.join(''));
775
776
    function addText(text) {
777
      if (!text) {
778
        return;
779
      }
780
      html.push(sanitizeText(text));
781
    }
782
783
    function addLink(url, text) {
784
      var key, linkAttributes = attributesFn(url);
785
      html.push('<a ');
786
787
      for (key in linkAttributes) {
788
        html.push(key + '="' + linkAttributes[key] + '" ');
789
      }
790
791
      if (isDefined(target) && !('target' in linkAttributes)) {
792
        html.push('target="',
793
                  target,
794
                  '" ');
795
      }
796
      html.push('href="',
797
                url.replace(/"/g, '&quot;'),
798
                '">');
799
      addText(text);
800
      html.push('</a>');
801
    }
802
  };
803
}]);
804
805
806
})(window, window.angular);
807