diff --git a/internal/output/json_test.go b/internal/output/json_test.go index 74c9b17c..7250c5d5 100644 --- a/internal/output/json_test.go +++ b/internal/output/json_test.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "testing" + "time" "github.com/openshift/tls-scanner/internal/k8s" "github.com/openshift/tls-scanner/internal/scanner" @@ -12,9 +13,11 @@ import ( func testScanResults() scanner.ScanResults { return scanner.ScanResults{ - Timestamp: "2026-05-13T12:00:00Z", - TotalIPs: 1, - ScannedIPs: 1, + Timestamp: "2026-05-13T12:00:00Z", + Duration: 5 * time.Second, + DurationSeconds: 5.0, + TotalIPs: 1, + ScannedIPs: 1, IPResults: []scanner.IPResult{{ IP: "10.0.0.1", Status: "scanned", diff --git a/internal/output/junit.go b/internal/output/junit.go index e679f267..66f3ab08 100644 --- a/internal/output/junit.go +++ b/internal/output/junit.go @@ -10,12 +10,22 @@ import ( "github.com/openshift/tls-scanner/internal/scanner" ) +func hostname() string { + h, err := os.Hostname() + if err != nil { + return "unknown" + } + return h +} + type JUnitTestSuite struct { XMLName xml.Name `xml:"testsuite"` Name string `xml:"name,attr"` Tests int `xml:"tests,attr"` Failures int `xml:"failures,attr"` Time float64 `xml:"time,attr"` + Timestamp string `xml:"timestamp,attr,omitempty"` + Hostname string `xml:"hostname,attr,omitempty"` Properties []JUnitProperty `xml:"properties>property,omitempty"` TestCases []JUnitTestCase `xml:"testcase"` } @@ -44,7 +54,10 @@ type JUnitProperty struct { func WriteJUnitOutput(scanResults scanner.ScanResults, filename string, pqcCheck bool) error { testSuite := JUnitTestSuite{ - Name: "TLSSecurityScan", + Name: "TLSSecurityScan", + Time: scanResults.Duration.Seconds(), + Timestamp: scanResults.Timestamp, + Hostname: hostname(), } enforceTLSCompliance := scanner.TLSConfigComplianceFailuresEnforced(scanResults) diff --git a/internal/output/junit_test.go b/internal/output/junit_test.go index ce27d484..5cfd674e 100644 --- a/internal/output/junit_test.go +++ b/internal/output/junit_test.go @@ -45,6 +45,15 @@ func TestWriteJUnitOutputBasic(t *testing.T) { if suite.Failures != 0 { t.Errorf("Failures = %d, want 0", suite.Failures) } + if suite.Timestamp != "2026-05-13T12:00:00Z" { + t.Errorf("Timestamp = %q, want %q", suite.Timestamp, "2026-05-13T12:00:00Z") + } + if suite.Hostname == "" { + t.Error("Hostname should not be empty") + } + if suite.Time != 5.0 { + t.Errorf("Time = %f, want 5.0", suite.Time) + } } func TestWriteJUnitOutputPQCFailures(t *testing.T) { diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index f0c00458..56a3dbd2 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -259,16 +259,15 @@ MAX_PARALLEL (testssl): %d results := assembleResults(startTime, totalIPs, tlsConfig, discovery.Skipped, batchResults) - duration := time.Since(startTime) fmt.Printf("\n========================================\n") fmt.Printf("CLUSTER SCAN COMPLETE!\n") fmt.Printf("========================================\n") fmt.Printf("Total IPs processed: %d\n", results.ScannedIPs) fmt.Printf("Total ports scanned: %d\n", len(batchResults)) fmt.Printf("Total ports skipped: %d\n", len(discovery.Skipped)) - fmt.Printf("Total time: %v\n", duration) + fmt.Printf("Total time: %v\n", results.Duration) if len(batchResults) > 0 { - fmt.Printf("Throughput: %.2f ports/min\n", float64(len(batchResults))/duration.Minutes()) + fmt.Printf("Throughput: %.2f ports/min\n", float64(len(batchResults))/results.Duration.Minutes()) } fmt.Printf("========================================\n") @@ -295,15 +294,14 @@ func Scan(jobs []ScanJob, concurrentScans int, client *k8s.Client, tlsConfig *k8 batchResults := batchScan(jobs, concurrentScans, client, tlsConfig, policy, timeouts, starttlsPorts) results := assembleResults(startTime, 0, tlsConfig, batchResults) - duration := time.Since(startTime) fmt.Printf("\n========================================\n") fmt.Printf("SCAN COMPLETE!\n") fmt.Printf("========================================\n") fmt.Printf("Total IPs processed: %d\n", results.ScannedIPs) fmt.Printf("Total targets: %d\n", len(jobs)) - fmt.Printf("Total time: %v\n", duration) + fmt.Printf("Total time: %v\n", results.Duration) if results.ScannedIPs > 0 { - fmt.Printf("Average time per host: %.2fs\n", duration.Seconds()/float64(results.ScannedIPs)) + fmt.Printf("Average time per host: %.2fs\n", results.Duration.Seconds()/float64(results.ScannedIPs)) } fmt.Printf("========================================\n") @@ -554,8 +552,11 @@ func assembleResults(startTime time.Time, totalIPs int, tlsConfig *k8s.TLSSecuri totalIPs = len(ipResultMap) } + elapsed := time.Since(startTime) results := ScanResults{ Timestamp: startTime.Format(time.RFC3339), + Duration: elapsed, + DurationSeconds: elapsed.Seconds(), TotalIPs: totalIPs, IPResults: make([]IPResult, 0, len(ipResultMap)), TLSSecurityConfig: tlsConfig, diff --git a/internal/scanner/types.go b/internal/scanner/types.go index f97daa77..e496a048 100644 --- a/internal/scanner/types.go +++ b/internal/scanner/types.go @@ -1,6 +1,8 @@ package scanner import ( + "time" + "github.com/openshift/tls-scanner/internal/k8s" ) @@ -54,6 +56,8 @@ type Elem struct { type ScanResults struct { Timestamp string `json:"timestamp"` + DurationSeconds float64 `json:"duration_seconds"` + Duration time.Duration `json:"-"` TotalIPs int `json:"total_ips"` ScannedIPs int `json:"scanned_ips"` IPResults []IPResult `json:"ip_results"`