aboutsummaryrefslogtreecommitdiffstats
path: root/apps/workers
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2026-02-04 13:44:39 +0000
committerGitHub <noreply@github.com>2026-02-04 13:44:39 +0000
commit93ad2e2001eb7070df50b0ab51dfd3e1ab377629 (patch)
tree26cefb449ec3769d1b19569a8c100d49fc7f8cc1 /apps/workers
parentd9329e89adc6ca579a299d42d115c850fc9305dd (diff)
downloadkarakeep-93ad2e2001eb7070df50b0ab51dfd3e1ab377629.tar.zst
fix(import): sanitize error messages to prevent backend detail leakage (#2455)
The catch block in processOneBookmark was storing raw error strings via String(error) in the resultReason field, which is exposed to users through the getImportSessionResults tRPC route. This could leak internal details like database constraint errors, file paths, stack traces, or connection strings. Replace String(error) with getSafeErrorMessage() that only allows through: - TRPCError client errors (designed to be user-facing) - Known safe validation messages from the import worker - A generic fallback for all other errors The full error is still logged server-side for debugging. https://claude.ai/code/session_01F1NHE9dqio5LJ177vmSCvt Co-authored-by: Claude <noreply@anthropic.com>
Diffstat (limited to 'apps/workers')
-rw-r--r--apps/workers/workers/importWorker.ts27
1 files changed, 26 insertions, 1 deletions
diff --git a/apps/workers/workers/importWorker.ts b/apps/workers/workers/importWorker.ts
index 2acbdcc1..047a8e39 100644
--- a/apps/workers/workers/importWorker.ts
+++ b/apps/workers/workers/importWorker.ts
@@ -10,6 +10,7 @@ import {
or,
} from "drizzle-orm";
import { Counter, Gauge, Histogram } from "prom-client";
+import { TRPCError } from "@trpc/server";
import { buildImpersonatingTRPCClient } from "trpc";
import { db } from "@karakeep/db";
@@ -71,6 +72,30 @@ function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
+/**
+ * Extract a safe, user-facing error message from an error.
+ * Avoids leaking internal details like database errors, stack traces, or file paths.
+ */
+function getSafeErrorMessage(error: unknown): string {
+ // TRPCError client errors are designed to be user-facing
+ if (error instanceof TRPCError && error.code !== "INTERNAL_SERVER_ERROR") {
+ return error.message;
+ }
+
+ // Known safe validation errors thrown within the import worker
+ if (error instanceof Error) {
+ const safeMessages = [
+ "URL is required for link bookmarks",
+ "Content is required for text bookmarks",
+ ];
+ if (safeMessages.includes(error.message)) {
+ return error.message;
+ }
+ }
+
+ return "An unexpected error occurred while processing the bookmark";
+}
+
export class ImportWorker {
private running = false;
private pollIntervalMs = 5000;
@@ -441,7 +466,7 @@ export class ImportWorker {
.set({
status: "failed",
result: "rejected",
- resultReason: String(error),
+ resultReason: getSafeErrorMessage(error),
completedAt: new Date(),
})
.where(eq(importStagingBookmarks.id, staged.id));