@modern-admin/feature-upload 0.2.0 → 0.3.0

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/dist/graphql.d.ts.map +1 -1
  3. package/dist/graphql.js +23 -5
  4. package/dist/graphql.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/mime.d.ts +21 -0
  10. package/dist/mime.d.ts.map +1 -0
  11. package/dist/mime.js +46 -0
  12. package/dist/mime.js.map +1 -0
  13. package/dist/nest/upload-sweeper.service.d.ts.map +1 -1
  14. package/dist/nest/upload-sweeper.service.js +7 -0
  15. package/dist/nest/upload-sweeper.service.js.map +1 -1
  16. package/dist/nest/upload.controller.d.ts.map +1 -1
  17. package/dist/nest/upload.controller.js +82 -15
  18. package/dist/nest/upload.controller.js.map +1 -1
  19. package/dist/nest/upload.module.d.ts +5 -0
  20. package/dist/nest/upload.module.d.ts.map +1 -1
  21. package/dist/nest/upload.module.js +5 -0
  22. package/dist/nest/upload.module.js.map +1 -1
  23. package/dist/nest/upload.tokens.d.ts +23 -0
  24. package/dist/nest/upload.tokens.d.ts.map +1 -1
  25. package/dist/path-safety.d.ts +45 -0
  26. package/dist/path-safety.d.ts.map +1 -0
  27. package/dist/path-safety.js +73 -0
  28. package/dist/path-safety.js.map +1 -0
  29. package/dist/pending-registry.d.ts +19 -4
  30. package/dist/pending-registry.d.ts.map +1 -1
  31. package/dist/pending-registry.js +19 -4
  32. package/dist/pending-registry.js.map +1 -1
  33. package/dist/providers/local.d.ts.map +1 -1
  34. package/dist/providers/local.js +16 -3
  35. package/dist/providers/local.js.map +1 -1
  36. package/dist/providers/s3.d.ts.map +1 -1
  37. package/dist/providers/s3.js +14 -0
  38. package/dist/providers/s3.js.map +1 -1
  39. package/dist/registry.d.ts +8 -0
  40. package/dist/registry.d.ts.map +1 -1
  41. package/dist/registry.js.map +1 -1
  42. package/dist/types.d.ts +10 -2
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/upload-feature.d.ts.map +1 -1
  45. package/dist/upload-feature.js +30 -31
  46. package/dist/upload-feature.js.map +1 -1
  47. package/package.json +8 -7
  48. package/src/graphql.ts +22 -5
  49. package/src/index.ts +5 -0
  50. package/src/mime.ts +42 -0
  51. package/src/nest/upload-sweeper.service.ts +9 -0
  52. package/src/nest/upload.controller.ts +95 -14
  53. package/src/nest/upload.module.ts +5 -0
  54. package/src/nest/upload.tokens.ts +23 -0
  55. package/src/path-safety.ts +73 -0
  56. package/src/pending-registry.ts +19 -4
  57. package/src/providers/local.ts +15 -3
  58. package/src/providers/s3.ts +17 -0
  59. package/src/registry.ts +8 -0
  60. package/src/types.ts +10 -2
  61. package/src/upload-feature.ts +31 -36
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Modern Admin contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1 +1 @@
1
- {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AASH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAE5B,MAAM,uBAAuB,CAAA;AAS9B,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,CAAC,GAAG,EAAE,gBAAgB,KAAK,sBAAsB,CAwFnD"}
1
+ {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AASH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAE5B,MAAM,uBAAuB,CAAA;AAW9B,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,CAAC,GAAG,EAAE,gBAAgB,KAAK,sBAAsB,CAuGnD"}
package/dist/graphql.js CHANGED
@@ -27,6 +27,8 @@ import { GraphQLBoolean, GraphQLNonNull, GraphQLObjectType, GraphQLString, Graph
27
27
  import { ForbiddenError, ResourceNotFoundError } from '@modern-admin/core';
28
28
  import { UploadProviderRegistry } from './registry.js';
29
29
  import { PendingUploadsRegistry } from './pending-registry.js';
30
+ import { isUnsafeKey, sanitizeFilename } from './path-safety.js';
31
+ import { mimeMatches } from './mime.js';
30
32
  /** Default TTL for pending upload entries, mirrored from the controller. */
31
33
  const DEFAULT_PENDING_TTL_MS = 60 * 60 * 1000;
32
34
  export function uploadGraphqlExtension(options = {}) {
@@ -60,11 +62,25 @@ export function uploadGraphqlExtension(options = {}) {
60
62
  const { resourceId, field } = args;
61
63
  const { providerId, registered } = resolveProperty(ctx, resourceId, field);
62
64
  const upload = args.file;
65
+ // Server-side MIME / size enforcement (the multipart parser has
66
+ // already applied the global hard size cap; this adds the tighter
67
+ // per-property limits).
68
+ if (!mimeMatches(upload.mimeType, registered.mimeTypes)) {
69
+ throw new Error(`File type "${upload.mimeType}" is not allowed`);
70
+ }
71
+ if (registered.maxSize != null && upload.size > registered.maxSize) {
72
+ throw new Error(`File exceeds the maximum size of ${registered.maxSize} bytes`);
73
+ }
74
+ // Sanitise the client filename before it can steer key generation.
75
+ const originalName = sanitizeFilename(upload.filename);
63
76
  const computedKey = registered.uploadPath
64
- ? registered.uploadPath(upload.filename)
77
+ ? registered.uploadPath(originalName)
65
78
  : undefined;
79
+ if (computedKey !== undefined && isUnsafeKey(computedKey)) {
80
+ throw new Error('Computed upload key is not allowed');
81
+ }
66
82
  const key = await registered.provider.upload({
67
- originalName: upload.filename,
83
+ originalName,
68
84
  mimeType: upload.mimeType,
69
85
  size: upload.size,
70
86
  buffer: upload.buffer,
@@ -74,7 +90,7 @@ export function uploadGraphqlExtension(options = {}) {
74
90
  const info = {
75
91
  key,
76
92
  url,
77
- name: upload.filename,
93
+ name: originalName,
78
94
  size: upload.size,
79
95
  mimeType: upload.mimeType,
80
96
  };
@@ -96,6 +112,8 @@ export function uploadGraphqlExtension(options = {}) {
96
112
  // the REST controller's check — prevents leaking arbitrary
97
113
  // resource ids to authenticated clients.
98
114
  resolveProperty(ctx, resourceId, field);
115
+ if (isUnsafeKey(key))
116
+ return false;
99
117
  return PendingUploadsRegistry.cancel(key);
100
118
  },
101
119
  },
@@ -116,9 +134,9 @@ function resolveProperty(ctx, resourceId, field) {
116
134
  }
117
135
  catch (err) {
118
136
  if (err instanceof ResourceNotFoundError)
119
- throw new Error(err.message);
137
+ throw new Error(err.message, { cause: err });
120
138
  if (err instanceof ForbiddenError)
121
- throw new Error(err.message);
139
+ throw new Error(err.message, { cause: err });
122
140
  throw err;
123
141
  }
124
142
  const decorator = resource.decorate();
@@ -1 +1 @@
1
- {"version":3,"file":"graphql.js","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graphql.js","sourceRoot":"","sources":["../src/graphql.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export { uploadFeature } from './upload-feature.js';
2
2
  export { UploadProviderRegistry } from './registry.js';
3
3
  export { PendingUploadsRegistry } from './pending-registry.js';
4
+ export { isUnsafeKey, sanitizeFilename, resolveWithinDir } from './path-safety.js';
5
+ export { mimeMatches } from './mime.js';
4
6
  export { LocalUploadProvider, type LocalUploadOptions } from './providers/local.js';
5
7
  export { S3UploadProvider, type S3UploadOptions } from './providers/s3.js';
6
8
  export type { IUploadProvider, UploadedFile, UploadedFileInfo, UploadFeatureOptions, UploadPropertyConfig, } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAG9D,OAAO,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG1E,YAAY,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAG9D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAElF,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAGvC,OAAO,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG1E,YAAY,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -6,6 +6,10 @@
6
6
  export { uploadFeature } from './upload-feature.js';
7
7
  export { UploadProviderRegistry } from './registry.js';
8
8
  export { PendingUploadsRegistry } from './pending-registry.js';
9
+ // Path-safety helpers (shared by providers + custom upload backends).
10
+ export { isUnsafeKey, sanitizeFilename, resolveWithinDir } from './path-safety.js';
11
+ // Server-side MIME allow-list matcher (HTML `accept` syntax).
12
+ export { mimeMatches } from './mime.js';
9
13
  // Built-in providers
10
14
  export { LocalUploadProvider } from './providers/local.js';
11
15
  export { S3UploadProvider } from './providers/s3.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,SAAS;AACT,wGAAwG;AACxG,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,qBAAqB;AACrB,OAAO,EAAE,mBAAmB,EAA2B,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAwB,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,SAAS;AACT,wGAAwG;AACxG,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,sEAAsE;AACtE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAClF,8DAA8D;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvC,qBAAqB;AACrB,OAAO,EAAE,mBAAmB,EAA2B,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAwB,MAAM,mBAAmB,CAAA"}
package/dist/mime.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Server-side MIME allow-list matching.
3
+ *
4
+ * `UploadPropertyConfig.mimeTypes` uses the same syntax as the HTML `accept`
5
+ * attribute — exact types (`image/jpeg`), type wildcards (`image/*`), or the
6
+ * catch-all wildcard (`*`, or its explicit `type`/`subtype` form). The
7
+ * frontend enforces it for UX; the server
8
+ * re-checks it so a hand-crafted request cannot store a disallowed type.
9
+ *
10
+ * Note: the matched MIME is the *declared* `Content-Type` from the multipart
11
+ * part, which a client can spoof. This is defense-in-depth (it stops the
12
+ * trivial "bypass the frontend accept filter" attack and pairs with the size
13
+ * limits), not content sniffing — magic-byte inspection would need a decoder
14
+ * per format and is intentionally out of scope here.
15
+ */
16
+ /**
17
+ * True when `type` matches at least one pattern in `patterns`. An empty or
18
+ * omitted pattern list allows everything (no restriction configured).
19
+ */
20
+ export declare function mimeMatches(type: string, patterns: readonly string[] | null | undefined): boolean;
21
+ //# sourceMappingURL=mime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAgBjG"}
package/dist/mime.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Server-side MIME allow-list matching.
3
+ *
4
+ * `UploadPropertyConfig.mimeTypes` uses the same syntax as the HTML `accept`
5
+ * attribute — exact types (`image/jpeg`), type wildcards (`image/*`), or the
6
+ * catch-all wildcard (`*`, or its explicit `type`/`subtype` form). The
7
+ * frontend enforces it for UX; the server
8
+ * re-checks it so a hand-crafted request cannot store a disallowed type.
9
+ *
10
+ * Note: the matched MIME is the *declared* `Content-Type` from the multipart
11
+ * part, which a client can spoof. This is defense-in-depth (it stops the
12
+ * trivial "bypass the frontend accept filter" attack and pairs with the size
13
+ * limits), not content sniffing — magic-byte inspection would need a decoder
14
+ * per format and is intentionally out of scope here.
15
+ */
16
+ /** Normalise a raw MIME to `type/subtype`, lower-cased, without parameters. */
17
+ function normalizeType(raw) {
18
+ return raw.split(';')[0].trim().toLowerCase();
19
+ }
20
+ /**
21
+ * True when `type` matches at least one pattern in `patterns`. An empty or
22
+ * omitted pattern list allows everything (no restriction configured).
23
+ */
24
+ export function mimeMatches(type, patterns) {
25
+ if (!patterns || patterns.length === 0)
26
+ return true;
27
+ const t = normalizeType(type);
28
+ if (!t)
29
+ return false;
30
+ const [tMain, tSub] = t.split('/');
31
+ for (const raw of patterns) {
32
+ const p = normalizeType(raw);
33
+ if (p === '*' || p === '*/*')
34
+ return true;
35
+ const [pMain, pSub] = p.split('/');
36
+ if (pSub === '*') {
37
+ if (pMain === tMain)
38
+ return true;
39
+ }
40
+ else if (pMain === tMain && pSub === tSub) {
41
+ return true;
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+ //# sourceMappingURL=mime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.js","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,+EAA+E;AAC/E,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAA8C;IACtF,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACnD,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IACpB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK;YAAE,OAAO,IAAI,CAAA;QACzC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,IAAI,KAAK,KAAK,KAAK;gBAAE,OAAO,IAAI,CAAA;QAClC,CAAC;aAAM,IAAI,KAAK,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"upload-sweeper.service.d.ts","sourceRoot":"","sources":["../../src/nest/upload-sweeper.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE5F,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAI/F,qBACa,oBAAqB,YAAW,YAAY,EAAE,eAAe;IAG7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFnE,OAAO,CAAC,KAAK,CAA8C;gBAEC,OAAO,EAAE,8BAA8B;IAEnG,YAAY,IAAI,IAAI;IAUpB,eAAe,IAAI,IAAI;CAMxB"}
1
+ {"version":3,"file":"upload-sweeper.service.d.ts","sourceRoot":"","sources":["../../src/nest/upload-sweeper.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE5F,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAI/F,qBACa,oBAAqB,YAAW,YAAY,EAAE,eAAe;IAG7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFnE,OAAO,CAAC,KAAK,CAA8C;gBAEC,OAAO,EAAE,8BAA8B;IAEnG,YAAY,IAAI,IAAI;IAmBpB,eAAe,IAAI,IAAI;CAMxB"}
@@ -45,6 +45,13 @@ let UploadSweeperService = class UploadSweeperService {
45
45
  const interval = this.options.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS;
46
46
  if (interval <= 0)
47
47
  return;
48
+ if (!this.options.acknowledgeSingleInstance) {
49
+ console.warn('[modern-admin/feature-upload] The pending-upload registry and sweeper are ' +
50
+ 'in-process (single-instance). Behind a load balancer with ≥2 replicas the ' +
51
+ 'sweeper can delete a just-saved file (see PendingUploadsRegistry docs). ' +
52
+ 'Run single-instance until a shared store lands, or pass ' +
53
+ '`acknowledgeSingleInstance: true` to silence this warning.');
54
+ }
48
55
  this.timer = setInterval(() => {
49
56
  void PendingUploadsRegistry.sweep();
50
57
  }, interval);
@@ -1 +1 @@
1
- {"version":3,"file":"upload-sweeper.service.js","sourceRoot":"","sources":["../../src/nest/upload-sweeper.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAA2C,MAAM,gBAAgB,CAAA;AAC5F,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAuC,MAAM,oBAAoB,CAAA;AAE/F,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAGxC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAG/B,YAA2C,OAAwD;QAAvC,YAAO,GAAP,OAAO,CAAgC;QAF3F,UAAK,GAA0C,IAAI,CAAA;IAE2C,CAAC;IAEvG,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAA;QAC1E,IAAI,QAAQ,IAAI,CAAC;YAAE,OAAM;QACzB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,KAAK,sBAAsB,CAAC,KAAK,EAAE,CAAA;QACrC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACZ,0EAA0E;QAC1E,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IAChE,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACnB,CAAC;IACH,CAAC;CACF,CAAA;AArBY,oBAAoB;IADhC,UAAU,EAAE;IAIE,WAAA,MAAM,CAAC,qBAAqB,CAAC,CAAA;;GAH/B,oBAAoB,CAqBhC"}
1
+ {"version":3,"file":"upload-sweeper.service.js","sourceRoot":"","sources":["../../src/nest/upload-sweeper.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAA2C,MAAM,gBAAgB,CAAA;AAC5F,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAuC,MAAM,oBAAoB,CAAA;AAE/F,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;AAGxC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAG/B,YAA2C,OAAwD;QAAvC,YAAO,GAAP,OAAO,CAAgC;QAF3F,UAAK,GAA0C,IAAI,CAAA;IAE2C,CAAC;IAEvG,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAA;QAC1E,IAAI,QAAQ,IAAI,CAAC;YAAE,OAAM;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC;YAC5C,OAAO,CAAC,IAAI,CACV,4EAA4E;gBAC1E,4EAA4E;gBAC5E,0EAA0E;gBAC1E,0DAA0D;gBAC1D,4DAA4D,CAC/D,CAAA;QACH,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,KAAK,sBAAsB,CAAC,KAAK,EAAE,CAAA;QACrC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACZ,0EAA0E;QAC1E,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IAChE,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACnB,CAAC;IACH,CAAC;CACF,CAAA;AA9BY,oBAAoB;IADhC,UAAU,EAAE;IAIE,WAAA,MAAM,CAAC,qBAAqB,CAAC,CAAA;;GAH/B,oBAAoB,CA8BhC"}
@@ -1 +1 @@
1
- {"version":3,"file":"upload.controller.d.ts","sourceRoot":"","sources":["../../src/nest/upload.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAkBH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAC/F,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAiEjE,qBAIa,gBAAgB;IAEH,OAAO,CAAC,QAAQ,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ,CAAC,aAAa;gBADtB,KAAK,EAAE,WAAW,EACT,aAAa,EAAE,8BAA8B;IAG/F;;;;;;OAMG;IAEG,MAAM,CACW,UAAU,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACtB,GAAG,EAAE,eAAe,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA2B9B;;;;;;;;OAQG;IAGG,MAAM,CACW,UAAU,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAchB,oFAAoF;IACpF,OAAO,CAAC,eAAe;CAkCxB"}
1
+ {"version":3,"file":"upload.controller.d.ts","sourceRoot":"","sources":["../../src/nest/upload.controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAoBH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAA;AAM3B,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAC/F,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AA4HjE,qBAIa,gBAAgB;IAEH,OAAO,CAAC,QAAQ,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ,CAAC,aAAa;gBADtB,KAAK,EAAE,WAAW,EACT,aAAa,EAAE,8BAA8B;IAG/F;;;;;;OAMG;IAEG,MAAM,CACW,UAAU,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACtB,GAAG,EAAE,eAAe,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA4C9B;;;;;;;;OAQG;IAGG,MAAM,CACW,UAAU,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAehB,oFAAoF;IACpF,OAAO,CAAC,eAAe;CAkCxB"}
@@ -31,20 +31,40 @@ var __metadata = (this && this.__metadata) || function (k, v) {
31
31
  var __param = (this && this.__param) || function (paramIndex, decorator) {
32
32
  return function (target, key) { decorator(target, key, paramIndex); }
33
33
  };
34
- import { BadRequestException, Controller, Delete, ForbiddenException, HttpCode, Inject, InternalServerErrorException, NotFoundException, Post, Param, Query, Req, UseGuards, } from '@nestjs/common';
34
+ import { BadRequestException, Controller, Delete, ForbiddenException, HttpCode, Inject, InternalServerErrorException, NotFoundException, PayloadTooLargeException, Post, Param, Query, Req, UnsupportedMediaTypeException, UseGuards, } from '@nestjs/common';
35
35
  import { ApiCookieAuth, ApiTags } from '@nestjs/swagger';
36
36
  import Busboy from 'busboy';
37
37
  import { ForbiddenError, ResourceNotFoundError, } from '@modern-admin/core';
38
38
  import { MODERN_ADMIN, ModernAdminAuthGuard } from '@modern-admin/nest';
39
39
  import { UploadProviderRegistry } from '../registry.js';
40
40
  import { PendingUploadsRegistry } from '../pending-registry.js';
41
+ import { isUnsafeKey, sanitizeFilename } from '../path-safety.js';
42
+ import { mimeMatches } from '../mime.js';
41
43
  import { UPLOAD_MODULE_OPTIONS } from './upload.tokens.js';
42
- /** Reads every file from a multipart/form-data stream using busboy. */
43
- function parseAllFiles(req) {
44
+ /** Default hard cap on a single buffered file (bytes) — 25 MiB. */
45
+ const DEFAULT_MAX_FILE_SIZE = 25 * 1024 * 1024;
46
+ /** Default cap on files per multipart request for `isArray` properties. */
47
+ const DEFAULT_MAX_FILES = 20;
48
+ /**
49
+ * Reads every file from a multipart/form-data stream using busboy, enforcing
50
+ * size / count / MIME limits *as it streams* so a hostile request cannot
51
+ * buffer unbounded bytes into memory before any check runs.
52
+ */
53
+ function parseAllFiles(req, limits) {
44
54
  return new Promise((resolve, reject) => {
45
55
  let bb;
46
56
  try {
47
- bb = Busboy({ headers: req.headers });
57
+ bb = Busboy({
58
+ headers: req.headers,
59
+ limits: {
60
+ fileSize: limits.maxFileSize,
61
+ files: limits.maxFiles,
62
+ // The upload endpoint carries no data fields — keep them tightly
63
+ // bounded so field spam cannot buffer memory either.
64
+ fields: 10,
65
+ fieldSize: 100 * 1024,
66
+ },
67
+ });
48
68
  }
49
69
  catch {
50
70
  reject(new BadRequestException('Request is not multipart/form-data'));
@@ -56,6 +76,9 @@ function parseAllFiles(req) {
56
76
  let finished = false;
57
77
  let settled = false;
58
78
  let firstError;
79
+ const fail = (err) => {
80
+ firstError = firstError ?? err;
81
+ };
59
82
  const tryResolve = () => {
60
83
  if (settled || !finished || pending > 0)
61
84
  return;
@@ -69,31 +92,56 @@ function parseAllFiles(req) {
69
92
  };
70
93
  bb.on('file', (_fieldname, stream, info) => {
71
94
  pending++;
95
+ // Reject a disallowed MIME up front — don't buffer a single byte of it.
96
+ const mimeType = info.mimeType || 'application/octet-stream';
97
+ if (!mimeMatches(mimeType, limits.mimeTypes)) {
98
+ fail(new UnsupportedMediaTypeException(`File type "${mimeType}" is not allowed`));
99
+ stream.resume(); // drain so busboy keeps parsing the rest of the body
100
+ stream.on('close', () => {
101
+ pending--;
102
+ tryResolve();
103
+ });
104
+ return;
105
+ }
72
106
  const chunks = [];
73
107
  stream.on('data', (chunk) => chunks.push(chunk));
108
+ // Busboy emits 'limit' when the per-file byte cap is exceeded.
109
+ stream.on('limit', () => {
110
+ fail(new PayloadTooLargeException(`File exceeds the maximum size of ${limits.maxFileSize} bytes`));
111
+ });
74
112
  stream.on('end', () => {
75
- const buffer = Buffer.concat(chunks);
76
- files.push({
77
- originalName: info.filename || 'upload',
78
- mimeType: info.mimeType || 'application/octet-stream',
79
- size: buffer.length,
80
- buffer,
81
- });
113
+ // `truncated` is set when the fileSize limit tripped mid-stream.
114
+ if (!stream.truncated) {
115
+ const buffer = Buffer.concat(chunks);
116
+ files.push({
117
+ // Sanitise before the name can steer key generation (extname /
118
+ // custom uploadPath) — strips separators and traversal segments.
119
+ originalName: sanitizeFilename(info.filename),
120
+ mimeType,
121
+ size: buffer.length,
122
+ buffer,
123
+ });
124
+ }
82
125
  pending--;
83
126
  tryResolve();
84
127
  });
85
128
  stream.on('error', (err) => {
86
- firstError = firstError ?? err;
129
+ fail(err);
87
130
  pending--;
88
131
  tryResolve();
89
132
  });
90
133
  });
134
+ // Busboy stops emitting files past the `files` limit — surface it as a 400
135
+ // rather than silently dropping the extras.
136
+ bb.on('filesLimit', () => {
137
+ fail(new BadRequestException(`Too many files — at most ${limits.maxFiles} allowed`));
138
+ });
91
139
  bb.on('finish', () => {
92
140
  finished = true;
93
141
  tryResolve();
94
142
  });
95
143
  bb.on('error', (err) => {
96
- firstError = firstError ?? err;
144
+ fail(err);
97
145
  finished = true;
98
146
  tryResolve();
99
147
  });
@@ -114,8 +162,20 @@ let UploadController = class UploadController {
114
162
  */
115
163
  async upload(resourceId, field, req) {
116
164
  const { providerId, registered } = this.resolveProperty(resourceId, field);
117
- // 1 — Parse the multipart body (collect every file).
118
- const files = await parseAllFiles(req);
165
+ // 1 — Parse the multipart body (collect every file), enforcing size /
166
+ // count / MIME limits while streaming so nothing unbounded is buffered.
167
+ const moduleMax = this.moduleOptions.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
168
+ // Per-property maxSize tightens the cap but can never raise it above the
169
+ // module-wide hard bound.
170
+ const maxFileSize = Math.min(moduleMax, registered.maxSize ?? Number.POSITIVE_INFINITY);
171
+ const maxFiles = registered.isArray
172
+ ? (this.moduleOptions.maxFiles ?? DEFAULT_MAX_FILES)
173
+ : 1;
174
+ const files = await parseAllFiles(req, {
175
+ maxFileSize,
176
+ maxFiles,
177
+ ...(registered.mimeTypes ? { mimeTypes: registered.mimeTypes } : {}),
178
+ });
119
179
  // 2 — If the property is single-value, only the first file is honoured.
120
180
  // The frontend sends one anyway; this is a safety bound.
121
181
  const accepted = registered.isArray ? files : files.slice(0, 1);
@@ -125,6 +185,11 @@ let UploadController = class UploadController {
125
185
  const results = [];
126
186
  for (const file of accepted) {
127
187
  const computedKey = registered.uploadPath ? registered.uploadPath(file.originalName) : undefined;
188
+ // A custom `uploadPath` could still produce a traversal/absolute key —
189
+ // reject at the boundary (the provider also contains it defensively).
190
+ if (computedKey !== undefined && isUnsafeKey(computedKey)) {
191
+ throw new BadRequestException('Computed upload key is not allowed');
192
+ }
128
193
  const key = await registered.provider.upload(file, computedKey);
129
194
  // Track as pending — it will be confirmed by `new.after`/`edit.after`
130
195
  // when the form is saved, or cleaned up by the sweeper when the TTL
@@ -147,6 +212,8 @@ let UploadController = class UploadController {
147
212
  async cancel(resourceId, field, key) {
148
213
  if (!key)
149
214
  throw new BadRequestException('Missing "key" query parameter');
215
+ if (isUnsafeKey(key))
216
+ throw new BadRequestException('Invalid "key" query parameter');
150
217
  // Validate that the resource/field is actually upload-enabled. This avoids
151
218
  // leaking the existence of arbitrary resource ids to unauthenticated callers
152
219
  // (the auth guard already requires authentication).
@@ -1 +1 @@
1
- {"version":3,"file":"upload.controller.js","sourceRoot":"","sources":["../../src/nest/upload.controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upload.controller.js","sourceRoot":"","sources":["../../src/nest/upload.controller.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,11 @@
18
18
  * The module depends on `MODERN_ADMIN` and `ModernAdminAuthGuard` being
19
19
  * available in the DI tree, which is satisfied when `ModernAdminModule.forRoot`
20
20
  * is registered with `global: true` (the recommended default).
21
+ *
22
+ * ⚠️ Single-instance: the pending-upload registry + sweeper are in-process.
23
+ * Behind a load balancer with ≥2 replicas the sweeper can delete a just-saved
24
+ * file (see `PendingUploadsRegistry` docs). The sweeper warns once at startup;
25
+ * pass `acknowledgeSingleInstance: true` to silence it once understood.
21
26
  */
22
27
  import { type DynamicModule } from '@nestjs/common';
23
28
  import { type ModernAdminUploadModuleOptions } from './upload.tokens.js';
@@ -1 +1 @@
1
- {"version":3,"file":"upload.module.d.ts","sourceRoot":"","sources":["../../src/nest/upload.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAU,MAAM,gBAAgB,CAAA;AAG3D,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAE/F,YAAY,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAExE,qBACa,uBAAuB;IAClC,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,8BAAmC,GAAG,aAAa;CAY5E"}
1
+ {"version":3,"file":"upload.module.d.ts","sourceRoot":"","sources":["../../src/nest/upload.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAU,MAAM,gBAAgB,CAAA;AAG3D,OAAO,EAAyB,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAE/F,YAAY,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAA;AAExE,qBACa,uBAAuB;IAClC,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,8BAAmC,GAAG,aAAa;CAY5E"}
@@ -18,6 +18,11 @@
18
18
  * The module depends on `MODERN_ADMIN` and `ModernAdminAuthGuard` being
19
19
  * available in the DI tree, which is satisfied when `ModernAdminModule.forRoot`
20
20
  * is registered with `global: true` (the recommended default).
21
+ *
22
+ * ⚠️ Single-instance: the pending-upload registry + sweeper are in-process.
23
+ * Behind a load balancer with ≥2 replicas the sweeper can delete a just-saved
24
+ * file (see `PendingUploadsRegistry` docs). The sweeper warns once at startup;
25
+ * pass `acknowledgeSingleInstance: true` to silence it once understood.
21
26
  */
22
27
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
23
28
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1 +1 @@
1
- {"version":3,"file":"upload.module.js","sourceRoot":"","sources":["../../src/nest/upload.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;;;;;;AAEH,OAAO,EAAsB,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAE,qBAAqB,EAAuC,MAAM,oBAAoB,CAAA;AAKxF,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAClC,MAAM,CAAC,OAAO,CAAC,UAA0C,EAAE;QACzD,OAAO;YACL,MAAM,EAAE,yBAAuB;YAC/B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;YAC/B,WAAW,EAAE,CAAC,gBAAgB,CAAC;YAC/B,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBACrD,oBAAoB;aACrB;YACD,OAAO,EAAE,CAAC,qBAAqB,CAAC;SACjC,CAAA;IACH,CAAC;CACF,CAAA;AAbY,uBAAuB;IADnC,MAAM,CAAC,EAAE,CAAC;GACE,uBAAuB,CAanC"}
1
+ {"version":3,"file":"upload.module.js","sourceRoot":"","sources":["../../src/nest/upload.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;;;;;;AAEH,OAAO,EAAsB,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAE,qBAAqB,EAAuC,MAAM,oBAAoB,CAAA;AAKxF,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAClC,MAAM,CAAC,OAAO,CAAC,UAA0C,EAAE;QACzD,OAAO;YACL,MAAM,EAAE,yBAAuB;YAC/B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;YAC/B,WAAW,EAAE,CAAC,gBAAgB,CAAC;YAC/B,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBACrD,oBAAoB;aACrB;YACD,OAAO,EAAE,CAAC,qBAAqB,CAAC;SACjC,CAAA;IACH,CAAC;CACF,CAAA;AAbY,uBAAuB;IADnC,MAAM,CAAC,EAAE,CAAC;GACE,uBAAuB,CAanC"}
@@ -19,5 +19,28 @@ export interface ModernAdminUploadModuleOptions {
19
19
  * just never expire).
20
20
  */
21
21
  sweepIntervalMs?: number;
22
+ /**
23
+ * Hard upper bound (bytes) on a single uploaded file, applied by the
24
+ * multipart parser regardless of per-property `maxSize`. Files are buffered
25
+ * in memory, so this caps memory use per request and blocks OOM-style DoS.
26
+ * A property's own `maxSize` still applies when it is *smaller* than this.
27
+ * Default: 25 MiB.
28
+ */
29
+ maxFileSize?: number;
30
+ /**
31
+ * Maximum number of files accepted in a single multipart request for an
32
+ * `isArray` property. Single-value properties always cap at 1. Default: 20.
33
+ */
34
+ maxFiles?: number;
35
+ /**
36
+ * Suppress the single-instance startup warning.
37
+ *
38
+ * `PendingUploadsRegistry` is an in-process `Map`: behind a load balancer
39
+ * with ≥2 replicas the confirm/sweeper lifecycle can delete a just-saved
40
+ * file (see the class docs). The sweeper logs a warning once at startup to
41
+ * make this loud. Set `true` once you have confirmed a single-instance
42
+ * deployment (or accepted the risk) to silence it.
43
+ */
44
+ acknowledgeSingleInstance?: boolean;
22
45
  }
23
46
  //# sourceMappingURL=upload.tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload.tokens.d.ts","sourceRoot":"","sources":["../../src/nest/upload.tokens.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,qBAAqB,eAAmD,CAAA;AAErF,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB"}
1
+ {"version":3,"file":"upload.tokens.d.ts","sourceRoot":"","sources":["../../src/nest/upload.tokens.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,qBAAqB,eAAmD,CAAA;AAErF,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAA;CACpC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Path-safety helpers shared by the upload providers and the upload
3
+ * entry points (REST controller + GraphQL extension).
4
+ *
5
+ * Storage keys and filenames are attacker-controllable in two ways:
6
+ *
7
+ * 1. **Upload** — the client-supplied `originalName` flows into key
8
+ * generation (default `uuid + extname`, or a custom `uploadPath`). A raw
9
+ * name like `../../evil.sh` must never be able to steer the key outside
10
+ * the intended storage space.
11
+ * 2. **Delete** — a `type: 'file'` property is just a string in the DB. An
12
+ * attacker who can edit a record can write `../../../../etc/passwd` into
13
+ * it; the next edit/delete would call `provider.delete(key)` with that
14
+ * value. Without containment the local provider's `unlink` escapes the
15
+ * upload directory.
16
+ *
17
+ * Defense is layered: keys are rejected at the transport boundary
18
+ * (`isUnsafeKey`), filenames are sanitised before they can influence a key
19
+ * (`sanitizeFilename`), and the local provider resolves every path against its
20
+ * upload directory (`resolveWithinDir`) as the authoritative backstop.
21
+ */
22
+ /**
23
+ * True when `key` must be refused: empty, containing a NUL byte, absolute
24
+ * (POSIX `/…` or Windows `C:\…`), or containing a `..` traversal segment.
25
+ *
26
+ * Forward and back slashes are both treated as separators so a key crafted on
27
+ * one platform cannot escape on another. Legitimate nested keys
28
+ * (`avatars/2024/uuid.jpg`) and prefixes are allowed — only traversal and
29
+ * absolute paths are rejected.
30
+ */
31
+ export declare function isUnsafeKey(key: unknown): boolean;
32
+ /**
33
+ * Reduce a client-supplied filename to a safe basename that cannot influence
34
+ * the directory a key resolves into. Strips any directory components (either
35
+ * separator), NUL bytes, and leading dots (so `..` / hidden-traversal names
36
+ * collapse). Falls back to `'upload'` when nothing safe remains.
37
+ */
38
+ export declare function sanitizeFilename(name: unknown): string;
39
+ /**
40
+ * Resolve `key` against `baseDir` and guarantee the result stays inside it.
41
+ * Throws when the resolved path escapes the directory (traversal / absolute
42
+ * key). Returns the absolute, contained path for the caller to read/write.
43
+ */
44
+ export declare function resolveWithinDir(baseDir: string, key: string): string;
45
+ //# sourceMappingURL=path-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../src/path-safety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAOjD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAQtD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CASrE"}