diff --git a/apps/frontend/src/middleware/auth.ts b/apps/frontend/src/middleware/auth.ts index 5cc782611..62d17006a 100644 --- a/apps/frontend/src/middleware/auth.ts +++ b/apps/frontend/src/middleware/auth.ts @@ -20,13 +20,11 @@ export default defineNuxtRouteMiddleware(async (_to, from) => { } } - const redirect = encodeURIComponent(url.pathname + url.search); - return await navigateTo( { path: "/auth/sign-in", query: { - redirect, + redirect: `${url.pathname}${url.search}`, ...extractedParams, }, }, diff --git a/packages/utils/parse.ts b/packages/utils/parse.ts index 13c06945f..aa53d64ef 100644 --- a/packages/utils/parse.ts +++ b/packages/utils/parse.ts @@ -1,9 +1,9 @@ import MarkdownIt from 'markdown-it' -import xss from 'xss' +import { escapeAttrValue, FilterXSS, safeAttrValue, whiteList } from 'xss' -export const configuredXss = new xss.FilterXSS({ +export const configuredXss = new FilterXSS({ whiteList: { - ...xss.whiteList, + ...whiteList, summary: [], h1: ['id'], h2: ['id'], @@ -14,12 +14,12 @@ export const configuredXss = new xss.FilterXSS({ kbd: ['id'], input: ['checked', 'disabled', 'type'], iframe: ['width', 'height', 'allowfullscreen', 'frameborder', 'start', 'end'], - img: [...xss.whiteList.img, 'usemap', 'style'], + img: [...(whiteList.img || []), 'usemap', 'style'], map: ['name'], - area: [...xss.whiteList.a, 'coords'], - a: [...xss.whiteList.a, 'rel'], - td: [...xss.whiteList.td, 'style'], - th: [...xss.whiteList.th, 'style'], + area: [...(whiteList.a || []), 'coords'], + a: [...(whiteList.a || []), 'rel'], + td: [...(whiteList.td || []), 'style'], + th: [...(whiteList.th || []), 'style'], picture: [], source: ['media', 'sizes', 'src', 'srcset', 'type'], }, @@ -35,35 +35,43 @@ export const configuredXss = new xss.FilterXSS({ if (tag === 'iframe' && name === 'src') { const allowedSources = [ { - regex: - /^https?:\/\/(www\.)?youtube(-nocookie)?\.com\/embed\/[a-zA-Z0-9_-]{11}(\?&autoplay=[0-1]{1})?$/, - remove: ['&autoplay=1'], // Prevents autoplay + url: /^https?:\/\/(www\.)?youtube(-nocookie)?\.com\/embed\/[a-zA-Z0-9_-]{11}/, + allowedParameters: [/start=\d+/, /end=\d+/], }, { - regex: /^https?:\/\/(www\.)?discord\.com\/widget\?id=\d{18,19}(&theme=\w+)?$/, - remove: [/&theme=\w+/], + url: /^https?:\/\/(www\.)?discord\.com\/widget/, + allowedParameters: [/id=\d{18,19}/], }, ] + const url = new URL(value) + for (const source of allowedSources) { - if (source.regex.test(value)) { - for (const remove of source.remove) { - value = value.replace(remove, '') - } - return `${name}="${xss.escapeAttrValue(value)}"` + if (!source.url.test(url.href)) { + continue } + + const newSearchParams = new URLSearchParams() + url.searchParams.forEach((value, key) => { + if (!source.allowedParameters.some((param) => param.test(`${key}=${value}`))) { + newSearchParams.delete(key) + } + }) + + url.search = newSearchParams.toString() + return `${name}="${escapeAttrValue(url.toString())}"` } } // For Highlight.JS if (name === 'class' && ['pre', 'code', 'span'].includes(tag)) { - const allowedClasses = [] + const allowedClasses: string[] = [] for (const className of value.split(/\s/g)) { if (className.startsWith('hljs-') || className.startsWith('language-')) { allowedClasses.push(className) } } - return `${name}="${xss.escapeAttrValue(allowedClasses.join(' '))}"` + return `${name}="${escapeAttrValue(allowedClasses.join(' '))}"` } }, safeAttrValue(tag, name, value, cssFilter) { @@ -92,7 +100,7 @@ export const configuredXss = new xss.FilterXSS({ ] if (!allowedHostnames.includes(url.hostname)) { - return xss.safeAttrValue( + return safeAttrValue( tag, name, `https://wsrv.nl/?url=${encodeURIComponent( @@ -101,13 +109,13 @@ export const configuredXss = new xss.FilterXSS({ cssFilter, ) } - return xss.safeAttrValue(tag, name, url.toString(), cssFilter) + return safeAttrValue(tag, name, url.toString(), cssFilter) } catch (err) { /* empty */ } } - return xss.safeAttrValue(tag, name, value, cssFilter) + return safeAttrValue(tag, name, value, cssFilter) }, }) @@ -129,7 +137,7 @@ export const md = (options = {}) => { const token = tokens[idx] const index = token.attrIndex('href') - if (index !== -1) { + if (token.attrs && index !== -1) { const href = token.attrs[index][1] try { @@ -152,4 +160,4 @@ export const md = (options = {}) => { return md } -export const renderString = (string) => configuredXss.process(md().render(string)) +export const renderString = (string: string) => configuredXss.process(md().render(string))