fix(scope): add_task_scope 支持 host:port 目标,入库前剥端口
ip/root_domain/subdomain 三类范围入库前统一 stripHostPort: - ip 带端口(如 10.0.188.136:3000)之前直接报"无效 ip/cidr" - 域名带端口更隐蔽,不报错却存下匹配不到资产的垃圾键 Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
e00a4a8e72
commit
a121e8967c
2 files changed
+39
-3
No files matched your search
+16
-3
@@ -22,6 +22,19 @@ type TaskScope struct {
|
|||||||
Reason string `json:"reason,omitempty"`
|
Reason string `json:"reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stripHostPort drops a trailing :port from a host:port / ip:port / [ipv6]:port
|
||||||
|
// value, returning the bare host. Bare hosts, bare IPs (v4 or v6, whose colons make
|
||||||
|
// them ambiguous), and anything not in host:port form are returned unchanged. Scope
|
||||||
|
// is host/net based, so the port from a target like "10.0.188.136:3000" or
|
||||||
|
// "api.example.com:8080" is simply discarded rather than baked into the key.
|
||||||
|
func stripHostPort(v string) string {
|
||||||
|
v = strings.TrimSpace(v)
|
||||||
|
if host, _, err := net.SplitHostPort(v); err == nil {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
// ipToHostCIDR turns a bare IP into its single-host CIDR (/32 or /128). "" if invalid.
|
// ipToHostCIDR turns a bare IP into its single-host CIDR (/32 or /128). "" if invalid.
|
||||||
func ipToHostCIDR(ip string) string {
|
func ipToHostCIDR(ip string) string {
|
||||||
ip = strings.TrimSpace(ip)
|
ip = strings.TrimSpace(ip)
|
||||||
@@ -132,7 +145,7 @@ func (s *AssetStore) AddAgentScope(taskID int64, kind, value, reason string) (Ta
|
|||||||
}
|
}
|
||||||
ts.CompanyID = &comp.ID
|
ts.CompanyID = &comp.ID
|
||||||
case "root_domain":
|
case "root_domain":
|
||||||
d := DomainKey(value)
|
d := DomainKey(stripHostPort(value))
|
||||||
root, _ := RootDomain(d)
|
root, _ := RootDomain(d)
|
||||||
if root == "" {
|
if root == "" {
|
||||||
root = d
|
root = d
|
||||||
@@ -142,7 +155,7 @@ func (s *AssetStore) AddAgentScope(taskID int64, kind, value, reason string) (Ta
|
|||||||
}
|
}
|
||||||
ts.Domain = root
|
ts.Domain = root
|
||||||
case "subdomain":
|
case "subdomain":
|
||||||
d := DomainKey(value)
|
d := DomainKey(stripHostPort(value))
|
||||||
if d == "" {
|
if d == "" {
|
||||||
return ts, fmt.Errorf("无效子域: %s", value)
|
return ts, fmt.Errorf("无效子域: %s", value)
|
||||||
}
|
}
|
||||||
@@ -150,7 +163,7 @@ func (s *AssetStore) AddAgentScope(taskID int64, kind, value, reason string) (Ta
|
|||||||
case "ip", "cidr":
|
case "ip", "cidr":
|
||||||
v := value
|
v := value
|
||||||
if !strings.Contains(v, "/") {
|
if !strings.Contains(v, "/") {
|
||||||
v = ipToHostCIDR(v)
|
v = ipToHostCIDR(stripHostPort(v))
|
||||||
ts.Kind = "ip"
|
ts.Kind = "ip"
|
||||||
} else {
|
} else {
|
||||||
ts.Kind = "cidr"
|
ts.Kind = "cidr"
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// TestStripHostPort covers the ip:port / [ipv6]:port stripping used by the
|
||||||
|
// ip/cidr scope path so a target like "10.0.188.136:3000" no longer 404s.
|
||||||
|
func TestStripHostPort(t *testing.T) {
|
||||||
|
cases := []struct{ in, want string }{
|
||||||
|
{"10.0.188.136:3000", "10.0.188.136"}, // the reported IP case
|
||||||
|
{"10.0.188.136", "10.0.188.136"}, // bare IPv4 unchanged
|
||||||
|
{"[2001:db8::1]:8080", "2001:db8::1"}, // bracketed IPv6 + port
|
||||||
|
{"2001:db8::1", "2001:db8::1"}, // bare IPv6 unchanged (has colons)
|
||||||
|
{" 1.2.3.4:80 ", "1.2.3.4"}, // trims surrounding space
|
||||||
|
{"example.com:443", "example.com"}, // domain + port → bare host
|
||||||
|
{"api.example.com:8080", "api.example.com"}, // subdomain + port
|
||||||
|
{"example.com", "example.com"}, // bare domain unchanged
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
if got := stripHostPort(c.in); got != c.want {
|
||||||
|
t.Errorf("stripHostPort(%q) = %q, want %q", c.in, got, c.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user