diff --git a/backend/src/middleware/helpers/cleanHtml.js b/backend/src/middleware/helpers/cleanHtml.js
new file mode 100644
index 000000000..c665dc8de
--- /dev/null
+++ b/backend/src/middleware/helpers/cleanHtml.js
@@ -0,0 +1,86 @@
+import sanitizeHtml from 'sanitize-html'
+import linkifyHtml from 'linkifyjs/html'
+
+const standardSanitizeHtmlOptions = {
+ allowedTags: [
+ 'img',
+ 'p',
+ 'h3',
+ 'h4',
+ 'br',
+ 'hr',
+ 'b',
+ 'i',
+ 'u',
+ 'em',
+ 'strong',
+ 'a',
+ 'pre',
+ 'ul',
+ 'li',
+ 'ol',
+ 's',
+ 'strike',
+ 'span',
+ 'blockquote',
+ ],
+ allowedAttributes: {
+ a: ['href', 'class', 'target', 'data-*', 'contenteditable'],
+ span: ['contenteditable', 'class', 'data-*'],
+ img: ['src'],
+ },
+ allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
+ parser: {
+ lowerCaseTags: true,
+ },
+ transformTags: {
+ h1: 'h3',
+ h2: 'h3',
+ h3: 'h3',
+ h4: 'h4',
+ h5: 'strong',
+ i: 'em',
+ a: (tagName, attribs) => {
+ return {
+ tagName: 'a',
+ attribs: {
+ ...attribs,
+ href: attribs.href || '',
+ target: '_blank',
+ rel: 'noopener noreferrer nofollow',
+ },
+ }
+ },
+ b: 'strong',
+ s: 'strike',
+ },
+}
+
+export function cleanHtml(dirty, sanitizeHtmlOptions = standardSanitizeHtmlOptions) {
+ if (!dirty) {
+ return dirty
+ }
+
+ dirty = linkifyHtml(dirty)
+ dirty = sanitizeHtml(dirty, sanitizeHtmlOptions)
+
+ // remove empty html tags and duplicated linebreaks and returns
+ dirty = dirty
+ // remove all tags with "space only"
+ .replace(/<[a-z-]+>[\s]+<\/[a-z-]+>/gim, '')
+ .replace(/[\n]{3,}/gim, '\n\n')
+ .replace(/(\r\n|\n\r|\r|\n)/g, '
$1')
+
+ // replace all p tags with line breaks (and spaces) only by single linebreaks
+ // limit linebreaks to max 2 (equivalent to html "br" linebreak)
+ .replace(/(
\s*){2,}/gim, '
')
+ // remove additional linebreaks after p tags
+ .replace(/<\/(p|div|th|tr)>\s*(
\s*)+\s*<(p|div|th|tr)>/gim, '
')
+ // remove additional linebreaks inside p tags
+ .replace(/<[a-z-]+>(<[a-z-]+>)*\s*(
\s*)+\s*(<\/[a-z-]+>)*<\/[a-z-]+>/gim, '')
+ // remove additional linebreaks when first child inside p tags
+ .replace(/
(\s*
\s*)+/gim, '
')
+ // remove additional linebreaks when last child inside p tags
+ .replace(/(\s*
\s*)+<\/p+>/gim, '
')
+ return dirty
+}
diff --git a/backend/src/middleware/helpers/email/sendMail.js b/backend/src/middleware/helpers/email/sendMail.js
index a25938a14..44c925482 100644
--- a/backend/src/middleware/helpers/email/sendMail.js
+++ b/backend/src/middleware/helpers/email/sendMail.js
@@ -1,4 +1,5 @@
import CONFIG from '../../../config'
+import { cleanHtml } from '../../../middleware/helpers/cleanHtml.js'
import nodemailer from 'nodemailer'
import { htmlToText } from 'nodemailer-html-to-text'
@@ -10,6 +11,27 @@ if (!hasEmailConfig) {
if (!CONFIG.TEST) {
// eslint-disable-next-line no-console
console.log('Warning: Middlewares will not try to send mails.')
+ // TODO: disable e-mail logging on database seeding?
+ // TODO: implement general logging like 'log4js', see Gradido project: https://github.com/gradido/gradido/blob/master/backend/log4js-config.json
+ sendMailCallback = async (templateArgs) => {
+ // eslint-disable-next-line no-console
+ console.log('--- Send E-Mail ---')
+ // eslint-disable-next-line no-console
+ console.log('To: ' + templateArgs.to)
+ // eslint-disable-next-line no-console
+ console.log('From: ' + templateArgs.from)
+ // eslint-disable-next-line no-console
+ console.log('Subject: ' + templateArgs.subject)
+ // eslint-disable-next-line no-console
+ console.log('Content:')
+ // eslint-disable-next-line no-console
+ console.log(
+ cleanHtml(templateArgs.html, {
+ allowedTags: ['a'],
+ allowedAttributes: { a: ['href'] },
+ }).replace(/&/g, '&'),
+ )
+ }
}
} else {
sendMailCallback = async (templateArgs) => {
diff --git a/backend/src/middleware/xssMiddleware.js b/backend/src/middleware/xssMiddleware.js
index 1292abb67..9c528b589 100644
--- a/backend/src/middleware/xssMiddleware.js
+++ b/backend/src/middleware/xssMiddleware.js
@@ -1,100 +1,15 @@
import walkRecursive from '../helpers/walkRecursive'
-// import { getByDot, setByDot, getItems, replaceItems } from 'feathers-hooks-common'
-import sanitizeHtml from 'sanitize-html'
-// import { isEmpty, intersection } from 'lodash'
-import linkifyHtml from 'linkifyjs/html'
-
-function clean(dirty) {
- if (!dirty) {
- return dirty
- }
-
- dirty = linkifyHtml(dirty)
- dirty = sanitizeHtml(dirty, {
- allowedTags: [
- 'img',
- 'p',
- 'h3',
- 'h4',
- 'br',
- 'hr',
- 'b',
- 'i',
- 'u',
- 'em',
- 'strong',
- 'a',
- 'pre',
- 'ul',
- 'li',
- 'ol',
- 's',
- 'strike',
- 'span',
- 'blockquote',
- ],
- allowedAttributes: {
- a: ['href', 'class', 'target', 'data-*', 'contenteditable'],
- span: ['contenteditable', 'class', 'data-*'],
- img: ['src'],
- },
- allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
- parser: {
- lowerCaseTags: true,
- },
- transformTags: {
- h1: 'h3',
- h2: 'h3',
- h3: 'h3',
- h4: 'h4',
- h5: 'strong',
- i: 'em',
- a: (tagName, attribs) => {
- return {
- tagName: 'a',
- attribs: {
- ...attribs,
- href: attribs.href || '',
- target: '_blank',
- rel: 'noopener noreferrer nofollow',
- },
- }
- },
- b: 'strong',
- s: 'strike',
- },
- })
-
- // remove empty html tags and duplicated linebreaks and returns
- dirty = dirty
- // remove all tags with "space only"
- .replace(/<[a-z-]+>[\s]+<\/[a-z-]+>/gim, '')
- .replace(/[\n]{3,}/gim, '\n\n')
- .replace(/(\r\n|\n\r|\r|\n)/g, '
$1')
-
- // replace all p tags with line breaks (and spaces) only by single linebreaks
- // limit linebreaks to max 2 (equivalent to html "br" linebreak)
- .replace(/(
\s*){2,}/gim, '
')
- // remove additional linebreaks after p tags
- .replace(/<\/(p|div|th|tr)>\s*(
\s*)+\s*<(p|div|th|tr)>/gim, '')
- // remove additional linebreaks inside p tags
- .replace(/<[a-z-]+>(<[a-z-]+>)*\s*(
\s*)+\s*(<\/[a-z-]+>)*<\/[a-z-]+>/gim, '')
- // remove additional linebreaks when first child inside p tags
- .replace(/
(\s*
\s*)+/gim, '
')
- // remove additional linebreaks when last child inside p tags
- .replace(/(\s*
\s*)+<\/p+>/gim, '
')
- return dirty
-}
+import { cleanHtml } from '../middleware/helpers/cleanHtml.js'
const fields = ['content', 'contentExcerpt', 'reasonDescription']
export default {
Mutation: async (resolve, root, args, context, info) => {
- args = walkRecursive(args, fields, clean)
+ args = walkRecursive(args, fields, cleanHtml)
return resolve(root, args, context, info)
},
Query: async (resolve, root, args, context, info) => {
const result = await resolve(root, args, context, info)
- return walkRecursive(result, fields, clean)
+ return walkRecursive(result, fields, cleanHtml)
},
}
diff --git a/cypress/integration/Notification.Mention.feature b/cypress/integration/Notification.Mention.feature
index cadfe11dd..0d3726492 100644
--- a/cypress/integration/Notification.Mention.feature
+++ b/cypress/integration/Notification.Mention.feature
@@ -5,9 +5,9 @@ Feature: Notification for a mention
Background:
Given the following "users" are in the database:
- | slug | email | password | id | name | termsAndConditionsAgreedVersion |
- | wolle-aus-hamburg | wolle@example.org | 1234 | wolle | Wolle aus Hamburg | 0.0.4 |
- | matt-rider | matt@example.org | 4321 | matt | Matt Rider | 0.0.4 |
+ | slug | email | password | id | name | termsAndConditionsAgreedVersion |
+ | wolle-aus-hamburg | wolle@example.org | 1234 | wolle | Wolfgang aus Hamburg | 0.0.4 |
+ | matt-rider | matt@example.org | 4321 | matt | Matt Rider | 0.0.4 |
Scenario: Mention another user, re-login as this user and see notifications
Given I am logged in as "wolle-aus-hamburg"
diff --git a/webapp/components/Registration/RegistrationSlider.story.js b/webapp/components/Registration/RegistrationSlider.story.js
index 9fb1c347f..ef048db47 100644
--- a/webapp/components/Registration/RegistrationSlider.story.js
+++ b/webapp/components/Registration/RegistrationSlider.story.js
@@ -97,7 +97,7 @@ storiesOf('RegistrationSlider', module)
email: 'wolle.huss@pjannto.com',
emailSend: false,
nonce: '47539',
- name: 'Wolle',
+ name: 'Wolfgang',
password: 'Hello',
passwordConfirmation: 'Hello',
termsAndConditionsConfirmed: true,
@@ -127,7 +127,7 @@ storiesOf('RegistrationSlider', module)
email: 'wolle.huss@pjannto.com',
emailSend: false,
nonce: '47539',
- name: 'Wolle',
+ name: 'Wolfgang',
password: 'Hello',
passwordConfirmation: 'Hello',
termsAndConditionsConfirmed: true,
@@ -171,7 +171,7 @@ storiesOf('RegistrationSlider', module)
email: 'wolle.huss@pjannto.com',
emailSend: true,
nonce: '47539',
- name: 'Wolle',
+ name: 'Wolfgang',
password: 'Hello',
passwordConfirmation: 'Hello',
termsAndConditionsConfirmed: true,