Skip to content

fix(ext/node): allow tls.Server SecureContext without cert/key for SNICallback#33715

Merged
bartlomieju merged 6 commits into
denoland:mainfrom
divybot:claude/test-tls-empty-sni-context
Apr 30, 2026
Merged

fix(ext/node): allow tls.Server SecureContext without cert/key for SNICallback#33715
bartlomieju merged 6 commits into
denoland:mainfrom
divybot:claude/test-tls-empty-sni-context

Conversation

@divybot

@divybot divybot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Enables test-tls-empty-sni-context in node_compat suite.

Test plan

  • cargo test --test node_compat -- test-tls-empty-sni-context

divybot and others added 2 commits April 30, 2026 02:07
…ICallback

Enables tests/node_compat/runner/suite/test/parallel/test-tls-empty-sni-context.js

Co-authored-by: Divy Srivastava <[email protected]>
The previous fix returned -1 from initServerTls when cert/key were
absent, surfacing as "unsupported protocol" / ERR_SSL_UNSUPPORTED_PROTOCOL.
The new fix lets rustls drive the handshake and the cert resolver returns
None, surfacing as "no suitable signature algorithm" — switch the test
to a real TLS client so it exercises that path.

Co-authored-by: Divy Srivastava <[email protected]>

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Substance is right and well-thought-out. Walking through:

  • Both cert and key made optional — the no_server_cert = cert_str.is_none() && key_str.is_none() short-circuit lets a tls.createSecureContext({}) placeholder or an SNICallback-only server pass through. ✓
  • NoCertResolver installed when no cert — the Option<Arc<CertifiedKey>>::None return from resolve() is what triggers rustls's Error::General("no server certificate chain resolved") + AccessDenied fatal alert. The PR mirrors Node/OpenSSL by mapping that error to ERR_SSL_NO_SUITABLE_SIGNATURE_ALGORITHM server-side. The matching client-side ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE is already covered by the catch-all _ arm in rustls_error_to_node_error. ✓
  • Existing "key was provided but malformed" path preserved: rustls_pemfile::private_key(...).ok().flatten()? still propagates None up to build_server_config's caller via ?, so a half-configured (cert without key, or key without cert) call still bails with the same behavior as before. ✓
  • E::General(msg) if msg == "no server certificate chain resolved" — string match on rustls's error message is fragile (rustls could change the wording), but there isn't a structured variant for "resolver returned None". A // keep in sync with rustls upstream wording TODO comment would help future-proof this; non-blocking.

Test enrollment placement is wrong

tests/node_compat/config.jsonc puts the new entry after test-tls-set-encoding:

     "parallel/test-tls-set-encoding.js": {},
+    "parallel/test-tls-empty-sni-context.js": {},
     "parallel/test-tls-snicallback-error.js": {},

But e-mpty < s-et alphabetically. The entry should sort with the test-tls-e* cluster, not between set-encoding and snicallback-error. Worth fixing for the next person who's grepping for test-tls-empty-*.

CI

94 success, 1 substantive failure (test node_compat (3/3) debug windows-x86_64) + a few pending. Same single-shard windows-x86_64-debug flake we've been seeing across today's TLS / lazy-load PRs; the matching release windows-x86_64 (3/3) and other windows-x86_64 debug shards (1/3, 2/3) all pass.

Holding to COMMENT until the alphabetical placement is fixed and the windows-x86_64 (3/3) shard is confirmed flake on rerun. Substance is good; happy to flip to APPROVE with those two cleanups.

divybot and others added 4 commits April 30, 2026 04:13
Enables tests/node_compat/runner/suite/test/parallel/test-tls-empty-sni-context.js

Co-authored-by: Divy Srivastava <[email protected]>
Per review feedback (denoland#33715): the entry was inserted between
test-tls-set-encoding and test-tls-snicallback-error, but alphabetically
"empty" sorts with the other test-tls-e* entries, before test-tls-enable-*.

Co-authored-by: Divy Srivastava <[email protected]>
Enables tests/node_compat/runner/suite/test/parallel/test-tls-empty-sni-context.js

Co-authored-by: Divy Srivastava <[email protected]>
Per review feedback (denoland#33715, non-blocking): the `Error::General` arm
matches on rustls's literal "no server certificate chain resolved"
message. Add a NOTE so future readers know to keep it in sync with
rustls upstream.

Co-authored-by: Divy Srivastava <[email protected]>

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on the delta. Both prior non-blocking notes addressed:

  1. Test enrollment moved to the correct alphabetical position: now slotted between test-tls-econnreset and test-tls-enable-trace-cli in the test-tls-e* cluster (was misplaced after set-encoding). ✓
  2. Sync-comment added for the rustls error-string match:
    // NOTE: keep this string in sync with rustls upstream — there is no
    // structured `Error` variant for "resolver returned None".
    Future-proofs the brittle string match. ✓

Substance unchanged from my prior review — cert/key both optional in build_server_config, NoCertResolver triggers rustls's Error::General("no server certificate chain resolved") + AccessDenied fatal alert, which rustls_error_to_node_error translates back to Node's ERR_SSL_NO_SUITABLE_SIGNATURE_ALGORITHM.

CI: 90 SUCCESS, 0 FAILURE, 23 still pending. No regressions on the relevant test node_compat and test unit_node shards.

Comment thread ext/node/ops/tls_wrap.rs
// both ends so existing Node code (and upstream tests) match.
// NOTE: keep this string in sync with rustls upstream — there is no
// structured `Error` variant for "resolver returned None".
E::General(msg) if msg == "no server certificate chain resolved" => (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of this one since it's matching string, but I don't have a better idea for now. Hopefully the tests catch the regression if this error message ever changes

@bartlomieju
bartlomieju merged commit d7c3714 into denoland:main Apr 30, 2026
136 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants