Skip to content

Commit 8f3c88f

Browse files
committed
fix(postgres): improve error messaging for unsupported TLS versions and add validation tests for TLS version bounds
1 parent 3cdedd5 commit 8f3c88f

1 file changed

Lines changed: 122 additions & 3 deletions

File tree

  • quaint/src/connector/postgres/native

quaint/src/connector/postgres/native/mod.rs

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ fn string_to_protocol(s: &str) -> Result<native_tls::Protocol, String> {
924924
"TLSV1.1" | "TLS1.1" => Ok(native_tls::Protocol::Tlsv11),
925925
"TLSV1.2" | "TLS1.2" => Ok(native_tls::Protocol::Tlsv12),
926926
"TLSV1.3" | "TLS1.3" => {
927-
Err("TLS 1.3 is not supported by this connector build".to_string())
927+
Err("TLS 1.3 is not yet supported in this build (native-tls version constraint)".to_string())
928928
}
929929
_ => Err(format!(
930930
"Unsupported TLS version: '{}'. Supported versions are: TLSv1.0, TLSv1.1, TLSv1.2",
@@ -1090,7 +1090,126 @@ mod tests {
10901090
#[test]
10911091
fn rejects_tls13_with_neutral_error_message() {
10921092
let err = string_to_protocol("TLS1.3").unwrap_err();
1093-
assert_eq!(err, "TLS 1.3 is not supported by this connector build");
1093+
assert_eq!(err, "TLS 1.3 is not yet supported in this build (native-tls version constraint)");
1094+
}
1095+
1096+
// --- TLS version bound validation (unit tests via get_connector) ---
1097+
1098+
/// Build a PostgresNativeUrl with the given tls_min_version / tls_max_version query params.
1099+
fn make_tls_with_bounds(min: Option<&str>, max: Option<&str>) -> PostgresNativeUrl {
1100+
let mut url = Url::parse("postgresql://localhost:5432/test").unwrap();
1101+
{
1102+
let mut pairs = url.query_pairs_mut();
1103+
if let Some(v) = min {
1104+
pairs.append_pair("tls_min_version", v);
1105+
}
1106+
if let Some(v) = max {
1107+
pairs.append_pair("tls_max_version", v);
1108+
}
1109+
}
1110+
PostgresNativeUrl::new(url).unwrap()
1111+
}
1112+
1113+
/// Helper: call get_connector and return the error message string if it fails.
1114+
async fn get_connector_err(url: PostgresNativeUrl) -> String {
1115+
let mgr = MakeTlsConnectorManager::new(url);
1116+
match mgr.get_connector().await {
1117+
Ok(_) => panic!("expected get_connector to fail"),
1118+
Err(e) => e.to_string(),
1119+
}
1120+
}
1121+
1122+
#[tokio::test]
1123+
async fn tls_min_version_greater_than_max_is_rejected() {
1124+
let url = make_tls_with_bounds(Some("TLSv1.2"), Some("TLSv1.1"));
1125+
let err = get_connector_err(url).await;
1126+
assert!(
1127+
err.contains("tls_min_version cannot be greater than tls_max_version"),
1128+
"unexpected error: {err:?}"
1129+
);
1130+
}
1131+
1132+
#[tokio::test]
1133+
async fn tls_invalid_min_version_is_rejected() {
1134+
let url = make_tls_with_bounds(Some("TLSv9.9"), None);
1135+
let err = get_connector_err(url).await;
1136+
assert!(
1137+
err.contains("Invalid tls_min_version"),
1138+
"unexpected error: {err:?}"
1139+
);
1140+
}
1141+
1142+
#[tokio::test]
1143+
async fn tls_invalid_max_version_is_rejected() {
1144+
let url = make_tls_with_bounds(None, Some("TLSv9.9"));
1145+
let err = get_connector_err(url).await;
1146+
assert!(
1147+
err.contains("Invalid tls_max_version"),
1148+
"unexpected error: {err:?}"
1149+
);
1150+
}
1151+
1152+
#[tokio::test]
1153+
async fn tls13_in_min_version_is_rejected() {
1154+
let url = make_tls_with_bounds(Some("TLSv1.3"), None);
1155+
let err = get_connector_err(url).await;
1156+
assert!(
1157+
err.contains("Invalid tls_min_version") &&
1158+
err.contains("TLS 1.3 is not yet supported in this build"),
1159+
"unexpected error: {err:?}"
1160+
);
1161+
}
1162+
1163+
#[tokio::test]
1164+
async fn tls13_in_max_version_is_rejected() {
1165+
let url = make_tls_with_bounds(None, Some("TLSv1.3"));
1166+
let err = get_connector_err(url).await;
1167+
assert!(
1168+
err.contains("Invalid tls_max_version") &&
1169+
err.contains("TLS 1.3 is not yet supported in this build"),
1170+
"unexpected error: {err:?}"
1171+
);
1172+
}
1173+
1174+
#[tokio::test]
1175+
async fn tls_valid_bounds_pass_validation() {
1176+
let url = make_tls_with_bounds(Some("TLSv1.0"), Some("TLSv1.2"));
1177+
let err = get_connector_err(url).await;
1178+
// Valid bounds pass the validation layer; the call fails later at
1179+
// tls_builder.build() because there is no real TLS backend in unit tests.
1180+
assert!(
1181+
!err.contains("tls_min_version")
1182+
&& !err.contains("tls_max_version")
1183+
&& !err.contains("Invalid TLS configuration"),
1184+
"unexpected validation error: {err}"
1185+
);
1186+
}
1187+
1188+
#[tokio::test]
1189+
#[cfg(target_os = "macos")]
1190+
async fn macos_tls12_floor_enforced() {
1191+
// On macOS, setting tls_min_version=TLSv1.0 should still result in
1192+
// TLSv1.2 as the effective minimum. Setting tls_max_version=TLSv1.0
1193+
// should then fail because the floor (1.2) > max (1.0).
1194+
let url = make_tls_with_bounds(Some("TLSv1.0"), Some("TLSv1.0"));
1195+
let err = get_connector_err(url).await;
1196+
assert!(
1197+
err.contains("tls_max_version") && err.contains("effective minimum"),
1198+
"unexpected error: {err:?}"
1199+
);
1200+
}
1201+
1202+
#[tokio::test]
1203+
#[cfg(not(target_os = "macos"))]
1204+
async fn non_macos_default_min_is_tls12() {
1205+
// On non-macOS, the default minimum is TLSv1.2. Setting
1206+
// tls_max_version=TLSv1.0 should fail because effective min (1.2) > max (1.0).
1207+
let url = make_tls_with_bounds(None, Some("TLSv1.0"));
1208+
let err = get_connector_err(url).await;
1209+
assert!(
1210+
err.contains("tls_min_version cannot be greater than tls_max_version"),
1211+
"unexpected error: {err:?}"
1212+
);
10941213
}
10951214

10961215
#[tokio::test]
@@ -1396,4 +1515,4 @@ mod tests {
13961515
assert!(!is_safe_identifier(ident));
13971516
}
13981517
}
1399-
}
1518+
}

0 commit comments

Comments
 (0)