Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions tests/gold_tests/autest-site/conditions.test.ext
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import subprocess
import json
import re
Expand Down Expand Up @@ -51,6 +52,57 @@ def IsOpenSSL(self):
"SSL library is not OpenSSL")


def HasLegacyTLSSupport(self):
"""Check if the system supports legacy TLS protocols (TLSv1.0 and TLSv1.1).

Modern OpenSSL 3.x installations often disable these protocols entirely,
even if the openssl binary still accepts the -tls1 flag and lists TLSv1 ciphers.

On Fedora/RHEL systems, the crypto-policies framework may disable legacy
TLS at runtime even when OpenSSL is compiled with support for it. This
causes 'openssl ciphers -v -tls1' to still list TLSv1 ciphers, but actual
TLS 1.0 connections will fail with "no protocols available".

We only probe TLSv1.0 (not TLSv1.1 separately) because crypto-policies
always disable both legacy versions together. If TLSv1.0 is unavailable,
TLSv1.1 will be too.

The check connects to localhost on a closed port to avoid any external
network dependency. A "connection refused" error means the TLS protocol
was available but nothing was listening; "no protocols available" means
the crypto-policy blocked TLSv1.0 entirely.
"""

def check_tls1_support():
try:
# Connect to localhost on a port nothing is listening on.
# This avoids external network dependency while still detecting
# whether the crypto-policy allows TLSv1.0.
result = subprocess.run(
['openssl', 's_client', '-tls1', '-connect', '127.0.0.1:1'],
capture_output=True,
text=True,
timeout=5,
input='' # Don't wait for interactive input
)
output = result.stdout + result.stderr
# "no protocols available" means TLSv1 is disabled by crypto-policy
if 'no protocols available' in output:
return False
# Connection refused or other errors mean TLSv1 was attempted
# (the protocol is available, just no server listening)
return True
Comment on lines 55 to 94
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

This condition claims to check TLSv1.0 and TLSv1.1 support, but it only probes -tls1. Either also probe -tls1_1 (and require both, if that’s what the tests need) or rename/update the messaging so it matches what is actually being detected.

Copilot uses AI. Check for mistakes.
except subprocess.TimeoutExpired:
# Timeout on localhost shouldn't happen, but if it does,
# assume TLSv1 is not available (safer than false positive)
return False
except Exception:
# If we can't determine, assume TLSv1 is not available (safer)
return False
Comment on lines 55 to 101
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

HasLegacyTLSSupport shells out to openssl s_client against www.google.com:443, which makes the test outcome depend on external network/DNS availability and the remote server’s configuration. In offline/locked-down CI this can lead to long timeouts or incorrect results (especially since TimeoutExpired currently returns True). Prefer an offline/local capability check (e.g., attempt to create a TLSv1/TLSv1.1 context with the ssl module, or run openssl s_client -tls1 against localhost and treat connection-refused as "protocol available" but policy-disabled errors as unsupported), and treat timeouts as "unsupported" to avoid false positives.

Copilot uses AI. Check for mistakes.

return self.Condition(check_tls1_support, "System does not support legacy TLS protocols (TLSv1.0/TLSv1.1)")


def HasCurlVersion(self, version):
return self.EnsureVersion(["curl", "--version"], min_version=version)

Expand Down Expand Up @@ -118,6 +170,7 @@ ExtendCondition(HasOpenSSLVersion)
ExtendCondition(HasProxyVerifierVersion)
ExtendCondition(IsBoringSSL)
ExtendCondition(IsOpenSSL)
ExtendCondition(HasLegacyTLSSupport)
ExtendCondition(HasATSFeature)
ExtendCondition(HasCurlVersion)
ExtendCondition(HasCurlFeature)
Expand Down
25 changes: 16 additions & 9 deletions tests/gold_tests/autest-site/microserver.test.ext
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# limitations under the License.

import json
import os
import socket
import ssl

Expand Down Expand Up @@ -109,7 +110,7 @@ def addSessionFromFiles(self, session_dir):
# make headers with the key and values provided
def makeHeader(self, requestString, **kwargs):
headerStr = requestString + '\r\n'
for k, v in kwargs.iteritems():
for k, v in kwargs.items():
headerStr += k + ': ' + v + '\r\n'
headerStr = headerStr + '\r\n'
return headerStr
Expand Down Expand Up @@ -142,14 +143,20 @@ def uServerUpAndRunning(serverHost, port, isSsl, isIPv6, request, clientcert='',

sock.sendall(request.encode())
decoded_output = ''
while True:
host.WriteDebug("??")
output = sock.recv(4096) # suggested bufsize from docs.python.org
host.WriteDebug("!!")
if len(output) <= 0:
break
else:
decoded_output += output.decode()
sock.settimeout(10.0) # 10 second timeout for recv
try:
while True:
host.WriteDebug("??")
output = sock.recv(4096) # suggested bufsize from docs.python.org
host.WriteDebug("!!")
if len(output) <= 0:
break
else:
decoded_output += output.decode('utf-8', errors='replace')
except socket.timeout:
host.WriteDebug(['uServerUpAndRunning', 'when'], "Socket timeout waiting for response")
sock.close()
return False
sock.close()
sock = None

Expand Down
2 changes: 1 addition & 1 deletion tests/gold_tests/autest-site/ports.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def PortOpen(port: int, address: str = None, listening_ports: Set[int] = None) -
host.WriteDebug(
'PortOpen', f"Connection to port {port} succeeded, the port is open, "
"and a future connection cannot use it")
except socket.error:
except OSError:
host.WriteDebug(
'PortOpen', f"socket error for port {port}, port is closed, "
"and therefore a future connection can use it")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,9 @@ Blocking_action::_thread_func(void *vba)
ba->_cont_mutex_locked.store(true, std::memory_order_release);

// This is a stand-in for some blocking call to validate the HTTP request in some way.
// Use a longer delay to account for slower systems and variable scheduling latency.
//
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::this_thread::sleep_for(std::chrono::milliseconds(500));

// Pass "validation" for first transaction, fail it for second.
//
Expand Down
1 change: 1 addition & 0 deletions tests/gold_tests/tls/tls_client_versions.test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# for special domain foo.com only offer TLSv1 and TLSv1_1

Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1"))
Test.SkipUnless(Condition.HasLegacyTLSSupport())

# Define default ATS
ts = Test.MakeATSProcess("ts", enable_tls=True)
Expand Down
1 change: 1 addition & 0 deletions tests/gold_tests/tls/tls_client_versions_minmax.test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# for special domain foo.com only offer TLSv1 and TLSv1_1

Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1"))
Test.SkipUnless(Condition.HasLegacyTLSSupport())

# Define default ATS
ts = Test.MakeATSProcess("ts", enable_tls=True)
Expand Down