fix security 1

This commit is contained in:
Mateusz Gruszczyński
2026-07-26 16:21:58 +02:00
parent a075d06b6d
commit 364af50a7c
9 changed files with 57 additions and 18 deletions
+14 -6
View File
@@ -5,9 +5,17 @@ function escapeHtml(value) {
}
function safeUrl(value) {
const url = String(value).trim();
if (/^(https?:\/\/|mailto:|\/|\.\/|\.\.\/|#)/i.test(url)) return escapeHtml(url);
return "#";
const raw = String(value || "").trim();
if (!raw || raw.startsWith("//")) return "#";
if (raw.startsWith("#")) return escapeHtml(raw);
try {
const url = new URL(raw, location.origin);
if (url.protocol === "mailto:") return escapeHtml(url.href);
if (url.protocol !== "http:" && url.protocol !== "https:") return "#";
return escapeHtml(url.href);
} catch {
return "#";
}
}
const emoji = EMOJI_SHORTCODES;
@@ -24,11 +32,11 @@ function inline(value) {
html = html.replace(/`([^`]+)`/g, (_, code) => stash(`<code>${code}</code>`));
html = html.replace(/!\[([^\]]*)\]\(([^\s)]+)(?:\s+["']([^"']*)["'])?\)/g, (_, alt, url, title) => {
const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
return stash(`<img src="${safeUrl(url)}" alt="${alt}" loading="lazy" decoding="async" draggable="false" contenteditable="false"${titleAttr}>`);
return stash(`<img src="${safeUrl(url)}" alt="${alt}" loading="lazy" decoding="async" referrerpolicy="no-referrer" draggable="false" contenteditable="false"${titleAttr}>`);
});
html = html.replace(/\[([^\]]+)\]\(([^\s)]+)(?:\s+["']([^"']*)["'])?\)/g, (_, label, url, title) => {
const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
return stash(`<a href="${safeUrl(url)}" target="_blank" rel="noopener noreferrer"${titleAttr}>${label}</a>`);
return stash(`<a href="${safeUrl(url)}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer"${titleAttr}>${label}</a>`);
});
html = html.replace(/\[\^([^\]\s]+)\]/g, (_, id) => stash(`<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">?</a></sup>`));
@@ -44,7 +52,7 @@ function inline(value) {
html = html.replace(/(^|[\s(])((?:https?:\/\/|mailto:)[^\s<]+)/gi, (match, prefix, url) => {
const clean = url.replace(/[.,!?;:]+$/, "");
const suffix = url.slice(clean.length);
return `${prefix}${stash(`<a href="${safeUrl(clean)}" target="_blank" rel="noopener noreferrer">${clean}</a>`)}${suffix}`;
return `${prefix}${stash(`<a href="${safeUrl(clean)}" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">${clean}</a>`)}${suffix}`;
});
return html.replace(/\u0000T(\d+)\u0000/g, (_, index) => tokens[Number(index)] || "");