@zoulabo/line-hive 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +55 -9
  2. package/dist/cli/config-writer.js +1 -1
  3. package/dist/cli/config-writer.js.map +1 -1
  4. package/dist/config.js +4 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/constants.js +8 -0
  7. package/dist/constants.js.map +1 -1
  8. package/dist/i18n.js +3 -1
  9. package/dist/i18n.js.map +1 -1
  10. package/dist/index.js +59 -3
  11. package/dist/index.js.map +1 -1
  12. package/dist/line/messages.js +141 -2
  13. package/dist/line/messages.js.map +1 -1
  14. package/dist/line/webhook.js +294 -54
  15. package/dist/line/webhook.js.map +1 -1
  16. package/dist/server.js +45 -18
  17. package/dist/server.js.map +1 -1
  18. package/dist/store/messageStore.js +123 -53
  19. package/dist/store/messageStore.js.map +1 -1
  20. package/dist/tools/ask.js +90 -44
  21. package/dist/tools/ask.js.map +1 -1
  22. package/dist/tools/createDocx.js +459 -0
  23. package/dist/tools/createDocx.js.map +1 -0
  24. package/dist/tools/createPptx.js +554 -0
  25. package/dist/tools/createPptx.js.map +1 -0
  26. package/dist/tools/createXlsx.js +360 -0
  27. package/dist/tools/createXlsx.js.map +1 -0
  28. package/dist/tools/gitReview-template.js +208 -0
  29. package/dist/tools/gitReview-template.js.map +1 -0
  30. package/dist/tools/gitReview.js +232 -0
  31. package/dist/tools/gitReview.js.map +1 -0
  32. package/dist/tools/readFile.js +22 -29
  33. package/dist/tools/readFile.js.map +1 -1
  34. package/dist/tools/renderMarkdown.js +248 -9
  35. package/dist/tools/renderMarkdown.js.map +1 -1
  36. package/dist/tools/sendMessage.js +20 -6
  37. package/dist/tools/sendMessage.js.map +1 -1
  38. package/dist/tools/setStatus.js +4 -2
  39. package/dist/tools/setStatus.js.map +1 -1
  40. package/dist/tunnel.js +1 -17
  41. package/dist/tunnel.js.map +1 -1
  42. package/dist/util/toolHelpers.js +471 -28
  43. package/dist/util/toolHelpers.js.map +1 -1
  44. package/package.json +7 -1
  45. package/templates/line-notification.instructions.md +137 -18
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/line/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/line/messages.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,11 @@
1
1
  import http from "http";
2
2
  import crypto from "crypto";
3
+ import fs from "fs";
4
+ import path from "path";
3
5
  import { buildDeleteQueueReply, errorMessage, parsePostbackData, textWithQuickReply, textWithQueueQuickReply, formatTimeAgo, STATUS_ICONS, getAgentDisplayName, } from "./messages.js";
4
6
  import { sendWithTokenPool } from "../util/sendWithTokenPool.js";
5
- import { CONFIG_TARGETED_AGENT } from "../constants.js";
7
+ import { CONFIG_TARGETED_AGENT, INCOMING_MEDIA_DIR } from "../constants.js";
8
+ import { getServeDir } from "../util/toolHelpers.js";
6
9
  import { t, resolveLocale } from "../i18n.js";
7
10
  export function verifySignature(secret, body, signature) {
8
11
  if (!secret || !signature) {
@@ -16,6 +19,73 @@ export function verifySignature(secret, body, signature) {
16
19
  return false;
17
20
  return crypto.timingSafeEqual(hashBuf, sigBuf);
18
21
  }
22
+ // ─── SSRF Protection ────────────────────────────────────────────────────────
23
+ /** Private/reserved CIDR ranges that should never be fetched externally */
24
+ const PRIVATE_RANGES = [
25
+ { prefix: "10.", mask: 8 },
26
+ { prefix: "127.", mask: 8 },
27
+ { prefix: "169.254.", mask: 16 },
28
+ { prefix: "192.168.", mask: 16 },
29
+ { prefix: "0.", mask: 8 },
30
+ // 172.16.0.0/12
31
+ ];
32
+ function isPrivateIp(ip) {
33
+ for (const range of PRIVATE_RANGES) {
34
+ if (ip.startsWith(range.prefix))
35
+ return true;
36
+ }
37
+ // 172.16.0.0 – 172.31.255.255
38
+ if (ip.startsWith("172.")) {
39
+ const second = parseInt(ip.split(".")[1], 10);
40
+ if (second >= 16 && second <= 31)
41
+ return true;
42
+ }
43
+ // IPv6 loopback
44
+ if (ip === "::1" || ip === "[::1]")
45
+ return true;
46
+ return false;
47
+ }
48
+ /**
49
+ * Check if a URL resolves to a private/reserved IP range (SSRF protection).
50
+ * Only checks the hostname — DNS rebinding is out of scope for this check.
51
+ */
52
+ function isPrivateUrl(urlStr) {
53
+ try {
54
+ const parsed = new URL(urlStr);
55
+ return isPrivateIp(parsed.hostname);
56
+ }
57
+ catch {
58
+ return true; // invalid URL → reject
59
+ }
60
+ }
61
+ // ─── Size-limited response reader ───────────────────────────────────────────
62
+ /**
63
+ * Read a fetch Response body with a hard byte limit.
64
+ * Prevents Content-Length TOCTOU: the actual bytes are counted during read.
65
+ * Returns null if the body exceeds the limit.
66
+ */
67
+ async function readResponseWithLimit(res, maxBytes) {
68
+ const reader = res.body?.getReader();
69
+ if (!reader) {
70
+ // Fallback for environments without readable stream
71
+ const buf = Buffer.from(await res.arrayBuffer());
72
+ return buf.length <= maxBytes ? buf : null;
73
+ }
74
+ const chunks = [];
75
+ let totalBytes = 0;
76
+ while (true) {
77
+ const { done, value } = await reader.read();
78
+ if (done)
79
+ break;
80
+ totalBytes += value.byteLength;
81
+ if (totalBytes > maxBytes) {
82
+ reader.cancel();
83
+ return null;
84
+ }
85
+ chunks.push(value);
86
+ }
87
+ return Buffer.concat(chunks);
88
+ }
19
89
  function getWebhookEventId(event) {
20
90
  return event.webhookEventId || event.message?.id || event.replyToken || `${event.timestamp}`;
21
91
  }
@@ -42,6 +112,18 @@ async function resolveUserLocale(deps, userId) {
42
112
  }
43
113
  return "en";
44
114
  }
115
+ /**
116
+ * Register the user as default (if first user) and auto-enable restricted mode.
117
+ * When the first user interacts with the bot and no allowed_users list exists yet,
118
+ * this switches from open mode to restricted mode with the first user auto-allowed.
119
+ */
120
+ function registerAndAutoAllow(deps, userId) {
121
+ const isFirst = deps.messageStore.registerDefaultUserIfMissing(userId);
122
+ if (isFirst && deps.messageStore.getAllowedUsers().length === 0) {
123
+ deps.messageStore.addAllowedUser(userId);
124
+ deps.logger?.info({ userId: userId.slice(0, 8) }, "First user auto-allowed, restricted mode enabled");
125
+ }
126
+ }
45
127
  /** Format a single agent's status into a line of text */
46
128
  export function formatAgentStatus(agent, locale = "en") {
47
129
  const m = t(locale);
@@ -136,8 +218,8 @@ async function replyViaPool(deps, replyToken, message, eventTimestamp) {
136
218
  }
137
219
  }
138
220
  /**
139
- * Download an image preview from the LINE Content API and return as base64.
140
- * Uses the /preview endpoint (~240×240, ~30KB) to keep payloads small.
221
+ * Download an image from the LINE Content API at full resolution.
222
+ * Uses the full content endpoint (not preview) to get the original image.
141
223
  * For external images (forwarded), fetches from the original URL directly.
142
224
  */
143
225
  async function downloadImagePreview(deps, event) {
@@ -151,10 +233,16 @@ async function downloadImagePreview(deps, event) {
151
233
  deps.logger?.warn({ url: provider.originalContentUrl }, "Rejected non-HTTPS external image URL");
152
234
  return null;
153
235
  }
236
+ // Block private/reserved IP ranges (SSRF protection)
237
+ if (isPrivateUrl(provider.originalContentUrl)) {
238
+ deps.logger?.warn({ url: provider.originalContentUrl }, "Rejected external URL targeting private/reserved IP");
239
+ return null;
240
+ }
154
241
  url = provider.originalContentUrl;
155
242
  }
156
243
  else if (messageId) {
157
- url = `https://api-data.line.me/v2/bot/message/${messageId}/content/preview`;
244
+ // Full-resolution content endpoint (not /preview)
245
+ url = `https://api-data.line.me/v2/bot/message/${messageId}/content`;
158
246
  }
159
247
  else {
160
248
  return null;
@@ -168,16 +256,20 @@ async function downloadImagePreview(deps, event) {
168
256
  deps.logger?.warn({ status: res.status, messageId }, "Image download failed");
169
257
  return null;
170
258
  }
171
- // Limit download size to 5MB to prevent memory exhaustion
259
+ // Limit download size to 10MB to prevent memory exhaustion
260
+ const MAX_DOWNLOAD = 10 * 1024 * 1024;
172
261
  const contentLength = res.headers.get("content-length");
173
- if (contentLength && parseInt(contentLength, 10) > 5 * 1024 * 1024) {
262
+ if (contentLength && parseInt(contentLength, 10) > MAX_DOWNLOAD) {
174
263
  deps.logger?.warn({ contentLength }, "Image too large, skipping download");
175
264
  return null;
176
265
  }
177
- const buffer = Buffer.from(await res.arrayBuffer());
178
- const base64 = buffer.toString("base64");
266
+ const buffer = await readResponseWithLimit(res, MAX_DOWNLOAD);
267
+ if (!buffer) {
268
+ deps.logger?.warn({ messageId }, "Image body exceeded size limit during read");
269
+ return null;
270
+ }
179
271
  const mimeType = res.headers.get("content-type") || "image/jpeg";
180
- return { base64, mimeType };
272
+ return { buffer, mimeType };
181
273
  }
182
274
  catch (error) {
183
275
  deps.logger?.warn({ error }, "Image download error");
@@ -202,17 +294,21 @@ async function downloadFileContent(deps, event) {
202
294
  deps.logger?.warn({ status: res.status, messageId }, "File download failed");
203
295
  return null;
204
296
  }
205
- // Limit download size to 10MB
297
+ // Limit download size (configurable) to prevent memory exhaustion.
298
+ const MAX_DOWNLOAD = deps.config.maxIncomingFileBytes;
206
299
  const contentLength = res.headers.get("content-length");
207
- if (contentLength && parseInt(contentLength, 10) > 10 * 1024 * 1024) {
300
+ if (contentLength && parseInt(contentLength, 10) > MAX_DOWNLOAD) {
208
301
  deps.logger?.warn({ contentLength }, "File too large, skipping download");
209
302
  return null;
210
303
  }
211
- const buffer = Buffer.from(await res.arrayBuffer());
212
- const base64 = buffer.toString("base64");
304
+ const buffer = await readResponseWithLimit(res, MAX_DOWNLOAD);
305
+ if (!buffer) {
306
+ deps.logger?.warn({ messageId }, "File body exceeded size limit during read");
307
+ return null;
308
+ }
213
309
  const mimeType = res.headers.get("content-type") || "application/octet-stream";
214
310
  return {
215
- base64,
311
+ buffer,
216
312
  mimeType,
217
313
  fileName: event.message?.fileName,
218
314
  fileSize: event.message?.fileSize,
@@ -223,6 +319,38 @@ async function downloadFileContent(deps, event) {
223
319
  return null;
224
320
  }
225
321
  }
322
+ /**
323
+ * Write downloaded media to disk under `.line-hive-tmp/incoming/`.
324
+ * Returns the relative disk path (from project root) for storage in media_json.
325
+ *
326
+ * Naming: `{lineMessageId}-{sanitizedFilename}` or `{lineMessageId}.{ext}`
327
+ */
328
+ function writeIncomingMedia(lineMessageId, buffer, mimeType, fileName) {
329
+ const dir = path.resolve(INCOMING_MEDIA_DIR);
330
+ fs.mkdirSync(dir, { recursive: true });
331
+ // Derive extension from filename or mime type
332
+ const mimeToExt = {
333
+ "image/jpeg": ".jpg", "image/png": ".png", "image/gif": ".gif",
334
+ "image/webp": ".webp", "audio/mpeg": ".mp3", "audio/m4a": ".m4a",
335
+ "audio/ogg": ".ogg", "video/mp4": ".mp4", "application/pdf": ".pdf",
336
+ };
337
+ // Sanitize message ID and filename to prevent path traversal
338
+ const safeId = lineMessageId.replace(/[^a-zA-Z0-9._-]/g, "_");
339
+ let safeName;
340
+ if (fileName) {
341
+ // Sanitize user-provided filename: keep alphanumeric, dots, hyphens, underscores
342
+ safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
343
+ }
344
+ else {
345
+ const ext = mimeToExt[mimeType] || "";
346
+ safeName = `${safeId}${ext}`;
347
+ }
348
+ const diskName = `${safeId}-${safeName}`;
349
+ const diskPath = path.join(dir, diskName);
350
+ fs.writeFileSync(diskPath, buffer, { mode: 0o600 });
351
+ // Return path relative to project root (for storage in media_json)
352
+ return path.join(INCOMING_MEDIA_DIR, diskName);
353
+ }
226
354
  async function handleFileEvent(deps, event, userId) {
227
355
  const replyToken = event.replyToken;
228
356
  const webhookEventId = getWebhookEventId(event);
@@ -230,7 +358,17 @@ async function handleFileEvent(deps, event, userId) {
230
358
  const fileSize = event.message?.fileSize;
231
359
  // Download file content immediately (LINE auto-deletes content after a period)
232
360
  const media = await downloadFileContent(deps, event);
233
- const mediaJson = media ? JSON.stringify(media) : null;
361
+ let mediaJson = null;
362
+ if (media) {
363
+ const lineMessageId = event.message?.id ?? webhookEventId;
364
+ const diskPath = writeIncomingMedia(lineMessageId, media.buffer, media.mimeType, media.fileName);
365
+ mediaJson = JSON.stringify({
366
+ mimeType: media.mimeType,
367
+ diskPath,
368
+ fileName: media.fileName,
369
+ fileSize: media.fileSize,
370
+ });
371
+ }
234
372
  const locale = await resolveUserLocale(deps, userId);
235
373
  const chatTargetedAgentId = deps.messageStore.getConfig(CONFIG_TARGETED_AGENT);
236
374
  const sizeStr = fileSize ? ` (${Math.round(fileSize / 1024)}KB)` : "";
@@ -261,7 +399,12 @@ async function handleImageEvent(deps, event, userId) {
261
399
  const webhookEventId = getWebhookEventId(event);
262
400
  // Download image preview immediately (LINE auto-deletes content after a period)
263
401
  const media = await downloadImagePreview(deps, event);
264
- const mediaJson = media ? JSON.stringify({ mimeType: media.mimeType, base64: media.base64 }) : null;
402
+ let mediaJson = null;
403
+ if (media) {
404
+ const lineMessageId = event.message?.id ?? webhookEventId;
405
+ const diskPath = writeIncomingMedia(lineMessageId, media.buffer, media.mimeType);
406
+ mediaJson = JSON.stringify({ mimeType: media.mimeType, diskPath });
407
+ }
265
408
  // Resolve user locale
266
409
  const locale = await resolveUserLocale(deps, userId);
267
410
  // For chat messages, use targeted routing if user has selected an agent
@@ -277,7 +420,7 @@ async function handleImageEvent(deps, event, userId) {
277
420
  }, { targetedAgentId: chatTargetedAgentId });
278
421
  if (messageResult.deduped)
279
422
  return;
280
- // Auto-register userId
423
+ // Auto-register userId (no auto-allow for image events)
281
424
  deps.messageStore.registerDefaultUserIfMissing(userId);
282
425
  if (!replyToken)
283
426
  return;
@@ -331,8 +474,13 @@ async function handleTextEvent(deps, event, text, userId) {
331
474
  if (isControlCommand && messageResult.messageId) {
332
475
  deps.messageStore.markMessageConsumed(messageResult.messageId);
333
476
  }
334
- // Auto-register userId from incoming webhook (DB-only, no env/file writes)
335
- deps.messageStore.registerDefaultUserIfMissing(userId);
477
+ // Auto-register: only auto-allow on status command ("?"), not on chat/other
478
+ if (command.type === "status") {
479
+ registerAndAutoAllow(deps, userId);
480
+ }
481
+ else {
482
+ deps.messageStore.registerDefaultUserIfMissing(userId);
483
+ }
336
484
  if (!replyToken) {
337
485
  return;
338
486
  }
@@ -356,13 +504,21 @@ async function handlePostbackEvent(deps, event, data, userId) {
356
504
  const replyToken = event.replyToken;
357
505
  if (!replyToken)
358
506
  return;
359
- // Auto-register userId if needed
507
+ // Auto-register userId (no auto-allow for postback events)
360
508
  deps.messageStore.registerDefaultUserIfMissing(userId);
361
509
  // Resolve user locale
362
510
  const locale = await resolveUserLocale(deps, userId);
363
511
  const command = parsePostbackData(data);
364
512
  await handleCommandReply(deps, command, replyToken, null, event.timestamp, locale);
365
513
  }
514
+ /**
515
+ * Strip [Quoting: "..."] prefix from message text for queue previews.
516
+ * Quoted context is preserved in the full message delivered to agents,
517
+ * but showing it in the 50-char preview makes it look like a separate message.
518
+ */
519
+ function stripQuotePrefix(text) {
520
+ return text.replace(/^\[Quoting: "[^"]*"\]\s*/, "");
521
+ }
366
522
  /**
367
523
  * Shared command handler — routes status/select/chat commands
368
524
  * and replies with rich messages.
@@ -488,7 +644,8 @@ async function handleCommandReply(deps, command, replyToken, messageId, eventTim
488
644
  : m.queuedHeader();
489
645
  const lines = queued.map((msg, i) => {
490
646
  const ago = formatTimeAgo(msg.timestamp, locale);
491
- const preview = msg.text.length > 50 ? msg.text.slice(0, 50) + "…" : msg.text;
647
+ const displayText = stripQuotePrefix(msg.text);
648
+ const preview = displayText.length > 50 ? displayText.slice(0, 50) + "…" : displayText;
492
649
  // Only show per-line agent label when agents differ
493
650
  const agentLabel = !allSameAgent && msg.targetAgentId
494
651
  ? ` → ${msg.targetAgentId.includes(":") ? msg.targetAgentId.split(":").pop() : msg.targetAgentId}`
@@ -516,7 +673,8 @@ async function handleCommandReply(deps, command, replyToken, messageId, eventTim
516
673
  else {
517
674
  const lines = queued.map((msg, i) => {
518
675
  const ago = formatTimeAgo(msg.timestamp, locale);
519
- const preview = msg.text.length > 50 ? msg.text.slice(0, 50) + "…" : msg.text;
676
+ const displayText = stripQuotePrefix(msg.text);
677
+ const preview = displayText.length > 50 ? displayText.slice(0, 50) + "…" : displayText;
520
678
  const emoji = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"][i] || `${i + 1}`;
521
679
  return `${emoji} ${preview} (${ago})`;
522
680
  });
@@ -544,7 +702,8 @@ async function handleCommandReply(deps, command, replyToken, messageId, eventTim
544
702
  const allSameAgent = queued.every(msg => msg.targetAgentId && msg.targetAgentId === queued[0].targetAgentId);
545
703
  const lines = queued.map((msg, i) => {
546
704
  const ago = formatTimeAgo(msg.timestamp, locale);
547
- const preview = msg.text.length > 50 ? msg.text.slice(0, 50) + "…" : msg.text;
705
+ const displayText = stripQuotePrefix(msg.text);
706
+ const preview = displayText.length > 50 ? displayText.slice(0, 50) + "…" : displayText;
548
707
  const agentLabel = !allSameAgent && msg.targetAgentId
549
708
  ? ` → ${msg.targetAgentId.includes(":") ? msg.targetAgentId.split(":").pop() : msg.targetAgentId}`
550
709
  : "";
@@ -600,7 +759,8 @@ async function handleCommandReply(deps, command, replyToken, messageId, eventTim
600
759
  deps.logger?.info({ targetAgent: agentDisplayName }, "Chat routed to targeted agent");
601
760
  // If agent is actively waiting (has active session), save token silently —
602
761
  // agent will respond within seconds via line_ask polling.
603
- if (deps.messageStore.hasWaitingSession()) {
762
+ // Scope to THIS agent to avoid stale sessions from dead agents suppressing acks.
763
+ if (deps.messageStore.getWaitingSessionForAgent(targetAgent.agentId)) {
604
764
  deps.messageStore.saveReplyToken(replyToken, eventTimestamp ?? Date.now());
605
765
  return;
606
766
  }
@@ -608,7 +768,7 @@ async function handleCommandReply(deps, command, replyToken, messageId, eventTim
608
768
  // send brief ack so user knows message was received and queued
609
769
  const queueCount = deps.messageStore.countUnclaimedMessages(undefined, targetAgent.agentId);
610
770
  const queueInfo = queueCount > 1 ? m.nQueued(queueCount) : "";
611
- await replyViaPool(deps, replyToken, textWithQuickReply(m.ackQueued(agentDisplayName, queueInfo), recentStatuses, locale), eventTimestamp);
771
+ await replyViaPool(deps, replyToken, textWithQuickReply(m.ackQueued(agentDisplayName, queueInfo), recentStatuses, locale, { dismissMessageId: messageId ?? undefined }), eventTimestamp);
612
772
  return;
613
773
  }
614
774
  // No targeted agent — fall back to needs_input routing
@@ -645,6 +805,11 @@ export async function handleWebhook(deps, body, signature) {
645
805
  const userId = event.source?.userId;
646
806
  if (!userId)
647
807
  continue;
808
+ // Access control: reject messages from unauthorized users
809
+ if (!deps.messageStore.isUserAllowed(userId)) {
810
+ deps.logger?.info({ userId: userId.slice(0, 8) }, "Blocked message from unauthorized user");
811
+ continue;
812
+ }
648
813
  if (event.type === "message" && event.message?.type === "text") {
649
814
  const text = event.message?.text ?? "";
650
815
  await handleTextEvent(deps, event, text, userId);
@@ -667,6 +832,14 @@ export async function handleWebhook(deps, body, signature) {
667
832
  const removed = deps.messageStore.deleteByLineMessageId(event.unsend.messageId);
668
833
  deps.logger?.info({ lineMessageId: event.unsend.messageId, removed }, "Unsend: message deleted");
669
834
  }
835
+ else if (event.type === "follow") {
836
+ // New user added bot as friend — auto-register and auto-allow
837
+ registerAndAutoAllow(deps, userId);
838
+ // Save reply token if available
839
+ if (event.replyToken) {
840
+ deps.messageStore.saveReplyToken(event.replyToken, event.timestamp);
841
+ }
842
+ }
670
843
  }
671
844
  return { statusCode: 200, body: "OK" };
672
845
  }
@@ -675,43 +848,109 @@ export function startWebhookServer(deps) {
675
848
  const server = http.createServer((req, res) => {
676
849
  const { method, url } = req;
677
850
  // Serve outgoing files/images for LINE to fetch or user to download
678
- // Supports both /files/ (new) and /images/ (legacy) paths
679
- const isFilesRoute = method === "GET" && (url?.startsWith("/files/") || url?.startsWith("/images/"));
851
+ const isFilesRoute = (method === "GET" || method === "HEAD") && url?.startsWith("/files/");
680
852
  if (isFilesRoute) {
681
- const prefix = url.startsWith("/files/") ? "/files/" : "/images/";
853
+ const prefix = "/files/";
682
854
  // Strip query string and decode percent-encoding for clean ID extraction
683
855
  const rawId = url.slice(prefix.length).split("?")[0];
684
- // Strip file extension (e.g. "abc123.html" → "abc123") for store lookup
685
- const imageId = decodeURIComponent(rawId).replace(/\.[^.]+$/, "");
686
- if (!imageId || imageId.includes("/") || imageId.includes("..")) {
856
+ const decodedRawId = decodeURIComponent(rawId);
857
+ if (!decodedRawId || decodedRawId.includes("/") || decodedRawId.includes("..") || decodedRawId.includes("\\")) {
687
858
  res.statusCode = 404;
688
859
  res.end("Not found");
689
860
  return;
690
861
  }
691
- const image = deps.messageStore.getOutgoingImage(imageId);
692
- if (!image) {
693
- res.statusCode = 404;
694
- res.end("Not found");
862
+ // Check TTL — all disk-served files MUST be tracked.
863
+ // Untracked files on disk return 404 (strict mode).
864
+ // Race: cleanup may delete between check and readFile → 404 (acceptable).
865
+ const fileExpiry = deps.messageStore.getServedFileExpiry(decodedRawId);
866
+ if (fileExpiry?.isExpired) {
867
+ res.statusCode = 410;
868
+ res.end("Gone — file expired");
695
869
  return;
696
870
  }
697
- // Add charset for text-based content types
698
- const contentType = image.mimeType.startsWith("text/")
699
- ? `${image.mimeType}; charset=utf-8`
700
- : image.mimeType;
701
- res.setHeader("Content-Type", contentType);
702
- res.setHeader("Content-Length", image.data.length);
703
- res.setHeader("Cache-Control", "public, max-age=3600");
704
- // Prevent MIME sniffing (defense-in-depth)
705
- res.setHeader("X-Content-Type-Options", "nosniff");
706
- // Images and browser-viewable types display inline; others trigger download
707
- if (image.mimeType.startsWith("image/") || image.mimeType === "text/html" || image.mimeType === "application/pdf") {
708
- res.setHeader("Content-Disposition", "inline");
709
- }
710
- else {
711
- res.setHeader("Content-Disposition", "attachment");
871
+ // Try disk (files MUST be tracked in served_files table)
872
+ const serveDir = getServeDir(deps.config);
873
+ const diskPath = path.resolve(serveDir, decodedRawId);
874
+ if (fileExpiry && diskPath.startsWith(serveDir + path.sep)) {
875
+ try {
876
+ const stat = fs.statSync(diskPath);
877
+ if (!stat.isFile()) {
878
+ res.statusCode = 404;
879
+ res.end("Not found");
880
+ return;
881
+ }
882
+ const ext = path.extname(diskPath).toLowerCase();
883
+ const mimeMap = {
884
+ ".html": "text/html",
885
+ ".css": "text/css",
886
+ ".js": "application/javascript",
887
+ ".json": "application/json",
888
+ ".png": "image/png",
889
+ ".jpg": "image/jpeg",
890
+ ".jpeg": "image/jpeg",
891
+ ".gif": "image/gif",
892
+ ".webp": "image/webp",
893
+ ".svg": "image/svg+xml",
894
+ ".pdf": "application/pdf",
895
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
896
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
897
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
898
+ ".csv": "text/csv",
899
+ ".txt": "text/plain",
900
+ };
901
+ const mimeType = mimeMap[ext] || "application/octet-stream";
902
+ const contentType = mimeType.startsWith("text/") ? `${mimeType}; charset=utf-8` : mimeType;
903
+ res.setHeader("Content-Type", contentType);
904
+ res.setHeader("Content-Length", stat.size);
905
+ res.setHeader("Cache-Control", "public, max-age=60");
906
+ res.setHeader("X-Content-Type-Options", "nosniff");
907
+ res.setHeader("Referrer-Policy", "no-referrer");
908
+ res.setHeader("Accept-Ranges", "bytes");
909
+ if (mimeType.startsWith("image/") || mimeType === "text/html" || mimeType === "application/pdf") {
910
+ res.setHeader("Content-Disposition", "inline");
911
+ }
912
+ else {
913
+ // Use the filename from the URL path as the download name
914
+ const baseName = path.basename(diskPath);
915
+ res.setHeader("Content-Disposition", `attachment; filename="${baseName}"`);
916
+ }
917
+ res.statusCode = 200;
918
+ if (method === "HEAD") {
919
+ res.end();
920
+ return;
921
+ }
922
+ const stream = fs.createReadStream(diskPath);
923
+ stream.on("error", (err) => {
924
+ if (err?.code === "ENOENT") {
925
+ res.statusCode = 404;
926
+ res.end("Not found");
927
+ }
928
+ else {
929
+ deps.logger?.warn({ error: err instanceof Error ? err.message : err, file: decodedRawId }, "Failed to stream file");
930
+ // If headers already sent, just destroy.
931
+ try {
932
+ res.destroy();
933
+ }
934
+ catch { }
935
+ }
936
+ });
937
+ stream.pipe(res);
938
+ }
939
+ catch (err) {
940
+ if (err?.code === "ENOENT") {
941
+ res.statusCode = 404;
942
+ res.end("Not found");
943
+ }
944
+ else {
945
+ deps.logger?.warn({ error: err instanceof Error ? err.message : err, file: decodedRawId }, "Failed to serve file");
946
+ res.statusCode = 500;
947
+ res.end("Internal error");
948
+ }
949
+ }
950
+ return;
712
951
  }
713
- res.statusCode = 200;
714
- res.end(image.data);
952
+ res.statusCode = 404;
953
+ res.end("Not found");
715
954
  return;
716
955
  }
717
956
  if (method !== "POST" || url !== deps.config.webhookPath) {
@@ -753,10 +992,11 @@ export function startWebhookServer(deps) {
753
992
  deps.logger?.error({ error }, "Webhook server error");
754
993
  throw error;
755
994
  });
756
- server.listen(deps.config.webhookPort, () => {
995
+ server.listen(deps.config.webhookPort, "127.0.0.1", () => {
757
996
  deps.logger?.info({
758
997
  port: deps.config.webhookPort,
759
- path: deps.config.webhookPath
998
+ path: deps.config.webhookPath,
999
+ host: "127.0.0.1"
760
1000
  }, "Webhook server listening");
761
1001
  });
762
1002
  return server;