From 2ae28d7f640ba67ab3ccaaa64260e8246d7f9e92 Mon Sep 17 00:00:00 2001
From: Riccardo Germenia <rgermenia@fbk.eu>
Date: Fri, 17 Jan 2025 12:03:34 +0100
Subject: [PATCH 01/20] fix curves findings in TLS1.2 and prior versions

---
 testssl.sh | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/testssl.sh b/testssl.sh
index 59cfcfd..5e9efde 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -10852,6 +10852,14 @@ run_fs() {
                                    "${ossl_supported[i]}" && ! "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
                               fi
                          done
+                         # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
+                         # used in the certificate. The easiest solution is to move the curves to the end of the list.
+                         # instead of removing them from the ClientHello.
+                         for (( i=low; i < high; i++ )); do
+                              if ! "$HAS_TLS13" || ! "${curves_deprecated[i]}" || [[ "$proto" == "-no_tls1_3" ]]; then
+                                   "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
+                              fi
+                         done
                          [[ -z "$curves_to_test" ]] && break
                          $OPENSSL s_client $(s_client_options "$proto -cipher "\'${ecdhe_cipher_list:1}\'" -ciphersuites "\'${tls13_cipher_list:1}\'" -curves "${curves_to_test:1}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") &>$TMPFILE </dev/null
                          sclient_connect_successful $? $TMPFILE || break

From 355b9d2dcc4d10d0f4ef5620e91bd2542a64883d Mon Sep 17 00:00:00 2001
From: Riccardo Germenia <rgermenia@fbk.eu>
Date: Mon, 20 Jan 2025 17:27:31 +0100
Subject: [PATCH 02/20] add fix to sockets and move test after original one

---
 testssl.sh | 81 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 73 insertions(+), 8 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 5e9efde..64a386f 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -10852,14 +10852,6 @@ run_fs() {
                                    "${ossl_supported[i]}" && ! "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
                               fi
                          done
-                         # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
-                         # used in the certificate. The easiest solution is to move the curves to the end of the list.
-                         # instead of removing them from the ClientHello.
-                         for (( i=low; i < high; i++ )); do
-                              if ! "$HAS_TLS13" || ! "${curves_deprecated[i]}" || [[ "$proto" == "-no_tls1_3" ]]; then
-                                   "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
-                              fi
-                         done
                          [[ -z "$curves_to_test" ]] && break
                          $OPENSSL s_client $(s_client_options "$proto -cipher "\'${ecdhe_cipher_list:1}\'" -ciphersuites "\'${tls13_cipher_list:1}\'" -curves "${curves_to_test:1}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") &>$TMPFILE </dev/null
                          sclient_connect_successful $? $TMPFILE || break
@@ -10881,6 +10873,44 @@ run_fs() {
                          [[ $i -eq $high ]] && break
                          supported_curve[i]=true
                     done
+                    while true; do
+                         # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
+                         # used in the certificate. The easiest solution is to move the curves to the end of the list.
+                         # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
+                         if ((! "$HAS_TLS13" || [[ "$proto" == "-no_tls1_3" ]]) && [[ ! "$ecdhe_cipher_list" == *RSA* ]]) || break; then
+                              curves_to_test=""
+                              for (( i=low; i < high; i++ )); do
+                                   if ! "${curves_deprecated[i]}"; then
+                                        "${ossl_supported[i]}" && ! "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
+                                   fi
+                              done
+                              [[ -z "$curves_to_test" ]] && break
+                              for (( i=low; i < high; i++ )); do
+                                   if ! "${curves_deprecated[i]}"; then
+                                        "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
+                                   fi
+                              done
+                              $OPENSSL s_client $(s_client_options "$proto -cipher "\'${ecdhe_cipher_list:1}\'" -ciphersuites "\'${tls13_cipher_list:1}\'" -curves "${curves_to_test:1}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") &>$TMPFILE </dev/null
+                              sclient_connect_successful $? $TMPFILE || break
+                              temp=$(awk -F': ' '/^Server Temp Key/ { print $2 }' "$TMPFILE")
+                              curve_found="${temp%%,*}"
+                              if [[ "$curve_found" == ECDH ]]; then
+                                   curve_found="${temp#*, }"
+                                   curve_found="${curve_found%%,*}"
+                                   if "$HAS_TLS13" && [[ ! "$proto" == "-no_tls1_3" ]] && [[ "$curve_found" == brainpoolP[235][581][642]r1 ]]; then
+                                        [[ "$(get_protocol "$TMPFILE")" == TLSv1.3 ]] && curve_found+="tls13"
+                                   fi
+                              fi
+                              for (( i=low; i < high; i++ )); do
+                                   if ! "${supported_curve[i]}"; then
+                                        [[ "${curves_ossl_output[i]}" == "$curve_found" ]] && break
+                                        [[ "${curves_ossl[i]}" == "$curve_found" ]] && break
+                                   fi
+                              done
+                              [[ $i -eq $high ]] && break
+                              supported_curve[i]=true
+                         fi
+                    done
                done
           done
      fi
@@ -10917,6 +10947,41 @@ run_fs() {
                     [[ $i -eq $nr_curves ]] && break
                     supported_curve[i]=true
                done
+               # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
+               # used in the certificate. The easiest solution is to move the curves to the end of the list.
+               # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
+               while true; do
+                    if ([[ "$proto" == 03 ]] && [[ ! "$ecdhe_cipher_list" == *RSA* ]]) || break; then
+                         curves_to_test=""
+                         for (( i=0; i < nr_curves; i++ )); do
+                              if ! "${curves_deprecated[i]}" || [[ "$proto" == 03 ]]; then
+                                   ! "${supported_curve[i]}" && curves_to_test+=", ${curves_hex[i]}"
+                              fi
+                         done
+                         [[ -z "$curves_to_test" ]] && break
+                         for (( i=0; i < nr_curves; i++ )); do
+                              if ! "${curves_deprecated[i]}" || [[ "$proto" == 03 ]]; then
+                                   "${supported_curve[i]}" && curves_to_test+=", ${curves_hex[i]}"
+                              fi
+                         done
+                         len1=$(printf "%02x" "$((2*${#curves_to_test}/7))")
+                         len2=$(printf "%02x" "$((2*${#curves_to_test}/7+2))")
+                         tls_sockets "$proto" "${ecdhe_cipher_list_hex:2}, 00,ff" "ephemeralkey" "00, 0a, 00, $len2, 00, $len1, ${curves_to_test:2}"
+                         sclient_success=$?
+                         [[ $sclient_success -ne 0 ]] && [[ $sclient_success -ne 2 ]] && break
+                         temp=$(awk -F': ' '/^Server Temp Key/ { print $2 }' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")
+                         curve_found="${temp%%,*}"
+                         if [[ "$curve_found" == "ECDH" ]]; then
+                              curve_found="${temp#*, }"
+                              curve_found="${curve_found%%,*}"
+                         fi
+                         for (( i=0; i < nr_curves; i++ )); do
+                              ! "${supported_curve[i]}" && [[ "${curves_ossl_output[i]}" == "$curve_found" ]] && break
+                         done
+                         [[ $i -eq $nr_curves ]] && break
+                         supported_curve[i]=true
+                    fi
+               done
           done
      fi
      if "$ecdhe_offered"; then

From 5e1db5f0a1c572722100a26a7506040b3c2d2f9b Mon Sep 17 00:00:00 2001
From: Dirk Wetter <dirk@testssl.sh>
Date: Fri, 7 Feb 2025 12:30:41 +0100
Subject: [PATCH 03/20] Address CA file parsing problem (3.2)

.... by forbidding spaces in supplied CA files/directories

Also now we're sanitizing the cmd line parameter better using `safe_echo()`

See also #2647 .
---
 testssl.sh | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 7f8d98a..038fd72 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -23545,10 +23545,10 @@ set_skip_tests() {
 # arg2: value (if no = provided)
 parse_opt_equal_sign() {
      if [[ "$1" == *=* ]]; then
-          echo ${1#*=}
+          safe_echo "${1#*=}"
           return 1  # = means we don't need to shift args!
      else
-          echo "$2"
+          safe_echo "${2}"
           return 0  # we need to shift
      fi
 }
@@ -24205,13 +24205,16 @@ parse_cmd_line() {
      [[ $CMDLINE_IP == one ]] && ( is_ipv4addr "$URI" || is_ipv6addr "$URI" )  && fatal_cmd_line "\"--ip=one\" plus supplying an IP address doesn't work" $ERR_CMDLINE
      "$do_mx_all_ips" && [[ "$NODNS" == none ]] && fatal_cmd_line "\"--mx\" and \"--nodns=none\" don't work together" $ERR_CMDLINE
 
-     if [[ -d $ADDTL_CA_FILES ]]; then
+     if [[ "${ADDTL_CA_FILES}" =~ \  ]]; then
+          fatal_cmd_line "The CA file \"${ADDTL_CA_FILES}\" must not contain spaces" $ERR_RESOURCE
+     fi
+     if [[ -d "${ADDTL_CA_FILES}" ]]; then
           ADDTL_CA_FILES="$ADDTL_CA_FILES/*.pem"
      else
           ADDTL_CA_FILES="${ADDTL_CA_FILES//,/ }"
      fi
-     for fname in $ADDTL_CA_FILES; do
-          [[ -s "$fname" ]] || fatal_cmd_line "CA file \"$fname\" does not exist" $ERR_RESOURCE
+     for fname in ${ADDTL_CA_FILES}; do
+          [[ -s "$fname" ]] || fatal_cmd_line "The CA file \"$fname\" does not exist" $ERR_RESOURCE
           grep -q 'BEGIN CERTIFICATE' "$fname" || fatal_cmd_line "\"$fname\" is not CA file in PEM format" $ERR_RESOURCE
      done
 

From ebc43ddafe6c93786b1fef51321f9272025e3e37 Mon Sep 17 00:00:00 2001
From: Dirk Wetter <dirk@testssl.sh>
Date: Fri, 7 Feb 2025 12:38:41 +0100
Subject: [PATCH 04/20] Add previously added line from 3.0 in change log

for consistency reasons
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0040a15..602b265 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -109,7 +109,7 @@
 * Renegotiation checks improved, also no false positive for Node.js anymore
 * Major update of client simulations with self-collected up-to-date data
 * Update of CA certificate stores
-* Lots of bug fixes
+* Lots of bug and security fixes
 * More travis/CI checks -- still place for improvements
 * Man page reviewed
 

From aa5d4917cfc04f5fb2f6b57c3726237cca6735b9 Mon Sep 17 00:00:00 2001
From: David Cooper <david.cooper@nist.gov>
Date: Mon, 3 Feb 2025 14:21:10 -0800
Subject: [PATCH 05/20] Enhance ticketbleed testing

Some versions of OpenSSL/LibreSSL do not support TLS 1.1 and earlier, either because they do not support the protocol (e.g, `$OEPNSSL s_client -tls1` results in a "unknown option" error) or because the cryptography needed to support these protocol versions (e.g., MD5/SHA1) is not available.

Given the limitations of some versions of $OPENSSL, this commit enhances ticketbleed testing in two ways. First, it performs the testing using the newest (non-TLS 1.3) version supported by the server, so that TLS 1 and TLS 1.1 aren't used unless TLS 1.2 is not supported. Second, it adds tests for whether the protocol version to be used is supported by $OPENSSL and for whether connection attempts were successful, rather than assuming connection attempts succeed.
---
 testssl.sh | 51 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 038fd72..9a6a339 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -16907,25 +16907,11 @@ run_ccs_injection(){
      return $ret
 }
 
-sub_session_ticket_tls() {
-     local tls_proto="$1"
-     local sessticket_tls=""
-     #FIXME: we likely have done this already before (either @ run_server_defaults() or at least the output
-     #       from a previous handshake) --> would save 1x connect. We have TLS_TICKET but not yet the ticket itself #FIXME
-     #ATTENTION: we DO NOT use SNI here as we assume ticketbleed is a vulnerability of the TLS stack. If we'd do SNI here, we'd also need
-     #           it in the ClientHello of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work!
-     #
-     sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $tls_proto $PROXY $SNI -connect $NODEIP:$PORT") </dev/null 2>$ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')"
-     sessticket_tls="$(sed -e 's/^.* - /x/g' -e 's/  .*$//g' <<< "$sessticket_tls" | tr '\n' ',')"
-     sed -e 's/ /,x/g' -e 's/-/,x/g' <<< "$sessticket_tls"
-
-}
-
 
 # see https://blog.filippo.io/finding-ticketbleed/ |  https://filippo.io/ticketbleed/
 run_ticketbleed() {
      local tls_hexcode tls_proto=""
-     local session_tckt_tls=""
+     local sessticket_tls="" session_tckt_tls=""
      local -i len_ch=300                            # fixed len of prepared clienthello below
      local sid="x00,x0B,xAD,xC0,xDE,x00,"           # some arbitrary bytes
      local len_sid="$(( ${#sid} / 4))"
@@ -16961,17 +16947,23 @@ run_ticketbleed() {
           return 0
      fi
 
-     if [[ 0 -eq $(has_server_protocol tls1) ]]; then
-          tls_hexcode="x03, x01"; tls_proto="-tls1"
+     if [[ 0 -eq $(has_server_protocol tls1_2) ]]; then
+          tls_hexcode="x03, x03"; tls_proto="-tls1_2"
      elif [[ 0 -eq $(has_server_protocol tls1_1) ]]; then
           tls_hexcode="x03, x02"; tls_proto="-tls1_1"
-     elif [[ 0 -eq $(has_server_protocol tls1_2) ]]; then
-          tls_hexcode="x03, x03"; tls_proto="-tls1_2"
+     elif [[ 0 -eq $(has_server_protocol tls1) ]]; then
+          tls_hexcode="x03, x01"; tls_proto="-tls1"
      elif [[ 0 -eq $(has_server_protocol ssl3) ]]; then
           tls_hexcode="x03, x00"; tls_proto="-ssl3"
      else # no protocol for some reason defined, determine TLS versions offered with a new handshake
           "$HAS_TLS13" && tls_proto="-no_tls1_3"
           $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS $tls_proto -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE </dev/null
+          sclient_connect_successful $? "$TMPFILE"
+          if [$? -ne 0 ]]; then
+               prln_warning "Cannot test for ticketbleed. Your OpenSSL cannot connect to $NODEIP:$PORT"
+               fileout "$jsonID" "WARN" "Cannot test for ticketbleed. Your OpenSSL cannot connect to $NODEIP:$PORT."
+               return 1
+          fi
           case "$(get_protocol $TMPFILE)" in
                *1.2)  tls_hexcode="x03, x03"; tls_proto="-tls1_2" ; add_proto_offered tls1_2 yes ;;
                *1.1)  tls_hexcode="x03, x02"; tls_proto="-tls1_1" ; add_proto_offered tls1_1 yes ;;
@@ -16979,9 +16971,28 @@ run_ticketbleed() {
                SSLv3) tls_hexcode="x03, x00"; tls_proto="-ssl3" ; add_proto_offered ssl3 yes ;;
           esac
      fi
+     if ! sclient_supported "$tls_proto"; then
+          prln_local_problem "Cannot test for ticketbleed. $OPENSSL doesn't support \"s_client $tls_proto\"."
+          fileout "$jsonID" "WARN" "Cannot test for ticketbleed. $OPENSSL doesn't support \"s_client $tls_proto\"."
+          return 1
+     fi
      debugme echo "using protocol $tls_hexcode"
 
-     session_tckt_tls="$(sub_session_ticket_tls "$tls_proto")"
+     #FIXME: we likely have done this already before (either @ run_server_defaults() or at least the output
+     #       from a previous handshake) --> would save 1x connect. We have TLS_TICKET but not yet the ticket itself #FIXME
+     #ATTENTION: we DO NOT use SNI here as we assume ticketbleed is a vulnerability of the TLS stack. If we'd do SNI here, we'd also need
+     #           it in the ClientHello of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work!
+     #
+     $OPENSSL s_client $(s_client_options "$BUGS $tls_proto $PROXY $SNI -connect $NODEIP:$PORT") </dev/null >$TMPFILE 2>$ERRFILE
+     sclient_connect_successful $? "$TMPFILE"
+     if [[ $? -ne 0 ]]; then
+          prln_warning "$OPENSSL unable to connect to $NODEIP:$PORT when testing for ticketbleed."
+          fileout "$jsonID" "WARN" "$OPENSSL unable to connect to $NODEIP:$PORT when testing for ticketbleed."
+          return 1
+     fi
+     sessticket_tls="$(awk '/TLS session ticket:/,/^$/' "$TMPFILE" | awk '!/TLS session ticket/')"
+     sessticket_tls="$(sed -e 's/^.* - /x/g' -e 's/  .*$//g' <<< "$sessticket_tls" | tr '\n' ',')"
+     session_tckt_tls="$(sed -e 's/ /,x/g' -e 's/-/,x/g' <<< "$sessticket_tls")"
      if [[ "$session_tckt_tls" == "," ]]; then
           pr_svrty_best "not vulnerable (OK)"
           outln ", no session tickets"

From acf48977c25ed57ee106901dcf246377141e35fd Mon Sep 17 00:00:00 2001
From: David Cooper <david.cooper@nist.gov>
Date: Thu, 13 Feb 2025 14:21:26 -0800
Subject: [PATCH 06/20] Fix pattern matches

This commit fixes three lines of code that use Bash substring matching. In each case, a list of strings to match was enclosed in brackets. This resulted in a match if the string to test contained any character from any of the strings to match. This commit fixes the issue by removing the brackets.

(The bugs were introduced in https://github.com/testssl/testssl.sh/commit/b8e9b09ca78832b1608dbce48305e65762368a0d and https://github.com/testssl/testssl.sh/commit/8149c2d5cf56d9874c91923e236b9feb5264b88b)
---
 testssl.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 038fd72..0015cba 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -3370,7 +3370,7 @@ run_cookie_flags() {     # ARG1: Path
      fi
 
      if [[ ! "$HTTP_STATUS_CODE" =~ 20 ]]; then
-          if [[ "$HTTP_STATUS_CODE" =~ [301|302] ]]; then
+          if [[ "$HTTP_STATUS_CODE" =~ 301|302 ]]; then
                msg302=" -- maybe better try target URL of 30x"
                msg302_=" (30x detected, better try target URL of 30x)"
           else
@@ -11312,7 +11312,7 @@ run_npn() {
           fileout "$jsonID" "INFO" "not offered"
      else
           # now comes a strange thing: "Protocols advertised by server:" is empty but connection succeeded
-          if [[ "$tmpstr" =~ [h2|spdy|http] ]]; then
+          if [[ "$tmpstr" =~ h2|spdy|http ]]; then
                out "$tmpstr"
                outln " (advertised)"
                fileout "$jsonID" "INFO" "offered with $tmpstr (advertised)"
@@ -16854,7 +16854,7 @@ run_ccs_injection(){
                fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe"
           fi
      elif [[ "${tls_hello_ascii:0:4}" == "1503" ]]; then
-          if [[ ! "${tls_hello_ascii:5:2}" =~ [03|02|01|00] ]]; then
+          if [[ ! "${tls_hello_ascii:5:2}" =~ 03|02|01|00 ]]; then
                pr_warning "test failed "
                out "no proper TLS reply (debug info: protocol sent: 1503${tls_hexcode#x03, x}, reply: ${tls_hello_ascii:0:14}"
                fileout "$jsonID" "DEBUG" "test failed, around line $LINENO, debug info (${tls_hello_ascii:0:14})" "$cve" "$cwe" "$hint"

From 96bd3072defec2e01c7335ee49ee225f73d01628 Mon Sep 17 00:00:00 2001
From: David Cooper <david.cooper@nist.gov>
Date: Fri, 14 Feb 2025 12:25:39 -0800
Subject: [PATCH 07/20] Enable run_npn() to use tls_sockets()

LibreSSL does not support the -nextprotoneg option. This commit enhances run_npn() to use tls_sockets() when $HAS_NPN is false, rather than reporting that the check can not be performed.
---
 testssl.sh | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 038fd72..3e3dede 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -11253,7 +11253,7 @@ npn_pre(){
           fileout "NPN" "WARN" "not tested as proxies do not support proxying it"
           return 1
      fi
-     if ! "$HAS_NPN"; then
+     if "$SSL_NATIVE" && ! "$HAS_NPN"; then
           pr_local_problem "$OPENSSL doesn't support NPN/SPDY";
           fileout "NPN" "WARN" "not tested $OPENSSL doesn't support NPN/SPDY"
           return 7
@@ -11299,13 +11299,24 @@ run_npn() {
           return 0
      fi
 
-     # TLS 1.3 s_client doesn't support -nextprotoneg when connecting with TLS 1.3. So we need to make sure it won't be used
-     # TLS13_ONLY is tested here again, just to be sure, see npn_pre
-     if "$HAS_TLS13" && ! $TLS13_ONLY ]] ; then
-           proto="-no_tls1_3"
+     if "$HAS_NPN"; then
+          # TLS 1.3 s_client doesn't support -nextprotoneg when connecting with TLS 1.3. So we need to make sure it won't be used
+          # TLS13_ONLY is tested here again, just to be sure, see npn_pre
+          if "$HAS_TLS13" && ! $TLS13_ONLY ]] ; then
+                proto="-no_tls1_3"
+          fi
+          $OPENSSL s_client $(s_client_options "$proto -connect $NODEIP:$PORT $BUGS $SNI -nextprotoneg "$NPN_PROTOs"") </dev/null 2>$ERRFILE >$TMPFILE
+          [[ $? -ne 0 ]] && ret=1
+     else
+          tls_sockets "03" "$TLS12_CIPHER" "all"
+          ret=$?
+          if [[ $ret -eq 0 ]] || [[ $ret -eq 2 ]]; then
+               ret=0
+          else
+               ret=1
+          fi
+          mv "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" "$TMPFILE"
      fi
-     $OPENSSL s_client $(s_client_options "$proto -connect $NODEIP:$PORT $BUGS $SNI -nextprotoneg "$NPN_PROTOs"") </dev/null 2>$ERRFILE >$TMPFILE
-     [[ $? -ne 0 ]] && ret=1
      tmpstr="$(grep -a '^Protocols' $TMPFILE | sed 's/Protocols.*: //')"
      if [[ -z "$tmpstr" ]] || [[ "$tmpstr" == " " ]]; then
           outln "not offered"

From e79dc8161e9cea19154d2b63f7db527915685ca1 Mon Sep 17 00:00:00 2001
From: Dirk <dirk@testssl.sh>
Date: Sat, 15 Feb 2025 13:33:52 +0100
Subject: [PATCH 08/20] Remove obsolete comment that SNI is not needed for
 ticketbleed

See also https://github.com/testssl/testssl.sh/pull/2656/files/aa5d4917cfc04f5fb2f6b57c3726237cca6735b9#r1954824502
---
 testssl.sh | 2 --
 1 file changed, 2 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 9a6a339..ba33b91 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -16980,8 +16980,6 @@ run_ticketbleed() {
 
      #FIXME: we likely have done this already before (either @ run_server_defaults() or at least the output
      #       from a previous handshake) --> would save 1x connect. We have TLS_TICKET but not yet the ticket itself #FIXME
-     #ATTENTION: we DO NOT use SNI here as we assume ticketbleed is a vulnerability of the TLS stack. If we'd do SNI here, we'd also need
-     #           it in the ClientHello of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work!
      #
      $OPENSSL s_client $(s_client_options "$BUGS $tls_proto $PROXY $SNI -connect $NODEIP:$PORT") </dev/null >$TMPFILE 2>$ERRFILE
      sclient_connect_successful $? "$TMPFILE"

From 5c7e7bcbc790caeaddc238c3d1924013467bb877 Mon Sep 17 00:00:00 2001
From: David Cooper <david.cooper@nist.gov>
Date: Wed, 19 Feb 2025 12:47:35 -0800
Subject: [PATCH 09/20] Fix check for OpenSSL supported curves

OpenSSL 3.X outputs a different error message than previous versions when $OPENSSL s_client -curves X ... is called with an unsupported curve. This was resulting in the check within find_openssl_binary() adding every curve to $OPENSSL_SUPPORTED_CURVES, even ones that were not supported. This commit changes to check in order to detect the new error message.
---
 testssl.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testssl.sh b/testssl.sh
index 76efce6..963d063 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -20505,7 +20505,7 @@ find_openssl_binary() {
           HAS_CURVES=true
           for curve in "${curves_ossl[@]}"; do
                # Same as above, we just don't need a port for invalid.
-               $OPENSSL s_client -curves $curve -connect $NXCONNECT </dev/null 2>&1 | grep -Eiaq "Error with command|unknown option"
+               $OPENSSL s_client -curves $curve -connect $NXCONNECT </dev/null 2>&1 | grep -Eiaq "Error with command|unknown option|Call to SSL_CONF_cmd(.*) failed"
                [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve "
           done
      fi

From ec220e7c278e17e3642105866e3dbe55ff4e2a22 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 20 Feb 2025 00:56:34 +0000
Subject: [PATCH 10/20] Bump docker/build-push-action from 6.13.0 to 6.14.0

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.13.0 to 6.14.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.13.0...v6.14.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/docker-3.2.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/docker-3.2.yml b/.github/workflows/docker-3.2.yml
index 096d4b2..f3ed375 100644
--- a/.github/workflows/docker-3.2.yml
+++ b/.github/workflows/docker-3.2.yml
@@ -48,7 +48,7 @@ jobs:
           password: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Build and push
-        uses: docker/build-push-action@v6.13.0
+        uses: docker/build-push-action@v6.14.0
         with:
           push: ${{ github.event_name != 'pull_request' }}
           context: .

From b3609603f9319e784a6aab62e8eac2e2d742cc84 Mon Sep 17 00:00:00 2001
From: Riccardo Germenia <rgermenia@fbk.eu>
Date: Thu, 20 Feb 2025 15:45:05 +0100
Subject: [PATCH 11/20] remove unnecessary "if" statements and remove break
 from "if" statements

---
 testssl.sh | 38 +++++++++++++++-----------------------
 1 file changed, 15 insertions(+), 23 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 64a386f..49c8d0e 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -10873,22 +10873,18 @@ run_fs() {
                          [[ $i -eq $high ]] && break
                          supported_curve[i]=true
                     done
-                    while true; do
-                         # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
-                         # used in the certificate. The easiest solution is to move the curves to the end of the list.
-                         # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
-                         if ((! "$HAS_TLS13" || [[ "$proto" == "-no_tls1_3" ]]) && [[ ! "$ecdhe_cipher_list" == *RSA* ]]) || break; then
+                    # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
+                    # used in the certificate. The easiest solution is to move the curves to the end of the list.
+                    # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
+                    if (! "$HAS_TLS13" || [[ "$proto" == "-no_tls1_3" ]]) && [[ ! "$ecdhe_cipher_list" == *RSA* ]]; then
+                         while true; do
                               curves_to_test=""
                               for (( i=low; i < high; i++ )); do
-                                   if ! "${curves_deprecated[i]}"; then
-                                        "${ossl_supported[i]}" && ! "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
-                                   fi
+                                   "${ossl_supported[i]}" && ! "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
                               done
                               [[ -z "$curves_to_test" ]] && break
                               for (( i=low; i < high; i++ )); do
-                                   if ! "${curves_deprecated[i]}"; then
-                                        "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
-                                   fi
+                                   "${supported_curve[i]}" && curves_to_test+=":${curves_ossl[i]}"
                               done
                               $OPENSSL s_client $(s_client_options "$proto -cipher "\'${ecdhe_cipher_list:1}\'" -ciphersuites "\'${tls13_cipher_list:1}\'" -curves "${curves_to_test:1}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") &>$TMPFILE </dev/null
                               sclient_connect_successful $? $TMPFILE || break
@@ -10909,8 +10905,8 @@ run_fs() {
                               done
                               [[ $i -eq $high ]] && break
                               supported_curve[i]=true
-                         fi
-                    done
+                         done
+                    fi
                done
           done
      fi
@@ -10950,19 +10946,15 @@ run_fs() {
                # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
                # used in the certificate. The easiest solution is to move the curves to the end of the list.
                # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
-               while true; do
-                    if ([[ "$proto" == 03 ]] && [[ ! "$ecdhe_cipher_list" == *RSA* ]]) || break; then
+               if ([[ "$proto" == 03 ]] && [[ ! "$ecdhe_cipher_list" == *RSA* ]]); then
+                    while true; do
                          curves_to_test=""
                          for (( i=0; i < nr_curves; i++ )); do
-                              if ! "${curves_deprecated[i]}" || [[ "$proto" == 03 ]]; then
-                                   ! "${supported_curve[i]}" && curves_to_test+=", ${curves_hex[i]}"
-                              fi
+                              ! "${supported_curve[i]}" && curves_to_test+=", ${curves_hex[i]}"
                          done
                          [[ -z "$curves_to_test" ]] && break
                          for (( i=0; i < nr_curves; i++ )); do
-                              if ! "${curves_deprecated[i]}" || [[ "$proto" == 03 ]]; then
-                                   "${supported_curve[i]}" && curves_to_test+=", ${curves_hex[i]}"
-                              fi
+                              "${supported_curve[i]}" && curves_to_test+=", ${curves_hex[i]}"
                          done
                          len1=$(printf "%02x" "$((2*${#curves_to_test}/7))")
                          len2=$(printf "%02x" "$((2*${#curves_to_test}/7+2))")
@@ -10980,8 +10972,8 @@ run_fs() {
                          done
                          [[ $i -eq $nr_curves ]] && break
                          supported_curve[i]=true
-                    fi
-               done
+                    done
+               fi
           done
      fi
      if "$ecdhe_offered"; then

From 1539148f0bfceed49a76d01472ebcdf1b612a180 Mon Sep 17 00:00:00 2001
From: Fabio Kruger <10956489+krufab@users.noreply.github.com>
Date: Sat, 22 Feb 2025 00:55:08 +0100
Subject: [PATCH 12/20] Corrected typo in the help message

Signed-off-by: Fabio Kruger <10956489+krufab@users.noreply.github.com>
---
 testssl.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testssl.sh b/testssl.sh
index 963d063..f40d466 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -20824,7 +20824,7 @@ file output options (can also be preset via environment variables)
      --html                        additional output as HTML to file '\${NODE}-p\${port}\${YYYYMMDD-HHMM}.html'
      --htmlfile|-oH <htmlfile>     additional output as HTML to the specified file or directory, similar to --logfile
      --out(f,F)ile|-oa/-oA <fname> log to a LOG,JSON,CSV,HTML file (see nmap). -oA/-oa: pretty/flat JSON.
-                                   "auto" uses '\${NODE}-p\${port}\${YYYYMMDD-HHMM}'. If fname if a dir uses 'dir/\${NODE}-p\${port}\${YYYYMMDD-HHMM}'
+                                   "auto" uses '\${NODE}-p\${port}\${YYYYMMDD-HHMM}'. If fname is a dir uses 'dir/\${NODE}-p\${port}\${YYYYMMDD-HHMM}'
      --hints                       additional hints to findings
      --severity <severity>         severities with lower level will be filtered for CSV+JSON, possible values <LOW|MEDIUM|HIGH|CRITICAL>
      --append                      if (non-empty) <logfile>, <csvfile>, <jsonfile> or <htmlfile> exists, append to file. Omits any header

From 9429afade1cf716e4563d727ddea1a9ff728b1c8 Mon Sep 17 00:00:00 2001
From: Magnus Larsen <magnusfynbo@hotmail.com>
Date: Sun, 23 Feb 2025 11:48:41 +0100
Subject: [PATCH 13/20] fix(rating): explicit enable rating if required tests
 are ran

---
 testssl.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/testssl.sh b/testssl.sh
index f40d466..0ae0160 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -23427,6 +23427,7 @@ set_rating_state() {
           return 1
      fi
 
+     do_rating=true
      return 0
 }
 

From 352ed61a2e62524ae0bbeede43687802bcf904e2 Mon Sep 17 00:00:00 2001
From: Brett Randall <javabrett@gmail.com>
Date: Wed, 19 Feb 2025 18:59:15 +1100
Subject: [PATCH 14/20] Improved (experimental) Extended Validation (EV)
 certificate identification.

Three changes:

- added grep for "EV TLS" in addition to "EV SSL", as some issuers are
  using this.  This grep link actually picks-up most EV policies.
- Added policy detection for 2.23.140.1.1.  This is from CA Browser
  Forum https://cabforum.org/resources/object-registry/ extended-validation(1).
- Added policy detection for 1.3.6.1.4.1.38064.1.3.1.4 , which is SSL.com's EV policy.
---
 testssl.sh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/testssl.sh b/testssl.sh
index 0ae0160..7279976 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -9637,13 +9637,15 @@ certificate_info() {
      jsonID="cert_certificatePolicies_EV"
      # only the first one, seldom we have two
      policy_oid=$(awk '/ .Policy: / { print $2 }' <<< "$cert_txt" | awk 'NR < 2')
-     if grep -Eq 'Extended Validation|Extended Validated|EV SSL|EV CA' <<< "$issuer" || \
+     if grep -Eq 'Extended Validation|Extended Validated|EV SSL|EV CA|EV TLS' <<< "$issuer" || \
+          [[ 2.23.140.1.1 == "$policy_oid" ]] || \
           [[ 2.16.840.1.114028.10.1.2 == "$policy_oid" ]] || \
           [[ 2.16.840.1.114412.1.3.0.2 == "$policy_oid" ]] || \
           [[ 2.16.840.1.114412.2.1 == "$policy_oid" ]] || \
           [[ 2.16.578.1.26.1.3.3 == "$policy_oid" ]] || \
           [[ 1.3.6.1.4.1.17326.10.14.2.1.2 == "$policy_oid" ]] || \
           [[ 1.3.6.1.4.1.17326.10.8.12.1.2 == "$policy_oid" ]] || \
+          [[ 1.3.6.1.4.1.38064.1.3.1.4 == "$policy_oid" ]] || \
           [[ 1.3.6.1.4.1.13177.10.1.3.10 == "$policy_oid" ]] ; then
           out "yes "
           fileout "${jsonID}${json_postfix}" "OK" "yes"

From 5f548b421402e3a1f1ebc91b1e14a8a86a00f0e5 Mon Sep 17 00:00:00 2001
From: Brett Randall <javabrett@gmail.com>
Date: Wed, 26 Feb 2025 13:02:16 +1100
Subject: [PATCH 15/20] Update CONTRIBUTING.md

Fixed typo complains -> complaints.
---
 CONTRIBUTING.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1029fbe..d28938f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,7 +10,7 @@ Please note the following:
 * Please one pull request per feature or bug fix or improvement. Please do not mix issues.
 * Documentation pays off in the long run. So please your document your code and the pull request and/or commit message.
 * Please test your changes thoroughly as reliability is important for this project. You may want to check different servers with different settings.
-* GitHub actions are running automatically when anything is committed. You should see any complains. Beforehand you can check with `prove -v` from the "root dir" of this project.
+* GitHub actions are running automatically when anything is committed. You should see any complaints. Beforehand you can check with `prove -v` from the "root dir" of this project.
 * If it's a new feature, please consider writing a unit test for it. You can use e.g. `t/10_baseline_ipv4_http.t` or `t/61_diff_testsslsh.t` as a template. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start.
 * If it's a new feature, it would need to be documented in the appropriate section in `help()` and in `~/doc/testssl.1.md`
 

From c38f46880f6f7878bc5578ae10be10c79129243a Mon Sep 17 00:00:00 2001
From: David Cooper <david.cooper@nist.gov>
Date: Wed, 26 Feb 2025 13:25:49 -0800
Subject: [PATCH 16/20] Avoid subshell overhead

This commit removes the use of parenthesis in two expressions in run_fs() in order to avoid subshell overhead.
---
 testssl.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index fa05197..fbfe660 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -10929,7 +10929,7 @@ run_fs() {
                     # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
                     # used in the certificate. The easiest solution is to move the curves to the end of the list.
                     # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
-                    if (! "$HAS_TLS13" || [[ "$proto" == "-no_tls1_3" ]]) && [[ ! "$ecdhe_cipher_list" == *RSA* ]]; then
+                    if { ! "$HAS_TLS13" || [[ "$proto" == "-no_tls1_3" ]]; } && [[ ! "$ecdhe_cipher_list" == *RSA* ]]; then
                          while true; do
                               curves_to_test=""
                               for (( i=low; i < high; i++ )); do
@@ -10999,7 +10999,7 @@ run_fs() {
                # Versions of TLS prior to 1.3 close the connection if the client does not support the curve
                # used in the certificate. The easiest solution is to move the curves to the end of the list.
                # instead of removing them from the ClientHello. This is only needed if there is no RSA certificate.
-               if ([[ "$proto" == 03 ]] && [[ ! "$ecdhe_cipher_list" == *RSA* ]]); then
+               if [[ "$proto" == 03 ]] && [[ ! "$ecdhe_cipher_list" == *RSA* ]]; then
                     while true; do
                          curves_to_test=""
                          for (( i=0; i < nr_curves; i++ )); do

From 5bfe6d63bd616a03f8cb2e7025bf3950d719359c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Feb 2025 00:03:46 +0000
Subject: [PATCH 17/20] Bump docker/build-push-action from 6.14.0 to 6.15.0

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.14.0 to 6.15.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.14.0...v6.15.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/docker-3.2.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/docker-3.2.yml b/.github/workflows/docker-3.2.yml
index f3ed375..cc28ca1 100644
--- a/.github/workflows/docker-3.2.yml
+++ b/.github/workflows/docker-3.2.yml
@@ -48,7 +48,7 @@ jobs:
           password: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Build and push
-        uses: docker/build-push-action@v6.14.0
+        uses: docker/build-push-action@v6.15.0
         with:
           push: ${{ github.event_name != 'pull_request' }}
           context: .

From c37e1714240dff27ec720bb70901bc491c47932e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Feb 2025 00:03:48 +0000
Subject: [PATCH 18/20] Bump docker/setup-qemu-action from 3.4.0 to 3.5.0

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/docker-3.2.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/docker-3.2.yml b/.github/workflows/docker-3.2.yml
index f3ed375..42d8471 100644
--- a/.github/workflows/docker-3.2.yml
+++ b/.github/workflows/docker-3.2.yml
@@ -23,7 +23,7 @@ jobs:
 
       - name: Setup QEMU
         id: qemu
-        uses: docker/setup-qemu-action@v3.4.0
+        uses: docker/setup-qemu-action@v3.5.0
 
       - name: Setup Buildx
         id: buildx

From 4d43d976223e2fc9fe41981e582ea61f6ccbec90 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 3 Mar 2025 00:22:11 +0000
Subject: [PATCH 19/20] Bump docker/setup-qemu-action from 3.5.0 to 3.6.0

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3.5.0...v3.6.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/docker-3.2.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/docker-3.2.yml b/.github/workflows/docker-3.2.yml
index f7d766d..96a6b70 100644
--- a/.github/workflows/docker-3.2.yml
+++ b/.github/workflows/docker-3.2.yml
@@ -23,7 +23,7 @@ jobs:
 
       - name: Setup QEMU
         id: qemu
-        uses: docker/setup-qemu-action@v3.5.0
+        uses: docker/setup-qemu-action@v3.6.0
 
       - name: Setup Buildx
         id: buildx

From bbdf19df8500d4ffab335a9f0594c1df47e92d7c Mon Sep 17 00:00:00 2001
From: David Cooper <david.cooper@nist.gov>
Date: Tue, 4 Mar 2025 14:01:50 -0800
Subject: [PATCH 20/20] Fix typo

This commit fixes a typo that was introduced by #2656.
---
 testssl.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testssl.sh b/testssl.sh
index 04f6f09..c2f06a2 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -17037,7 +17037,7 @@ run_ticketbleed() {
           "$HAS_TLS13" && tls_proto="-no_tls1_3"
           $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS $tls_proto -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE </dev/null
           sclient_connect_successful $? "$TMPFILE"
-          if [$? -ne 0 ]]; then
+          if [[ $? -ne 0 ]]; then
                prln_warning "Cannot test for ticketbleed. Your OpenSSL cannot connect to $NODEIP:$PORT"
                fileout "$jsonID" "WARN" "Cannot test for ticketbleed. Your OpenSSL cannot connect to $NODEIP:$PORT."
                return 1