@solidxai/core 0.1.14 → 0.1.15-beta.1
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.
- package/CHANGELOG.md +1197 -0
- package/CLAUDE.md +26 -0
- package/dist/constants/media-file-types.d.ts +1 -0
- package/dist/constants/media-file-types.d.ts.map +1 -1
- package/dist/constants/media-file-types.js +10 -1
- package/dist/constants/media-file-types.js.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +5 -5
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/helpers/user-helper.d.ts.map +1 -1
- package/dist/helpers/user-helper.js +2 -0
- package/dist/helpers/user-helper.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +0 -2
- package/dist/services/import-transaction.service.d.ts +1 -0
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +21 -0
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +0 -1
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +0 -13
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +0 -4
- package/dist/solid-core.module.js.map +1 -1
- package/package.json +1 -1
- package/src/constants/media-file-types.ts +18 -2
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +14 -6
- package/src/helpers/user-helper.ts +2 -0
- package/src/seeders/seed-data/solid-core-metadata.json +0 -2
- package/src/services/import-transaction.service.ts +35 -0
- package/src/services/model-metadata.service.ts +0 -25
- package/src/solid-core.module.ts +0 -4
- package/dist/commands/migrate-removed-fields.command.d.ts +0 -19
- package/dist/commands/migrate-removed-fields.command.d.ts.map +0 -1
- package/dist/commands/migrate-removed-fields.command.js +0 -77
- package/dist/commands/migrate-removed-fields.command.js.map +0 -1
- package/dist/services/removed-field-migration.service.d.ts +0 -30
- package/dist/services/removed-field-migration.service.d.ts.map +0 -1
- package/dist/services/removed-field-migration.service.js +0 -319
- package/dist/services/removed-field-migration.service.js.map +0 -1
- package/src/commands/migrate-removed-fields.command.ts +0 -81
- package/src/services/removed-field-migration.service.ts +0 -334
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This is the SolidX API project.
|
|
2
|
+
This is included as a dependency in consuming projects.
|
|
3
|
+
|
|
4
|
+
# SolidX UI
|
|
5
|
+
# The corresponding UI project is at the below path:
|
|
6
|
+
/Users/oswald/projects/Solid_Starters/solid-core-ui
|
|
7
|
+
|
|
8
|
+
# SolidX Docs
|
|
9
|
+
# The documentation project is at the below path:
|
|
10
|
+
/Users/oswald/projects/solidxai-docs
|
|
11
|
+
|
|
12
|
+
# SolidX Agent
|
|
13
|
+
# The agent project which has the skills for helping users configure, build and use SolidX is at the below path:
|
|
14
|
+
/Users/oswald/projects/Solid_Starters/agent
|
|
15
|
+
|
|
16
|
+
# SolidX Control Plane Commands
|
|
17
|
+
# The control plane commands for managing SolidX instances are in the below path:
|
|
18
|
+
/Users/oswald/projects/Solid_Starters/solidctl
|
|
19
|
+
|
|
20
|
+
# Testing reference project
|
|
21
|
+
# This is the reference library management project that is used for testing and development of SolidX features. It is located at the below path:
|
|
22
|
+
/Users/oswald/projects/Solid_Starters/solid-library-management
|
|
23
|
+
|
|
24
|
+
# Code Builder project
|
|
25
|
+
# This is the project that contains the code builder which generates code based on user input and templates. It is located at the below path:
|
|
26
|
+
/Users/oswald/projects/Solid_Starters/solid-code-builder
|
|
@@ -26,6 +26,7 @@ export interface UploadedFileLike {
|
|
|
26
26
|
}
|
|
27
27
|
export declare function getUploadedFileName(file?: UploadedFileLike | null): string;
|
|
28
28
|
export declare function isDangerousMediaFile(file?: UploadedFileLike | null): boolean;
|
|
29
|
+
export declare const EXT_TO_MEDIA_TYPES: Record<string, MediaCategory[]>;
|
|
29
30
|
export declare const EXT_TO_MEDIA_TYPE: Record<string, MediaCategory>;
|
|
30
31
|
export declare const INLINE_SAFE_EXTENSIONS: Set<string>;
|
|
31
32
|
//# sourceMappingURL=media-file-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-file-types.d.ts","sourceRoot":"","sources":["../../src/constants/media-file-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAEzE,eAAO,MAAM,gBAAgB,qDAAsD,CAAC;AAEpF,MAAM,MAAM,uBAAuB,GAAG;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC;AAIF,eAAO,MAAM,gBAAgB,EAAE,uBAAuB,EAkDrD,CAAC;AAUF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,CAKzC,CAAC;AAE7B,eAAO,MAAM,gBAAgB,UAAiC,CAAC;AAC/D,eAAO,MAAM,gBAAgB,UAAiC,CAAC;AAC/D,eAAO,MAAM,gBAAgB,UAAiC,CAAC;AAC/D,eAAO,MAAM,mBAAmB,UAAgC,CAAC;AACjE,eAAO,MAAM,aAAa,QAAQ,CAAC;AAEnC,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK1B,CAAC;AAEjC,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAKtB,CAAC;AAOxC,eAAO,MAAM,oBAAoB,aAK/B,CAAC;AAQH,eAAO,MAAM,oBAAoB,aAE/B,CAAC;AAuCH,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAmB1E;AAOD,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAStF;AAWD,wBAAgB,iCAAiC,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAGpF;AAGD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CAE1E;AAQD,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAO5E;
|
|
1
|
+
{"version":3,"file":"media-file-types.d.ts","sourceRoot":"","sources":["../../src/constants/media-file-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAEzE,eAAO,MAAM,gBAAgB,qDAAsD,CAAC;AAEpF,MAAM,MAAM,uBAAuB,GAAG;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC;AAIF,eAAO,MAAM,gBAAgB,EAAE,uBAAuB,EAkDrD,CAAC;AAUF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,CAKzC,CAAC;AAE7B,eAAO,MAAM,gBAAgB,UAAiC,CAAC;AAC/D,eAAO,MAAM,gBAAgB,UAAiC,CAAC;AAC/D,eAAO,MAAM,gBAAgB,UAAiC,CAAC;AAC/D,eAAO,MAAM,mBAAmB,UAAgC,CAAC;AACjE,eAAO,MAAM,aAAa,QAAQ,CAAC;AAEnC,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK1B,CAAC;AAEjC,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAKtB,CAAC;AAOxC,eAAO,MAAM,oBAAoB,aAK/B,CAAC;AAQH,eAAO,MAAM,oBAAoB,aAE/B,CAAC;AAuCH,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAmB1E;AAOD,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAStF;AAWD,wBAAgB,iCAAiC,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAGpF;AAGD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CAE1E;AAQD,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAO5E;AAUD,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAatB,CAAC;AAE1C,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAE3D,CAAC;AAMF,eAAO,MAAM,sBAAsB,aAKjC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.INLINE_SAFE_EXTENSIONS = exports.EXT_TO_MEDIA_TYPE = exports.DANGEROUS_MIME_TYPES = exports.DANGEROUS_EXTENSIONS = exports.MIME_TO_MEDIA_TYPE = exports.EXTENSION_TO_MIME_TYPE = exports.PDF_EXTENSION = exports.DOCUMENT_EXTENSIONS = exports.VIDEO_EXTENSIONS = exports.AUDIO_EXTENSIONS = exports.IMAGE_EXTENSIONS = exports.MEDIA_TYPE_TO_EXTENSIONS = exports.MEDIA_FILE_TYPES = exports.MEDIA_CATEGORIES = void 0;
|
|
3
|
+
exports.INLINE_SAFE_EXTENSIONS = exports.EXT_TO_MEDIA_TYPE = exports.EXT_TO_MEDIA_TYPES = exports.DANGEROUS_MIME_TYPES = exports.DANGEROUS_EXTENSIONS = exports.MIME_TO_MEDIA_TYPE = exports.EXTENSION_TO_MIME_TYPE = exports.PDF_EXTENSION = exports.DOCUMENT_EXTENSIONS = exports.VIDEO_EXTENSIONS = exports.AUDIO_EXTENSIONS = exports.IMAGE_EXTENSIONS = exports.MEDIA_TYPE_TO_EXTENSIONS = exports.MEDIA_FILE_TYPES = exports.MEDIA_CATEGORIES = void 0;
|
|
4
4
|
exports.normalizeUploadedFileName = normalizeUploadedFileName;
|
|
5
5
|
exports.getLowercaseFileExtension = getLowercaseFileExtension;
|
|
6
6
|
exports.getLowercaseFileExtensionSegments = getLowercaseFileExtensionSegments;
|
|
@@ -148,6 +148,15 @@ function isDangerousMediaFile(file) {
|
|
|
148
148
|
}
|
|
149
149
|
return exports.DANGEROUS_MIME_TYPES.has((file?.mimetype || '').toLowerCase().trim());
|
|
150
150
|
}
|
|
151
|
+
exports.EXT_TO_MEDIA_TYPES = exports.MEDIA_FILE_TYPES.reduce((acc, fileType) => {
|
|
152
|
+
if (!acc[fileType.extension]) {
|
|
153
|
+
acc[fileType.extension] = [];
|
|
154
|
+
}
|
|
155
|
+
if (!acc[fileType.extension].includes(fileType.mediaType)) {
|
|
156
|
+
acc[fileType.extension].push(fileType.mediaType);
|
|
157
|
+
}
|
|
158
|
+
return acc;
|
|
159
|
+
}, {});
|
|
151
160
|
exports.EXT_TO_MEDIA_TYPE = {
|
|
152
161
|
...Object.fromEntries(exports.MEDIA_FILE_TYPES.map((fileType) => [fileType.extension, fileType.mediaType])),
|
|
153
162
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-file-types.js","sourceRoot":"","sources":["../../src/constants/media-file-types.ts"],"names":[],"mappings":"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","sourcesContent":["export type MediaCategory = 'image' | 'audio' | 'video' | 'file' | 'pdf';\n\nexport const MEDIA_CATEGORIES = ['image', 'audio', 'video', 'file', 'pdf'] as const;\n\nexport type MediaFileTypeDefinition = {\n mediaType: MediaCategory;\n mimeType: string;\n extension: string;\n};\n\n// Single source of truth for every supported media extension, its canonical MIME type,\n// and the coarse media category used across validation and UI metadata.\nexport const MEDIA_FILE_TYPES: MediaFileTypeDefinition[] = [\n { mediaType: 'image', mimeType: 'image/png', extension: 'png' },\n { mediaType: 'image', mimeType: 'image/jpeg', extension: 'jpg' },\n { mediaType: 'image', mimeType: 'image/jpg', extension: 'jpg' },\n { mediaType: 'image', mimeType: 'image/jpeg', extension: 'jpeg' },\n { mediaType: 'image', mimeType: 'image/webp', extension: 'webp' },\n { mediaType: 'image', mimeType: 'image/gif', extension: 'gif' },\n { mediaType: 'image', mimeType: 'image/bmp', extension: 'bmp' },\n { mediaType: 'image', mimeType: 'image/tiff', extension: 'tiff' },\n { mediaType: 'image', mimeType: 'image/heic', extension: 'heic' },\n { mediaType: 'image', mimeType: 'image/heif', extension: 'heif' },\n\n { mediaType: 'audio', mimeType: 'audio/mpeg', extension: 'mp3' },\n { mediaType: 'audio', mimeType: 'audio/mp3', extension: 'mp3' },\n { mediaType: 'audio', mimeType: 'audio/wav', extension: 'wav' },\n { mediaType: 'audio', mimeType: 'audio/x-wav', extension: 'wav' },\n { mediaType: 'audio', mimeType: 'audio/ogg', extension: 'ogg' },\n { mediaType: 'audio', mimeType: 'audio/aac', extension: 'aac' },\n { mediaType: 'audio', mimeType: 'audio/mp4', extension: 'm4a' },\n { mediaType: 'audio', mimeType: 'audio/x-m4a', extension: 'm4a' },\n { mediaType: 'audio', mimeType: 'audio/flac', extension: 'flac' },\n { mediaType: 'audio', mimeType: 'audio/webm', extension: 'webm' },\n\n { mediaType: 'video', mimeType: 'video/mp4', extension: 'mp4' },\n { mediaType: 'video', mimeType: 'video/quicktime', extension: 'mov' },\n { mediaType: 'video', mimeType: 'video/x-msvideo', extension: 'avi' },\n { mediaType: 'video', mimeType: 'video/x-matroska', extension: 'mkv' },\n { mediaType: 'video', mimeType: 'video/mpeg', extension: 'mpeg' },\n { mediaType: 'video', mimeType: 'video/mpeg', extension: 'mpg' },\n { mediaType: 'video', mimeType: 'video/3gpp', extension: '3gp' },\n { mediaType: 'video', mimeType: 'video/3gpp2', extension: '3g2' },\n { mediaType: 'video', mimeType: 'video/webm', extension: 'webm' },\n { mediaType: 'video', mimeType: 'video/ogg', extension: 'ogg' },\n\n { mediaType: 'file', mimeType: 'text/plain', extension: 'txt' },\n { mediaType: 'file', mimeType: 'text/markdown', extension: 'md' },\n { mediaType: 'file', mimeType: 'text/csv', extension: 'csv' },\n { mediaType: 'file', mimeType: 'application/json', extension: 'json' },\n { mediaType: 'file', mimeType: 'application/msword', extension: 'doc' },\n { mediaType: 'file', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', extension: 'docx' },\n { mediaType: 'file', mimeType: 'application/vnd.ms-excel', extension: 'xls' },\n { mediaType: 'file', mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', extension: 'xlsx' },\n { mediaType: 'file', mimeType: 'application/vnd.ms-powerpoint', extension: 'ppt' },\n { mediaType: 'file', mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', extension: 'pptx' },\n { mediaType: 'file', mimeType: 'application/zip', extension: 'zip' },\n { mediaType: 'file', mimeType: 'application/x-zip-compressed', extension: 'zip' },\n { mediaType: 'file', mimeType: 'application/x-rar-compressed', extension: 'rar' },\n { mediaType: 'file', mimeType: 'application/x-7z-compressed', extension: '7z' },\n\n { mediaType: 'pdf', mimeType: 'application/pdf', extension: 'pdf' },\n];\n\nconst createMediaTypeBuckets = (): Record<MediaCategory, string[]> => ({\n image: [],\n audio: [],\n video: [],\n file: [],\n pdf: [],\n});\n\nexport const MEDIA_TYPE_TO_EXTENSIONS: Record<MediaCategory, string[]> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n if (!acc[fileType.mediaType].includes(fileType.extension)) {\n acc[fileType.mediaType].push(fileType.extension);\n }\n return acc;\n}, createMediaTypeBuckets());\n\nexport const IMAGE_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.image;\nexport const AUDIO_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.audio;\nexport const VIDEO_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.video;\nexport const DOCUMENT_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.file;\nexport const PDF_EXTENSION = 'pdf';\n\nexport const EXTENSION_TO_MIME_TYPE: Record<string, string> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n if (!(fileType.extension in acc)) {\n acc[fileType.extension] = fileType.mimeType;\n }\n return acc;\n}, {} as Record<string, string>);\n\nexport const MIME_TO_MEDIA_TYPE: Record<string, MediaCategory> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n if (!(fileType.mimeType in acc)) {\n acc[fileType.mimeType] = fileType.mediaType;\n }\n return acc;\n}, {} as Record<string, MediaCategory>);\n\n/**\n * Extensions that must never be accepted as media uploads, regardless of declared mimetype or\n * admin-configured mediaTypes allowlist. Checked before any other resolution so a spoofed\n * Content-Type (e.g. application/octet-stream on an .html file) can't bypass this.\n */\nexport const DANGEROUS_EXTENSIONS = new Set([\n 'html', 'htm', 'xhtml', 'svg', 'xml',\n 'js', 'mjs', 'php', 'phtml', 'jsp', 'asp', 'aspx',\n 'exe', 'sh', 'bat', 'cmd', 'ps1', 'jar', 'dll',\n 'msi', 'msix', 'msp', 'com', 'scr', 'vbs', 'vbe', 'wsf', 'wsh', 'hta', 'cpl', 'pif', 'reg',\n]);\n\n\n/**\n * Mimetypes that are never safe to accept, regardless of extension. These render as active\n * content in a browser (svg can carry <script>), so they are rejected even when the filename\n * looks harmless. Kept alongside DANGEROUS_EXTENSIONS so upload paths check both.\n */\nexport const DANGEROUS_MIME_TYPES = new Set([\n 'image/svg+xml', 'text/html', 'application/xhtml+xml',\n]);\n\n/**\n * Percent-decodes a filename, repeating a bounded number of times so a double-encoded payload\n * (`%252e` -> `%2e` -> `.`) survives a single pass through a proxy/framework that already\n * decoded once. Malformed escape sequences are left as-is rather than thrown, since this runs\n * on attacker-controlled input and a literal `%` in a filename (`100%done.pdf`) is legal.\n */\nfunction decodePercentEncodingSafely(name: string): string {\n let decoded = name;\n for (let i = 0; i < 3; i++) {\n let next: string;\n try {\n next = decodeURIComponent(decoded);\n } catch {\n return decoded;\n }\n if (next === decoded) {\n return decoded;\n }\n decoded = next;\n }\n return decoded;\n}\n\n/**\n * Reduces a client-supplied name to the name that will actually be resolved on disk, so\n * extension checks can't be dodged by decorating it. Each step defends a known bypass:\n *\n * - `evil.exe%2e` -> percent-encoding a trailing dot (or anything else) hides the real\n * extension from naive string matching; decode before inspecting it.\n * - `../../evil.exe` -> only the final path segment is the file name.\n * - `evil.exe\\0.png` -> a NUL truncates the name at the syscall/filesystem layer, so\n * anything after it is invisible to the OS but visible to naive\n * validation.\n * - `evil.exe::$DATA` -> NTFS alternate data stream suffix; the file on disk is evil.exe.\n * - `evil.exe.` / `.exe ` -> Windows silently strips trailing dots and spaces when resolving\n * a path, so these land on disk as evil.exe.\n */\nexport function normalizeUploadedFileName(fileName?: string | null): string {\n if (!fileName) {\n return '';\n }\n\n let name = decodePercentEncodingSafely(String(fileName));\n name = name.split(/[/\\\\]/).pop() ?? '';\n\n const nulIndex = name.indexOf('\\0');\n if (nulIndex >= 0) {\n name = name.slice(0, nulIndex);\n }\n\n const streamIndex = name.indexOf(':');\n if (streamIndex >= 0) {\n name = name.slice(0, streamIndex);\n }\n\n return name.replace(/[\\s.]+$/, '');\n}\n\n/**\n * The effective extension - what the OS/browser will treat the file as. Used for media\n * category resolution and inline-safety decisions. Always normalizes first, so\n * `evil.exe.` resolves to `exe` rather than to nothing.\n */\nexport function getLowercaseFileExtension(fileName?: string | null): string | undefined {\n const name = normalizeUploadedFileName(fileName);\n\n const lastDotIndex = name.lastIndexOf('.');\n if (lastDotIndex < 0 || lastDotIndex === name.length - 1) {\n return undefined;\n }\n\n return name.slice(lastDotIndex + 1).toLowerCase();\n}\n\n/**\n * Every dot-delimited segment after the stem, lowercased. The danger check uses this rather\n * than only the effective extension so a dangerous type buried in a double extension\n * (`evil.exe.png`, `shell.php.jpg`) is still caught - several servers and browsers resolve\n * such names by an earlier segment than the last one.\n *\n * The stem is skipped so an innocuous file that merely starts with a keyword (`exe.png`)\n * isn't rejected.\n */\nexport function getLowercaseFileExtensionSegments(fileName?: string | null): string[] {\n const segments = normalizeUploadedFileName(fileName).toLowerCase().split('.');\n return segments.slice(1).filter(segment => segment.length > 0);\n}\n\n/** Minimal shape of an uploaded file, so this module stays free of framework imports. */\nexport interface UploadedFileLike {\n mimetype?: string;\n originalname?: string;\n filename?: string;\n}\n\n/** originalname is the client-supplied name; filename is multer's on-disk name. */\nexport function getUploadedFileName(file?: UploadedFileLike | null): string {\n return file?.originalname ?? file?.filename ?? '';\n}\n\n/**\n * Single predicate for \"must never be stored\". Shared by every upload entry point\n * (MediaFieldCrudManager, chatter attachments) so the rules can't drift between them.\n * Extensions are checked before mimetype so a spoofed Content-Type (e.g.\n * application/octet-stream, or image/png, on an .html file) can't bypass it.\n */\nexport function isDangerousMediaFile(file?: UploadedFileLike | null): boolean {\n const segments = getLowercaseFileExtensionSegments(getUploadedFileName(file));\n if (segments.some(segment => DANGEROUS_EXTENSIONS.has(segment))) {\n return true;\n }\n\n return DANGEROUS_MIME_TYPES.has((file?.mimetype || '').toLowerCase().trim());\n}\n\n/**\n * Single source of truth for extension -> coarse media category. Shared by upload-time\n * validation (MediaFieldCrudManager) and serve-time header hardening (ServeStaticModule), so\n * both agree on which extensions belong to which category and neither drifts out of sync.\n * webm/ogg are shared between AUDIO_EXTENSIONS and VIDEO_EXTENSIONS - last one wins here, they\n * default to 'video' since that's the more common upload case.\n */\nexport const EXT_TO_MEDIA_TYPE: Record<string, MediaCategory> = {\n ...Object.fromEntries(MEDIA_FILE_TYPES.map((fileType) => [fileType.extension, fileType.mediaType])),\n};\n\n/**\n * Extensions safe to serve inline with their natural Content-Type; everything else served from\n * media-files-storage is forced to application/octet-stream + Content-Disposition: attachment.\n */\nexport const INLINE_SAFE_EXTENSIONS = new Set<string>([\n ...IMAGE_EXTENSIONS,\n ...AUDIO_EXTENSIONS,\n ...VIDEO_EXTENSIONS,\n PDF_EXTENSION,\n]);\n"]}
|
|
1
|
+
{"version":3,"file":"media-file-types.js","sourceRoot":"","sources":["../../src/constants/media-file-types.ts"],"names":[],"mappings":"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","sourcesContent":["export type MediaCategory = 'image' | 'audio' | 'video' | 'file' | 'pdf';\n\nexport const MEDIA_CATEGORIES = ['image', 'audio', 'video', 'file', 'pdf'] as const;\n\nexport type MediaFileTypeDefinition = {\n mediaType: MediaCategory;\n mimeType: string;\n extension: string;\n};\n\n// Single source of truth for every supported media extension, its canonical MIME type,\n// and the coarse media category used across validation and UI metadata.\nexport const MEDIA_FILE_TYPES: MediaFileTypeDefinition[] = [\n { mediaType: 'image', mimeType: 'image/png', extension: 'png' },\n { mediaType: 'image', mimeType: 'image/jpeg', extension: 'jpg' },\n { mediaType: 'image', mimeType: 'image/jpg', extension: 'jpg' },\n { mediaType: 'image', mimeType: 'image/jpeg', extension: 'jpeg' },\n { mediaType: 'image', mimeType: 'image/webp', extension: 'webp' },\n { mediaType: 'image', mimeType: 'image/gif', extension: 'gif' },\n { mediaType: 'image', mimeType: 'image/bmp', extension: 'bmp' },\n { mediaType: 'image', mimeType: 'image/tiff', extension: 'tiff' },\n { mediaType: 'image', mimeType: 'image/heic', extension: 'heic' },\n { mediaType: 'image', mimeType: 'image/heif', extension: 'heif' },\n\n { mediaType: 'audio', mimeType: 'audio/mpeg', extension: 'mp3' },\n { mediaType: 'audio', mimeType: 'audio/mp3', extension: 'mp3' },\n { mediaType: 'audio', mimeType: 'audio/wav', extension: 'wav' },\n { mediaType: 'audio', mimeType: 'audio/x-wav', extension: 'wav' },\n { mediaType: 'audio', mimeType: 'audio/ogg', extension: 'ogg' },\n { mediaType: 'audio', mimeType: 'audio/aac', extension: 'aac' },\n { mediaType: 'audio', mimeType: 'audio/mp4', extension: 'm4a' },\n { mediaType: 'audio', mimeType: 'audio/x-m4a', extension: 'm4a' },\n { mediaType: 'audio', mimeType: 'audio/flac', extension: 'flac' },\n { mediaType: 'audio', mimeType: 'audio/webm', extension: 'webm' },\n\n { mediaType: 'video', mimeType: 'video/mp4', extension: 'mp4' },\n { mediaType: 'video', mimeType: 'video/quicktime', extension: 'mov' },\n { mediaType: 'video', mimeType: 'video/x-msvideo', extension: 'avi' },\n { mediaType: 'video', mimeType: 'video/x-matroska', extension: 'mkv' },\n { mediaType: 'video', mimeType: 'video/mpeg', extension: 'mpeg' },\n { mediaType: 'video', mimeType: 'video/mpeg', extension: 'mpg' },\n { mediaType: 'video', mimeType: 'video/3gpp', extension: '3gp' },\n { mediaType: 'video', mimeType: 'video/3gpp2', extension: '3g2' },\n { mediaType: 'video', mimeType: 'video/webm', extension: 'webm' },\n { mediaType: 'video', mimeType: 'video/ogg', extension: 'ogg' },\n\n { mediaType: 'file', mimeType: 'text/plain', extension: 'txt' },\n { mediaType: 'file', mimeType: 'text/markdown', extension: 'md' },\n { mediaType: 'file', mimeType: 'text/csv', extension: 'csv' },\n { mediaType: 'file', mimeType: 'application/json', extension: 'json' },\n { mediaType: 'file', mimeType: 'application/msword', extension: 'doc' },\n { mediaType: 'file', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', extension: 'docx' },\n { mediaType: 'file', mimeType: 'application/vnd.ms-excel', extension: 'xls' },\n { mediaType: 'file', mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', extension: 'xlsx' },\n { mediaType: 'file', mimeType: 'application/vnd.ms-powerpoint', extension: 'ppt' },\n { mediaType: 'file', mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', extension: 'pptx' },\n { mediaType: 'file', mimeType: 'application/zip', extension: 'zip' },\n { mediaType: 'file', mimeType: 'application/x-zip-compressed', extension: 'zip' },\n { mediaType: 'file', mimeType: 'application/x-rar-compressed', extension: 'rar' },\n { mediaType: 'file', mimeType: 'application/x-7z-compressed', extension: '7z' },\n\n { mediaType: 'pdf', mimeType: 'application/pdf', extension: 'pdf' },\n];\n\nconst createMediaTypeBuckets = (): Record<MediaCategory, string[]> => ({\n image: [],\n audio: [],\n video: [],\n file: [],\n pdf: [],\n});\n\nexport const MEDIA_TYPE_TO_EXTENSIONS: Record<MediaCategory, string[]> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n if (!acc[fileType.mediaType].includes(fileType.extension)) {\n acc[fileType.mediaType].push(fileType.extension);\n }\n return acc;\n}, createMediaTypeBuckets());\n\nexport const IMAGE_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.image;\nexport const AUDIO_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.audio;\nexport const VIDEO_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.video;\nexport const DOCUMENT_EXTENSIONS = MEDIA_TYPE_TO_EXTENSIONS.file;\nexport const PDF_EXTENSION = 'pdf';\n\nexport const EXTENSION_TO_MIME_TYPE: Record<string, string> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n if (!(fileType.extension in acc)) {\n acc[fileType.extension] = fileType.mimeType;\n }\n return acc;\n}, {} as Record<string, string>);\n\nexport const MIME_TO_MEDIA_TYPE: Record<string, MediaCategory> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n if (!(fileType.mimeType in acc)) {\n acc[fileType.mimeType] = fileType.mediaType;\n }\n return acc;\n}, {} as Record<string, MediaCategory>);\n\n/**\n * Extensions that must never be accepted as media uploads, regardless of declared mimetype or\n * admin-configured mediaTypes allowlist. Checked before any other resolution so a spoofed\n * Content-Type (e.g. application/octet-stream on an .html file) can't bypass this.\n */\nexport const DANGEROUS_EXTENSIONS = new Set([\n 'html', 'htm', 'xhtml', 'svg', 'xml',\n 'js', 'mjs', 'php', 'phtml', 'jsp', 'asp', 'aspx',\n 'exe', 'sh', 'bat', 'cmd', 'ps1', 'jar', 'dll',\n 'msi', 'msix', 'msp', 'com', 'scr', 'vbs', 'vbe', 'wsf', 'wsh', 'hta', 'cpl', 'pif', 'reg',\n]);\n\n\n/**\n * Mimetypes that are never safe to accept, regardless of extension. These render as active\n * content in a browser (svg can carry <script>), so they are rejected even when the filename\n * looks harmless. Kept alongside DANGEROUS_EXTENSIONS so upload paths check both.\n */\nexport const DANGEROUS_MIME_TYPES = new Set([\n 'image/svg+xml', 'text/html', 'application/xhtml+xml',\n]);\n\n/**\n * Percent-decodes a filename, repeating a bounded number of times so a double-encoded payload\n * (`%252e` -> `%2e` -> `.`) survives a single pass through a proxy/framework that already\n * decoded once. Malformed escape sequences are left as-is rather than thrown, since this runs\n * on attacker-controlled input and a literal `%` in a filename (`100%done.pdf`) is legal.\n */\nfunction decodePercentEncodingSafely(name: string): string {\n let decoded = name;\n for (let i = 0; i < 3; i++) {\n let next: string;\n try {\n next = decodeURIComponent(decoded);\n } catch {\n return decoded;\n }\n if (next === decoded) {\n return decoded;\n }\n decoded = next;\n }\n return decoded;\n}\n\n/**\n * Reduces a client-supplied name to the name that will actually be resolved on disk, so\n * extension checks can't be dodged by decorating it. Each step defends a known bypass:\n *\n * - `evil.exe%2e` -> percent-encoding a trailing dot (or anything else) hides the real\n * extension from naive string matching; decode before inspecting it.\n * - `../../evil.exe` -> only the final path segment is the file name.\n * - `evil.exe\\0.png` -> a NUL truncates the name at the syscall/filesystem layer, so\n * anything after it is invisible to the OS but visible to naive\n * validation.\n * - `evil.exe::$DATA` -> NTFS alternate data stream suffix; the file on disk is evil.exe.\n * - `evil.exe.` / `.exe ` -> Windows silently strips trailing dots and spaces when resolving\n * a path, so these land on disk as evil.exe.\n */\nexport function normalizeUploadedFileName(fileName?: string | null): string {\n if (!fileName) {\n return '';\n }\n\n let name = decodePercentEncodingSafely(String(fileName));\n name = name.split(/[/\\\\]/).pop() ?? '';\n\n const nulIndex = name.indexOf('\\0');\n if (nulIndex >= 0) {\n name = name.slice(0, nulIndex);\n }\n\n const streamIndex = name.indexOf(':');\n if (streamIndex >= 0) {\n name = name.slice(0, streamIndex);\n }\n\n return name.replace(/[\\s.]+$/, '');\n}\n\n/**\n * The effective extension - what the OS/browser will treat the file as. Used for media\n * category resolution and inline-safety decisions. Always normalizes first, so\n * `evil.exe.` resolves to `exe` rather than to nothing.\n */\nexport function getLowercaseFileExtension(fileName?: string | null): string | undefined {\n const name = normalizeUploadedFileName(fileName);\n\n const lastDotIndex = name.lastIndexOf('.');\n if (lastDotIndex < 0 || lastDotIndex === name.length - 1) {\n return undefined;\n }\n\n return name.slice(lastDotIndex + 1).toLowerCase();\n}\n\n/**\n * Every dot-delimited segment after the stem, lowercased. The danger check uses this rather\n * than only the effective extension so a dangerous type buried in a double extension\n * (`evil.exe.png`, `shell.php.jpg`) is still caught - several servers and browsers resolve\n * such names by an earlier segment than the last one.\n *\n * The stem is skipped so an innocuous file that merely starts with a keyword (`exe.png`)\n * isn't rejected.\n */\nexport function getLowercaseFileExtensionSegments(fileName?: string | null): string[] {\n const segments = normalizeUploadedFileName(fileName).toLowerCase().split('.');\n return segments.slice(1).filter(segment => segment.length > 0);\n}\n\n/** Minimal shape of an uploaded file, so this module stays free of framework imports. */\nexport interface UploadedFileLike {\n mimetype?: string;\n originalname?: string;\n filename?: string;\n}\n\n/** originalname is the client-supplied name; filename is multer's on-disk name. */\nexport function getUploadedFileName(file?: UploadedFileLike | null): string {\n return file?.originalname ?? file?.filename ?? '';\n}\n\n/**\n * Single predicate for \"must never be stored\". Shared by every upload entry point\n * (MediaFieldCrudManager, chatter attachments) so the rules can't drift between them.\n * Extensions are checked before mimetype so a spoofed Content-Type (e.g.\n * application/octet-stream, or image/png, on an .html file) can't bypass it.\n */\nexport function isDangerousMediaFile(file?: UploadedFileLike | null): boolean {\n const segments = getLowercaseFileExtensionSegments(getUploadedFileName(file));\n if (segments.some(segment => DANGEROUS_EXTENSIONS.has(segment))) {\n return true;\n }\n\n return DANGEROUS_MIME_TYPES.has((file?.mimetype || '').toLowerCase().trim());\n}\n\n/**\n * Single source of truth for extension -> coarse media category. Shared by upload-time\n * validation (MediaFieldCrudManager) and serve-time header hardening (ServeStaticModule), so\n * both agree on which extensions belong to which category and neither drifts out of sync.\n * Some extensions, such as ogg and webm, are valid for more than one category.\n * Keep every matching category instead of allowing a later entry to overwrite an earlier one.\n */\n// Example: { ogg: ['audio', 'video'], mp3: ['audio'], jpg: ['image'] }\nexport const EXT_TO_MEDIA_TYPES: Record<string, MediaCategory[]> = MEDIA_FILE_TYPES.reduce((acc, fileType) => {\n // Create an empty category list the first time we see an extension.\n if (!acc[fileType.extension]) {\n acc[fileType.extension] = [];\n }\n\n // Add the category for this extension, but do not add duplicates.\n if (!acc[fileType.extension].includes(fileType.mediaType)) {\n acc[fileType.extension].push(fileType.mediaType);\n }\n\n // Return the map so the next media definition can be added to it.\n return acc;\n}, {} as Record<string, MediaCategory[]>);\n\nexport const EXT_TO_MEDIA_TYPE: Record<string, MediaCategory> = {\n ...Object.fromEntries(MEDIA_FILE_TYPES.map((fileType) => [fileType.extension, fileType.mediaType])),\n};\n\n/**\n * Extensions safe to serve inline with their natural Content-Type; everything else served from\n * media-files-storage is forced to application/octet-stream + Content-Disposition: attachment.\n */\nexport const INLINE_SAFE_EXTENSIONS = new Set<string>([\n ...IMAGE_EXTENSIONS,\n ...AUDIO_EXTENSIONS,\n ...VIDEO_EXTENSIONS,\n PDF_EXTENSION,\n]);\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaFieldCrudManager.d.ts","sourceRoot":"","sources":["../../../src/helpers/field-crud-managers/MediaFieldCrudManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGnE,oBAAY,cAAc;IACtB,WAAW,gBAAgB;IAC3B,aAAa,kBAAkB;CAClC;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACvC,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC1C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;CACxC;AAID,qBAAa,qBAAsB,YAAW,gBAAgB;IAE9C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAGvD,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"MediaFieldCrudManager.d.ts","sourceRoot":"","sources":["../../../src/helpers/field-crud-managers/MediaFieldCrudManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGnE,oBAAY,cAAc;IACtB,WAAW,gBAAgB;IAC3B,aAAa,kBAAkB;CAClC;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACvC,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC1C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,sBAAsB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;CACxC;AAID,qBAAa,qBAAsB,YAAW,gBAAgB;IAE9C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAGvD,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,gBAAgB;IAuCxB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,eAAe,EAAE;IAKxE,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,yBAAyB;IAmBjC,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,qBAAqB;IAI7B,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;CAKpC"}
|
|
@@ -19,17 +19,17 @@ class MediaFieldCrudManager {
|
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
21
|
const ext = (0, media_file_types_1.getLowercaseFileExtension)((0, media_file_types_1.getUploadedFileName)(file));
|
|
22
|
-
const
|
|
23
|
-
if (!
|
|
22
|
+
const extToMediaTypes = ext ? media_file_types_1.EXT_TO_MEDIA_TYPES[ext] : undefined;
|
|
23
|
+
if (!extToMediaTypes || extToMediaTypes.length === 0) {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
const mt = (file.mimetype || '').toLowerCase().trim();
|
|
27
|
-
const
|
|
27
|
+
const mimeToMediaTypes = media_file_types_1.MIME_TO_MEDIA_TYPE[mt]
|
|
28
28
|
?? (mt.startsWith('image/') ? 'image' : mt.startsWith('audio/') ? 'audio' : mt.startsWith('video/') ? 'video' : undefined);
|
|
29
|
-
if (
|
|
29
|
+
if (mimeToMediaTypes && !extToMediaTypes.includes(mimeToMediaTypes)) {
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return mimeToMediaTypes || extToMediaTypes[0];
|
|
33
33
|
}
|
|
34
34
|
validate(dto, files) {
|
|
35
35
|
const fieldFiles = files.filter(file => file.fieldname === this.options.fieldName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaFieldCrudManager.js","sourceRoot":"","sources":["../../../src/helpers/field-crud-managers/MediaFieldCrudManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { FieldCrudManager, ValidationError } from \"src/interfaces\";\nimport { EXT_TO_MEDIA_TYPE, getLowercaseFileExtension, getUploadedFileName, isDangerousMediaFile, MediaCategory, MIME_TO_MEDIA_TYPE } from \"src/constants/media-file-types\";\n\nexport enum SolidMediaType {\n mediaSingle = 'mediaSingle',\n mediaMultiple = 'mediaMultiple'\n}\n\nexport interface MediaFieldOptions {\n type: SolidMediaType;\n required: boolean | undefined | null;\n fieldName: string | undefined | null;\n fieldLabel?: string | undefined | null;\n mediaMaxSizeKb: number | undefined | null;\n mediaTypes: string[];\n mediaAllowedExtensions?: string[] | null;\n isUpdate: boolean | undefined | null;\n}\n\ntype MediaType = MediaCategory;\n\nexport class MediaFieldCrudManager implements FieldCrudManager {\n\n constructor(private readonly options: MediaFieldOptions) {\n }\n\n private getFieldLabel(): string {\n return this.options.fieldLabel || this.options.fieldName || 'File';\n }\n\n private resolveMediaType(file: Express.Multer.File): MediaType | null {\n // Belt-and-braces: dangerous files are already rejected outright by\n // validateDangerousFiles, but never let one resolve to a usable category.\n if (isDangerousMediaFile(file)) {\n return null;\n }\n\n // Extension is authoritative: a declared mimetype is fully attacker-controlled and\n // proved insufficient on its own (a .msi uploaded as image/png previously resolved to\n // 'image', since the mimetype match short-circuited before the extension was ever\n // checked). The extension must independently resolve to a known-safe category before\n // anything is accepted - not merely \"not on the denylist\".\n const ext = getLowercaseFileExtension(getUploadedFileName(file));\n const extType = ext ? EXT_TO_MEDIA_TYPE[ext] : undefined;\n if (!extType) {\n return null;\n }\n\n // The mimetype may only confirm or veto what the extension already established, never\n // grant a category on its own. A mimetype that doesn't resolve to anything (e.g.\n // application/octet-stream) has no opinion and is treated as agreeing.\n const mt = (file.mimetype || '').toLowerCase().trim();\n const mimeType = MIME_TO_MEDIA_TYPE[mt]\n ?? (mt.startsWith('image/') ? 'image' : mt.startsWith('audio/') ? 'audio' : mt.startsWith('video/') ? 'video' : undefined);\n if (mimeType && mimeType !== extType) {\n return null;\n }\n\n return extType;\n }\n\n validate(dto: any, files: Array<Express.Multer.File>): ValidationError[] {\n const fieldFiles = files.filter(file => file.fieldname === this.options.fieldName);\n return this.applyValidations(fieldFiles);\n }\n\n private applyValidations(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n // A dangerous upload is never acceptable, regardless of admin config, so this is the\n // only check that sits outside validateCommon() and can't be skipped either way below.\n const dangerousFileErrors = this.validateDangerousFiles(fieldFiles);\n\n switch (this.options.type) {\n case SolidMediaType.mediaSingle:\n return [...dangerousFileErrors, ...this.validateMediaSingle(fieldFiles)];\n case SolidMediaType.mediaMultiple:\n return [...dangerousFileErrors, ...this.validateMediaMultiple(fieldFiles)];\n default:\n return dangerousFileErrors;\n }\n }\n\n private validateDangerousFiles(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n return fieldFiles\n .filter(fieldFile => isDangerousMediaFile(fieldFile))\n .map(fieldFile => ({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} file type not allowed. ` +\n `${getUploadedFileName(fieldFile) || 'File'} is a potentially dangerous file type.`\n }));\n }\n\n // Every optional, admin-configured, per-field check - no-ops when its setting is unset -\n // shared between mediaSingle and mediaMultiple so neither can drift out of sync with it.\n private validateCommon(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n return [\n ...this.validateRequired(fieldFiles),\n ...this.validateSize(fieldFiles),\n ...this.validateTypes(fieldFiles),\n ...this.validateAllowedExtensions(fieldFiles),\n ];\n }\n\n private validateRequired(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors: ValidationError[] = [];\n if (!this.options.isUpdate && this.options.required && fieldFiles.length === 0) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} is required`\n });\n }\n return errors;\n }\n\n private validateSize(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors: ValidationError[] = [];\n if (this.options.mediaMaxSizeKb) {\n for (let i = 0; i < fieldFiles.length; i++) {\n const fieldFile = fieldFiles[i];\n const fieldFileSizeInBytes = Math.ceil(fieldFile.size / 1024);\n if (fieldFileSizeInBytes > this.options.mediaMaxSizeKb) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} with size ${fieldFileSizeInBytes} KB exceeds max size limit of ${this.options.mediaMaxSizeKb} KB`\n });\n }\n }\n }\n return errors;\n }\n\n private validateTypes(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors: ValidationError[] = [];\n if (this.options.mediaTypes && this.options.mediaTypes.length > 0) {\n const allowedFileTypes = this.options.mediaTypes as MediaType[];\n\n for (let i = 0; i < fieldFiles.length; i++) {\n const fieldFile = fieldFiles[i];\n\n const resolvedType = this.resolveMediaType(fieldFile);\n if (!resolvedType || !allowedFileTypes.includes(resolvedType)) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} file type not allowed. ` +\n `Allowed: ${allowedFileTypes.join(', ')}. ` +\n `Received mimetype: ${fieldFile.mimetype}${resolvedType ? ` (mapped to ${resolvedType})` : ''}`\n });\n }\n }\n }\n return errors;\n }\n\n private validateAllowedExtensions(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const allowed = this.options.mediaAllowedExtensions;\n if (!allowed || allowed.length === 0) {\n return [];\n }\n\n const normalizedAllowed = new Set(allowed.map(ext => ext.toLowerCase().replace(/^\\./, '')));\n return fieldFiles\n .filter(fieldFile => {\n const ext = getLowercaseFileExtension(getUploadedFileName(fieldFile));\n return !ext || !normalizedAllowed.has(ext);\n })\n .map(fieldFile => ({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} file type not allowed. ` +\n `Allowed extensions: ${[...normalizedAllowed].join(', ')}.`\n }));\n }\n\n private validateMediaSingle(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors = this.validateCommon(fieldFiles);\n if (fieldFiles.length > 1) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} must be a single file`\n });\n }\n return errors;\n }\n\n private validateMediaMultiple(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n return this.validateCommon(fieldFiles);\n }\n\n transformForCreate(dto: any): any {\n return dto;\n }\n\n // Validation to be applied\n}\n"]}
|
|
1
|
+
{"version":3,"file":"MediaFieldCrudManager.js","sourceRoot":"","sources":["../../../src/helpers/field-crud-managers/MediaFieldCrudManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { FieldCrudManager, ValidationError } from \"src/interfaces\";\nimport { EXT_TO_MEDIA_TYPES, getLowercaseFileExtension, getUploadedFileName, isDangerousMediaFile, MediaCategory, MIME_TO_MEDIA_TYPE } from \"src/constants/media-file-types\";\n\nexport enum SolidMediaType {\n mediaSingle = 'mediaSingle',\n mediaMultiple = 'mediaMultiple'\n}\n\nexport interface MediaFieldOptions {\n type: SolidMediaType;\n required: boolean | undefined | null;\n fieldName: string | undefined | null;\n fieldLabel?: string | undefined | null;\n mediaMaxSizeKb: number | undefined | null;\n mediaTypes: string[];\n mediaAllowedExtensions?: string[] | null;\n isUpdate: boolean | undefined | null;\n}\n\ntype MediaType = MediaCategory;\n\nexport class MediaFieldCrudManager implements FieldCrudManager {\n\n constructor(private readonly options: MediaFieldOptions) {\n }\n\n private getFieldLabel(): string {\n return this.options.fieldLabel || this.options.fieldName || 'File';\n }\n\n private resolveMediaType(file: Express.Multer.File): MediaType | null {\n // Belt-and-braces: dangerous files are already rejected outright by\n // validateDangerousFiles, but never let one resolve to a usable category.\n if (isDangerousMediaFile(file)) {\n return null;\n }\n\n // Extension is authoritative: a declared mimetype is fully attacker-controlled and\n // proved insufficient on its own (a .msi uploaded as image/png previously resolved to\n // 'image', since the mimetype match short-circuited before the extension was ever\n // checked). The extension must independently resolve to a known-safe category before\n // anything is accepted - not merely \"not on the denylist\".\n // Read the file extension from the uploaded filename, for example \"song.ogg\" -> \"ogg\".\n const ext = getLowercaseFileExtension(getUploadedFileName(file));\n\n // Find every media category that supports this extension.\n // An extension can belong to multiple categories, for example .ogg can be audio or video.\n const extToMediaTypes = ext ? EXT_TO_MEDIA_TYPES[ext] : undefined;\n\n // Reject files whose extension is not part of the supported media definitions.\n if (!extToMediaTypes || extToMediaTypes.length === 0) {\n return null;\n }\n\n // The mimetype may only confirm or veto what the extension already established, never\n // grant a category on its own. A mimetype that doesn't resolve to anything (e.g.\n // application/octet-stream) has no opinion and is treated as agreeing.\n const mt = (file.mimetype || '').toLowerCase().trim();\n const mimeToMediaTypes = MIME_TO_MEDIA_TYPE[mt]\n ?? (mt.startsWith('image/') ? 'image' : mt.startsWith('audio/') ? 'audio' : mt.startsWith('video/') ? 'video' : undefined);\n // When the extension is shared, the MIME type identifies the actual category:\n // audio/ogg becomes audio, while video/ogg becomes video.\n if (mimeToMediaTypes && !extToMediaTypes.includes(mimeToMediaTypes)) {\n return null;\n }\n\n return mimeToMediaTypes || extToMediaTypes[0];\n }\n\n validate(dto: any, files: Array<Express.Multer.File>): ValidationError[] {\n const fieldFiles = files.filter(file => file.fieldname === this.options.fieldName);\n return this.applyValidations(fieldFiles);\n }\n\n private applyValidations(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n // A dangerous upload is never acceptable, regardless of admin config, so this is the\n // only check that sits outside validateCommon() and can't be skipped either way below.\n const dangerousFileErrors = this.validateDangerousFiles(fieldFiles);\n\n switch (this.options.type) {\n case SolidMediaType.mediaSingle:\n return [...dangerousFileErrors, ...this.validateMediaSingle(fieldFiles)];\n case SolidMediaType.mediaMultiple:\n return [...dangerousFileErrors, ...this.validateMediaMultiple(fieldFiles)];\n default:\n return dangerousFileErrors;\n }\n }\n\n private validateDangerousFiles(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n return fieldFiles\n .filter(fieldFile => isDangerousMediaFile(fieldFile))\n .map(fieldFile => ({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} file type not allowed. ` +\n `${getUploadedFileName(fieldFile) || 'File'} is a potentially dangerous file type.`\n }));\n }\n\n // Every optional, admin-configured, per-field check - no-ops when its setting is unset -\n // shared between mediaSingle and mediaMultiple so neither can drift out of sync with it.\n private validateCommon(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n return [\n ...this.validateRequired(fieldFiles),\n ...this.validateSize(fieldFiles),\n ...this.validateTypes(fieldFiles),\n ...this.validateAllowedExtensions(fieldFiles),\n ];\n }\n\n private validateRequired(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors: ValidationError[] = [];\n if (!this.options.isUpdate && this.options.required && fieldFiles.length === 0) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} is required`\n });\n }\n return errors;\n }\n\n private validateSize(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors: ValidationError[] = [];\n if (this.options.mediaMaxSizeKb) {\n for (let i = 0; i < fieldFiles.length; i++) {\n const fieldFile = fieldFiles[i];\n const fieldFileSizeInBytes = Math.ceil(fieldFile.size / 1024);\n if (fieldFileSizeInBytes > this.options.mediaMaxSizeKb) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} with size ${fieldFileSizeInBytes} KB exceeds max size limit of ${this.options.mediaMaxSizeKb} KB`\n });\n }\n }\n }\n return errors;\n }\n\n private validateTypes(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors: ValidationError[] = [];\n if (this.options.mediaTypes && this.options.mediaTypes.length > 0) {\n const allowedFileTypes = this.options.mediaTypes as MediaType[];\n\n for (let i = 0; i < fieldFiles.length; i++) {\n const fieldFile = fieldFiles[i];\n\n const resolvedType = this.resolveMediaType(fieldFile);\n if (!resolvedType || !allowedFileTypes.includes(resolvedType)) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} file type not allowed. ` +\n `Allowed: ${allowedFileTypes.join(', ')}. ` +\n `Received mimetype: ${fieldFile.mimetype}${resolvedType ? ` (mapped to ${resolvedType})` : ''}`\n });\n }\n }\n }\n return errors;\n }\n\n private validateAllowedExtensions(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const allowed = this.options.mediaAllowedExtensions;\n if (!allowed || allowed.length === 0) {\n return [];\n }\n\n const normalizedAllowed = new Set(allowed.map(ext => ext.toLowerCase().replace(/^\\./, '')));\n return fieldFiles\n .filter(fieldFile => {\n const ext = getLowercaseFileExtension(getUploadedFileName(fieldFile));\n return !ext || !normalizedAllowed.has(ext);\n })\n .map(fieldFile => ({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} file type not allowed. ` +\n `Allowed extensions: ${[...normalizedAllowed].join(', ')}.`\n }));\n }\n\n private validateMediaSingle(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n const errors = this.validateCommon(fieldFiles);\n if (fieldFiles.length > 1) {\n errors.push({\n field: this.options.fieldName,\n error: `${this.getFieldLabel()} must be a single file`\n });\n }\n return errors;\n }\n\n private validateMediaMultiple(fieldFiles: Array<Express.Multer.File>): ValidationError[] {\n return this.validateCommon(fieldFiles);\n }\n\n transformForCreate(dto: any): any {\n return dto;\n }\n\n // Validation to be applied\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-helper.d.ts","sourceRoot":"","sources":["../../src/helpers/user-helper.ts"],"names":[],"mappings":"AAAA,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"user-helper.d.ts","sourceRoot":"","sources":["../../src/helpers/user-helper.ts"],"names":[],"mappings":"AAAA,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CA8ChD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-helper.js","sourceRoot":"","sources":["../../src/helpers/user-helper.ts"],"names":[],"mappings":";;AAAA,
|
|
1
|
+
{"version":3,"file":"user-helper.js","sourceRoot":"","sources":["../../src/helpers/user-helper.ts"],"names":[],"mappings":";;AAAA,sDA8CC;AA9CD,SAAgB,qBAAqB;IACnC,OAAO;QACL,gBAAgB;QAChB,uBAAuB;QACvB,YAAY;QACZ,YAAY;QACZ,kBAAkB;QAClB,mBAAmB;QACnB,UAAU;QACV,QAAQ;QACR,qBAAqB;QACrB,qBAAqB;QACrB,4BAA4B;QAC5B,mBAAmB;QACnB,sBAAsB;QACtB,qBAAqB;QACrB,YAAY;QACZ,sBAAsB;QACtB,aAAa;QACb,yBAAyB;QACzB,qCAAqC;QACrC,4BAA4B;QAC5B,wCAAwC;QACxC,2BAA2B;QAC3B,mCAAmC;QACnC,4CAA4C;QAC5C,+BAA+B;QAC/B,sCAAsC;QACtC,+CAA+C;QAC/C,gCAAgC;QAChC,uCAAuC;QACvC,gDAAgD;QAChD,wBAAwB;QACxB,+BAA+B;QAC/B,wCAAwC;QACxC,yBAAyB;QACzB,gCAAgC;QAChC,yCAAyC;QACzC,eAAe;QACf,OAAO;QACP,UAAU;QACV,cAAc;QACd,kBAAkB;QAClB,qBAAqB;QACrB,yBAAyB;KAC1B,CAAC;AACJ,CAAC","sourcesContent":["export function getUserExcludedFields(): string[] {\n return [\n \"passwordScheme\",\n \"passwordSchemeVersion\",\n \"rehashedAt\",\n \"accessCode\",\n \"googleAccessCode\",\n \"googleAccessToken\",\n \"googleId\",\n \"active\",\n \"forcePasswordChange\",\n \"failedLoginAttempts\",\n \"isAllowedToGenerateApiKeys\",\n \"lastLoginProvider\",\n \"googleProfilePicture\",\n \"facebookAccessToken\",\n \"facebookId\",\n \"microsoftAccessToken\",\n \"microsoftId\",\n \"microsoftProfilePicture\",\n \"microsoftActiveDirectoryAccessToken\",\n \"microsoftActiveDirectoryId\",\n \"microsoftActiveDirectoryProfilePicture\",\n \"forgotPasswordConfirmedAt\",\n \"verificationTokenOnForgotPassword\",\n \"verificationTokenOnForgotPasswordExpiresAt\",\n \"emailVerifiedOnRegistrationAt\",\n \"emailVerificationTokenOnRegistration\",\n \"emailVerificationTokenOnRegistrationExpiresAt\",\n \"mobileVerifiedOnRegistrationAt\",\n \"mobileVerificationTokenOnRegistration\",\n \"mobileVerificationTokenOnRegistrationExpiresAt\",\n \"emailVerifiedOnLoginAt\",\n \"emailVerificationTokenOnLogin\",\n \"emailVerificationTokenOnLoginExpiresAt\",\n \"mobileVerifiedOnLoginAt\",\n \"mobileVerificationTokenOnLogin\",\n \"mobileVerificationTokenOnLoginExpiresAt\",\n \"customPayload\",\n \"roles\",\n \"rolesIds\",\n \"rolesCommand\",\n \"userViewMetadata\",\n \"userViewMetadataIds\",\n \"userViewMetadataCommand\",\n ];\n}\n"]}
|
|
@@ -10567,7 +10567,6 @@
|
|
|
10567
10567
|
"enableGlobalSearch": true,
|
|
10568
10568
|
"create": false,
|
|
10569
10569
|
"edit": false,
|
|
10570
|
-
"delete": false,
|
|
10571
10570
|
"import": false,
|
|
10572
10571
|
"export": false,
|
|
10573
10572
|
"allowedViews": [
|
|
@@ -14959,7 +14958,6 @@
|
|
|
14959
14958
|
"enableGlobalSearch": true,
|
|
14960
14959
|
"create": false,
|
|
14961
14960
|
"edit": false,
|
|
14962
|
-
"delete": false,
|
|
14963
14961
|
"import": false,
|
|
14964
14962
|
"export": false,
|
|
14965
14963
|
"allowedViews": [
|
|
@@ -87,6 +87,7 @@ export declare class ImportTransactionService extends CRUDService<ImportTransact
|
|
|
87
87
|
private insertRecord;
|
|
88
88
|
private getModelService;
|
|
89
89
|
private convertImportedRecordToDto;
|
|
90
|
+
private parseImportDefaultValue;
|
|
90
91
|
private populateDtoForACell;
|
|
91
92
|
private populateDtoForSelectionValues;
|
|
92
93
|
private populateDtoForBoolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-transaction.service.d.ts","sourceRoot":"","sources":["../../src/services/import-transaction.service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"import-transaction.service.d.ts","sourceRoot":"","sources":["../../src/services/import-transaction.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -436,8 +436,29 @@ let ImportTransactionService = ImportTransactionService_1 = class ImportTransact
|
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
+
for (const field of modelMetadataWithFields.fields) {
|
|
440
|
+
if (field.required &&
|
|
441
|
+
field.defaultValue !== null &&
|
|
442
|
+
field.defaultValue !== undefined &&
|
|
443
|
+
!(field.name in dtoRecord)) {
|
|
444
|
+
dtoRecord[field.name] = this.parseImportDefaultValue(field);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
439
447
|
return dtoRecord;
|
|
440
448
|
}
|
|
449
|
+
parseImportDefaultValue(field) {
|
|
450
|
+
const defaultValue = field.defaultValue;
|
|
451
|
+
switch (field.type) {
|
|
452
|
+
case create_field_metadata_dto_1.SolidFieldType.boolean:
|
|
453
|
+
return ['true', '1', 'yes'].includes(String(defaultValue).toLowerCase());
|
|
454
|
+
case create_field_metadata_dto_1.SolidFieldType.int:
|
|
455
|
+
case create_field_metadata_dto_1.SolidFieldType.bigint:
|
|
456
|
+
case create_field_metadata_dto_1.SolidFieldType.decimal:
|
|
457
|
+
return Number(defaultValue);
|
|
458
|
+
default:
|
|
459
|
+
return defaultValue;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
441
462
|
async populateDtoForACell(dtoRecord, fieldMetadata, record, key) {
|
|
442
463
|
const fieldType = fieldMetadata.type;
|
|
443
464
|
switch (fieldType) {
|