From 8a6f0157eb0640ea03b7007e903ceb63f9ed07c9 Mon Sep 17 00:00:00 2001 From: Aurora <261931150+TheAuroraAI@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:39:18 +0000 Subject: [PATCH 1/4] fix(runner): split comma-separated targets from -l file and stdin (#859) The -u flag accepts comma-separated hosts via CommaSeparatedStringSliceOptions, but the file-based -l (and stdin) path fed each raw line to processInputItem as a single string. A line like '192.168.1.0/24,192.168.2.0/24' was treated as one invalid host, producing: [WRN] Could not connect input 192.168.1.0/24,192.168.2.0/24:443 ... could not dial address <- no address found for host Fix: when reading lines from the input file or stdin, split each line on commas and trim whitespace before forwarding to processInputItem, so that the two input methods behave identically. A new table-driven unit test verifies that both entries are queued when the input file contains a comma-separated line. Fixes #859. Co-Authored-By: Claude Sonnet 4.6 --- internal/runner/runner.go | 14 ++++++++++++-- internal/runner/runner_test.go | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 91e80136..122d0802 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -440,7 +440,12 @@ func (r *Runner) normalizeAndQueueInputs(inputs chan taskInput) error { for scanner.Scan() { text := scanner.Text() if text != "" { - r.processInputItem(text, inputs) + // Support comma-separated targets on a single line, matching the -u flag behaviour. + for _, item := range strings.Split(text, ",") { + if item = strings.TrimSpace(item); item != "" { + r.processInputItem(item, inputs) + } + } } } } @@ -449,7 +454,12 @@ func (r *Runner) normalizeAndQueueInputs(inputs chan taskInput) error { for scanner.Scan() { text := scanner.Text() if text != "" { - r.processInputItem(text, inputs) + // Support comma-separated targets on a single line, matching the -u flag behaviour. + for _, item := range strings.Split(text, ",") { + if item = strings.TrimSpace(item); item != "" { + r.processInputItem(item, inputs) + } + } } } } diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index b4ed856e..4e3f289d 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -429,3 +429,38 @@ func Test_CTLogsModeOutputOptions(t *testing.T) { }) } } + +// Test_CommaSeperatedInputList_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_CommaSeperatedInputList_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()) + + _, 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") +} From 6af7c6c2e8e3efc2d8678ba4ee48a9e9b549d1d2 Mon Sep 17 00:00:00 2001 From: Aurora <261931150+TheAuroraAI@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:47:28 +0000 Subject: [PATCH 2/4] refactor(runner): fix typo in test name and extract comma-split helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename Test_CommaSeperatedInputList → Test_CommaSeparatedInputList (typo fix) - Extract processCommaSeparatedTargets helper to deduplicate comma-split logic from file and stdin reading paths --- internal/runner/runner.go | 30 ++++++++++++++---------------- internal/runner/runner_test.go | 4 ++-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 122d0802..a57dade1 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -438,34 +438,32 @@ func (r *Runner) normalizeAndQueueInputs(inputs chan taskInput) error { scanner := bufio.NewScanner(file) for scanner.Scan() { - text := scanner.Text() - if text != "" { - // Support comma-separated targets on a single line, matching the -u flag behaviour. - for _, item := range strings.Split(text, ",") { - if item = strings.TrimSpace(item); item != "" { - r.processInputItem(item, 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 != "" { - // Support comma-separated targets on a single line, matching the -u flag behaviour. - for _, item := range strings.Split(text, ",") { - if item = strings.TrimSpace(item); item != "" { - r.processInputItem(item, 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 4e3f289d..03bc2ffd 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -430,10 +430,10 @@ func Test_CTLogsModeOutputOptions(t *testing.T) { } } -// Test_CommaSeperatedInputList_normalizeAndQueueInputs verifies that comma-separated +// 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_CommaSeperatedInputList_normalizeAndQueueInputs(t *testing.T) { +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) From 61d151622280215c7662593afa61e4bb86c15188 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Thu, 19 Mar 2026 15:41:58 +0100 Subject: [PATCH 3/4] fix lint errors --- internal/runner/healthcheck.go | 28 ++++++++++++++-------------- internal/runner/runner.go | 2 +- internal/runner/runner_test.go | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) 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 a57dade1..b71c248d 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() { diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index 03bc2ffd..581fe555 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -437,7 +437,7 @@ 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()) + defer os.Remove(f.Name()) //nolint:errcheck _, err = f.WriteString("example.com,scanme.sh\n") require.NoError(t, err) From 9ebf288c3b70bb2bb8d564d32c23eff46fcc0911 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Thu, 19 Mar 2026 16:01:27 +0100 Subject: [PATCH 4/4] fix actions --- internal/runner/runner_test.go | 1 + pkg/tlsx/tls/tls.go | 4 ++-- pkg/tlsx/tls/tls_test.go | 4 ++-- pkg/tlsx/ztls/regression_test.go | 12 ++++++------ pkg/tlsx/ztls/ztls.go | 4 ++-- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index 941b1550..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" 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() {