使用openai进行了代码review
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
const ALLOWED_TAGS = new Set([
|
||||
"a",
|
||||
"b",
|
||||
"blockquote",
|
||||
"br",
|
||||
"code",
|
||||
"del",
|
||||
"em",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"hr",
|
||||
"i",
|
||||
"li",
|
||||
"ol",
|
||||
"p",
|
||||
"pre",
|
||||
"span",
|
||||
"strong",
|
||||
"table",
|
||||
"tbody",
|
||||
"td",
|
||||
"th",
|
||||
"thead",
|
||||
"tr",
|
||||
"ul",
|
||||
]);
|
||||
|
||||
const DROP_CONTENT_TAGS = new Set(["script", "style", "iframe", "object", "embed", "link", "meta"]);
|
||||
|
||||
const GLOBAL_ALLOWED_ATTRS = new Set(["class"]);
|
||||
const TAG_ALLOWED_ATTRS: Record<string, Set<string>> = {
|
||||
a: new Set(["href", "title", "target", "rel"]),
|
||||
code: new Set(["class"]),
|
||||
span: new Set(["class"]),
|
||||
};
|
||||
|
||||
function isSafeUrl(value: string): boolean {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (!normalized) return false;
|
||||
|
||||
if (
|
||||
normalized.startsWith("http://") ||
|
||||
normalized.startsWith("https://") ||
|
||||
normalized.startsWith("mailto:") ||
|
||||
normalized.startsWith("tel:") ||
|
||||
normalized.startsWith("/") ||
|
||||
normalized.startsWith("#")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function sanitizeAttributes(element: Element): void {
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
const allowedForTag = TAG_ALLOWED_ATTRS[tagName] || new Set<string>();
|
||||
|
||||
const attrs = Array.from(element.attributes);
|
||||
for (const attr of attrs) {
|
||||
const name = attr.name.toLowerCase();
|
||||
const value = attr.value;
|
||||
|
||||
const allowed = GLOBAL_ALLOWED_ATTRS.has(name) || allowedForTag.has(name);
|
||||
if (!allowed) {
|
||||
element.removeAttribute(attr.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((name === "href" || name === "src") && !isSafeUrl(value)) {
|
||||
element.removeAttribute(attr.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (tagName === "a") {
|
||||
if (element.getAttribute("target") === "_blank") {
|
||||
element.setAttribute("rel", "noopener noreferrer nofollow");
|
||||
} else {
|
||||
element.removeAttribute("target");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeNode(node: Node): void {
|
||||
if (node.nodeType === Node.TEXT_NODE) return;
|
||||
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) {
|
||||
node.parentNode?.removeChild(node);
|
||||
return;
|
||||
}
|
||||
|
||||
const element = node as Element;
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
|
||||
if (!ALLOWED_TAGS.has(tagName)) {
|
||||
if (DROP_CONTENT_TAGS.has(tagName)) {
|
||||
element.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = element.parentNode;
|
||||
if (!parent) return;
|
||||
while (element.firstChild) {
|
||||
parent.insertBefore(element.firstChild, element);
|
||||
}
|
||||
parent.removeChild(element);
|
||||
return;
|
||||
}
|
||||
|
||||
sanitizeAttributes(element);
|
||||
const children = Array.from(element.childNodes);
|
||||
for (const child of children) {
|
||||
sanitizeNode(child);
|
||||
}
|
||||
}
|
||||
|
||||
export function sanitizeHtml(input: string): string {
|
||||
if (typeof window === "undefined") return input;
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(input, "text/html");
|
||||
const nodes = Array.from(doc.body.childNodes);
|
||||
for (const node of nodes) {
|
||||
sanitizeNode(node);
|
||||
}
|
||||
return doc.body.innerHTML;
|
||||
}
|
||||
Reference in New Issue
Block a user