mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 23:40:13 +01:00
Fix DOMPurify paths. Part 4.
Thanks to xet7 !
This commit is contained in:
parent
a85479f73c
commit
2119c6ab0c
4 changed files with 128 additions and 9 deletions
|
|
@ -52,14 +52,6 @@ template(name="userFormsLayout")
|
||||||
else
|
else
|
||||||
// ARIA live region for error messages
|
// ARIA live region for error messages
|
||||||
div#login-error-message(role="alert" aria-live="assertive" style="color: #d32f2f; margin-bottom: 1em;")
|
div#login-error-message(role="alert" aria-live="assertive" style="color: #d32f2f; margin-bottom: 1em;")
|
||||||
// Add autocomplete attribute to login input for WCAG compliance
|
|
||||||
script.
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var loginInput = document.querySelector('input[type="text"], input[type="email"]');
|
|
||||||
if (loginInput && loginInput.name && (loginInput.name.toLowerCase().includes('user') || loginInput.name.toLowerCase().includes('email'))) {
|
|
||||||
loginInput.setAttribute('autocomplete', 'username email');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
+Template.dynamic(template=content)
|
+Template.dynamic(template=content)
|
||||||
if currentSetting.displayAuthenticationMethod
|
if currentSetting.displayAuthenticationMethod
|
||||||
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,12 @@ Template.userFormsLayout.onRendered(() => {
|
||||||
validator,
|
validator,
|
||||||
);
|
);
|
||||||
EscapeActions.executeAll();
|
EscapeActions.executeAll();
|
||||||
|
|
||||||
|
// Add autocomplete attribute to login input for WCAG compliance
|
||||||
|
const loginInput = document.querySelector('input[type="text"], input[type="email"]');
|
||||||
|
if (loginInput && loginInput.name && (loginInput.name.toLowerCase().includes('user') || loginInput.name.toLowerCase().includes('email'))) {
|
||||||
|
loginInput.setAttribute('autocomplete', 'username email');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
121
packages/markdown/src/secureDOMPurify.js
Normal file
121
packages/markdown/src/secureDOMPurify.js
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
|
// Centralized secure DOMPurify configuration to prevent XSS and CSS injection attacks
|
||||||
|
export function getSecureDOMPurifyConfig() {
|
||||||
|
return {
|
||||||
|
// Block dangerous elements that can cause XSS and CSS injection
|
||||||
|
FORBID_TAGS: [
|
||||||
|
'svg', 'defs', 'use', 'g', 'symbol', 'marker', 'pattern', 'mask', 'clipPath',
|
||||||
|
'linearGradient', 'radialGradient', 'stop', 'animate', 'animateTransform',
|
||||||
|
'animateMotion', 'set', 'switch', 'foreignObject', 'script', 'style', 'link',
|
||||||
|
'meta', 'iframe', 'object', 'embed', 'applet', 'form', 'input', 'textarea',
|
||||||
|
'select', 'option', 'button', 'label', 'fieldset', 'legend', 'frameset',
|
||||||
|
'frame', 'noframes', 'base', 'basefont', 'isindex', 'dir', 'menu', 'menuitem'
|
||||||
|
],
|
||||||
|
// Block dangerous attributes that can cause XSS and CSS injection
|
||||||
|
FORBID_ATTR: [
|
||||||
|
'xlink:href', 'href', 'onload', 'onerror', 'onclick', 'onmouseover',
|
||||||
|
'onfocus', 'onblur', 'onchange', 'onsubmit', 'onreset', 'onselect',
|
||||||
|
'onunload', 'onresize', 'onscroll', 'onkeydown', 'onkeyup', 'onkeypress',
|
||||||
|
'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmousemove',
|
||||||
|
'ondblclick', 'oncontextmenu', 'onwheel', 'ontouchstart', 'ontouchend',
|
||||||
|
'ontouchmove', 'ontouchcancel', 'onabort', 'oncanplay', 'oncanplaythrough',
|
||||||
|
'ondurationchange', 'onemptied', 'onended', 'onerror', 'onloadeddata',
|
||||||
|
'onloadedmetadata', 'onloadstart', 'onpause', 'onplay', 'onplaying',
|
||||||
|
'onprogress', 'onratechange', 'onseeked', 'onseeking', 'onstalled',
|
||||||
|
'onsuspend', 'ontimeupdate', 'onvolumechange', 'onwaiting', 'onbeforeunload',
|
||||||
|
'onhashchange', 'onpagehide', 'onpageshow', 'onpopstate', 'onstorage',
|
||||||
|
'onunload', 'style', 'class', 'id', 'data-*', 'aria-*'
|
||||||
|
],
|
||||||
|
// Allow only safe image formats and protocols
|
||||||
|
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
|
||||||
|
// Remove dangerous protocols
|
||||||
|
ALLOW_UNKNOWN_PROTOCOLS: false,
|
||||||
|
// Sanitize URLs to prevent malicious content loading
|
||||||
|
SANITIZE_DOM: true,
|
||||||
|
// Remove dangerous elements completely
|
||||||
|
KEEP_CONTENT: false,
|
||||||
|
// Additional security measures
|
||||||
|
ADD_ATTR: [],
|
||||||
|
// Block data URIs that could contain malicious content
|
||||||
|
ALLOW_DATA_ATTR: false,
|
||||||
|
// Custom hook to further sanitize content
|
||||||
|
HOOKS: {
|
||||||
|
uponSanitizeElement: function(node, data) {
|
||||||
|
// Block any remaining dangerous elements
|
||||||
|
const dangerousTags = ['svg', 'style', 'script', 'link', 'meta', 'iframe', 'object', 'embed', 'applet'];
|
||||||
|
if (node.tagName && dangerousTags.includes(node.tagName.toLowerCase())) {
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.warn('Blocked potentially dangerous element:', node.tagName);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block img tags with SVG data URIs
|
||||||
|
if (node.tagName && node.tagName.toLowerCase() === 'img') {
|
||||||
|
const src = node.getAttribute('src');
|
||||||
|
if (src && (src.startsWith('data:image/svg') || src.endsWith('.svg'))) {
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.warn('Blocked potentially malicious SVG image:', src);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block elements with dangerous attributes
|
||||||
|
const dangerousAttrs = ['style', 'onload', 'onerror', 'onclick', 'onmouseover', 'onfocus', 'onblur'];
|
||||||
|
for (const attr of dangerousAttrs) {
|
||||||
|
if (node.hasAttribute && node.hasAttribute(attr)) {
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.warn('Blocked element with dangerous attribute:', node.tagName, attr);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
uponSanitizeAttribute: function(node, data) {
|
||||||
|
// Block style attributes completely
|
||||||
|
if (data.attrName === 'style') {
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.warn('Blocked style attribute');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block class and id attributes that might be used for CSS injection
|
||||||
|
if (data.attrName === 'class' || data.attrName === 'id') {
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.warn('Blocked class/id attribute:', data.attrName, data.attrValue);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block data attributes
|
||||||
|
if (data.attrName && data.attrName.startsWith('data-')) {
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.warn('Blocked data attribute:', data.attrName);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience function for secure sanitization
|
||||||
|
export function sanitizeHTML(html) {
|
||||||
|
return DOMPurify.sanitize(html, getSecureDOMPurifyConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience function for sanitizing text (no HTML)
|
||||||
|
export function sanitizeText(text) {
|
||||||
|
return DOMPurify.sanitize(text, {
|
||||||
|
ALLOWED_TAGS: [],
|
||||||
|
ALLOWED_ATTR: [],
|
||||||
|
KEEP_CONTENT: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { getSecureDOMPurifyConfig } from '/imports/lib/secureDOMPurify';
|
import { getSecureDOMPurifyConfig } from './secureDOMPurify';
|
||||||
|
|
||||||
var Markdown = require('markdown-it')({
|
var Markdown = require('markdown-it')({
|
||||||
html: true,
|
html: true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue