/* eslint-disable no-param-reassign */
const sanitizeHtml = require('sanitize-html');

const maxDeep = 12;
// enable html for all current editors
const keys = ['content', 'text', 'comment', 'gradeComment', 'description'];
const paths = ['lessons', 'news', 'newsModel', 'homework', 'submissions'];
const saveKeys = ['password', 'secret'];
const allowedTags = ['h1', 'h2', 'h3', 'blockquote', 'p', 'a', 'ul', 'ol', 's', 'u', 'span', 'del',
	'li', 'b', 'i', 'img', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
	'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'audio', 'video', 'iframe', 'sub', 'sup'];
const allowedSchemes = ['http', 'https', 'ftp', 'mailto'];

const htmlTrueOptions = {
	allowedTags,
	allowedAttributes: false, // allow all attributes of allowed tags
	allowedSchemes,
	parser: {
		decodeEntities: true,
	},
};

const htmlFalseOptions = {
	allowedTags: [], // disallow all tags
	allowedAttributes: [], // disallow all attributes
	allowedSchemes: [], // disallow url schemes
	parser: {
		decodeEntities: true,
	},
};

const sanitize = (data, options) => {
	// https://www.npmjs.com/package/sanitize-html
	if ((options || {}).html === true) {
		// editor-content data
		data = sanitizeHtml(data, htmlTrueOptions);
		data = data.replace(/(&lt;script&gt;).*?(&lt;\/script&gt;)/gim, ''); // force remove script tags
		data = data.replace(/(<script>).*?(<\/script>)/gim, ''); // force remove script tags
	} else {
		// non editor-content data
		data = sanitizeHtml(data, htmlFalseOptions);
	}
	return data;
};

const allowedHtmlByPathAndKeys = (path, key) => paths.includes(path) && keys.includes(key);

/**
 * Strips JS/HTML Code from data and returns clean version of it
 * @param data {object/array/string}
 * @param path {string}
 * @param depth {number} -
 * @param safeAttributes {array} - attributes over which sanitization won't be performed
 * @returns data - clean without JS
 */
const sanitizeDeep = (data, path, depth = 0, safeAttributes = []) => {
	if (depth >= maxDeep) {
		throw new Error('Data level is to deep. (sanitizeDeep)', { path, data });
	}
	if (typeof data === 'object' && data !== null) {
		// eslint-disable-next-line consistent-return
		Object.entries(data).forEach(([key, value]) => {
			if (typeof value === 'string') {
				// ignore values completely
				if (saveKeys.includes(key) || safeAttributes.includes(key)) {
					return data; // TODO:  why not over keys in allowedHtmlByPathAndKeys
				}
				data[key] = sanitize(value, { html: allowedHtmlByPathAndKeys(path, key) });
			} else {
				sanitizeDeep(value, path, depth + 1);
			}
		});
	} else if (typeof data === 'string') {
		data = sanitize(data, { html: false });
	} else if (Array.isArray(data)) {
		for (let i = 0; i < data.length; i += 1) {
			if (typeof data[i] === 'string') {
				data[i] = sanitize(data[i], { html: false });
			} else {
				sanitizeDeep(data[i], path, depth + 1);
			}
		}
	}
	return data;
};

module.exports = {
	sanitizeDeep,
	sanitizeConsts: {
		keys,
		paths,
		saveKeys,
		allowedTags,
		allowedSchemes,
		maxDeep,
	},
};

@@ -2135,24 +2135,91 @@
 !== null) %7B%0A
+%09%09// we have an object, can match strings or recurse child objects%0A
 %09%09// eslint-
@@ -2254,16 +2254,16 @@
 -return%0A
-
 %09%09Object
@@ -2642,32 +2642,48 @@
  path, depth + 1
+, safeAttributes
 );%0A%09%09%09%7D%0A%09%09%7D);%0A%09%7D
@@ -2720,16 +2720,52 @@
 ing') %7B%0A
+%09%09// here we can sanitize the input%0A
 %09%09data =
@@ -2833,16 +2833,99 @@
 ata)) %7B%0A
+%09%09// here we have to check all array elements and sanitize strings or do recursion%0A
 %09%09for (l
@@ -3053,32 +3053,32 @@
 %7D);%0A%09%09%09%7D else %7B%0A
-
 %09%09%09%09sanitizeDeep
@@ -3102,16 +3102,32 @@
 epth + 1
+, safeAttributes
 );%0A%09%09%09%7D%0A

fix sanitizeDeep recursion call to keep safeAttributes