| 
									
										
										
										
											2016-06-23 09:47:54 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-02 12:12:46 -08:00
										 |  |  | import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import {getDOM} from '../../src/dom/dom_adapter'; | 
					
						
							|  |  |  | import {sanitizeHtml} from '../../src/security/html_sanitizer'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function main() { | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |   describe('HTML sanitizer', () => { | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |     let defaultDoc: any; | 
					
						
							| 
									
										
										
										
											2017-03-24 09:59:41 -07:00
										 |  |  |     let originalLog: (msg: any) => any = null !; | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |     let logMsgs: string[]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |     beforeEach(() => { | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |       defaultDoc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument(); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |       logMsgs = []; | 
					
						
							|  |  |  |       originalLog = getDOM().log;  // Monkey patch DOM.log.
 | 
					
						
							|  |  |  |       getDOM().log = (msg) => logMsgs.push(msg); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |     afterEach(() => { getDOM().log = originalLog; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('serializes nested structures', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>')) | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |           .toEqual('<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>'); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |       expect(logMsgs).toEqual([]); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('serializes self closing elements', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<p>Hello <br> World</p>')) | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |           .toEqual('<p>Hello <br> World</p>'); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('supports namespaced elements', | 
					
						
							|  |  |  |        () => { expect(sanitizeHtml(defaultDoc, 'a<my:hr/><my:div>b</my:div>c')).toEqual('abc'); }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('supports namespaced attributes', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<a xlink:href="something">t</a>')) | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |           .toEqual('<a xlink:href="something">t</a>'); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |       expect(sanitizeHtml(defaultDoc, '<a xlink:evil="something">t</a>')).toEqual('<a>t</a>'); | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<a xlink:href="javascript:foo()">t</a>')) | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |           .toEqual('<a xlink:href="unsafe:javascript:foo()">t</a>'); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('supports HTML5 elements', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<main><summary>Works</summary></main>')) | 
					
						
							| 
									
										
										
										
											2016-06-27 12:18:48 -07:00
										 |  |  |           .toEqual('<main><summary>Works</summary></main>'); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('sanitizes srcset attributes', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<img srcset="/foo.png 400px, javascript:evil() 23px">')) | 
					
						
							| 
									
										
										
										
											2016-06-27 12:18:48 -07:00
										 |  |  |           .toEqual('<img srcset="/foo.png 400px, unsafe:javascript:evil() 23px">'); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |     it('supports sanitizing plain text', | 
					
						
							|  |  |  |        () => { expect(sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World'); }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('ignores non-element, non-attribute nodes', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<!-- comments? -->no.')).toEqual('no.'); | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<?pi nodes?>no.')).toEqual('no.'); | 
					
						
							|  |  |  |       expect(logMsgs.join('\n')).toMatch(/sanitizing HTML stripped some content/); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('supports sanitizing escaped entities', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '🚀')).toEqual('🚀'); | 
					
						
							|  |  |  |       expect(logMsgs).toEqual([]); | 
					
						
							| 
									
										
										
										
											2016-06-23 22:06:19 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('does not warn when just re-encoding text', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<p>Hellö Wörld</p>')) | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |           .toEqual('<p>Hellö Wörld</p>'); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |       expect(logMsgs).toEqual([]); | 
					
						
							| 
									
										
										
										
											2016-07-26 11:39:09 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('escapes entities', () => { | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<p>Hello < World</p>')) | 
					
						
							| 
									
										
										
										
											2017-02-14 16:14:40 -08:00
										 |  |  |           .toEqual('<p>Hello < World</p>'); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |       expect(sanitizeHtml(defaultDoc, '<p>Hello < World</p>')).toEqual('<p>Hello < World</p>'); | 
					
						
							|  |  |  |       expect(sanitizeHtml(defaultDoc, '<p alt="% & " !">Hello</p>')) | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |           .toEqual('<p alt="% & " !">Hello</p>');  // NB: quote encoded as ASCII ".
 | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     describe('should strip dangerous elements', () => { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |       const dangerousTags = [ | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  |         'frameset', 'form', 'param', 'object', 'embed', 'textarea', 'input', 'button', 'option', | 
					
						
							|  |  |  |         'select', 'script', 'style', 'link', 'base', 'basefont' | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |       ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |       for (const tag of dangerousTags) { | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |         it(`${tag}`, | 
					
						
							|  |  |  |            () => { expect(sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual('evil!'); }); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       it(`swallows frame entirely`, () => { | 
					
						
							|  |  |  |         expect(sanitizeHtml(defaultDoc, `<frame>evil!</frame>`)).not.toContain('<frame>'); | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     describe('should strip dangerous attributes', () => { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |       const dangerousAttrs = ['id', 'name', 'style']; | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |       for (const attr of dangerousAttrs) { | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |         it(`${attr}`, () => { | 
					
						
							|  |  |  |           expect(sanitizeHtml(defaultDoc, `<a ${attr}="x">evil!</a>`)).toEqual('<a>evil!</a>'); | 
					
						
							| 
									
										
										
										
											2016-06-08 16:38:52 -07:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 17:15:46 -07:00
										 |  |  |     it('should not enter an infinite loop on clobbered elements', () => { | 
					
						
							|  |  |  |       // Some browsers are vulnerable to clobbered elements and will throw an expected exception
 | 
					
						
							|  |  |  |       // IE and EDGE does not seems to be affected by those cases
 | 
					
						
							|  |  |  |       // Anyway what we want to test is that browsers do not enter an infinite loop which would
 | 
					
						
							|  |  |  |       // result in a timeout error for the test.
 | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         sanitizeHtml(defaultDoc, '<form><input name="parentNode" /></form>'); | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         // depending on the browser, we might ge an exception
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         sanitizeHtml(defaultDoc, '<form><input name="nextSibling" /></form>') | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         // depending on the browser, we might ge an exception
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         sanitizeHtml(defaultDoc, '<form><div><div><input name="nextSibling" /></div></div></form>'); | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         // depending on the browser, we might ge an exception
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |     if (browserDetection.isWebkit) { | 
					
						
							| 
									
										
										
										
											2017-03-14 16:52:34 -07:00
										 |  |  |       it('should prevent mXSS attacks', function() { | 
					
						
							|  |  |  |         expect(sanitizeHtml(defaultDoc, '<a href=" javascript:alert(1)">CLICKME</a>')) | 
					
						
							| 
									
										
										
										
											2016-04-30 19:02:05 -07:00
										 |  |  |             .toEqual('<a href="unsafe:javascript:alert(1)">CLICKME</a>'); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } |