From 50672923733be7c6eb1b422258466038c493efdb Mon Sep 17 00:00:00 2001 From: "Dr. Adedayo Adetoye" Date: Thu, 21 Mar 2019 12:41:19 +0000 Subject: [PATCH] Fix: improvements to allow grouped scans - helpful for reporting --- go.sum | 6 ++ pkg/model/models.go | 55 +++++++++++++------ pkg/persistence.go | 12 ++-- pkg/scan-service.go | 130 +++++++++++++++++++++++++------------------- pkg/websocket.go | 122 ++++++++++++++++++++++------------------- 5 files changed, 195 insertions(+), 130 deletions(-) diff --git a/go.sum b/go.sum index 56f40c4..28d3e3d 100644 --- a/go.sum +++ b/go.sum @@ -62,11 +62,14 @@ github.com/adedayo/tcpscan v0.5.5/go.mod h1:6v2onzEsetkP35RqQnKhB5B5MLVV1w+eepgC github.com/adedayo/tcpscan v0.5.6 h1:EzNBxn2nIJ5WzuAFb5tmxUGX83Yc0Xg6YN1hn3bLMao= github.com/adedayo/tcpscan v0.5.6/go.mod h1:6v2onzEsetkP35RqQnKhB5B5MLVV1w+eepgCkXWQzwc= github.com/adedayo/tlsaudit v0.2.2/go.mod h1:qlilsuzRz/YHkPXQvhnKxLr0ROWQTEvLEihXq7nHAdk= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82 h1:9bAydALqAjBfPHd/eAiJBHnMZUYov8m2PkXVr+YGQeI= github.com/carlescere/scheduler v0.0.0-20170109141437-ee74d2f83d82/go.mod h1:tyA14J0sA3Hph4dt+AfCjPrYR13+vVodshQSM7km9qw= +github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg= @@ -135,8 +138,10 @@ github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 h1:d9qaMM+ODpCq+9We41//fu/sHsTnXcrqd1en3x+GKy4= @@ -167,6 +172,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 h1:Ggy3mWN4l3PUFPfSG0YB3n5fVYggzysUmiUQ89SnX6Y= gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8/go.mod h1:cKXr3E0k4aosgycml1b5z33BVV6hai1Kh7uDgFOkbcs= diff --git a/pkg/model/models.go b/pkg/model/models.go index 36ac19f..dec5f43 100644 --- a/pkg/model/models.go +++ b/pkg/model/models.go @@ -600,20 +600,43 @@ func GetCipherConfig(cipher uint16) (config CipherConfig, err error) { } //ScanRequest is a model to describe a given TLS Audit scan -type ScanRequest struct { - CIDRs []string +// type ScanRequest struct { +// CIDRs []string +// Config ScanConfig +// //Next two fields will be automatically set once scan starts +// Day string //Date the scan was run in the format yyyy-mm-dd +// ScanID string //Non-empty ScanID means this is a ScanRequest to resume an existing, possibly incomplete, scan +// } + +//ScanGroup is a grouping of CIDR ranges to be scanned with descriptions, useful for reporting +type ScanGroup struct { + Description string `yaml:"description"` //Freeform text used in reporting + CIDRRanges []string `yaml:"cidrRanges"` +} + +//AdvancedScanRequest is a model to describe a given TLS Audit scan +type AdvancedScanRequest struct { Config ScanConfig - Day string //Date the scan was run in the format yyyy-mm-dd - ScanID string //Non-empty ScanID means this is a ScanRequest to resume an existing, possibly incomplete, scan + //Next two fields will be automatically set once scan starts + Day string //Date the scan was run in the format yyyy-mm-dd + ScanID string //Non-empty ScanID means this is a ScanRequest to resume an existing, possibly incomplete, scan + ScanGroups []ScanGroup +} + +//GroupedHost exploded hosts from an associated ScanGroup +type GroupedHost struct { + ScanGroup ScanGroup + Hosts []string } //PersistedScanRequest persisted version of ScanRequest type PersistedScanRequest struct { - Request ScanRequest - Hosts []string - ScanStart time.Time - ScanEnd time.Time - Progress int + Request AdvancedScanRequest + GroupedHosts []GroupedHost + ScanStart time.Time + ScanEnd time.Time + Progress int + HostCount int } // //ServerResultSummary is a mini report of scan result @@ -626,7 +649,7 @@ type PersistedScanRequest struct { //ScanResultSummary is the summary of a scan result session type ScanResultSummary struct { - Request ScanRequest + Request AdvancedScanRequest ScanStart time.Time ScanEnd time.Time Progress int @@ -1443,12 +1466,12 @@ func scoreCipher(cipher, protocol uint16, scan ScanResult) (score string) { //TLSAuditConfig is the configuration of the nmap runner type TLSAuditConfig struct { - DailySchedules []string `yaml:"dailySchedules"` // in the format 13:45, 01:20 etc - ServicePort int `yaml:"servicePort"` - IsProduction bool `yaml:"isProduction"` - PacketsPerSecond int `yaml:"packetsPerSecond"` - Timeout int `yaml:"timeout"` - CIDRRanges []string `yaml:"cidrRanges"` + DailySchedules []string `yaml:"dailySchedules"` // in the format 13:45, 01:20 etc + ServicePort int `yaml:"servicePort"` + IsProduction bool `yaml:"isProduction"` + PacketsPerSecond int `yaml:"packetsPerSecond"` + Timeout int `yaml:"timeout"` + ScanGroups []ScanGroup `yaml:"scanGroups"` } //TLSAuditSnapshot a snapshot representing the results of a given scan session diff --git a/pkg/persistence.go b/pkg/persistence.go index c38ca7e..a1a1d40 100644 --- a/pkg/persistence.go +++ b/pkg/persistence.go @@ -56,7 +56,7 @@ func GetScanData(date, scanID string) []tlsmodel.HumanScanResult { } //ListScans returns the ScanID list of persisted scans -func ListScans(rewindDays int, completed bool) (result []tlsmodel.ScanRequest) { +func ListScans(rewindDays int, completed bool) (result []tlsmodel.AdvancedScanRequest) { if rewindDays < 0 { log.Print("The number of days in the past must be non-negative.") return @@ -91,7 +91,7 @@ func ListScans(rewindDays int, completed bool) (result []tlsmodel.ScanRequest) { for _, sID := range dirs { scanID := sID.Name() //LoadScanRequest retrieves persisted scan request from folder following a layout pattern - if psr, err := LoadScanRequest(d, scanID); err == nil && (len(psr.Hosts) == psr.Progress) == completed { + if psr, err := LoadScanRequest(d, scanID); err == nil && (psr.HostCount == psr.Progress) == completed { result = append(result, psr.Request) } } @@ -140,7 +140,11 @@ func streamExistingResult(psr tlsmodel.PersistedScanRequest, defer db.Close() hostResults := make(map[string][]tlsmodel.ScanResult) - total := len(psr.Hosts) + total := 0 + for _, gs := range psr.GroupedHosts { + total += len(gs.Hosts) + } + position := 0 db.View(func(txn *badger.Txn) error { @@ -317,7 +321,7 @@ func LoadScanRequest(dir, scanID string) (psr tlsmodel.PersistedScanRequest, e e return psr, outErr } psr, e = tlsmodel.UnmasharlPersistedScanRequest(data) - if e == nil && len(psr.Hosts) == psr.Progress { + if e == nil && psr.HostCount == psr.Progress { lock.Lock() psrCache[fmt.Sprintf("%s:%s", dir, scanID)] = psr lock.Unlock() diff --git a/pkg/scan-service.go b/pkg/scan-service.go index 4f98dde..9791ac6 100644 --- a/pkg/scan-service.go +++ b/pkg/scan-service.go @@ -1,7 +1,6 @@ package tlsaudit import ( - "bufio" "encoding/json" "fmt" "io/ioutil" @@ -59,7 +58,7 @@ func init() { //AddTLSAuditRoutes adds TLSAudit service's routes to an existing router setup func AddTLSAuditRoutes(r *mux.Router) { - r.HandleFunc("/scan", RealtimeScan).Methods("GET") + r.HandleFunc("/scan", RealtimeAdvancedScan).Methods("GET") r.HandleFunc("/listtlsscan/{rewind}/{completed}", getTLSAuditScanRequests).Methods("GET") r.HandleFunc("/getscandata/{date}/{scanID}", getTLSAuditScanData).Methods("GET") r.HandleFunc("/getprotocols/{date}/{scanID}", getTLSProtocols).Methods("GET") @@ -188,55 +187,62 @@ func ipResolver(ip string) string { } return "" } -func getIPsFromConfig() []string { +func getIPsFromConfig() []tlsmodel.GroupedHost { config, err := loadTLSConfig(TLSAuditConfigPath) if err != nil { - return []string{} + return []tlsmodel.GroupedHost{} } ips := getIPsToScan(config) return ips } -func getIPsToScan(config tlsmodel.TLSAuditConfig) []string { - data := make(map[string]string) - ips := []string{} - for _, c := range config.CIDRRanges { - ports := "" - if strings.Contains(c, ":") { - cc, p, err := extractPorts(c) - if err != nil { - continue +func getIPsToScan(config tlsmodel.TLSAuditConfig) []tlsmodel.GroupedHost { + + groupedHosts := []tlsmodel.GroupedHost{} + for _, sg := range config.ScanGroups { + data := make(map[string]string) + ips := []string{} + for _, c := range sg.CIDRRanges { + ports := "" + if strings.Contains(c, ":") { + cc, p, err := extractPorts(c) + if err != nil { + continue + } + c = cc + ports = p } - c = cc - ports = p - } - for _, ip := range cidr.Expand(c) { - ip = fmt.Sprintf("%s/32", ip) - if ps, present := data[ip]; present { - if ps == "" { + for _, ip := range cidr.Expand(c) { + ip = fmt.Sprintf("%s/32", ip) + if ps, present := data[ip]; present { + if ps == "" { + data[ip] = ports + } else if ports != "" { + data[ip] = fmt.Sprintf("%s,%s", ps, ports) + } + } else { data[ip] = ports - } else if ports != "" { - data[ip] = fmt.Sprintf("%s,%s", ps, ports) } - } else { - data[ip] = ports } } - } - for ip, ports := range data { - x := ip - - if ports != "" { - z := strings.Split(ip, "/") - if len(z) != 2 { - continue + for ip, ports := range data { + x := ip + if ports != "" { + z := strings.Split(ip, "/") + if len(z) != 2 { + continue + } + x = fmt.Sprintf("%s:%s/%s", z[0], ports, z[1]) + println(x) } - x = fmt.Sprintf("%s:%s/%s", z[0], ports, z[1]) - println(x) + ips = append(ips, x) } - ips = append(ips, x) + groupedHosts = append(groupedHosts, tlsmodel.GroupedHost{ + ScanGroup: sg, + Hosts: ips, + }) } - return ips + return groupedHosts } func extractPorts(cidrX string) (string, string, error) { @@ -256,7 +262,7 @@ func extractPorts(cidrX string) (string, string, error) { } //ScheduleTLSAudit runs TLSAudit scan -func ScheduleTLSAudit(ipSource func() []string, resolver func(string) string) { +func ScheduleTLSAudit(ipSource func() []tlsmodel.GroupedHost, resolver func(string) string) { //a restart schould clear the lock file if _, err := os.Stat(runFlag2); !os.IsNotExist(err) { // there is a runlock @@ -286,7 +292,7 @@ func ScheduleTLSAudit(ipSource func() []string, resolver func(string) string) { } //RunTLSScan accepts generator of IP addresses to scan and a function to map the IPs to hostnames (if any) - function to allow the hostname resolution happen in parallel if necessary -func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) string) { +func runTLSScan(ipSource func() []tlsmodel.GroupedHost, ipToHostnameResolver func(string) string) { //create a directory, if not exist, for tlsaudit to keep temporary file path := filepath.Join("data", "tlsaudit", "scan") if _, err := os.Stat(path); os.IsNotExist(err) { @@ -302,8 +308,6 @@ func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) stri } psr := tlsmodel.PersistedScanRequest{} - hosts := []string{} - // ipHostNameMapping := ipToHostnameResolver if _, err := os.Stat(workList); !os.IsNotExist(err) { // there is a worklist (due to a previous crash!) //load the list of IPs from there println("Resuming due to a worklist") @@ -314,10 +318,10 @@ func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) stri } defer file.Close() - scanner := bufio.NewScanner(file) - for scanner.Scan() { - hosts = append(hosts, scanner.Text()) - } + // scanner := bufio.NewScanner(file) + // for scanner.Scan() { + // hosts = append(hosts, scanner.Text()) + // } day, err := ioutil.ReadFile(runFlag) if err != nil { @@ -343,20 +347,24 @@ func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) stri } fmt.Printf("Will be scanning with PSR %#v", psr) } else { // starting a fresh scan - hosts = ipSource() - //shuffle hosts randomly - rand.Shuffle(len(hosts), func(i, j int) { - hosts[i], hosts[j] = hosts[j], hosts[i] - }) + shuffledHosts := []string{} + for _, gh := range ipSource() { + //shuffle hosts randomly + rand.Shuffle(len(gh.Hosts), func(i, j int) { + gh.Hosts[i], gh.Hosts[j] = gh.Hosts[j], gh.Hosts[i] + }) + psr.GroupedHosts = append(psr.GroupedHosts, gh) + shuffledHosts = append(shuffledHosts, gh.Hosts...) + } - //write shuffled hosts into worklist file - if err := ioutil.WriteFile(workList, []byte(strings.Join(hosts, "\n")+"\n"), 0644); err != nil { + //write shuffled hosts into worklist file - we no longer rely on this for scanning, kept for debugging + if err := ioutil.WriteFile(workList, []byte(strings.Join(shuffledHosts, "\n")+"\n"), 0644); err != nil { log.Error(err) return } //track progress in the progress file - if err := ioutil.WriteFile(progress, []byte(fmt.Sprintf("-1,%d", len(hosts))), 0644); err != nil { + if err := ioutil.WriteFile(progress, []byte(fmt.Sprintf("-1,%d", len(shuffledHosts))), 0644); err != nil { log.Error(err) return } @@ -372,8 +380,11 @@ func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) stri log.Error(err) return } - psr.Hosts = hosts - request := tlsmodel.ScanRequest{} + psr.HostCount = len(shuffledHosts) + request := tlsmodel.AdvancedScanRequest{} + for _, gh := range psr.GroupedHosts { + request.ScanGroups = append(request.ScanGroups, gh.ScanGroup) + } request.Day = today request.ScanID = GetNextScanID() config, _ := loadTLSConfig(TLSAuditConfigPath) @@ -385,7 +396,11 @@ func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) stri psr.Request = request } - go resolveIPs(hosts) + //we've got the psr. Now use it as the basis of the scans + hosts := []string{} // hosts to scan + for _, gh := range psr.GroupedHosts { + hosts = append(hosts, gh.Hosts...) + } //get ready to scan //get where we "stopped" last time possibly after a crash @@ -402,6 +417,11 @@ func runTLSScan(ipSource func() []string, ipToHostnameResolver func(string) stri } psr.Progress = stopped + //only resolve IPs for the hosts that have not been scanned + if stopped < len(hosts) { + go resolveIPs(hosts[stopped:]) + } + PersistScanRequest(psr) count := len(hosts) diff --git a/pkg/websocket.go b/pkg/websocket.go index 5251adf..901840e 100644 --- a/pkg/websocket.go +++ b/pkg/websocket.go @@ -39,44 +39,52 @@ type mydata struct { Data []byte } -//RealtimeScan runs a scan asynchronously and streams result over a websocket -func RealtimeScan(w http.ResponseWriter, req *http.Request) { +//RealtimeAdvancedScan runs a scan asynchronously and streams result over a websocket +func RealtimeAdvancedScan(w http.ResponseWriter, req *http.Request) { if conn, err := upgrader.Upgrade(w, req, nil); err == nil { go func() { defer conn.Close() - var request tlsmodel.ScanRequest + var request tlsmodel.AdvancedScanRequest if err := conn.ReadJSON(&request); err == nil { hosts := []string{} + hostCount := 0 psr := tlsmodel.PersistedScanRequest{} if request.ScanID == "" { //start a fresh scan request.ScanID = GetNextScanID() - for _, x := range request.CIDRs { - x = strings.ReplaceAll(x, ",", "") - rng := "/32" - ports := "" - if strings.Contains(x, "/") { - rng = "/" + strings.Split(x, "/")[1] - } - if strings.Contains(x, ":") { - ports = strings.Split(strings.Split(x, "/")[0], ":")[1] - x = strings.Split(x, ":")[0] + rng + for _, sg := range request.ScanGroups { + for _, x := range sg.CIDRRanges { + x = strings.ReplaceAll(x, ",", "") + rng := "/32" + ports := "" + if strings.Contains(x, "/") { + rng = "/" + strings.Split(x, "/")[1] + } + if strings.Contains(x, ":") { + ports = strings.Split(strings.Split(x, "/")[0], ":")[1] + x = strings.Split(x, ":")[0] + rng - } else { - x = strings.Split(x, "/")[0] + rng - } - hs := cidr.Expand(x) - if ports != "" { - for i, h := range hs { - hs[i] = fmt.Sprintf("%s:%s/32", h, ports) + } else { + x = strings.Split(x, "/")[0] + rng } + hs := cidr.Expand(x) + if ports != "" { + for i, h := range hs { + hs[i] = fmt.Sprintf("%s:%s/32", h, ports) + } + } + hosts = append(hosts, hs...) } - hosts = append(hosts, hs...) + //shuffle hosts randomly + rand.Shuffle(len(hosts), func(i, j int) { + hosts[i], hosts[j] = hosts[j], hosts[i] + }) + psr.GroupedHosts = append(psr.GroupedHosts, tlsmodel.GroupedHost{ + ScanGroup: sg, + Hosts: hosts, + }) + hostCount += len(hosts) } - //shuffle hosts randomly - rand.Shuffle(len(hosts), func(i, j int) { - hosts[i], hosts[j] = hosts[j], hosts[i] - }) - psr.Hosts = hosts + psr.HostCount = hostCount psr.ScanStart = time.Now() request.Day = psr.ScanStart.Format(dayFormat) psr.Request = request @@ -99,7 +107,7 @@ func RealtimeScan(w http.ResponseWriter, req *http.Request) { } out := tlsmodel.ScanProgress{ ScanID: scanID, - Progress: 100 * float32(position) / float32(len(psr.Hosts)), + Progress: 100 * float32(position) / float32(psr.HostCount), ScanResults: res, Narrative: narrative, } @@ -107,36 +115,40 @@ func RealtimeScan(w http.ResponseWriter, req *http.Request) { } streamExistingResult(psr, callback) - for index, host := range psr.Hosts { - if index < psr.Progress { - continue - } - position := index + 1 - scan := make(map[string]tlsmodel.ScanResult) - results := []<-chan tlsmodel.ScanResult{} - results = append(results, ScanCIDRTLS(host, request.Config)) - for result := range MergeResultChannels(results...) { - key := result.Server + result.Port - if _, present := scan[key]; !present { - scan[key] = result - narrative := fmt.Sprintf("Partial scan of %s. Progress %f%% %d hosts of a total of %d in %f seconds\n", - result.Server, 100*float32(position)/float32(len(psr.Hosts)), position, len(psr.Hosts), time.Since(psr.ScanStart).Seconds()) - callback(position, []tlsmodel.ScanResult{result}, narrative) + + counter := 0 + for _, gs := range psr.GroupedHosts { + for _, host := range gs.Hosts { + counter++ + if counter < psr.Progress { + continue } + position := counter + scan := make(map[string]tlsmodel.ScanResult) + results := []<-chan tlsmodel.ScanResult{} + results = append(results, ScanCIDRTLS(host, request.Config)) + for result := range MergeResultChannels(results...) { + key := result.Server + result.Port + if _, present := scan[key]; !present { + scan[key] = result + narrative := fmt.Sprintf("Partial scan of %s. Progress %f%% %d hosts of a total of %d in %f seconds\n", + result.Server, 100*float32(position)/float32(psr.HostCount), position, psr.HostCount, time.Since(psr.ScanStart).Seconds()) + callback(position, []tlsmodel.ScanResult{result}, narrative) + } + } + psr.Progress = position + psr.ScanEnd = time.Now() + PersistScanRequest(psr) + var scanResults []tlsmodel.ScanResult + for k := range scan { + scanResults = append(scanResults, scan[k]) + } + sort.Sort(tlsmodel.ScanResultSorter(scanResults)) + PersistScans(psr, host, scanResults) + narrative := fmt.Sprintf("Finished scan of %s. Progress %f%% %d hosts of a total of %d in %f seconds\n", + host, 100*float32(position)/float32(psr.HostCount), position, psr.HostCount, psr.ScanEnd.Sub(psr.ScanStart).Seconds()) + callback(position, scanResults, narrative) } - psr.Progress = position - psr.ScanEnd = time.Now() - PersistScanRequest(psr) - var scanResults []tlsmodel.ScanResult - for k := range scan { - scanResults = append(scanResults, scan[k]) - } - sort.Sort(tlsmodel.ScanResultSorter(scanResults)) - PersistScans(psr, host, scanResults) - narrative := fmt.Sprintf("Finished scan of %s. Progress %f%% %d hosts of a total of %d in %f seconds\n", - host, 100*float32(position)/float32(len(psr.Hosts)), position, len(psr.Hosts), psr.ScanEnd.Sub(psr.ScanStart).Seconds()) - callback(position, scanResults, narrative) - } } else { println(err.Error())