Преглед на файлове

More cleanup and finish tests

Steven Silvester преди 8 години
родител
ревизия
e0ae8c920c
променени са 4 файла, в които са добавени 122 реда и са изтрити 14 реда
  1. 33 4
      src/renderers/index.ts
  2. 1 1
      src/sanitizer/index.ts
  3. 15 8
      test/src/renderers/renderers.spec.ts
  4. 73 1
      test/src/rendermime/rendermime.spec.ts

+ 33 - 4
src/renderers/index.ts

@@ -10,7 +10,7 @@ import * as marked
   from 'marked';
 
 import {
-  ansi_to_html
+  ansi_to_html, escape_for_html
 } from 'ansi_up';
 
 import {
@@ -200,20 +200,21 @@ class TextRenderer implements RenderMime.IRenderer<Widget> {
    * Whether the input can safely sanitized for a given mimetype.
    */
   sanitizable(mimetype: string): boolean {
-    return true;
+    return false;
   }
 
   /**
    * Whether the input is safe without sanitization.
    */
   isSafe(mimetype: string): boolean {
-    return false;
+    return true;
   }
 
   /**
    * Transform the input bundle.
    */
   transform(mimetype: string, data: string): string {
+    data = escape_for_html(data);
     return `<pre>${ansi_to_html(data)}</pre>`;
   }
 
@@ -369,11 +370,39 @@ class PDFRenderer implements RenderMime.IRenderer<Widget> {
  * A renderer for LateX data.
  */
 export
-class LatexRenderer extends HTMLRenderer {
+class LatexRenderer implements RenderMime.IRenderer<Widget>  {
   /**
    * The mimetypes this renderer accepts.
    */
   mimetypes = ['text/latex'];
+
+  /**
+   * Whether the input can safely sanitized for a given mimetype.
+   */
+  sanitizable(mimetype: string): boolean {
+    return false;
+  }
+
+  /**
+   * Whether the input is safe without sanitization.
+   */
+  isSafe(mimetype: string): boolean {
+    return false;
+  }
+
+  /**
+   * Transform the input bundle.
+   */
+  transform(mimetype: string, data: string): string {
+    return data;
+  }
+
+  /**
+   * Render the transformed mime bundle.
+   */
+  render(mimetype: string, data: string): Widget {
+    return new HTMLWidget(data);
+  }
 }
 
 

+ 1 - 1
src/sanitizer/index.ts

@@ -24,7 +24,7 @@ class Sanitizer implements ISanitizer {
   }
 
   private _options: sanitize.IOptions = {
-    allowedTags: sanitize.defaults.allowedTags.concat('h1', 'h2', 'img', 'span'),
+    allowedTags: sanitize.defaults.allowedTags.concat('svg', 'h1', 'h2', 'img', 'span'),
     allowedAttributes: {
       // Allow the "rel" attribute for <a> tags.
       'a': sanitize.defaults.allowedAttributes['a'].concat('rel'),

+ 15 - 8
test/src/renderers/renderers.spec.ts

@@ -25,20 +25,20 @@ describe('renderers', () => {
 
     describe('#sanitizable()', () => {
 
-      it('should be `true`', () => {
+      it('should be `false`', () => {
         let t = new TextRenderer();
-        expect(t.sanitizable('text/plain')).to.be(true);
-        expect(t.sanitizable('application/vnd.jupyter.console-text')).to.be(true);
+        expect(t.sanitizable('text/plain')).to.be(false);
+        expect(t.sanitizable('application/vnd.jupyter.console-text')).to.be(false);
       });
 
     });
 
     describe('#isSafe()', () => {
 
-      it('should be `false`', () => {
+      it('should be `true`', () => {
         let t = new TextRenderer();
-        expect(t.isSafe('text/plain')).to.be(false);
-        expect(t.isSafe('application/vnd.jupyter.console-text')).to.be(false);
+        expect(t.isSafe('text/plain')).to.be(true);
+        expect(t.isSafe('application/vnd.jupyter.console-text')).to.be(true);
       });
 
     });
@@ -58,6 +58,13 @@ describe('renderers', () => {
         expect(text).to.be('<pre>There is no text but <span style="color:rgb(0, 255, 0);background-color:rgb(187, 0, 0)">text</span>.\nWoo.</pre>');
       });
 
+      it('should escape inline html', () => {
+        let t = new TextRenderer();
+        let text = 'There is no text <script>window.x=1</script> but \x1b[01;41;32mtext\x1b[00m.\nWoo.';
+        text = t.transform('application/vnd.jupyter.console-text', text);
+        expect(text).to.be('<pre>There is no text &lt;script&gt;window.x=1&lt;/script&gt; but <span style="color:rgb(0, 255, 0);background-color:rgb(187, 0, 0)">text</span>.\nWoo.</pre>');
+      });
+
     });
 
     describe('#render()', () => {
@@ -86,9 +93,9 @@ describe('renderers', () => {
 
     describe('#sanitizable()', () => {
 
-      it('should be `true`', () => {
+      it('should be `false`', () => {
         let t = new LatexRenderer();
-        expect(t.sanitizable('text/latex')).to.be(true);
+        expect(t.sanitizable('text/latex')).to.be(false);
       });
 
     });

+ 73 - 1
test/src/rendermime/rendermime.spec.ts

@@ -78,12 +78,66 @@ describe('jupyter-ui', () => {
           'text/html': '<h1>foo</h1>'
         };
         let r = defaultRenderMime();
-        r.render(bundle).then(w => {
+        r.render(bundle, true).then(w => {
           let el = w.node.firstChild as HTMLElement;
           expect(el.localName).to.be('h1');
         }).then(done, done);
       });
 
+      it('should render the mimetype that is safe', (done) => {
+        let bundle: RenderMime.MimeMap<string> = {
+          'text/plain': 'foo',
+          'text/javascript': 'window.x = 1',
+          'image/png': 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
+        };
+        let r = defaultRenderMime();
+        r.render(bundle, false).then(w => {
+          let el = w.node.firstChild as HTMLElement;
+          expect(el.localName).to.be('img');
+        }).then(done, done);
+      });
+
+      it('should render the mimetype that is sanitizable', (done) => {
+        let bundle: RenderMime.MimeMap<string> = {
+          'text/plain': 'foo',
+          'text/html': '<h1>foo</h1>'
+        };
+        let r = defaultRenderMime();
+        r.render(bundle, false).then(w => {
+          let el = w.node.firstChild as HTMLElement;
+          expect(el.localName).to.be('h1');
+        }).then(done, done);
+      });
+
+      it('should sanitize markdown', (done) => {
+        let md = require('../../../examples/filebrowser/sample.md');
+        let r = defaultRenderMime();
+        r.render({ 'text/markdown': md as string }).then(widget => {
+          expect(widget.node.innerHTML).to.be(`<h1>Title first level</h1>\n<h2>Title second Level</h2>\n<h3>Title third level</h3>\n<h4>h4</h4>\n<h5>h5</h5>\n<h6>h6</h6>\n<h1>h1</h1>\n<h2>h2</h2>\n<h3>h3</h3>\n<h4>h4</h4>\n<h5>h6</h5>\n<p>This is just a sample paragraph<br>You can look at different level of nested unorderd list ljbakjn arsvlasc asc asc awsc asc ascd ascd ascd asdc asc</p>\n<ul>\n<li>level 1<ul>\n<li>level 2</li>\n<li>level 2</li>\n<li>level 2<ul>\n<li>level 3</li>\n<li>level 3<ul>\n<li>level 4<ul>\n<li>level 5<ul>\n<li>level 6</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>level 2</li>\n</ul>\n</li>\n<li>level 1</li>\n<li>level 1</li>\n<li>level 1<br>Ordered list</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1<ol>\n<li>level 1</li>\n<li>level 1</li>\n<li>level 1</li>\n</ol>\n</li>\n</ol>\n</li>\n</ol>\n</li>\n</ol>\n</li>\n<li>level 1</li>\n<li>level 1<br>some Horizontal line</li>\n</ul>\n<hr>\n<h2>and another one</h2>\n<p>Colons can be used to align columns.</p>\n<table>\n<thead>\n<tr>\n<th>Tables</th>\n<th>Are</th>\n<th>Cool</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>col 3 is</td>\n<td>right-aligned</td>\n<td>1600</td>\n</tr>\n<tr>\n<td>col 2 is</td>\n<td>centered</td>\n<td>12</td>\n</tr>\n<tr>\n<td>zebra stripes</td>\n<td>are neat</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>\n<p>There must be at least 3 dashes separating each header cell.<br>The outer pipes (|) are optional, and you don\'t need to make the<br>raw Markdown line up prettily. You can also use inline Markdown.</p>\n`);
+        }).then(done, done);
+      });
+
+      it('should sanitize html', (done) => {
+        let bundle: RenderMime.MimeMap<string> = {
+          'text/html': '<h1>foo <script>window.x=1></scrip></h1>'
+        };
+        let r = defaultRenderMime();
+        r.render(bundle).then(widget => {
+          expect(widget.node.innerHTML).to.be('<h1>foo </h1>');
+        }).then(done, done);
+      });
+
+      it('should sanitize svg', (done) => {
+        let bundle: RenderMime.MimeMap<string> = {
+          'image/svg+xml': '<svg><script>windox.x=1</script></svg>'
+        };
+        let r = defaultRenderMime();
+        r.render(bundle).then(widget => {
+          expect(widget.node.innerHTML.indexOf('svg')).to.not.be(-1);
+          expect(widget.node.innerHTML.indexOf('script')).to.be(-1);
+        }).then(done, done);
+      });
+
     });
 
     describe('#preferredMimetype()', () => {
@@ -102,6 +156,24 @@ describe('jupyter-ui', () => {
         expect(r.preferredMimetype({ 'text/fizz': 'buzz' })).to.be(void 0);
       });
 
+      it('should select the mimetype that is safe', () => {
+        let bundle: RenderMime.MimeMap<string> = {
+          'text/plain': 'foo',
+          'text/javascript': 'window.x = 1',
+          'image/png': 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
+        };
+        let r = defaultRenderMime();
+        expect(r.preferredMimetype(bundle, false)).to.be('image/png');
+      });
+
+      it('should render the mimetype that is sanitizable', () => {
+        let bundle: RenderMime.MimeMap<string> = {
+          'text/plain': 'foo',
+          'text/html': '<h1>foo</h1>'
+        };
+        let r = defaultRenderMime();
+        expect(r.preferredMimetype(bundle, false)).to.be('text/html');
+      });
     });
 
     describe('#clone()', () => {