@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.
@@ -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.4",
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,