diff --git a/apps/client-standalone/src/test_setup.ts b/apps/client-standalone/src/test_setup.ts index 4bae132f62..08f26d0278 100644 --- a/apps/client-standalone/src/test_setup.ts +++ b/apps/client-standalone/src/test_setup.ts @@ -14,6 +14,7 @@ import BrowserCryptoProvider from "./lightweight/crypto_provider.js"; import StandalonePlatformProvider from "./lightweight/platform_provider.js"; import BrowserSqlProvider from "./lightweight/sql_provider.js"; import BrowserZipProvider from "./lightweight/zip_provider.js"; +import { standaloneImageProvider } from "./services/image_provider.js"; // ============================================================================= // SQLite WASM compatibility shims @@ -131,6 +132,7 @@ beforeAll(async () => { }, platform: new StandalonePlatformProvider(""), backup: new StandaloneBackupService(options), + image: standaloneImageProvider, schema: schemaSql, dbConfig: { provider: sqlProvider, diff --git a/apps/client-standalone/vite.config.mts b/apps/client-standalone/vite.config.mts index 40d5def66b..73b1ff09c6 100644 --- a/apps/client-standalone/vite.config.mts +++ b/apps/client-standalone/vite.config.mts @@ -1,5 +1,5 @@ import fs from "fs"; -import { join } from "path"; +import { join, resolve, sep } from "path"; import prefresh from "@prefresh/vite"; import { defineConfig, type Plugin } from "vite"; @@ -45,8 +45,11 @@ const pdfjsServePlugin = (): Plugin => ({ const relativePath = urlWithoutQuery.replace(/^\/pdfjs\//, ""); const filePath = join(pdfjsRoot, relativePath); - // Security: ensure we're still within pdfjsRoot - if (!filePath.startsWith(pdfjsRoot)) { + // Security: resolve both paths to prevent prefix-collision attacks + // (e.g. pdfjsRoot="/foo/bar" matching "/foo/bar2/evil.js") + const resolvedRoot = resolve(pdfjsRoot); + const resolvedFilePath = resolve(filePath); + if (!resolvedFilePath.startsWith(resolvedRoot + sep)) { return next(); } diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index e1ca1759bf..9045aa1dcb 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -12,6 +12,7 @@ import tray from "@triliumnext/server/src/services/tray.js"; import windowService from "@triliumnext/server/src/services/window.js"; import WebSocketMessagingProvider from "@triliumnext/server/src/services/ws_messaging_provider.js"; import ServerBackupService from "@triliumnext/server/src/backup_provider.js"; +import ServerLogService from "@triliumnext/server/src/log_provider.js"; import BetterSqlite3Provider from "@triliumnext/server/src/sql_provider.js"; import NodejsZipProvider from "@triliumnext/server/src/zip_provider.js"; import { app, BrowserWindow,globalShortcut } from "electron"; @@ -151,6 +152,7 @@ async function main() { // both source and bundled-production modes. getDemoArchive: async () => fs.readFileSync(path.join(RESOURCE_DIR, "db", "demo.zip")), inAppHelp: new NodejsInAppHelpProvider(), + log: new ServerLogService(), backup: new ServerBackupService(options), image: (await import("@triliumnext/server/src/services/image_provider.js")).serverImageProvider, extraAppInfo: { diff --git a/apps/desktop/tsconfig.app.json b/apps/desktop/tsconfig.app.json index ee86e59582..bd3c2fa5f1 100644 --- a/apps/desktop/tsconfig.app.json +++ b/apps/desktop/tsconfig.app.json @@ -27,6 +27,9 @@ }, { "path": "../../packages/commons/tsconfig.lib.json" + }, + { + "path": "../../packages/trilium-core/tsconfig.lib.json" } ] } diff --git a/apps/server/spec/setup.ts b/apps/server/spec/setup.ts index 838fa6f007..e22d00f14b 100644 --- a/apps/server/spec/setup.ts +++ b/apps/server/spec/setup.ts @@ -11,6 +11,8 @@ import ServerPlatformProvider from "../src/platform_provider.js"; import BetterSqlite3Provider from "../src/sql_provider.js"; import NodejsInAppHelpProvider from "../src/in_app_help_provider.js"; import { initializeTranslationsWithParams } from "../src/services/i18n.js"; +import ServerLogService from "../src/log_provider.js"; +import { serverImageProvider } from "../src/services/image_provider.js"; // Initialize environment variables. process.env.TRILIUM_DATA_DIR = join(__dirname, "db"); @@ -44,6 +46,8 @@ beforeAll(async () => { platform: new ServerPlatformProvider(), translations: initializeTranslationsWithParams, inAppHelp: new NodejsInAppHelpProvider(), - backup: new ServerBackupService(options) + backup: new ServerBackupService(options), + log: new ServerLogService(), + image: serverImageProvider }); }); diff --git a/apps/server/src/services/log.ts b/apps/server/src/services/log.ts index 9b276c1353..570da8b7f2 100644 --- a/apps/server/src/services/log.ts +++ b/apps/server/src/services/log.ts @@ -1,21 +1,22 @@ import { getLog } from "@triliumnext/core"; import type { Request, Response } from "express"; -import type ServerLogService from "../log_provider.js"; +import ServerLogService from "../log_provider.js"; -function getServerLog(): ServerLogService { - return getLog() as ServerLogService; +function getServerLog(): ServerLogService | undefined { + const log = getLog(); + return log instanceof ServerLogService ? log : undefined; } function info(message: string | Error) { - getServerLog().info(message); + getLog().info(message); } function error(message: string | Error | unknown) { - getServerLog().error(message); + getLog().error(message); } function request(req: Request, res: Response, timeMs: number, responseLength: number | string = "?") { - getServerLog().request(req, res, timeMs, responseLength); + getServerLog()?.request(req, res, timeMs, responseLength); } export default { diff --git a/apps/server/src/sql_provider.ts b/apps/server/src/sql_provider.ts index a17a23a396..8195e08060 100644 --- a/apps/server/src/sql_provider.ts +++ b/apps/server/src/sql_provider.ts @@ -46,11 +46,6 @@ export default class BetterSqlite3Provider implements DatabaseProvider { this.dbConnection?.backup(destinationFile); } - serialize(): Uint8Array { - // Server uses backup() for file-based backups, serialize() is only needed for standalone (OPFS) - throw new Error("serialize() is not implemented on the server - use backup() instead"); - } - prepare(query: string): Statement { if (!this.dbConnection) throw new Error("DB not open."); // Cast is safe: better-sqlite3 only returns bigint when safeIntegers() is enabled, which we don't use.