ui: fix xss whitelist for katex

This commit is contained in:
undefined committed 2022-02-09 17:15:58 +08:00
1 parent 83d4f9fd5f
commit 27ab64902b
3 files changed
+38 -31

No files matched your search

@@ -1,7 +1,35 @@
import * as Xss from 'xss';
const stack = [];
let isFull = false;
const tagCheck = new Xss.FilterXSS({
css: false,
whiteList: {},
onIgnoreTag(tag, html, options) {
if (html.endsWith('/>')) return html;
if (!options.isClosing) {
stack.push(tag);
return html;
}
if (stack.length === 0) {
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;'); // 没有标签可供闭合
}
if (stack[stack.length - 1] === tag) {
stack.pop(); // 正常关闭
return html;
}
if (stack.length - 2 >= 0 && stack[stack.length - 2] === tag) {
// 可能丢失了一个结束标签
stack.pop();
stack.pop();
return html;
}
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;'); // 可能多出了一个结束标签
},
onIgnoreTagAttr(tag, name, value) {
return value;
},
});
const xss = new Xss.FilterXSS({
whiteList: {
@@ -76,34 +104,13 @@ const xss = new Xss.FilterXSS({
if (name === 'class') return value.replace(/badge/g, 'xss-badge');
return value;
},
onTag(tag, html, options) {
if (!options.isWhite || !isFull) return null;
if (!options.isClosing) {
stack.push(tag);
return null;
}
if (stack.length === 0) return `&lt;/${tag}&gt;`; // 没有标签可供闭合
if (stack[stack.length - 1] === tag) {
stack.pop(); // 正常关闭
return null;
}
if (stack.length - 2 >= 0 && stack[stack.length - 2] === tag) {
// 可能丢失了一个结束标签
stack.pop();
stack.pop();
return null;
}
return `&lt;/${tag}&gt;`; // 可能多出了一个结束标签
},
});
xss.process = ((original) => (html: string, full: boolean = false) => {
function ensureTag(html: string) {
stack.length = 0;
isFull = full;
const res = original(html);
if (!full) return res;
const res = tagCheck.process(html);
return res + stack.map((i) => `</${i}>`).join('');
})(xss.process.bind(xss));
}
function xssProtector(md) {
function protector(state) {
@@ -126,4 +133,4 @@ function xssProtector(md) {
md.core.ruler.after('linkify', 'xss', protector);
}
module.exports = { xss, xssProtector };
module.exports = { xss, ensureTag, xssProtector };
+4 -4
View File
@@ -9,7 +9,7 @@ const argv = require('cac')().parse();
const { findFileSync } = require('@hydrooj/utils/lib/utils');
const status = require('@hydrooj/utils/lib/status');
const markdown = require('./markdown');
const { xss } = require('./markdown-it-xss');
const { xss, ensureTag } = require('./markdown-it-xss');
const { misc, buildContent, avatar } = global.Hydro.lib;
@@ -63,8 +63,8 @@ class Nunjucks extends nunjucks.Environment {
this.addFilter('dumpYaml', (self) => yaml.dump(self));
this.addFilter('serialize', (self, ignoreFunction = true) => serialize(self, { ignoreFunction }));
this.addFilter('assign', (self, data) => Object.assign(self, data));
this.addFilter('markdown', (self, html = false) => xss.process(markdown.render(self, html), true));
this.addFilter('markdownInline', (self, html = false) => xss.process(markdown.renderInline(self, html), true));
this.addFilter('markdown', (self, html = false) => ensureTag(markdown.render(self, html), true));
this.addFilter('markdownInline', (self, html = false) => ensureTag(markdown.renderInline(self, html), true));
this.addFilter('ansi', (self) => misc.ansiToHtml(self));
this.addFilter('base64_encode', (s) => Buffer.from(s).toString('base64'));
this.addFilter('base64_decode', (s) => Buffer.from(s, 'base64').toString());
@@ -87,7 +87,7 @@ class Nunjucks extends nunjucks.Environment {
else s = s[langs[0]];
}
if (s instanceof Array) s = buildContent(s, html ? 'html' : 'markdown', (str) => str.translate(language));
return xss.process(html ? s : markdown.render(s), true);
return ensureTag(html ? xss.process(s) : markdown.render(s), true);
});
this.addFilter('log', (self) => {
console.log(self);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/ui-default",
"version": "4.31.10",
"version": "4.31.11",
"author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0",
"main": "hydro.js",