mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
feat: 统一服务缓存 + 指纹驱动插件匹配
将 webServiceCache 扩展为通用 serviceCache,所有指纹识别结果 统一缓存,插件匹配时端口不命中则回退到服务名称匹配。 删除多余的 service_cache.go,复用已有的 ServiceInfo 体系。 补充 nil 防御、Explicit 标记、大量单元/集成/回归测试。
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -106,7 +107,7 @@ func configureHTTPProxy(tr *http.Transport, legacyProxy string, networkConfig *c
|
||||
} else if httpProxyURL == ProxyShortcutSocks5 {
|
||||
httpProxyURL = ProxySocks5URL
|
||||
} else if !strings.Contains(httpProxyURL, "://") {
|
||||
httpProxyURL = "http://127.0.0.1:" + httpProxyURL
|
||||
httpProxyURL = normalizeHTTPProxyURL(httpProxyURL)
|
||||
}
|
||||
|
||||
// 验证代理类型
|
||||
@@ -127,6 +128,13 @@ func configureHTTPProxy(tr *http.Transport, legacyProxy string, networkConfig *c
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeHTTPProxyURL(proxyURL string) string {
|
||||
if _, err := strconv.Atoi(proxyURL); err == nil {
|
||||
return "http://127.0.0.1:" + proxyURL
|
||||
}
|
||||
return "http://" + proxyURL
|
||||
}
|
||||
|
||||
// InitHTTPClient 创建HTTP客户端
|
||||
func InitHTTPClient(ThreadsNum int, DownProxy string, Timeout time.Duration, maxRedirects int, networkConfig *common.NetworkConfig) error {
|
||||
// 配置基础连接参数
|
||||
|
||||
+12
-1
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand" //nolint:gosec // G404: math/rand用于生成测试数据,非加密用途
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -176,7 +177,7 @@ func URLTypeToString(u *UrlType) string {
|
||||
builder.WriteString("//")
|
||||
}
|
||||
if host := u.Host; host != "" {
|
||||
builder.WriteString(host)
|
||||
builder.WriteString(urlTypeHost(host))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,6 +526,16 @@ func ParseURL(u *url.URL) *UrlType {
|
||||
}
|
||||
}
|
||||
|
||||
func urlTypeHost(host string) string {
|
||||
if strings.HasPrefix(host, "[") {
|
||||
return host
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil && strings.Contains(host, ":") {
|
||||
return "[" + host + "]"
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
// ParseRequest 将标准 HTTP 请求转换为自定义请求对象
|
||||
func ParseRequest(oReq *http.Request) (*Request, error) {
|
||||
req := &Request{
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package lib
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNormalizeHTTPProxyURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{name: "port shortcut", in: "8080", want: "http://127.0.0.1:8080"},
|
||||
{name: "ipv4 host port", in: "127.0.0.1:8080", want: "http://127.0.0.1:8080"},
|
||||
{name: "hostname port", in: "proxy.local:8080", want: "http://proxy.local:8080"},
|
||||
{name: "bracketed ipv6 port", in: "[::1]:8080", want: "http://[::1]:8080"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := normalizeHTTPProxyURL(tt.in); got != tt.want {
|
||||
t.Fatalf("normalizeHTTPProxyURL(%q) = %q, want %q", tt.in, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -639,6 +639,15 @@ func TestURLTypeToString(t *testing.T) {
|
||||
},
|
||||
expected: "http://example.com/test",
|
||||
},
|
||||
{
|
||||
name: "IPv6 host",
|
||||
url: &UrlType{
|
||||
Scheme: "http",
|
||||
Host: "2001:db8::1",
|
||||
Path: "/test",
|
||||
},
|
||||
expected: "http://[2001:db8::1]/test",
|
||||
},
|
||||
{
|
||||
name: "仅路径",
|
||||
url: &UrlType{
|
||||
|
||||
+28
-1
@@ -107,7 +107,7 @@ func buildTargetURL(info *common.HostInfo) (string, error) {
|
||||
if info.URL == "" {
|
||||
info.URL = protocolHTTP + net.JoinHostPort(info.Host, fmt.Sprint(info.Port))
|
||||
} else if !hasProtocolPrefix(info.URL) {
|
||||
info.URL = protocolHTTP + info.URL
|
||||
info.URL = protocolHTTP + normalizeSchemelessWebTarget(info.URL)
|
||||
}
|
||||
|
||||
// 解析URL以提取基础部分
|
||||
@@ -115,6 +115,7 @@ func buildTargetURL(info *common.HostInfo) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %w", ErrInvalidURL, err)
|
||||
}
|
||||
parsedURL.Host = normalizeWebURLHost(parsedURL.Host)
|
||||
|
||||
return fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host), nil
|
||||
}
|
||||
@@ -125,6 +126,32 @@ func hasProtocolPrefix(urlStr string) bool {
|
||||
return strings.HasPrefix(urlStr, protocolHTTP) || strings.HasPrefix(urlStr, protocolHTTPS)
|
||||
}
|
||||
|
||||
func normalizeSchemelessWebTarget(rawURL string) string {
|
||||
authority := rawURL
|
||||
suffix := ""
|
||||
if idx := strings.IndexAny(rawURL, "/?#"); idx >= 0 {
|
||||
authority = rawURL[:idx]
|
||||
suffix = rawURL[idx:]
|
||||
}
|
||||
if strings.HasPrefix(authority, "[") {
|
||||
return authority + suffix
|
||||
}
|
||||
if ip := net.ParseIP(authority); ip != nil && strings.Contains(authority, ":") {
|
||||
return "[" + authority + "]" + suffix
|
||||
}
|
||||
return rawURL
|
||||
}
|
||||
|
||||
func normalizeWebURLHost(host string) string {
|
||||
if strings.HasPrefix(host, "[") {
|
||||
return host
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil && strings.Contains(host, ":") {
|
||||
return "[" + host + "]"
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
// scanByFingerprints 根据指纹执行POC
|
||||
func scanByFingerprints(ctx context.Context, target string, fingerprints []string, cfg *common.Config, session *common.ScanSession) {
|
||||
for _, fingerprint := range fingerprints {
|
||||
|
||||
@@ -134,6 +134,26 @@ func TestBuildTargetURL(t *testing.T) {
|
||||
expected: "http://[2001:db8::1]:443",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "bare ipv6 url without protocol gets brackets",
|
||||
hostInfo: &common.HostInfo{
|
||||
Host: "2001:db8::1",
|
||||
Port: 80,
|
||||
URL: "2001:db8::1/admin",
|
||||
},
|
||||
expected: "http://[2001:db8::1]",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "bare ipv6 url with protocol gets brackets",
|
||||
hostInfo: &common.HostInfo{
|
||||
Host: "2001:db8::1",
|
||||
Port: 80,
|
||||
URL: "http://2001:db8::1/admin",
|
||||
},
|
||||
expected: "http://[2001:db8::1]",
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user