From dc9e3bfb585ba374e7947e4f06ef96af07c272c5 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 7 Feb 2017 14:25:41 -0500 Subject: [PATCH 01/12] Add option to create HTML This PR adds the option to generate HTML. The code was created as follows: * For each output function (`out()`, `outln()`, `pr_liteblue()`, etc.) I created two functions: one that just outputs to the terminal and one that outputs to the terminal and to the HTML file (if an HTML file is to be created). * I modified the code so that any output that should appear in the HTML file in addition to being displayed on the terminal is sent through one of the display functions: out()`, `outln()`, `pr_liteblue()`, etc. * I created a new function `retstring()` to use in place of `out()` when a function is creating a string to be "captured" by the calling function. * I modified the code so that no string returned by a function includes color-coding escape characters. --- testssl.sh | 670 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 407 insertions(+), 263 deletions(-) diff --git a/testssl.sh b/testssl.sh index 6d2462b..75987e0 100755 --- a/testssl.sh +++ b/testssl.sh @@ -156,6 +156,7 @@ WIDE=${WIDE:-false} # whether to display for some options ju LOGFILE=${LOGFILE:-""} # logfile if used JSONFILE=${JSONFILE:-""} # jsonfile if used CSVFILE=${CSVFILE:-""} # csvfile if used +HTMLFILE=${CSVFILE:-""} # HTML if used APPEND=${APPEND:-false} # append to csv/json file instead of overwriting it GIVE_HINTS=false # give an addtional info to findings HAS_IPv6=${HAS_IPv6:-false} # if you have OpenSSL with IPv6 support AND IPv6 networking set it to yes @@ -558,81 +559,146 @@ declare TLS_CIPHER_OSSL_SUPPORTED=() ###### output functions ###### # a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%' and '--' does the rest. -out(){ +out_html() { + "$do_html" && printf -- "%b" "${1//%/%%}" >> "$HTMLFILE" +} + +out() { +# if [[ "$BASH_VERSINFO" -eq 4 ]]; then + printf -- "%b" "${1//%/%%}" + out_html "$1" +# else +# /usr/bin/printf -- "${1//%/%%}" +# fi +} +outln() { out "$1\n"; } + +out_term(){ # if [[ "$BASH_VERSINFO" -eq 4 ]]; then printf -- "%b" "${1//%/%%}" # else # /usr/bin/printf -- "${1//%/%%}" # fi } -outln() { out "$1\n"; } +outln_term() { out_term "$1\n"; } + +retstring(){ + printf -- "%b" "${1//%/%%}" +} #TODO: Still no shell injection safe but if just run it from the cmd line: that's fine # color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html -pr_liteblue() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[0;32m$1" || out "\033[0;34m$1" ) || out "$1"; pr_off; } # not yet used +pr_liteblue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;32m$1" || out_term "\033[0;34m$1" ) || out_term "$1"; pr_off; } # not yet used +pr_liteblue() { pr_liteblue_term "$1"; out_html "$1"; } +pr_liteblueln_term() { pr_liteblue_term "$1"; outln_term; } pr_liteblueln() { pr_liteblue "$1"; outln; } -pr_blue() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[1;32m$1" || out "\033[1;34m$1" ) || out "$1"; pr_off; } # used for head lines of single tests +pr_blue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;32m$1" || out_term "\033[1;34m$1" ) || out_term "$1"; pr_off; } # used for head lines of single tests +pr_blue() { pr_blue_term "$1"; out_html "$1"; } +pr_blueln_term() { pr_blue_term "$1"; outln_term; } pr_blueln() { pr_blue "$1"; outln; } -pr_warning() { [[ "$COLOR" -eq 2 ]] && out "\033[0;35m$1" || pr_underline "$1"; pr_off; } # some local problem: one test cannot be done -pr_warningln() { pr_warning "$1"; outln; } # litemagenta -pr_magenta() { [[ "$COLOR" -eq 2 ]] && out "\033[1;35m$1" || pr_underline "$1"; pr_off; } # fatal error: quitting because of this! -pr_magentaln() { pr_magenta "$1"; outln; } +pr_warning_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;35m$1" || pr_underline_term "$1"; pr_off; } # some local problem: one test cannot be done +pr_warning() { pr_warning_term "$1"; out_html "$1"; } +pr_warningln_term() { pr_warning_term "$1"; outln_term; } # litemagenta +pr_warningln() { pr_warning "$1"; outln; } +pr_magenta_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;35m$1" || pr_underline_term "$1"; pr_off; } # fatal error: quitting because of this! +pr_magenta() { pr_magenta_term "$1"; out_html "$1"; } +pr_magentaln_term() { pr_magenta_term "$1"; outln_term; } +pr_magentaln() { pr_magenta "$1"; outln; } -pr_litecyan() { [[ "$COLOR" -eq 2 ]] && out "\033[0;36m$1" || out "$1"; pr_off; } # not yet used +pr_litecyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;36m$1" || out_term "$1"; pr_off; } # not yet used +pr_litecyan() { pr_litecyan_term "$1"; out_html "$1"; } +pr_litecyanln_term() { pr_litecyan_term "$1"; outln_term; } pr_litecyanln() { pr_litecyan "$1"; outln; } -pr_cyan() { [[ "$COLOR" -eq 2 ]] && out "\033[1;36m$1" || out "$1"; pr_off; } # additional hint +pr_cyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;36m$1" || out_term "$1"; pr_off; } # additional hint +pr_cyan() { pr_cyan_term "$1"; out_html "$1"; } +pr_cyanln_term() { pr_cyan_term "$1"; outln_term; } pr_cyanln() { pr_cyan "$1"; outln; } -pr_litegreyln() { pr_litegrey "$1"; outln; } # not really usable on a black background, see .. -pr_litegrey() { [[ "$COLOR" -eq 2 ]] && out "\033[0;37m$1" || out "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 -pr_grey() { [[ "$COLOR" -eq 2 ]] && out "\033[1;30m$1" || out "$1"; pr_off; } +pr_litegreyln_term() { pr_litegrey_term "$1"; outln_term; } # not really usable on a black background, see .. +pr_litegreyln() { pr_litegrey "$1"; outln; } +pr_litegrey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 +pr_litegrey() { pr_litegrey_term "$1"; out_html "$1"; } +pr_grey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } +pr_grey() { pr_grey_term "$1"; out_html "$1"; } +pr_greyln_term() { pr_grey_term "$1"; outln_term; } pr_greyln() { pr_grey "$1"; outln; } -pr_done_good() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[0;34m$1" || out "\033[0;32m$1" ) || out "$1"; pr_off; } # litegreen (liteblue), This is good -pr_done_goodln() { pr_done_good "$1"; outln; } -pr_done_best() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[1;34m$1" || out "\033[1;32m$1" ) || out "$1"; pr_off; } # green (blue), This is the best -pr_done_bestln() { pr_done_best "$1"; outln; } +pr_done_good_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;34m$1" || out_term "\033[0;32m$1" ) || out_term "$1"; pr_off; } # litegreen (liteblue), This is good +pr_done_good() { pr_done_good_term "$1"; out_html "$1"; } +pr_done_goodln_term() { pr_done_good_term "$1"; outln_term; } +pr_done_goodln() { pr_done_good "$1"; outln; } +pr_done_best_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;34m$1" || out_term "\033[1;32m$1" ) || out_term "$1"; pr_off; } # green (blue), This is the best +pr_done_best() { pr_done_best_term "$1"; out_html "$1"; } +pr_done_bestln_term() { pr_done_best_term "$1"; outln_term; } +pr_done_bestln() { pr_done_best "$1"; outln; } -pr_svrty_low() { [[ "$COLOR" -eq 2 ]] && out "\033[1;33m$1" || out "$1"; pr_off; } # yellow brown | academic or minor problem +pr_svrty_low_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;33m$1" || out_term "$1"; pr_off; } # yellow brown | academic or minor problem +pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$1"; } +pr_svrty_lowln_term() { pr_svrty_low_term "$1"; outln_term; } pr_svrty_lowln() { pr_svrty_low "$1"; outln; } -pr_svrty_medium() { [[ "$COLOR" -eq 2 ]] && out "\033[0;33m$1" || out "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this +pr_svrty_medium_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;33m$1" || out_term "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this +pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$1"; } +pr_svrty_mediumln_term() { pr_svrty_medium_term "$1"; outln_term; } pr_svrty_mediumln() { pr_svrty_medium "$1"; outln; } -pr_svrty_high() { [[ "$COLOR" -eq 2 ]] && out "\033[0;31m$1" || pr_bold "$1"; pr_off; } # litered +pr_svrty_high_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;31m$1" || pr_bold_term "$1"; pr_off; } # litered +pr_svrty_high() { pr_svrty_high_term "$1"; out_html "$1"; } +pr_svrty_highln_term() { pr_svrty_high_term "$1"; outln_term; } pr_svrty_highln() { pr_svrty_high "$1"; outln; } -pr_svrty_critical() { [[ "$COLOR" -eq 2 ]] && out "\033[1;31m$1" || pr_bold "$1"; pr_off; } # red +pr_svrty_critical_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;31m$1" || pr_bold_term "$1"; pr_off; } # red +pr_svrty_critical() { pr_svrty_critical_term "$1"; out_html "$1"; } +pr_svrty_criticalln_term() { pr_svrty_critical_term "$1"; outln_term; } pr_svrty_criticalln(){ pr_svrty_critical "$1"; outln; } -pr_deemphasize() { out "$1"; } # hook for a weakened screen output, see #600 -pr_deemphasizeln() { outln "$1"; } +pr_deemphasize_term() { out_term "$1"; } # hook for a weakened screen output, see #600 +pr_deemphasize() { pr_deemphasize_term "$1"; out_html "$1"; } +pr_deemphasizeln_term() { pr_deemphasize_term "$1"; outln_term; } +pr_deemphasizeln() { pr_deemphasize "$1"; outln; } # color=1 functions -pr_off() { [[ "$COLOR" -ne 0 ]] && out "\033[m"; } -pr_bold() { [[ "$COLOR" -ne 0 ]] && out "\033[1m$1" || out "$1"; pr_off; } +pr_off() { [[ "$COLOR" -ne 0 ]] && out_term "\033[m"; } +pr_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1m$1" || out_term "$1"; pr_off; } +pr_bold() { pr_bold_term "$1"; out_html "$1"; } +pr_boldln_term() { pr_bold_term "$1"; outln_term; } pr_boldln() { pr_bold "$1" ; outln; } -pr_italic() { [[ "$COLOR" -ne 0 ]] && out "\033[3m$1" || out "$1"; pr_off; } +pr_italic_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[3m$1" || out_term "$1"; pr_off; } +pr_italic() { pr_italic_term "$1"; out_html "$1"; } +pr_italicln_term() { pr_italic_term "$1"; outln_term; } pr_italicln() { pr_italic "$1" ; outln; } -pr_strikethru() { [[ "$COLOR" -ne 0 ]] && out "\033[9m$1" || out "$1"; pr_off; } # ugly! +pr_strikethru_term() { [[ "$COLOR" -ne 0 ]] && out "\033[9m$1" || out "$1"; pr_off; } # ugly! +pr_strikethru() { pr_strikethru_term "$1"; out_html "$1"; } +pr_strikethruln_term() { pr_strikethru_term "$1"; outln_term; } pr_strikethruln() { pr_strikethru "$1" ; outln; } -pr_underline() { [[ "$COLOR" -ne 0 ]] && out "\033[4m$1" || out "$1"; pr_off; } -pr_reverse() { [[ "$COLOR" -ne 0 ]] && out "\033[7m$1" || out "$1"; pr_off; } -pr_reverse_bold() { [[ "$COLOR" -ne 0 ]] && out "\033[7m\033[1m$1" || out "$1"; pr_off; } +pr_underline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[4m$1" || out_term "$1"; pr_off; } +pr_underline() { pr_underline_term "$1"; out_html "$1"; } +pr_reverse_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m$1" || out_term "$1"; pr_off; } +pr_reverse() { pr_reverse_term "$1"; out_html "$1"; } +pr_reverse_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m\033[1m$1" || out_term "$1"; pr_off; } +pr_reverse_bold() { pr_reverse_bold_term "$1"; out_html "$1"; } #pr_headline() { pr_blue "$1"; } #http://misc.flogisoft.com/bash/tip_colors_and_formatting #pr_headline() { [[ "$COLOR" -eq 2 ]] && out "\033[1;30m\033[47m$1" || out "$1"; pr_off; } -pr_headline() { [[ "$COLOR" -ne 0 ]] && out "\033[1m\033[4m$1" || out "$1"; pr_off; } +pr_headline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1m\033[4m$1" || out_term "$1"; pr_off; } +pr_headline() { pr_headline_term "$1"; out_html "$1"; } +pr_headlineln_term() { pr_headline_term "$1"; outln_term; } pr_headlineln() { pr_headline "$1" ; outln; } +pr_squoted_term() { out_term "'$1'"; } pr_squoted() { out "'$1'"; } +pr_dquoted_term() { out_term "\"$1\""; } pr_dquoted() { out "\"$1\""; } +local_problem_term() { pr_warning_term "Local problem: $1"; } local_problem() { pr_warning "Local problem: $1"; } +local_problem_ln_term() { pr_warningln_term "Local problem: $1"; } local_problem_ln() { pr_warningln "Local problem: $1"; } +fixme_term() { pr_warning_term "fixme: $1"; } fixme() { pr_warning "fixme: $1"; } +fixmeln_term() { pr_warningln_term "fixme: $1"; } fixmeln() { pr_warningln "fixme: $1"; } ### color switcher (see e.g. https://linuxtidbits.wordpress.com/2008/08/11/output-color-on-bash-scripts/ @@ -893,6 +959,24 @@ fileout() { # ID, SEVERITY, FINDING, CVE, CWE, HINT } ################### FILE FORMATING END ######################### +html_header() { + out_html "\n" + out_html "\n" + out_html "\n" + out_html "\n" + out_html "\n" + out_html "testssl.sh\n" + out_html "\n" + out_html "\n" + out_html "
\n"
+}
+
+html_footer() {
+     out_html "
\n" + out_html "\n" + out_html "\n" +} + ###### helper function definitions ###### if [[ $(uname) == "Linux" ]] ; then @@ -1120,7 +1204,7 @@ string_to_asciihex() { output+="$(printf "%02x," "'${string:i:1}")" done [[ -n "$string" ]] && output+="$(printf "%02x" "'${string:eos:1}")" - out "$output" + retstring "$output" return 0 } @@ -1360,7 +1444,7 @@ run_http_date() { out "Got no HTTP time, maybe try different URL?"; fileout "http_clock_skew" "INFO" "HTTP clock skew not measured. Got no HTTP time, maybe try different URL?" fi - debugme out ", epoch: $HTTP_TIME" + debugme out_term ", epoch: $HTTP_TIME" fi outln detect_ipv4 @@ -1394,7 +1478,7 @@ detect_header() { out "\n$spaces" # first awk matches the key, second extracts the from the first line the value, be careful with quotes here! HEADERVALUE=$(grep -Faiw "$key:" $HEADERFILE | sed 's/^.*://' | head -1) - [[ $DEBUG -ge 2 ]] && pr_italic "$HEADERVALUE" && out "\n$spaces" + [[ $DEBUG -ge 2 ]] && pr_italic_term "$HEADERVALUE" && out_term "\n$spaces" fileout "$2""_multiple" "WARN" "Multiple $2 headers. Using first header: $HEADERVALUE" return $nr fi @@ -1559,7 +1643,7 @@ run_hpkp() { # Get the SPKIs first spki=$(tr ';' '\n' < $TMPFILE | tr -d ' ' | tr -d '\"' | awk -F'=' '/pin.*=/ { print $2 }') - debugme outln "\n$spki" + debugme outln_term "\n$spki" # Look at the host certificate first # get the key fingerprint from the host certificate @@ -1609,7 +1693,7 @@ run_hpkp() { pr_done_good "$hpkp_spki" fileout "hpkp_$hpkp_spki" "OK" "SPKI $hpkp_spki matches the host certificate" fi - debugme out "\n $hpkp_spki | $hpkp_spki_hostcert" + debugme out_term "\n $hpkp_spki | $hpkp_spki_hostcert" # Check for intermediate match if ! "$certificate_found"; then @@ -1989,7 +2073,7 @@ normalize_ciphercode() { } prettyprint_local() { - local arg + local arg line local hexcode dash ciph sslvers kx auth enc mac export local re='^[0-9A-Fa-f]+$' @@ -2012,8 +2096,7 @@ prettyprint_local() { if [[ -z "$1" ]]; then $OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE | while read hexcode dash ciph sslvers kx auth enc mac export ; do # -V doesn't work with openssl < 1.0 normalize_ciphercode $hexcode - neat_list "$HEXC" "$ciph" "$kx" "$enc" - outln + outln "$(neat_list "$HEXC" "$ciph" "$kx" "$enc")" done else #for arg in $(echo $@ | sed 's/,/ /g'); do @@ -2022,8 +2105,9 @@ prettyprint_local() { normalize_ciphercode $hexcode # for numbers we don't do word matching: [[ $arg =~ $re ]] && \ - neat_list "$HEXC" "$ciph" "$kx" "$enc" | grep -ai "$arg" || \ - neat_list "$HEXC" "$ciph" "$kx" "$enc" | grep -wai "$arg" + line="$(neat_list "$HEXC" "$ciph" "$kx" "$enc" | grep -ai "$arg" || \ + neat_list "$HEXC" "$ciph" "$kx" "$enc" | grep -wai "$arg")" + [[ -n "$line" ]] && outln "$line" done done fi @@ -2206,13 +2290,13 @@ show_rfc_style(){ [[ "$hexcode" == "${TLS_CIPHER_HEXCODE[i]}" ]] && rfcname="${TLS_CIPHER_RFC_NAME[i]}" && break done [[ "$rfcname" == "-" ]] && rfcname="" - [[ -n "$rfcname" ]] && out "$rfcname" + [[ -n "$rfcname" ]] && retstring "$rfcname" return 0 } neat_header(){ - printf -- "Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits${ADD_RFC_STR:+ Cipher Suite Name (RFC)}\n" - printf -- "%s--------------------------------------------------------------------------${ADD_RFC_STR:+---------------------------------------------------}\n" + outln "$(printf -- "Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits${ADD_RFC_STR:+ Cipher Suite Name (RFC)}")" + outln "$(printf -- "%s--------------------------------------------------------------------------${ADD_RFC_STR:+---------------------------------------------------}")" } @@ -2222,11 +2306,12 @@ neat_header(){ # arg4: encryption (maybe included "export") # arg5: "true" if the cipher's "quality" should be highlighted # "false" if the line should be printed in light grey -# empty if line should be printed in black +# empty if line should be returned as a string neat_list(){ local hexcode="$1" local ossl_cipher="$2" tls_cipher="" - local kx enc strength line + local kx enc strength line what_dh bits + local -i i len kx="${3//Kx=/}" enc="${4//Enc=/}" @@ -2241,25 +2326,30 @@ neat_list(){ [[ -n "$ADD_RFC_STR" ]] && tls_cipher="$(show_rfc_style "$hexcode")" - if [[ "$5" == "false" ]]; then + if [[ -z "$5" ]]; then + retstring "$(printf -- " %-7s %-33s %-10s %-12s%-8s${ADD_RFC_STR:+ %-49s}${SHOW_EACH_C:+ %-0s}" "$hexcode" "$ossl_cipher" "$kx" "$enc" "$strength" "$tls_cipher")" + return 0 + elif [[ "$5" == "false" ]]; then line="$(printf -- " %-7s %-33s %-10s %-12s%-8s${ADD_RFC_STR:+ %-49s}${SHOW_EACH_C:+ %-0s}" "$hexcode" "$ossl_cipher" "$kx" "$enc" "$strength" "$tls_cipher")" pr_deemphasize "$line" return 0 fi + out "$(printf -- " %-7s %-33s " "$hexcode" "$ossl_cipher")" - #printf -- "%q" "$kx" | xxd | head -1 - # length correction for color escape codes (printf counts the escape color codes!!) - if printf -- "%q" "$kx" | egrep -aq '.;3.m|E\[1m' ; then # here's a color code which screws up the formatting with printf below - while [[ ${#kx} -lt 20 ]]; do - kx="$kx " - done - elif printf -- "%q" "$kx" | grep -aq 'E\[m' ; then # for color=1/0 we have the pr_off which screws up the formatting - while [[ ${#kx} -lt 13 ]]; do # so it'll be filled up ok - kx="$kx " - done + read what_dh bits <<< "$kx" + out "$what_dh" + if [[ -n "$bits" ]]; then + if [[ $what_dh == "DH" ]] || [[ $what_dh == "EDH" ]]; then + pr_dh_quality "$bits" " $bits" + elif [[ $what_dh == "ECDH" ]]; then + pr_ecdh_quality "$bits" " $bits" + fi fi - #echo "${#kx}" # should be always 20 / 13 - printf -- " %-7s %-33s %-10s %-12s%-8s${ADD_RFC_STR:+ %-49s}${SHOW_EACH_C:+ %-0s}" "$hexcode" "$ossl_cipher" "$kx" "$enc" "$strength" "$tls_cipher" + len=${#kx} + for (( i=len; i<10; i++ )); do + out " " + done + out "$(printf -- " %-12s%-8s${ADD_RFC_STR:+ %-49s}${SHOW_EACH_C:+ %-0s}" "$enc" "$strength" "$tls_cipher")" } test_just_one(){ @@ -3147,7 +3237,7 @@ create_client_simulation_tls_clienthello() { done if ! $sni_extension_found; then - out "$tls_handshake_ascii" + retstring "$tls_handshake_ascii" return 0 fi @@ -3166,7 +3256,7 @@ create_client_simulation_tls_clienthello() { tls_handshake_ascii_len_hex=$(printf "%02x\n" $tls_handshake_ascii_len) len2twobytes "$tls_handshake_ascii_len_hex" tls_handshake_ascii="${tls_content_type}${tls_version_reclayer}${LEN_STR:0:2}${LEN_STR:4:2}${tls_handshake_ascii}" - out "$tls_handshake_ascii" + retstring "$tls_handshake_ascii" return 0 } @@ -3235,7 +3325,7 @@ client_simulation_sockets() { fi done - debugme outln "reading server hello..." + debugme outln_term "reading server hello..." if [[ "$DEBUG" -ge 4 ]]; then hexdump -C $SOCK_REPLY_FILE | head -6 echo @@ -3255,7 +3345,7 @@ client_simulation_sockets() { # see https://secure.wand.net.nz/trac/libprotoident/wiki/SSL lines=$(count_lines "$(hexdump -C "$SOCK_REPLY_FILE" 2>$ERRFILE)") - debugme out " (returned $lines lines) " + debugme out_term " (returned $lines lines) " # determine the return value for higher level, so that they can tell what the result is if [[ $save -eq 1 ]] || [[ $lines -eq 1 ]]; then @@ -3263,7 +3353,7 @@ client_simulation_sockets() { else ret=0 fi - debugme outln + debugme outln_term close_socket TMPFILE=$SOCK_REPLY_FILE @@ -4051,7 +4141,7 @@ run_client_simulation() { fi outln - debugme outln + debugme outln_term for name in "${short[@]}"; do #FIXME: printf formatting would look better, especially if we want a wide option here out " ${names[i]} " @@ -4250,7 +4340,7 @@ run_protocols() { ;; 3) # everything else lines=$(count_lines "$(hexdump -C "$TEMPDIR/$NODEIP.sslv2_sockets.dd" 2>/dev/null)") - [[ "$DEBUG" -ge 2 ]] && out " ($lines lines) " + [[ "$DEBUG" -ge 2 ]] && out_term " ($lines lines) " if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) add_tls_offered "ssl2" @@ -4264,7 +4354,7 @@ run_protocols() { fi fi ;; esac - debugme outln + debugme outln_term else run_prototest_openssl "-ssl2" case $? in @@ -4354,7 +4444,7 @@ run_protocols() { 2) pr_svrty_medium "not offered" if [[ "$DETECTED_TLS_VERSION" == "0300" ]]; then - [[ $DEBUG -eq 1 ]] && out " -- downgraded" + [[ $DEBUG -eq 1 ]] && out_term " -- downgraded" outln fileout "tls1" "MEDIUM" "TLSv1.0 is not offered, and downgraded to SSL" elif [[ "$DETECTED_TLS_VERSION" == 03* ]]; then @@ -4403,7 +4493,7 @@ run_protocols() { 2) out "not offered" if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then - [[ $DEBUG -eq 1 ]] && out " -- downgraded" + [[ $DEBUG -eq 1 ]] && out_term " -- downgraded" outln fileout "tls1_1" "CRITICAL" "TLSv1.1 is not offered, and downgraded to a weaker protocol" elif [[ "$DETECTED_TLS_VERSION" == "0300" ]] && [[ "$latest_supported" == "0301" ]]; then @@ -4459,7 +4549,7 @@ run_protocols() { detected_version_string="TLSv1.$((0x$DETECTED_TLS_VERSION-0x0301))" fi if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then - [[ $DEBUG -eq 1 ]] && out " -- downgraded" + [[ $DEBUG -eq 1 ]] && out_term " -- downgraded" outln fileout "tls1_2" "MEDIUM" "TLSv1.2 is not offered and downgraded to a weaker protocol" elif [[ "$DETECTED_TLS_VERSION" == 03* ]] && [[ 0x$DETECTED_TLS_VERSION -lt 0x$latest_supported ]]; then @@ -4543,6 +4633,44 @@ run_std_cipherlists() { return 0 } +pr_dh_quality() { + local bits="$1" + local string="$2" + + if [[ "$bits" -le 600 ]]; then + pr_svrty_critical "$string" + elif [[ "$bits" -le 800 ]]; then + pr_svrty_high "$string" + elif [[ "$bits" -le 1280 ]]; then + pr_svrty_medium "$string" + elif [[ "$bits" -ge 2048 ]]; then + pr_done_good "$string" + else + out "$string" + fi +} + +pr_ecdh_quality() { + local bits="$1" + local string="$2" + + if [[ "$bits" -le 80 ]]; then # has that ever existed? + pr_svrty_critical "$string" + elif [[ "$bits" -le 108 ]]; then # has that ever existed? + pr_svrty_high "$string" + elif [[ "$bits" -le 163 ]]; then + pr_svrty_medium "$string" + elif [[ "$bits" -le 193 ]]; then # hmm, according to https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography it should ok + pr_svrty_low "$string" # but openssl removed it https://github.com/drwetter/testssl.sh/issues/299#issuecomment-220905416 + elif [[ "$bits" -le 224 ]]; then + out "$string" + elif [[ "$bits" -gt 224 ]]; then + pr_done_good "$string" + else + out "$string" + fi +} + # arg1: file with input for grepping the bit length for ECDH/DHE # arg2: whether to print warning "old fart" or not (empty: no) @@ -4567,57 +4695,46 @@ read_dhbits_from_file() { what_dh="ECDH" fi - if [[ -n "$curve" ]]; then - debugme echo ">$HAS_DH_BITS|$what_dh($curve)|$bits<" - else - debugme echo ">$HAS_DH_BITS|$what_dh|$bits<" + if [[ -z "$2" ]]; then + if [[ -n "$curve" ]]; then + debugme echo ">$HAS_DH_BITS|$what_dh($curve)|$bits<" + else + debugme echo ">$HAS_DH_BITS|$what_dh|$bits<" + fi fi [[ -n "$what_dh" ]] && HAS_DH_BITS=true # FIX 190 if [[ -z "$what_dh" ]] && ! "$HAS_DH_BITS"; then - if [[ -z "$2" ]]; then + if [[ "$2" == "string" ]]; then + retstring "$old_fart" + elif [[ -z "$2" ]]; then pr_warning "$old_fart" fi return 0 fi - [[ -n "$bits" ]] && [[ -z "$2" ]] && out ", " + if [[ "$2" == "quiet" ]]; then + retstring "$bits" + return 0 + fi + + [[ -z "$2" ]] && [[ -n "$bits" ]] && out ", " if [[ $what_dh == "DH" ]] || [[ $what_dh == "EDH" ]]; then - if [[ -z "$2" ]]; then - add="bit DH" - [[ -n "$curve" ]] && add+=" ($curve)" - fi - if [[ "$bits" -le 600 ]]; then - pr_svrty_critical "$bits $add" - elif [[ "$bits" -le 800 ]]; then - pr_svrty_high "$bits $add" - elif [[ "$bits" -le 1280 ]]; then - pr_svrty_medium "$bits $add" - elif [[ "$bits" -ge 2048 ]]; then - pr_done_good "$bits $add" + add="bit DH" + [[ -n "$curve" ]] && add+=" ($curve)" + if [[ "$2" == "string" ]]; then + retstring ", $bits $add" else - out "$bits $add" + pr_dh_quality "$bits" "$bits $add" fi # https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography, http://www.keylength.com/en/compare/ elif [[ $what_dh == "ECDH" ]]; then - if [[ -z "$2" ]]; then - add="bit ECDH" - [[ -n "$curve" ]] && add+=" ($curve)" - fi - if [[ "$bits" -le 80 ]]; then # has that ever existed? - pr_svrty_critical "$bits $add" - elif [[ "$bits" -le 108 ]]; then # has that ever existed? - pr_svrty_high "$bits $add" - elif [[ "$bits" -le 163 ]]; then - pr_svrty_medium "$bits $add" - elif [[ "$bits" -le 193 ]]; then # hmm, according to https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography it should ok - pr_svrty_low "$bits $add" # but openssl removed it https://github.com/drwetter/testssl.sh/issues/299#issuecomment-220905416 - elif [[ "$bits" -le 224 ]]; then - out "$bits $add" - elif [[ "$bits" -gt 224 ]]; then - pr_done_good "$bits $add" + add="bit ECDH" + [[ -n "$curve" ]] && add+=" ($curve)" + if [[ "$2" == "string" ]]; then + retstring ", $bits $add" else - out "$bits $add" + pr_ecdh_quality "$bits" "$bits $add" fi fi @@ -4666,7 +4783,7 @@ run_server_preference() { elif [[ -n "$STARTTLS_PROTOCOL" ]]; then # now it still could be that we hit this bug: https://github.com/drwetter/testssl.sh/issues/188 # workaround is to connect with a protocol - debugme out "(workaround #188) " + debugme out_term "(workaround #188) " determine_optimal_proto $STARTTLS_PROTOCOL $OPENSSL s_client $STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2 $ERRFILE >$TMPFILE if ! sclient_connect_successful $? $TMPFILE; then @@ -4705,7 +4822,7 @@ run_server_preference() { remark4default_cipher="" fileout "order" "OK" "Server sets a cipher order" fi - debugme out " $cipher1 | $cipher2" + debugme out_term " $cipher1 | $cipher2" outln pr_bold " Negotiated protocol " @@ -4757,23 +4874,23 @@ run_server_preference() { case "$default_cipher" in *NULL*|*EXP*) pr_svrty_critical "$default_cipher" - fileout "order_cipher" "CRITICAL" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE") $remark4default_cipher" + fileout "order_cipher" "CRITICAL" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE" "string") $remark4default_cipher" ;; *RC4*) pr_svrty_high "$default_cipher" - fileout "order_cipher" "HIGH" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE") $remark4default_cipher" + fileout "order_cipher" "HIGH" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE" "string") $remark4default_cipher" ;; *CBC*) pr_svrty_medium "$default_cipher" - fileout "order_cipher" "MEDIUM" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE") $remark4default_cipher" + fileout "order_cipher" "MEDIUM" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE" "string") $remark4default_cipher" ;; # FIXME BEAST: We miss some CBC ciphers here, need to work w/ a list *GCM*|*CHACHA20*) pr_done_best "$default_cipher" - fileout "order_cipher" "OK" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE") $remark4default_cipher" + fileout "order_cipher" "OK" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE" "string") $remark4default_cipher" ;; # best ones ECDHE*AES*) pr_svrty_low "$default_cipher" - fileout "order_cipher" "LOW" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE") (cbc) $remark4default_cipher" + fileout "order_cipher" "LOW" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE" "string") (cbc) $remark4default_cipher" ;; # it's CBC. --> lucky13 "") pr_warning "default cipher empty" ; @@ -4786,7 +4903,7 @@ run_server_preference() { ;; *) out "$default_cipher" - fileout "order_cipher" "INFO" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE") $remark4default_cipher" + fileout "order_cipher" "INFO" "Default cipher: $default_cipher$(read_dhbits_from_file "$TMPFILE" "string") $remark4default_cipher" ;; esac read_dhbits_from_file "$TMPFILE" @@ -4817,7 +4934,7 @@ run_server_preference() { fi fi done - [[ $DEBUG -ge 2 ]] && outln "Default cipher for ${proto[i]}: ${cipher[i]}" + [[ $DEBUG -ge 2 ]] && outln_term "Default cipher for ${proto[i]}: ${cipher[i]}" else proto[i]="" cipher[i]="" @@ -4835,7 +4952,7 @@ run_server_preference() { cipher1=$(awk '/Cipher *:/ { print $3 }' "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt") [[ $TLS_NR_CIPHERS -ne 0 ]] && cipher[i]="$(rfc2openssl "$cipher1")" [[ -z "${cipher[i]}" ]] && cipher[i]="$cipher1" - [[ $DEBUG -ge 2 ]] && outln "Default cipher for ${proto[i]}: ${cipher[i]}" + [[ $DEBUG -ge 2 ]] && outln_term "Default cipher for ${proto[i]}: ${cipher[i]}" else proto[i]="" cipher[i]="" @@ -4848,7 +4965,7 @@ run_server_preference() { proto[i]=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g') cipher[i]=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g') [[ ${cipher[i]} == "0000" ]] && cipher[i]="" # Hack! - [[ $DEBUG -ge 2 ]] && outln "Default cipher for ${proto[i]}: ${cipher[i]}" + [[ $DEBUG -ge 2 ]] && outln_term "Default cipher for ${proto[i]}: ${cipher[i]}" else proto[i]="" cipher[i]="" @@ -4868,7 +4985,7 @@ run_server_preference() { cipher[i]="" else cipher[i]=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g') - [[ $DEBUG -ge 2 ]] && outln "Default cipher for ${proto[i]}: ${cipher[i]}" + [[ $DEBUG -ge 2 ]] && outln_term "Default cipher for ${proto[i]}: ${cipher[i]}" fi fi else @@ -4879,13 +4996,13 @@ run_server_preference() { if [[ -n "${cipher[i]}" ]]; then # cipher not empty if [[ -z "${cipher[i-1]}" ]]; then # previous one empty #outln - printf -- " %-30s %s" "${cipher[i]}:" "${proto[i]}" # print out both + out "$(printf -- " %-30s %s" "${cipher[i]}:" "${proto[i]}")" # print out both else # previous NOT empty if [[ "${cipher[i-1]}" == "${cipher[i]}" ]]; then # and previous protocol same cipher out ", ${proto[i]}" # same cipher --> only print out protocol behind it else outln - printf -- " %-30s %s" "${cipher[i]}:" "${proto[i]}" # print out both + out "$(printf -- " %-30s %s" "${cipher[i]}:" "${proto[i]}")" # print out both fi fi fi @@ -4920,7 +5037,7 @@ check_tls12_pref() { nr_ciphers_found_r1+=1 "$FAST" && break else - debugme outln "A: $tested_cipher" + debugme outln_term "A: $tested_cipher" break fi done @@ -4935,10 +5052,10 @@ check_tls12_pref() { order+=" $cipher" batchremoved="$batchremoved:-$cipher" nr_ciphers_found_r1+=1 - debugme outln "B1: $batchremoved" + debugme outln_term "B1: $batchremoved" "$FAST" && break else - debugme outln "B2: $batchremoved" + debugme outln_term "B2: $batchremoved" break # nothing left with batchremoved ciphers, we need to put everything together fi @@ -4970,7 +5087,7 @@ check_tls12_pref() { return 1 fi fi - out "$order" + retstring "$order" tmpfile_handle $FUNCNAME.txt return 0 @@ -4993,7 +5110,7 @@ cipher_pref_check() { pr_bold " Cipher order" - outln " ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2"| while read p proto_hex proto; do + retstring " ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n"| while read p proto_hex proto; do order=""; ciphers_found_with_sockets=false if [[ $p == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; @@ -5151,7 +5268,7 @@ cipher_pref_check() { if [[ -n "$order" ]]; then outln - printf " %-10s" "$proto: " + out "$(printf " %-10s" "$proto: ")" out "$order" fileout "order_$p" "INFO" "Default cipher order for protocol $p: $order" fi @@ -5213,17 +5330,17 @@ verify_retcode_helper() { case $retcode in # codes from ./doc/apps/verify.pod | verify(1ssl) - 26) out "(unsupported certificate purpose)" ;; # X509_V_ERR_INVALID_PURPOSE - 24) out "(certificate unreadable)" ;; # X509_V_ERR_INVALID_CA - 23) out "(certificate revoked)" ;; # X509_V_ERR_CERT_REVOKED - 21) out "(chain incomplete, only 1 cert provided)" ;; # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE - 20) out "(chain incomplete)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY - 19) out "(self signed CA in chain)" ;; # X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN - 18) out "(self signed)" ;; # X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT - 10) out "(expired)" ;; # X509_V_ERR_CERT_HAS_EXPIRED - 9) out "(not yet valid)" ;; # X509_V_ERR_CERT_NOT_YET_VALID - 2) out "(issuer cert missing)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT - *) ret=1 ; pr_warning " (unknown, pls report) $1" ;; + 26) retstring "(unsupported certificate purpose)" ;; # X509_V_ERR_INVALID_PURPOSE + 24) retstring "(certificate unreadable)" ;; # X509_V_ERR_INVALID_CA + 23) retstring "(certificate revoked)" ;; # X509_V_ERR_CERT_REVOKED + 21) retstring "(chain incomplete, only 1 cert provided)" ;; # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE + 20) retstring "(chain incomplete)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY + 19) retstring "(self signed CA in chain)" ;; # X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN + 18) retstring "(self signed)" ;; # X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + 10) retstring "(expired)" ;; # X509_V_ERR_CERT_HAS_EXPIRED + 9) retstring "(not yet valid)" ;; # X509_V_ERR_CERT_NOT_YET_VALID + 2) retstring "(issuer cert missing)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT + *) ret=1 ; retstring " (unknown, pls report) $1" ;; esac return $ret } @@ -5255,7 +5372,7 @@ determine_trust() { addtl_warning="(Your openssl <= 1.0.2 might be too unreliable to determine trust)" fileout "${json_prefix}chain_of_trust_warn" "WARN" "$addtl_warning" fi - debugme outln + debugme outln_term # if you run testssl.sh from a different path /you can set either TESTSSL_INSTALL_DIR or CA_BUNDLES_PATH to find the CA BUNDLES if [[ -z $CA_BUNDLES_PATH ]]; then @@ -5282,18 +5399,18 @@ determine_trust() { if [[ ${verify_retcode[i]} -eq 0 ]]; then trust[i]=true some_ok=true - debugme pr_done_good "Ok " - debugme outln "${verify_retcode[i]}" + debugme pr_done_good_term "Ok " + debugme outln_term "${verify_retcode[i]}" else trust[i]=false all_ok=false - debugme pr_svrty_high "not trusted " - debugme outln "${verify_retcode[i]}" + debugme pr_svrty_high_term "not trusted " + debugme outln_term "${verify_retcode[i]}" fi i=$((i + 1)) done num_ca_bundles=$((i - 1)) - debugme out " " + debugme out_term " " if $all_ok; then # all stores ok pr_done_good "Ok "; pr_warning "$addtl_warning" @@ -5305,8 +5422,13 @@ determine_trust() { if ! $some_ok; then # all failed (we assume with the same issue), we're displaying the reason out " " - verify_retcode_helper "${verify_retcode[1]}" - fileout "${json_prefix}chain_of_trust" "CRITICAL" "All certificate trust checks failed: $(verify_retcode_helper "${verify_retcode[1]}"). $addtl_warning" + code="$(verify_retcode_helper "${verify_retcode[1]}")" + if [[ "$code" =~ "pls report" ]]; then + pr_warning "$code" + else + out "$code" + fi + fileout "${json_prefix}chain_of_trust" "CRITICAL" "All certificate trust checks failed: $code. $addtl_warning" else # is one ok and the others not ==> display the culprit store if $some_ok ; then @@ -5318,15 +5440,20 @@ determine_trust() { #code="$(verify_retcode_helper ${verify_retcode[i]})" #notok_was="${certificate_file[i]} $notok_was" pr_svrty_high " ${certificate_file[i]} " - verify_retcode_helper "${verify_retcode[i]}" - notok_was="${certificate_file[i]} $(verify_retcode_helper "${verify_retcode[i]}") $notok_was" + code="$(verify_retcode_helper "${verify_retcode[i]}")" + if [[ "$code" =~ "pls report" ]]; then + pr_warning "$code" + else + out "$code" + fi + notok_was="${certificate_file[i]} $code $notok_was" fi done #pr_svrty_high "$notok_was " #outln "$code" outln # lf + green ones - [[ "$DEBUG" -eq 0 ]] && out "$spaces" + [[ "$DEBUG" -eq 0 ]] && out_term "$spaces" pr_done_good "OK: $ok_was" fi fileout "${json_prefix}chain_of_trust" "CRITICAL" "Some certificate trust checks failed : OK : $ok_was NOT ok: $notok_was $addtl_warning" @@ -5360,7 +5487,7 @@ tls_time() { out "$difftime"; out " sec from localtime"; fileout "tls_time" "INFO" "Your TLS time is skewed from your localtime by $difftime seconds" fi - debugme out "$TLS_TIME" + debugme out_term "$TLS_TIME" outln else pr_warningln "SSLv3 through TLS 1.2 didn't return a timestamp" @@ -5973,9 +6100,9 @@ certificate_info() { cn_nosni="$(get_cn_from_cert "$HOSTCERT.nosni")" [[ -z "$cn_nosni" ]] && cn_nosni="no CN field in subject" fi - debugme out "\"$NODE\" | \"$cn\" | \"$cn_nosni\"" + debugme out_term "\"$NODE\" | \"$cn\" | \"$cn_nosni\"" else - debugme out "\"$NODE\" | \"$cn\"" + debugme out_term "\"$NODE\" | \"$cn\"" fi #FIXME: check for SSLv3/v2 and look whether it goes to a different CN (probably not polite) @@ -6030,7 +6157,8 @@ certificate_info() { pr_svrty_criticalln "self-signed (NOT ok)" fileout "${json_prefix}issuer" "CRITICAL" "Issuer: selfsigned" else - issuerfinding="$(pr_italic "$issuer_CN")" + issuerfinding="$issuer_CN" + pr_italic "$issuer_CN" if [[ -z "$issuer_O" ]] && [[ -n "$issuer_DC" ]]; then for san in $issuer_DC; do if [[ -z "$issuer_O" ]]; then @@ -6042,14 +6170,19 @@ certificate_info() { fi if [[ -n "$issuer_O" ]]; then issuerfinding+=" (" - issuerfinding+="$(pr_italic "$issuer_O")" + out " (" + issuerfinding+="$issuer_O" + pr_italic "$issuer_O" if [[ -n "$issuer_C" ]]; then issuerfinding+=" from " - issuerfinding+="$(pr_italic "$issuer_C")" + out " from " + issuerfinding+="$issuer_C" + pr_italic "$issuer_C" fi issuerfinding+=")" + out ")" fi - outln "$issuerfinding" + outln fileout "${json_prefix}issuer" "INFO" "Issuer: $issuerfinding" fi @@ -6749,22 +6882,14 @@ run_pfs() { fi if "$ecdhe_offered"; then for (( i=0; i < nr_curves; i++ )); do - if "${supported_curve[i]}"; then - curves_offered+="${curves_ossl[i]} " - if [[ "${bits[i]}" -le 163 ]]; then - curves_offered_text+="$(pr_svrty_medium "${curves_ossl[i]}") " - elif [[ "${bits[i]}" -le 193 ]]; then # hmm, according to https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography it should ok - curves_offered_text+="$(pr_svrty_low "${curves_ossl[i]}") " # but openssl removed it https://github.com/drwetter/testssl.sh/issues/299#issuecomment-220905416 - elif [[ "${bits[i]}" -le 224 ]]; then - curves_offered_text+="${curves_ossl[i]} " - else - curves_offered_text+="$(pr_done_good "${curves_ossl[i]}") " - fi - fi + "${supported_curve[i]}" && curves_offered+="${curves_ossl[i]} " done if [[ -n "$curves_offered" ]]; then "$WIDE" && outln - pr_bold " Elliptic curves offered: "; outln "$curves_offered_text" + pr_bold " Elliptic curves offered: " + for (( i=0; i < nr_curves; i++ )); do + "${supported_curve[i]}" && pr_ecdh_quality "${bits[i]}" "${curves_ossl[i]} " + done fileout "ecdhe_curves" "INFO" "Elliptic curves offered $curves_offered" fi fi @@ -7511,7 +7636,7 @@ get_dh_ephemeralkey() { key_bitstring="$($OPENSSL pkey -pubin -in $tmp_der_key_file -inform DER 2> $ERRFILE)" rm $tmp_der_key_file [[ -z "$key_bitstring" ]] && return 1 - out "$key_bitstring" + retstring "$key_bitstring" return 0 } @@ -7751,7 +7876,7 @@ parse_tls_serverhello() { if [[ $tls_hello_ascii_len-$i -lt 10 ]]; then if [[ "$process_full" == "all" ]]; then # The entire server response should have been retrieved. - debugme pr_warningln "Malformed message." + debugme pr_warningln_term "Malformed message." return 1 else # This could just be a result of the server's response being @@ -7767,29 +7892,29 @@ parse_tls_serverhello() { i=$i+4 if [[ $DEBUG -ge 2 ]]; then - echo " tls_protocol (reclyr): 0x$tls_protocol" - out " tls_content_type: 0x$tls_content_type" + echo " tls_protocol (reclyr): 0x$tls_protocol" + out_term " tls_content_type: 0x$tls_content_type" case $tls_content_type in - 15) outln " (alert)" ;; - 16) outln " (handshake)" ;; - 17) outln " (application data)" ;; - *) outln ;; + 15) outln_term " (alert)" ;; + 16) outln_term " (handshake)" ;; + 17) outln_term " (application data)" ;; + *) outln_term ;; esac echo " msg_len: $((msg_len/2))" - outln + outln_term fi if [[ $tls_content_type != "15" ]] && [[ $tls_content_type != "16" ]] && [[ $tls_content_type != "17" ]]; then - debugme pr_warningln "Content type other than alert, handshake, or application data detected." + debugme pr_warningln_term "Content type other than alert, handshake, or application data detected." return 1 elif [[ "${tls_protocol:0:2}" != "03" ]]; then - debugme pr_warningln "Protocol record_version.major is not 03." + debugme pr_warningln_term "Protocol record_version.major is not 03." return 1 fi DETECTED_TLS_VERSION=$tls_protocol if [[ $msg_len -gt $tls_hello_ascii_len-$i ]]; then if [[ "$process_full" == "all" ]]; then - debugme pr_warningln "Malformed message." + debugme pr_warningln_term "Malformed message." return 1 else # This could just be a result of the server's response being @@ -7809,7 +7934,7 @@ parse_tls_serverhello() { # Now check the alert messages. tls_alert_ascii_len=${#tls_alert_ascii} if [[ "$process_full" == "all" ]] && [[ $tls_alert_ascii_len%4 -ne 0 ]]; then - debugme pr_warningln "Malformed message." + debugme pr_warningln_term "Malformed message." return 1 fi if [[ $tls_alert_ascii_len -gt 0 ]]; then @@ -7820,7 +7945,7 @@ parse_tls_serverhello() { tls_err_descr=${tls_alert_ascii:j:2} # 112/0x70: Unrecognized name, 111/0x6F: certificate_unobtainable, # 113/0x71: bad_certificate_status_response, #114/0x72: bad_certificate_hash_value - debugme out " tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})" + debugme out_term " tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})" case $tls_err_descr in 00) tls_alert_descrip="close notify" ;; 01) tls_alert_descrip="end of early data" ;; @@ -7866,17 +7991,17 @@ parse_tls_serverhello() { echo "alert $tls_alert_descrip" >> $TMPFILE echo "===============================================================================" >> $TMPFILE if [[ $DEBUG -ge 2 ]]; then - outln " ($tls_alert_descrip)" - out " tls_err_level: ${tls_err_level}" + outln_term " ($tls_alert_descrip)" + out_term " tls_err_level: ${tls_err_level}" case $tls_err_level in - 01) outln " (warning)" ;; - 02) outln " (fatal)" ;; - *) outln ;; + 01) outln_term " (warning)" ;; + 02) outln_term " (fatal)" ;; + *) outln_term ;; esac - outln + outln_term fi if [[ "$tls_err_level" != "01" ]] && [[ "$tls_err_level" != "02" ]]; then - debugme pr_warningln "Unexpected AlertLevel (0x$tls_err_level)." + debugme pr_warningln_term "Unexpected AlertLevel (0x$tls_err_level)." return 1 elif [[ "$tls_err_level" == "02" ]]; then # Fatal alert @@ -7896,7 +8021,7 @@ parse_tls_serverhello() { if [[ $tls_handshake_ascii_len-$i -lt 8 ]]; then if [[ "$process_full" == "all" ]]; then # The entire server response should have been retrieved. - debugme pr_warningln "Malformed message." + debugme pr_warningln_term "Malformed message." return 1 else # This could just be a result of the server's response being @@ -7910,34 +8035,34 @@ parse_tls_serverhello() { i=$i+6 if [[ $DEBUG -ge 2 ]]; then - out " handshake type: 0x${tls_msg_type}" + out_term " handshake type: 0x${tls_msg_type}" case $tls_msg_type in - 00) outln " (hello_request)" ;; - 01) outln " (client_hello)" ;; - 02) outln " (server_hello)" ;; - 03) outln " (hello_verify_request)" ;; - 04) outln " (NewSessionTicket)" ;; - 06) outln " (hello_retry_request)" ;; - 08) outln " (encrypted_extensions)" ;; - 0B) outln " (certificate)" ;; - 0C) outln " (server_key_exchange)" ;; - 0D) outln " (certificate_request)" ;; - 0E) outln " (server_hello_done)" ;; - 0F) outln " (certificate_verify)" ;; - 10) outln " (client_key_exchange)" ;; - 14) outln " (finished)" ;; - 15) outln " (certificate_url)" ;; - 16) outln " (certificate_status)" ;; - 17) outln " (supplemental_data)" ;; - 18) outln " (key_update)" ;; - *) outln ;; + 00) outln_term " (hello_request)" ;; + 01) outln_term " (client_hello)" ;; + 02) outln_term " (server_hello)" ;; + 03) outln_term " (hello_verify_request)" ;; + 04) outln_term " (NewSessionTicket)" ;; + 06) outln_term " (hello_retry_request)" ;; + 08) outln_term " (encrypted_extensions)" ;; + 0B) outln_term " (certificate)" ;; + 0C) outln_term " (server_key_exchange)" ;; + 0D) outln_term " (certificate_request)" ;; + 0E) outln_term " (server_hello_done)" ;; + 0F) outln_term " (certificate_verify)" ;; + 10) outln_term " (client_key_exchange)" ;; + 14) outln_term " (finished)" ;; + 15) outln_term " (certificate_url)" ;; + 16) outln_term " (certificate_status)" ;; + 17) outln_term " (supplemental_data)" ;; + 18) outln_term " (key_update)" ;; + *) outln_term ;; esac echo " msg_len: $((msg_len/2))" - outln + outln_term fi if [[ $msg_len -gt $tls_handshake_ascii_len-$i ]]; then if [[ "$process_full" == "all" ]]; then - debugme pr_warningln "Malformed message." + debugme pr_warningln_term "Malformed message." return 1 else # This could just be a result of the server's response being @@ -7949,28 +8074,28 @@ parse_tls_serverhello() { if [[ "$tls_msg_type" == "02" ]]; then if [[ -n "$tls_serverhello_ascii" ]]; then - debugme pr_warningln "Response contained more than one ServerHello handshake message." + debugme pr_warningln_term "Response contained more than one ServerHello handshake message." return 1 fi tls_serverhello_ascii="${tls_handshake_ascii:i:msg_len}" tls_serverhello_ascii_len=$msg_len elif [[ "$process_full" == "all" ]] && [[ "$tls_msg_type" == "0B" ]]; then if [[ -n "$tls_certificate_ascii" ]]; then - debugme pr_warningln "Response contained more than one Certificate handshake message." + debugme pr_warningln_term "Response contained more than one Certificate handshake message." return 1 fi tls_certificate_ascii="${tls_handshake_ascii:i:msg_len}" tls_certificate_ascii_len=$msg_len elif ( [[ "$process_full" == "all" ]] || [[ "$process_full" == "ephemeralkey" ]] ) && [[ "$tls_msg_type" == "0C" ]]; then if [[ -n "$tls_serverkeyexchange_ascii" ]]; then - debugme pr_warningln "Response contained more than one ServerKeyExchange handshake message." + debugme pr_warningln_term "Response contained more than one ServerKeyExchange handshake message." return 1 fi tls_serverkeyexchange_ascii="${tls_handshake_ascii:i:msg_len}" tls_serverkeyexchange_ascii_len=$msg_len elif [[ "$process_full" == "all" ]] && [[ "$tls_msg_type" == "16" ]]; then if [[ -n "$tls_certificate_status_ascii" ]]; then - debugme pr_warningln "Response contained more than one certificate_status handshake message." + debugme pr_warningln_term "Response contained more than one certificate_status handshake message." return 1 fi tls_certificate_status_ascii="${tls_handshake_ascii:i:msg_len}" @@ -7987,7 +8112,7 @@ parse_tls_serverhello() { return 1 elif [[ "${tls_handshake_ascii:0:2}" != "02" ]]; then # the ServerHello MUST be the first handshake message - debugme pr_warningln "The first handshake protocol message is not a ServerHello." + debugme pr_warningln_term "The first handshake protocol message is not a ServerHello." return 1 fi @@ -8001,7 +8126,7 @@ parse_tls_serverhello() { # byte 38+39+sid-len: extension length tls_protocol2="${tls_serverhello_ascii:0:4}" if [[ "${tls_protocol2:0:2}" != "03" ]]; then - debugme pr_warningln "server_version.major in ServerHello is not 03." + debugme pr_warningln_term "server_version.major in ServerHello is not 03." return 1 fi DETECTED_TLS_VERSION="$tls_protocol2" @@ -8038,7 +8163,7 @@ parse_tls_serverhello() { fi tls_extensions_len=$(hex2dec "${tls_serverhello_ascii:extns_offset:4}")*2 if [[ $tls_extensions_len -ne $tls_serverhello_ascii_len-$extns_offset-4 ]]; then - debugme pr_warningln "Malformed message." + debugme pr_warningln_term "Malformed message." return 1 fi for (( i=0; i$ERRFILE)") - debugme out " (returned $lines lines) " + debugme out_term " (returned $lines lines) " # determine the return value for higher level, so that they can tell what the result is if [[ $save -eq 1 ]] || [[ $lines -eq 1 ]]; then @@ -8928,7 +9053,7 @@ tls_sockets() { ret=2 # protocol NOT available, server downgraded to $DETECTED_TLS_VERSION fi fi - debugme outln + debugme outln_term else debugme echo "stuck on sending: $ret" fi @@ -9035,16 +9160,16 @@ run_heartbleed(){ for (( n=1; n <= hb_rounds; n++)); do fd_socket 5 || return 6 - debugme out "\nsending client hello (TLS version $tls_hexcode)" - debugme outln " ($n of $hb_rounds)" + debugme out_term "\nsending client hello (TLS version $tls_hexcode)" + debugme outln_term " ($n of $hb_rounds)" socksend "$client_hello" 1 - debugme outln "\nreading server hello" + debugme outln_term "\nreading server hello" sockread_serverhello 32768 if [[ $DEBUG -ge 4 ]]; then hexdump -C "$SOCK_REPLY_FILE" | head -20 - outln "[...]" - outln "\nsending payload with TLS version $tls_hexcode:" + outln_term "[...]" + outln_term "\nsending payload with TLS version $tls_hexcode:" fi rm "$SOCK_REPLY_FILE" @@ -9054,10 +9179,10 @@ run_heartbleed(){ lines_returned=$(hexdump -ve '16/1 "%02x " " \n"' "$SOCK_REPLY_FILE" | wc -l | sed 's/ //g') if [[ $DEBUG -ge 3 ]]; then - outln "\nheartbleed reply: " + outln_term "\nheartbleed reply: " hexdump -C "$SOCK_REPLY_FILE" | head -20 - [[ $lines_returned -gt 20 ]] && outln "[...]" - outln + [[ $lines_returned -gt 20 ]] && outln_term "[...]" + outln_term fi if [[ $lines_returned -gt 1 ]]; then @@ -9066,7 +9191,7 @@ run_heartbleed(){ saved_sockreply[n]="$(hexdump -ve '1/1 "%.2x"' "$SOCK_REPLY_FILE")" [[ $n -eq 1 ]] && grep -q '500 OOPS' "$SOCK_REPLY_FILE" && found_500_oops=true rm "$SOCK_REPLY_FILE" - #debugme out "${saved_sockreply[n]}" + #debugme out_term "${saved_sockreply[n]}" #TMPFILE="${saved_sockreply[n]}" close_socket #tmpfile_handle "$FUNCNAME,$n.txt" @@ -9093,12 +9218,12 @@ run_heartbleed(){ if [[ "${saved_sockreply[1]}" == "${saved_sockreply[2]}" ]] && [[ "${saved_sockreply[2]}" == "${saved_sockreply[3]}" ]] \ && "$found_500_oops"; then pr_done_best "not vulnerable (OK)$append" - [[ $DEBUG -ge 1 ]] && out ", successful weeded out vsftpd false positive" + [[ $DEBUG -ge 1 ]] && out_term ", successful weeded out vsftpd false positive" fileout "heartbleed" "OK" "Heartbleed: not vulnerable $append" "$cve" "$cwe" else out "likely " pr_svrty_critical "VULNERABLE (NOT ok)" - [[ $DEBUG -ge 1 ]] && out " use debug >=2 to confirm" + [[ $DEBUG -ge 1 ]] && out_term " use debug >=2 to confirm" fileout "heartbleed" "CRITICAL" "Heartbleed: likely VULNERABLE $append" "$cve" "$cwe" "$hint" fi else @@ -9181,15 +9306,15 @@ run_ccs_injection(){ fd_socket 5 || return 6 # we now make a standard handshake ... - debugme out "\nsending client hello, " + debugme out_term "\nsending client hello, " socksend "$client_hello" 1 - debugme outln "\nreading server hello" + debugme outln_term "\nreading server hello" sockread_serverhello 32768 if [[ $DEBUG -ge 4 ]]; then hexdump -C "$SOCK_REPLY_FILE" | head -20 - outln "[...]" - outln "\npayload #1 with TLS version $tls_hexcode:" + outln_term "[...]" + outln_term "\npayload #1 with TLS version $tls_hexcode:" fi rm "$SOCK_REPLY_FILE" @@ -9197,13 +9322,13 @@ run_ccs_injection(){ socksend "$ccs_message" 1 || ok_ids sockread_serverhello 2048 $CCS_MAX_WAITSOCK if [[ $DEBUG -ge 3 ]]; then - outln "\n1st reply: " + outln_term "\n1st reply: " hexdump -C "$SOCK_REPLY_FILE" | head -20 # ok: 15 | 0301 | 02 | 02 | 0a # ALERT | TLS 1.0 | Length=2 | Unexpected Message (0a) # or just timed out - outln - outln "payload #2 with TLS version $tls_hexcode:" + outln_term + outln_term "payload #2 with TLS version $tls_hexcode:" fi rm "$SOCK_REPLY_FILE" @@ -9212,12 +9337,12 @@ run_ccs_injection(){ retval=$? if [[ $DEBUG -ge 3 ]]; then - outln "\n2nd reply: " + outln_term "\n2nd reply: " printf -- "$(hexdump -C "$SOCK_REPLY_FILE")" # not ok: 15 | 0301 | 02 | 02 | 15 # ALERT | TLS 1.0 | Length=2 | Decryption failed (21) # ok: 0a or nothing: ==> RST - outln + outln_term fi sockreply=$(cat "$SOCK_REPLY_FILE" 2>/dev/null) rm "$SOCK_REPLY_FILE" @@ -9452,7 +9577,7 @@ run_crime() { # fi # fi # fi -# [[ $DEBUG -eq 2 ]] outln "$STR" +# [[ $DEBUG -eq 2 ]] outln_term "$STR" tmpfile_handle $FUNCNAME.txt return $ret } @@ -9799,9 +9924,9 @@ run_freak() { for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$hexc" == "${TLS_CIPHER_HEXCODE[i]}" ]] && break done - [[ $i -eq $TLS_NR_CIPHERS ]] && out "$hexc " || out "${TLS_CIPHER_OSSL_NAME[i]} " + [[ $i -eq $TLS_NR_CIPHERS ]] && out_term "$hexc " || out_term "${TLS_CIPHER_OSSL_NAME[i]} " done - outln + outln_term else echo $(actually_supported_ciphers $exportrsa_cipher_list) fi @@ -9875,9 +10000,9 @@ run_logjam() { for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$hexc" == "${TLS_CIPHER_HEXCODE[i]}" ]] && break done - [[ $i -eq $TLS_NR_CIPHERS ]] && out "$hexc " || out "${TLS_CIPHER_OSSL_NAME[i]} " + [[ $i -eq $TLS_NR_CIPHERS ]] && out_term "$hexc " || out_term "${TLS_CIPHER_OSSL_NAME[i]} " done - outln + outln_term else echo $(actually_supported_ciphers $exportdh_cipher_list) fi @@ -9918,7 +10043,7 @@ run_logjam() { dh_p="$(strip_spaces "$(colon_to_spaces "$(newline_to_spaces "$dh_p")")")" [[ "${dh_p:0:2}" == "00" ]] && dh_p="${dh_p:2}" len_dh_p="$((4*${#dh_p}))" - debugme outln "len(dh_p): $len_dh_p | dh_p: $dh_p" + debugme outln_term "len(dh_p): $len_dh_p | dh_p: $dh_p" echo "$dh_p" > $TEMPDIR/dh_p.txt if [[ ! -s "$common_primes_file" ]]; then local_problem_ln "couldn't read common primes file $common_primes_file" @@ -10044,7 +10169,7 @@ run_drown() { ;; 3) # vulnerable lines=$(count_lines "$(hexdump -C "$TEMPDIR/$NODEIP.sslv2_sockets.dd" 2>/dev/null)") - debugme out " ($lines lines) " + debugme out_term " ($lines lines) " if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) if [[ 0 -eq "$nr_ciphers_detected" ]]; then @@ -10992,6 +11117,7 @@ file output options (can also be preset via environment variables): --jsonfile-pretty additional pretty structured output as JSON to the specified file --csv additional output of findings to CSV file in cwd --csvfile additional output as CSV to the specified file + --htmlfile additional output as HTML to the specifed file --hints additional hints to findings --severity severities with lower level will be filtered for CSV+JSON, possible values --append if or exists rather append then overwrite @@ -11127,7 +11253,7 @@ EOF mybanner() { local idtag - local bb + local bb1 bb2 local openssl_location="$(which $OPENSSL)" local cwd="" @@ -11136,12 +11262,14 @@ mybanner() { [[ -z "$GIT_REL" ]] && \ idtag="$CVS_REL" || \ idtag="$GIT_REL -- $CVS_REL_SHORT" - [[ "$COLOR" -ne 0 ]] && idtag="\033[1;30m$idtag\033[m\033[1m" - bb=$(cat </dev/null)\" [~$OPENSSL_NR_CIPHERS ciphers]" out " on $HNAME:" @@ -11167,16 +11299,16 @@ EOF else OPENSSL_LOCATION="$openssl_location" fi - echo "$OPENSSL_LOCATION" + outln "$OPENSSL_LOCATION" outln " (built: \"$OSSL_BUILD_DATE\", platform: \"$OSSL_VER_PLATFORM\")\n" } cleanup () { if [[ "$DEBUG" -ge 1 ]]; then - outln - pr_underline "DEBUG (level $DEBUG): see files in $TEMPDIR" - outln + outln_term + pr_underline_term "DEBUG (level $DEBUG): see files in $TEMPDIR" + outln_term else [[ -d "$TEMPDIR" ]] && rm -rf "$TEMPDIR"; fi @@ -11850,7 +11982,7 @@ display_rdns_etc() { outln " A record via $CORRECT_SPACES supplied IP \"$CMDLINE_IP\"" fi if [[ -n "$rDNS" ]]; then - printf " %-23s %s" "rDNS ($nodeip):" "$rDNS" + out "$(printf " %-23s %s" "rDNS ($nodeip):" "$rDNS")" fi } @@ -11985,6 +12117,7 @@ initialize_globals() { do_json=false do_pretty_json=false do_csv=false + do_html=false do_pfs=false do_protocols=false do_rc4=false @@ -12308,7 +12441,7 @@ parse_cmd_line() { [[ $? -eq 0 ]] && shift case $DEBUG in [0-6]) ;; - *) pr_magentaln "\nunrecognized debug value \"$1\", must be between 0..6" 1>&2 + *) pr_magentaln_term "\nunrecognized debug value \"$1\", must be between 0..6" 1>&2 help 1 esac ;; @@ -12367,6 +12500,16 @@ parse_cmd_line() { [[ $? -eq 0 ]] && shift do_csv=true ;; + --htmlfile) + HTMLFILE=$(parse_opt_equal_sign "$1" "$2") + [[ $? -eq 0 ]] && shift + if [[ -d "$HTMLFILE" ]]; then + pr_warningln_term "$HTMLFILE exists and is a directory" + exit -6 + fi + do_html=true + html_header + ;; --append) APPEND=true ;; @@ -12609,5 +12752,6 @@ else fi fi fi +html_footer exit $? From 2af8198f27cb4a833673901998d83869c0c3068b Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 7 Feb 2017 17:06:27 -0500 Subject: [PATCH 02/12] Change HTML colors Rather than use the colors created by `aha` use colors that more closely match the colors that appear in the terminal. --- testssl.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/testssl.sh b/testssl.sh index 75987e0..f817875 100755 --- a/testssl.sh +++ b/testssl.sh @@ -589,7 +589,7 @@ retstring(){ # color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html pr_liteblue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;32m$1" || out_term "\033[0;34m$1" ) || out_term "$1"; pr_off; } # not yet used -pr_liteblue() { pr_liteblue_term "$1"; out_html "$1"; } +pr_liteblue() { pr_liteblue_term "$1"; out_html "$1"; } pr_liteblueln_term() { pr_liteblue_term "$1"; outln_term; } pr_liteblueln() { pr_liteblue "$1"; outln; } pr_blue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;32m$1" || out_term "\033[1;34m$1" ) || out_term "$1"; pr_off; } # used for head lines of single tests @@ -598,27 +598,27 @@ pr_blueln_term() { pr_blue_term "$1"; outln_term; } pr_blueln() { pr_blue "$1"; outln; } pr_warning_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;35m$1" || pr_underline_term "$1"; pr_off; } # some local problem: one test cannot be done -pr_warning() { pr_warning_term "$1"; out_html "$1"; } +pr_warning() { pr_warning_term "$1"; out_html "$1"; } pr_warningln_term() { pr_warning_term "$1"; outln_term; } # litemagenta pr_warningln() { pr_warning "$1"; outln; } pr_magenta_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;35m$1" || pr_underline_term "$1"; pr_off; } # fatal error: quitting because of this! -pr_magenta() { pr_magenta_term "$1"; out_html "$1"; } +pr_magenta() { pr_magenta_term "$1"; out_html "$1"; } pr_magentaln_term() { pr_magenta_term "$1"; outln_term; } pr_magentaln() { pr_magenta "$1"; outln; } pr_litecyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;36m$1" || out_term "$1"; pr_off; } # not yet used -pr_litecyan() { pr_litecyan_term "$1"; out_html "$1"; } +pr_litecyan() { pr_litecyan_term "$1"; out_html "$1"; } pr_litecyanln_term() { pr_litecyan_term "$1"; outln_term; } pr_litecyanln() { pr_litecyan "$1"; outln; } pr_cyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;36m$1" || out_term "$1"; pr_off; } # additional hint -pr_cyan() { pr_cyan_term "$1"; out_html "$1"; } +pr_cyan() { pr_cyan_term "$1"; out_html "$1"; } pr_cyanln_term() { pr_cyan_term "$1"; outln_term; } pr_cyanln() { pr_cyan "$1"; outln; } pr_litegreyln_term() { pr_litegrey_term "$1"; outln_term; } # not really usable on a black background, see .. pr_litegreyln() { pr_litegrey "$1"; outln; } pr_litegrey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 -pr_litegrey() { pr_litegrey_term "$1"; out_html "$1"; } +pr_litegrey() { pr_litegrey_term "$1"; out_html "$1"; } pr_grey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } pr_grey() { pr_grey_term "$1"; out_html "$1"; } pr_greyln_term() { pr_grey_term "$1"; outln_term; } @@ -634,11 +634,11 @@ pr_done_bestln_term() { pr_done_best_term "$1"; outln_term; } pr_done_bestln() { pr_done_best "$1"; outln; } pr_svrty_low_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;33m$1" || out_term "$1"; pr_off; } # yellow brown | academic or minor problem -pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$1"; } +pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$1"; } pr_svrty_lowln_term() { pr_svrty_low_term "$1"; outln_term; } pr_svrty_lowln() { pr_svrty_low "$1"; outln; } pr_svrty_medium_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;33m$1" || out_term "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this -pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$1"; } +pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$1"; } pr_svrty_mediumln_term() { pr_svrty_medium_term "$1"; outln_term; } pr_svrty_mediumln() { pr_svrty_medium "$1"; outln; } @@ -652,7 +652,7 @@ pr_svrty_criticalln_term() { pr_svrty_critical_term "$1"; outln_term; } pr_svrty_criticalln(){ pr_svrty_critical "$1"; outln; } pr_deemphasize_term() { out_term "$1"; } # hook for a weakened screen output, see #600 -pr_deemphasize() { pr_deemphasize_term "$1"; out_html "$1"; } +pr_deemphasize() { pr_deemphasize_term "$1"; out_html "$1"; } pr_deemphasizeln_term() { pr_deemphasize_term "$1"; outln_term; } pr_deemphasizeln() { pr_deemphasize "$1"; outln; } @@ -673,9 +673,9 @@ pr_strikethruln() { pr_strikethru "$1" ; outln; } pr_underline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[4m$1" || out_term "$1"; pr_off; } pr_underline() { pr_underline_term "$1"; out_html "$1"; } pr_reverse_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m$1" || out_term "$1"; pr_off; } -pr_reverse() { pr_reverse_term "$1"; out_html "$1"; } +pr_reverse() { pr_reverse_term "$1"; out_html "$1"; } pr_reverse_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m\033[1m$1" || out_term "$1"; pr_off; } -pr_reverse_bold() { pr_reverse_bold_term "$1"; out_html "$1"; } +pr_reverse_bold() { pr_reverse_bold_term "$1"; out_html "$1"; } #pr_headline() { pr_blue "$1"; } #http://misc.flogisoft.com/bash/tip_colors_and_formatting From 1c5ef78913659e90fe911f804bc2966ccc0e5fe2 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 8 Feb 2017 15:16:51 -0500 Subject: [PATCH 03/12] Another update to HTML colors For the most part I used the RGB values for xterm from https://en.wikipedia.org/wiki/ANSI_escape_code#Colors for the HTML colors, but with a few exceptions. For example, I did not use "yellow" for `pr_svrty_low()`, since that color is very difficult to read. I also used a different color for `pr_svrty_medium()` so that `pr_svrty_medium()` would appear more red than `pr_svrty_low()`. These color choices could use more adjustment. --- testssl.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/testssl.sh b/testssl.sh index da793b1..d25730b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -590,16 +590,16 @@ retstring(){ # color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html pr_liteblue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;32m$1" || out_term "\033[0;34m$1" ) || out_term "$1"; pr_off; } # not yet used -pr_liteblue() { pr_liteblue_term "$1"; out_html "$1"; } +pr_liteblue() { pr_liteblue_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } pr_liteblueln_term() { pr_liteblue_term "$1"; outln_term; } pr_liteblueln() { pr_liteblue "$1"; outln; } pr_blue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;32m$1" || out_term "\033[1;34m$1" ) || out_term "$1"; pr_off; } # used for head lines of single tests -pr_blue() { pr_blue_term "$1"; out_html "$1"; } +pr_blue() { pr_blue_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } pr_blueln_term() { pr_blue_term "$1"; outln_term; } pr_blueln() { pr_blue "$1"; outln; } pr_warning_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;35m$1" || pr_underline_term "$1"; pr_off; } # some local problem: one test cannot be done -pr_warning() { pr_warning_term "$1"; out_html "$1"; } +pr_warning() { pr_warning_term "$1"; out_html "$1"; } pr_warningln_term() { pr_warning_term "$1"; outln_term; } # litemagenta pr_warningln() { pr_warning "$1"; outln; } pr_magenta_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;35m$1" || pr_underline_term "$1"; pr_off; } # fatal error: quitting because of this! @@ -608,7 +608,7 @@ pr_magentaln_term() { pr_magenta_term "$1"; outln_term; } pr_magentaln() { pr_magenta "$1"; outln; } pr_litecyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;36m$1" || out_term "$1"; pr_off; } # not yet used -pr_litecyan() { pr_litecyan_term "$1"; out_html "$1"; } +pr_litecyan() { pr_litecyan_term "$1"; out_html "$1"; } pr_litecyanln_term() { pr_litecyan_term "$1"; outln_term; } pr_litecyanln() { pr_litecyan "$1"; outln; } pr_cyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;36m$1" || out_term "$1"; pr_off; } # additional hint @@ -621,30 +621,30 @@ pr_litegreyln() { pr_litegrey "$1"; outln; } pr_litegrey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 pr_litegrey() { pr_litegrey_term "$1"; out_html "$1"; } pr_grey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } -pr_grey() { pr_grey_term "$1"; out_html "$1"; } +pr_grey() { pr_grey_term "$1"; out_html "$1"; } pr_greyln_term() { pr_grey_term "$1"; outln_term; } pr_greyln() { pr_grey "$1"; outln; } pr_done_good_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;34m$1" || out_term "\033[0;32m$1" ) || out_term "$1"; pr_off; } # litegreen (liteblue), This is good -pr_done_good() { pr_done_good_term "$1"; out_html "$1"; } +pr_done_good() { pr_done_good_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } pr_done_goodln_term() { pr_done_good_term "$1"; outln_term; } pr_done_goodln() { pr_done_good "$1"; outln; } pr_done_best_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;34m$1" || out_term "\033[1;32m$1" ) || out_term "$1"; pr_off; } # green (blue), This is the best -pr_done_best() { pr_done_best_term "$1"; out_html "$1"; } +pr_done_best() { pr_done_best_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } pr_done_bestln_term() { pr_done_best_term "$1"; outln_term; } pr_done_bestln() { pr_done_best "$1"; outln; } pr_svrty_low_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;33m$1" || out_term "$1"; pr_off; } # yellow brown | academic or minor problem -pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$1"; } +pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$1"; } pr_svrty_lowln_term() { pr_svrty_low_term "$1"; outln_term; } pr_svrty_lowln() { pr_svrty_low "$1"; outln; } pr_svrty_medium_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;33m$1" || out_term "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this -pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$1"; } +pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$1"; } pr_svrty_mediumln_term() { pr_svrty_medium_term "$1"; outln_term; } pr_svrty_mediumln() { pr_svrty_medium "$1"; outln; } pr_svrty_high_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;31m$1" || pr_bold_term "$1"; pr_off; } # litered -pr_svrty_high() { pr_svrty_high_term "$1"; out_html "$1"; } +pr_svrty_high() { pr_svrty_high_term "$1"; out_html "$1"; } pr_svrty_highln_term() { pr_svrty_high_term "$1"; outln_term; } pr_svrty_highln() { pr_svrty_high "$1"; outln; } pr_svrty_critical_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;31m$1" || pr_bold_term "$1"; pr_off; } # red From a50488c44f11e222cadc291a9817284391ac8cd7 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 9 Feb 2017 17:03:21 -0500 Subject: [PATCH 04/12] Handle --file option Introduced "trick" so that if the `--file` option is used, `html_header()` will only be called once before anything is printed and `html_footer()` will only be called once after all printing is complete. With this, `html_header()` now delete the output file if it exists. Also introduced the `html_reserved()`, which is called for all text to be sent to `out_html()`. `html_reserved()` converts any HTML reserved characters (", ', &, <, >) to their corresponding entity names (", ', &, <, >). --- testssl.sh | 94 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/testssl.sh b/testssl.sh index d25730b..9c93718 100755 --- a/testssl.sh +++ b/testssl.sh @@ -158,6 +158,7 @@ LOGFILE=${LOGFILE:-""} # logfile if used JSONFILE=${JSONFILE:-""} # jsonfile if used CSVFILE=${CSVFILE:-""} # csvfile if used HTMLFILE=${CSVFILE:-""} # HTML if used +HTMLHEADER=true # include HTML headers and footers in HTML file, if one is being created APPEND=${APPEND:-false} # append to csv/json file instead of overwriting it GIVE_HINTS=false # give an addtional info to findings HAS_IPv6=${HAS_IPv6:-false} # if you have OpenSSL with IPv6 support AND IPv6 networking set it to yes @@ -586,106 +587,111 @@ outln_term() { out_term "$1\n"; } retstring(){ printf -- "%b" "${1//%/%%}" } + +# For HTML output, replace any HTML reserved characters with the entity name +html_reserved(){ + echo "$1" | sed -e 's/\&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\"/g" +} #TODO: Still no shell injection safe but if just run it from the cmd line: that's fine # color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html pr_liteblue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;32m$1" || out_term "\033[0;34m$1" ) || out_term "$1"; pr_off; } # not yet used -pr_liteblue() { pr_liteblue_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } +pr_liteblue() { pr_liteblue_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_liteblueln_term() { pr_liteblue_term "$1"; outln_term; } pr_liteblueln() { pr_liteblue "$1"; outln; } pr_blue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;32m$1" || out_term "\033[1;34m$1" ) || out_term "$1"; pr_off; } # used for head lines of single tests -pr_blue() { pr_blue_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } +pr_blue() { pr_blue_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_blueln_term() { pr_blue_term "$1"; outln_term; } pr_blueln() { pr_blue "$1"; outln; } pr_warning_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;35m$1" || pr_underline_term "$1"; pr_off; } # some local problem: one test cannot be done -pr_warning() { pr_warning_term "$1"; out_html "$1"; } +pr_warning() { pr_warning_term "$1"; out_html "$(html_reserved "$1")"; } pr_warningln_term() { pr_warning_term "$1"; outln_term; } # litemagenta pr_warningln() { pr_warning "$1"; outln; } pr_magenta_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;35m$1" || pr_underline_term "$1"; pr_off; } # fatal error: quitting because of this! -pr_magenta() { pr_magenta_term "$1"; out_html "$1"; } +pr_magenta() { pr_magenta_term "$1"; out_html "$(html_reserved "$1")"; } pr_magentaln_term() { pr_magenta_term "$1"; outln_term; } pr_magentaln() { pr_magenta "$1"; outln; } pr_litecyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;36m$1" || out_term "$1"; pr_off; } # not yet used -pr_litecyan() { pr_litecyan_term "$1"; out_html "$1"; } +pr_litecyan() { pr_litecyan_term "$1"; out_html "$(html_reserved "$1")"; } pr_litecyanln_term() { pr_litecyan_term "$1"; outln_term; } pr_litecyanln() { pr_litecyan "$1"; outln; } pr_cyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;36m$1" || out_term "$1"; pr_off; } # additional hint -pr_cyan() { pr_cyan_term "$1"; out_html "$1"; } +pr_cyan() { pr_cyan_term "$1"; out_html "$(html_reserved "$1")"; } pr_cyanln_term() { pr_cyan_term "$1"; outln_term; } pr_cyanln() { pr_cyan "$1"; outln; } pr_litegreyln_term() { pr_litegrey_term "$1"; outln_term; } # not really usable on a black background, see .. pr_litegreyln() { pr_litegrey "$1"; outln; } pr_litegrey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 -pr_litegrey() { pr_litegrey_term "$1"; out_html "$1"; } +pr_litegrey() { pr_litegrey_term "$1"; out_html "$(html_reserved "$1")"; } pr_grey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } -pr_grey() { pr_grey_term "$1"; out_html "$1"; } +pr_grey() { pr_grey_term "$1"; out_html "$(html_reserved "$1")"; } pr_greyln_term() { pr_grey_term "$1"; outln_term; } pr_greyln() { pr_grey "$1"; outln; } pr_done_good_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;34m$1" || out_term "\033[0;32m$1" ) || out_term "$1"; pr_off; } # litegreen (liteblue), This is good -pr_done_good() { pr_done_good_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } +pr_done_good() { pr_done_good_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_done_goodln_term() { pr_done_good_term "$1"; outln_term; } pr_done_goodln() { pr_done_good "$1"; outln; } pr_done_best_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;34m$1" || out_term "\033[1;32m$1" ) || out_term "$1"; pr_off; } # green (blue), This is the best -pr_done_best() { pr_done_best_term "$1"; "$COLORBLIND" && out_html "$1" || out_html "$1"; } +pr_done_best() { pr_done_best_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_done_bestln_term() { pr_done_best_term "$1"; outln_term; } pr_done_bestln() { pr_done_best "$1"; outln; } pr_svrty_low_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;33m$1" || out_term "$1"; pr_off; } # yellow brown | academic or minor problem -pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$1"; } +pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$(html_reserved "$1")"; } pr_svrty_lowln_term() { pr_svrty_low_term "$1"; outln_term; } pr_svrty_lowln() { pr_svrty_low "$1"; outln; } pr_svrty_medium_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;33m$1" || out_term "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this -pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$1"; } +pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$(html_reserved "$1")"; } pr_svrty_mediumln_term() { pr_svrty_medium_term "$1"; outln_term; } pr_svrty_mediumln() { pr_svrty_medium "$1"; outln; } pr_svrty_high_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;31m$1" || pr_bold_term "$1"; pr_off; } # litered -pr_svrty_high() { pr_svrty_high_term "$1"; out_html "$1"; } +pr_svrty_high() { pr_svrty_high_term "$1"; out_html "$(html_reserved "$1")"; } pr_svrty_highln_term() { pr_svrty_high_term "$1"; outln_term; } pr_svrty_highln() { pr_svrty_high "$1"; outln; } pr_svrty_critical_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;31m$1" || pr_bold_term "$1"; pr_off; } # red -pr_svrty_critical() { pr_svrty_critical_term "$1"; out_html "$1"; } +pr_svrty_critical() { pr_svrty_critical_term "$1"; out_html "$(html_reserved "$1")"; } pr_svrty_criticalln_term() { pr_svrty_critical_term "$1"; outln_term; } pr_svrty_criticalln(){ pr_svrty_critical "$1"; outln; } pr_deemphasize_term() { out_term "$1"; } # hook for a weakened screen output, see #600 -pr_deemphasize() { pr_deemphasize_term "$1"; out_html "$1"; } +pr_deemphasize() { pr_deemphasize_term "$1"; out_html "$(html_reserved "$1")"; } pr_deemphasizeln_term() { pr_deemphasize_term "$1"; outln_term; } pr_deemphasizeln() { pr_deemphasize "$1"; outln; } # color=1 functions pr_off() { [[ "$COLOR" -ne 0 ]] && out_term "\033[m"; } pr_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1m$1" || out_term "$1"; pr_off; } -pr_bold() { pr_bold_term "$1"; out_html "$1"; } +pr_bold() { pr_bold_term "$1"; out_html "$(html_reserved "$1")"; } pr_boldln_term() { pr_bold_term "$1"; outln_term; } pr_boldln() { pr_bold "$1" ; outln; } pr_italic_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[3m$1" || out_term "$1"; pr_off; } -pr_italic() { pr_italic_term "$1"; out_html "$1"; } +pr_italic() { pr_italic_term "$1"; out_html "$(html_reserved "$1")"; } pr_italicln_term() { pr_italic_term "$1"; outln_term; } pr_italicln() { pr_italic "$1" ; outln; } pr_strikethru_term() { [[ "$COLOR" -ne 0 ]] && out "\033[9m$1" || out "$1"; pr_off; } # ugly! -pr_strikethru() { pr_strikethru_term "$1"; out_html "$1"; } +pr_strikethru() { pr_strikethru_term "$1"; out_html "$(html_reserved "$1")"; } pr_strikethruln_term() { pr_strikethru_term "$1"; outln_term; } pr_strikethruln() { pr_strikethru "$1" ; outln; } pr_underline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[4m$1" || out_term "$1"; pr_off; } -pr_underline() { pr_underline_term "$1"; out_html "$1"; } +pr_underline() { pr_underline_term "$1"; out_html "$(html_reserved "$1")"; } pr_underlineln_term() { pr_underline_term "$1"; outln_term; } pr_underlineln() { pr_underline "$1"; outln; } pr_reverse_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m$1" || out_term "$1"; pr_off; } -pr_reverse() { pr_reverse_term "$1"; out_html "$1"; } +pr_reverse() { pr_reverse_term "$1"; out_html "$(html_reserved "$1")"; } pr_reverse_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m\033[1m$1" || out_term "$1"; pr_off; } -pr_reverse_bold() { pr_reverse_bold_term "$1"; out_html "$1"; } +pr_reverse_bold() { pr_reverse_bold_term "$1"; out_html "$(html_reserved "$1")"; } #pr_headline() { pr_blue "$1"; } #http://misc.flogisoft.com/bash/tip_colors_and_formatting #pr_headline() { [[ "$COLOR" -eq 2 ]] && out "\033[1;30m\033[47m$1" || out "$1"; pr_off; } pr_headline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1m\033[4m$1" || out_term "$1"; pr_off; } -pr_headline() { pr_headline_term "$1"; out_html "$1"; } +pr_headline() { pr_headline_term "$1"; out_html "$(html_reserved "$1")"; } pr_headlineln_term() { pr_headline_term "$1"; outln_term; } pr_headlineln() { pr_headline "$1" ; outln; } @@ -963,21 +969,28 @@ fileout() { # ID, SEVERITY, FINDING, CVE, CWE, HINT ################### FILE FORMATING END ######################### html_header() { - out_html "\n" - out_html "\n" - out_html "\n" - out_html "\n" - out_html "\n" - out_html "testssl.sh\n" - out_html "\n" - out_html "\n" - out_html "
\n"
+     if "$HTMLHEADER"; then
+          rm -f "$HTMLFILE"
+          out_html "\n"
+          out_html "\n"
+          out_html "\n"
+          out_html "\n"
+          out_html "\n"
+          out_html "testssl.sh\n"
+          out_html "\n"
+          out_html "\n"
+          out_html "
\n"
+     fi
+     return 0
 }
 
 html_footer() {
-     out_html "
\n" - out_html "\n" - out_html "\n" + if "$HTMLHEADER"; then + out_html "
\n" + out_html "\n" + out_html "\n" + fi + return 0 } ###### helper function definitions ###### @@ -11322,6 +11335,7 @@ cleanup () { fi outln "$APPEND" || fileout_footer + html_footer } fatal() { @@ -11386,7 +11400,7 @@ ignore_no_or_lame() { [[ "$WARNINGS" == off ]] && return 0 [[ "$WARNINGS" == false ]] && return 0 [[ "$WARNINGS" == batch ]] && return 1 - pr_warning "$1 --> " + pr_warning_term "$1 --> " read a if [[ "$a" == "$(tolower "$2")" ]]; then $ok_arg return 0 @@ -12002,7 +12016,7 @@ datebanner() { # one line with char $1 over screen width $2 draw_line() { - printf -- "$1"'%.s' $(eval "echo {1.."$(($2))"}") + out "$(printf -- "$1"'%.s' $(eval "echo {1.."$(($2))"}"))" } @@ -12092,7 +12106,7 @@ run_mass_testing() { cmdline=$(filter_input "$cmdline") [[ -z "$cmdline" ]] && continue [[ "$cmdline" == "EOF" ]] && break - cmdline="$0 $global_cmdline --warnings=batch -q --append $cmdline" + cmdline="$0 $global_cmdline --warnings=batch -q --no-html-header --append $cmdline" draw_line "=" $((TERM_WIDTH / 2)); outln; outln "$cmdline" $cmdline @@ -12516,7 +12530,9 @@ parse_cmd_line() { exit -6 fi do_html=true - html_header + ;; + --no-html-header) + HTMLHEADER=false ;; --append) APPEND=true @@ -12700,6 +12716,7 @@ lets_roll() { initialize_globals parse_cmd_line "$@" +html_header get_install_dir set_color_functions maketempf @@ -12760,6 +12777,5 @@ else fi fi fi -html_footer exit $? From 2b5324b8efcec4d295bb7a0a8b7a9e1cf028484d Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 10 Feb 2017 10:59:20 -0500 Subject: [PATCH 05/12] Fix emphasize_stuff_in_headers() Changed `emphasize_stuff_in_headers()` so that the appropriate coloring would appear both in the terminal and in the HTML. It's slow, but it works. --- testssl.sh | 139 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 35 deletions(-) diff --git a/testssl.sh b/testssl.sh index 9c93718..9f92e34 100755 --- a/testssl.sh +++ b/testssl.sh @@ -560,6 +560,12 @@ declare TLS_CIPHER_EXPORT=() declare TLS_CIPHER_OSSL_SUPPORTED=() ###### output functions ###### + +# For HTML output, replace any HTML reserved characters with the entity name +html_reserved(){ + echo "$1" | sed -e 's/\&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\'/g" +} + # a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%' and '--' does the rest. out_html() { "$do_html" && printf -- "%b" "${1//%/%%}" >> "$HTMLFILE" @@ -588,10 +594,6 @@ retstring(){ printf -- "%b" "${1//%/%%}" } -# For HTML output, replace any HTML reserved characters with the entity name -html_reserved(){ - echo "$1" | sed -e 's/\&/\&/g' -e 's//\>/g' -e 's/"/\"/g' -e "s/'/\"/g" -} #TODO: Still no shell injection safe but if just run it from the cmd line: that's fine # color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html @@ -973,6 +975,7 @@ html_header() { rm -f "$HTMLFILE" out_html "\n" out_html "\n" + out_html "\n" out_html "\n" out_html "\n" out_html "\n" @@ -1369,7 +1372,7 @@ run_http_header() { "Testing HTTP header response @ \"$URL_PATH\", $HTTP_STATUS_CODE$msg_thereafter (Hint: better try another URL)" ;; 401) - grep -aq "^WWW-Authenticate" $HEADERFILE && out " "; strip_lf "$(grep -a "^WWW-Authenticate" $HEADERFILE)" + grep -aq "^WWW-Authenticate" $HEADERFILE && out " "; out "$(strip_lf "$(grep -a "^WWW-Authenticate" $HEADERFILE)")" fileout "HTTP_STATUS_CODE" "INFO" \ "Testing HTTP header response @ \"$URL_PATH\", $HTTP_STATUS_CODE$msg_thereafter $(grep -a "^WWW-Authenticate" $HEADERFILE)" ;; @@ -1817,36 +1820,102 @@ run_hpkp() { } emphasize_stuff_in_headers(){ -# see http://www.grymoire.com/Unix/Sed.html#uh-3 -# outln "$1" | sed "s/[0-9]*/$brown&$off/g" - outln "$1" | sed -e "s/\([0-9]\)/$brown\1$off/g" \ - -e "s/Debian/"$yellow"\Debian$off/g" \ - -e "s/Win32/"$yellow"\Win32$off/g" \ - -e "s/Win64/"$yellow"\Win64$off/g" \ - -e "s/Ubuntu/"$yellow"Ubuntu$off/g" \ - -e "s/ubuntu/"$yellow"ubuntu$off/g" \ - -e "s/jessie/"$yellow"jessie$off/g" \ - -e "s/squeeze/"$yellow"squeeze$off/g" \ - -e "s/wheezy/"$yellow"wheezy$off/g" \ - -e "s/lenny/"$yellow"lenny$off/g" \ - -e "s/SUSE/"$yellow"SUSE$off/g" \ - -e "s/Red Hat Enterprise Linux/"$yellow"Red Hat Enterprise Linux$off/g" \ - -e "s/Red Hat/"$yellow"Red Hat$off/g" \ - -e "s/CentOS/"$yellow"CentOS$off/g" \ - -e "s/Via/"$yellow"Via$off/g" \ - -e "s/X-Forwarded/"$yellow"X-Forwarded$off/g" \ - -e "s/Liferay-Portal/"$yellow"Liferay-Portal$off/g" \ - -e "s/X-Cache-Lookup/"$yellow"X-Cache-Lookup$off/g" \ - -e "s/X-Cache/"$yellow"X-Cache$off/g" \ - -e "s/X-Squid/"$yellow"X-Squid$off/g" \ - -e "s/X-Server/"$yellow"X-Server$off/g" \ - -e "s/X-Varnish/"$yellow"X-Varnish$off/g" \ - -e "s/X-OWA-Version/"$yellow"X-OWA-Version$off/g" \ - -e "s/MicrosoftSharePointTeamServices/"$yellow"MicrosoftSharePointTeamServices$off/g" \ - -e "s/X-Version/"$yellow"X-Version$off/g" \ - -e "s/X-Powered-By/"$yellow"X-Powered-By$off/g" \ - -e "s/X-UA-Compatible/"$yellow"X-UA-Compatible$off/g" \ - -e "s/X-AspNet-Version/"$yellow"X-AspNet-Version$off/g" + local text="$1" + local -i len + + len=${#text} + while [[ $len -gt 0 ]]; do + if [[ -z "$(tr -d '0-9' <<< "${text:0:1}")" ]]; then + out_term "$brown${text:0:1}$off" + out_html "${text:0:1}" + text="${text:1}" + len=$len-1 + elif [[ $len -ge 31 ]] && [[ "${text:0:31}" == "MicrosoftSharePointTeamServices" ]]; then + out_term "$yellow${text:0:31}$off" + out_html "${text:0:31}" + text="${text:31}" + len=$len-31 + elif [[ $len -ge 24 ]] && [[ "${text:0:24}" == "Red Hat Enterprise Linux" ]]; then + out_term "$yellow${text:0:24}$off" + out_html "${text:0:24}" + text="${text:24}" + len=$len-24 + elif [[ $len -ge 16 ]] && [[ "${text:0:16}" == "X-AspNet-Version" ]]; then + out_term "$yellow${text:0:16}$off" + out_html "${text:0:16}" + text="${text:16}" + len=$len-16 + elif [[ $len -ge 15 ]] && [[ "${text:0:15}" == "X-UA-Compatible" ]]; then + out_term "$yellow${text:0:15}$off" + out_html "${text:0:15}" + text="${text:15}" + len=$len-15 + elif [[ $len -ge 14 ]] && ( [[ "${text:0:14}" == "Liferay-Portal" ]] || [[ "${text:0:14}" == "X-Cache-Lookup" ]] || \ + [[ "${text:0:14}" == "X-Cache-Status" ]] ) ; then + out_term "$yellow${text:0:14}$off" + out_html "${text:0:14}" + text="${text:14}" + len=$len-14 + elif [[ $len -ge 13 ]] && [[ "${text:0:13}" == "X-OWA-Version" ]]; then + out_term "$yellow${text:0:13}$off" + out_html "${text:0:13}" + text="${text:13}" + len=$len-13 + elif [[ $len -ge 12 ]] && [[ "${text:0:12}" == "X-Powered-By" ]]; then + out_term "$yellow${text:0:12}$off" + out_html "${text:0:12}" + text="${text:12}" + len=$len-12 + elif [[ $len -ge 11 ]] && [[ "${text:0:11}" == "X-Forwarded" ]]; then + out_term "$yellow${text:0:11}$off" + out_html "${text:0:11}" + text="${text:11}" + len=$len-11 + elif [[ $len -ge 9 ]] && ( [[ "${text:0:9}" == "X-Varnish" ]] || [[ "${text:0:9}" == "X-Version" ]] ); then + out_term "$yellow${text:0:9}$off" + out_html "${text:0:9}" + text="${text:9}" + len=$len-9 + elif [[ $len -ge 8 ]] && [[ "${text:0:8}" == "X-Server" ]]; then + out_term "$yellow${text:0:8}$off" + out_html "${text:0:8}" + text="${text:8}" + len=$len-8 + elif [[ $len -ge 7 ]] && ( [[ "${text:0:7}" == "squeeze" ]] || [[ "${text:0:7}" == "Red Hat" ]] || \ + [[ "${text:0:7}" == "X-Cache" ]] || [[ "${text:0:7}" == "X-Squid" ]] ) ; then + out_term "$yellow${text:0:7}$off" + out_html "${text:0:7}" + text="${text:7}" + len=$len-7 + elif [[ $len -ge 6 ]] && ( [[ "${text:0:6}" == "Debian" ]] || [[ "${text:0:6}" == "Ubuntu" ]] || \ + [[ "${text:0:6}" == "ubuntu" ]] || [[ "${text:0:6}" == "jessie" ]] || \ + [[ "${text:0:6}" == "wheezy" ]] || [[ "${text:0:6}" == "CentOS" ]] ) ; then + out_term "$yellow${text:0:6}$off" + out_html "${text:0:6}" + text="${text:6}" + len=$len-6 + elif [[ $len -ge 5 ]] && ( [[ "${text:0:5}" == "Win32" ]] || [[ "${text:0:5}" == "Win64" ]] || [[ "${text:0:5}" == "lenny" ]] ); then + out_term "$yellow${text:0:5}$off" + out_html "${text:0:5}" + text="${text:5}" + len=$len-5 + elif [[ $len -ge 4 ]] && [[ "${text:0:4}" == "SUSE" ]]; then + out_term "$yellow${text:0:4}$off" + out_html "${text:0:4}" + text="${text:4}" + len=$len-4 + elif [[ $len -ge 3 ]] && [[ "${text:0:3}" == "Via" ]]; then + out_term "$yellow${text:0:3}$off" + out_html "${text:0:3}" + text="${text:3}" + len=$len-3 + else + out "${text:0:1}" + text="${text:1}" + len=$len-1 + fi + done + outln } run_server_banner() { From 2652362ce0fa5d2c77cf9303612327eff7426cb9 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 10 Feb 2017 14:47:49 -0500 Subject: [PATCH 06/12] Final fixes Found more places where output should only go to terminal, or where it was only going to the terminal (e.g., printf) but should also be in the HTML. Also added the ability to include active URLs in the HTML output. To Do: Handle automatic generation of HTML file name and support for parallel testing. --- testssl.sh | 92 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/testssl.sh b/testssl.sh index 9f92e34..8633084 100755 --- a/testssl.sh +++ b/testssl.sh @@ -712,6 +712,9 @@ fixme() { pr_warning "fixme: $1"; } fixmeln_term() { pr_warningln_term "fixme: $1"; } fixmeln() { pr_warningln "fixme: $1"; } +pr_url() { out_term "$1"; out_html "$1"; } +pr_boldurl() { pr_bold_term "$1"; out_html "$1"; } + ### color switcher (see e.g. https://linuxtidbits.wordpress.com/2008/08/11/output-color-on-bash-scripts/ ### http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html set_color_functions() { @@ -2343,7 +2346,7 @@ openssl2rfc() { [[ "$1" == "${TLS_CIPHER_OSSL_NAME[i]}" ]] && rfcname="${TLS_CIPHER_RFC_NAME[i]}" && break done [[ "$rfcname" == "-" ]] && rfcname="" - [[ -n "$rfcname" ]] && out "$rfcname" + [[ -n "$rfcname" ]] && retstring "$rfcname" return 0 } @@ -2355,7 +2358,7 @@ rfc2openssl() { [[ "$1" == "${TLS_CIPHER_RFC_NAME[i]}" ]] && ossl_name="${TLS_CIPHER_OSSL_NAME[i]}" && break done [[ "$ossl_name" == "-" ]] && ossl_name="" - [[ -n "$ossl_name" ]] && out "$ossl_name" + [[ -n "$ossl_name" ]] && retstring "$ossl_name" return 0 } @@ -2990,7 +2993,7 @@ run_cipher_per_proto() { fi outln neat_header - outln " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2"| while read proto proto_hex proto_text; do + retstring " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2\n" | while read proto proto_hex proto_text; do "$using_sockets" || locally_supported "$proto" "$proto_text" || continue "$using_sockets" && out "$proto_text " outln @@ -3289,7 +3292,7 @@ create_client_simulation_tls_clienthello() { if [[ $offset -ge $tls_handshake_ascii_len ]]; then # No extensions - out "$tls_handshake_ascii" + retstring "$tls_handshake_ascii" return 0 fi @@ -4363,7 +4366,7 @@ run_prototest_openssl() { # idempotent function to add SSL/TLS protocols. It should ease testing # PROTOS_OFFERED's content is in openssl terminology add_tls_offered() { - grep -w "$1" <<< "$PROTOS_OFFERED" || PROTOS_OFFERED+="$1 " + grep -qw "$1" <<< "$PROTOS_OFFERED" || PROTOS_OFFERED+="$1 " } # function which checks whether SSLv2 - TLS 1.2 is being offereed @@ -5190,7 +5193,7 @@ cipher_pref_check() { pr_bold " Cipher order" - retstring " ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n"| while read p proto_hex proto; do + retstring " ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n" | while read p proto_hex proto; do order=""; ciphers_found_with_sockets=false if [[ $p == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; @@ -5363,7 +5366,7 @@ cipher_pref_check() { order="" $OPENSSL s_client $BUGS -nextprotoneg "$p" -connect $NODEIP:$PORT $SNI >$ERRFILE >$TMPFILE cipher=$(awk '/Cipher.*:/ { print $3 }' $TMPFILE) - printf " %-10s %s " "$p:" "$cipher" + out "$(printf " %-10s %s " "$p:" "$cipher")" tested_cipher="-"$cipher order="$cipher" if ! "$FAST"; then @@ -6583,7 +6586,7 @@ run_server_defaults() { i=1 newhostcert=$(cat $HOSTCERT) while [[ $i -le $certs_found ]]; do - if [ "$newhostcert" == "${previous_hostcert[i]}" ]; then + if [[ "$newhostcert" == "${previous_hostcert[i]}" ]]; then match_found=true break; fi @@ -9304,12 +9307,12 @@ run_heartbleed(){ if [[ "${saved_sockreply[1]}" == "${saved_sockreply[2]}" ]] && [[ "${saved_sockreply[2]}" == "${saved_sockreply[3]}" ]] \ && "$found_500_oops"; then pr_done_best "not vulnerable (OK)$append" - [[ $DEBUG -ge 1 ]] && out_term ", successful weeded out vsftpd false positive" + [[ $DEBUG -ge 1 ]] && out ", successful weeded out vsftpd false positive" fileout "heartbleed" "OK" "Heartbleed: not vulnerable $append" "$cve" "$cwe" else out "likely " pr_svrty_critical "VULNERABLE (NOT ok)" - [[ $DEBUG -ge 1 ]] && out_term " use debug >=2 to confirm" + [[ $DEBUG -ge 1 ]] && out " use debug >=2 to confirm" fileout "heartbleed" "CRITICAL" "Heartbleed: likely VULNERABLE $append" "$cve" "$cwe" "$hint" fi else @@ -9850,7 +9853,7 @@ run_tls_poodle() { pr_bold " POODLE, TLS"; out " ($cve), experimental " #FIXME - echo "#FIXME" + pr_warningln "#FIXME" fileout "poodle_tls" "WARN" "POODLE, TLS: Not tested. Not yet implemented #FIXME" "$cve" "$cwe" return 7 } @@ -10285,7 +10288,9 @@ run_drown() { if [[ "$DEBUG" -ge 1 ]] || "$SHOW_CENSYS_LINK"; then # not advertising it as it after 5 tries and account is needed cert_fingerprint_sha2=${cert_fingerprint_sha2/SHA256 /} - outln "$spaces https://censys.io/ipv4?q=$cert_fingerprint_sha2 could help you to find out" + out "$spaces " + pr_url "https://censys.io/ipv4?q=$cert_fingerprint_sha2" + outln " could help you to find out" fileout "drown" "INFO" "make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://censys.io/ipv4?q=$cert_fingerprint_sha2" fi else @@ -10889,9 +10894,11 @@ run_youknowwho() { old_fart() { - outln "Get precompiled bins or compile https://github.com/PeterMosmans/openssl ." + out "Get precompiled bins or compile " + pr_url "https://github.com/PeterMosmans/openssl" + outln "." fileout "old_fart" "WARN" "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed. Get precompiled bins or compile https://github.com/PeterMosmans/openssl ." - fatal "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed." -5 + fatal "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn't make much sense to proceed." -5 } # try very hard to determine the install path to get ahold of the mapping file and the CA bundles @@ -10977,7 +10984,7 @@ find_openssl_binary() { # 0. check environment variable whether it's executable if [[ -n "$OPENSSL" ]] && [[ ! -x "$OPENSSL" ]]; then pr_warningln "\ncannot find specified (\$OPENSSL=$OPENSSL) binary." - outln " Looking some place else ..." + outln_term " Looking some place else ..." elif [[ -x "$OPENSSL" ]]; then : # 1. all ok supplied $OPENSSL was found and has excutable bit set -- testrun comes below elif [[ -e "/mnt/c/Windows/System32/bash.exe" ]] && test_openssl_suffix "$(dirname "$(which openssl)")"; then @@ -11087,10 +11094,12 @@ check4openssl_oldfarts() { pr_warningln " Your \"$OPENSSL\" is way too old (/dev/null)\" [~$OPENSSL_NR_CIPHERS ciphers]" out " on $HNAME:" @@ -11536,10 +11546,10 @@ prepare_logging() { : # just for clarity: a log file was specified, no need to do anything else fi >$LOGFILE - outln "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE} - outln "## at $HNAME:$OPENSSL_LOCATION" >>${LOGFILE} - outln "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE" >>${LOGFILE} - outln "## version openssl: \"$OSSL_VER\" from \"$OSSL_BUILD_DATE\")\n" >>${LOGFILE} + outln_term "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE} + outln_term "## at $HNAME:$OPENSSL_LOCATION" >>${LOGFILE} + outln_term "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE" >>${LOGFILE} + outln_term "## version openssl: \"$OSSL_VER\" from \"$OSSL_BUILD_DATE\")\n" >>${LOGFILE} exec > >(tee -a ${LOGFILE}) # not decided yet. Maybe good to have a separate file or none at all #exec 2> >(tee -a ${LOGFILE} >&2) @@ -12031,8 +12041,8 @@ determine_service() { grep -q '^Server Temp Key' $TMPFILE && HAS_DH_BITS=true # FIX #190 out " Service set:$CORRECT_SPACES STARTTLS via " fileout "service" "INFO" "$protocol" - toupper "$protocol" - [[ -n "$XMPP_HOST" ]] && echo -n " (XMPP domain=\'$XMPP_HOST\')" + out "$(toupper "$protocol")" + [[ -n "$XMPP_HOST" ]] && out " (XMPP domain=\'$XMPP_HOST\')" outln ;; *) outln @@ -12355,7 +12365,7 @@ parse_cmd_line() { case $STARTTLS_PROTOCOL in ftp|smtp|pop3|imap|xmpp|telnet|ldap|nntp|postgres) ;; ftps|smtps|pop3s|imaps|xmpps|telnets|ldaps|nntps|postgress) ;; - *) pr_magentaln "\nunrecognized STARTTLS protocol \"$1\", see help" 1>&2 + *) pr_magentaln_term "\nunrecognized STARTTLS protocol \"$1\", see help" 1>&2 help 1 ;; esac ;; @@ -12514,7 +12524,7 @@ parse_cmd_line() { [[ $? -eq 0 ]] && shift case "$WARNINGS" in batch|off|false) ;; - *) pr_magentaln "\nwarnings can be either \"batch\", \"off\" or \"false\"" + *) pr_magentaln_term "\nwarnings can be either \"batch\", \"off\" or \"false\"" help 1 esac ;; @@ -12532,7 +12542,7 @@ parse_cmd_line() { [[ $? -eq 0 ]] && shift case $DEBUG in [0-6]) ;; - *) pr_magentaln_term "\nunrecognized debug value \"$1\", must be between 0..6" 1>&2 + *) pr_magentaln_term_term "\nunrecognized debug value \"$1\", must be between 0..6" 1>&2 help 1 esac ;; @@ -12542,7 +12552,7 @@ parse_cmd_line() { case $COLOR in [0-2]) ;; *) COLOR=2 - pr_magentaln "\nunrecognized color: \"$1\", must be between 0..2" 1>&2 + pr_magentaln_term "\nunrecognized color: \"$1\", must be between 0..2" 1>&2 help 1 esac ;; @@ -12595,7 +12605,7 @@ parse_cmd_line() { HTMLFILE=$(parse_opt_equal_sign "$1" "$2") [[ $? -eq 0 ]] && shift if [[ -d "$HTMLFILE" ]]; then - pr_warningln_term "$HTMLFILE exists and is a directory" + pr_warningln_term_term "$HTMLFILE exists and is a directory" exit -6 fi do_html=true @@ -12620,7 +12630,7 @@ parse_cmd_line() { [[ $? -eq 0 ]] && shift case "$cipher_mapping" in no-rfc) unset ADD_RFC_STR;; - *) pr_magentaln "\nmapping can only be \"no-rfc\"" + *) pr_magentaln_term "\nmapping can only be \"no-rfc\"" help 1 ;; esac ;; @@ -12640,7 +12650,7 @@ parse_cmd_line() { (--) shift break ;; - (-*) pr_magentaln "0: unrecognized option \"$1\"" 1>&2; + (-*) pr_magentaln_term "0: unrecognized option \"$1\"" 1>&2; help 1 ;; (*) break @@ -12672,7 +12682,7 @@ nodeip_to_proper_ip6() { if is_ipv6addr $NODEIP; then ${UNBRACKTD_IPV6} || NODEIP="[$NODEIP]" len_nodeip=${#NODEIP} - CORRECT_SPACES="$(draw_line " " "$((len_nodeip - 17))" )" + CORRECT_SPACES="$(printf -- " "'%.s' $(eval "echo {1.."$((len_nodeip - 17))"}"))" # IPv6 addresses are longer, this varaible takes care that "further IP" and "Service" is properly aligned fi } From f633ce67d6ed46b775e830360e201d2eca26ccae Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 10 Feb 2017 15:05:43 -0500 Subject: [PATCH 07/12] Color change Change `emphasize_stuff_in_headers()` to use olive and bold olive rather than brown and yellow. This matches what `aha` creates and appears similar to what is displayed in the terminal on a Mac. Also, yellow text is very difficult to read. --- testssl.sh | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/testssl.sh b/testssl.sh index 8633084..5d7e6c4 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1830,86 +1830,86 @@ emphasize_stuff_in_headers(){ while [[ $len -gt 0 ]]; do if [[ -z "$(tr -d '0-9' <<< "${text:0:1}")" ]]; then out_term "$brown${text:0:1}$off" - out_html "${text:0:1}" + out_html "${text:0:1}" text="${text:1}" len=$len-1 elif [[ $len -ge 31 ]] && [[ "${text:0:31}" == "MicrosoftSharePointTeamServices" ]]; then out_term "$yellow${text:0:31}$off" - out_html "${text:0:31}" + out_html "${text:0:31}" text="${text:31}" len=$len-31 elif [[ $len -ge 24 ]] && [[ "${text:0:24}" == "Red Hat Enterprise Linux" ]]; then out_term "$yellow${text:0:24}$off" - out_html "${text:0:24}" + out_html "${text:0:24}" text="${text:24}" len=$len-24 elif [[ $len -ge 16 ]] && [[ "${text:0:16}" == "X-AspNet-Version" ]]; then out_term "$yellow${text:0:16}$off" - out_html "${text:0:16}" + out_html "${text:0:16}" text="${text:16}" len=$len-16 elif [[ $len -ge 15 ]] && [[ "${text:0:15}" == "X-UA-Compatible" ]]; then out_term "$yellow${text:0:15}$off" - out_html "${text:0:15}" + out_html "${text:0:15}" text="${text:15}" len=$len-15 elif [[ $len -ge 14 ]] && ( [[ "${text:0:14}" == "Liferay-Portal" ]] || [[ "${text:0:14}" == "X-Cache-Lookup" ]] || \ [[ "${text:0:14}" == "X-Cache-Status" ]] ) ; then out_term "$yellow${text:0:14}$off" - out_html "${text:0:14}" + out_html "${text:0:14}" text="${text:14}" len=$len-14 elif [[ $len -ge 13 ]] && [[ "${text:0:13}" == "X-OWA-Version" ]]; then out_term "$yellow${text:0:13}$off" - out_html "${text:0:13}" + out_html "${text:0:13}" text="${text:13}" len=$len-13 elif [[ $len -ge 12 ]] && [[ "${text:0:12}" == "X-Powered-By" ]]; then out_term "$yellow${text:0:12}$off" - out_html "${text:0:12}" + out_html "${text:0:12}" text="${text:12}" len=$len-12 elif [[ $len -ge 11 ]] && [[ "${text:0:11}" == "X-Forwarded" ]]; then out_term "$yellow${text:0:11}$off" - out_html "${text:0:11}" + out_html "${text:0:11}" text="${text:11}" len=$len-11 elif [[ $len -ge 9 ]] && ( [[ "${text:0:9}" == "X-Varnish" ]] || [[ "${text:0:9}" == "X-Version" ]] ); then out_term "$yellow${text:0:9}$off" - out_html "${text:0:9}" + out_html "${text:0:9}" text="${text:9}" len=$len-9 elif [[ $len -ge 8 ]] && [[ "${text:0:8}" == "X-Server" ]]; then out_term "$yellow${text:0:8}$off" - out_html "${text:0:8}" + out_html "${text:0:8}" text="${text:8}" len=$len-8 elif [[ $len -ge 7 ]] && ( [[ "${text:0:7}" == "squeeze" ]] || [[ "${text:0:7}" == "Red Hat" ]] || \ [[ "${text:0:7}" == "X-Cache" ]] || [[ "${text:0:7}" == "X-Squid" ]] ) ; then out_term "$yellow${text:0:7}$off" - out_html "${text:0:7}" + out_html "${text:0:7}" text="${text:7}" len=$len-7 elif [[ $len -ge 6 ]] && ( [[ "${text:0:6}" == "Debian" ]] || [[ "${text:0:6}" == "Ubuntu" ]] || \ [[ "${text:0:6}" == "ubuntu" ]] || [[ "${text:0:6}" == "jessie" ]] || \ [[ "${text:0:6}" == "wheezy" ]] || [[ "${text:0:6}" == "CentOS" ]] ) ; then out_term "$yellow${text:0:6}$off" - out_html "${text:0:6}" + out_html "${text:0:6}" text="${text:6}" len=$len-6 elif [[ $len -ge 5 ]] && ( [[ "${text:0:5}" == "Win32" ]] || [[ "${text:0:5}" == "Win64" ]] || [[ "${text:0:5}" == "lenny" ]] ); then out_term "$yellow${text:0:5}$off" - out_html "${text:0:5}" + out_html "${text:0:5}" text="${text:5}" len=$len-5 elif [[ $len -ge 4 ]] && [[ "${text:0:4}" == "SUSE" ]]; then out_term "$yellow${text:0:4}$off" - out_html "${text:0:4}" + out_html "${text:0:4}" text="${text:4}" len=$len-4 elif [[ $len -ge 3 ]] && [[ "${text:0:3}" == "Via" ]]; then out_term "$yellow${text:0:3}$off" - out_html "${text:0:3}" + out_html "${text:0:3}" text="${text:3}" len=$len-3 else From fea2558b20a8a8c919c4d501db8013ca5546629e Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 10 Feb 2017 16:30:14 -0500 Subject: [PATCH 08/12] Show gray for COLOR=1 Gray should appear for COLOR=1 or COLOR=2. Since `pr_grey()` is basically the same as `out()` for COLOR=0, `mybanner()` should just call `pr_grey()` without checking the value of `$COLOR`. --- testssl.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testssl.sh b/testssl.sh index 5d7e6c4..6d6e05b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -626,9 +626,9 @@ pr_cyanln() { pr_cyan "$1"; outln; } pr_litegreyln_term() { pr_litegrey_term "$1"; outln_term; } # not really usable on a black background, see .. pr_litegreyln() { pr_litegrey "$1"; outln; } -pr_litegrey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 +pr_litegrey_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 pr_litegrey() { pr_litegrey_term "$1"; out_html "$(html_reserved "$1")"; } -pr_grey_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } +pr_grey_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } pr_grey() { pr_grey_term "$1"; out_html "$(html_reserved "$1")"; } pr_greyln_term() { pr_grey_term "$1"; outln_term; } pr_greyln() { pr_grey "$1"; outln; } @@ -11380,7 +11380,7 @@ EOF pr_bold "$bb1" pr_boldurl "$SWURL"; outln pr_bold " (" - [[ "$COLOR" -ne 0 ]] && pr_grey "$idtag" || out "$idtag" + pr_grey "$idtag" pr_boldln ")" pr_bold "$bb2" pr_boldurl "https://testssl.sh/bugs/"; outln From 376fb95d04ec2e5a1f644d77366644e997173e4c Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 10 Feb 2017 17:08:49 -0500 Subject: [PATCH 09/12] Follow $COLOR value in HTML output Use the value of `$COLOR` to affect the HTML output in addition to the output to the terminal. --- testssl.sh | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/testssl.sh b/testssl.sh index 6d6e05b..46b26e2 100755 --- a/testssl.sh +++ b/testssl.sh @@ -598,65 +598,65 @@ retstring(){ # color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html pr_liteblue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;32m$1" || out_term "\033[0;34m$1" ) || out_term "$1"; pr_off; } # not yet used -pr_liteblue() { pr_liteblue_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } +pr_liteblue() { pr_liteblue_term "$1"; [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ) || out_html "$(html_reserved "$1")"; } pr_liteblueln_term() { pr_liteblue_term "$1"; outln_term; } pr_liteblueln() { pr_liteblue "$1"; outln; } pr_blue_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;32m$1" || out_term "\033[1;34m$1" ) || out_term "$1"; pr_off; } # used for head lines of single tests -pr_blue() { pr_blue_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } +pr_blue() { pr_blue_term "$1"; [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ) || out_html "$(html_reserved "$1")"; } pr_blueln_term() { pr_blue_term "$1"; outln_term; } pr_blueln() { pr_blue "$1"; outln; } pr_warning_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;35m$1" || pr_underline_term "$1"; pr_off; } # some local problem: one test cannot be done -pr_warning() { pr_warning_term "$1"; out_html "$(html_reserved "$1")"; } +pr_warning() { pr_warning_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || ( [[ "$COLOR" -eq 1 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ); } pr_warningln_term() { pr_warning_term "$1"; outln_term; } # litemagenta pr_warningln() { pr_warning "$1"; outln; } pr_magenta_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;35m$1" || pr_underline_term "$1"; pr_off; } # fatal error: quitting because of this! -pr_magenta() { pr_magenta_term "$1"; out_html "$(html_reserved "$1")"; } +pr_magenta() { pr_magenta_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || ( [[ "$COLOR" -eq 1 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ); } pr_magentaln_term() { pr_magenta_term "$1"; outln_term; } pr_magentaln() { pr_magenta "$1"; outln; } pr_litecyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;36m$1" || out_term "$1"; pr_off; } # not yet used -pr_litecyan() { pr_litecyan_term "$1"; out_html "$(html_reserved "$1")"; } +pr_litecyan() { pr_litecyan_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_litecyanln_term() { pr_litecyan_term "$1"; outln_term; } pr_litecyanln() { pr_litecyan "$1"; outln; } pr_cyan_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;36m$1" || out_term "$1"; pr_off; } # additional hint -pr_cyan() { pr_cyan_term "$1"; out_html "$(html_reserved "$1")"; } +pr_cyan() { pr_cyan_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_cyanln_term() { pr_cyan_term "$1"; outln_term; } pr_cyanln() { pr_cyan "$1"; outln; } pr_litegreyln_term() { pr_litegrey_term "$1"; outln_term; } # not really usable on a black background, see .. pr_litegreyln() { pr_litegrey "$1"; outln; } pr_litegrey_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[0;37m$1" || out_term "$1"; pr_off; } # ... https://github.com/drwetter/testssl.sh/pull/600#issuecomment-276129876 -pr_litegrey() { pr_litegrey_term "$1"; out_html "$(html_reserved "$1")"; } +pr_litegrey() { pr_litegrey_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_grey_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1;30m$1" || out_term "$1"; pr_off; } -pr_grey() { pr_grey_term "$1"; out_html "$(html_reserved "$1")"; } +pr_grey() { pr_grey_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_greyln_term() { pr_grey_term "$1"; outln_term; } pr_greyln() { pr_grey "$1"; outln; } pr_done_good_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[0;34m$1" || out_term "\033[0;32m$1" ) || out_term "$1"; pr_off; } # litegreen (liteblue), This is good -pr_done_good() { pr_done_good_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } +pr_done_good() { pr_done_good_term "$1"; [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ) || out_html "$(html_reserved "$1")"; } pr_done_goodln_term() { pr_done_good_term "$1"; outln_term; } pr_done_goodln() { pr_done_good "$1"; outln; } pr_done_best_term() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_term "\033[1;34m$1" || out_term "\033[1;32m$1" ) || out_term "$1"; pr_off; } # green (blue), This is the best -pr_done_best() { pr_done_best_term "$1"; "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } +pr_done_best() { pr_done_best_term "$1"; [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ) || out_html "$(html_reserved "$1")"; } pr_done_bestln_term() { pr_done_best_term "$1"; outln_term; } pr_done_bestln() { pr_done_best "$1"; outln; } pr_svrty_low_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;33m$1" || out_term "$1"; pr_off; } # yellow brown | academic or minor problem -pr_svrty_low() { pr_svrty_low_term "$1"; out_html "$(html_reserved "$1")"; } +pr_svrty_low() { pr_svrty_low_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_svrty_lowln_term() { pr_svrty_low_term "$1"; outln_term; } pr_svrty_lowln() { pr_svrty_low "$1"; outln; } pr_svrty_medium_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;33m$1" || out_term "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this -pr_svrty_medium() { pr_svrty_medium_term "$1"; out_html "$(html_reserved "$1")"; } +pr_svrty_medium() { pr_svrty_medium_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_svrty_mediumln_term() { pr_svrty_medium_term "$1"; outln_term; } pr_svrty_mediumln() { pr_svrty_medium "$1"; outln; } pr_svrty_high_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[0;31m$1" || pr_bold_term "$1"; pr_off; } # litered -pr_svrty_high() { pr_svrty_high_term "$1"; out_html "$(html_reserved "$1")"; } +pr_svrty_high() { pr_svrty_high_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || ( [[ "$COLOR" -eq 1 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ); } pr_svrty_highln_term() { pr_svrty_high_term "$1"; outln_term; } pr_svrty_highln() { pr_svrty_high "$1"; outln; } pr_svrty_critical_term() { [[ "$COLOR" -eq 2 ]] && out_term "\033[1;31m$1" || pr_bold_term "$1"; pr_off; } # red -pr_svrty_critical() { pr_svrty_critical_term "$1"; out_html "$(html_reserved "$1")"; } +pr_svrty_critical() { pr_svrty_critical_term "$1"; [[ "$COLOR" -eq 2 ]] && out_html "$(html_reserved "$1")" || ( [[ "$COLOR" -eq 1 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")" ); } pr_svrty_criticalln_term() { pr_svrty_critical_term "$1"; outln_term; } pr_svrty_criticalln(){ pr_svrty_critical "$1"; outln; } @@ -668,32 +668,32 @@ pr_deemphasizeln() { pr_deemphasize "$1"; outln; } # color=1 functions pr_off() { [[ "$COLOR" -ne 0 ]] && out_term "\033[m"; } pr_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1m$1" || out_term "$1"; pr_off; } -pr_bold() { pr_bold_term "$1"; out_html "$(html_reserved "$1")"; } +pr_bold() { pr_bold_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_boldln_term() { pr_bold_term "$1"; outln_term; } pr_boldln() { pr_bold "$1" ; outln; } pr_italic_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[3m$1" || out_term "$1"; pr_off; } -pr_italic() { pr_italic_term "$1"; out_html "$(html_reserved "$1")"; } +pr_italic() { pr_italic_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_italicln_term() { pr_italic_term "$1"; outln_term; } pr_italicln() { pr_italic "$1" ; outln; } -pr_strikethru_term() { [[ "$COLOR" -ne 0 ]] && out "\033[9m$1" || out "$1"; pr_off; } # ugly! -pr_strikethru() { pr_strikethru_term "$1"; out_html "$(html_reserved "$1")"; } +pr_strikethru_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[9m$1" || out_term "$1"; pr_off; } # ugly! +pr_strikethru() { pr_strikethru_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_strikethruln_term() { pr_strikethru_term "$1"; outln_term; } pr_strikethruln() { pr_strikethru "$1" ; outln; } pr_underline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[4m$1" || out_term "$1"; pr_off; } -pr_underline() { pr_underline_term "$1"; out_html "$(html_reserved "$1")"; } +pr_underline() { pr_underline_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_underlineln_term() { pr_underline_term "$1"; outln_term; } pr_underlineln() { pr_underline "$1"; outln; } pr_reverse_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m$1" || out_term "$1"; pr_off; } -pr_reverse() { pr_reverse_term "$1"; out_html "$(html_reserved "$1")"; } +pr_reverse() { pr_reverse_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_reverse_bold_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[7m\033[1m$1" || out_term "$1"; pr_off; } -pr_reverse_bold() { pr_reverse_bold_term "$1"; out_html "$(html_reserved "$1")"; } +pr_reverse_bold() { pr_reverse_bold_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } #pr_headline() { pr_blue "$1"; } #http://misc.flogisoft.com/bash/tip_colors_and_formatting #pr_headline() { [[ "$COLOR" -eq 2 ]] && out "\033[1;30m\033[47m$1" || out "$1"; pr_off; } pr_headline_term() { [[ "$COLOR" -ne 0 ]] && out_term "\033[1m\033[4m$1" || out_term "$1"; pr_off; } -pr_headline() { pr_headline_term "$1"; out_html "$(html_reserved "$1")"; } +pr_headline() { pr_headline_term "$1"; [[ "$COLOR" -ne 0 ]] && out_html "$(html_reserved "$1")" || out_html "$(html_reserved "$1")"; } pr_headlineln_term() { pr_headline_term "$1"; outln_term; } pr_headlineln() { pr_headline "$1" ; outln; } @@ -1826,6 +1826,10 @@ emphasize_stuff_in_headers(){ local text="$1" local -i len + if [[ $COLOR -ne 2 ]]; then + out "$text" + text="" + fi len=${#text} while [[ $len -gt 0 ]]; do if [[ -z "$(tr -d '0-9' <<< "${text:0:1}")" ]]; then From 308b24cbe98b77e30d2f5ede49bac80fa69a9428 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 14 Feb 2017 13:19:12 -0500 Subject: [PATCH 10/12] Let testssl.sh create HTML file name Add option for testssl.sh to create the HTML file name. If testssl.sh creates the file name, then, in the case of mass testing, a separate HTML file is created for each test (i.e., for each line in the file provided to `--file`). --- testssl.sh | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/testssl.sh b/testssl.sh index fe05661..20ef416 100755 --- a/testssl.sh +++ b/testssl.sh @@ -569,7 +569,7 @@ html_reserved(){ # a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%' and '--' does the rest. out_html() { - "$do_html" && printf -- "%b" "${1//%/%%}" >> "$HTMLFILE" + "$do_html" && [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && printf -- "%b" "${1//%/%%}" >> "$HTMLFILE" } out() { @@ -975,8 +975,17 @@ fileout() { # ID, SEVERITY, FINDING, CVE, CWE, HINT ################### FILE FORMATING END ######################### html_header() { + local fname_prefix="$1" + if "$HTMLHEADER"; then - rm -f "$HTMLFILE" + [[ -z "$fname_prefix" ]] && fname_prefix="$NODE"_"$PORT" + if [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]]; then + rm -f "$HTMLFILE" + elif [[ -z "$HTMLFILE" ]]; then + HTMLFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".html) + else + HTMLFILE=$HTMLFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".html) + fi out_html "\n" out_html "\n" out_html "\n" @@ -991,6 +1000,15 @@ html_header() { return 0 } +html_banner() { + if "$QUIET" && "$HTMLHEADER"; then + out_html "## Scan started as: \"$PROG_NAME $CMDLINE\"\n" + out_html "## at $HNAME:$OPENSSL_LOCATION\n" + out_html "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\n" + out_html "## version openssl: \"$OSSL_VER\" from \"$OSSL_BUILD_DATE\")\n\n" + fi +} + html_footer() { if "$HTMLHEADER"; then out_html "\n" @@ -11374,6 +11392,7 @@ file output options (can also be preset via environment variables): --jsonfile-pretty additional pretty structured output as JSON to the specified file --csv additional output of findings to CSV file in cwd --csvfile additional output as CSV to the specified file + --html additional output as HTML to file --htmlfile additional output as HTML to the specifed file --hints additional hints to findings --severity severities with lower level will be filtered for CSV+JSON, possible values @@ -12356,10 +12375,12 @@ run_mass_testing_parallel() { run_mass_testing() { local cmdline="" local global_cmdline=${CMDLINE%%--file*} + local html_header="" if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then fatal "Can't read file \"$FNAME\"" "2" fi + [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && html_header="--no-html-header" pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n" APPEND=false # Make sure we close out our files @@ -12367,7 +12388,7 @@ run_mass_testing() { cmdline=$(filter_input "$cmdline") [[ -z "$cmdline" ]] && continue [[ "$cmdline" == "EOF" ]] && break - cmdline="$0 $global_cmdline --warnings=batch -q --no-html-header --append $cmdline" + cmdline="$0 $global_cmdline --warnings=batch -q $html_header --append $cmdline" draw_line "=" $((TERM_WIDTH / 2)); outln; outln "$cmdline" $cmdline @@ -12783,13 +12804,13 @@ parse_cmd_line() { [[ $? -eq 0 ]] && shift do_csv=true ;; + --html) + do_html=true + ;; # DEFINITION of HTMLFILE is not arg specified: automagically in parse_hn_port() + # following does the same but we can specify a file location additionally --htmlfile) HTMLFILE=$(parse_opt_equal_sign "$1" "$2") [[ $? -eq 0 ]] && shift - if [[ -d "$HTMLFILE" ]]; then - pr_warningln_term_term "$HTMLFILE exists and is a directory" - exit -6 - fi do_html=true ;; --no-html-header) @@ -12978,7 +12999,18 @@ lets_roll() { initialize_globals parse_cmd_line "$@" -html_header +if ! "$do_mass_testing" || ( [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] ); then + if "$do_display_only"; then + html_header "local-ciphers" + elif "$do_mass_testing"; then + html_header + elif "$do_mx_all_ips"; then + html_header "mx-$URI" + else + parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now + html_header + fi +fi get_install_dir set_color_functions maketempf @@ -13003,6 +13035,7 @@ if $do_mass_testing; then exit $? fi +html_banner #TODO: there shouldn't be the need for a special case for --mx, only the ip adresses we would need upfront and the do-parser if $do_mx_all_ips; then query_globals # if we have just 1x "do_*" --> we do a standard run -- otherwise just the one specified @@ -13010,7 +13043,6 @@ if $do_mx_all_ips; then run_mx_all_ips "${URI}" $PORT # we should reduce run_mx_all_ips to the stuff neccessary as ~15 lines later we have sililar code ret=$? else - parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now prepare_logging if ! determine_ip_addresses; then fatal "No IP address could be determined" 2 From 48088bbceb0caab4e420246c7fceb3c6aafc5769 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 14 Feb 2017 13:44:03 -0500 Subject: [PATCH 11/12] Cleanup Rearrange code so that in the case of just a single test, `parse_hn_port()` is not called earlier than it was previously unless it needs to be called in order to create the HTML file name. Doing this ensures that the banner is displayed even if the `$URI` cannot be parsed (except in the case that the `$URI` needs to be parsed in order to create a file name) and that any error messages created by `parse_hn_port()` will be included in the HTML, if possible. --- testssl.sh | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/testssl.sh b/testssl.sh index 20ef416..880e049 100755 --- a/testssl.sh +++ b/testssl.sh @@ -977,26 +977,24 @@ fileout() { # ID, SEVERITY, FINDING, CVE, CWE, HINT html_header() { local fname_prefix="$1" - if "$HTMLHEADER"; then - [[ -z "$fname_prefix" ]] && fname_prefix="$NODE"_"$PORT" - if [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]]; then - rm -f "$HTMLFILE" - elif [[ -z "$HTMLFILE" ]]; then - HTMLFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".html) - else - HTMLFILE=$HTMLFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".html) - fi - out_html "\n" - out_html "\n" - out_html "\n" - out_html "\n" - out_html "\n" - out_html "\n" - out_html "testssl.sh\n" - out_html "\n" - out_html "\n" - out_html "
\n"
+     [[ -z "$fname_prefix" ]] && fname_prefix="$NODE"_"$PORT"
+     if [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]]; then
+          rm -f "$HTMLFILE"
+     elif [[ -z "$HTMLFILE" ]]; then
+          HTMLFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".html)
+     else
+          HTMLFILE=$HTMLFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".html)
      fi
+     out_html "\n"
+     out_html "\n"
+     out_html "\n"
+     out_html "\n"
+     out_html "\n"
+     out_html "\n"
+     out_html "testssl.sh\n"
+     out_html "\n"
+     out_html "\n"
+     out_html "
\n"
      return 0
 }
 
@@ -12999,7 +12997,8 @@ lets_roll() {
 
 initialize_globals
 parse_cmd_line "$@"
-if ! "$do_mass_testing" || ( [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] ); then
+! "$do_html" && HTMLHEADER=false
+if "$HTMLHEADER" && ( ! "$do_mass_testing" || ( [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] ) ); then
      if "$do_display_only"; then
           html_header "local-ciphers"
      elif "$do_mass_testing"; then
@@ -13007,7 +13006,7 @@ if ! "$do_mass_testing" || ( [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] ); t
      elif "$do_mx_all_ips"; then
           html_header "mx-$URI"
      else
-          parse_hn_port "${URI}"    # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now
+          ( [[ -z "$HTMLFILE" ]] || [[ -d "$HTMLFILE" ]] ) && parse_hn_port "${URI}"    # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now
           html_header
      fi
 fi
@@ -13043,6 +13042,7 @@ if $do_mx_all_ips; then
      run_mx_all_ips "${URI}" $PORT # we should reduce run_mx_all_ips to the stuff neccessary as ~15 lines later we have sililar code
      ret=$?
 else
+     [[ -z "$NODE" ]] && parse_hn_port "${URI}"                                 # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now
      prepare_logging
      if ! determine_ip_addresses; then
           fatal "No IP address could be determined" 2

From 4b1435f958654d5ebdcac67a566058fac2d31fee Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Fri, 17 Feb 2017 16:40:50 -0500
Subject: [PATCH 12/12] Make link from redirect URL

If the HTTP Status Code includes a redirect URL, then make the URL a hyper link in the HTTP output.
---
 testssl.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testssl.sh b/testssl.sh
index 88cdb95..2283d2c 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -1423,7 +1423,7 @@ run_http_header() {
      case $HTTP_STATUS_CODE in
           301|302|307|308)
                redirect=$(grep -a '^Location' $HEADERFILE | sed 's/Location: //' | tr -d '\r\n')
-               out ", redirecting to \"$redirect\""
+               out ", redirecting to \""; pr_url "$redirect"; out "\""
                if [[ $redirect == "http://"* ]]; then
                     pr_svrty_high " -- Redirect to insecure URL (NOT ok)"
                     fileout "HTTP_STATUS_CODE" "HIGH" \, "Redirect to insecure URL. Url: \"$redirect\""