Yuki Takei 1 год назад
Родитель
Сommit
71ffbc5603

+ 39 - 0
apps/app/src/services/general-xss-filter/general-xss-filter.spec.ts

@@ -0,0 +1,39 @@
+import { generalXssFilter } from './general-xss-filter';
+
+describe('generalXssFilter', () => {
+
+  test('should be sanitize script tag', () => {
+    // Act
+    const result = generalXssFilter.process('<script>alert("XSS")</script>');
+
+    // Assert
+    expect(result).toBe('alert("XSS")');
+  });
+
+  test('should be sanitize nested script tag recursively', () => {
+    // Act
+    const result = generalXssFilter.process('<scr<script>ipt>alert("XSS")</scr<script>ipt>');
+
+    // Assert
+    expect(result).toBe('alert("XSS")');
+  });
+
+  // for https://github.com/weseek/growi/issues/221
+  test('should not be sanitize blockquote', () => {
+    // Act
+    const result = generalXssFilter.process('> foo\n> bar');
+
+    // Assert
+    expect(result).toBe('> foo\n> bar');
+  });
+
+  // https://github.com/weseek/growi/pull/505
+  test('should not be sanitize next closing-tag', () => {
+    // Act
+    const result = generalXssFilter.process('<evil /><span>text</span>');
+
+    // Assert
+    expect(result).toBe('<span>text</span>');
+  });
+
+});

+ 12 - 31
apps/app/src/services/general-xss-filter/general-xss-filter.ts

@@ -1,39 +1,18 @@
 import type { IFilterXSSOptions } from 'xss';
 import { FilterXSS } from 'xss';
 
-import { uriAutolinkRegexp, emailAutolinkRegexp } from './commonmark-spec';
-import type XssOption from './xssOption';
-
-
 const REPETITIONS_NUM = 50;
 
-export class Xss {
-
-  myxss: FilterXSS;
-
-  constructor(xssOption?: XssOption) {
-
-    const option: IFilterXSSOptions = {
-      stripIgnoreTag: true,
-      stripIgnoreTagBody: false, // see https://github.com/weseek/growi/pull/505
-      css: false,
-      whiteList: xssOption != null
-        ? xssOption.attrWhitelist as Record<string, string[] | undefined>
-        : {},
-      escapeHtml: (html) => { return html }, // resolve https://github.com/weseek/growi/issues/221
-      onTag: (tag, html) => {
-        // pass autolink
-        if (tag.match(uriAutolinkRegexp) || tag.match(emailAutolinkRegexp)) {
-          return html;
-        }
-      },
-    };
-
-    // create the XSS Filter instance
-    this.myxss = new FilterXSS(option);
-  }
+const option: IFilterXSSOptions = {
+  stripIgnoreTag: true,
+  stripIgnoreTagBody: false, // see https://github.com/weseek/growi/pull/505
+  css: false,
+  escapeHtml: (html) => { return html }, // resolve https://github.com/weseek/growi/issues/221
+};
 
-  process(document: string | undefined): string {
+class GeneralXssFilter extends FilterXSS {
+
+  override process(document: string | undefined): string {
     let count = 0;
     let currDoc = document;
     let prevDoc = document;
@@ -46,7 +25,7 @@ export class Xss {
       }
 
       prevDoc = currDoc;
-      currDoc = this.myxss.process(currDoc ?? '');
+      currDoc = super.process(currDoc ?? '');
     }
     while (currDoc !== prevDoc);
 
@@ -54,3 +33,5 @@ export class Xss {
   }
 
 }
+
+export const generalXssFilter = new GeneralXssFilter(option);

+ 1 - 0
apps/app/src/services/general-xss-filter/index.ts

@@ -0,0 +1 @@
+export * from './general-xss-filter';

+ 0 - 36
apps/app/src/services/xss/commonmark-spec.ts

@@ -1,36 +0,0 @@
-/**
- * Valid schemes
- * @see https://spec.commonmark.org/0.16/#autolinks
- */
-const schemesForAutolink = [
-  'coap', 'doi', 'javascript', 'aaa', 'aaas', 'about', 'acap', 'cap', 'cid', 'crid', 'data', 'dav', 'dict', 'dns',
-  'file', 'ftp', 'geo', 'go', 'gopher', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipp', 'iris',
-  'iris.beep', 'iris.xpc', 'iris.xpcs', 'iris.lwz', 'ldap', 'mailto', 'mid', 'msrp', 'msrps', 'mtqp', 'mupdate',
-  'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'pop', 'pres', 'rtsp', 'service', 'session', 'shttp',
-  'sieve', 'sip', 'sips', 'sms', 'snmp,soap.beep', 'soap.beeps', 'tag', 'tel', 'telnet', 'tftp', 'thismessage',
-  'tn3270', 'tip', 'tv', 'urn', 'vemmi', 'ws', 'wss', 'xcon', 'xcon-userid', 'xmlrpc.beep', 'xmlrpc.beeps', 'xmpp',
-  'z39.50r', 'z39.50s', 'adiumxtra', 'afp', 'afs', 'aim', 'apt,attachment', 'aw', 'beshare', 'bitcoin', 'bolo',
-  'callto', 'chrome,chrome-extension', 'com-eventbrite-attendee', 'content', 'cvs,dlna-playsingle', 'dlna-playcontainer',
-  'dtn', 'dvb', 'ed2k', 'facetime', 'feed', 'finger', 'fish', 'gg', 'git', 'gizmoproject', 'gtalk', 'hcp', 'icon',
-  'ipn', 'irc', 'irc6', 'ircs', 'itms', 'jar', 'jms', 'keyparc', 'lastfm', 'ldaps', 'magnet', 'maps', 'market,message',
-  'mms', 'ms-help', 'msnim', 'mumble', 'mvn', 'notes', 'oid', 'palm', 'paparazzi', 'platform', 'proxy', 'psyc',
-  'query', 'res', 'resource', 'rmi', 'rsync', 'rtmp', 'secondlife', 'sftp', 'sgn', 'skype', 'smb', 'soldat', 'spotify',
-  'ssh', 'steam', 'svn', 'teamspeak', 'things', 'udp', 'unreal', 'ut2004', 'ventrilo', 'view-source', 'webcal',
-  'wtai', 'wyciwyg', 'xfire', 'xri', 'ymsgr',
-];
-const schemesCondition = schemesForAutolink.join('|');
-
-/**
- * RegExp for URI
- * @type {RegExp}
- * @see https://spec.commonmark.org/0.16/#autolinks
- */
-export const uriAutolinkRegexp = new RegExp(`^(${schemesCondition}):\\/\\/.+$`);
-
-/**
- * RegExp for email
- * @type {RegExp}
- * @see https://spec.commonmark.org/0.16/#autolinks
- */
-// eslint-disable-next-line max-len
-export const emailAutolinkRegexp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;

+ 0 - 1
apps/app/src/services/xss/index.ts

@@ -1 +0,0 @@
-export * from './xss';

+ 0 - 44
apps/app/src/services/xss/xss.spec.ts

@@ -1,44 +0,0 @@
-import { Xss } from './xss';
-
-describe('XSSService', () => {
-
-  describe('without config', () => {
-    const xss = new Xss();
-
-    test('should be sanitize script tag', () => {
-      // Act
-      const result = xss.process('<script>alert("XSS")</script>');
-
-      // Assert
-      expect(result).toBe('alert("XSS")');
-    });
-
-    test('should be sanitize nested script tag recursively', () => {
-      // Act
-      const result = xss.process('<scr<script>ipt>alert("XSS")</scr<script>ipt>');
-
-      // Assert
-      expect(result).toBe('alert("XSS")');
-    });
-
-    // for https://github.com/weseek/growi/issues/221
-    test('should not be sanitize blockquote', () => {
-      // Act
-      const result = xss.process('> foo\n> bar');
-
-      // Assert
-      expect(result).toBe('> foo\n> bar');
-    });
-
-    // https://github.com/weseek/growi/pull/505
-    test('should not be sanitize next closing-tag', () => {
-      // Act
-      const result = xss.process('<code /><span>text</span>');
-
-      // Assert
-      expect(result).toBe('text');
-    });
-
-  });
-
-});