From 86c81f227699f231afe8f606888f983009bc458b Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 29 Mar 2017 11:16:09 -0400 Subject: [PATCH 01/10] Use CHILD_MASS_TESTING environment variable This PR introduces the environment variable `CHILD_MASS_TESTING`, and uses it as an indicator that testssl.sh is running as a child within mass testing rather than using the `$APPEND` flag. It also makes a number of other changes to make the handling, of HTML, CSV, JSON, and log files consistent, and it fixes a number of bugs related to the generation of these files when mass testing is being performed. Please let me know if you disagree with any of the changes in this PR, or if you would prefer that it be broken up into multiple smaller PRs. Some of the changes are as follows: - When the `$APPEND` flag is true, all of these files are appended to and headers and footers are omitted. (Perhaps this should be changed. Appending to a log file isn't an issue, but appending to a JSON or HTML file without including headers or footers seems to just create an improperly formatted file). - Following the code in `prepare_logging()`, an error is printed and the program stops if the `$APPEND` flag is false and one of the files to be written to already exists. Some of the bugs fixed: Creating log files did not work with mass testing: - If `--logfile ` is used, then the parent and each child try to write to "logfile". - If `--logging` is used, then a log file is created for each child, but an oddly-named log file is also created for the parent. The one created by the parent contains the entire output. Plain JSON files: - When `--jsonfile ` is run, there is no comma separating the final finding for one child and the first finding for the next child. Pretty JSON files: - When `--jsonfile-pretty ` is called without mass testing, the "target host" line is empty, since `$NODE` has not yet been set. - When `--jsonfile ` is run with mass testing, there is no comma separating the final finding for one child and the first finding for the next child. In addition, `fileout_pretty_json_banner()` is never called, and the entries for individual tests have insufficient information to determine what is being tested (it lists "service" and "ip", but not port number). For the final issue, when mass testing is being performed and all output is being placed in a single file, I have the parent call `fileout_pretty_json_banner()`, but tell `fileout_pretty_json_banner()` to not include a "target host" or "port", but then have each child include a "target host" or "port" (when the "service" and "ip" are being printed). --- testssl.sh | 224 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 136 insertions(+), 88 deletions(-) diff --git a/testssl.sh b/testssl.sh index 9bdcb7c..8ef0e1a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -186,6 +186,7 @@ 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 UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses SERVER_SIZE_LIMIT_BUG=false # Some servers have either a ClientHello total size limit or a 128 cipher limit (e.g. old ASAs) +CHILD_MASS_TESTING=${CHILD_MASS_TESTING:-false} # tuning vars, can not be set by a cmd line switch EXPERIMENTAL=${EXPERIMENTAL:-false} @@ -866,6 +867,8 @@ fileout_json_print_parameter() { } fileout_json_finding() { + local target + if "$do_json"; then "$FIRST_FINDING" || echo -n "," >> "$JSONFILE" echo -e " {" >> "$JSONFILE" @@ -884,9 +887,19 @@ fileout_json_finding() { if [[ $SERVER_COUNTER -gt 1 ]]; then echo " ," >> "$JSONFILE" fi - echo -e " { + if "$CHILD_MASS_TESTING" && ! "$JSONHEADER"; then + target="$NODE" + $do_mx_all_ips && target="$URI" + echo -e " { + \"target host\" : \"$target\", + \"port\" : \"$PORT\" \"service\" : \"$finding\", \"ip\" : \"$NODEIP\"," >> "$JSONFILE" + else + echo -e " { + \"service\" : \"$finding\", + \"ip\" : \"$NODEIP\"," >> "$JSONFILE" + fi $do_mx_all_ips && echo -e " \"hostname\" : \"$NODE\"," >> "$JSONFILE" else ("$FIRST_FINDING" && echo -n " {" >> "$JSONFILE") || echo -n ",{" >> "$JSONFILE" @@ -905,20 +918,33 @@ fileout_json_finding() { ##################### FILE FORMATING ######################### fileout_pretty_json_banner() { - echo -e " \"Invocation\" : \"$PROG_NAME $CMDLINE\", - \"at\" : \"$HNAME:$OPENSSL_LOCATION\", - \"version\" : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\", - \"openssl\" : \"$OSSL_VER from $OSSL_BUILD_DATE\", - \"target host\" : \"$target\", - \"port\" : \"$PORT\", - \"startTime\" : \"$START_TIME\", - \"scanResult\" : [" + local target + + if "$do_mass_testing"; then + echo -e " \"Invocation\" : \"$PROG_NAME $CMDLINE\", + \"at\" : \"$HNAME:$OPENSSL_LOCATION\", + \"version\" : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\", + \"openssl\" : \"$OSSL_VER from $OSSL_BUILD_DATE\", + \"startTime\" : \"$START_TIME\", + \"scanResult\" : [" + else + [[ -z "$NODE" ]] && parse_hn_port "${URI}" + # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place + target="$NODE" + $do_mx_all_ips && target="$URI" + + echo -e " \"Invocation\" : \"$PROG_NAME $CMDLINE\", + \"at\" : \"$HNAME:$OPENSSL_LOCATION\", + \"version\" : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\", + \"openssl\" : \"$OSSL_VER from $OSSL_BUILD_DATE\", + \"target host\" : \"$target\", + \"port\" : \"$PORT\", + \"startTime\" : \"$START_TIME\", + \"scanResult\" : [" + fi } fileout_banner() { - local target="$NODE" - $do_mx_all_ips && target="$URI" - #if ! "$APPEND"; then # if "$CSVHEADER"; then # : @@ -956,18 +982,18 @@ fileout() { json_header() { local fname_prefix + local filename_provided=false + + [[ -n "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]] && filename_provided=true # Similar to HTML: Don't create headers and footers in the following scenarios: # * no JSON/CSV output is being created. # * mass testing is being performed and each test will have its own file. # * this is an individual test within a mass test and all output is being placed in a single file. + ! "$do_json" && ! "$do_pretty_json" && JSONHEADER=false && return 0 + "$do_mass_testing" && ! "$filename_provided" && JSONHEADER=false && return 0 + "$CHILD_MASS_TESTING" && "$filename_provided" && JSONHEADER=false && return 0 - if ( ! "$do_json" && ! "$do_pretty_json" ) || \ - ( "$do_mass_testing" && ( [[ -z "$JSONFILE" ]] || [[ -d "$JSONFILE" ]] ) ) || \ - ( "$APPEND" && [[ -n "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]] ); then - JSONHEADER=false - return 0 - fi if "$do_display_only"; then fname_prefix="local-ciphers" elif "$do_mass_testing"; then @@ -975,19 +1001,22 @@ json_header() { elif "$do_mx_all_ips"; then fname_prefix="mx-$URI" else - ( [[ -z "$JSONFILE" ]] || [[ -d "$JSONFILE" ]] ) && parse_hn_port "${URI}" + ! "$filename_provided" && [[ -z "$NODE" ]] && parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place fname_prefix="${NODE}"_p"${PORT}" fi - if [[ -n "$JSONFILE" ]] && [[ ! -d "$JSONFILE" ]]; then - rm -f "$JSONFILE" - elif [[ -z "$JSONFILE" ]]; then + if [[ -z "$JSONFILE" ]]; then JSONFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".json) - else + elif [[ -d "$JSONFILE" ]]; then JSONFILE=$JSONFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".json) fi - "$do_json" && printf "[\n" > "$JSONFILE" - "$do_pretty_json" && printf "{\n" > "$JSONFILE" + if "$APPEND"; then + JSONHEADER=false + else + [[ -e "$JSONFILE" ]] && fatal "\"$JSONFILE\" exists. Either use \"--append\" or (re)move it" 1 + "$do_json" && printf "[\n" > "$JSONFILE" + "$do_pretty_json" && printf "{\n" > "$JSONFILE" + fi #FIRST_FINDING=false return 0 } @@ -995,14 +1024,15 @@ json_header() { csv_header() { local fname_prefix + local filename_provided=false + + [[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]] && filename_provided=true # CSV similar: - if ! "$do_csv" || \ - ( "$do_mass_testing" && ( [[ -z "$CSVFILE" ]] || [[ -d "$CSVFILE" ]] ) ) || \ - ( "$APPEND" && [[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]] ); then - CSVHEADER=false - return 0 - fi + ! "$do_csv" && CSVHEADER=false && return 0 + "$do_mass_testing" && ! "$filename_provided" && CSVHEADER=false && return 0 + "$CHILD_MASS_TESTING" && "$filename_provided" && CSVHEADER=false && return 0 + if "$do_display_only"; then fname_prefix="local-ciphers" elif "$do_mass_testing"; then @@ -1010,18 +1040,22 @@ csv_header() { elif "$do_mx_all_ips"; then fname_prefix="mx-$URI" else - ( [[ -z "$CSVFILE" ]] || [[ -d "$CSVFILE" ]] ) && parse_hn_port "${URI}" + ! "$filename_provided" && [[ -z "$NODE" ]] && parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place fname_prefix="${NODE}"_p"${PORT}" fi - if [[ -n "$CSVFILE" ]] && [[ ! -d "$CSVFILE" ]]; then - rm -f "$CSVFILE" - elif [[ -z "$CSVFILE" ]]; then + + if [[ -z "$CSVFILE" ]]; then CSVFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".csv) - else + elif [[ -d "$CSVFILE" ]]; then CSVFILE=$CSVFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".csv) fi - "$do_csv" && echo "\"id\",\"fqdn/ip\",\"port\",\"severity\",\"finding\",\"cve\",\"cwe\",\"hint\"" > "$CSVFILE" + if "$APPEND"; then + CSVHEADER=false + else + [[ -e "$CSVFILE" ]] && fatal "\"$CSVFILE\" exists. Either use \"--append\" or (re)move it" 1 + echo "\"id\",\"fqdn/ip\",\"port\",\"severity\",\"finding\",\"cve\",\"cwe\",\"hint\"" > "$CSVFILE" + fi return 0 } @@ -1030,17 +1064,17 @@ csv_header() { html_header() { local fname_prefix + local filename_provided=false + + [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && filename_provided=true # Don't create HTML headers and footers in the following scenarios: # * HTML output is not being created. # * mass testing is being performed and each test will have its own HTML file. # * this is an individual test within a mass test and all HTML output is being placed in a single file. - if ! "$do_html" || \ - ( "$do_mass_testing" && ( [[ -z "$HTMLFILE" ]] || [[ -d "$HTMLFILE" ]] ) ) || \ - ( "$APPEND" && [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] ); then - HTMLHEADER=false - return 0 - fi + ! "$do_html" && HTMLHEADER=false && return 0 + "$do_mass_testing" && ! "$filename_provided" && HTMLHEADER=false && return 0 + "$CHILD_MASS_TESTING" && "$filename_provided" && HTMLHEADER=false && return 0 if "$do_display_only"; then fname_prefix="local-ciphers" @@ -1049,33 +1083,36 @@ html_header() { elif "$do_mx_all_ips"; then fname_prefix="mx-$URI" else - ( [[ -z "$HTMLFILE" ]] || [[ -d "$HTMLFILE" ]] ) && parse_hn_port "${URI}" + ! "$filename_provided" && [[ -z "$NODE" ]] && parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now --> wrong place fname_prefix="${NODE}"_p"${PORT}" fi - if [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]]; then - rm -f "$HTMLFILE" - elif [[ -z "$HTMLFILE" ]]; then + if [[ -z "$HTMLFILE" ]]; then HTMLFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".html) - else + elif [[ -d "$HTMLFILE" ]]; then HTMLFILE=$HTMLFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".html) fi - html_out "\n" - html_out "\n" - html_out "\n" - html_out "\n" - html_out "\n" - html_out "\n" - html_out "testssl.sh\n" - html_out "\n" - html_out "\n" - html_out "
\n"
+     if "$APPEND"; then
+          HTMLHEADER=false
+     else
+          [[ -e "$HTMLFILE" ]] && fatal "\"$HTMLFILE\" exists. Either use \"--append\" or (re)move it" 1
+          html_out "\n"
+          html_out "\n"
+          html_out "\n"
+          html_out "\n"
+          html_out "\n"
+          html_out "\n"
+          html_out "testssl.sh\n"
+          html_out "\n"
+          html_out "\n"
+          html_out "
\n"
+     fi
      return 0
 }
 
 html_banner() {
-     if "$APPEND" && "$HTMLHEADER"; then
+     if "$CHILD_MASS_TESTING" && "$HTMLHEADER"; then
           html_out "## Scan started as: \"$PROG_NAME $CMDLINE\"\n"
           html_out "## at $HNAME:$OPENSSL_LOCATION\n"
           html_out "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\n"
@@ -10946,7 +10983,8 @@ mybanner() {
      local openssl_location="$(which $OPENSSL)"
      local cwd=""
 
-     $QUIET && return
+     "$QUIET" && return
+     "$CHILD_MASS_TESTING" && return
      OPENSSL_NR_CIPHERS=$(count_ciphers "$($OPENSSL ciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>/dev/null)")
      [[ -z "$GIT_REL" ]] && \
           idtag="$CVS_REL" || \
@@ -11133,30 +11171,34 @@ parse_hn_port() {
 # arg1: for testing mx records name we put a name of logfile in here, otherwise we get strange file names
 prepare_logging() {
      local fname_prefix="$1"
+     local filename_provided=false
+
+     [[ -n "$LOGFILE" ]] && [[ ! -d "$LOGFILE" ]] && filename_provided=true
+
+     # Similar to html_header():
+     ! "$do_logging" && return 0
+     "$do_mass_testing" && ! "$filename_provided" && return 0
+     "$CHILD_MASS_TESTING" && "$filename_provided" && return 0
 
      [[ -z "$fname_prefix" ]] && fname_prefix="${NODE}"_p"${PORT}"
 
-     if "$do_logging"; then
-          if [[ -z "$LOGFILE" ]]; then
-               LOGFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".log)
-          elif [[ -d "$LOGFILE" ]]; then
-               # actually we were instructed to place all files in a DIR instead of the current working dir
-               LOGFILE=$LOGFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".log)
-          else
-               : # just for clarity: a log file was specified, no need to do anything else
-          fi
-
-          if ! "$APPEND"; then
-               [[ -e $LOGFILE ]] && fatal "\"$LOGFILE\" exists. Either use \"--append\" or (re)move it" 1
-          else
-               >$LOGFILE
-          fi
-          tmln_out "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE}
-          tmln_out "## at $HNAME:$OPENSSL_LOCATION" >>${LOGFILE}
-          tmln_out "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE" >>${LOGFILE}
-          tmln_out "## version openssl: \"$OSSL_VER\" from \"$OSSL_BUILD_DATE\")\n" >>${LOGFILE}
-          exec > >(tee -a ${LOGFILE})
+     if [[ -z "$LOGFILE" ]]; then
+          LOGFILE=$fname_prefix-$(date +"%Y%m%d-%H%M".log)
+     elif [[ -d "$LOGFILE" ]]; then
+          # actually we were instructed to place all files in a DIR instead of the current working dir
+          LOGFILE=$LOGFILE/$fname_prefix-$(date +"%Y%m%d-%H%M".log)
+     else
+          : # just for clarity: a log file was specified, no need to do anything else
      fi
+
+     if ! "$APPEND"; then
+          [[ -e $LOGFILE ]] && fatal "\"$LOGFILE\" exists. Either use \"--append\" or (re)move it" 1
+     fi
+     tmln_out "## Scan started as: \"$PROG_NAME $CMDLINE\"" >>${LOGFILE}
+     tmln_out "## at $HNAME:$OPENSSL_LOCATION" >>${LOGFILE}
+     tmln_out "## version testssl: $VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE" >>${LOGFILE}
+     tmln_out "## version openssl: \"$OSSL_VER\" from \"$OSSL_BUILD_DATE\")\n" >>${LOGFILE}
+     exec > >(tee -a ${LOGFILE})
 }
 
 
@@ -11777,11 +11819,11 @@ run_mass_testing_parallel() {
           cmdline=$(filter_input "$cmdline")
           [[ -z "$cmdline" ]] && continue
           [[ "$cmdline" == "EOF" ]] && break
-          cmdline="$0 $global_cmdline --warnings=batch -q $cmdline"
+          cmdline="$0 $global_cmdline --warnings=batch $cmdline"
           draw_line "=" $((TERM_WIDTH / 2)); outln;
           determine_logfile
           outln "$cmdline"
-          $cmdline >$LOGFILE &
+          CHILD_MASS_TESTING=true $cmdline >$LOGFILE &
           sleep $PARALLEL_SLEEP
      done < "$FNAME"
      return $?
@@ -11790,6 +11832,7 @@ run_mass_testing_parallel() {
 
 run_mass_testing() {
      local cmdline=""
+     local first=true
      local global_cmdline=${CMDLINE%%--file*}
 
      if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then
@@ -11797,15 +11840,19 @@ run_mass_testing() {
      fi
 
      pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n"
-     APPEND=false # Make sure we close out our files
      while read cmdline; do
           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 $cmdline"
           draw_line "=" $((TERM_WIDTH / 2)); outln;
           outln "$cmdline"
-          $cmdline
+          if ! "$first" && "$JSONHEADER"; then
+               "$do_pretty_json" && echo "          ," >> "$JSONFILE"
+               "$do_json" && echo -n "," >> "$JSONFILE"
+          fi
+          CHILD_MASS_TESTING=true $cmdline
+          first=false
      done < "${FNAME}"
      return $?
 }
@@ -12453,19 +12500,20 @@ check_proxy
 check4openssl_oldfarts
 check_bsd_mount
 
-if $do_display_only; then
+if "$do_display_only"; then
      prettyprint_local "$PATTERN2SHOW"
      exit $?
 fi
 
-if $do_mass_testing; then
+fileout_banner
+
+if "$do_mass_testing"; then
      prepare_logging
      run_mass_testing
      exit $?
 fi
 
 html_banner
-fileout_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

From 04f86f94697c03c5560705210b11898e4baf185d Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Wed, 29 Mar 2017 11:22:29 -0400
Subject: [PATCH 02/10] Fix indentation of JSON pretty banner

---
 testssl.sh | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 8ef0e1a..7963bf6 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -921,26 +921,26 @@ fileout_pretty_json_banner() {
      local target
 
      if "$do_mass_testing"; then
-          echo -e "          \"Invocation\"  : \"$PROG_NAME $CMDLINE\",
-               \"at\"          : \"$HNAME:$OPENSSL_LOCATION\",
-               \"version\"     : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\",
-               \"openssl\"     : \"$OSSL_VER from $OSSL_BUILD_DATE\",
-               \"startTime\"   : \"$START_TIME\",
-               \"scanResult\"  : ["
+        echo -e "          \"Invocation\"  : \"$PROG_NAME $CMDLINE\",
+          \"at\"          : \"$HNAME:$OPENSSL_LOCATION\",
+          \"version\"     : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\",
+          \"openssl\"     : \"$OSSL_VER from $OSSL_BUILD_DATE\",
+          \"startTime\"   : \"$START_TIME\",
+          \"scanResult\"  : ["
      else
-          [[ -z "$NODE" ]] && parse_hn_port "${URI}"
-          # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now  --> wrong place
-          target="$NODE"
-          $do_mx_all_ips && target="$URI"
+        [[ -z "$NODE" ]] && parse_hn_port "${URI}"
+        # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now  --> wrong place
+        target="$NODE"
+        $do_mx_all_ips && target="$URI"
 
-          echo -e "          \"Invocation\"  : \"$PROG_NAME $CMDLINE\",
-               \"at\"          : \"$HNAME:$OPENSSL_LOCATION\",
-               \"version\"     : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\",
-               \"openssl\"     : \"$OSSL_VER from $OSSL_BUILD_DATE\",
-               \"target host\" : \"$target\",
-               \"port\"        : \"$PORT\",
-               \"startTime\"   : \"$START_TIME\",
-               \"scanResult\"  : ["
+        echo -e "          \"Invocation\"  : \"$PROG_NAME $CMDLINE\",
+          \"at\"          : \"$HNAME:$OPENSSL_LOCATION\",
+          \"version\"     : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\",
+          \"openssl\"     : \"$OSSL_VER from $OSSL_BUILD_DATE\",
+          \"target host\" : \"$target\",
+          \"port\"        : \"$PORT\",
+          \"startTime\"   : \"$START_TIME\",
+          \"scanResult\"  : ["
      fi
 }
 

From e7c0ca13f673935672d1082ff5c70e4d6b30f17d Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Wed, 29 Mar 2017 11:41:23 -0400
Subject: [PATCH 03/10] Remove tmp.json files after use

Remove tmp.json files are use so that testssl.sh doesn't complain that they already exist.
---
 t/01_badssl.com.t | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/t/01_badssl.com.t b/t/01_badssl.com.t
index 8c91e07..01ca476 100755
--- a/t/01_badssl.com.t
+++ b/t/01_badssl.com.t
@@ -16,6 +16,7 @@ my (
 pass("Running testssl.sh against badssl.com to create a baseline (may take 2~3 minutes)"); $tests++;
 my $okout = `./testssl.sh -S -e -U --jsonfile tmp.json --color 0 badssl.com`;
 my $okjson = json('tmp.json');
+unlink 'tmp.json';
 cmp_ok(@$okjson,'>',10,"We have more then 10 findings"); $tests++;
 
 # Expiration
@@ -23,6 +24,7 @@ pass("Running testssl against expired.badssl.com"); $tests++;
 $out = `./testssl.sh -S --jsonfile tmp.json --color 0 expired.badssl.com`;
 like($out, qr/Certificate Expiration\s+expired\!/,"The certificate should be expired"); $tests++;
 $json = json('tmp.json');
+unlink 'tmp.json';
 $found = 0;
 foreach my $f ( @$json ) {
 	if ( $f->{id} eq "expiration" ) {
@@ -39,6 +41,7 @@ pass("Running testssl against self-signed.badssl.com"); $tests++;
 $out = `./testssl.sh -S --jsonfile tmp.json --color 0 self-signed.badssl.com`;
 like($out, qr/Certificate Expiration\s+\d+/,"The certificate should not be expired"); $tests++;
 $json = json('tmp.json');
+unlink 'tmp.json';
 $found = 0;
 foreach my $f ( @$json ) {
 	if ( $f->{id} eq "expiration" ) {
@@ -79,6 +82,7 @@ is($found,1,"We had a finding for this in the JSON output"); $tests++;
 #$out = `./testssl.sh -S --jsonfile tmp.json --color 0 wrong.host.badssl.com`;
 #unlike($out, qr/Certificate Expiration\s+expired\!/,"The certificate should not be expired"); $tests++;
 #$json = json('tmp.json');
+#unlink 'tmp.json';
 #$found = 0;
 #foreach my $f ( @$json ) {
 #	if ( $f->{id} eq "expiration" ) {
@@ -95,6 +99,7 @@ pass("Running testssl against incomplete-chain.badssl.com"); $tests++;
 $out = `./testssl.sh -S --jsonfile tmp.json --color 0 incomplete-chain.badssl.com`;
 like($out, qr/Chain of trust.*?NOT ok\s+\(chain incomplete\)/,"Chain of trust should fail because of incomplete"); $tests++;
 $json = json('tmp.json');
+unlink 'tmp.json';
 $found = 0;
 foreach my $f ( @$json ) {
 	if ( $f->{id} eq "chain_of_trust" ) {
@@ -113,6 +118,7 @@ is($found,1,"We had a finding for this in the JSON output"); $tests++;
 #$out = `./testssl.sh -e -U --jsonfile tmp.json --color 0 cbc.badssl.com`;
 #like($out, qr/Chain of trust.*?NOT ok\s+\(chain incomplete\)/,"Chain of trust should fail because of incomplete"); $tests++;
 #$json = json('tmp.json');
+#unlink 'tmp.json';
 #$found = 0;
 #foreach my $f ( @$json ) {
 #	if ( $f->{id} eq "chain_of_trust" ) {
@@ -132,4 +138,4 @@ sub json($) {
 	$file = `cat $file`;
 	unlink $file;
 	return from_json($file);
-}
\ No newline at end of file
+}

From 172337451150d0aa1bcb3f0a1d98b8886fd4aa45 Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Wed, 29 Mar 2017 11:42:09 -0400
Subject: [PATCH 04/10] Remove tmp.file files after use

Remove tmp.json files are use so that testssl.sh doesn't complain that they already exist.
---
 t/100_report_structure.t | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/t/100_report_structure.t b/t/100_report_structure.t
index 5fdfb47..b933b4b 100644
--- a/t/100_report_structure.t
+++ b/t/100_report_structure.t
@@ -19,6 +19,7 @@ $tests = 0;
 pass("Running testssl.sh against badssl.com to create a JSON report with severity level equal greater than LOW (may take 2~3 minutes)"); $tests++;
 $out = `./testssl.sh -S -e -U --jsonfile tmp.json --severity LOW --color 0 badssl.com`;
 $json = json('tmp.json');
+unlink 'tmp.json';
 $found = 0;
 cmp_ok(@$json,'>',0,"At least 1 finding is expected"); $tests++;
 foreach my $f ( @$json ) {
@@ -33,6 +34,7 @@ is($found,0,"We should not have any finding with INFO level"); $tests++;
 pass("Running testssl.sh against badssl.com to create a JSON-PRETTY report with severity level equal greater than LOW (may take 2~3 minutes)"); $tests++;
 $out = `./testssl.sh -S -e -U --jsonfile-pretty tmp.json --severity LOW --color 0 badssl.com`;
 $json_pretty = json('tmp.json');
+unlink 'tmp.json';
 $found = 0;
 my $vulnerabilities = $json_pretty->{scanResult}->[0]->{vulnerabilities};
 foreach my $f ( @$vulnerabilities ) {
@@ -50,4 +52,4 @@ sub json($) {
     $file = `cat $file`;
     unlink $file;
     return from_json($file);
-}
\ No newline at end of file
+}

From 603f03e79a76289df74057bdd8f5ffad29b68cca Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Wed, 29 Mar 2017 11:43:03 -0400
Subject: [PATCH 05/10] Remove tmp.json files after use

Remove tmp.json files after use so that testssl.sh doesn't complain that they already exist.
---
 t/11_hpkp.t | 1 +
 1 file changed, 1 insertion(+)

diff --git a/t/11_hpkp.t b/t/11_hpkp.t
index b701558..dc5464b 100755
--- a/t/11_hpkp.t
+++ b/t/11_hpkp.t
@@ -16,6 +16,7 @@ my (
 pass("Running testssl.sh against ssl.sectionzero.org"); $tests++;
 $out = `./testssl.sh -H --jsonfile tmp.json --color 0 ssl.sectionzero.org`;
 $json = json('tmp.json');
+unlink 'tmp.json';
 
 # It is better to have findings in a hash
 # Look for a host cert match in the process.

From ba2a75b09395d83dbf81dd0b62b25acc321d9432 Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Thu, 30 Mar 2017 10:08:26 -0400
Subject: [PATCH 06/10] Cleanup variable definitions in run_server_defaults()

In `run_server_defaults()` the variable `success` is defined twice, once an an ordinary variable and once as an array. The PR removes the incorrect definition. It also removes the definitions of some variables that are no longer used and reorganizes the definitions so that each line has only one variable type.

I also noticed a typo later in `run_server_defaults()` and corrected it.
---
 testssl.sh | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 9bdcb7c..c24286b 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -6080,18 +6080,16 @@ certificate_info() {
 
 
 run_server_defaults() {
-     local ciph match_found newhostcert sni
-     local sessticket_str=""
-     local lifetime unit
-     local line
+     local ciph newhostcert sni
+     local match_found
+     local sessticket_str="" lifetime unit
      local -i i n
      local -i certs_found=0
      local -a previous_hostcert previous_intermediates keysize cipher
      local -a ocsp_response ocsp_response_status sni_used
-     local -a ciphers_to_test success
+     local -a ciphers_to_test
+     local -a -i success
      local cn_nosni cn_sni sans_nosni sans_sni san tls_extensions
-     local alpn_proto alpn="" alpn_list_len_hex alpn_extn_len_hex success
-     local -i alpn_list_len alpn_extn_len
 
      # Try each public key type once:
      # ciphers_to_test[1]: cipher suites using certificates with RSA signature public keys
@@ -6249,7 +6247,7 @@ run_server_defaults() {
           unit=$(grep -a lifetime <<< "$sessticket_str" | sed -e 's/^.*'"$lifetime"'//' -e 's/[ ()]//g')
           out "$lifetime $unit "
           prln_svrty_low "(PFS requires session ticket keys to be rotated <= daily)"
-          fileout "session_ticket" "LOW" "TLS session tickes RFC 5077 valid for $lifetime $unit (PFS requires session ticket keys to be rotated at least daily)"
+          fileout "session_ticket" "LOW" "TLS session ticket RFC 5077 valid for $lifetime $unit (PFS requires session ticket keys to be rotated at least daily)"
      fi
 
      pr_bold " SSL Session ID support       "

From 73a24cba27366a335ac6749e798981e4b6e896c2 Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Thu, 30 Mar 2017 10:27:08 -0400
Subject: [PATCH 07/10] Correct indentation in run_server_defaults()

This second commit doesn't make any changes to the code, it just corrects the indentation.
---
 testssl.sh | 182 +++++++++++++++++++++++++++--------------------------
 1 file changed, 92 insertions(+), 90 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index c24286b..5917122 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -6102,11 +6102,11 @@ run_server_defaults() {
      ciphers_to_test[1]=""
      ciphers_to_test[2]=""
      for ciph in $(colon_to_spaces $($OPENSSL ciphers "aRSA")); do
-         if grep -q "\-RSA\-" <<<$ciph; then
-             ciphers_to_test[1]="${ciphers_to_test[1]}:$ciph"
-         else
-             ciphers_to_test[2]="${ciphers_to_test[2]}:$ciph"
-         fi
+          if grep -q "\-RSA\-" <<<$ciph; then
+               ciphers_to_test[1]="${ciphers_to_test[1]}:$ciph"
+          else
+               ciphers_to_test[2]="${ciphers_to_test[2]}:$ciph"
+          fi
      done
      [[ -n "${ciphers_to_test[1]}" ]] && ciphers_to_test[1]="${ciphers_to_test[1]:1}"
      [[ -n "${ciphers_to_test[2]}" ]] && ciphers_to_test[2]="${ciphers_to_test[2]:1}"
@@ -6117,94 +6117,96 @@ run_server_defaults() {
      ciphers_to_test[7]="aGOST"
 
      for (( n=1; n <= 14 ; n++ )); do
-         # Some servers use a different certificate if the ClientHello
-         # specifies TLSv1.1 and doesn't include a server name extension.
-         # So, for each public key type for which a certificate was found,
-         # try again, but only with TLSv1.1 and without SNI.
-         if [[ $n -ge 8 ]]; then
-              ciphers_to_test[n]=""
-              [[ ${success[n-7]} -eq 0 ]] && ciphers_to_test[n]="${ciphers_to_test[n-7]}"
-         fi
+          # Some servers use a different certificate if the ClientHello
+          # specifies TLSv1.1 and doesn't include a server name extension.
+          # So, for each public key type for which a certificate was found,
+          # try again, but only with TLSv1.1 and without SNI.
+          if [[ $n -ge 8 ]]; then
+               ciphers_to_test[n]=""
+               [[ ${success[n-7]} -eq 0 ]] && ciphers_to_test[n]="${ciphers_to_test[n-7]}"
+          fi
 
-         if [[ -n "${ciphers_to_test[n]}" ]] && [[ $(count_ciphers $($OPENSSL ciphers "${ciphers_to_test[n]}" 2>>$ERRFILE)) -ge 1 ]]; then
-             if [[ $n -ge 8 ]]; then
-                  sni="$SNI"
-                  SNI=""
-                  get_server_certificate "-cipher ${ciphers_to_test[n]}" "tls1_1"
-                  success[n]=$?
-                  SNI="$sni"
-             else
-                  get_server_certificate "-cipher ${ciphers_to_test[n]}"
-                  success[n]=$?
-             fi
-             if [[ ${success[n]} -eq 0 ]]; then
-                 cp "$TEMPDIR/$NODEIP.get_server_certificate.txt" $TMPFILE
-                 >$ERRFILE
-                 if [[ -z "$sessticket_str" ]]; then
-                     sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime)
-                 fi
+          if [[ -n "${ciphers_to_test[n]}" ]] && [[ $(count_ciphers $($OPENSSL ciphers "${ciphers_to_test[n]}" 2>>$ERRFILE)) -ge 1 ]]; then
+               if [[ $n -ge 8 ]]; then
+                    sni="$SNI"
+                    SNI=""
+                    get_server_certificate "-cipher ${ciphers_to_test[n]}" "tls1_1"
+                    success[n]=$?
+                    SNI="$sni"
+               else
+                    get_server_certificate "-cipher ${ciphers_to_test[n]}"
+                    success[n]=$?
+               fi
+               if [[ ${success[n]} -eq 0 ]]; then
+                    cp "$TEMPDIR/$NODEIP.get_server_certificate.txt" $TMPFILE
+                    >$ERRFILE
+                    if [[ -z "$sessticket_str" ]]; then
+                         sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime)
+                    fi
 
-                 # check whether the host's certificate has been seen before
-                 match_found=false
-                 i=1
-                 newhostcert=$(cat $HOSTCERT)
-                 while [[ $i -le $certs_found ]]; do
-                     if [[ "$newhostcert" == "${previous_hostcert[i]}" ]]; then
-                        match_found=true
-                        break;
-                     fi
-                     i=$((i + 1))
-                 done
-                 if ! "$match_found" && [[ $n -ge 8 ]] && [[ $certs_found -ne 0 ]]; then
-                     # A new certificate was found using TLSv1.1 without SNI.
-                     # Check to see if the new certificate should be displayed.
-                     # It should be displayed if it is either a match for the
-                     # $NODE being tested or if it has the same subject
-                     # (CN and SAN) as other certificates for this host.
-                     compare_server_name_to_cert "$NODE" "$HOSTCERT"
-                     [[ $? -ne 0 ]] && success[n]=0 || success[n]=1
+                    # check whether the host's certificate has been seen before
+                    match_found=false
+                    i=1
+                    newhostcert=$(cat $HOSTCERT)
+                    while [[ $i -le $certs_found ]]; do
+                         if [[ "$newhostcert" == "${previous_hostcert[i]}" ]]; then
+                              match_found=true
+                              break;
+                         fi
+                         i=$((i + 1))
+                    done
+                    if ! "$match_found" && [[ $n -ge 8 ]] && [[ $certs_found -ne 0 ]]; then
+                         # A new certificate was found using TLSv1.1 without SNI.
+                         # Check to see if the new certificate should be displayed.
+                         # It should be displayed if it is either a match for the
+                         # $NODE being tested or if it has the same subject
+                         # (CN and SAN) as other certificates for this host.
+                         compare_server_name_to_cert "$NODE" "$HOSTCERT"
+                         [[ $? -ne 0 ]] && success[n]=0 || success[n]=1
 
-                     if [[ ${success[n]} -ne 0 ]]; then
-                         cn_nosni="$(toupper "$(get_cn_from_cert $HOSTCERT)")"
-                         sans_nosni="$(toupper "$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | \
-                              tr ',' '\n' |  grep "DNS:" | sed -e 's/DNS://g' -e 's/ //g' | tr '\n' ' ')")"
+                         if [[ ${success[n]} -ne 0 ]]; then
+                              cn_nosni="$(toupper "$(get_cn_from_cert $HOSTCERT)")"
+                              sans_nosni="$(toupper "$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | \
+                                   grep -A2 "Subject Alternative Name" | tr ',' '\n' | grep "DNS:" | \
+                                   sed -e 's/DNS://g' -e 's/ //g' | tr '\n' ' ')")"
 
-                         echo "${previous_hostcert[1]}" > $HOSTCERT
-                         cn_sni="$(toupper "$(get_cn_from_cert $HOSTCERT)")"
+                              echo "${previous_hostcert[1]}" > $HOSTCERT
+                              cn_sni="$(toupper "$(get_cn_from_cert $HOSTCERT)")"
 
-                         # FIXME: Not sure what the matching rule should be. At
-                         # the moment, the no SNI certificate is considered a
-                         # match if the CNs are the same and the SANs (if
-                         # present) contain at least one DNS name in common.
-                         if [[ "$cn_nosni" == "$cn_sni" ]]; then
-                              sans_sni="$(toupper "$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A2 "Subject Alternative Name" | \
-                                       tr ',' '\n' |  grep "DNS:" | sed -e 's/DNS://g' -e 's/ //g' | tr '\n' ' ')")"
-                              if [[ "$sans_nosni" == "$sans_sni" ]]; then
-                                   success[n]=0
-                              else
-                                   for san in $sans_nosni; do
-                                        [[ " $sans_sni " =~ " $san " ]] && success[n]=0 && break
-                                   done
+                              # FIXME: Not sure what the matching rule should be. At
+                              # the moment, the no SNI certificate is considered a
+                              # match if the CNs are the same and the SANs (if
+                              # present) contain at least one DNS name in common.
+                              if [[ "$cn_nosni" == "$cn_sni" ]]; then
+                                   sans_sni="$(toupper "$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | \
+                                        grep -A2 "Subject Alternative Name" | tr ',' '\n' | grep "DNS:" | \
+                                        sed -e 's/DNS://g' -e 's/ //g' | tr '\n' ' ')")"
+                                   if [[ "$sans_nosni" == "$sans_sni" ]]; then
+                                        success[n]=0
+                                   else
+                                        for san in $sans_nosni; do
+                                             [[ " $sans_sni " =~ " $san " ]] && success[n]=0 && break
+                                        done
+                                   fi
                               fi
                          fi
-                     fi
-                     # If the certificate found for TLSv1.1 w/o SNI appears to
-                     # be for a different host, then set match_found to true so
-                     # that the new certificate will not be included in the output.
-                     [[ ${success[n]} -ne 0 ]] && match_found=true
-                 fi
-                 if ! "$match_found"; then
-                     certs_found=$(($certs_found + 1))
-                     cipher[certs_found]=${ciphers_to_test[n]}
-                     keysize[certs_found]=$(grep -aw "^Server public key is" $TMPFILE | sed -e 's/^Server public key is //' -e 's/bit//' -e 's/ //')
-                     ocsp_response[certs_found]=$(grep -aA 20 "OCSP response" $TMPFILE)
-                     ocsp_response_status[certs_found]=$(grep -a "OCSP Response Status" $TMPFILE)
-                     previous_hostcert[certs_found]=$newhostcert
-                     previous_intermediates[certs_found]=$(cat $TEMPDIR/intermediatecerts.pem)
-                     [[ $n -ge 8 ]] && sni_used[certs_found]="" || sni_used[certs_found]="$SNI"
-                 fi
-             fi
-         fi
+                         # If the certificate found for TLSv1.1 w/o SNI appears to
+                         # be for a different host, then set match_found to true so
+                         # that the new certificate will not be included in the output.
+                         [[ ${success[n]} -ne 0 ]] && match_found=true
+                    fi
+                    if ! "$match_found"; then
+                         certs_found=$(($certs_found + 1))
+                         cipher[certs_found]=${ciphers_to_test[n]}
+                         keysize[certs_found]=$(grep -aw "^Server public key is" $TMPFILE | sed -e 's/^Server public key is //' -e 's/bit//' -e 's/ //')
+                         ocsp_response[certs_found]=$(grep -aA 20 "OCSP response" $TMPFILE)
+                         ocsp_response_status[certs_found]=$(grep -a "OCSP Response Status" $TMPFILE)
+                         previous_hostcert[certs_found]=$newhostcert
+                         previous_intermediates[certs_found]=$(cat $TEMPDIR/intermediatecerts.pem)
+                         [[ $n -ge 8 ]] && sni_used[certs_found]="" || sni_used[certs_found]="$SNI"
+                    fi
+               fi
+          fi
      done
 
      determine_tls_extensions
@@ -6263,10 +6265,10 @@ run_server_defaults() {
 
      i=1
      while [[ $i -le $certs_found ]]; do
-         echo "${previous_hostcert[i]}" > $HOSTCERT
-         echo "${previous_intermediates[i]}" > $TEMPDIR/intermediatecerts.pem
-         certificate_info "$i" "$certs_found" "${cipher[i]}" "${keysize[i]}" "${ocsp_response[i]}" "${ocsp_response_status[i]}" "${sni_used[i]}"
-         i=$((i + 1))
+          echo "${previous_hostcert[i]}" > $HOSTCERT
+          echo "${previous_intermediates[i]}" > $TEMPDIR/intermediatecerts.pem
+          certificate_info "$i" "$certs_found" "${cipher[i]}" "${keysize[i]}" "${ocsp_response[i]}" "${ocsp_response_status[i]}" "${sni_used[i]}"
+          i=$((i + 1))
      done
 }
 

From d8a70370006441660b05cf494f325762b00debff Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Thu, 30 Mar 2017 12:37:41 -0400
Subject: [PATCH 08/10] Add missing comma

I did some testing with http://jsonlint.com/ and discovered a missing comma when massing testing is being performed and a single JSON file is being created.
---
 testssl.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/testssl.sh b/testssl.sh
index 7963bf6..d009372 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -892,7 +892,7 @@ fileout_json_finding() {
                  $do_mx_all_ips && target="$URI"
                  echo -e "          {
                     \"target host\"     : \"$target\",
-                    \"port\"            : \"$PORT\"
+                    \"port\"            : \"$PORT\",
                     \"service\"         : \"$finding\",
                     \"ip\"              : \"$NODEIP\","  >> "$JSONFILE"
             else

From 9f93d9d578a8a34c695d983f1d0a9d7d140cdf5c Mon Sep 17 00:00:00 2001
From: David Cooper 
Date: Thu, 30 Mar 2017 12:48:25 -0400
Subject: [PATCH 09/10] Move insertion of commas to a separate file

Create a separate function to insert the comma separators between findings for different tests within mass testing.
---
 testssl.sh | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index d009372..4697baa 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -956,6 +956,13 @@ fileout_banner() {
      #fi
 }
 
+fileout_separator() {
+     if "$JSONHEADER"; then
+          "$do_pretty_json" && echo "          ," >> "$JSONFILE"
+          "$do_json" && echo -n "," >> "$JSONFILE"
+     fi
+}
+
 fileout_footer() {
      if "$JSONHEADER"; then
           fileout_json_footer
@@ -11847,10 +11854,7 @@ run_mass_testing() {
           cmdline="$0 $global_cmdline --warnings=batch $cmdline"
           draw_line "=" $((TERM_WIDTH / 2)); outln;
           outln "$cmdline"
-          if ! "$first" && "$JSONHEADER"; then
-               "$do_pretty_json" && echo "          ," >> "$JSONFILE"
-               "$do_json" && echo -n "," >> "$JSONFILE"
-          fi
+          "$first" || fileout_separator
           CHILD_MASS_TESTING=true $cmdline
           first=false
      done < "${FNAME}"

From a480e5f699983207651aa0a8717dc395d13e6e52 Mon Sep 17 00:00:00 2001
From: Dirk 
Date: Fri, 31 Mar 2017 12:24:25 +0200
Subject: [PATCH 10/10] count_ciphers is now un-sed'ed, minor improvements

---
 testssl.sh | 98 +++++++++++++++++++++++++++---------------------------
 1 file changed, 49 insertions(+), 49 deletions(-)

diff --git a/testssl.sh b/testssl.sh
index 4697baa..44cf79f 100755
--- a/testssl.sh
+++ b/testssl.sh
@@ -742,7 +742,7 @@ pr_url()     { tm_out "$1"; html_out "$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
+###                          http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
 set_color_functions() {
      local ncurses_tput=true
 
@@ -845,7 +845,7 @@ fileout_section_header() {
     "$do_pretty_json" && FIRST_FINDING=true && (printf "%s%s\n" "$str" "$(fileout_json_section "$1")") >> "$JSONFILE"
 }
 
-fileout_section_footer() { 
+fileout_section_footer() {
     "$do_pretty_json" && printf "\n                    ]" >> "$JSONFILE"
     "$do_pretty_json" && "$1" && echo -e "\n          }" >> "$JSONFILE"
 }
@@ -1072,7 +1072,7 @@ csv_header() {
 html_header() {
      local fname_prefix
      local filename_provided=false
-     
+
      [[ -n "$HTMLFILE" ]] && [[ ! -d "$HTMLFILE" ]] && filename_provided=true
 
      # Don't create HTML headers and footers in the following scenarios:
@@ -1146,8 +1146,8 @@ if [[ $(uname) == "Linux" ]] ; then
      toupper() { echo -n "${1^^}" ;  }
      tolower() { echo -n "${1,,}" ;  }
 else
-     toupper() { echo -n "$1" | tr 'a-z' 'A-Z'; }
-     tolower() { echo -n "$1" | tr 'A-Z' 'a-z' ; }
+     toupper() { tr 'a-z' 'A-Z' <<< "$1"; }
+     tolower() { tr 'A-Z' 'a-z'  <<< "$1"; }
 fi
 
 debugme() {
@@ -1181,7 +1181,7 @@ count_words() {
 }
 
 count_ciphers() {
-     echo -n "$1" | sed 's/:/ /g' | wc -w | sed 's/ //g'
+     echo $(wc -w <<< "${1//:/ }")
 }
 
 actually_supported_ciphers() {
@@ -1476,7 +1476,6 @@ service_detection() {
           head $TMPFILE | egrep -aqw "Jive News|InterNetNews|NNRP|INN" && SERVICE=NNTP
           debugme head -50 $TMPFILE
      fi
-# FIXME: we can guess ports by port number if not properly recognized (and label it as guessed)
 
      out " Service detected:      $CORRECT_SPACES"
      case $SERVICE in
@@ -2494,7 +2493,7 @@ std_cipherlists() {
                     ;;
           esac
           tmpfile_handle $FUNCNAME.$debugname.txt
-          [[ $DEBUG -ge 1 ]] && outln " -- $1" || outln  #FIXME: should be in standard output at some time
+          [[ $DEBUG -ge 1 ]] && tmln_out " -- $1" || tmln_out
      else
           singlespaces=$(sed -e 's/ \+/ /g' -e 's/^ //' -e 's/ $//g' -e 's/  //g' <<< "$2")
           if [[ "$OPTIMAL_PROTO" == "-ssl2" ]]; then
@@ -10155,7 +10154,7 @@ run_beast(){
                fi
           else
                if ! "$vuln_beast" ; then
-                    prln_done_good " no CBC ciphers for $(toupper $proto) (OK)"
+                    prln_done_good "no CBC ciphers for $(toupper $proto) (OK)"
                     fileout "cbc_$proto" "OK" "BEAST: No CBC ciphers for $(toupper $proto)" "$cve" "$cwe"
                fi
           fi
@@ -10862,7 +10861,7 @@ EOF
 }
 
 maketempf() {
-     TEMPDIR=$(mktemp -d /tmp/ssltester.XXXXXX) || exit -6
+     TEMPDIR=$(mktemp -d /tmp/testssl.XXXXXX) || exit -6
      TMPFILE=$TEMPDIR/tempfile.txt || exit -6
      if [[ "$DEBUG" -eq 0 ]]; then
           ERRFILE="/dev/null"
@@ -10999,7 +10998,7 @@ mybanner() {
      bb1=$(cat <$LOGFILE &
+          # first=false
           sleep $PARALLEL_SLEEP
      done < "$FNAME"
      return $?
 }
 
 
-run_mass_testing() {
-     local cmdline=""
-     local first=true
-     local global_cmdline=${CMDLINE%%--file*}
-
-     if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then
-          fatal "Can't read file \"$FNAME\"" "2"
-     fi
-
-     pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n"
-     while read cmdline; do
-          cmdline=$(filter_input "$cmdline")
-          [[ -z "$cmdline" ]] && continue
-          [[ "$cmdline" == "EOF" ]] && break
-          cmdline="$0 $global_cmdline --warnings=batch $cmdline"
-          draw_line "=" $((TERM_WIDTH / 2)); outln;
-          outln "$cmdline"
-          "$first" || fileout_separator
-          CHILD_MASS_TESTING=true $cmdline
-          first=false
-     done < "${FNAME}"
-     return $?
-}
-
-
 
 # This initializes boolean global do_* variables. They keep track of what to do
 # -- as the name insinuates
@@ -12490,9 +12490,6 @@ ip=""
 lets_roll init
 initialize_globals
 parse_cmd_line "$@"
-json_header
-csv_header
-html_header
 get_install_dir
 set_color_functions
 maketempf
@@ -12503,6 +12500,9 @@ mybanner
 check_proxy
 check4openssl_oldfarts
 check_bsd_mount
+json_header
+csv_header
+html_header
 
 if "$do_display_only"; then
      prettyprint_local "$PATTERN2SHOW"