@bymax-one/nest-config 1.2.0 → 1.2.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 (3) hide show
  1. package/CHANGELOG.md +38 -3
  2. package/README.md +54 -5
  3. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -11,6 +11,39 @@ as the GitHub Release body, so each released version needs a matching
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.2.1] - 2026-10-30
15
+
16
+ ### Security
17
+
18
+ - **The `zod` peer range now starts at `4.6.6`, the first release without
19
+ GHSA-mrhx-c7xc-m3w5** (high: memory exhaustion while validating oversized constrained
20
+ collections). The range `^4.0.0` declared installs below it as supported, and the
21
+ advisory applies to them in an application that validates untrusted collection input
22
+ through its own schemas. **Apply to a derived backend:** raise `zod` to `4.6.6` or later
23
+ (`pnpm up zod`); an install below it is outside the declared peer range.
24
+
25
+ ### Documentation
26
+
27
+ - **Support policy: only the latest minor receives security fixes, and the Discussions
28
+ link is gone.** `SECURITY.md` supports the latest minor alone; earlier versions get no fix and no
29
+ backport, and the guidance is to upgrade. The issue-template chooser no longer links to
30
+ Discussions; the security contact by e-mail remains.
31
+
32
+ - **`strict` names the two prefixed-variable sources a search will not find.** Both came
33
+ from an adopter enabling it in a real deployment. A container's own `environment:` block
34
+ assigns a variable whose host-side name is a different one, so a search over
35
+ interpolations misses it — and it fails on the default path, because Compose assigns an
36
+ unset interpolation the empty string rather than absent, and validation skips only
37
+ `undefined`. Kubernetes service links appear in no file at all: at the default
38
+ `enableServiceLinks`, a Service named `redis` supplies `REDIS_PORT`,
39
+ `REDIS_SERVICE_HOST`, `REDIS_SERVICE_PORT` and the `REDIS_PORT_6379_TCP*` family into a
40
+ closed `redis` namespace — a crash loop in production that never appears in CI.
41
+
42
+ The cost of an open namespace is also stated in its sharpest form rather than its
43
+ general one: the typo it cannot catch may be in that namespace's own declared variable,
44
+ so `OTEL_ENABLE` is waived, the leaf falls back to its default, and the feature stays
45
+ off with nothing reported.
46
+
14
47
  ## [1.2.0] - 2026-09-04
15
48
 
16
49
  ### Added
@@ -49,8 +82,9 @@ as the GitHub Release body, so each released version needs a matching
49
82
  (GHSA-5jgf-p345-68v8, GHSA-jqff-g426-hqxp — host confusion; GHSA-f65p-4m7j-42xc,
50
83
  GHSA-fph4-wmhf-6fwf — server-side request forgery; CVSS 7.5 each) and `qs`
51
84
  (GHSA-4mjr-xmp4-gh2g, GHSA-x5fp-wj9c-mxmx, CVSS 6.3 each). All three are reached through
52
- the mutation-testing and commit-tooling chains, none is a runtime dependency, and this
53
- package ships `dist` alone — so no consumer resolved a vulnerable version through it.
85
+ the mutation-testing and commit-tooling chains, none is a runtime dependency, and the
86
+ package ships only `dist` as runtime code — so no consumer resolved a vulnerable version
87
+ through it.
54
88
 
55
89
  Recorded here rather than omitted as internal because a consumer reading an advisory feed
56
90
  needs to know whether a library in their tree pulled one in, and "it did not" is an answer
@@ -375,4 +409,5 @@ have regressed from. They are kept because the reasoning is worth having.
375
409
  [1.1.2]: https://github.com/bymaxone/nest-config/compare/v1.1.1...v1.1.2
376
410
  [1.1.3]: https://github.com/bymaxone/nest-config/compare/v1.1.2...v1.1.3
377
411
  [1.2.0]: https://github.com/bymaxone/nest-config/compare/v1.1.3...v1.2.0
378
- [Unreleased]: https://github.com/bymaxone/nest-config/compare/v1.2.0...HEAD
412
+ [1.2.1]: https://github.com/bymaxone/nest-config/compare/v1.2.0...v1.2.1
413
+ [Unreleased]: https://github.com/bymaxone/nest-config/compare/v1.2.1...HEAD
package/README.md CHANGED
@@ -125,7 +125,7 @@ pnpm add @bymax-one/nest-config @nestjs/common @nestjs/core reflect-metadata zod
125
125
  | `@nestjs/common` | `^11.0.16` |
126
126
  | `@nestjs/core` | `^11.1.18` |
127
127
  | `reflect-metadata` | `^0.2.0` |
128
- | `zod` | `^4.0.0` |
128
+ | `zod` | `^4.6.6` |
129
129
 
130
130
  ## 🚀 Quick Start
131
131
 
@@ -343,11 +343,60 @@ What the waiver does and does not do:
343
343
  and `otelExporter` (closed), `OTEL_EXPORTER_TYPO` belongs to `otelExporter`
344
344
  and is still reported.
345
345
 
346
+ #### Where prefixed variables come from that a sweep will miss
347
+
348
+ Before enabling `strict`, know that reading your own repository does not enumerate
349
+ what reaches `process.env`. Two sources defeat a migration sweep in different ways
350
+ — one because the search you would naturally run is the wrong one, the other
351
+ because there is nothing to search — and both were found by adopters rather than
352
+ by reading manifests.
353
+
354
+ **A container's own `environment:` block.** Compose assigns variables to the
355
+ container with no `${...}` on the host side to give them away:
356
+
357
+ ```yaml
358
+ services:
359
+ app:
360
+ environment:
361
+ REDIS_PASSWORD: ${STACK_REDIS_PASSWORD:-}
362
+ ```
363
+
364
+ `REDIS_PASSWORD` lands in the application's environment. A repository-wide search
365
+ for the prefix does find this line — but the search a migration actually reaches
366
+ for is over interpolations, `${REDIS_`, and that one finds nothing, because the
367
+ interpolated name is the _other_ one. The variable the container receives and the
368
+ variable the host supplies are different names on the same line.
369
+
370
+ This one fails on the default path rather than only when someone opts in, and the
371
+ reason is worth stating: **Compose assigns an unset interpolation the empty string,
372
+ not absent.** Validation skips a variable whose value is `undefined` — an absent
373
+ variable is the schema's business, not strict mode's — and `''` is not `undefined`.
374
+ So the boot fails even when nobody set a password. That is deliberate: a validator
375
+ that ignored empty strings would also ignore a real misconfiguration that sets one.
376
+
377
+ **Kubernetes service links, which appear in no file at all.** With
378
+ `enableServiceLinks` at its default, a pod receives Docker-link-compatible
379
+ variables for every Service in its namespace. A Service named `redis` supplies
380
+ `REDIS_PORT`, `REDIS_SERVICE_HOST`, `REDIS_SERVICE_PORT` and the
381
+ `REDIS_PORT_6379_TCP*` family — all inside a closed `redis` namespace, none
382
+ declared, none written down anywhere you can search. Under `strict` that is a crash
383
+ loop in production over variables the application never reads, and it will not have
384
+ appeared in CI.
385
+
386
+ Set `enableServiceLinks: false` on the pod spec, or open the namespace. Managed
387
+ add-ons injecting names like `REDIS_PUBLIC_URL` or `DATABASE_URL_UNPOOLED` are the
388
+ same family.
389
+
346
390
  **The cost, stated plainly:** an open namespace cannot tell a foreign variable
347
- from a misspelled local one. Under an open `otel`, a typo of `OTEL_ENABLED`
348
- passes silently and the leaf falls back to its default. No design removes this —
349
- the two cases are indistinguishable by name — so open the namespaces whose
350
- prefix genuinely belongs to someone else, and leave the rest closed.
391
+ from a misspelled local one. No design removes this — the two cases are
392
+ indistinguishable by name — so open the namespaces whose prefix genuinely belongs
393
+ to someone else, and leave the rest closed.
394
+
395
+ The sharpest form of that cost is worth seeing before you accept it. The typo can
396
+ be in the open namespace's **own** declared variable: write `OTEL_ENABLE` instead
397
+ of `OTEL_ENABLED` and it is waived like any other unclaimed name, the leaf falls
398
+ back to its default, and the feature you meant to switch on stays off with nothing
399
+ reported. A closed namespace would have named that variable and refused to boot.
351
400
 
352
401
  > Filtering the foreign names out of `source` instead is **not** equivalent, and
353
402
  > is worse in a way that is easy to miss: removing `OTEL_*` from the source also
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-config",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Typed and validated environment configuration entry point for NestJS 11 applications, built on Zod v4.",
5
5
  "author": "Bymax One <support@bymax.one>",
6
6
  "license": "MIT",
@@ -91,7 +91,7 @@
91
91
  "@nestjs/common": "^11.0.16",
92
92
  "@nestjs/core": "^11.1.18",
93
93
  "reflect-metadata": "^0.2.0",
94
- "zod": "^4.0.0"
94
+ "zod": "^4.6.6"
95
95
  },
96
96
  "devDependencies": {
97
97
  "@arethetypeswrong/cli": "^0.18.2",
@@ -126,7 +126,7 @@
126
126
  "ts-node": "^10.9.2",
127
127
  "tsup": "^8.5.1",
128
128
  "typescript": "^5.9.3",
129
- "zod": "^4.0.0"
129
+ "zod": "^4.6.7"
130
130
  },
131
131
  "keywords": [
132
132
  "nestjs",