Files
Situation-Awareness-Platfor…/src/directives/xss-protect.ts
2025-03-12 18:41:20 +08:00

68 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Directive } from 'vue';
import { watch } from 'vue';
const xssTokenReplace = { // xss repalce rules
'<': '&lt;',
'>': '&gt;'
// '"': '&quot;',
// "'": '&#x27;'
};
function generateReg(replaceRules:Record<string, string>) { // 替换字符查找
const temArr = [];
temArr.push('(');
for (const key in replaceRules) {
temArr.push(key);
temArr.push('|');
}
temArr.length > 1 && temArr.pop();
temArr.push(')');
return new RegExp(temArr.join(''), 'g');
}
const replaceReg = generateReg(xssTokenReplace);
function replaceAll(str:string, replaceRules:Record<string, string>) {
return str.replace(replaceReg, function(match, position, original) { return replaceRules[match]; });
}
const repairMaxLength = function(str:string, maxLength:number) { // 设置最大长度不生效的问题
const len = str.length;
return len > maxLength ? str.slice(0, maxLength) : str;
};
const handlePaste = function(vnode:any, event:any) { // 复制粘贴过滤
const value = event.target.value;
const pos = event.target.selectionStart;
const pasetText = (event.clipboardData || (window as any).clipboardData).getData('text');
const insertStr = replaceAll(pasetText, xssTokenReplace);
let composeStr = value.slice(0, pos) + insertStr + value.slice(pos);
if (vnode.ctx.attrs?.maxlength) composeStr = repairMaxLength(composeStr, vnode.ctx.attrs?.maxlength);
event.target.value = composeStr;
event.target.setSelectionRange(pos + insertStr.length, pos + insertStr.length);// 计算鼠标位置
vnode.ctx.emit('update:modelValue', event.target.value);// 双向绑定更新
event.preventDefault();
};
const handleInput = function(vnode:any, event:any) { // 输入替换
if (replaceReg.test(event.key)) {
const pos = event.target.selectionStart;
const value = event.target.value;
const insertStr = replaceAll(event.key, xssTokenReplace);
let composeStr = value.slice(0, pos) + insertStr + value.slice(pos);
if (vnode.ctx.attrs?.maxlength) composeStr = repairMaxLength(composeStr, vnode.ctx.attrs?.maxlength);
event.target.value = composeStr;
event.target.setSelectionRange(pos + insertStr.length, pos + insertStr.length);// 计算鼠标位置
vnode.ctx.emit('update:modelValue', event.target.value);// 双向绑定更新
event.preventDefault();
}
};
export const xssEscape = (str:string) => replaceAll(str, xssTokenReplace);// 替换渲染中的非法字符如果需要保留v-html的元素渲染请勿使用
let handleMoveInput:any = null;
let handleMovePaste:any = null;
export default {
mounted(el, binding, vnode) {
el.addEventListener('paste', handleMovePaste = handlePaste.bind(null, vnode));
el.addEventListener('keypress', handleMoveInput = handleInput.bind(null, vnode));
},
beforeUnmount(el) {
el.removeEventListener('paste', handleMovePaste);
el.removeEventListener('keypress', handleMoveInput);
}
} as Directive;