Procházet zdrojové kódy

Merge remote-tracking branch 'origin/master' into imprv/refactor-attachment

Yuki Takei před 7 roky
rodič
revize
6a20d0f4ca

+ 11 - 1
CHANGES.md

@@ -1,9 +1,19 @@
 CHANGES
 ========
 
-## 3.3.4-RC
+## 3.3.5-RC
 
+* Fix: Prevent XSS by DetachCodeBlockInterceptor
+* Fix: NPE occured on /admin/security when Crowi Classic Auth Mechanism is set
+
+## 3.3.4
+
+* Improvement: SAML configuration with environment variables
+* Improvement: Upload file with pasting from clipboard
 * Fix: `/_api/revisions.get` doesn't populate author data correctly
+* Fix: Wrong OAuth callback url are shown at admin page
+* Fix: Connecting to MongoDB failed when processing migration
+* Support: Get ready to use new config management system
 
 ## 3.3.3
 

+ 1 - 1
config/migrate.js

@@ -18,7 +18,7 @@ const match = mongoUri.match(/^(.+)\/([^/]+)$/);
 module.exports = {
   mongoUri,
   mongodb: {
-    url: match[1],
+    url: match[0],
     databaseName: match[2],
     options: {
       useNewUrlParser: true, // removes a deprecation warning when connecting

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.3.4-RC",
+  "version": "3.3.5-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",

+ 11 - 10
resource/cdn-manifests.js

@@ -10,7 +10,7 @@ module.exports = {
     },
     {
       name: 'highlight',
-      url: 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/highlight.min.js',
+      url: 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.13.0/build/highlight.min.js',
       groups: ['basis'],
       args: {
         integrity: '',
@@ -19,14 +19,15 @@ module.exports = {
     {
       name: 'highlight-addons',
       url: 'https://cdn.jsdelivr.net/combine/' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/dockerfile.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/go.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/gradle.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/json.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/less.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/scss.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/typescript.min.js,' +
-'gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/dockerfile.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/go.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/gradle.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/json.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/less.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/plaintext.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/scss.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/typescript.min.js,' +
+'gh/highlightjs/cdn-release@9.13.0/build/languages/yaml.min.js,' +
 'npm/highlightjs-line-numbers.js@2.6.0/dist/highlightjs-line-numbers.min.js',
       args: {
         async: true,
@@ -120,7 +121,7 @@ module.exports = {
     },
     {
       name: 'highlight-theme-github',
-      url: 'https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/github.css',
+      url: 'https://cdn.jsdelivr.net/npm/highlight.js@9.13.0/styles/github.css',
       args: {
         integrity: ''
       },

+ 1 - 1
resource/locales/en-US/sandbox.md

@@ -77,7 +77,7 @@ piyo
 
 ## Code コード
 
-`` `バッククオート` `` 3つ、あるいはダッシュ`~`3つで囲みます。
+`` `バッククオート` `` 3つ、あるいはチルダ`~`3つで囲みます。
 
 ```
 print 'hoge'

+ 1 - 1
resource/locales/ja/sandbox.md

@@ -77,7 +77,7 @@ piyo
 
 ## Code コード
 
-`` `バッククオート` `` 3つ、あるいはダッシュ`~`3つで囲みます。
+`` `バッククオート` `` 3つ、あるいはチルダ`~`3つで囲みます。
 
 ```
 print 'hoge'

+ 7 - 5
src/client/js/components/PageEditor/CodeMirrorEditor.js

@@ -461,14 +461,16 @@ export default class CodeMirrorEditor extends AbstractEditor {
   pasteHandler(editor, event) {
     const types = event.clipboardData.types;
 
-    // text
-    if (types.includes('text/plain')) {
-      pasteHelper.pasteText(this, event);
-    }
     // files
-    else if (types.includes('Files')) {
+    if (types.includes('Files')) {
+      event.preventDefault();
       this.dispatchPasteFiles(event);
     }
+    // text
+    else if (types.includes('text/plain')) {
+      pasteHelper.pasteText(this, event);
+    }
+
   }
 
   /**

+ 15 - 7
src/client/js/components/PageEditor/Editor.jsx

@@ -109,17 +109,25 @@ export default class Editor extends AbstractEditor {
     const items = event.clipboardData.items || event.clipboardData.files || [];
 
     // abort if length is not 1
-    if (items.length != 1) {
+    if (items.length < 1) {
       return;
     }
 
-    const file = items[0].getAsFile();
-    // check type and size
-    if (pasteHelper.fileAccepted(file, dropzone.props.accept) &&
-        pasteHelper.fileMatchSize(file, dropzone.props.maxSize, dropzone.props.minSize)) {
+    for (let i = 0; i < items.length; i++) {
+      try {
+        const file = items[i].getAsFile();
+        // check type and size
+        if (file != null &&
+            pasteHelper.fileAccepted(file, dropzone.props.accept) &&
+            pasteHelper.fileMatchSize(file, dropzone.props.maxSize, dropzone.props.minSize)) {
 
-      this.dispatchUpload(file);
-      this.setState({ isUploading: true });
+          this.dispatchUpload(file);
+          this.setState({ isUploading: true });
+        }
+      }
+      catch (e) {
+        this.logger.error(e);
+      }
     }
   }
 

+ 6 - 5
src/client/js/components/PageEditor/TextAreaEditor.js

@@ -218,14 +218,15 @@ export default class TextAreaEditor extends AbstractEditor {
   pasteHandler(event) {
     const types = event.clipboardData.types;
 
-    // text
-    if (types.includes('text/plain')) {
-      pasteHelper.pasteText(this, event);
-    }
     // files
-    else if (types.includes('Files')) {
+    if (types.includes('Files')) {
+      event.preventDefault();
       this.dispatchPasteFiles(event);
     }
+    // text
+    else if (types.includes('text/plain')) {
+      pasteHelper.pasteText(this, event);
+    }
   }
 
   dragEnterHandler(event) {

+ 30 - 20
src/client/js/util/GrowiRenderer.js

@@ -1,5 +1,4 @@
 import MarkdownIt from 'markdown-it';
-import xss from 'xss';
 
 import Linker        from './PreProcessor/Linker';
 import CsvToTable    from './PreProcessor/CsvToTable';
@@ -19,6 +18,9 @@ import BlockdiagConfigurer from './markdown-it/blockdiag';
 import TableWithHandsontableButtonConfigurer from './markdown-it/table-with-handsontable-button';
 import HeaderWithEditLinkConfigurer from './markdown-it/header-with-edit-link';
 
+const logger = require('@alias/logger')('growi:util:GrowiRenderer');
+
+
 export default class GrowiRenderer {
 
   /**
@@ -34,8 +36,6 @@ export default class GrowiRenderer {
       { isAutoSetup: true },      // default options
       options || {});             // specified options
 
-    this.xssFilterForCode = new xss.FilterXSS();
-
     // initialize processors
     //  that will be retrieved if originRenderer exists
     this.preProcessors = this.originRenderer.preProcessors || [
@@ -166,33 +166,43 @@ export default class GrowiRenderer {
     const config = this.crowi.getConfig();
     const noborder = (!config.highlightJsStyleBorder) ? 'hljs-no-border' : '';
 
+    let citeTag = '';
+    let hljsLang = 'plaintext';
+    let showLinenumbers = false;
+
     if (langExt) {
-      // https://regex101.com/r/qGs7eZ/1
-      const match = langExt.match(/^([^:=\n]+)(=([^:=\n]*))?(:([^:=\n]+))?(=([^:=\n]*))?$/);
+      // https://regex101.com/r/qGs7eZ/3
+      const match = langExt.match(/^([^:=\n]+)?(=([^:=\n]*))?(:([^:=\n]*))?(=([^:=\n]*))?$/);
 
       const lang = match[1];
       const fileName = match[5] || null;
-      const showLinenumbers = (match[2] != null) || (match[6] != null);
-
-      const citeTag = (fileName) ? `<cite>${fileName}</cite>` : '';
+      showLinenumbers = (match[2] != null) || (match[6] != null);
 
+      if (fileName != null) {
+        citeTag = `<cite>${fileName}</cite>`;
+      }
       if (hljs.getLanguage(lang)) {
-        try {
-          const highlightCode = showLinenumbers ? hljs.lineNumbersValue(hljs.highlight(lang, code, true).value) : hljs.highlight(lang, code, true).value;
-          return `<pre class="hljs ${noborder}">${citeTag}<code class="language-${lang}">${highlightCode}</code></pre>`;
-        }
-        catch (__) {
-          return `<pre class="hljs ${noborder}">${citeTag}<code class="language-${lang}">${code}}</code></pre>`;
-        }
+        hljsLang = lang;
       }
-      else {
-        const escapedCode = this.xssFilterForCode.process(code);
-        return `<pre class="hljs ${noborder}">${citeTag}<code>${escapedCode}</code></pre>`;
+    }
+
+    let highlightCode = code;
+    try {
+      highlightCode = hljs.highlight(hljsLang, code, true).value;
+
+      // add line numbers
+      if (showLinenumbers) {
+        highlightCode = hljs.lineNumbersValue((highlightCode));
       }
     }
+    catch (err) {
+      logger.error(err);
+    }
+
+    return `<pre class="hljs ${noborder}">${citeTag}<code>${highlightCode}</code></pre>`;
+  }
 
-    const escapedCode = this.xssFilterForCode.process(code);
-    return `<pre class="hljs ${noborder}"><code>${escapedCode}</code></pre>`;
+  highlightCode(code, lang) {
   }
 
 }

+ 2 - 2
src/client/js/util/interceptor/detach-code-blocks.js

@@ -50,8 +50,8 @@ export class DetachCodeBlockInterceptor extends BasicInterceptor {
 
     context.dcbContextMap = {};
 
-    // see: https://regex101.com/r/8PAEcC/4
-    context[targetKey] = context[targetKey].replace(/((```|~~~)(.|[\r\n])*?(```|~~~))|(`[^\r\n]*?`)|(<pre>(.|[\r\n])*?<\/pre>)|(<pre\s[^>]*>(.|[\r\n])*?<\/pre>)/gm, (all) => {
+    // see: https://regex101.com/r/8PAEcC/5
+    context[targetKey] = context[targetKey].replace(/(^(```|~~~)(.|[\r\n])*?(```|~~~)$)|(`[^\r\n]*?`)|(<pre>(.|[\r\n])*?<\/pre>)|(<pre\s[^>]*>(.|[\r\n])*?<\/pre>)/gm, (all) => {
       // create ID
       const replaceId = 'dcb-' + this.createRandomStr(8);
       this.logger.debug(`'replaceId'=${replaceId} : `, all);

+ 2 - 1
src/server/models/user.js

@@ -1,5 +1,6 @@
 module.exports = function(crowi) {
   const debug = require('debug')('growi:models:user')
+    , logger = require('@alias/logger')('growi:models:user')
     , path = require('path')
     , mongoose = require('mongoose')
     , mongoosePaginate = require('mongoose-paginate')
@@ -767,7 +768,7 @@ module.exports = function(crowi) {
 
     newUser.save(function(err, userData) {
       if (err) {
-        debug('createUserByEmailAndPassword failed: ', err);
+        logger.error('createUserByEmailAndPasswordAndStatus failed: ', err);
         return callback(err);
       }
 

+ 6 - 0
src/server/util/swigFunctions.js

@@ -141,6 +141,12 @@ module.exports = function(crowi, app, req, locals) {
   };
 
   locals.getSamlMissingMandatoryConfigKeys = function() {
+    // return an empty array if Passport is not enabled
+    // because crowi.passportService is null.
+    if (!locals.isEnabledPassport()) {
+      return [];
+    }
+
     return crowi.passportService.getSamlMissingMandatoryConfigKeys();
   };
 

+ 1 - 1
src/server/views/admin/customize.html

@@ -417,7 +417,7 @@ export  $initHighlight;</code></pre>
 
         <p class="help-block">
           Examples:
-          <pre class="hljs"><code>&lt;script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js" defer&gt;&lt;/script&gt;</code></pre>
+          <pre class="hljs"><code>&lt;script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.13.0/build/languages/yaml.min.js" defer&gt;&lt;/script&gt;</code></pre>
         </p>
 
         <div class="form-group">