Skip to content

Commit 5b19309

Browse files
committed
docs(security): cite CVE-2026-45091 throughout the repo
MITRE has formally assigned CVE-2026-45091 to the JWS-payload TOTP-secret leak that affected 0.1.0-alpha.{1,2,3} and was patched in alpha.4. The advisory entry was previously cited as 'CVE-2026-XXXXX' or 'CVE was requested' — replaced with the official identifier alongside the existing GHSA-x3r2-fj3r-g5mv GitHub Security Advisory link. Touched: - README.md: CVE badge added next to threat-model badge - THREAT_MODEL.md: section 6 (Token-payload exposure) now cites the CVE directly with NVD link - CHANGELOG.md: alpha.4 security banner cites CVE; 0.1.0 summary expanded with the CVE link; new [Unreleased] entry documenting the assignment - java/sealed-env-core/.../SealedEnvSmokeTest.java: regression comment now anchors the test to the CVE id Also reverts a stale-info edit from a previous commit that claimed Node now writes 'KDF=argon2id' — Node still writes 'KDF=scrypt' (the spec accepts both for cross-stack interop). The README diagram and wire-format example now reflect reality.
1 parent 3881b40 commit 5b19309

4 files changed

Lines changed: 33 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ files written today will remain readable forever. See [SPEC.md](./SPEC.md).
1212

1313
## [Unreleased]
1414

15+
### Security
16+
17+
- **CVE-2026-45091 has been formally assigned** to the JWS-payload
18+
TOTP-secret leak that affected `0.1.0-alpha.{1,2,3}` and was patched
19+
in alpha.4 on 2026-05-07. The MITRE entry is now public at
20+
[nvd.nist.gov/vuln/detail/CVE-2026-45091](https://nvd.nist.gov/vuln/detail/CVE-2026-45091).
21+
No code change in this entry — references throughout the repository
22+
(README, THREAT_MODEL, Java regression tests, CHANGELOG) updated to
23+
cite the official identifier alongside the existing GitHub Security
24+
Advisory ([GHSA-x3r2-fj3r-g5mv](https://github.com/davidalmeidac/sealed-env/security/advisories/GHSA-x3r2-fj3r-g5mv)).
25+
1526
---
1627

1728
## [0.1.0] — 2026-05-07
@@ -42,9 +53,10 @@ What 0.1.0 represents:
4253
Render, Railway, Heroku, Docker, Kubernetes, generic SSH) plus an
4354
OIDC-federation pattern for shops that want zero persistent
4455
master-key storage in CI.
45-
- A public threat model (T1-T13) and a track record of CVE response
46-
(alpha.6 closed a JWS-payload TOTP-secret leak in under four hours
47-
with cross-stack vectors and a migration playbook).
56+
- A public threat model (T1-T13) and a track record of CVE response.
57+
alpha.6 closed [CVE-2026-45091](https://nvd.nist.gov/vuln/detail/CVE-2026-45091)
58+
(JWS-payload TOTP-secret leak) in under four hours with cross-stack
59+
vectors and a migration playbook.
4860

4961
### Added
5062

@@ -383,12 +395,14 @@ and vice versa.
383395

384396
## [0.1.0-alpha.4] — 2026-05-07
385397

386-
> **🚨 SECURITY: this release fixes a critical issue in `enterprise` mode.
387-
> Prior versions (alpha.1, alpha.2, alpha.3) embedded the operator's TOTP
388-
> secret in the JWS payload of every unseal token. JWS payload is base64-
389-
> encoded JSON, NOT encrypted — anyone observing a token (CI logs, container
390-
> env dumps, stack traces) could extract the secret and use it (with the
391-
> master key) to mint unseal tokens for FUTURE deploys indefinitely.**
398+
> **🚨 SECURITY: [CVE-2026-45091](https://nvd.nist.gov/vuln/detail/CVE-2026-45091)
399+
> ([GHSA-x3r2-fj3r-g5mv](https://github.com/davidalmeidac/sealed-env/security/advisories/GHSA-x3r2-fj3r-g5mv)).
400+
> This release fixes a critical issue in `enterprise` mode. Prior versions
401+
> (alpha.1, alpha.2, alpha.3) embedded the operator's TOTP secret in the JWS
402+
> payload of every unseal token. JWS payload is base64-encoded JSON, NOT
403+
> encrypted — anyone observing a token (CI logs, container env dumps, stack
404+
> traces) could extract the secret and use it (with the master key) to mint
405+
> unseal tokens for FUTURE deploys indefinitely.**
392406
>
393407
> **All `0.1.0-alpha.{1,2,3}` releases are deprecated on npm and Maven
394408
> Central. If you adopted enterprise mode in any of those versions:**

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ with optional TOTP-based unsealing for production deploys.
1515
[![Java CI](https://img.shields.io/github/actions/workflow/status/davidalmeidac/sealed-env/java-ci.yml?branch=main&style=flat-square&color=1a1612&labelColor=c4471f&label=java%20ci)](https://github.com/davidalmeidac/sealed-env/actions/workflows/java-ci.yml)
1616
[![License](https://img.shields.io/badge/license-MIT-1a1612?style=flat-square&labelColor=c4471f)](LICENSE)
1717
[![Threat model](https://img.shields.io/badge/threat--model-public-1a1612?style=flat-square&labelColor=c4471f)](THREAT_MODEL.md)
18+
[![CVE response](https://img.shields.io/badge/CVE--2026--45091-resolved%20%E2%9C%93-1a1612?style=flat-square&labelColor=c4471f)](https://nvd.nist.gov/vuln/detail/CVE-2026-45091)
1819

1920
[Docs](docs/) · [Threat Model](THREAT_MODEL.md) · [File Format](SPEC.md) · [Security Policy](SECURITY.md) · [Landing](https://davidalmeidac.github.io/sealed-env/)
2021

@@ -54,7 +55,7 @@ pipeline is fully compromised, attackers cannot decrypt without the operator's p
5455
│ ──────────── │ │ ──────────── │
5556
│ • CLI: sealed-env seal │ │ • SealedEnv core lib │
5657
│ • npm: sealed-env │ │ • Spring Boot starter │
57-
│ • writes KDF=argon2id │ │ • writes KDF=argon2id │
58+
│ • writes KDF=scrypt │ │ • writes KDF=argon2id │
5859
└────────────┬────────────┘ └────────────┬────────────┘
5960
│ │
6061
│ both speak SEALED-ENV-V1 │
@@ -64,7 +65,7 @@ pipeline is fully compromised, attackers cannot decrypt without the operator's p
6465
│ .env.sealed │
6566
│ ─────────────────────────── │
6667
│ SEALED-ENV-V1 MODE=team │
67-
│ KDF=argon2id
68+
│ KDF=<scrypt|argon2id>
6869
│ KDF-PARAMS=... SALT=... │
6970
│ NONCE=... AAD-DIGEST=... │
7071
│ HMAC=... CREATED=2026-... │

THREAT_MODEL.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
- A single misconfig (Actuator exposed) defeats encryption-at-rest
6363
- Plaintext secrets must not survive boot
6464

65-
### 6. Token-payload exposure (lesson from sealed-env's own CVE-2026-XXXXX)
65+
### 6. Token-payload exposure (lesson from sealed-env's own CVE-2026-45091)
6666

6767
**What happened (to us, May 2026):**
6868

@@ -76,7 +76,8 @@ the leaked secret allowed minting **future** unseal tokens indefinitely.
7676
The reviewer found it in 5 minutes by base32-encoding the bytes from
7777
`payload.totp_secret` and comparing to the operator's `.env.local` value.
7878

79-
Affected versions are deprecated and a CVE was requested. See
79+
Affected versions are deprecated. Tracked as
80+
**[CVE-2026-45091](https://nvd.nist.gov/vuln/detail/CVE-2026-45091)** /
8081
[GHSA-x3r2-fj3r-g5mv](https://github.com/davidalmeidac/sealed-env/security/advisories/GHSA-x3r2-fj3r-g5mv).
8182

8283
**What it teaches us (and what we now do):**

java/sealed-env-core/src/test/java/io/github/davidalmeidac/sealedenv/SealedEnvSmokeTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,10 @@ void fullFlow() {
154154
String serialized = SealedEnv.seal(opts).serialized();
155155
assertThat(serialized).contains("\nEPOCH-COMMIT=");
156156
assertThat(serialized).contains("\nCHALLENGE-BIND=enabled\n");
157-
// Hardening regression: the serialized file must NEVER carry
158-
// the literal TOTP secret. Pre-alpha.4 files exposed it via
159-
// `totp_secret` in the JWS payload of unseal tokens.
157+
// Hardening regression for CVE-2026-45091: the serialized
158+
// file must NEVER carry the literal TOTP secret. Pre-alpha.4
159+
// files exposed it via `totp_secret` in the JWS payload of
160+
// unseal tokens.
160161
assertThat(serialized).doesNotContain("TOTP-VERIFIER=");
161162

162163
SealedFile file = SealedFileParser.parse(serialized);

0 commit comments

Comments
 (0)