소스 검색

feat: rendering security module

NGPixel 5 년 전
부모
커밋
278cd7173d

+ 1 - 0
package.json

@@ -161,6 +161,7 @@
     "uuid": "3.3.3",
     "validate.js": "0.13.1",
     "winston": "3.2.1",
+    "xss": "1.0.6",
     "yargs": "15.0.2"
   },
   "devDependencies": {

+ 9 - 7
server/graph/resolvers/rendering.js

@@ -19,14 +19,16 @@ module.exports = {
           ...rendererInfo,
           ...rdr,
           config: _.sortBy(_.transform(rdr.config, (res, value, key) => {
-            const configData = _.get(rendererInfo.props, key, {})
-            res.push({
-              key,
-              value: JSON.stringify({
-                ...configData,
-                value
+            const configData = _.get(rendererInfo.props, key, false)
+            if (configData) {
+              res.push({
+                key,
+                value: JSON.stringify({
+                  ...configData,
+                  value
+                })
               })
-            })
+            }
           }, []), 'key')
         }
       })

+ 1 - 0
server/modules/rendering/html-codehighlighter/definition.yml

@@ -5,4 +5,5 @@ author: requarks.io
 icon: mdi-code-braces
 enabledDefault: true
 dependsOn: htmlCore
+step: pre
 props: {}

+ 17 - 2
server/modules/rendering/html-core/renderer.js

@@ -14,7 +14,11 @@ module.exports = {
       return ''
     }
 
-    for (let child of this.children) {
+    // --------------------------------
+    // STEP: PRE
+    // --------------------------------
+
+    for (let child of _.reject(this.children, ['step', 'post'])) {
       const renderer = require(`../${_.kebabCase(child.key)}/renderer.js`)
       renderer.init($, child.config)
     }
@@ -211,6 +215,17 @@ module.exports = {
       headers.push(headerSlug)
     })
 
-    return $.html('body').replace('<body>', '').replace('</body>', '')
+    let output = $.html('body').replace('<body>', '').replace('</body>', '')
+
+    // --------------------------------
+    // STEP: POST
+    // --------------------------------
+
+    for (let child of _.filter(this.children, ['step', 'post'])) {
+      const renderer = require(`../${_.kebabCase(child.key)}/renderer.js`)
+      output = renderer.init(output, child.config)
+    }
+
+    return output
   }
 }

+ 1 - 0
server/modules/rendering/html-mathjax/definition.yml

@@ -5,4 +5,5 @@ author: requarks.io
 icon: mdi-function-variant
 enabledDefault: false
 dependsOn: htmlCore
+step: pre
 props: {}

+ 5 - 9
server/modules/rendering/html-security/definition.yml

@@ -5,14 +5,10 @@ author: requarks.io
 icon: mdi-fire
 enabledDefault: true
 dependsOn: htmlCore
+step: post
 props:
-  stripJS:
+  safeHTML:
     type: Boolean
-    title: Strip Javascript
-    default: false
-    hint: Javascript code within code blocks won't be affected
-  filterBadWords:
-    type: Boolean
-    title: Filter Bad Words
-    default: false
-    hint: Replace bad words with asterisks
+    title: Sanitize HTML
+    default: true
+    hint: Sanitize HTML from unsafe attributes and tags that could lead to XSS attacks

+ 35 - 2
server/modules/rendering/html-security/renderer.js

@@ -1,5 +1,38 @@
-module.exports = {
-  init($, config) {
+const xss = require('xss')
 
+module.exports = {
+  async init(input, config) {
+    if (config.safeHTML) {
+      input = xss(input, {
+        whiteList: {
+          ...xss.whiteList,
+          a: ['class', 'id', 'href', 'target', 'title'],
+          blockquote: ['class', 'id'],
+          code: ['class'],
+          div: ['class', 'id'],
+          em: ['class'],
+          h1: ['class', 'id'],
+          h2: ['class', 'id'],
+          h3: ['class', 'id'],
+          h4: ['class', 'id'],
+          h5: ['class', 'id'],
+          h6: ['class', 'id'],
+          img: ['alt', 'class', 'draggable', 'height', 'src', 'width'],
+          li: ['class'],
+          ol: ['class'],
+          p: ['class'],
+          pre: ['class'],
+          strong: ['class'],
+          table: ['border', 'class', 'id', 'width'],
+          tbody: ['class'],
+          td: ['align', 'class', 'colspan', 'rowspan', 'valign'],
+          th: ['align', 'class', 'colspan', 'rowspan', 'valign'],
+          thead: ['class'],
+          tr: ['class', 'rowspan', 'align', 'valign'],
+          ul: ['class']
+        }
+      })
+    }
+    return input
   }
 }

+ 13 - 0
yarn.lock

@@ -4447,6 +4447,11 @@ cssesc@^3.0.0:
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
+cssfilter@0.0.10:
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
+  integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=
+
 cssnano-preset-default@^4.0.7:
   version "4.0.7"
   resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76"
@@ -14363,6 +14368,14 @@ xpath@0.0.27:
   resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92"
   integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==
 
+xss@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.6.tgz#eaf11e9fc476e3ae289944a1009efddd8a124b51"
+  integrity sha512-6Q9TPBeNyoTRxgZFk5Ggaepk/4vUOYdOsIUYvLehcsIZTFjaavbVnsuAkLA5lIFuug5hw8zxcB9tm01gsjph2A==
+  dependencies:
+    commander "^2.9.0"
+    cssfilter "0.0.10"
+
 xtend@^4.0.0, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"