Просмотр исходного кода

Set content disposition header from current settings to uploaded files

arvid-e 9 месяцев назад
Родитель
Сommit
2e7308ba82

+ 26 - 3
apps/app/src/server/service/file-uploader/utils/headers.ts

@@ -2,8 +2,11 @@ import type { Response } from 'express';
 
 import type { ExpressHttpHeader, IContentHeaders } from '~/server/interfaces/attachment';
 import type { IAttachmentDocument } from '~/server/models/attachment';
+import type { ConfigManager } from '~/server/service/config-manager';
 
-import { INLINE_ALLOWLIST_MIME_TYPES } from './security'; // Adjust path if necessary
+import type { ConfigKey } from '../../config-manager/config-definition';
+
+import { DEFAULT_ALLOWLIST_MIME_TYPES, SAFE_INLINE_CONFIGURABLE_MIME_TYPES } from './security';
 
 
 export class ContentHeaders implements IContentHeaders {
@@ -18,9 +21,13 @@ export class ContentHeaders implements IContentHeaders {
 
   xContentTypeOptions?: ExpressHttpHeader<'X-Content-Type-Options'>;
 
+  private configManager: ConfigManager;
+
   constructor(attachment: IAttachmentDocument, opts?: {
+    configManager: ConfigManager,
     inline?: boolean,
   }) {
+
     const attachmentContentType = attachment.fileFormat;
     const filename = attachment.originalName;
 
@@ -35,8 +42,24 @@ export class ContentHeaders implements IContentHeaders {
     // Determine Content-Disposition based on allowlist and the 'inline' request flag
     const requestedInline = opts?.inline ?? false;
 
-    // Should only be inline if it was requested and MIME type is explicitly in the security allowlist.
-    const shouldBeInline = requestedInline && INLINE_ALLOWLIST_MIME_TYPES.has(actualContentTypeString);
+    const configKey = `attachments:contentDisposition:${actualContentTypeString}:inline` as ConfigKey;
+
+    // Get current inline setting
+    const rawConfigValue = this.configManager.getConfig(configKey);
+
+    let isConfiguredInline: boolean;
+    if (typeof rawConfigValue === 'boolean') {
+      isConfiguredInline = rawConfigValue;
+    }
+
+    else {
+      isConfiguredInline = DEFAULT_ALLOWLIST_MIME_TYPES.has(actualContentTypeString);
+    }
+
+    // Final decision
+    const shouldBeInline = requestedInline
+      && isConfiguredInline
+      && SAFE_INLINE_CONFIGURABLE_MIME_TYPES.has(actualContentTypeString);
 
     this.contentDisposition = {
       field: 'Content-Disposition',

+ 36 - 2
apps/app/src/server/service/file-uploader/utils/security.ts

@@ -1,9 +1,9 @@
 /**
- * Defines MIME types that are explicitly safe for INLINE display when served
+ * Defines default MIME types that are explicitly safe for inline display when served
  * from user uploads. All other types will be forced to download, regardless of
  * their file extension or sniffed content.
  */
-export const INLINE_ALLOWLIST_MIME_TYPES = new Set<string>([
+export const DEFAULT_ALLOWLIST_MIME_TYPES = new Set<string>([
   'image/png',
   'image/jpeg',
   'image/gif',
@@ -11,3 +11,37 @@ export const INLINE_ALLOWLIST_MIME_TYPES = new Set<string>([
   'image/bmp',
   'image/x-icon',
 ]);
+
+/**
+ * Defines safe MIME types that can be set to inline by the admin.
+ */
+export const SAFE_INLINE_CONFIGURABLE_MIME_TYPES = new Set<string>([
+  // --- Images ---
+  'image/jpeg',
+  'image/png',
+  'image/gif',
+  'image/webp',
+  'image/bmp',
+  'image/tiff',
+  'image/x-icon',
+
+  // --- Audio ---
+  'audio/mpeg',
+  'audio/ogg',
+  'audio/wav',
+  'audio/aac',
+  'audio/webm',
+
+  // --- Video ---
+  'video/mp4',
+  'video/webm',
+  'video/ogg',
+
+  // --- Documents / Text ---
+  'application/pdf',
+  'text/plain',
+  'text/markdown', // Assumes GROWI's markdown rendering is safe and isolated
+  'text/css',
+  'text/csv',
+  'text/tab-separated-values',
+]);