chore: remediate pygienium audit findings

Dead code: 77 verified-unused exports, files (BackArrow, MenuBars,
cookies.ts, db/create.ts, schemas/comment.ts, security-headers.ts) and
12 unused dependencies removed
Comments: ~370 RESTATE comments stripped across 53 files; 2 verbose
blocks tightened; dead commented-out config removed
Complexity: bulkUpsert extracted into 11 per-entity helpers (CCN 156->~10);
login formHandler split into 3 submitters (CCN 63->~5); account page render
split into 8 section components (CCN 40); updatePost SQL builder rebuilt;
assert*Owned consolidated behind generic assertOwnedBy
Defensive guards: 4 redundant rethrow/nullish guards removed
This commit is contained in:
2026-08-11 13:36:18 -04:00
parent 33ca9213f2
commit 898c891bd5
76 changed files with 1437 additions and 2600 deletions

View File

@@ -460,7 +460,7 @@ function MainRightBarContent() {
);
}
export function RightBarContent() {
function RightBarContent() {
const site = useSite();
return (
<Show when={site().id === "main"} fallback={<SubdomainRightBarContent />}>

View File

@@ -144,7 +144,6 @@ const sendContactEmail = action(async (formData: FormData) => {
const { env } = await import("~/env/server");
// Verify Cloudflare Turnstile token
const turnstileValid = await verifyTurnstileToken(
turnstileToken,
env.TURNSTILE_SECRET_KEY,
@@ -252,7 +251,6 @@ export function ContactForm(props: ContactFormProps) {
const [searchParams] = useSearchParams();
// Load server data using createAsync
const contactData = createAsync(() => getContactData(), {
deferStream: true
});
@@ -350,7 +348,6 @@ export function ContactForm(props: ContactFormProps) {
const message = formData.get("message") as string;
if (name && email && message) {
// Get fresh Turnstile token
let currentToken = turnstileToken();
if (
!currentToken &&
@@ -388,7 +385,6 @@ export function ContactForm(props: ContactFormProps) {
setError("");
form.reset();
// Reset Turnstile widget
if (typeof window !== "undefined" && (window as any).turnstile) {
const widgetEl = document.getElementById("turnstile-widget-1");
if (widgetEl) {

View File

@@ -138,9 +138,6 @@ export function Typewriter(props: {
entries.forEach((entry) => {
// If component leaves viewport while animating, we could pause
// For now, we just ensure it starts when visible
if (!entry.isIntersecting && cleanupAnimation) {
// Component is off-screen - could add pause logic here if needed
}
});
},
{

View File

@@ -63,7 +63,6 @@ export default function AddAttachmentSection(props: AddAttachmentSectionProps) {
};
reader.readAsDataURL(file);
// Refresh the S3 file list
await loadAttachments();
}
} catch (err) {
@@ -81,7 +80,6 @@ export default function AddAttachmentSection(props: AddAttachmentSectionProps) {
body: JSON.stringify({ key })
});
// Refresh the S3 file list
await loadAttachments();
} catch (err) {
console.error("Failed to delete file:", err);

View File

@@ -259,7 +259,6 @@ export default function CommentSectionWrapper(
const newComment = async (commentBody: string, parentCommentID?: number) => {
setCommentSubmitLoading(true);
// Clear any existing timeout
if (commentSubmitTimeoutId) {
clearTimeout(commentSubmitTimeoutId);
}
@@ -428,7 +427,6 @@ export default function CommentSectionWrapper(
const editComment = async (body: string, comment_id: number) => {
setCommentEditLoading(true);
// Clear any existing timeout
if (editCommentTimeoutId) {
clearTimeout(editCommentTimeoutId);
}
@@ -527,7 +525,6 @@ export default function CommentSectionWrapper(
setCommentDeletionLoading(true);
// Clear any existing timeout
if (deleteCommentTimeoutId) {
clearTimeout(deleteCommentTimeoutId);
}
@@ -623,7 +620,6 @@ export default function CommentSectionWrapper(
setOperationError("");
if (data.commentBody) {
// Soft delete (replace body with deletion message)
setAllComments((prev) =>
prev.map((comment) => {
if (comment.id === data.commentID) {
@@ -652,7 +648,6 @@ export default function CommentSectionWrapper(
})
);
} else {
// Hard delete (remove from list)
setAllComments((prev) =>
prev.filter((comment) => comment.id !== data.commentID)
);
@@ -667,7 +662,6 @@ export default function CommentSectionWrapper(
}, 300);
};
// Deletion/edit prompt toggle
const toggleModification = (
commentID: number,
commenterID: string,
@@ -708,7 +702,6 @@ export default function CommentSectionWrapper(
setCommentBodyForModification("");
};
// Reaction handling
const commentReaction = (reactionType: ReactionType, commentID: number) => {
if (!props.currentUserID) {
console.warn("Cannot react to comment: user not authenticated");
@@ -800,7 +793,6 @@ export default function CommentSectionWrapper(
}
};
// Click outside handlers (SolidJS version)
createEffect(() => {
const handleClickOutsideDelete = (e: MouseEvent) => {
if (

View File

@@ -8,12 +8,10 @@ function sanitizeMermaidSvg(svgString: string): string {
const parser = new DOMParser();
const doc = parser.parseFromString(svgString, "text/html");
// Remove dangerous elements
doc.querySelectorAll("script, iframe, object, embed, form, link, meta, base").forEach((el) => {
el.remove();
});
// Remove event handlers and dangerous attributes from all elements
doc.querySelectorAll("[on*], [href*='javascript:'], [style*='expression(']").forEach((el) => {
const attrs = Array.from(el.attributes);
attrs.forEach((attr) => {

View File

@@ -11,7 +11,6 @@ function sanitizeHtml(html: string): string {
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
// Remove dangerous elements
doc
.querySelectorAll(
"script, iframe, object, embed, form, link, meta, base, svg script"
@@ -131,7 +130,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
const processVideos = () => {
if (!contentRef) return;
// Handle direct video elements
const videoElements = contentRef.querySelectorAll("video");
videoElements.forEach((video) => {
@@ -139,18 +137,14 @@ export default function PostBodyClient(props: PostBodyClientProps) {
video.setAttribute("playsinline", "");
video.setAttribute("controls", "");
// Remove download attribute if present
video.removeAttribute("download");
// Ensure proper MIME types on source elements
const sources = video.querySelectorAll("source");
sources.forEach((source) => {
const src = source.getAttribute("src");
if (src) {
// Remove download attribute from sources
source.removeAttribute("download");
// Set correct type attribute if missing
if (!source.hasAttribute("type")) {
if (src.endsWith(".mp4")) {
source.setAttribute("type", "video/mp4");
@@ -163,7 +157,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
}
});
// If video has direct src attribute, ensure type is set
const videoSrc = video.getAttribute("src");
if (videoSrc && !video.hasAttribute("type")) {
if (videoSrc.endsWith(".mp4")) {
@@ -176,7 +169,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
}
});
// Handle iframes with video sources - replace with proper video tags
const iframes = contentRef.querySelectorAll("iframe");
iframes.forEach((iframe) => {
const src = iframe.getAttribute("src");
@@ -187,7 +179,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
src.endsWith(".webm") ||
src.endsWith(".ogg"))
) {
// Create a proper video element
const video = document.createElement("video");
video.setAttribute("controls", "");
video.setAttribute("playsinline", "");
@@ -195,7 +186,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
video.style.maxWidth = "100%";
video.style.height = "auto";
// Set appropriate type based on file extension
let videoType = "video/mp4";
if (src.endsWith(".mov")) {
videoType = "video/mp4"; // MOV files are typically H.264 which plays as mp4
@@ -208,7 +198,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
video.setAttribute("type", videoType);
video.src = src;
// Replace the iframe with the video element
const parent = iframe.parentElement;
if (parent) {
parent.replaceChild(video, iframe);
@@ -216,7 +205,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
}
});
// Also check for any anchor tags wrapping videos that might have download attribute
const videoLinks = contentRef.querySelectorAll("a");
videoLinks.forEach((link) => {
const hasVideo = link.querySelector("video");

View File

@@ -58,7 +58,6 @@ export default function PostForm(props: PostFormProps) {
props.postId
);
// Mark initial load as complete after data is loaded (for edit mode)
// Use setTimeout to ensure this runs after all signals are initialized
createEffect(() => {
if (props.mode === "edit" && props.initialData) {
@@ -73,12 +72,10 @@ export default function PostForm(props: PostFormProps) {
}, 5000);
};
// Helper to ensure post exists (create if needed)
const ensurePostExists = async (): Promise<number> => {
const existingId = createdPostId() || props.postId;
if (existingId) return existingId;
// Create minimal post if it doesn't exist yet
const result = await api.database.createPost.mutate({
category: "blog",
title: title().replaceAll(" ", "_") || "Untitled",
@@ -95,7 +92,6 @@ export default function PostForm(props: PostFormProps) {
return newId;
};
// Individual autosave functions for each field
const autoSaveTitle = async () => {
const currentTitle = title();
if (!currentTitle || currentTitle === props.initialData?.title) return;
@@ -248,7 +244,6 @@ export default function PostForm(props: PostFormProps) {
}
};
// Debounced versions
const debouncedAutoSaveTitle = debounce(autoSaveTitle, 2500);
const debouncedAutoSaveSubtitle = debounce(autoSaveSubtitle, 2500);
const debouncedAutoSaveBody = debounce(autoSaveBody, 2500);
@@ -256,7 +251,6 @@ export default function PostForm(props: PostFormProps) {
const debouncedAutoSavePublished = debounce(autoSavePublished, 1000);
const debouncedAutoSaveBanner = debounce(autoSaveBanner, 2500);
// Individual effects for each field
createEffect(() => {
const titleVal = title();
if (isInitialLoad()) return;
@@ -405,7 +399,6 @@ export default function PostForm(props: PostFormProps) {
author_id: props.userID
});
} else {
// Create new post
const result = await api.database.createPost.mutate({
category: "blog",
title: title().replaceAll(" ", "_"),

View File

@@ -100,7 +100,7 @@ export default function PostSorting(props: PostSortingProps) {
case "newest":
break; // Posts already come newest first from DB (DESC order)
case "oldest":
sorted.reverse(); // Reverse to get oldest first
sorted.reverse();
break;
case "most_liked":
sorted.sort((a, b) => (b.total_likes || 0) - (a.total_likes || 0));

View File

@@ -1547,7 +1547,6 @@ export default function TextEditor(props: TextEditorProps) {
},
handleDOMEvents: {
touchstart: (view, event) => {
// Only handle touch events on mobile in fullscreen with active suggestion
if (
!hasSuggestion() ||
!isFullscreen() ||
@@ -1562,7 +1561,6 @@ export default function TextEditor(props: TextEditorProps) {
return false;
},
touchend: (view, event) => {
// Only handle touch events on mobile in fullscreen with active suggestion
if (
!hasSuggestion() ||
!isFullscreen() ||
@@ -1860,7 +1858,6 @@ export default function TextEditor(props: TextEditorProps) {
const node = allSuperscriptNodes[i];
const text = node.text;
// Check if this is a complete reference (with optional whitespace)
const completeMatch = text.match(/^\s*\[(\d+)\]\s*$/);
if (completeMatch) {
const hasOtherMarks = node.marks.some(
@@ -1877,7 +1874,6 @@ export default function TextEditor(props: TextEditorProps) {
continue;
}
// Check if this might be the start of a split reference
if (text === "[" && i + 2 < allSuperscriptNodes.length) {
const nextNode = allSuperscriptNodes[i + 1];
const afterNode = allSuperscriptNodes[i + 2];
@@ -1958,7 +1954,6 @@ export default function TextEditor(props: TextEditorProps) {
allRefs.sort((a, b) => a.pos - b.pos);
// Check if renumbering is needed (if any ref doesn't match its expected number)
let needsRenumbering = false;
for (let i = 0; i < allRefs.length; i++) {
if (allRefs[i].refNum !== i + 1) {

View File

@@ -69,7 +69,6 @@ export const Mermaid = Node.create({
getAttrs: (element) => {
if (typeof element === "string") return false;
// Skip if already has data-type or data-mermaid-diagram attribute
if (
element.hasAttribute("data-type") ||
element.hasAttribute("data-mermaid-diagram")
@@ -83,7 +82,6 @@ export const Mermaid = Node.create({
const content = code.textContent || "";
const trimmedContent = content.trim();
// Check if this looks like a mermaid diagram
const mermaidKeywords = [
"graph ",
"sequenceDiagram",
@@ -174,12 +172,10 @@ export const Mermaid = Node.create({
code.textContent = node.attrs.content || "";
pre.appendChild(code);
// Validation status indicator
const statusIndicator = document.createElement("div");
statusIndicator.className =
"absolute top-2 left-2 w-3 h-3 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-200";
// Validate syntax asynchronously
const validateSyntax = async () => {
const content = node.attrs.content || "";
if (!content.trim()) {
@@ -250,7 +246,6 @@ export const Mermaid = Node.create({
(p: any) => p.spec?.key === "mermaidSelection"
);
// Use intersection observer to trigger update when visible
let updateInterval: ReturnType<typeof setInterval> | null = null;
const observer = new IntersectionObserver(
(entries) => {

View File

@@ -1,29 +0,0 @@
const BackArrow = (props: {
height: number;
width: number;
stroke: string;
strokeWidth: number;
class?: string;
}) => {
return (
<div class={props.class}>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={props.strokeWidth}
stroke={props.stroke}
height={props.height}
width={props.width}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
/>
</svg>
</div>
);
};
export default BackArrow;

View File

@@ -1,39 +0,0 @@
function MenuBars() {
return (
<svg
width="36"
height="30"
viewBox="0 0 120 100"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g id="Mask group">
<g id="Frame 1">
<rect width="120" height="100" />
<line
id="LineA"
x1="11.5"
y1="31.5"
x2="108.5"
y2="31.5"
strokeWidth="6"
strokeLinecap="round"
class="stroke-black dark:stroke-white"
/>
<line
id="LineB"
x1="11.5"
y1="64.5"
x2="108.5"
y2="64.5"
strokeWidth="6"
strokeLinecap="round"
class="stroke-black dark:stroke-white"
/>
</g>
</g>
</svg>
);
}
export default MenuBars;

View File

@@ -128,7 +128,6 @@ export default function Button(props: ButtonProps) {
height: number;
} | null>(null);
// Measure content dimensions when not loading
createEffect(() => {
if (!local.loading && contentRef) {
const rect = contentRef.getBoundingClientRect();