feat(platform-server): add API to render Module and ModuleFactory to string (#14381)

- PlatformState provides an interface to serialize the current Platform State as a string or Document.

- renderModule and renderModuleFactory are convenience methods to wait for Angular Application to stabilize and then render the state to a string.

- refactor code to remove defaultDoc from DomAdapter and inject DOCUMENT where it's needed.
This commit is contained in:
vikerman
2017-02-14 16:14:40 -08:00
committed by Igor Minar
parent 2f2b65bd38
commit b4d444a0a7
39 changed files with 462 additions and 187 deletions

View File

@ -14,7 +14,7 @@ import {DomSanitizerImpl} from '../../src/security/dom_sanitization_service';
export function main() {
t.describe('DOM Sanitization Service', () => {
t.it('accepts resource URL values for resource contexts', () => {
const svc = new DomSanitizerImpl();
const svc = new DomSanitizerImpl(null);
const resourceUrl = svc.bypassSecurityTrustResourceUrl('http://hello/world');
t.expect(svc.sanitize(SecurityContext.URL, resourceUrl)).toBe('http://hello/world');
});

View File

@ -14,10 +14,12 @@ import {sanitizeHtml} from '../../src/security/html_sanitizer';
export function main() {
t.describe('HTML sanitizer', () => {
let defaultDoc: any;
let originalLog: (msg: any) => any = null;
let logMsgs: string[];
t.beforeEach(() => {
defaultDoc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
logMsgs = [];
originalLog = getDOM().log; // Monkey patch DOM.log.
getDOM().log = (msg) => logMsgs.push(msg);
@ -25,52 +27,55 @@ export function main() {
t.afterEach(() => { getDOM().log = originalLog; });
t.it('serializes nested structures', () => {
t.expect(sanitizeHtml('<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>'))
t.expect(sanitizeHtml(defaultDoc, '<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>'))
.toEqual('<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>');
t.expect(logMsgs).toEqual([]);
});
t.it('serializes self closing elements', () => {
t.expect(sanitizeHtml('<p>Hello <br> World</p>')).toEqual('<p>Hello <br> World</p>');
t.expect(sanitizeHtml(defaultDoc, '<p>Hello <br> World</p>'))
.toEqual('<p>Hello <br> World</p>');
});
t.it('supports namespaced elements', () => {
t.expect(sanitizeHtml('a<my:hr/><my:div>b</my:div>c')).toEqual('abc');
t.expect(sanitizeHtml(defaultDoc, 'a<my:hr/><my:div>b</my:div>c')).toEqual('abc');
});
t.it('supports namespaced attributes', () => {
t.expect(sanitizeHtml('<a xlink:href="something">t</a>'))
t.expect(sanitizeHtml(defaultDoc, '<a xlink:href="something">t</a>'))
.toEqual('<a xlink:href="something">t</a>');
t.expect(sanitizeHtml('<a xlink:evil="something">t</a>')).toEqual('<a>t</a>');
t.expect(sanitizeHtml('<a xlink:href="javascript:foo()">t</a>'))
t.expect(sanitizeHtml(defaultDoc, '<a xlink:evil="something">t</a>')).toEqual('<a>t</a>');
t.expect(sanitizeHtml(defaultDoc, '<a xlink:href="javascript:foo()">t</a>'))
.toEqual('<a xlink:href="unsafe:javascript:foo()">t</a>');
});
t.it('supports HTML5 elements', () => {
t.expect(sanitizeHtml('<main><summary>Works</summary></main>'))
t.expect(sanitizeHtml(defaultDoc, '<main><summary>Works</summary></main>'))
.toEqual('<main><summary>Works</summary></main>');
});
t.it('sanitizes srcset attributes', () => {
t.expect(sanitizeHtml('<img srcset="/foo.png 400px, javascript:evil() 23px">'))
t.expect(sanitizeHtml(defaultDoc, '<img srcset="/foo.png 400px, javascript:evil() 23px">'))
.toEqual('<img srcset="/foo.png 400px, unsafe:javascript:evil() 23px">');
});
t.it('supports sanitizing plain text', () => {
t.expect(sanitizeHtml('Hello, World')).toEqual('Hello, World');
t.expect(sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World');
});
t.it('ignores non-element, non-attribute nodes', () => {
t.expect(sanitizeHtml('<!-- comments? -->no.')).toEqual('no.');
t.expect(sanitizeHtml('<?pi nodes?>no.')).toEqual('no.');
t.expect(sanitizeHtml(defaultDoc, '<!-- comments? -->no.')).toEqual('no.');
t.expect(sanitizeHtml(defaultDoc, '<?pi nodes?>no.')).toEqual('no.');
t.expect(logMsgs.join('\n')).toMatch(/sanitizing HTML stripped some content/);
});
t.it('supports sanitizing escaped entities', () => {
t.expect(sanitizeHtml('&#128640;')).toEqual('&#128640;');
t.expect(sanitizeHtml(defaultDoc, '&#128640;')).toEqual('&#128640;');
t.expect(logMsgs).toEqual([]);
});
t.it('does not warn when just re-encoding text', () => {
t.expect(sanitizeHtml('<p>Hellö Wörld</p>')).toEqual('<p>Hell&#246; W&#246;rld</p>');
t.expect(sanitizeHtml(defaultDoc, '<p>Hellö Wörld</p>'))
.toEqual('<p>Hell&#246; W&#246;rld</p>');
t.expect(logMsgs).toEqual([]);
});
t.it('escapes entities', () => {
t.expect(sanitizeHtml('<p>Hello &lt; World</p>')).toEqual('<p>Hello &lt; World</p>');
t.expect(sanitizeHtml('<p>Hello < World</p>')).toEqual('<p>Hello &lt; World</p>');
t.expect(sanitizeHtml('<p alt="% &amp; &quot; !">Hello</p>'))
t.expect(sanitizeHtml(defaultDoc, '<p>Hello &lt; World</p>'))
.toEqual('<p>Hello &lt; World</p>');
t.expect(sanitizeHtml(defaultDoc, '<p>Hello < World</p>')).toEqual('<p>Hello &lt; World</p>');
t.expect(sanitizeHtml(defaultDoc, '<p alt="% &amp; &quot; !">Hello</p>'))
.toEqual('<p alt="% &amp; &#34; !">Hello</p>'); // NB: quote encoded as ASCII &#34;.
});
t.describe('should strip dangerous elements', () => {
@ -80,11 +85,12 @@ export function main() {
];
for (const tag of dangerousTags) {
t.it(
`${tag}`, () => { t.expect(sanitizeHtml(`<${tag}>evil!</${tag}>`)).toEqual('evil!'); });
t.it(`${tag}`, () => {
t.expect(sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual('evil!');
});
}
t.it(`swallows frame entirely`, () => {
t.expect(sanitizeHtml(`<frame>evil!</frame>`)).not.toContain('<frame>');
t.expect(sanitizeHtml(defaultDoc, `<frame>evil!</frame>`)).not.toContain('<frame>');
});
});
t.describe('should strip dangerous attributes', () => {
@ -92,14 +98,14 @@ export function main() {
for (const attr of dangerousAttrs) {
t.it(`${attr}`, () => {
t.expect(sanitizeHtml(`<a ${attr}="x">evil!</a>`)).toEqual('<a>evil!</a>');
t.expect(sanitizeHtml(defaultDoc, `<a ${attr}="x">evil!</a>`)).toEqual('<a>evil!</a>');
});
}
});
if (browserDetection.isWebkit) {
t.it('should prevent mXSS attacks', function() {
t.expect(sanitizeHtml('<a href="&#x3000;javascript:alert(1)">CLICKME</a>'))
t.expect(sanitizeHtml(defaultDoc, '<a href="&#x3000;javascript:alert(1)">CLICKME</a>'))
.toEqual('<a href="unsafe:javascript:alert(1)">CLICKME</a>');
});
}