@lenne.tech/nest-server 11.43.4 → 11.43.6
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/.claude/rules/configurable-features.md +1 -0
- package/.claude/rules/package-management.md +56 -7
- package/.claude/rules/testing.md +3 -3
- package/.claude/rules/versioning.md +13 -2
- package/FRAMEWORK-API.md +2 -2
- package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
- package/dist/core/common/services/email.service.d.ts +3 -0
- package/dist/core/common/services/email.service.js +20 -0
- package/dist/core/common/services/email.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/security-overrides.md +15 -0
- package/migration-guides/11.43.4-to-11.43.5.md +102 -0
- package/migration-guides/11.43.5-to-11.43.6.md +53 -0
- package/package.json +1 -1
- package/src/core/common/interfaces/server-options.interface.ts +11 -0
- package/src/core/common/services/email.service.ts +33 -1
|
@@ -74,6 +74,21 @@ overrides:
|
|
|
74
74
|
'@graphql-tools/utils@>=12.0.0 <12.1.0': '12.1.0'
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
### Advisories no override can fix yet: `minimatch` and `glob` (11.43.5)
|
|
78
|
+
|
|
79
|
+
[GHSA-g3hv-75wg-m249](https://github.com/advisories/GHSA-g3hv-75wg-m249) (minimatch `< 11.0.1`, moderate, DoS
|
|
80
|
+
through globparts amplification) and [GHSA-3g3m-2fjr-6pw4](https://github.com/advisories/GHSA-3g3m-2fjr-6pw4)
|
|
81
|
+
(glob `<= 14.0.1`, moderate) are fixed only in new majors that no consumer can take yet: `@nestjs/cli` 11.0.24, the
|
|
82
|
+
newest 11.x, pins glob 13.0.6 and fork-ts-checker-webpack-plugin 9.1.0 (minimatch ^3) exactly, ts-morph 28
|
|
83
|
+
(`@ts-morph/common` 0.29), nodemon, `@swc/cli` and glob 13 need the 9.x / 10.x lines, rimraf 6.1.3 needs glob 13 — all
|
|
84
|
+
on their newest release. An override across the major is no way out (minimatch 11 removed `makeRe()` and
|
|
85
|
+
`Minimatch#regexp`). nest-server suppresses both in its own repository with `auditConfig.unusableFixConsumers`, which
|
|
86
|
+
`check:overrides` re-tests on every run. What it found for its own tree: the only production path is
|
|
87
|
+
`@lenne.tech/nest-server > ts-morph > @ts-morph/common > minimatch`, used by the Permissions module's scanner with
|
|
88
|
+
globs the developer writes; every other path is dev tooling. Your project's tree may have other paths, so check
|
|
89
|
+
`pnpm why minimatch --prod` and `pnpm why glob --prod` before you suppress the same way, and remove the entries once
|
|
90
|
+
your consumers move to minimatch 11 / glob 14.
|
|
91
|
+
|
|
77
92
|
### Retired: `@hono/node-server` (removed 2026-08-22, nest-server 11.36.1)
|
|
78
93
|
|
|
79
94
|
This list used to carry a third entry. `@modelcontextprotocol/sdk` declared `@hono/node-server@^1.19.9`
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Migration Guide: 11.43.4 → 11.43.5
|
|
2
|
+
|
|
3
|
+
A security patch for projects that still run on 11.x. It is published under the npm dist-tag `latest-11`, not `latest`:
|
|
4
|
+
12.x stays the version a plain install resolves to, and moving every project to 12.x remains the goal. This release
|
|
5
|
+
carries only urgent security fixes; functional fixes from 12.x are not backported.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
| Category | Details |
|
|
10
|
+
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| **Bugfixes** | None in the framework code |
|
|
12
|
+
| **New Features** | None |
|
|
13
|
+
| **Framework API** | Unchanged |
|
|
14
|
+
| **Dependencies** | `follow-redirects` 1.16.1 → 1.16.2, transitive (§1) |
|
|
15
|
+
| **Breaking Changes** | None |
|
|
16
|
+
| **Migration Effort** | Raise the version, refresh one transitive package (§1); decide on two advisories that have no usable fix yet (§2) |
|
|
17
|
+
|
|
18
|
+
## Quick Migration
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# npm mode: the version is pinned exactly, and `latest` is 12.x, so name the version
|
|
22
|
+
pnpm add @lenne.tech/nest-server@11.43.5
|
|
23
|
+
pnpm update --depth Infinity follow-redirects
|
|
24
|
+
pnpm audit --prod
|
|
25
|
+
pnpm test
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Vendor-mode projects update the core to the tag `11.43.5` with the core updater and refresh `follow-redirects` the same
|
|
29
|
+
way.
|
|
30
|
+
|
|
31
|
+
## 1. follow-redirects 1.16.2
|
|
32
|
+
|
|
33
|
+
[GHSA-mrwp-f3q8-3g9p](https://github.com/advisories/GHSA-mrwp-f3q8-3g9p) (high, CVSS 5.9): `sensitiveHeaders` arrays
|
|
34
|
+
from another realm are discarded, so custom credentials can follow a redirect to another host. It covers 1.16.0 and
|
|
35
|
+
1.16.1, patched in 1.16.2.
|
|
36
|
+
|
|
37
|
+
nest-server reaches it through `node-mailjet > axios > follow-redirects`. axios requests a caret range, so no override is
|
|
38
|
+
needed, but an existing lockfile keeps 1.16.1 until it is refreshed (the command in Quick Migration). Projects that
|
|
39
|
+
reach axios on another path are covered by the same refresh.
|
|
40
|
+
|
|
41
|
+
## 2. minimatch and glob: fixed only in a new major
|
|
42
|
+
|
|
43
|
+
[GHSA-g3hv-75wg-m249](https://github.com/advisories/GHSA-g3hv-75wg-m249) (minimatch `< 11.0.1`, moderate, DoS through
|
|
44
|
+
globparts amplification) and [GHSA-3g3m-2fjr-6pw4](https://github.com/advisories/GHSA-3g3m-2fjr-6pw4) (glob `9.0.0` to
|
|
45
|
+
`14.0.1`, moderate, AbortSignal cancellation bypass) were published on 2026-10-30. Both fixes exist only in new majors
|
|
46
|
+
(minimatch 11.0.1, glob 14.0.2), and none of their consumers in the 11.x tree can take them: `@nestjs/cli` 11.0.24, the
|
|
47
|
+
newest 11.x, pins glob 13.0.6 and fork-ts-checker-webpack-plugin 9.1.0 (minimatch ^3) exactly; ts-morph, nodemon,
|
|
48
|
+
`@swc/cli`, glob 13 and rimraf 6 are on their newest releases and request the older majors. An override across the
|
|
49
|
+
major is no way out: minimatch 11 removed `makeRe()` and `Minimatch#regexp`.
|
|
50
|
+
|
|
51
|
+
`pnpm audit --prod` in a project reports minimatch: ts-morph is a dependency of nest-server (the Permissions module's
|
|
52
|
+
scanner), so the path `@lenne.tech/nest-server > ts-morph > @ts-morph/common > minimatch` reaches every production
|
|
53
|
+
tree. That scanner matches globs the developer writes, never request input. glob is dev tooling in nest-server's own
|
|
54
|
+
tree.
|
|
55
|
+
|
|
56
|
+
What a project does depends on its audit gate:
|
|
57
|
+
|
|
58
|
+
- **No blocking `pnpm audit` gate:** nothing to do. Re-check when the consumers move to minimatch 11 / glob 14.
|
|
59
|
+
- **A blocking gate:** check `pnpm why minimatch --prod` and `pnpm why glob --prod` first. Your tree may reach either
|
|
60
|
+
package on a path that does take request input; then that path is a finding of its own. If every path matches
|
|
61
|
+
patterns the developer writes, suppress both advisories with the reason next to them. A project that carries
|
|
62
|
+
`scripts/check-overrides.mjs` (starter-based projects do) declares them in `unusableFixConsumers`, so the guard
|
|
63
|
+
reports the day a consumer moves:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
# pnpm-workspace.yaml of the project — list the consumers YOUR lockfile shows, at their versions
|
|
67
|
+
auditConfig:
|
|
68
|
+
ignoreGhsas:
|
|
69
|
+
- GHSA-g3hv-75wg-m249
|
|
70
|
+
- GHSA-3g3m-2fjr-6pw4
|
|
71
|
+
unusableFixConsumers:
|
|
72
|
+
GHSA-g3hv-75wg-m249: '@swc/cli@0.8.1 + @ts-morph/common@0.29.0 + fork-ts-checker-webpack-plugin@9.1.0 + glob@13.0.6 + nodemon@3.1.14 cannot use 11.0.1'
|
|
73
|
+
GHSA-3g3m-2fjr-6pw4: '@nestjs/cli@11.0.24 + rimraf@6.1.3 cannot use 14.0.2'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
nest-server's own repository carries exactly these entries, with the reasoning in `pnpm-workspace.yaml` and a scope
|
|
77
|
+
guard (`tests/unit/audit-suppression-scope.spec.ts`) that fails if glob reaches the production tree or minimatch reaches
|
|
78
|
+
it through any parent other than `@ts-morph/common`.
|
|
79
|
+
|
|
80
|
+
## 3. `check:overrides`: the guard of 12.x (projects that copied it)
|
|
81
|
+
|
|
82
|
+
Only relevant for a project that carries a copy of `scripts/check-overrides.mjs`. The guard on `release/11.x` is now the
|
|
83
|
+
one 12.x runs, which the declarations in §2 need:
|
|
84
|
+
|
|
85
|
+
- `unusableFixConsumers` takes several consumers, joined with ` + `, each at its assessed version. A listed consumer
|
|
86
|
+
that no longer pulls an affected version fails the run.
|
|
87
|
+
- Only edges to an AFFECTED version count as consumers; a parent that already pulls the fixed release does not. An edge
|
|
88
|
+
is matched by the package it pulls, not by its key (npm aliases).
|
|
89
|
+
- An advisory that names no patched version is looked up in the registry: a release outside every vulnerable range
|
|
90
|
+
makes the suppression `FIX AVAILABLE`, a release younger than `minimumReleaseAge` is reported as `pending`.
|
|
91
|
+
|
|
92
|
+
Copy the file from the tag `11.43.5` to take these over. Nothing changes for a project without the guard.
|
|
93
|
+
|
|
94
|
+
## Not in this release
|
|
95
|
+
|
|
96
|
+
Fixes from 12.x that are not urgent security fixes stay there. They arrive with the move to 12:
|
|
97
|
+
`migration-guides/11.43.x-to-12.0.x.md`.
|
|
98
|
+
|
|
99
|
+
## Module Documentation
|
|
100
|
+
|
|
101
|
+
- [Security overrides](../docs/security-overrides.md)
|
|
102
|
+
- [Package management rules](../.claude/rules/package-management.md)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Migration Guide: 11.43.5 → 11.43.6
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| **New Features** | Optional Reply-To for outgoing mail: `email.defaultReplyTo` and a per-call `replyTo` (§1) |
|
|
8
|
+
| **Breaking Changes** | None |
|
|
9
|
+
| **Migration Effort** | None. Set `email.defaultReplyTo` only if answers to your mail should go to another address (§1) |
|
|
10
|
+
|
|
11
|
+
## Quick Migration
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @lenne.tech/nest-server@11.43.6
|
|
15
|
+
pnpm run update # nest-server-starter based projects
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Vendor-mode projects update the core to the tag `11.43.6`: `/lt-dev:backend:update-nest-server-core --target 11.43.6`.
|
|
19
|
+
Without `--target` the updater syncs to the latest tag, which is on the 12.x line.
|
|
20
|
+
|
|
21
|
+
## 1. Reply-To for outgoing mail
|
|
22
|
+
|
|
23
|
+
`EmailService.sendMail()` can set a `Reply-To` header, so a recipient's answer goes to another address than the
|
|
24
|
+
sender, e.g. a shared support mailbox while mail goes out from `noreply@…`. Nothing changes until you set it.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// config.env.ts
|
|
28
|
+
email: {
|
|
29
|
+
defaultSender: { email: 'noreply@example.com', name: 'Example' },
|
|
30
|
+
defaultReplyTo: 'support@example.com', // or '"Example Support" <support@example.com>'
|
|
31
|
+
},
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
As an environment variable:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
NSC__EMAIL__DEFAULT_REPLY_TO=support@example.com
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A call can override it, or send one mail without it:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
await this.emailService.sendMail(to, subject, { replyTo: 'sales@example.com', htmlTemplate, templateData });
|
|
44
|
+
await this.emailService.sendMail(to, subject, { replyTo: false, htmlTemplate, templateData });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The default applies to every mail the service sends over SMTP, Better-Auth's verification and password-reset mail
|
|
48
|
+
included. Mail sent through Brevo (`brevo.apiKey`) or Mailjet is not affected: their templates carry their own
|
|
49
|
+
reply-to. A value that is not a string is ignored with one warning rather than stopping the mail.
|
|
50
|
+
|
|
51
|
+
## Module Documentation
|
|
52
|
+
|
|
53
|
+
- Configuration reference: `email.defaultReplyTo` in `FRAMEWORK-API.md` (`IServerOptions.email`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.43.
|
|
3
|
+
"version": "11.43.6",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -2510,6 +2510,17 @@ export interface IServerOptions {
|
|
|
2510
2510
|
* SMTP and template configuration for sending emails
|
|
2511
2511
|
*/
|
|
2512
2512
|
email?: {
|
|
2513
|
+
/**
|
|
2514
|
+
* Default Reply-To of every mail `EmailService.sendMail()` sends (11.43.6+): where a recipient's answer goes when it
|
|
2515
|
+
* should not reach the sender address, e.g. a shared support mailbox. An address, or `"Name" <address>`.
|
|
2516
|
+
*
|
|
2517
|
+
* Absent (the default) → no Reply-To header, exactly as before. A call overrides it with `replyTo`, and
|
|
2518
|
+
* `replyTo: false` sends that one mail without it. Set it through `NSC__EMAIL__DEFAULT_REPLY_TO`. Applies to the
|
|
2519
|
+
* SMTP path (nodemailer, `email.smtp`): Brevo and Mailjet templates carry their own reply-to.
|
|
2520
|
+
* @default undefined
|
|
2521
|
+
*/
|
|
2522
|
+
defaultReplyTo?: string;
|
|
2523
|
+
|
|
2513
2524
|
/**
|
|
2514
2525
|
* Data for default sender
|
|
2515
2526
|
*/
|
|
@@ -28,6 +28,9 @@ export class EmailService implements OnModuleDestroy {
|
|
|
28
28
|
/** Once-per-process latch for the SMTP TLS warning — a per-send warning would be noise. */
|
|
29
29
|
private smtpTlsWarningEmitted = false;
|
|
30
30
|
|
|
31
|
+
/** Once-per-process latch for a Reply-To that is not a string. */
|
|
32
|
+
private replyToWarningEmitted = false;
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* Inject services
|
|
33
36
|
*/
|
|
@@ -76,6 +79,31 @@ export class EmailService implements OnModuleDestroy {
|
|
|
76
79
|
);
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The Reply-To of one mail (11.43.6+): the call's `replyTo`, else `email.defaultReplyTo`, else none. `false` (or an
|
|
84
|
+
* empty string) sends this mail without one. A value that is not a string is dropped with a warning instead of
|
|
85
|
+
* failing the mail: a misconfigured default must not stop password-reset or verification mail.
|
|
86
|
+
*/
|
|
87
|
+
protected resolveReplyTo(replyTo: false | string | undefined): string | undefined {
|
|
88
|
+
if (replyTo === false) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
const value: unknown = replyTo ?? this.configService.getFastButReadOnly('email.defaultReplyTo');
|
|
92
|
+
if (value === undefined || value === null) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === 'string') {
|
|
96
|
+
return value.trim() || undefined;
|
|
97
|
+
}
|
|
98
|
+
if (!this.replyToWarningEmitted) {
|
|
99
|
+
this.replyToWarningEmitted = true;
|
|
100
|
+
this.emailServiceLogger.warn(
|
|
101
|
+
`Reply-To ignored: expected an address string (email.defaultReplyTo / replyTo), got ${typeof value}.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
79
107
|
onModuleDestroy(): void {
|
|
80
108
|
if (this.cachedTransporter) {
|
|
81
109
|
this.cachedTransporter.close();
|
|
@@ -94,6 +122,8 @@ export class EmailService implements OnModuleDestroy {
|
|
|
94
122
|
attachments?: Attachment[];
|
|
95
123
|
html?: string;
|
|
96
124
|
htmlTemplate?: string;
|
|
125
|
+
/** Reply-To of this mail; overrides `email.defaultReplyTo`, `false` sends it without one (11.43.6+) */
|
|
126
|
+
replyTo?: false | string;
|
|
97
127
|
senderEmail?: string;
|
|
98
128
|
senderName?: string;
|
|
99
129
|
smtp?: MailTransportOptions;
|
|
@@ -189,11 +219,13 @@ export class EmailService implements OnModuleDestroy {
|
|
|
189
219
|
this.cachedSmtpConfig = smtpKey;
|
|
190
220
|
}
|
|
191
221
|
|
|
192
|
-
// Send mail
|
|
222
|
+
// Send mail; without a Reply-To the header is left out entirely
|
|
223
|
+
const replyTo = this.resolveReplyTo(config.replyTo);
|
|
193
224
|
return this.cachedTransporter.sendMail({
|
|
194
225
|
attachments,
|
|
195
226
|
from: `"${senderName}" <${senderEmail}>`,
|
|
196
227
|
html,
|
|
228
|
+
...(replyTo ? { replyTo } : {}),
|
|
197
229
|
subject,
|
|
198
230
|
text,
|
|
199
231
|
to: recipients,
|