Skip to content

feat: TDS 8 strict encryption for Microsoft Fabric, Azure SQL, and SQL Server 2025+#420

Open
iemejia wants to merge 15 commits into
prisma:mainfrom
iemejia:feat/tds8-strict-encryption
Open

feat: TDS 8 strict encryption for Microsoft Fabric, Azure SQL, and SQL Server 2025+#420
iemejia wants to merge 15 commits into
prisma:mainfrom
iemejia:feat/tds8-strict-encryption

Conversation

@iemejia
Copy link
Copy Markdown

@iemejia iemejia commented May 19, 2026

Summary

This PR implements TDS 8 strict encryption (encrypt=strict), enabling tiberius to connect to:

  • Microsoft Fabric Data Warehouse (*.datawarehouse.fabric.microsoft.com)
  • Microsoft Fabric SQL Database (*.database.fabric.microsoft.com)
  • Azure SQL Database (*.database.windows.net)
  • SQL Server 2025+ with forcestrict=1

Fixes #412

What is TDS 8 Strict Mode?

TDS 8 introduces a "strict" transport mode where the TLS handshake happens before any TDS traffic (TLS-first), as opposed to the traditional flow where PRELOGIN is sent in cleartext and TLS is negotiated after. This provides stronger security guarantees and is required by Microsoft Fabric Data Warehouse endpoints.

Changes

Core Implementation

  • EncryptionLevel::Strict variant — performs TLS-first handshake directly on the TCP stream before any TDS packets
  • Routing redirect handling — Fabric/Azure SQL gateways return a routing token; the client reconnects to the backend with the correct encryption mode (strict for Fabric DW, regular TLS-upgrade for Azure SQL/Fabric SQL DB)
  • Pipelined PRELOGIN+LOGIN — Fabric backend servers require both packets sent back-to-back without waiting for the PRELOGIN response (matches ODBC Driver 18 behavior)
  • Per-packet TLS record alignment — each TDS packet is flushed as a separate TLS record (required by Azure SQL gateways)
  • Packet ID incrementing — per MS-TDS 2.2.3.1, PacketID increments within multi-packet messages
  • FEDAUTHREQUIRED in PRELOGIN — signals AAD auth intent to the server
  • AZURESQLSUPPORT feature extension — required by Azure SQL/Fabric backends
  • FEATUREEXTACK decoder — handles AZURESQLSUPPORT ack and unknown features gracefully

Authentication

  • TokenProvider traitasync fn get_token() for dynamic AAD/Entra ID token refresh on each connection/reconnection, supporting long-lived applications where tokens expire
  • AuthMethod::AADTokenProvider(Arc<dyn TokenProvider>) — stores the provider with manual Clone/Debug/PartialEq/Eq impls

Auto-Detection

  • Hostname-based auto-detect — when encryption is not explicitly configured, connections to *.datawarehouse.fabric.microsoft.com or *.pbidedicated.windows.net automatically upgrade to strict mode
  • Explicit user configuration is always respected (auto-detect only upgrades from the default)

Connection Pooling Support

  • Client::connection_encryption() — returns the negotiated EncryptionLevel for pool security verification
  • Client::is_healthy() — executes SELECT 1 for pool health-check hooks

Error Handling

  • Improved strict TLS error messages — wraps raw TLS/IO errors with the target hostname, server requirements, and actionable guidance (encrypt=true fallback suggestion)

Configuration

  • encrypt=strict supported in both ADO.NET and JDBC connection string formats
  • Config::strict_pipelined() — enables pipelined mode for backend reconnection after routing
  • Config::login_server_name() — sets the original gateway hostname in LOGIN for routed connections

Integration Tests

36 integration tests across 4 target environments (all skip gracefully when env vars are not set):

Target Tests Notes
SQL Server 2025 (Docker, forcestrict=1) 10 Direct strict connection, no routing
Microsoft Fabric Data Warehouse 11 Strict gateway + strict backend via routing
Azure SQL Database 8 Strict gateway + regular TLS-upgrade backend via routing
Fabric SQL Database 7 Works with both strict and regular encryption

Architecture Findings

Product Gateway Backend (after routing) Strict required?
Fabric Data Warehouse *.datawarehouse.fabric.microsoft.com (strict) *.pbidedicated.windows.net (strict) Yes
Fabric SQL Database *.database.fabric.microsoft.com (strict OK) *.worker.database.windows.net (regular) No
Azure SQL Database *.database.windows.net (strict OK) *.worker.database.windows.net (regular) No

Security Design

  • No TLS downgrade fallback: if strict mode fails, the connection errors out (prevents downgrade attacks; matches ODBC driver behavior)
  • No ALPN sent: Azure SQL gateways reject TLS connections with the ms-tds ALPN extension
  • LOGIN server_name = original endpoint: per MS-TDS spec, the server name in LOGIN is the original gateway hostname, not the routing target

Testing Locally

# SQL Server 2025 (Docker):
docker run -d --name mssql-tds8 -p 1434:1433 \
  -e ACCEPT_EULA=Y -e MSSQL_SA_PASSWORD=YourPassword123 \
  mcr.microsoft.com/mssql/server:2025-latest
export SQL_SERVER_PASSWORD=YourPassword123
cargo test --test sql_server_tds8 -- --nocapture

# Fabric / Azure SQL (requires AAD token):
export FABRIC_ENDPOINT=your-workspace.datawarehouse.fabric.microsoft.com
export FABRIC_DATABASE=your-database
export FABRIC_AAD_TOKEN=$(az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv)
cargo test --test fabric -- --nocapture

Disclaimer: I am a Microsoft employee but I do not work with the SQL Server or Fabric engineering teams. I put this together because I am using tiberius as a dependency in an upcoming Rust project and needed Fabric connectivity. I am not an expert on TDS 8 protocol internals or Rust, so be gentle!

iemejia added 14 commits May 18, 2026 23:28
Implement EncryptionLevel::Strict which performs the TLS handshake
directly on the TCP stream before any TDS traffic, as required by
TDS 8 (Microsoft Fabric Data Warehouse, SQL Server 2022+ strict mode).

- Add EncryptionLevel::Strict variant
- Add tls_handshake_strict() that bypasses TDS packet wrapping
- Support encrypt=strict in ADO.NET and JDBC connection strings
- Map Strict to ENCRYPT_ON (0x01) on the wire in PRELOGIN
- Make encryption negotiation non-negotiable for strict mode
Complete the TDS 8 strict encryption implementation to support
Microsoft Fabric SQL Data Warehouse end-to-end connectivity:

- Handle routing reconnection flow (gateway -> backend redirect)
- Add FEDAUTHREQUIRED in PRELOGIN for AAD auth state setup
- Add AZURESQLSUPPORT (0x08) feature extension in LOGIN
- Fix FEATUREEXTACK decoder to handle AZURESQLSUPPORT ack and
  unknown features gracefully (no more panics)
- Fix Packet::encode to use offset-aware length patching
- Fix packet_id to start from 1 (not 0)
- Add TRACEID support in PRELOGIN for backend connections
- Add pipelined PRELOGIN+LOGIN via feed()+flush() for strict mode
- Set TDS version to SqlServer2022 for strict mode LOGIN
- Set client_lcid=0x0409 and clt_int_name='tiberius' defaults
- Add Config fields: strict_pipelined, login_server_name, instance_name
- Add ALPN 'ms-tds' for strict TLS connections (native-tls)
- Use UseTSQL flag in LOGIN type_flags
- Add integration tests proving Fabric connectivity with routing

Tested against live Fabric endpoint: connects, authenticates with
AAD token, handles routing redirect, and executes queries including
parameterized statements.
Key fixes for Azure SQL Database TDS 8 strict mode compatibility:

1. Per-packet TLS record alignment: Changed send() from batch
   flush (feed all + flush once) to per-packet send (feed+flush
   each). In TDS 8 strict mode, Azure SQL's gateway requires each
   TDS packet to arrive as a separate TLS record. Batching multiple
   TDS packets into a single TLS write caused connection resets.

2. Packet ID incrementing: Per MS-TDS 2.2.3.1, PacketID must
   increment by 1 (mod 256) for each packet within a multi-packet
   message. Previously all packets in a message reused the same ID.

3. TDS version in LOGIN7: Changed from 0x08000000 (SqlServer2022)
   to 0x74000004 (SqlServerN/TDS 7.4). TDS 8 is a transport-mode
   indicator, not a protocol version — the LOGIN7 version field
   must still report 7.4.

4. FEDAUTH library type: Using SECURITYTOKEN (0x01) for pre-obtained
   JWT tokens, not MSAL (0x02). MSAL type has a different FeatureData
   format without inline token.

5. Removed ALPN "ms-tds": Azure SQL Database gateways reject TLS
   connections that advertise this ALPN extension.

Tested against:
- Azure SQL Database (a test Azure SQL Database instance):
  gateway accepts LOGIN, returns routing redirect, backend accepts
  LOGIN with FEDAUTH token
- Microsoft Fabric: all 5 integration tests pass (pipelined backend
  path unaffected by send() change)
- 120 unit tests pass
- Add tests/azure_sql.rs: 5 integration tests for Azure SQL Database
  with TDS 8 strict encryption on the gateway + regular TLS-upgrade
  on the backend worker (the correct pattern for Azure SQL)
- Gateway (port 1433): accepts TDS 8 strict (TLS-first)
- Backend worker (port 11010): requires regular TLS-upgrade
- Tests: connectivity, metadata, multiple queries, DDL/DML, regular encryption
- Remove unused FED_AUTH_LIBRARY_MSAL constant
- Suppress dead_code warnings on hostname/client_pid/client_prog_ver setters

Key finding: Azure SQL backend workers do NOT support TDS 8 strict mode.
After routing redirect, clients must reconnect with EncryptionLevel::Required
(standard TLS-upgrade flow), not EncryptionLevel::Strict.
7 integration tests covering:
- Basic strict mode connection and query
- Server metadata verification (version, database, transport)
- Multiple sequential queries over a single connection
- DDL/DML operations (CREATE TABLE, INSERT, UPDATE, DELETE)
- Encryption verification via sys.dm_exec_connections
- Large result set (1000 rows) to stress TLS framing
- CA certificate validation (non-trust-all path)

Tests run against SQL Server 2025 with network.forcestrict=1
(strict mode is only available in SQL Server 2025+, not 2022 on Linux).
Tests skip gracefully when SQL_SERVER_PASSWORD env var is not set.
…d Azure SQL

Brings Fabric and Azure SQL integration tests to parity with SQL Server:
- DDL/DML: CREATE TABLE, INSERT, UPDATE, DELETE, SELECT with params
- Large result set: 1000 rows to stress TLS framing across packets
- Encryption verification: CONNECTIONPROPERTY + sys.dm_exec_connections
- String/unicode: CONCAT, unicode chars, 8000-char strings

Fabric-specific considerations:
- Uses permanent table (not #temp) since Fabric DW doesn't support local temps
- Large result tries tpch_sf1.lineitem first, falls back to recursive CTE
- dm_exec_connections access is optional (may not be available on all SKUs)

Total integration test count: 24 (9 Fabric + 8 Azure SQL + 7 SQL Server)
- Use VARCHAR(100) instead of NVARCHAR(100) — Fabric DW does not
  support nvarchar with specified length
- Replace recursive CTE with OPTION(MAXRECURSION) by cross-join row
  generation — Fabric DW disallows query hints
- Skip DDL test gracefully when identity lacks CREATE TABLE permission
  (e.g., read-only lakehouse endpoints)
- Apply cargo fmt across all changed files
- Remove dead debug TDS frame dump to /tmp in feed_to_wire()
- Remove unnecessary #[allow(dead_code)] on feed_to_wire (it's used)
- Remove unused lifetime 'a on login() function
- Use eq_ignore_ascii_case() instead of to_ascii_lowercase() comparisons
  in TLS cert extension matching
Add async TokenProvider trait enabling dynamic token acquisition on each
connection/reconnection, replacing the need for static pre-obtained tokens
in long-lived applications.

Changes:
- Define TokenProvider trait with async get_token() method (uses async-trait)
- Add AuthMethod::AADTokenProvider(Arc<dyn TokenProvider>) variant
- Add AuthMethod::token_provider() constructor and is_aad() helper
- Manual Clone/Debug/PartialEq/Eq impls for AuthMethod (Arc<dyn> breaks derives)
- Resolve token via provider in both pipelined and standard login paths
- Export TokenProvider from crate root
- Integration test verifying provider is called twice (gateway + backend routing)
When the caller does not explicitly set the encryption level, the client
now automatically upgrades to Strict mode for known endpoints:
- *.datawarehouse.fabric.microsoft.com (Fabric SQL endpoints)
- *.pbidedicated.windows.net (Fabric backend servers)

This means users connecting to Fabric no longer need to specify
encrypt=strict in connection strings — the client detects it from
the hostname. Explicit encryption settings are always respected.

Implementation:
- Add encryption_explicit flag to Config (tracks user intent)
- Add has_encrypt_key() to ConfigString trait (detects conn string key)
- Add resolve_encryption() method with hostname heuristic
- Call resolve_encryption() at start of Connection::connect()
- Emit tracing::info event when auto-detection fires
- 9 unit tests for the detection heuristic
- Integration test verifying end-to-end auto-detect with Fabric
Wrap Tls and Io errors from the strict handshake with contextual
guidance: the target hostname, server requirements (SQL Server 2025+
with forcestrict=1, Azure SQL, or Fabric), and the suggested fix
(encrypt=true instead of encrypt=strict).

Add 3 unit tests for the wrapping logic and 1 integration test that
verifies the message against a non-strict TCP endpoint.
- Client::connection_encryption() returns the negotiated EncryptionLevel,
  allowing pool implementations to verify security properties of managed
  connections (e.g., assert all pooled connections use Strict).

- Client::is_healthy() executes a lightweight SELECT 1 round-trip,
  suitable for pool is_valid / health-check hooks.

- Store EncryptionLevel in Connection struct, set to the final negotiated
  value (Strict for TDS 8, or the PRELOGIN-negotiated level otherwise).

- Integration tests verify both methods on live strict connections.
…osoft.com)

Verify tiberius connects to SQL Database in Microsoft Fabric, which
uses the same gateway/backend architecture as Azure SQL:
- Gateway: supports TDS 8 strict TLS on *.database.fabric.microsoft.com
- Backend: routes to *.worker.database.windows.net with regular TLS upgrade

Key finding: Fabric SQL DB does NOT require strict mode (unlike Data
Warehouse). Both encrypt=strict and encrypt=true work. No auto-detect
upgrade needed for this endpoint pattern.

7 integration tests: strict, required, metadata, DDL/DML, health check,
connection_encryption, and works-without-strict verification.
- cargo fmt: fix line-length formatting in unit tests and integration tests
- Fix resolve_encryption() doc comment to list both auto-detect patterns
  (*.datawarehouse.fabric.microsoft.com and *.pbidedicated.windows.net)
- Remove unused `resolved` variable binding in connect() — the return
  value was redundant with config.encryption after mutation
- Rename misleading `is_backend` to `include_trace_id` in
  finish_connect_after_tls() to match the parameter it maps to
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Review Change Stack

Warning

Rate limit exceeded

@iemejia has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 14 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 24502aa1-3781-4b8c-9515-42a6f7ee7758

📥 Commits

Reviewing files that changed from the base of the PR and between f1b2fcb and 1730952.

📒 Files selected for processing (3)
  • src/client/tls_stream/native_tls_stream.rs
  • src/tds/codec/token/token_feature_ext_ack.rs
  • tests/fabric_sqldb.rs

Walkthrough

This PR introduces TDS 8.0 strict encryption support, enabling TLS-before-prelogin handshakes for cloud endpoints and dynamic AAD token refresh. Core changes include a new EncryptionLevel::Strict enum variant, config auto-detection for Fabric/pbidedicated hosts, dynamic TokenProvider trait for token refresh, strict-mode connection flows with optional PRELOGIN+LOGIN pipelining, updated LOGIN/PRELOGIN codec handling for TDS 8.0/2022 features, and native TLS strict handshake implementation. Comprehensive integration tests validate strict encryption against Azure SQL gateway, Fabric SQL DW, and SQL Server 2025+ with routing, DDL/DML, large result sets, and encryption verification.

Changes

TDS 8 Strict Encryption Implementation

Layer / File(s) Summary
Encryption level and core types
src/tds.rs, src/tds/codec/header.rs
EncryptionLevel::Strict enum variant added; PacketHeader gains public id() getter and set_id() setter for packet ID manipulation in pipelined flows.
AAD token provider trait and AuthMethod
src/client/auth.rs, src/lib.rs
New TokenProvider trait enables dynamic per-connection token refresh; AuthMethod extends with AADTokenProvider(Arc<dyn TokenProvider>) variant; manual trait impls (Clone, Debug, PartialEq) use Arc pointer identity and redact secrets; re-exported in crate public API.
Config encryption tracking and auto-detection
src/client/config.rs, src/client/config/ado_net.rs, src/client/config/jdbc.rs
Config tracks encryption_explicit, strict_pipelined, login_server_name; resolve_encryption() auto-upgrades to Strict for Fabric/pbidedicated endpoints when encryption is not explicit; connection-string parsers recognize encrypt=strict and conditionally apply encryption only when key is present; tests validate auto-detection and explicit-setting preservation.
Connection establishment and strict TLS handshake
src/client/connection.rs
Connection tracks negotiated encryption field; connect() takes mutable config for auto-detection, routes strict mode to tls_handshake_strict → sequential/pipelined completion; adds strict error wrapping with host/mode context and encrypt=true fallback guidance; supports both gateway sequential and backend pipelined PRELOGIN+LOGIN flows; adds tracing for fed-auth and nonce; refactors Stream::poll_next to direct match.
LOGIN packet TDS 8.0/2022 support
src/tds/codec/login.rs
Adds SqlServer2022 feature level treating it as modern; extends LoginMessage with clt_int_name and azure_sql_support fields/setters; FeatureExt encoding writes both FEDAUTH and AZURESQLSUPPORT extensions when needed; decoder skips additional known extensions (COLUMNENCRYPTION, AZURESQLSUPPORT, UTF8_SUPPORT).
PRELOGIN packet strict mode and TRACEID
src/tds/codec/pre_login.rs
PRELOGIN adds include_trace_id flag to conditionally emit TRACEID with random GUIDs; strict mode encryption maps to wire value 0x08; instance name encoding refactored to null-terminate ASCII; decoding maps 0x08 back to Strict.
Packet encoding for pipelined messages
src/tds/codec/packet.rs
Packet::encode tracks buffer offset to patch TDS header length at correct position when multiple packets buffered, supporting strict-mode pipelined PRELOGIN+LOGIN in backend flows.
Token codec updates for TDS 8 features
src/tds/codec/token/token_error.rs, src/tds/codec/token/token_feature_ext_ack.rs, src/tds/context.rs
TokenError decoder uses version().is_modern() instead of hardcoded version check; FeatureExtAck decoder gains variants for Azure SQL Support, Column Encryption, UTF-8 Support, and Unknown to forward-compatibly parse extensions; Context::new() initializes packet_id to 1.
Native TLS strict mode handshake
src/client/tls_stream/native_tls_stream.rs
create_tls_stream adds EncryptionLevel::Strict branch using native TlsConnector for direct TLS; supports CA file loading with case-insensitive extension checking (pem/crt/der); non-strict path updated to case-insensitive matching.
Client API for encryption reporting and health checks
src/client.rs
Client gains connection_encryption() to report negotiated EncryptionLevel and async is_healthy() to validate connection via SELECT 1 query.
Cargo manifest and dependencies
Cargo.toml
Adds fabric test target; enables uuid v4 feature; introduces optional native-tls-crate (package native-tls) with ALPN; updates native-tls feature to include both async-native-tls and native-tls-crate.
Azure SQL integration tests
tests/azure_sql.rs
New test suite validating strict encryption via Azure SQL gateway with routing redirects, non-strict backend reconnection, AAD auth, DDL/DML, large result sets, encryption verification, and unicode handling.
Fabric SQL DW integration tests
tests/fabric.rs
Comprehensive Fabric test suite with AAD token helpers, strict connection routing, DDL/DML, large result sets, encryption verification, token-provider auth with refresh counting, and auto-detection path for implicit strict mode.
Fabric SQL Database integration tests
tests/fabric_sqldb.rs
Fabric SQL Database test suite covering strict and required encryption modes, routing redirect handling, basic queries, metadata, DDL/DML, encryption reporting, health checks, and fallback for non-strict paths.
SQL Server TDS 8 strict mode integration tests
tests/sql_server_tds8.rs
Test suite for direct SQL Server 2025+ TDS 8 strict connections; covers basic queries, metadata, sequential queries, DDL/DML, encryption verification, large result sets, optional CA cert validation, strict error messaging, and client encryption reporting.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The PR does not implement the optional TLS 1.2 max-version restriction for non-strict paths mentioned in issue #412, and does not add an explicit tds80 feature flag or compile error guard as originally specified. Clarify whether the TLS 1.2 max-version restriction for TDS 7.x paths and explicit tds80 feature flag with compile-time guards are required objectives, or if their omission is intentional and acceptable for this scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title "feat: TDS 8 strict encryption for Microsoft Fabric, Azure SQL, and SQL Server 2025+" clearly and specifically summarizes the main feature addition, identifying the three primary target environments.
Description check ✅ Passed The PR description is comprehensive and directly related to the changeset, covering implementation details, architecture, testing strategy, and security design for TDS 8 strict encryption support.
Linked Issues check ✅ Passed The PR implements all core coding requirements from issue #412: adds EncryptionLevel::Strict variant, performs TLS-first handshake before PRELOGIN, supports encrypt=strict in connection strings, and includes comprehensive integration tests across target platforms.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/tls_stream/native_tls_stream.rs`:
- Around line 53-59: The TrustAll branch explicitly disables SNI via
native_builder.use_sni(false) while the strict branch does not, causing
divergent SNI behavior; update the strict-path code around TrustConfig (the
branch handling non-TrustAll vs TrustConfig::TrustAll and the native_builder.*
calls such as native_builder.danger_accept_invalid_certs and
native_builder.use_sni) to either (a) match the non-strict branch by disabling
SNI where appropriate, or (b) add a concise inline comment in the strict branch
explaining why SNI must remain enabled for strict mode (e.g., required for cloud
endpoint routing), so the divergence is intentional and documented.
- Around line 66-67: The current conversion native_builder.into() won't compile;
instead call native_builder.build() to produce a native_tls::TlsConnector, then
wrap/convert that into the async connector before using connect — e.g. build the
native connector with native_builder.build(), convert/wrap it into an
async_native_tls connector (via async_native_tls::TlsConnector::from(...) or the
async-native-tls builder API), and then call
connector.connect(config.get_host(), stream).await?; reference native_builder,
native_builder.build(), async_native_tls::TlsConnector::from, connector.connect,
and config.get_host() to find the exact lines to change.

In `@src/tds/codec/token/token_feature_ext_ack.rs`:
- Around line 62-89: The code reads a wire-controlled data_len and allocates
Vec<u8> without checks in the FeatureEXTACK parsing (branches that push
FeatureAck::AzureSqlSupport, ::ColumnEncryption, ::Utf8Support, and ::Unknown);
add a safe upper bound check (e.g. compare data_len against a
MAX_FEATURE_PAYLOAD constant like 1_048_576 or another protocol-appropriate
limit) before allocating or reading, return an error (or io::Error::new with
InvalidData) if data_len exceeds the limit, and prefer using Vec::try_reserve or
read into a limited buffer to avoid unbounded allocations in the parsing
function that builds the features vector and pushes FeatureAck variants.

In `@tests/fabric_sqldb.rs`:
- Around line 40-50: The helper functions get_endpoint, get_database, and
get_token currently call env::var(...).unwrap() which can panic with unhelpful
messages; change them to return Result<String, anyhow::Error> (or Result<String,
env::VarError>) with descriptive error text, e.g. "missing
FABRIC_SQLDB_DATABASE" / "missing FABRIC_SQLDB_TOKEN", or alternatively expand
the existing skip macro to check all three env vars before running tests; update
call sites in tests to propagate or unwrap the Result with context (following
the pattern in fabric.rs's get_aad_token()) so missing env vars produce clear,
consistent errors instead of panics.

In `@tests/sql_server_tds8.rs`:
- Around line 442-446: The test assertion in tests/sql_server_tds8.rs currently
expects the exact substring "encrypt=true" in err_msg which is brittle; update
the assertion to check for a broader marker such as "encrypt=" so minor wording
changes (e.g., "encrypt=true" -> "encrypt: true") won't break the test—locate
the assert block referencing err_msg and replace the contains("encrypt=true")
check with contains("encrypt=") (still using the same err_msg variable and
assert! macro).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b3d9868d-2384-4d8b-bdf0-0471cc535948

📥 Commits

Reviewing files that changed from the base of the PR and between a6b4fcd and f1b2fcb.

📒 Files selected for processing (21)
  • Cargo.toml
  • src/client.rs
  • src/client/auth.rs
  • src/client/config.rs
  • src/client/config/ado_net.rs
  • src/client/config/jdbc.rs
  • src/client/connection.rs
  • src/client/tls_stream/native_tls_stream.rs
  • src/lib.rs
  • src/tds.rs
  • src/tds/codec/header.rs
  • src/tds/codec/login.rs
  • src/tds/codec/packet.rs
  • src/tds/codec/pre_login.rs
  • src/tds/codec/token/token_error.rs
  • src/tds/codec/token/token_feature_ext_ack.rs
  • src/tds/context.rs
  • tests/azure_sql.rs
  • tests/fabric.rs
  • tests/fabric_sqldb.rs
  • tests/sql_server_tds8.rs

Comment thread src/client/tls_stream/native_tls_stream.rs
Comment on lines +66 to +67
let connector: TlsConnector = native_builder.into();
return Ok(connector.connect(config.get_host(), stream).await?);
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does async-native-tls 0.4 support From<native_tls::TlsConnectorBuilder> for TlsConnector?

💡 Result:

Yes. async-native-tls 0.4 supports converting from its own TlsConnectorBuilder type via From for TlsConnector; the TlsConnector docs show impl From for TlsConnector with fn from(builder: TlsConnectorBuilder) -> Self (i.e., TlsConnector::from(builder)). [1] However, the docs page we found does not indicate a blanket From<native_tls::TlsConnectorBuilder> implementation. It only documents From<TlsConnectorBuilder> (async-native-tls’s builder), not native_tls::TlsConnectorBuilder. [1] Conclusion: For async-native-tls 0.4, you can rely on From<async_native_tls::TlsConnectorBuilder> for async_native_tls::TlsConnector, but support for From<native_tls::TlsConnectorBuilder> is not evidenced by the authoritative docs result we located. [1]

Citations:


This conversion will not compile.

Line 66 attempts to convert native_tls_crate::TlsConnectorBuilder directly to async_native_tls::TlsConnector using .into(). According to async-native-tls 0.4 documentation, the From trait is only implemented for async_native_tls::TlsConnectorBuilder, not for native_tls::TlsConnectorBuilder.

The native-tls builder must first be built into a native_tls::TlsConnector via .build(), then wrapped appropriately for async use. Alternatively, use async-native-tls's own builder API throughout the strict mode path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/tls_stream/native_tls_stream.rs` around lines 66 - 67, The current
conversion native_builder.into() won't compile; instead call
native_builder.build() to produce a native_tls::TlsConnector, then wrap/convert
that into the async connector before using connect — e.g. build the native
connector with native_builder.build(), convert/wrap it into an async_native_tls
connector (via async_native_tls::TlsConnector::from(...) or the async-native-tls
builder API), and then call connector.connect(config.get_host(), stream).await?;
reference native_builder, native_builder.build(),
async_native_tls::TlsConnector::from, connector.connect, and config.get_host()
to find the exact lines to change.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is a false positive. async-native-tls 0.4.0 does implement From<native_tls::TlsConnectorBuilder> for TlsConnector — verified in the crate source at async-native-tls-0.4.0/src/lib.rs. The code compiles and all 168 tests pass against live SQL Server, Azure SQL, and Fabric endpoints. No change needed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

Comment thread src/tds/codec/token/token_feature_ext_ack.rs Outdated
Comment thread tests/fabric_sqldb.rs
Comment thread tests/sql_server_tds8.rs
Comment on lines +442 to +446
assert!(
err_msg.contains("encrypt=true"),
"Error message should suggest alternative, got: {}",
err_msg
);
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Loosen the strict TLS guidance assertion to avoid brittle failures.

Line 443 hard-codes encrypt=true; benign wording updates in the error text can break this test despite correct behavior. Assert on a broader guidance marker (e.g., encrypt=) instead.

Suggested change
     assert!(
-        err_msg.contains("encrypt=true"),
-        "Error message should suggest alternative, got: {}",
+        err_msg.contains("encrypt="),
+        "Error message should suggest an encrypt setting alternative, got: {}",
         err_msg
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert!(
err_msg.contains("encrypt=true"),
"Error message should suggest alternative, got: {}",
err_msg
);
assert!(
err_msg.contains("encrypt="),
"Error message should suggest an encrypt setting alternative, got: {}",
err_msg
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/sql_server_tds8.rs` around lines 442 - 446, The test assertion in
tests/sql_server_tds8.rs currently expects the exact substring "encrypt=true" in
err_msg which is brittle; update the assertion to check for a broader marker
such as "encrypt=" so minor wording changes (e.g., "encrypt=true" -> "encrypt:
true") won't break the test—locate the assert block referencing err_msg and
replace the contains("encrypt=true") check with contains("encrypt=") (still
using the same err_msg variable and assert! macro).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Intentionally kept as-is. The assertion tests our specific error message wording — we control that message text in wrap_strict_tls_error() and it intentionally says encrypt=true (the exact connection string parameter users need). If we ever change the guidance text, we want this test to fail so we consciously update it. Loosening to encrypt= would mask unintentional regressions in user-facing error messages.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

- Add size guard (1 MiB max) on FEATUREEXTACK payload allocations to
  prevent malformed/malicious packets from causing large memory allocs.
  Extract read_feature_data() helper to deduplicate the pattern.
- Replace panic!() in FEDAUTH data_len handling with proper Error::Protocol
- Add comment explaining why SNI stays enabled in strict TrustAll path
  (cloud endpoints need SNI for tenant routing)
- Use expect() with descriptive messages instead of unwrap() in
  fabric_sqldb.rs test helpers for clearer failure diagnostics
@iemejia
Copy link
Copy Markdown
Author

iemejia commented May 19, 2026

Addressing CodeRabbit review comments

Pushed commit 1730952 addressing the valid feedback. Here's a summary:

1. SNI handling in strict TrustAll (nitpick) — Fixed

Added a comment explaining that SNI must remain enabled in strict mode because cloud endpoints (Azure SQL, Fabric) use SNI to route the TLS connection to the correct tenant/gateway, even in trust-all mode. The non-strict TrustAll path disables SNI because it's typically a local dev/test scenario where routing isn't needed.

2. native_tls::TlsConnectorBuilder .into() won't compile — False positive

This is incorrect. async-native-tls 0.4.0 does implement From<native_tls::TlsConnectorBuilder> for TlsConnector — verified in the source at ~/.cargo/registry/src/.../async-native-tls-0.4.0/src/lib.rs. The code compiles and all 168 tests pass. No change needed.

3. Bound FEATUREEXTACK payload length — Fixed

Valid security concern. Added a MAX_FEATURE_ACK_DATA_LEN (1 MiB) constant and a read_feature_data() helper that validates the wire-controlled data_len before allocating. Also replaced the pre-existing panic!() in the FEDAUTH branch with a proper Error::Protocol return.

4. fabric_sqldb.rs unwrap on env vars — Fixed

Replaced .unwrap() with .expect("FABRIC_SQLDB_... must be set") for clearer diagnostics if a test is run with incomplete env configuration.

5. Loosen encrypt=true assertion — Intentionally not changed

The test validates our specific error message guidance. We control this text and want to assert the exact wording. If we change the message, we should update the test — a looser match would let regressions in the user-facing guidance slip through undetected.

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.

TDS 8.0 and "Strict" encryption option

1 participant