11package discovery
22
33import (
4- "fmt"
5- "net"
6- "strings"
7- "time"
8-
9- "github.com/rcourtman/pulse-go-rewrite/internal/config"
10- pkgdiscovery "github.com/rcourtman/pulse-go-rewrite/pkg/discovery"
11- "github.com/rcourtman/pulse-go-rewrite/pkg/discovery/envdetect"
4+ "fmt"
5+ "net"
6+ "strings"
7+ "time"
8+
9+ "github.com/rcourtman/pulse-go-rewrite/internal/config"
10+ pkgdiscovery "github.com/rcourtman/pulse-go-rewrite/pkg/discovery"
11+ "github.com/rcourtman/pulse-go-rewrite/pkg/discovery/envdetect"
1212)
1313
1414// BuildScanner creates a discovery scanner configured using the supplied discovery config.
1515func BuildScanner (cfg config.DiscoveryConfig ) (* pkgdiscovery.Scanner , error ) {
16- cfg = config .NormalizeDiscoveryConfig (cfg )
16+ cfg = config .NormalizeDiscoveryConfig (cfg )
1717
18- profile , err := envdetect .DetectEnvironment ()
19- if err != nil {
20- return nil , err
21- }
18+ profile , err := envdetect .DetectEnvironment ()
19+ if err != nil {
20+ return nil , err
21+ }
2222
23- ApplyConfigToProfile (profile , cfg )
24- return pkgdiscovery .NewScannerWithProfile (profile ), nil
23+ ApplyConfigToProfile (profile , cfg )
24+ return pkgdiscovery .NewScannerWithProfile (profile ), nil
2525}
2626
2727// ApplyConfigToProfile mutates the supplied environment profile according to the discovery config.
2828func ApplyConfigToProfile (profile * envdetect.EnvironmentProfile , cfg config.DiscoveryConfig ) {
29- if profile == nil {
30- return
31- }
32-
33- // Environment override
34- if env , ok := environmentFromOverride (cfg .EnvironmentOverride ); ok {
35- profile .Type = env
36- filterPhasesForEnvironment (profile , env )
37- } else if cfg .EnvironmentOverride != "" && strings .ToLower (cfg .EnvironmentOverride ) != "auto" {
38- profile .Warnings = append (profile .Warnings , fmt .Sprintf ("Unknown environment override: %s" , cfg .EnvironmentOverride ))
39- }
40-
41- // Apply subnet blocklist
42- blocked := parseCIDRMap (cfg .SubnetBlocklist , & profile .Warnings )
43- if len (blocked ) > 0 {
44- var filtered []envdetect.SubnetPhase
45- for _ , phase := range profile .Phases {
46- var kept []net.IPNet
47- for _ , subnet := range phase .Subnets {
48- if _ , blocked := blocked [subnet .String ()]; blocked {
49- continue
50- }
51- kept = append (kept , subnet )
52- }
53- if len (kept ) > 0 {
54- phase .Subnets = kept
55- filtered = append (filtered , phase )
56- }
57- }
58- profile .Phases = filtered
59- }
60-
61- // Apply subnet allowlist as highest priority phase
29+ if profile == nil {
30+ return
31+ }
32+
33+ // Environment override
34+ if env , ok := environmentFromOverride (cfg .EnvironmentOverride ); ok {
35+ profile .Type = env
36+ filterPhasesForEnvironment (profile , env )
37+ } else if cfg .EnvironmentOverride != "" && strings .ToLower (cfg .EnvironmentOverride ) != "auto" {
38+ profile .Warnings = append (profile .Warnings , fmt .Sprintf ("Unknown environment override: %s" , cfg .EnvironmentOverride ))
39+ }
40+
41+ // Apply subnet blocklist
42+ blocked := parseCIDRMap (cfg .SubnetBlocklist , & profile .Warnings )
43+ if len (blocked ) > 0 {
44+ var filtered []envdetect.SubnetPhase
45+ for _ , phase := range profile .Phases {
46+ var kept []net.IPNet
47+ for _ , subnet := range phase .Subnets {
48+ if _ , blocked := blocked [subnet .String ()]; blocked {
49+ continue
50+ }
51+ kept = append (kept , subnet )
52+ }
53+ if len (kept ) > 0 {
54+ phase .Subnets = kept
55+ filtered = append (filtered , phase )
56+ }
57+ }
58+ profile .Phases = filtered
59+ }
60+
61+ // Apply subnet allowlist as highest priority phase
6262 if len (cfg .SubnetAllowlist ) > 0 {
6363 allowlist := parseCIDRs (cfg .SubnetAllowlist , & profile .Warnings )
6464 if len (allowlist ) > 0 {
@@ -79,104 +79,126 @@ func ApplyConfigToProfile(profile *envdetect.EnvironmentProfile, cfg config.Disc
7979 Subnets : allowlist ,
8080 Confidence : 1.0 ,
8181 Priority : 0 ,
82- }
83- profile .Phases = append ([]envdetect.SubnetPhase {allowPhase }, profile .Phases ... )
84- }
85- }
86-
87- // Override scan policy
88- if cfg .MaxHostsPerScan > 0 {
89- profile .Policy .MaxHostsPerScan = cfg .MaxHostsPerScan
90- }
91- if cfg .MaxConcurrent > 0 {
92- profile .Policy .MaxConcurrent = cfg .MaxConcurrent
93- }
94- profile .Policy .EnableReverseDNS = cfg .EnableReverseDNS
95- profile .Policy .ScanGateways = cfg .ScanGateways
96-
97- if cfg .DialTimeout > 0 {
98- profile .Policy .DialTimeout = time .Duration (cfg .DialTimeout ) * time .Millisecond
99- }
100- if cfg .HTTPTimeout > 0 {
101- profile .Policy .HTTPTimeout = time .Duration (cfg .HTTPTimeout ) * time .Millisecond
102- }
82+ }
83+ profile .Phases = append ([]envdetect.SubnetPhase {allowPhase }, profile .Phases ... )
84+ }
85+ }
86+
87+ if len (cfg .SubnetAllowlist ) == 0 && shouldPruneContainerNetworks (profile .Type ) {
88+ pruned := make ([]envdetect.SubnetPhase , 0 , len (profile .Phases ))
89+ for _ , phase := range profile .Phases {
90+ if isLikelyContainerPhase (phase .Name ) {
91+ continue
92+ }
93+ pruned = append (pruned , phase )
94+ }
95+ if len (pruned ) > 0 {
96+ profile .Phases = pruned
97+ }
98+ }
99+
100+ // Override scan policy
101+ if cfg .MaxHostsPerScan > 0 {
102+ profile .Policy .MaxHostsPerScan = cfg .MaxHostsPerScan
103+ }
104+ if cfg .MaxConcurrent > 0 {
105+ profile .Policy .MaxConcurrent = cfg .MaxConcurrent
106+ }
107+ profile .Policy .EnableReverseDNS = cfg .EnableReverseDNS
108+ profile .Policy .ScanGateways = cfg .ScanGateways
109+
110+ if cfg .DialTimeout > 0 {
111+ profile .Policy .DialTimeout = time .Duration (cfg .DialTimeout ) * time .Millisecond
112+ }
113+ if cfg .HTTPTimeout > 0 {
114+ profile .Policy .HTTPTimeout = time .Duration (cfg .HTTPTimeout ) * time .Millisecond
115+ }
116+ }
117+
118+ func shouldPruneContainerNetworks (env envdetect.Environment ) bool {
119+ return env == envdetect .DockerBridge || env == envdetect .LXCUnprivileged
120+ }
121+
122+ func isLikelyContainerPhase (name string ) bool {
123+ name = strings .ToLower (strings .TrimSpace (name ))
124+ return strings .Contains (name , "container" )
103125}
104126
105127func parseCIDRs (values []string , warnings * []string ) []net.IPNet {
106- var subnets []net.IPNet
107- for _ , value := range values {
108- value = strings .TrimSpace (value )
109- if value == "" {
110- continue
111- }
112- _ , ipNet , err := net .ParseCIDR (value )
113- if err != nil {
114- if warnings != nil {
115- * warnings = append (* warnings , fmt .Sprintf ("Invalid CIDR '%s' ignored" , value ))
116- }
117- continue
118- }
119- subnets = append (subnets , * ipNet )
120- }
121- return subnets
128+ var subnets []net.IPNet
129+ for _ , value := range values {
130+ value = strings .TrimSpace (value )
131+ if value == "" {
132+ continue
133+ }
134+ _ , ipNet , err := net .ParseCIDR (value )
135+ if err != nil {
136+ if warnings != nil {
137+ * warnings = append (* warnings , fmt .Sprintf ("Invalid CIDR '%s' ignored" , value ))
138+ }
139+ continue
140+ }
141+ subnets = append (subnets , * ipNet )
142+ }
143+ return subnets
122144}
123145
124146func parseCIDRMap (values []string , warnings * []string ) map [string ]struct {} {
125- cidrs := parseCIDRs (values , warnings )
126- result := make (map [string ]struct {}, len (cidrs ))
127- for _ , cidr := range cidrs {
128- result [cidr .String ()] = struct {}{}
129- }
130- return result
147+ cidrs := parseCIDRs (values , warnings )
148+ result := make (map [string ]struct {}, len (cidrs ))
149+ for _ , cidr := range cidrs {
150+ result [cidr .String ()] = struct {}{}
151+ }
152+ return result
131153}
132154
133155func environmentFromOverride (value string ) (envdetect.Environment , bool ) {
134- normalized := strings .ToLower (strings .TrimSpace (value ))
135- switch normalized {
136- case "" , "auto" :
137- return envdetect .Unknown , false
138- case "native" :
139- return envdetect .Native , true
140- case "docker_host" :
141- return envdetect .DockerHost , true
142- case "docker_bridge" :
143- return envdetect .DockerBridge , true
144- case "lxc_privileged" :
145- return envdetect .LXCPrivileged , true
146- case "lxc_unprivileged" :
147- return envdetect .LXCUnprivileged , true
148- default :
149- return envdetect .Unknown , false
150- }
156+ normalized := strings .ToLower (strings .TrimSpace (value ))
157+ switch normalized {
158+ case "" , "auto" :
159+ return envdetect .Unknown , false
160+ case "native" :
161+ return envdetect .Native , true
162+ case "docker_host" :
163+ return envdetect .DockerHost , true
164+ case "docker_bridge" :
165+ return envdetect .DockerBridge , true
166+ case "lxc_privileged" :
167+ return envdetect .LXCPrivileged , true
168+ case "lxc_unprivileged" :
169+ return envdetect .LXCUnprivileged , true
170+ default :
171+ return envdetect .Unknown , false
172+ }
151173}
152174
153175func filterPhasesForEnvironment (profile * envdetect.EnvironmentProfile , env envdetect.Environment ) {
154- if len (profile .Phases ) == 0 {
155- return
156- }
157-
158- var keep []envdetect.SubnetPhase
159- for _ , phase := range profile .Phases {
160- name := strings .ToLower (phase .Name )
161- switch env {
162- case envdetect .Native , envdetect .DockerHost , envdetect .LXCPrivileged :
163- if strings .Contains (name , "local" ) || strings .Contains (name , "host" ) {
164- keep = append (keep , phase )
165- }
166- case envdetect .DockerBridge :
167- if strings .Contains (name , "container" ) || strings .Contains (name , "inferred" ) || strings .Contains (name , "host" ) {
168- keep = append (keep , phase )
169- }
170- case envdetect .LXCUnprivileged :
171- if strings .Contains (name , "lxc" ) || strings .Contains (name , "container" ) || strings .Contains (name , "parent" ) {
172- keep = append (keep , phase )
173- }
174- default :
175- keep = append (keep , phase )
176- }
177- }
178-
179- if len (keep ) > 0 {
180- profile .Phases = keep
181- }
176+ if len (profile .Phases ) == 0 {
177+ return
178+ }
179+
180+ var keep []envdetect.SubnetPhase
181+ for _ , phase := range profile .Phases {
182+ name := strings .ToLower (phase .Name )
183+ switch env {
184+ case envdetect .Native , envdetect .DockerHost , envdetect .LXCPrivileged :
185+ if strings .Contains (name , "local" ) || strings .Contains (name , "host" ) {
186+ keep = append (keep , phase )
187+ }
188+ case envdetect .DockerBridge :
189+ if strings .Contains (name , "container" ) || strings .Contains (name , "inferred" ) || strings .Contains (name , "host" ) {
190+ keep = append (keep , phase )
191+ }
192+ case envdetect .LXCUnprivileged :
193+ if strings .Contains (name , "lxc" ) || strings .Contains (name , "container" ) || strings .Contains (name , "parent" ) {
194+ keep = append (keep , phase )
195+ }
196+ default :
197+ keep = append (keep , phase )
198+ }
199+ }
200+
201+ if len (keep ) > 0 {
202+ profile .Phases = keep
203+ }
182204}
0 commit comments