mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
Harden scan robustness and tests
This commit is contained in:
+47
-5
@@ -113,8 +113,8 @@ func (s *ResultStore) Add(result interface{}) *ResultItem {
|
||||
if details, ok := m["details"].(map[string]interface{}); ok {
|
||||
item.Details = details
|
||||
if port, ok := details["port"]; ok {
|
||||
if item.Target != "" && !strings.Contains(item.Target, ":") {
|
||||
item.Target = fmt.Sprintf("%s:%v", item.Target, port)
|
||||
if target := targetWithDetailsPort(item.Target, port); target != "" {
|
||||
item.Target = target
|
||||
}
|
||||
}
|
||||
item.Status = buildStatusFromDetails(item.Type, item.Status, details)
|
||||
@@ -165,6 +165,37 @@ func (s *ResultStore) Add(result interface{}) *ResultItem {
|
||||
return &item
|
||||
}
|
||||
|
||||
func targetWithDetailsPort(target string, port interface{}) string {
|
||||
if target == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.Contains(target, "://") || strings.ContainsAny(target, "/?#") {
|
||||
return ""
|
||||
}
|
||||
if _, _, ok := splitTargetHostPort(target); ok {
|
||||
return ""
|
||||
}
|
||||
if strings.Contains(target, ":") {
|
||||
hostForIP := target
|
||||
if strings.HasPrefix(hostForIP, "[") && strings.HasSuffix(hostForIP, "]") {
|
||||
hostForIP = strings.TrimPrefix(strings.TrimSuffix(hostForIP, "]"), "[")
|
||||
}
|
||||
if net.ParseIP(hostForIP) == nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
portText := strings.TrimSpace(fmt.Sprint(port))
|
||||
portNum, err := strconv.Atoi(portText)
|
||||
if err != nil || portNum < 1 || portNum > 65535 {
|
||||
return ""
|
||||
}
|
||||
host := target
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = strings.TrimPrefix(strings.TrimSuffix(host, "]"), "[")
|
||||
}
|
||||
return net.JoinHostPort(host, portText)
|
||||
}
|
||||
|
||||
// List 获取所有结果
|
||||
func (s *ResultStore) List() []ResultItem {
|
||||
s.mu.RLock()
|
||||
@@ -444,14 +475,25 @@ func extractServiceInfo(details interface{}) (service, version, banner string) {
|
||||
}
|
||||
if b, ok := m["banner"].(string); ok {
|
||||
banner = escapeControlChars(b)
|
||||
if len(banner) > 100 {
|
||||
banner = banner[:100] + "..."
|
||||
}
|
||||
banner = truncateString(banner, 100)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func truncateString(s string, maxRunes int) string {
|
||||
if maxRunes < 0 {
|
||||
return s
|
||||
}
|
||||
for i := range s {
|
||||
if maxRunes == 0 {
|
||||
return s[:i] + "..."
|
||||
}
|
||||
maxRunes--
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// extractVulnType 从 details 中提取漏洞类型
|
||||
func extractVulnType(details interface{}) string {
|
||||
if m, ok := details.(map[string]interface{}); ok {
|
||||
|
||||
+44
-1
@@ -2,7 +2,10 @@
|
||||
|
||||
package api
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestExtractHostPortIPv6(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -29,3 +32,43 @@ func TestExtractHostPortIPv6(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetWithDetailsPort(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target string
|
||||
port interface{}
|
||||
want string
|
||||
}{
|
||||
{name: "hostname", target: "example.com", port: 443, want: "example.com:443"},
|
||||
{name: "ipv4", target: "192.168.1.1", port: "80", want: "192.168.1.1:80"},
|
||||
{name: "bare ipv6", target: "2001:db8::1", port: 8443, want: "[2001:db8::1]:8443"},
|
||||
{name: "bracketed ipv6", target: "[2001:db8::1]", port: 8443, want: "[2001:db8::1]:8443"},
|
||||
{name: "already has port", target: "[2001:db8::1]:8443", port: 9443, want: ""},
|
||||
{name: "invalid colon target", target: "example.com:abc", port: 80, want: ""},
|
||||
{name: "url target", target: "http://example.com", port: 80, want: ""},
|
||||
{name: "bad port", target: "example.com", port: 70000, want: ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := targetWithDetailsPort(tt.target, tt.port); got != tt.want {
|
||||
t.Fatalf("targetWithDetailsPort(%q, %v) = %q, want %q", tt.target, tt.port, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractServiceInfoTruncatesBannerByRune(t *testing.T) {
|
||||
banner := ""
|
||||
for i := 0; i < 105; i++ {
|
||||
banner += "界"
|
||||
}
|
||||
_, _, got := extractServiceInfo(map[string]interface{}{"banner": banner})
|
||||
if !utf8.ValidString(got) {
|
||||
t.Fatalf("banner is not valid utf8: %q", got)
|
||||
}
|
||||
if len([]rune(got)) != 103 || got[len(got)-3:] != "..." {
|
||||
t.Fatalf("banner = %q, rune len %d", got, len([]rune(got)))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user