fix: harden scan edge cases

This commit is contained in:
ZacharyZcR
2026-06-01 03:32:13 +08:00
parent 8b558b4f12
commit 8ec96bfe6d
21 changed files with 534 additions and 67 deletions
+9 -4
View File
@@ -39,8 +39,11 @@ func NewHostIterator(host string, filename string, nohosts ...string) (*HostIter
sources = append(sources, hostSources...)
matcher := newHostMatcher()
if len(nohosts) > 0 && strings.TrimSpace(nohosts[0]) != "" {
if err := matcher.add(nohosts[0]); err != nil {
for _, exclude := range nohosts {
if strings.TrimSpace(exclude) == "" {
continue
}
if err := matcher.add(exclude); err != nil {
closeHostSources(sources)
return nil, err
}
@@ -180,10 +183,12 @@ func newFileHostSource(filename string) (*fileHostSource, error) {
if err != nil {
return nil, err
}
return &fileHostSource{
src := &fileHostSource{
file: file,
scanner: bufio.NewScanner(file),
}, nil
}
src.scanner.Buffer(make([]byte, 64*1024), 4*1024*1024)
return src, nil
}
func (s *fileHostSource) Next() (string, bool, error) {
+44
View File
@@ -2,7 +2,9 @@ package parsers
import (
"context"
"os"
"reflect"
"strings"
"testing"
)
@@ -59,3 +61,45 @@ func TestHostIteratorExcludeCIDR(t *testing.T) {
t.Fatalf("batch = %#v, want %#v", batch, want)
}
}
func TestHostIteratorAcceptsMultipleExcludeSources(t *testing.T) {
iter, err := NewHostIterator("192.168.1.0/29", "", "192.168.1.2", "192.168.1.5")
if err != nil {
t.Fatalf("NewHostIterator error = %v", err)
}
defer iter.Close()
batch, err := iter.NextBatch(context.Background(), 10)
if err != nil {
t.Fatalf("NextBatch error = %v", err)
}
want := []string{"192.168.1.1", "192.168.1.3", "192.168.1.4", "192.168.1.6"}
if !reflect.DeepEqual(batch, want) {
t.Fatalf("batch = %#v, want %#v", batch, want)
}
}
func TestHostIteratorReadsLongHostFileLine(t *testing.T) {
dir := t.TempDir()
path := dir + "/hosts.txt"
longPrefix := strings.Repeat("a", 70*1024)
host := longPrefix + ".example.com"
if err := os.WriteFile(path, []byte(host+"\n"), 0o600); err != nil {
t.Fatalf("WriteFile error = %v", err)
}
iter, err := NewHostIterator("", path)
if err != nil {
t.Fatalf("NewHostIterator error = %v", err)
}
defer iter.Close()
batch, err := iter.NextBatch(context.Background(), 1)
if err != nil {
t.Fatalf("NextBatch error = %v", err)
}
if !reflect.DeepEqual(batch, []string{host}) {
t.Fatalf("batch = %#v, want long host", batch)
}
}
+16 -2
View File
@@ -696,6 +696,18 @@ func TestParseIP_Exclude(t *testing.T) {
}
}
func TestParseIPMultipleExcludeSources(t *testing.T) {
result, err := ParseIP("192.168.1.1-192.168.1.4", "", "192.168.1.2", "192.168.1.4")
if err != nil {
t.Fatalf("ParseIP error = %v", err)
}
expected := []string{"192.168.1.1", "192.168.1.3"}
if !reflect.DeepEqual(result, expected) {
t.Fatalf("ParseIP with multiple excludes = %v, want %v", result, expected)
}
}
// TestParseIP_Deduplicate 测试去重
func TestParseIP_Deduplicate(t *testing.T) {
result, err := ParseIP("192.168.1.1,192.168.1.1,192.168.1.2,192.168.1.2", "", "")
@@ -800,7 +812,9 @@ func TestParsePortRange(t *testing.T) {
// TestExcludeHosts 测试排除主机
func TestExcludeHosts(t *testing.T) {
hosts := []string{"host1", "host2", "host3", "host4"}
exclude := []string{"host2", "host4"}
exclude := newHostMatcher()
exclude.exact["host2"] = struct{}{}
exclude.exact["host4"] = struct{}{}
result := excludeFromList(hosts, exclude)
expected := []string{"host1", "host3"}
@@ -815,7 +829,7 @@ func TestExcludeHosts(t *testing.T) {
// TestExcludeHosts_EmptyExclude 测试空排除列表
func TestExcludeHosts_EmptyExclude(t *testing.T) {
hosts := []string{"host1", "host2"}
result := excludeFromList(hosts, []string{})
result := excludeFromList(hosts, nil)
if !reflect.DeepEqual(result, hosts) {
t.Errorf("excludeFromList(空排除列表) 应该返回原列表")
+19 -14
View File
@@ -53,18 +53,27 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
if host != "" {
hostList, err := parseHostString(host)
if err != nil {
return nil, fmt.Errorf(i18n.GetText("parser_parse_host_failed")+": %w", err)
return nil, fmt.Errorf(i18n.GetText("parser_parse_host_failed")+": %w", err)
}
hosts = append(hosts, hostList...)
}
// 处理排除主机
if len(nohosts) > 0 && nohosts[0] != "" {
excludeList, err := parseHostString(nohosts[0])
if err != nil {
return nil, fmt.Errorf(i18n.GetText("parser_parse_exclude_failed")+": %w", err)
if len(nohosts) > 0 {
matcher := newHostMatcher()
hasExclude := false
for _, exclude := range nohosts {
if strings.TrimSpace(exclude) == "" {
continue
}
hasExclude = true
if err := matcher.add(exclude); err != nil {
return nil, fmt.Errorf(i18n.GetText("parser_parse_exclude_failed")+": %w", err)
}
}
if hasExclude {
hosts = excludeFromList(hosts, matcher)
}
hosts = excludeFromList(hosts, excludeList)
}
// 去重和排序
@@ -212,6 +221,7 @@ func ReadLinesFromFile(filename string) ([]string, error) {
var lines []string
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 64*1024), 4*1024*1024)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" && !strings.HasPrefix(line, "#") {
@@ -420,19 +430,14 @@ func incrementIP(ip net.IP) {
}
// excludeFromList 从列表中排除指定项
func excludeFromList(hosts, excludeList []string) []string {
if len(excludeList) == 0 {
func excludeFromList(hosts []string, matcher *hostMatcher) []string {
if matcher == nil {
return hosts
}
excludeMap := make(map[string]struct{}, len(excludeList))
for _, e := range excludeList {
excludeMap[e] = struct{}{}
}
result := make([]string, 0, len(hosts))
for _, h := range hosts {
if _, found := excludeMap[h]; !found {
if !matcher.match(h) {
result = append(result, h)
}
}