diff --git a/internal/runner/healthcheck.go b/internal/runner/healthcheck.go index e4686ed4..27543fea 100644 --- a/internal/runner/healthcheck.go +++ b/internal/runner/healthcheck.go @@ -17,11 +17,11 @@ func DoHealthCheck(flagSet *goflags.FlagSet) string { // RW permissions on config file cfgFilePath, _ := flagSet.GetConfigFilePath() var test strings.Builder - test.WriteString(fmt.Sprintf("Version: %s\n", version)) - test.WriteString(fmt.Sprintf("Operative System: %s\n", runtime.GOOS)) - test.WriteString(fmt.Sprintf("Architecture: %s\n", runtime.GOARCH)) - test.WriteString(fmt.Sprintf("Go Version: %s\n", runtime.Version())) - test.WriteString(fmt.Sprintf("Compiler: %s\n", runtime.Compiler)) + fmt.Fprintf(&test, "Version: %s\n", version) + fmt.Fprintf(&test, "Operative System: %s\n", runtime.GOOS) + fmt.Fprintf(&test, "Architecture: %s\n", runtime.GOARCH) + fmt.Fprintf(&test, "Go Version: %s\n", runtime.Version()) + fmt.Fprintf(&test, "Compiler: %s\n", runtime.Compiler) var testResult string ok, err := fileutil.IsReadable(cfgFilePath) @@ -33,7 +33,7 @@ func DoHealthCheck(flagSet *goflags.FlagSet) string { if err != nil { testResult += fmt.Sprintf(" (%s)", err) } - test.WriteString(fmt.Sprintf("Config file \"%s\" Read => %s\n", cfgFilePath, testResult)) + fmt.Fprintf(&test, "Config file \"%s\" Read => %s\n", cfgFilePath, testResult) ok, err = fileutil.IsWriteable(cfgFilePath) if ok { testResult = "Ok" @@ -43,7 +43,7 @@ func DoHealthCheck(flagSet *goflags.FlagSet) string { if err != nil { testResult += fmt.Sprintf(" (%s)", err) } - test.WriteString(fmt.Sprintf("Config file \"%s\" Write => %s\n", cfgFilePath, testResult)) + fmt.Fprintf(&test, "Config file \"%s\" Write => %s\n", cfgFilePath, testResult) c4, err := net.Dial("tcp4", "scanme.sh:80") if err == nil && c4 != nil { _ = c4.Close() @@ -52,7 +52,7 @@ func DoHealthCheck(flagSet *goflags.FlagSet) string { if err != nil { testResult = fmt.Sprintf("Ko (%s)", err) } - test.WriteString(fmt.Sprintf("IPv4 connectivity to scanme.sh:80 => %s\n", testResult)) + fmt.Fprintf(&test, "IPv4 connectivity to scanme.sh:80 => %s\n", testResult) c6, err := net.Dial("tcp6", "scanme.sh:80") if err == nil && c6 != nil { _ = c6.Close() @@ -61,20 +61,20 @@ func DoHealthCheck(flagSet *goflags.FlagSet) string { if err != nil { testResult = fmt.Sprintf("Ko (%s)", err) } - test.WriteString(fmt.Sprintf("IPv6 connectivity to scanme.sh:80 => %s\n", testResult)) + fmt.Fprintf(&test, "IPv6 connectivity to scanme.sh:80 => %s\n", testResult) test.WriteString("Supported Engines\n") test.WriteString("ctls\n") - test.WriteString(fmt.Sprintf("TLS: %s\n", strings.Join(tls.SupportedTlsVersions, ", "))) - test.WriteString(fmt.Sprintf("Ciphers: %s\n", strings.Join(tls.AllCiphersNames, ", "))) + fmt.Fprintf(&test, "TLS: %s\n", strings.Join(tls.SupportedTlsVersions, ", ")) + fmt.Fprintf(&test, "Ciphers: %s\n", strings.Join(tls.AllCiphersNames, ", ")) test.WriteString("ztls\n") - test.WriteString(fmt.Sprintf("TLS: %s\n", strings.Join(ztls.SupportedTlsVersions, ", "))) - test.WriteString(fmt.Sprintf("Ciphers: %s\n", strings.Join(ztls.AllCiphersNames, ", "))) + fmt.Fprintf(&test, "TLS: %s\n", strings.Join(ztls.SupportedTlsVersions, ", ")) + fmt.Fprintf(&test, "Ciphers: %s\n", strings.Join(ztls.AllCiphersNames, ", ")) if openssl.IsAvailable() { test.WriteString("openssl\n") - test.WriteString(fmt.Sprintf("location: %s\n", openssl.BinaryPath)) + fmt.Fprintf(&test, "location: %s\n", openssl.BinaryPath) } return test.String() diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 7dd0c887..a9705721 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -217,7 +217,7 @@ func (r *Runner) Close() error { if err != nil { gologger.Warning().Msgf("Could not open file for upload: %s", err) } else { - defer file.Close() + defer file.Close() //nolint:errcheck scanner := bufio.NewScanner(file) callback := pdcpWriter.GetWriterCallback() for scanner.Scan() { @@ -453,24 +453,32 @@ func (r *Runner) normalizeAndQueueInputs(inputs chan taskInput) error { scanner := bufio.NewScanner(file) for scanner.Scan() { - text := scanner.Text() - if text != "" { - r.processInputItem(text, inputs) + if text := scanner.Text(); text != "" { + r.processCommaSeparatedTargets(text, inputs) } } } if r.hasStdin { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { - text := scanner.Text() - if text != "" { - r.processInputItem(text, inputs) + if text := scanner.Text(); text != "" { + r.processCommaSeparatedTargets(text, inputs) } } } return nil } +// processCommaSeparatedTargets splits a line by comma and queues each non-empty target, +// matching the -u flag behaviour for comma-separated input (#859). +func (r *Runner) processCommaSeparatedTargets(text string, inputs chan taskInput) { + for _, item := range strings.Split(text, ",") { + if item = strings.TrimSpace(item); item != "" { + r.processInputItem(item, inputs) + } + } +} + // resolveFQDN resolves a FQDN and returns the IP addresses func (r *Runner) resolveFQDN(target string) ([]string, error) { // If the host is a Domain, then perform resolution and discover all IP diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index 730fa3e0..8fe3b882 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -1,6 +1,7 @@ package runner import ( + "os" "testing" "github.com/projectdiscovery/dnsx/libs/dnsx" @@ -416,3 +417,38 @@ func Test_CTLogsModeOutputOptions(t *testing.T) { }) } } + +// Test_CommaSeparatedInputList_normalizeAndQueueInputs verifies that comma-separated +// targets on a single line in an input file (-l) are split and queued individually, +// matching the behaviour of the -u flag (#859). +func Test_CommaSeparatedInputList_normalizeAndQueueInputs(t *testing.T) { + // Write a temporary file with comma-separated entries on a single line. + f, err := os.CreateTemp("", "tlsx-test-*.txt") + require.NoError(t, err) + defer os.Remove(f.Name()) //nolint:errcheck + + _, err = f.WriteString("example.com,scanme.sh\n") + require.NoError(t, err) + require.NoError(t, f.Close()) + + options := &clients.Options{ + Ports: []string{"443"}, + InputList: f.Name(), + } + runner := &Runner{options: options} + runner.hasStdin = false + + inputs := make(chan taskInput, 10) + err = runner.normalizeAndQueueInputs(inputs) + require.NoError(t, err) + close(inputs) + + var hosts []string + for task := range inputs { + hosts = append(hosts, task.host) + } + + assert.Contains(t, hosts, "example.com", "first comma-separated host should be queued") + assert.Contains(t, hosts, "scanme.sh", "second comma-separated host should be queued") + assert.Len(t, hosts, 2, "exactly two hosts should be queued from a single comma-separated line") +} diff --git a/pkg/tlsx/tls/tls.go b/pkg/tlsx/tls/tls.go index d4a392de..f7cd83f0 100644 --- a/pkg/tlsx/tls/tls.go +++ b/pkg/tlsx/tls/tls.go @@ -260,14 +260,14 @@ func (c *Client) EnumerateCiphers(hostname, ip, port string, options clients.Con if err != nil || baseConn == nil { return } - defer baseConn.Close() + defer baseConn.Close() //nolint:errcheck stats.IncrementCryptoTLSConnections() cfg := baseCfg.Clone() cfg.CipherSuites = []uint16{tlsCiphers[v]} conn := tls.Client(baseConn, cfg) - defer conn.Close() + defer conn.Close() //nolint:errcheck errChan := make(chan error, 1) go func() { diff --git a/pkg/tlsx/tls/tls_test.go b/pkg/tlsx/tls/tls_test.go index d811a294..ab78476b 100644 --- a/pkg/tlsx/tls/tls_test.go +++ b/pkg/tlsx/tls/tls_test.go @@ -131,7 +131,7 @@ func TestHandshakeTimeoutLeak(t *testing.T) { if err != nil { t.Fatal(err) } - defer l.Close() + defer l.Close() //nolint:errcheck var conns []net.Conn var mu sync.Mutex @@ -200,7 +200,7 @@ func TestHighConcurrencyTimeouts(t *testing.T) { if err != nil { t.Fatal(err) } - defer l.Close() + defer l.Close() //nolint:errcheck var conns []net.Conn var mu sync.Mutex diff --git a/pkg/tlsx/ztls/regression_test.go b/pkg/tlsx/ztls/regression_test.go index 9f2d891d..44d75c3f 100644 --- a/pkg/tlsx/ztls/regression_test.go +++ b/pkg/tlsx/ztls/regression_test.go @@ -19,7 +19,7 @@ func TestHandshakeTimeoutLeak(t *testing.T) { if err != nil { t.Fatal(err) } - defer l.Close() + defer l.Close() //nolint:errcheck var conns []net.Conn var mu sync.Mutex @@ -76,7 +76,7 @@ func TestHandshakeTimeoutLeak(t *testing.T) { config, err := client.getConfig(host, host, port, clients.ConnectOptions{}) if err != nil { - rawConn.Close() + rawConn.Close() //nolint:errcheck cancel() continue } @@ -105,7 +105,7 @@ func TestUnresponsiveServer(t *testing.T) { if err != nil { t.Fatal(err) } - defer l.Close() + defer l.Close() //nolint:errcheck var conns []net.Conn var mu sync.Mutex @@ -154,7 +154,7 @@ func TestUnresponsiveServer(t *testing.T) { } config, err := client.getConfig(host, host, port, clients.ConnectOptions{}) if err != nil { - rawConn.Close() + rawConn.Close() //nolint:errcheck t.Fatal(err) } tlsConn := tls.Client(rawConn, config) @@ -176,7 +176,7 @@ func TestSlowServer(t *testing.T) { if err != nil { t.Fatal(err) } - defer l.Close() + defer l.Close() //nolint:errcheck go func() { conn, _ := l.Accept() @@ -216,7 +216,7 @@ func TestSlowServer(t *testing.T) { } config, err := client.getConfig(host, host, port, clients.ConnectOptions{}) if err != nil { - rawConn.Close() + rawConn.Close() //nolint:errcheck t.Fatal(err) } tlsConn := tls.Client(rawConn, config) diff --git a/pkg/tlsx/ztls/ztls.go b/pkg/tlsx/ztls/ztls.go index b86bd5d5..1fce232e 100644 --- a/pkg/tlsx/ztls/ztls.go +++ b/pkg/tlsx/ztls/ztls.go @@ -262,14 +262,14 @@ func (c *Client) EnumerateCiphers(hostname, ip, port string, options clients.Con if err != nil || baseConn == nil { return } - defer baseConn.Close() + defer baseConn.Close() //nolint:errcheck stats.IncrementZcryptoTLSConnections() cfg := baseCfg.Clone() cfg.CipherSuites = []uint16{ztlsCiphers[v]} tlsConn := tls.Client(baseConn, cfg) - defer tlsConn.Close() + defer tlsConn.Close() //nolint:errcheck errChan := make(chan error, 1) go func() {