One capital letter bypassed TLS in Bun. Ensemble caught it.

serverName vs servername -- how a casing mismatch let attackers MITM every Postgres, Redis, and MySQL connection.

The bug

Bun (89k stars) had a TLS hostname verification bypass in tls.connect(). One file returned a value under one key. Another file read it under a different key. The difference was a single capital letter.

In src/js/node/tls.ts, the host fallback was returned under the key serverName (camelCase). In src/js/node/net.ts, the consumer read tls.servername (all lowercase). The result: self.servername was always undefined. The guard && self.servername evaluated to false. checkServerIdentity was skipped entirely. And tls.connect({host, port}) without an explicit servername option reported authorized: true for any certificate.

Here is the one-letter difference that broke TLS for every Bun user:

The mismatch
// src/js/node/tls.ts — the producer
return { serverName: host || "localhost" };
          ^
// src/js/node/net.ts — the consumer
if (self.authorized && self.servername) {
  checkServerIdentity(self.servername, cert);
}
// self.servername is undefined — check never runs

The impact was severe. An attacker with any valid CA-signed certificate -- a free Let's Encrypt cert for attacker.com -- could intercept TLS connections from any Bun application. Every pg.connect(), every ioredis connection, every mysql2 query, every nodemailer send. The certificate didn't need to match the hostname. The check that would have caught the mismatch never executed.

This wasn't a regression. The bug existed since the TLS implementation was written. Every version of Bun with tls.connect() was affected.

1
capital letter
caused the bypass
4
libraries exposed
pg, ioredis, mysql2, nodemailer
17
issues found
by 4 Ensemble reviewers

The fix: oven-sh/bun #29096 -- "tls: fix tls.connect skipping hostname verification when servername omitted."


What Ensemble found

We ran Ensemble against the fix PR with 4 independent reviewers: a Security Engineer, an Architecture Reviewer, a Senior Code Reviewer, and a Skeptic Reviewer. They produced 17 issues total. The interesting part isn't just that they validated the fix -- it's that they found problems in the fix itself.

SE
Security Engineer
Identified the core vulnerability
"The original code skipped checkServerIdentity when self.servername was undefined. This means tls.connect({host, port}) without explicit servername would report authorized: true for ANY certificate -- a complete hostname verification bypass."

The Security Engineer validated the fix, confirmed it addressed the bypass, and noted RFC 6066 compliance: IP addresses should be rejected for SNI, which the fix handles correctly. This is the straightforward value -- an expert-persona reviewer confirming the security properties of a patch.

But the Architecture Reviewer found something more interesting.

AR
Architecture Reviewer
Found issues in the fix itself
"The hostname verification logic is duplicated across SocketHandlers and SocketHandlers2. This DRY violation already caused a bug in this PR -- proving the maintenance burden is real."
"Subtle ordering dependency: the fix relies on self._host and self._port being set BEFORE async callbacks trigger the TLS handshake. If initialization order changes, the security fix silently breaks."

The Architecture Reviewer flagged two structural weaknesses in the fix PR itself. First: the hostname verification logic was duplicated across two handler implementations (SocketHandlers and SocketHandlers2). This is the exact kind of copy-paste that caused the original bug -- a naming inconsistency between two files. Duplicating the fix across two handlers means the next developer who touches this code has to remember to update both. The Architecture Reviewer pointed out that this already went wrong once in this very PR.

Second: a fragile fallback chain. The fix uses self.servername || self._host || "localhost". That trailing "localhost" means a misconfigured connection silently verifies against "localhost" instead of failing. A security fix that fails silently is a security fix that will eventually stop being a security fix.

SR
Senior Code Reviewer
Noted test certificate concerns
"Test certificates use specific CN/SAN details that may not cover edge cases like wildcard matching or IP-based connections."
SK
Skeptic Reviewer
0 issues found
No issues flagged.

Why this bug is hard for humans

This is a cross-file naming inconsistency. The producer (tls.ts) and the consumer (net.ts) are in different files. A human reviewing either file in isolation would see correct-looking code. serverName is a perfectly reasonable key. servername is a perfectly reasonable property. You have to hold both files in your head simultaneously and notice that the casing doesn't match.

JavaScript doesn't help. There's no type error. No runtime exception. undefined is falsy, so the guard silently skips the check. The connection succeeds. The authorized property is true. Everything looks correct unless you specifically test with a mismatched certificate and verify that the connection is rejected.

The failure mode is silence. The connection works. The certificate is "authorized." There is no error, no warning, no log entry. The security check simply doesn't execute. This is the worst kind of bug -- the kind where the system appears to be working correctly while providing zero protection.

Multi-model review helps here because each reviewer parses the full diff independently. They don't share context. They don't inherit assumptions. When one file writes serverName and another reads servername, three independent reviewers parsing the code fresh are more likely to notice the mismatch than a single reviewer who has been staring at the codebase for hours.


The honest part

Three things to be upfront about.

1
We reviewed the fix, not the original code. We ran Ensemble on PR #29096, which is the patch that corrects the bug. We're showing "Ensemble validates the fix and explains the vulnerability," not "Ensemble would have prevented this bug from shipping." For that claim, we'd need to have reviewed the original TLS implementation -- a much larger diff with a much harder needle to find.
2
The Skeptic Reviewer found nothing. 0 issues. On a PR that fixes a TLS hostname verification bypass. The Skeptic persona is still not pulling its weight. Four reviewers, but functionally three contributed. We're iterating on this.
3
The Architecture Reviewer's findings are the real story. Validating that a security fix addresses the vulnerability is table stakes. Finding structural weaknesses in the fix itself -- duplicated logic that already broke once, fragile fallback chains that fail silently -- that's the value of having multiple perspectives. A security reviewer confirms the fix works. An architecture reviewer asks whether the fix will keep working.

References

Reviewers
Security Engineer, Architecture, Senior Code, Skeptic
Issues found
17 across 4 reviewers
Affected files
tls.ts (producer), net.ts (consumer)

Cross-file bugs hide in plain sight

Ensemble runs multiple independent AI reviewers on every PR. Different perspectives catch what single-pass review misses -- like a capital letter that bypassed TLS.

Read-only access · Code deleted after review · Free for public repos