@solidxai/core 0.1.13-beta.14 → 0.1.13-beta.16

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 (38) hide show
  1. package/CHANGELOG.md +1150 -0
  2. package/dist/constants/media-file-types.d.ts +2 -0
  3. package/dist/constants/media-file-types.d.ts.map +1 -1
  4. package/dist/constants/media-file-types.js +26 -7
  5. package/dist/constants/media-file-types.js.map +1 -1
  6. package/dist/filters/http-exception.filter.d.ts.map +1 -1
  7. package/dist/filters/http-exception.filter.js +5 -1
  8. package/dist/filters/http-exception.filter.js.map +1 -1
  9. package/dist/helpers/bootstrap.helper.d.ts +3 -1
  10. package/dist/helpers/bootstrap.helper.d.ts.map +1 -1
  11. package/dist/helpers/bootstrap.helper.js +6 -2
  12. package/dist/helpers/bootstrap.helper.js.map +1 -1
  13. package/dist/helpers/file-download.helper.d.ts.map +1 -1
  14. package/dist/helpers/file-download.helper.js +6 -1
  15. package/dist/helpers/file-download.helper.js.map +1 -1
  16. package/dist/services/media-storage.utils.d.ts +1 -0
  17. package/dist/services/media-storage.utils.d.ts.map +1 -1
  18. package/dist/services/media-storage.utils.js +12 -1
  19. package/dist/services/media-storage.utils.js.map +1 -1
  20. package/dist/services/module-metadata.service.d.ts.map +1 -1
  21. package/dist/services/module-metadata.service.js +2 -1
  22. package/dist/services/module-metadata.service.js.map +1 -1
  23. package/dist/services/setting.service.d.ts.map +1 -1
  24. package/dist/services/setting.service.js +2 -1
  25. package/dist/services/setting.service.js.map +1 -1
  26. package/dist/solid-core.module.d.ts +1 -3
  27. package/dist/solid-core.module.d.ts.map +1 -1
  28. package/dist/solid-core.module.js +0 -39
  29. package/dist/solid-core.module.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/constants/media-file-types.ts +58 -9
  32. package/src/filters/http-exception.filter.ts +11 -1
  33. package/src/helpers/bootstrap.helper.ts +22 -3
  34. package/src/helpers/file-download.helper.ts +15 -1
  35. package/src/services/media-storage.utils.ts +26 -1
  36. package/src/services/module-metadata.service.ts +3 -1
  37. package/src/services/setting.service.ts +3 -1
  38. package/src/solid-core.module.ts +5 -12
@@ -5,6 +5,11 @@ import { MediaStorageProviderMetadata } from 'src/entities/media-storage-provide
5
5
  import { SettingService } from 'src/services/setting.service';
6
6
  import { DEFAULT_MEDIA_FILE_STORAGE_DIR } from 'src/services/settings/default-settings-provider.service';
7
7
  import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
8
+ import { normalizeUploadedFileName } from 'src/constants/media-file-types';
9
+
10
+ // Keeps the joined path well clear of filesystem NAME_MAX (255 on ext4/APFS) once the
11
+ // multer hash prefix and separator are added.
12
+ const MAX_STORED_FILE_NAME_LENGTH = 150;
8
13
 
9
14
 
10
15
  export interface MediaRecordCreateInput {
@@ -18,9 +23,29 @@ export interface MediaRecordCreateInput {
18
23
  originalFileName?: string;
19
24
  }
20
25
 
26
+ /**
27
+ * Reduces a client-supplied name to something safe to interpolate into a filesystem path.
28
+ * `originalname` is fully attacker-controlled, so without this a name like
29
+ * `../../../../tmp/pwned.png` escapes the media storage directory entirely - the multer hash
30
+ * prefix does not contain it, because the leading `..` simply cancels the prefix segment.
31
+ *
32
+ * Everything outside a conservative allowlist becomes `_`; the result can never contain a
33
+ * path separator, so the joined path always stays inside the storage root.
34
+ */
35
+ export function sanitizeStoredMediaFileName(originalName?: string | null): string {
36
+ const sanitized = normalizeUploadedFileName(originalName)
37
+ .replace(/[^A-Za-z0-9._-]/g, '_')
38
+ // Collapse any run of dots so no `..` segment can survive.
39
+ .replace(/\.{2,}/g, '.')
40
+ .replace(/^[._-]+/, '')
41
+ .slice(0, MAX_STORED_FILE_NAME_LENGTH);
42
+
43
+ return sanitized || 'file';
44
+ }
45
+
21
46
  // Builds the stored file name format shared by media storage implementations.
22
47
  export function buildStoredMediaFileName(file: Pick<Express.Multer.File, 'filename' | 'originalname'>): string {
23
- return `${file.filename}-${file.originalname}`;
48
+ return `${file.filename}-${sanitizeStoredMediaFileName(file.originalname)}`;
24
49
  }
25
50
 
26
51
  // Resolves the on-disk path for a media file based on settings and provider visibility.
@@ -18,6 +18,7 @@ import { ModuleMetadataHelperService } from 'src/helpers/module-metadata-helper.
18
18
  import { ModuleMetadataRepository } from 'src/repository/module-metadata.repository';
19
19
  import { PermissionMetadataSeederService } from 'src/seeders/permission-metadata-seeder.service';
20
20
  import { DiskFileService } from 'src/services/file';
21
+ import { sanitizeStoredMediaFileName } from 'src/services/media-storage.utils';
21
22
  import { BasicFilterDto } from '../dtos/basic-filters.dto';
22
23
  import { UpdateModuleMetadataDto } from '../dtos/update-module-metadata.dto';
23
24
  import {
@@ -609,7 +610,8 @@ export class ModuleMetadataService {
609
610
  }
610
611
 
611
612
  private getFileName(file: Express.Multer.File): string {
612
- return `${file.filename}-${file.originalname}`;
613
+ // originalname is attacker-controlled and feeds straight into a storage path.
614
+ return `${file.filename}-${sanitizeStoredMediaFileName(file.originalname)}`;
613
615
  }
614
616
 
615
617
  }
@@ -16,6 +16,7 @@ import { ModuleMetadataRepository } from 'src/repository/module-metadata.reposit
16
16
  import type { SolidCoreSetting } from './settings/default-settings-provider.service';
17
17
  import { Logger } from '@nestjs/common';
18
18
  import { EncryptionService } from './encryption.service';
19
+ import { sanitizeStoredMediaFileName } from './media-storage.utils';
19
20
 
20
21
 
21
22
  @Injectable()
@@ -358,7 +359,8 @@ export class SettingService {
358
359
  if (uploadedFiles?.length) {
359
360
  for (const file of uploadedFiles) {
360
361
  const settingKey = file.fieldname;
361
- const relativeFileName = `${file.filename}-${file.originalname}`;
362
+ // originalname is attacker-controlled and feeds straight into a storage path.
363
+ const relativeFileName = `${file.filename}-${sanitizeStoredMediaFileName(file.originalname)}`;
362
364
 
363
365
  // Read file from local disk (where Multer stores uploads) and write to storage
364
366
  // The path builder constructs the provider-appropriate storage path
@@ -1,6 +1,5 @@
1
1
  import "multer";
2
- import { Global, MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
3
- import * as express from "express";
2
+ import { Global, Module } from "@nestjs/common";
4
3
  import { ConfigModule, ConfigService } from "@nestjs/config";
5
4
  import {
6
5
  APP_FILTER,
@@ -1050,13 +1049,7 @@ import { SwitchNode } from './services/workflow/nodes/switch.node';
1050
1049
  WorkflowExpressionService,
1051
1050
  ],
1052
1051
  })
1053
- export class SolidCoreModule implements NestModule {
1054
- configure(consumer: MiddlewareConsumer) {
1055
- consumer
1056
- .apply(
1057
- express.json({ limit: "10mb" }),
1058
- express.urlencoded({ limit: "10mb", extended: true }),
1059
- )
1060
- .forRoutes("*");
1061
- }
1062
- }
1052
+ // Body-size limits live in bootstrapSolidApp (see SolidBootstrapOptions.bodyLimit).
1053
+ // They cannot be applied here: module middleware is registered in registerRouter(),
1054
+ // which runs after Nest's own parser middleware, so anything set here never takes effect.
1055
+ export class SolidCoreModule {}