diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts index 211cf997..5388c363 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts @@ -30,12 +30,13 @@ import { emitEvent, getPermission, getRights, - uploadFile, + uploadFile, Config, getUrlSignature, } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import multer from "multer"; import { handleMessage, postHandleMessage, route } from "../../../../../util"; +import { URL } from "url"; const router = Router(); // TODO: message content/embed string length limit @@ -244,7 +245,7 @@ router.put( console.error("[Message] post-message handler failed", e), ); - return res.json(message); + return res.json(message.withSignedAttachments(req)); }, ); diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts index 8cc444e9..cc3f6830 100644 --- a/src/cdn/Server.ts +++ b/src/cdn/Server.ts @@ -17,14 +17,20 @@ */ import { Server, ServerOptions } from "lambert-server"; -import { Config, initDatabase, registerRoutes, Sentry } from "@spacebar/util"; +import { + Attachment, + Config, + initDatabase, + registerRoutes, + Sentry, +} from "@spacebar/util"; +import { CORS, BodyParser } from "@spacebar/api"; import path from "path"; import avatarsRoute from "./routes/avatars"; import guildProfilesRoute from "./routes/guild-profiles"; import iconsRoute from "./routes/role-icons"; -import { CORS } from "../api/middlewares/CORS"; -import { BodyParser } from "../api/middlewares/BodyParser"; import morgan from "morgan"; +import { Like, Or } from "typeorm"; export type CDNServerOptions = ServerOptions; @@ -38,6 +44,7 @@ export class CDNServer extends Server { async start() { await initDatabase(); await Config.init(); + await this.cleanupSignaturesInDb(); await Sentry.init(this.app); const logRequests = process.env["LOG_REQUESTS"] != undefined; @@ -124,4 +131,26 @@ export class CDNServer extends Server { async stop() { return super.stop(); } + + async cleanupSignaturesInDb() { + this.log("verbose", "[Server] Cleaning up signatures in database"); + const attachmentsToFix = await Attachment.find({ + where: { url: Like("%?ex=%") }, + }); + if (attachmentsToFix.length === 0) { + this.log("verbose", "[Server] No attachments to fix"); + return; + } + + this.log( + "verbose", + `[Server] Found ${attachmentsToFix.length} attachments to fix`, + ); + for (const attachment of attachmentsToFix) { + attachment.url = attachment.url.split("?ex=")[0]; + attachment.proxy_url = attachment.proxy_url?.split("?ex=")[0]; + await attachment.save(); + this.log("verbose", `[Server] Fixed attachment ${attachment.id}`); + } + } } diff --git a/src/cdn/routes/attachments.ts b/src/cdn/routes/attachments.ts index 15e15cbd..d252a386 100644 --- a/src/cdn/routes/attachments.ts +++ b/src/cdn/routes/attachments.ts @@ -69,12 +69,7 @@ router.post( } } - let finalUrl = `${endpoint}/${path}`; - - if (Config.get().security.cdnSignUrls) { - const signatureData = getUrlSignature(path, req); - finalUrl = `${finalUrl}?ex=${signatureData.expiresAt}&is=${signatureData.issuedAt}&hm=${signatureData.hash}&ip=${req.ip}`; - } + const finalUrl = `${endpoint}/${path}`; const file = { id, @@ -82,6 +77,7 @@ router.post( filename: filename, size, url: finalUrl, + path, width, height, }; diff --git a/src/util/Signing.ts b/src/util/Signing.ts index 28fe6ea7..2d3abe07 100644 --- a/src/util/Signing.ts +++ b/src/util/Signing.ts @@ -21,6 +21,7 @@ import { createHmac, timingSafeEqual } from "crypto"; import ms, { StringValue } from "ms"; import { ParsedQs } from "qs"; import { Request } from "express"; +import attachments from "../cdn/routes/attachments"; export const getUrlSignature = ( path: string, diff --git a/src/util/entities/Attachment.ts b/src/util/entities/Attachment.ts index e489609d..5ba9f944 100644 --- a/src/util/entities/Attachment.ts +++ b/src/util/entities/Attachment.ts @@ -28,6 +28,8 @@ import { URL } from "url"; import { deleteFile } from "../util/cdn"; import { BaseClass } from "./BaseClass"; import { dbEngine } from "../util/Database"; +import { Request } from "express"; +import { resignUrl } from "../Signing"; @Entity({ name: "attachments", @@ -73,4 +75,11 @@ export class Attachment extends BaseClass { onDelete() { return deleteFile(new URL(this.url).pathname); } + + signUrls(req: Request) { + return { + url: this.url + resignUrl(this.url, req), + proxy_url: this.proxy_url + resignUrl(this.proxy_url, req), + } + } } diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 9dc2949d..c1fda06e 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -40,6 +40,7 @@ import { Webhook } from "./Webhook"; import { Sticker } from "./Sticker"; import { Attachment } from "./Attachment"; import { dbEngine } from "../util/Database"; +import { Request } from "express"; export enum MessageType { DEFAULT = 0, @@ -260,6 +261,15 @@ export class Message extends BaseClass { content: this.content ?? "", }; } + + withSignedAttachments(req: Request) { + return { + ...this, + attachments: this.attachments?.map((attachment: Attachment) => + attachment.signUrls(req) + ) + }; + } } export interface MessageComponent {