@nestjs/common 12.0.4 → 12.1.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.
Files changed (37) hide show
  1. package/decorators/http/route-params.decorator.d.ts +48 -0
  2. package/decorators/http/route-params.decorator.js +58 -0
  3. package/enums/route-paramtypes.enum.d.ts +3 -1
  4. package/enums/route-paramtypes.enum.js +2 -0
  5. package/index.d.ts +1 -1
  6. package/interfaces/http/cookie-options.interface.d.ts +79 -0
  7. package/interfaces/http/cookie-options.interface.js +1 -0
  8. package/interfaces/http/csrf-protection-options.interface.d.ts +47 -0
  9. package/interfaces/http/csrf-protection-options.interface.js +1 -0
  10. package/interfaces/http/http-server.interface.d.ts +51 -0
  11. package/interfaces/http/index.d.ts +3 -0
  12. package/interfaces/http/index.js +3 -0
  13. package/interfaces/http/security-headers-options.interface.d.ts +142 -0
  14. package/interfaces/http/security-headers-options.interface.js +1 -0
  15. package/interfaces/nest-application-options.interface.d.ts +7 -0
  16. package/interfaces/nest-application.interface.d.ts +43 -0
  17. package/internal.d.ts +1 -1
  18. package/package.json +2 -2
  19. package/pipes/parse-array.pipe.d.ts +1 -0
  20. package/pipes/parse-array.pipe.js +18 -3
  21. package/pipes/parse-date.pipe.d.ts +2 -1
  22. package/pipes/parse-date.pipe.js +8 -2
  23. package/pipes/parse-enum.pipe.js +3 -1
  24. package/services/console-logger.service.d.ts +71 -4
  25. package/services/console-logger.service.js +182 -14
  26. package/services/log-levels.constant.d.ts +5 -0
  27. package/services/log-levels.constant.js +8 -0
  28. package/services/logger.service.d.ts +6 -6
  29. package/services/logger.service.js +25 -9
  30. package/services/utils/filter-log-levels.util.d.ts +1 -1
  31. package/services/utils/filter-log-levels.util.js +1 -1
  32. package/services/utils/get-env-log-levels.util.d.ts +14 -0
  33. package/services/utils/get-env-log-levels.util.js +25 -0
  34. package/services/utils/is-log-level.util.d.ts +1 -1
  35. package/services/utils/is-log-level.util.js +1 -1
  36. package/services/utils/redact.util.d.ts +27 -0
  37. package/services/utils/redact.util.js +225 -0
@@ -0,0 +1,25 @@
1
+ import { filterLogLevels } from './filter-log-levels.util.js';
2
+ import { isLogLevel } from './is-log-level.util.js';
3
+ /**
4
+ * The environment variable that sets the default log levels of `ConsoleLogger`.
5
+ */
6
+ export const LOG_LEVEL_ENV_VAR = 'NEST_LOG_LEVEL';
7
+ /**
8
+ * Reads the log levels from the `NEST_LOG_LEVEL` environment variable.
9
+ * Accepts the formats of `filterLogLevels`: a level (`warn`, that level and
10
+ * above), a list (`warn,error`) or a threshold (`>=warn`, `>debug`).
11
+ *
12
+ * @returns `undefined` when the variable is unset or empty, `false` when its
13
+ * value can't be parsed, the log levels otherwise.
14
+ */
15
+ export function getEnvLogLevels() {
16
+ const value = process.env[LOG_LEVEL_ENV_VAR]?.replaceAll(' ', '');
17
+ if (!value) {
18
+ return undefined;
19
+ }
20
+ const threshold = /^>=?(.*)$/.exec(value.toLowerCase());
21
+ const isValid = threshold
22
+ ? isLogLevel(threshold[1])
23
+ : value.toLowerCase().split(',').every(isLogLevel);
24
+ return isValid ? filterLogLevels(value) : false;
25
+ }
@@ -1,4 +1,4 @@
1
- import { LogLevel } from '../logger.service.js';
1
+ import { LogLevel } from '../log-levels.constant.js';
2
2
  /**
3
3
  * @publicApi
4
4
  */
@@ -1,4 +1,4 @@
1
- import { LOG_LEVELS } from '../logger.service.js';
1
+ import { LOG_LEVELS } from '../log-levels.constant.js';
2
2
  /**
3
3
  * @publicApi
4
4
  */
@@ -0,0 +1,27 @@
1
+ export declare const DEFAULT_REDACT_CENSOR = "[REDACTED]";
2
+ /**
3
+ * Returns a copy of the value in which the properties matching the paths are
4
+ * replaced with the censor, or the value itself when nothing matched.
5
+ */
6
+ export type Redactor = (value: unknown) => unknown;
7
+ /**
8
+ * Creates a function that masks properties of logged values.
9
+ *
10
+ * - A path without dots (`password`) matches a property with that name at any
11
+ * depth.
12
+ * - A dotted path (`user.password`) matches when the last keys leading to a
13
+ * property are those keys, at any depth. Array indices are not keys, so
14
+ * `users.password` also matches `{ users: [{ password }] }`.
15
+ * - Keys are compared case-insensitively.
16
+ *
17
+ * Plain objects, arrays, class instances, errors (including `cause` and
18
+ * `AggregateError#errors`), `Map`s (string keys) and `Set`s are traversed.
19
+ * Only the objects on the way to a redacted property are copied; the rest is
20
+ * returned as is, and the input is never mutated. A circular reference is
21
+ * replaced with `"[Circular]"` (so the objects on the cycle are copied too). If
22
+ * the value can't be traversed (for example, a getter throws), the whole value
23
+ * is replaced with the censor.
24
+ *
25
+ * @returns `undefined` when there is nothing to redact.
26
+ */
27
+ export declare function createRedactor(paths: string[], censor?: string): Redactor | undefined;
@@ -0,0 +1,225 @@
1
+ export const DEFAULT_REDACT_CENSOR = '[REDACTED]';
2
+ const CIRCULAR = '[Circular]';
3
+ /**
4
+ * Creates a function that masks properties of logged values.
5
+ *
6
+ * - A path without dots (`password`) matches a property with that name at any
7
+ * depth.
8
+ * - A dotted path (`user.password`) matches when the last keys leading to a
9
+ * property are those keys, at any depth. Array indices are not keys, so
10
+ * `users.password` also matches `{ users: [{ password }] }`.
11
+ * - Keys are compared case-insensitively.
12
+ *
13
+ * Plain objects, arrays, class instances, errors (including `cause` and
14
+ * `AggregateError#errors`), `Map`s (string keys) and `Set`s are traversed.
15
+ * Only the objects on the way to a redacted property are copied; the rest is
16
+ * returned as is, and the input is never mutated. A circular reference is
17
+ * replaced with `"[Circular]"` (so the objects on the cycle are copied too). If
18
+ * the value can't be traversed (for example, a getter throws), the whole value
19
+ * is replaced with the censor.
20
+ *
21
+ * @returns `undefined` when there is nothing to redact.
22
+ */
23
+ export function createRedactor(paths, censor = DEFAULT_REDACT_CENSOR) {
24
+ const keys = new Set();
25
+ const dottedPaths = [];
26
+ for (const path of paths) {
27
+ const segments = path
28
+ .split('.')
29
+ .map(segment => segment.trim().toLowerCase())
30
+ .filter(segment => segment.length > 0);
31
+ if (segments.length === 1) {
32
+ keys.add(segments[0]);
33
+ }
34
+ else if (segments.length > 1) {
35
+ dottedPaths.push(segments);
36
+ }
37
+ }
38
+ if (keys.size === 0 && dottedPaths.length === 0) {
39
+ return undefined;
40
+ }
41
+ return (value) => {
42
+ // Lower-cased keys from the root to the value being visited.
43
+ const path = [];
44
+ // Objects on that path, to detect circular references.
45
+ const ancestors = [];
46
+ const isRedacted = (key) => {
47
+ if (keys.has(key)) {
48
+ return true;
49
+ }
50
+ return dottedPaths.some(segments => {
51
+ const last = segments.length - 1;
52
+ if (segments[last] !== key || last > path.length) {
53
+ return false;
54
+ }
55
+ for (let i = 1; i <= last; i++) {
56
+ if (segments[last - i] !== path[path.length - i]) {
57
+ return false;
58
+ }
59
+ }
60
+ return true;
61
+ });
62
+ };
63
+ // Each call starts with a fresh "path" and "ancestors", so an exception
64
+ // (caught below) doesn't need to restore them.
65
+ const visitProperty = (key, propertyValue) => {
66
+ const normalizedKey = key.toLowerCase();
67
+ if (isRedacted(normalizedKey)) {
68
+ return censor;
69
+ }
70
+ if (typeof propertyValue !== 'object' || propertyValue === null) {
71
+ return propertyValue;
72
+ }
73
+ path.push(normalizedKey);
74
+ const redacted = visit(propertyValue);
75
+ path.pop();
76
+ return redacted;
77
+ };
78
+ const visit = (current) => {
79
+ if (typeof current !== 'object' || current === null) {
80
+ return current;
81
+ }
82
+ if (ancestors.includes(current)) {
83
+ return CIRCULAR;
84
+ }
85
+ let redacted;
86
+ ancestors.push(current);
87
+ if (Array.isArray(current)) {
88
+ redacted = redactArray(current);
89
+ }
90
+ else if (Object.getPrototypeOf(current) === Object.prototype) {
91
+ redacted = redactObject(current);
92
+ }
93
+ else if (current instanceof Map) {
94
+ redacted = redactMap(current);
95
+ }
96
+ else if (current instanceof Set) {
97
+ redacted = redactSet(current);
98
+ }
99
+ else if (isOpaque(current)) {
100
+ redacted = current;
101
+ }
102
+ else {
103
+ redacted = redactObject(current);
104
+ }
105
+ ancestors.pop();
106
+ return redacted;
107
+ };
108
+ const redactArray = (array) => {
109
+ let copy;
110
+ for (let i = 0; i < array.length; i++) {
111
+ const redacted = visit(array[i]);
112
+ if (!Object.is(redacted, array[i])) {
113
+ copy ??= array.slice();
114
+ copy[i] = redacted;
115
+ }
116
+ }
117
+ return copy ?? array;
118
+ };
119
+ const redactMap = (map) => {
120
+ let changed = false;
121
+ const entries = [];
122
+ for (const [key, entryValue] of map) {
123
+ const redacted = typeof key === 'string'
124
+ ? visitProperty(key, entryValue)
125
+ : visit(entryValue);
126
+ changed ||= !Object.is(redacted, entryValue);
127
+ entries.push([key, redacted]);
128
+ }
129
+ return changed ? new Map(entries) : map;
130
+ };
131
+ const redactSet = (set) => {
132
+ let changed = false;
133
+ const values = [];
134
+ for (const setValue of set) {
135
+ const redacted = visit(setValue);
136
+ changed ||= !Object.is(redacted, setValue);
137
+ values.push(redacted);
138
+ }
139
+ return changed ? new Set(values) : set;
140
+ };
141
+ const redactObject = (object) => {
142
+ const source = object;
143
+ const keysToVisit = Object.keys(source);
144
+ if (object instanceof Error) {
145
+ // Non-enumerable, but part of the logged error.
146
+ for (const key of ['cause', 'errors']) {
147
+ if (Object.prototype.hasOwnProperty.call(object, key) &&
148
+ !keysToVisit.includes(key)) {
149
+ keysToVisit.push(key);
150
+ }
151
+ }
152
+ }
153
+ let changes;
154
+ for (const key of keysToVisit) {
155
+ const original = source[key];
156
+ const redacted = visitProperty(key, original);
157
+ if (!Object.is(redacted, original)) {
158
+ changes ??= new Map();
159
+ changes.set(key, redacted);
160
+ }
161
+ }
162
+ if (!changes) {
163
+ return object;
164
+ }
165
+ if (Object.getPrototypeOf(object) === Object.prototype) {
166
+ const copy = { ...source };
167
+ for (const [key, redacted] of changes) {
168
+ if (key === '__proto__') {
169
+ // An assignment would call the "__proto__" setter.
170
+ Object.defineProperty(copy, key, {
171
+ value: redacted,
172
+ enumerable: true,
173
+ writable: true,
174
+ configurable: true,
175
+ });
176
+ }
177
+ else {
178
+ copy[key] = redacted;
179
+ }
180
+ }
181
+ return copy;
182
+ }
183
+ // Keeps the prototype and the other properties (including the
184
+ // non-enumerable "message" and "stack" of errors).
185
+ const descriptors = Object.getOwnPropertyDescriptors(object);
186
+ if (object instanceof Error && descriptors.stack?.get) {
187
+ // V8 may define "stack" as an accessor that only works on the
188
+ // original error.
189
+ descriptors.stack = {
190
+ value: object.stack,
191
+ enumerable: descriptors.stack.enumerable,
192
+ writable: true,
193
+ configurable: true,
194
+ };
195
+ }
196
+ for (const [key, redacted] of changes) {
197
+ descriptors[key] = {
198
+ value: redacted,
199
+ enumerable: descriptors[key].enumerable,
200
+ writable: true,
201
+ configurable: true,
202
+ };
203
+ }
204
+ return Object.create(Object.getPrototypeOf(object), descriptors);
205
+ };
206
+ try {
207
+ return visit(value);
208
+ }
209
+ catch {
210
+ return censor;
211
+ }
212
+ };
213
+ }
214
+ /**
215
+ * Objects whose content isn't made of properties worth traversing.
216
+ */
217
+ function isOpaque(value) {
218
+ return (value instanceof Date ||
219
+ value instanceof RegExp ||
220
+ value instanceof Promise ||
221
+ value instanceof WeakMap ||
222
+ value instanceof WeakSet ||
223
+ value instanceof ArrayBuffer ||
224
+ ArrayBuffer.isView(value));
225
+ }