diff --git a/CHANGELOG.txt b/CHANGELOG.txt
@@ -1,5 +1,6 @@
## v0.0.8
+- fix(cli): target files stream line by line ~ @djnnvx
- fix(cli): fingerprint -d should only run wappalyzer and cvemap ~ @djnnvx
- fix(scan): bind every response body read ~ @djnnvx
- fix(scan): nuclei ignores non-match events, a rate-limited host reported 95913 fake findings ~ @djnnvx
diff --git a/cli/parser.go b/cli/parser.go
@@ -85,8 +85,8 @@ func GetParser(opts *StandardOptions) *cobra.Command {
scanner := helper.LoadTargetFile(opts.TargetFilePath)
defer scanner.Close()
- for scanner.Scan() {
- scanOpts.Domain = scanner.Text()
+ for target := range scanner.Lines() {
+ scanOpts.Domain = target
scanOpts.Fingerprint()
}
},
@@ -139,8 +139,8 @@ func GetParser(opts *StandardOptions) *cobra.Command {
scanner := helper.LoadTargetFile(opts.TargetFilePath)
defer scanner.Close()
- for scanner.Scan() {
- scanOpts.Domain = scanner.Text()
+ for target := range scanner.Lines() {
+ scanOpts.Domain = target
scanOpts.Run()
}
},
@@ -188,8 +188,8 @@ func GetParser(opts *StandardOptions) *cobra.Command {
scanner := helper.LoadTargetFile(opts.TargetFilePath)
defer scanner.Close()
- for scanner.Scan() {
- osintOpts.Domain = scanner.Text()
+ for target := range scanner.Lines() {
+ osintOpts.Domain = target
osintOpts.Run()
}
},
diff --git a/helpers/file.go b/helpers/file.go
@@ -4,12 +4,15 @@ import (
"bufio"
"fmt"
"io"
+ "iter"
"os"
+ "strings"
)
type FileScanner struct {
io.Closer
- *bufio.Scanner
+ reader *bufio.Reader
+ path string
}
func LoadTargetFile(targetPath string) *FileScanner {
@@ -20,5 +23,25 @@ func LoadTargetFile(targetPath string) *FileScanner {
}
fmt.Println("[+] Loaded targets from", targetPath)
- return &FileScanner{file, bufio.NewScanner(file)}
+ return &FileScanner{file, bufio.NewReader(file), targetPath}
+}
+
+// Lines streams the file with no line-length cap. A read failure exits instead
+// of ending the run short with nothing to show for it.
+func (f *FileScanner) Lines() iter.Seq[string] {
+ return func(yield func(string) bool) {
+ for {
+ line, err := f.reader.ReadString('\n')
+ if t := strings.TrimSpace(line); t != "" && !yield(t) {
+ return
+ }
+ if err != nil {
+ if err != io.EOF {
+ fmt.Printf("[!] Could not read target file %q: %v\n", f.path, err)
+ os.Exit(1)
+ }
+ return
+ }
+ }
+ }
}
diff --git a/helpers/file_test.go b/helpers/file_test.go
@@ -0,0 +1,38 @@
+package helper
+
+import (
+ "os"
+ "path/filepath"
+ "slices"
+ "strings"
+ "testing"
+)
+
+// a line past bufio.Scanner's 64KB cap used to end the run silently
+func TestLoadTargetFileKeepsGoingPastALongLine(t *testing.T) {
+ long := strings.Repeat("A", 70_000)
+ path := filepath.Join(t.TempDir(), "targets.txt")
+ if err := os.WriteFile(path, []byte(long+"\n\nexample.com\n example.org \n"), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ f := LoadTargetFile(path)
+ defer f.Close()
+
+ got := slices.Collect(f.Lines())
+ want := []string{long, "example.com", "example.org"}
+ if !slices.Equal(got, want) {
+ t.Errorf("got %d targets %q, want %d", len(got), truncate(got), len(want))
+ }
+}
+
+func truncate(lines []string) []string {
+ out := make([]string, len(lines))
+ for i, l := range lines {
+ if len(l) > 20 {
+ l = l[:20] + "..."
+ }
+ out[i] = l
+ }
+ return out
+}
diff --git a/prune/run.go b/prune/run.go
@@ -25,9 +25,7 @@ func (opts *PruneOpts) Run() {
scanner := helper.LoadTargetFile(opts.InFilePath)
defer scanner.Close()
- for scanner.Scan() {
- targetDomain := scanner.Text()
-
+ for targetDomain := range scanner.Lines() {
if target, ok := helper.ResolveWebTarget(targetDomain, opts.ForceInsecure); ok {
fmt.Printf("[+] %s is alive (%s)!\n", targetDomain, target)
results = append(results, targetDomain)
| | | | |