├── LICENSE.md ├── README.md ├── angular-sanitize.js ├── angular-sanitize.min.js ├── angular-sanitize.min.js.map ├── bower.json ├── index.js └── package.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Angular 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # packaged angular-sanitize 2 | 3 | **This package contains the legacy AngularJS (version 1.x). AngularJS support has officially ended 4 | as of January 2022. 5 | [See what ending support means](https://docs.angularjs.org/misc/version-support-status) and 6 | [read the end of life announcement](https://goo.gle/angularjs-end-of-life).** 7 | 8 | **[See `@angular/core` for the actively supported Angular](https://npmjs.com/@angular/core).** 9 | 10 | ## Install 11 | 12 | You can install this package either with `npm` or with `bower`. 13 | 14 | ### npm 15 | 16 | ```shell 17 | npm install angular-sanitize 18 | ``` 19 | 20 | Then add `ngSanitize` as a dependency for your app: 21 | 22 | ```javascript 23 | angular.module('myApp', [require('angular-sanitize')]); 24 | ``` 25 | 26 | ### bower 27 | 28 | ```shell 29 | bower install angular-sanitize 30 | ``` 31 | 32 | Add a ` 36 | ``` 37 | 38 | Then add `ngSanitize` as a dependency for your app: 39 | 40 | ```javascript 41 | angular.module('myApp', ['ngSanitize']); 42 | ``` 43 | 44 | ## Documentation 45 | 46 | Documentation is available on the 47 | [AngularJS docs site](http://docs.angularjs.org/api/ngSanitize). 48 | 49 | ## License 50 | 51 | The MIT License 52 | 53 | Copyright (c) 2022 Google LLC 54 | 55 | Permission is hereby granted, free of charge, to any person obtaining a copy 56 | of this software and associated documentation files (the "Software"), to deal 57 | in the Software without restriction, including without limitation the rights 58 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 59 | copies of the Software, and to permit persons to whom the Software is 60 | furnished to do so, subject to the following conditions: 61 | 62 | The above copyright notice and this permission notice shall be included in 63 | all copies or substantial portions of the Software. 64 | 65 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 66 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 67 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 68 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 69 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 70 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 71 | THE SOFTWARE. 72 | -------------------------------------------------------------------------------- /angular-sanitize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.8.3 3 | * (c) 2010-2020 Google LLC. 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 isArray; 24 | var isDefined; 25 | var lowercase; 26 | var noop; 27 | var nodeContains; 28 | var htmlParser; 29 | var htmlSanitizeWriter; 30 | 31 | /** 32 | * @ngdoc module 33 | * @name ngSanitize 34 | * @description 35 | * 36 | * The `ngSanitize` module provides functionality to sanitize HTML. 37 | * 38 | * See {@link ngSanitize.$sanitize `$sanitize`} for usage. 39 | */ 40 | 41 | /** 42 | * @ngdoc service 43 | * @name $sanitize 44 | * @kind function 45 | * 46 | * @description 47 | * Sanitizes an html string by stripping all potentially dangerous tokens. 48 | * 49 | * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a trusted URI list) are 50 | * then serialized back to a properly escaped HTML string. This means that no unsafe input can make 51 | * it into the returned string. 52 | * 53 | * The trusted URIs for URL sanitization of attribute values is configured using the functions 54 | * `aHrefSanitizationTrustedUrlList` and `imgSrcSanitizationTrustedUrlList` of {@link $compileProvider}. 55 | * 56 | * The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}. 57 | * 58 | * @param {string} html HTML input. 59 | * @returns {string} Sanitized HTML. 60 | * 61 | * @example 62 | 63 | 64 | 76 |
77 | Snippet: 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
DirectiveHowSourceRendered
ng-bind-htmlAutomatically uses $sanitize
<div ng-bind-html="snippet">
</div>
ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value 95 |
<div ng-bind-html="deliberatelyTrustDangerousSnippet()">
 96 | </div>
97 |
ng-bindAutomatically escapes
<div ng-bind="snippet">
</div>
107 |
108 |
109 | 110 | it('should sanitize the html snippet by default', function() { 111 | expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). 112 | toBe('

an html\nclick here\nsnippet

'); 113 | }); 114 | 115 | it('should inline raw snippet if bound to a trusted value', function() { 116 | expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')). 117 | toBe("

an html\n" + 118 | "click here\n" + 119 | "snippet

"); 120 | }); 121 | 122 | it('should escape snippet without any filter', function() { 123 | expect(element(by.css('#bind-default div')).getAttribute('innerHTML')). 124 | toBe("<p style=\"color:blue\">an html\n" + 125 | "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 126 | "snippet</p>"); 127 | }); 128 | 129 | it('should update', function() { 130 | element(by.model('snippet')).clear(); 131 | element(by.model('snippet')).sendKeys('new text'); 132 | expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). 133 | toBe('new text'); 134 | expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe( 135 | 'new text'); 136 | expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe( 137 | "new <b onclick=\"alert(1)\">text</b>"); 138 | }); 139 |
140 |
141 | */ 142 | 143 | 144 | /** 145 | * @ngdoc provider 146 | * @name $sanitizeProvider 147 | * @this 148 | * 149 | * @description 150 | * Creates and configures {@link $sanitize} instance. 151 | */ 152 | function $SanitizeProvider() { 153 | var hasBeenInstantiated = false; 154 | var svgEnabled = false; 155 | 156 | this.$get = ['$$sanitizeUri', function($$sanitizeUri) { 157 | hasBeenInstantiated = true; 158 | if (svgEnabled) { 159 | extend(validElements, svgElements); 160 | } 161 | return function(html) { 162 | var buf = []; 163 | htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { 164 | return !/^unsafe:/.test($$sanitizeUri(uri, isImage)); 165 | })); 166 | return buf.join(''); 167 | }; 168 | }]; 169 | 170 | 171 | /** 172 | * @ngdoc method 173 | * @name $sanitizeProvider#enableSvg 174 | * @kind function 175 | * 176 | * @description 177 | * Enables a subset of svg to be supported by the sanitizer. 178 | * 179 | *
180 | *

By enabling this setting without taking other precautions, you might expose your 181 | * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned 182 | * outside of the containing element and be rendered over other elements on the page (e.g. a login 183 | * link). Such behavior can then result in phishing incidents.

184 | * 185 | *

To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg 186 | * tags within the sanitized content:

187 | * 188 | *
189 | * 190 | *

191 |    *   .rootOfTheIncludedContent svg {
192 |    *     overflow: hidden !important;
193 |    *   }
194 |    *   
195 | *
196 | * 197 | * @param {boolean=} flag Enable or disable SVG support in the sanitizer. 198 | * @returns {boolean|$sanitizeProvider} Returns the currently configured value if called 199 | * without an argument or self for chaining otherwise. 200 | */ 201 | this.enableSvg = function(enableSvg) { 202 | if (isDefined(enableSvg)) { 203 | svgEnabled = enableSvg; 204 | return this; 205 | } else { 206 | return svgEnabled; 207 | } 208 | }; 209 | 210 | 211 | /** 212 | * @ngdoc method 213 | * @name $sanitizeProvider#addValidElements 214 | * @kind function 215 | * 216 | * @description 217 | * Extends the built-in lists of valid HTML/SVG elements, i.e. elements that are considered safe 218 | * and are not stripped off during sanitization. You can extend the following lists of elements: 219 | * 220 | * - `htmlElements`: A list of elements (tag names) to extend the current list of safe HTML 221 | * elements. HTML elements considered safe will not be removed during sanitization. All other 222 | * elements will be stripped off. 223 | * 224 | * - `htmlVoidElements`: This is similar to `htmlElements`, but marks the elements as 225 | * "void elements" (similar to HTML 226 | * [void elements](https://rawgit.com/w3c/html/html5.1-2/single-page.html#void-elements)). These 227 | * elements have no end tag and cannot have content. 228 | * 229 | * - `svgElements`: This is similar to `htmlElements`, but for SVG elements. This list is only 230 | * taken into account if SVG is {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for 231 | * `$sanitize`. 232 | * 233 | *
234 | * This method must be called during the {@link angular.Module#config config} phase. Once the 235 | * `$sanitize` service has been instantiated, this method has no effect. 236 | *
237 | * 238 | *
239 | * Keep in mind that extending the built-in lists of elements may expose your app to XSS or 240 | * other vulnerabilities. Be very mindful of the elements you add. 241 | *
242 | * 243 | * @param {Array|Object} elements - A list of valid HTML elements or an object with one or 244 | * more of the following properties: 245 | * - **htmlElements** - `{Array}` - A list of elements to extend the current list of 246 | * HTML elements. 247 | * - **htmlVoidElements** - `{Array}` - A list of elements to extend the current list of 248 | * void HTML elements; i.e. elements that do not have an end tag. 249 | * - **svgElements** - `{Array}` - A list of elements to extend the current list of SVG 250 | * elements. The list of SVG elements is only taken into account if SVG is 251 | * {@link ngSanitize.$sanitizeProvider#enableSvg enabled} for `$sanitize`. 252 | * 253 | * Passing an array (`[...]`) is equivalent to passing `{htmlElements: [...]}`. 254 | * 255 | * @return {$sanitizeProvider} Returns self for chaining. 256 | */ 257 | this.addValidElements = function(elements) { 258 | if (!hasBeenInstantiated) { 259 | if (isArray(elements)) { 260 | elements = {htmlElements: elements}; 261 | } 262 | 263 | addElementsTo(svgElements, elements.svgElements); 264 | addElementsTo(voidElements, elements.htmlVoidElements); 265 | addElementsTo(validElements, elements.htmlVoidElements); 266 | addElementsTo(validElements, elements.htmlElements); 267 | } 268 | 269 | return this; 270 | }; 271 | 272 | 273 | /** 274 | * @ngdoc method 275 | * @name $sanitizeProvider#addValidAttrs 276 | * @kind function 277 | * 278 | * @description 279 | * Extends the built-in list of valid attributes, i.e. attributes that are considered safe and are 280 | * not stripped off during sanitization. 281 | * 282 | * **Note**: 283 | * The new attributes will not be treated as URI attributes, which means their values will not be 284 | * sanitized as URIs using `$compileProvider`'s 285 | * {@link ng.$compileProvider#aHrefSanitizationTrustedUrlList aHrefSanitizationTrustedUrlList} and 286 | * {@link ng.$compileProvider#imgSrcSanitizationTrustedUrlList imgSrcSanitizationTrustedUrlList}. 287 | * 288 | *
289 | * This method must be called during the {@link angular.Module#config config} phase. Once the 290 | * `$sanitize` service has been instantiated, this method has no effect. 291 | *
292 | * 293 | *
294 | * Keep in mind that extending the built-in list of attributes may expose your app to XSS or 295 | * other vulnerabilities. Be very mindful of the attributes you add. 296 | *
297 | * 298 | * @param {Array} attrs - A list of valid attributes. 299 | * 300 | * @returns {$sanitizeProvider} Returns self for chaining. 301 | */ 302 | this.addValidAttrs = function(attrs) { 303 | if (!hasBeenInstantiated) { 304 | extend(validAttrs, arrayToMap(attrs, true)); 305 | } 306 | return this; 307 | }; 308 | 309 | ////////////////////////////////////////////////////////////////////////////////////////////////// 310 | // Private stuff 311 | ////////////////////////////////////////////////////////////////////////////////////////////////// 312 | 313 | bind = angular.bind; 314 | extend = angular.extend; 315 | forEach = angular.forEach; 316 | isArray = angular.isArray; 317 | isDefined = angular.isDefined; 318 | lowercase = angular.$$lowercase; 319 | noop = angular.noop; 320 | 321 | htmlParser = htmlParserImpl; 322 | htmlSanitizeWriter = htmlSanitizeWriterImpl; 323 | 324 | nodeContains = window.Node.prototype.contains || /** @this */ function(arg) { 325 | // eslint-disable-next-line no-bitwise 326 | return !!(this.compareDocumentPosition(arg) & 16); 327 | }; 328 | 329 | // Regular Expressions for parsing tags and attributes 330 | var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 331 | // Match everything outside of normal chars and " (quote character) 332 | NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g; 333 | 334 | 335 | // Good source of info about elements and attributes 336 | // http://dev.w3.org/html5/spec/Overview.html#semantics 337 | // http://simon.html5.org/html-elements 338 | 339 | // Safe Void Elements - HTML5 340 | // http://dev.w3.org/html5/spec/Overview.html#void-elements 341 | var voidElements = stringToMap('area,br,col,hr,img,wbr'); 342 | 343 | // Elements that you can, intentionally, leave open (and which close themselves) 344 | // http://dev.w3.org/html5/spec/Overview.html#optional-tags 345 | var optionalEndTagBlockElements = stringToMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'), 346 | optionalEndTagInlineElements = stringToMap('rp,rt'), 347 | optionalEndTagElements = extend({}, 348 | optionalEndTagInlineElements, 349 | optionalEndTagBlockElements); 350 | 351 | // Safe Block Elements - HTML5 352 | var blockElements = extend({}, optionalEndTagBlockElements, stringToMap('address,article,' + 353 | 'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' + 354 | 'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul')); 355 | 356 | // Inline Elements - HTML5 357 | var inlineElements = extend({}, optionalEndTagInlineElements, stringToMap('a,abbr,acronym,b,' + 358 | 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' + 359 | 'samp,small,span,strike,strong,sub,sup,time,tt,u,var')); 360 | 361 | // SVG Elements 362 | // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements 363 | // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. 364 | // They can potentially allow for arbitrary javascript to be executed. See #11290 365 | var svgElements = stringToMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' + 366 | 'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' + 367 | 'radialGradient,rect,stop,svg,switch,text,title,tspan'); 368 | 369 | // Blocked Elements (will be stripped) 370 | var blockedElements = stringToMap('script,style'); 371 | 372 | var validElements = extend({}, 373 | voidElements, 374 | blockElements, 375 | inlineElements, 376 | optionalEndTagElements); 377 | 378 | //Attributes that have href and hence need to be sanitized 379 | var uriAttrs = stringToMap('background,cite,href,longdesc,src,xlink:href,xml:base'); 380 | 381 | var htmlAttrs = stringToMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + 382 | 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + 383 | 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + 384 | 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + 385 | 'valign,value,vspace,width'); 386 | 387 | // SVG attributes (without "id" and "name" attributes) 388 | // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes 389 | var svgAttrs = stringToMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + 390 | 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + 391 | 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + 392 | 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + 393 | 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + 394 | 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + 395 | 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + 396 | 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + 397 | 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + 398 | 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + 399 | 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + 400 | 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + 401 | 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + 402 | 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + 403 | 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); 404 | 405 | var validAttrs = extend({}, 406 | uriAttrs, 407 | svgAttrs, 408 | htmlAttrs); 409 | 410 | function stringToMap(str, lowercaseKeys) { 411 | return arrayToMap(str.split(','), lowercaseKeys); 412 | } 413 | 414 | function arrayToMap(items, lowercaseKeys) { 415 | var obj = {}, i; 416 | for (i = 0; i < items.length; i++) { 417 | obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true; 418 | } 419 | return obj; 420 | } 421 | 422 | function addElementsTo(elementsMap, newElements) { 423 | if (newElements && newElements.length) { 424 | extend(elementsMap, arrayToMap(newElements)); 425 | } 426 | } 427 | 428 | /** 429 | * Create an inert document that contains the dirty HTML that needs sanitizing. 430 | * We use the DOMParser API by default and fall back to createHTMLDocument if DOMParser is not 431 | * available. 432 | */ 433 | var getInertBodyElement /* function(html: string): HTMLBodyElement */ = (function(window, document) { 434 | if (isDOMParserAvailable()) { 435 | return getInertBodyElement_DOMParser; 436 | } 437 | 438 | if (!document || !document.implementation) { 439 | throw $sanitizeMinErr('noinert', 'Can\'t create an inert html document'); 440 | } 441 | var inertDocument = document.implementation.createHTMLDocument('inert'); 442 | var inertBodyElement = (inertDocument.documentElement || inertDocument.getDocumentElement()).querySelector('body'); 443 | return getInertBodyElement_InertDocument; 444 | 445 | function isDOMParserAvailable() { 446 | try { 447 | return !!getInertBodyElement_DOMParser(''); 448 | } catch (e) { 449 | return false; 450 | } 451 | } 452 | 453 | function getInertBodyElement_DOMParser(html) { 454 | // We add this dummy element to ensure that the rest of the content is parsed as expected 455 | // e.g. leading whitespace is maintained and tags like `` do not get hoisted to the `` tag. 456 | html = '' + html; 457 | try { 458 | var body = new window.DOMParser().parseFromString(html, 'text/html').body; 459 | body.firstChild.remove(); 460 | return body; 461 | } catch (e) { 462 | return undefined; 463 | } 464 | } 465 | 466 | function getInertBodyElement_InertDocument(html) { 467 | inertBodyElement.innerHTML = html; 468 | 469 | // Support: IE 9-11 only 470 | // strip custom-namespaced attributes on IE<=11 471 | if (document.documentMode) { 472 | stripCustomNsAttrs(inertBodyElement); 473 | } 474 | 475 | return inertBodyElement; 476 | } 477 | })(window, window.document); 478 | 479 | /** 480 | * @example 481 | * htmlParser(htmlString, { 482 | * start: function(tag, attrs) {}, 483 | * end: function(tag) {}, 484 | * chars: function(text) {}, 485 | * comment: function(text) {} 486 | * }); 487 | * 488 | * @param {string} html string 489 | * @param {object} handler 490 | */ 491 | function htmlParserImpl(html, handler) { 492 | if (html === null || html === undefined) { 493 | html = ''; 494 | } else if (typeof html !== 'string') { 495 | html = '' + html; 496 | } 497 | 498 | var inertBodyElement = getInertBodyElement(html); 499 | if (!inertBodyElement) return ''; 500 | 501 | //mXSS protection 502 | var mXSSAttempts = 5; 503 | do { 504 | if (mXSSAttempts === 0) { 505 | throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable'); 506 | } 507 | mXSSAttempts--; 508 | 509 | // trigger mXSS if it is going to happen by reading and writing the innerHTML 510 | html = inertBodyElement.innerHTML; 511 | inertBodyElement = getInertBodyElement(html); 512 | } while (html !== inertBodyElement.innerHTML); 513 | 514 | var node = inertBodyElement.firstChild; 515 | while (node) { 516 | switch (node.nodeType) { 517 | case 1: // ELEMENT_NODE 518 | handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes)); 519 | break; 520 | case 3: // TEXT NODE 521 | handler.chars(node.textContent); 522 | break; 523 | } 524 | 525 | var nextNode; 526 | if (!(nextNode = node.firstChild)) { 527 | if (node.nodeType === 1) { 528 | handler.end(node.nodeName.toLowerCase()); 529 | } 530 | nextNode = getNonDescendant('nextSibling', node); 531 | if (!nextNode) { 532 | while (nextNode == null) { 533 | node = getNonDescendant('parentNode', node); 534 | if (node === inertBodyElement) break; 535 | nextNode = getNonDescendant('nextSibling', node); 536 | if (node.nodeType === 1) { 537 | handler.end(node.nodeName.toLowerCase()); 538 | } 539 | } 540 | } 541 | } 542 | node = nextNode; 543 | } 544 | 545 | while ((node = inertBodyElement.firstChild)) { 546 | inertBodyElement.removeChild(node); 547 | } 548 | } 549 | 550 | function attrToMap(attrs) { 551 | var map = {}; 552 | for (var i = 0, ii = attrs.length; i < ii; i++) { 553 | var attr = attrs[i]; 554 | map[attr.name] = attr.value; 555 | } 556 | return map; 557 | } 558 | 559 | 560 | /** 561 | * Escapes all potentially dangerous characters, so that the 562 | * resulting string can be safely inserted into attribute or 563 | * element text. 564 | * @param value 565 | * @returns {string} escaped text 566 | */ 567 | function encodeEntities(value) { 568 | return value. 569 | replace(/&/g, '&'). 570 | replace(SURROGATE_PAIR_REGEXP, function(value) { 571 | var hi = value.charCodeAt(0); 572 | var low = value.charCodeAt(1); 573 | return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; 574 | }). 575 | replace(NON_ALPHANUMERIC_REGEXP, function(value) { 576 | return '&#' + value.charCodeAt(0) + ';'; 577 | }). 578 | replace(//g, '>'); 580 | } 581 | 582 | /** 583 | * create an HTML/XML writer which writes to buffer 584 | * @param {Array} buf use buf.join('') to get out sanitized html string 585 | * @returns {object} in the form of { 586 | * start: function(tag, attrs) {}, 587 | * end: function(tag) {}, 588 | * chars: function(text) {}, 589 | * comment: function(text) {} 590 | * } 591 | */ 592 | function htmlSanitizeWriterImpl(buf, uriValidator) { 593 | var ignoreCurrentElement = false; 594 | var out = bind(buf, buf.push); 595 | return { 596 | start: function(tag, attrs) { 597 | tag = lowercase(tag); 598 | if (!ignoreCurrentElement && blockedElements[tag]) { 599 | ignoreCurrentElement = tag; 600 | } 601 | if (!ignoreCurrentElement && validElements[tag] === true) { 602 | out('<'); 603 | out(tag); 604 | forEach(attrs, function(value, key) { 605 | var lkey = lowercase(key); 606 | var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); 607 | if (validAttrs[lkey] === true && 608 | (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { 609 | out(' '); 610 | out(key); 611 | out('="'); 612 | out(encodeEntities(value)); 613 | out('"'); 614 | } 615 | }); 616 | out('>'); 617 | } 618 | }, 619 | end: function(tag) { 620 | tag = lowercase(tag); 621 | if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) { 622 | out(''); 625 | } 626 | // eslint-disable-next-line eqeqeq 627 | if (tag == ignoreCurrentElement) { 628 | ignoreCurrentElement = false; 629 | } 630 | }, 631 | chars: function(chars) { 632 | if (!ignoreCurrentElement) { 633 | out(encodeEntities(chars)); 634 | } 635 | } 636 | }; 637 | } 638 | 639 | 640 | /** 641 | * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare 642 | * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want 643 | * to allow any of these custom attributes. This method strips them all. 644 | * 645 | * @param node Root element to process 646 | */ 647 | function stripCustomNsAttrs(node) { 648 | while (node) { 649 | if (node.nodeType === window.Node.ELEMENT_NODE) { 650 | var attrs = node.attributes; 651 | for (var i = 0, l = attrs.length; i < l; i++) { 652 | var attrNode = attrs[i]; 653 | var attrName = attrNode.name.toLowerCase(); 654 | if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) { 655 | node.removeAttributeNode(attrNode); 656 | i--; 657 | l--; 658 | } 659 | } 660 | } 661 | 662 | var nextNode = node.firstChild; 663 | if (nextNode) { 664 | stripCustomNsAttrs(nextNode); 665 | } 666 | 667 | node = getNonDescendant('nextSibling', node); 668 | } 669 | } 670 | 671 | function getNonDescendant(propName, node) { 672 | // An element is clobbered if its `propName` property points to one of its descendants 673 | var nextNode = node[propName]; 674 | if (nextNode && nodeContains.call(node, nextNode)) { 675 | throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText); 676 | } 677 | return nextNode; 678 | } 679 | } 680 | 681 | function sanitizeText(chars) { 682 | var buf = []; 683 | var writer = htmlSanitizeWriter(buf, noop); 684 | writer.chars(chars); 685 | return buf.join(''); 686 | } 687 | 688 | 689 | // define ngSanitize module and register $sanitize service 690 | angular.module('ngSanitize', []) 691 | .provider('$sanitize', $SanitizeProvider) 692 | .info({ angularVersion: '1.8.3' }); 693 | 694 | /** 695 | * @ngdoc filter 696 | * @name linky 697 | * @kind function 698 | * 699 | * @description 700 | * Finds links in text input and turns them into html links. Supports `http/https/ftp/sftp/mailto` and 701 | * plain email address links. 702 | * 703 | * Requires the {@link ngSanitize `ngSanitize`} module to be installed. 704 | * 705 | * @param {string} text Input text. 706 | * @param {string} [target] Window (`_blank|_self|_parent|_top`) or named frame to open links in. 707 | * @param {object|function(url)} [attributes] Add custom attributes to the link element. 708 | * 709 | * Can be one of: 710 | * 711 | * - `object`: A map of attributes 712 | * - `function`: Takes the url as a parameter and returns a map of attributes 713 | * 714 | * If the map of attributes contains a value for `target`, it overrides the value of 715 | * the target parameter. 716 | * 717 | * 718 | * @returns {string} Html-linkified and {@link $sanitize sanitized} text. 719 | * 720 | * @usage 721 | 722 | * 723 | * @example 724 | 725 | 726 |
727 | Snippet: 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 739 | 742 | 743 | 744 | 745 | 748 | 751 | 752 | 753 | 754 | 757 | 760 | 761 | 762 | 763 | 764 | 765 | 766 |
FilterSourceRendered
linky filter 737 |
<div ng-bind-html="snippet | linky">
</div>
738 |
740 |
741 |
linky target 746 |
<div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
</div>
747 |
749 |
750 |
linky custom attributes 755 |
<div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
</div>
756 |
758 |
759 |
no filter
<div ng-bind="snippet">
</div>
767 | 768 | 769 | angular.module('linkyExample', ['ngSanitize']) 770 | .controller('ExampleController', ['$scope', function($scope) { 771 | $scope.snippet = 772 | 'Pretty text with some links:\n' + 773 | 'http://angularjs.org/,\n' + 774 | 'mailto:us@somewhere.org,\n' + 775 | 'another@somewhere.org,\n' + 776 | 'and one more: ftp://127.0.0.1/.'; 777 | $scope.snippetWithSingleURL = 'http://angularjs.org/'; 778 | }]); 779 | 780 | 781 | it('should linkify the snippet with urls', function() { 782 | expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). 783 | toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + 784 | 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); 785 | expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); 786 | }); 787 | 788 | it('should not linkify snippet without the linky filter', function() { 789 | expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). 790 | toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + 791 | 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); 792 | expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); 793 | }); 794 | 795 | it('should update', function() { 796 | element(by.model('snippet')).clear(); 797 | element(by.model('snippet')).sendKeys('new http://link.'); 798 | expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). 799 | toBe('new http://link.'); 800 | expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); 801 | expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) 802 | .toBe('new http://link.'); 803 | }); 804 | 805 | it('should work with the target property', function() { 806 | expect(element(by.id('linky-target')). 807 | element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). 808 | toBe('http://angularjs.org/'); 809 | expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); 810 | }); 811 | 812 | it('should optionally add custom attributes', function() { 813 | expect(element(by.id('linky-custom-attributes')). 814 | element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). 815 | toBe('http://angularjs.org/'); 816 | expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); 817 | }); 818 | 819 | 820 | */ 821 | angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { 822 | var LINKY_URL_REGEXP = 823 | /((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, 824 | MAILTO_REGEXP = /^mailto:/i; 825 | 826 | var linkyMinErr = angular.$$minErr('linky'); 827 | var isDefined = angular.isDefined; 828 | var isFunction = angular.isFunction; 829 | var isObject = angular.isObject; 830 | var isString = angular.isString; 831 | 832 | return function(text, target, attributes) { 833 | if (text == null || text === '') return text; 834 | if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text); 835 | 836 | var attributesFn = 837 | isFunction(attributes) ? attributes : 838 | isObject(attributes) ? function getAttributesObject() {return attributes;} : 839 | function getEmptyAttributesObject() {return {};}; 840 | 841 | var match; 842 | var raw = text; 843 | var html = []; 844 | var url; 845 | var i; 846 | while ((match = raw.match(LINKY_URL_REGEXP))) { 847 | // We can not end in these as they are sometimes found at the end of the sentence 848 | url = match[0]; 849 | // if we did not match ftp/http/www/mailto then assume mailto 850 | if (!match[2] && !match[4]) { 851 | url = (match[3] ? 'http://' : 'mailto:') + url; 852 | } 853 | i = match.index; 854 | addText(raw.substr(0, i)); 855 | addLink(url, match[0].replace(MAILTO_REGEXP, '')); 856 | raw = raw.substring(i + match[0].length); 857 | } 858 | addText(raw); 859 | return $sanitize(html.join('')); 860 | 861 | function addText(text) { 862 | if (!text) { 863 | return; 864 | } 865 | html.push(sanitizeText(text)); 866 | } 867 | 868 | function addLink(url, text) { 869 | var key, linkAttributes = attributesFn(url); 870 | html.push(''); 884 | addText(text); 885 | html.push(''); 886 | } 887 | }; 888 | }]); 889 | 890 | 891 | })(window, window.angular); 892 | -------------------------------------------------------------------------------- /angular-sanitize.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.8.3 3 | (c) 2010-2020 Google LLC. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(s,e){'use strict';function O(e){var g=[];B(g,D).chars(e);return g.join("")}var C=e.$$minErr("$sanitize"),E,g,F,G,H,q,D,I,J,B;e.module("ngSanitize",[]).provider("$sanitize",function(){function h(a,d){return A(a.split(","),d)}function A(a,d){var c={},b;for(b=0;b/g,">")}function z(a){for(;a;){if(a.nodeType===s.Node.ELEMENT_NODE)for(var d=a.attributes,c=0,b=d.length;c"))},end:function(a){a=q(a);c||!0!==m[a]||!0===r[a]||(b(""));a==c&&(c=!1)},chars:function(a){c|| 11 | b(K(a))}}};I=s.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)};var Q=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,u=/([^#-~ |!])/g,r=h("area,br,col,hr,img,wbr"),x=h("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),p=h("rp,rt"),n=g({},p,x),x=g({},x,h("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),p=g({},p,h("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")), 12 | l=h("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),w=h("script,style"),m=g({},r,x,p,n),N=h("background,cite,href,longdesc,src,xlink:href,xml:base"),n=h("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"), 13 | p=h("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan", 14 | !0),L=g({},N,p,n),M=function(a,d){function c(b){b=""+b;try{var c=(new a.DOMParser).parseFromString(b,"text/html").body;c.firstChild.remove();return c}catch(d){}}var b;try{b=!!c("")}catch(f){b=!1}if(b)return c;if(!d||!d.implementation)throw C("noinert");b=d.implementation.createHTMLDocument("inert");var e=(b.documentElement||b.getDocumentElement()).querySelector("body");return function(a){e.innerHTML=a;d.documentMode&&z(e);return e}}(s,s.document)}).info({angularVersion:"1.8.3"}); 15 | e.module("ngSanitize").filter("linky",["$sanitize",function(h){var g=/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,t=/^mailto:/i,q=e.$$minErr("linky"),s=e.isDefined,z=e.isFunction,v=e.isObject,y=e.isString;return function(f,e,u){function r(e){e&&l.push(O(e))}function x(f,h){var g,a=p(f);l.push("');r(h);l.push("")}if(null== 16 | f||""===f)return f;if(!y(f))throw q("notstring",f);for(var p=z(u)?u:v(u)?function(){return u}:function(){return{}},n=f,l=[],w,m;f=n.match(g);)w=f[0],f[2]||f[4]||(w=(f[3]?"http://":"mailto:")+w),m=f.index,r(n.substr(0,m)),x(w,f[0].replace(t,"")),n=n.substring(m+f[0].length);r(n);return h(l.join(""))}}])})(window,window.angular); 17 | //# sourceMappingURL=angular-sanitize.min.js.map 18 | -------------------------------------------------------------------------------- /angular-sanitize.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version":3, 3 | "file":"angular-sanitize.min.js", 4 | "lineCount":16, 5 | "mappings":"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", 6 | "sources":["angular-sanitize.js"], 7 | "names":["window","angular","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","$sanitizeMinErr","$$minErr","bind","extend","forEach","isArray","isDefined","lowercase","nodeContains","htmlParser","module","provider","$SanitizeProvider","stringToMap","str","lowercaseKeys","arrayToMap","split","items","obj","i","length","addElementsTo","elementsMap","newElements","attrToMap","attrs","map","ii","attr","name","value","encodeEntities","replace","SURROGATE_PAIR_REGEXP","hi","charCodeAt","low","NON_ALPHANUMERIC_REGEXP","stripCustomNsAttrs","node","nodeType","Node","ELEMENT_NODE","attributes","l","attrNode","attrName","toLowerCase","lastIndexOf","removeAttributeNode","nextNode","firstChild","getNonDescendant","propName","call","outerHTML","outerText","hasBeenInstantiated","svgEnabled","$get","$$sanitizeUri","validElements","svgElements","html","uri","isImage","test","enableSvg","this.enableSvg","addValidElements","this.addValidElements","elements","htmlElements","voidElements","htmlVoidElements","addValidAttrs","this.addValidAttrs","validAttrs","$$lowercase","htmlParserImpl","handler","undefined","inertBodyElement","getInertBodyElement","mXSSAttempts","innerHTML","start","nodeName","textContent","end","removeChild","htmlSanitizeWriterImpl","uriValidator","ignoreCurrentElement","out","push","tag","blockedElements","key","lkey","uriAttrs","prototype","contains","arg","compareDocumentPosition","optionalEndTagBlockElements","optionalEndTagInlineElements","optionalEndTagElements","blockElements","inlineElements","htmlAttrs","svgAttrs","document","getInertBodyElement_DOMParser","body","parseFromString","DOMParser","remove","e","implementation","inertDocument","createHTMLDocument","querySelector","documentElement","getDocumentElement","getInertBodyElement_InertDocument","documentMode","info","angularVersion","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","linkyMinErr","isFunction","isObject","isString","text","target","addText","addLink","url","linkAttributes","attributesFn","getAttributesObject","getEmptyAttributesObject","raw","match","index","substr","substring"] 8 | } 9 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-sanitize", 3 | "version": "1.8.3", 4 | "license": "MIT", 5 | "main": "./angular-sanitize.js", 6 | "ignore": [], 7 | "dependencies": { 8 | "angular": "1.8.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('./angular-sanitize'); 2 | module.exports = 'ngSanitize'; 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-sanitize", 3 | "version": "1.8.3", 4 | "description": "AngularJS module for sanitizing HTML", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "html", 18 | "client-side" 19 | ], 20 | "author": "Angular Core Team ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/angular/angular.js/issues" 24 | }, 25 | "homepage": "http://angularjs.org", 26 | "jspm": { 27 | "shim": { 28 | "angular-sanitize": { 29 | "deps": ["angular"] 30 | } 31 | } 32 | } 33 | } 34 | --------------------------------------------------------------------------------