Fix SSRF, XSS, dedup, force-post, and DB hot-path issues
- Add internal/safehttp: hardened HTTP client (DNS-resolved dial guard blocking loopback/RFC1918/CGNAT/link-local, redirect re-validation, body-size cap) and rewire article/feed/thumb clients through it - Cap goquery body at 5 MiB so a hostile origin can't OOM the process - search.js: reject non-http(s) hrefs to block stored XSS via javascript: - dedup: tracking-param key "CMP" was unreachable (lookup lowercases); fixed to "cmp" so CMP= is actually stripped from canonical URLs - ForcePost: postItem now returns bool; on dedup-skip ForcePost returns false so !post falls back to DB lookup instead of silently consuming - Bound reaction callbacks behind an 8-slot semaphore; drop overflow - Add stories indexes on (channel, classified, seen_at DESC), (classified, seen_at DESC), and partial image_url to kill full scans in IsKnownImageURL and ORDER BY seen_at hot paths - Surface FTS5 probe Scan error instead of swallowing it
This commit is contained in:
@@ -40,6 +40,15 @@
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
// safeURL rejects any href whose scheme isn't http(s). Stored article_url
|
||||
// comes from feeds we don't control; a hostile feed publishing
|
||||
// `javascript:fetch(...)` would otherwise execute on click.
|
||||
function safeURL(s) {
|
||||
const raw = String(s == null ? "" : s).trim();
|
||||
if (/^https?:\/\//i.test(raw)) return raw;
|
||||
return "";
|
||||
}
|
||||
|
||||
function render(results) {
|
||||
items = results || [];
|
||||
activeIndex = items.length > 0 ? 0 : -1;
|
||||
@@ -55,8 +64,10 @@
|
||||
</div>`
|
||||
: `<div class="hidden sm:flex w-20 h-20 shrink-0 items-center justify-center rounded-2xl bg-[color:var(--ink)]/5 text-2xl">${escapeHTML(r.channel_emoji || "·")}</div>`;
|
||||
const postedRing = r.posted ? ` border-theme-${escapeHTML(r.channel_theme)}` : "";
|
||||
const href = safeURL(r.article_url);
|
||||
if (!href) return "";
|
||||
return `
|
||||
<a href="${escapeHTML(r.article_url)}" target="_blank" rel="noopener noreferrer"
|
||||
<a href="${escapeHTML(href)}" target="_blank" rel="noopener noreferrer"
|
||||
data-idx="${i}"
|
||||
class="search-result group flex items-start gap-4 rounded-2xl p-3 transition border-2 border-transparent hover:bg-[color:var(--ink)]/5${postedRing}">
|
||||
${thumb}
|
||||
@@ -138,7 +149,8 @@
|
||||
} else if (e.key === "Enter") {
|
||||
if (activeIndex >= 0 && items[activeIndex]) {
|
||||
e.preventDefault();
|
||||
window.open(items[activeIndex].article_url, "_blank", "noopener");
|
||||
const href = safeURL(items[activeIndex].article_url);
|
||||
if (href) window.open(href, "_blank", "noopener");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user