@@ -44,11 +44,15 @@ def find_vuln_by_details(details_map, host, service=nil)
4444 other_vulns . empty? ? nil : other_vulns . first
4545 end
4646
47- def find_vuln_by_refs ( refs , host , service = nil , cve_only = true )
47+ def find_vuln_by_refs ( refs , host , service = nil , cve_only = true , resource = nil )
4848 ref_ids = cve_only ? refs . find_all { |ref | ref . name . starts_with? 'CVE-' } : refs
4949 relation = host . vulns . joins ( :refs )
5050 if !service . try ( :id ) . nil?
51- return relation . where ( service_id : service . try ( :id ) , refs : { id : ref_ids } ) . first
51+ if resource
52+ return relation . where ( service_id : service . try ( :id ) , refs : { id : ref_ids } , resource : resource ) . first
53+ else
54+ return relation . where ( service_id : service . try ( :id ) , refs : { id : ref_ids } ) . first
55+ end
5256 end
5357 return relation . where ( refs : { id : ref_ids } ) . first
5458 end
@@ -80,12 +84,20 @@ def has_vuln?(name)
8084 # opts MUST contain
8185 # +:host+:: the host where this vulnerability resides
8286 # +:name+:: the friendly name for this vulnerability (title)
87+ # +:workspace+:: the workspace to report this vulnerability in
8388 #
8489 # opts can contain
8590 # +:info+:: a human readable description of the vuln, free-form text
8691 # +:refs+:: an array of Ref objects or string names of references
8792 # +:details+:: a hash with :key pointed to a find criteria hash and the rest containing VulnDetail fields
8893 # +:sname+:: the name of the service this vulnerability relates to, used to associate it or create it.
94+ # +:exploited_at+:: a timestamp indicating when this vulnerability was exploited, if applicable
95+ # +:ref_ids+:: an array of reference IDs to associate with this vulnerability
96+ # +:service+:: a Mdm::Service object or a Hash with service attributes to associate this vulnerability with
97+ # +:port+:: the port number of the service this vulnerability relates to, if applicable
98+ # +:proto+:: the transport layer protocol of the service this vulnerability relates to, if applicable
99+ # +:details_match+:: a Mdm:VulnDetail with details related to this vulnerability
100+ # +:resource+:: a resource hash to associate with this vulnerability, such as a URI or pipe name
89101 #
90102 def report_vuln ( opts )
91103 return if not active
@@ -141,7 +153,16 @@ def report_vuln(opts)
141153 vuln = nil
142154
143155 # Identify the associated service
144- service = opts . delete ( :service )
156+ service_opt = opts . delete ( :service )
157+ case service_opt
158+ when Mdm ::Service
159+ service = service_opt
160+ when Hash
161+ service = report_service ( service_opt . merge ( workspace : wspace , host : host ) )
162+ else
163+ dlog ( "Skipping service since it is not a Hash or Mdm::Service: #{ service . class } " )
164+ service = nil
165+ end
145166
146167 # Treat port zero as no service
147168 if service or opts [ :port ] . to_i > 0
@@ -160,9 +181,17 @@ def report_vuln(opts)
160181 sname = opts [ :proto ]
161182 end
162183
163- services = host . services . where ( port : opts [ :port ] . to_i , proto : proto )
164- services = services . where ( name : sname ) if sname . present?
165- service = services . first_or_create
184+ # If sname and proto are not provided, this will assign the first service
185+ # registered in the database for this host with the given port and proto.
186+ # This is likely to be the TCP service.
187+ sopts = {
188+ workspace : wspace ,
189+ host : host ,
190+ port : opts [ :port ] . to_i ,
191+ proto : proto
192+ }
193+ sopts [ :name ] = sname if sname . present?
194+ service = report_service ( sopts )
166195 end
167196
168197 # Try to find an existing vulnerability with the same service & references
@@ -172,8 +201,12 @@ def report_vuln(opts)
172201 # prevent dupes of the same vuln found by both local patch and
173202 # service detection.
174203 if rids and rids . length > 0
175- vuln = find_vuln_by_refs ( rids , host , service )
176- vuln . service = service if vuln
204+ if opts [ :resource ]
205+ vuln = find_vuln_by_refs ( rids , host , service , nil , opts [ :resource ] )
206+ else
207+ vuln = find_vuln_by_refs ( rids , host , service )
208+ end
209+ vuln . service = service if vuln && !vuln . service_id?
177210 end
178211 else
179212 # Try to find an existing vulnerability with the same host & references
@@ -194,9 +227,17 @@ def report_vuln(opts)
194227 # No matches, so create a new vuln record
195228 unless vuln
196229 if service
197- vuln = service . vulns . find_by_name ( name )
230+ if opts [ :resource ]
231+ vuln = service . vulns . find_by ( name : name , resource : opts [ :resource ] )
232+ else
233+ vuln = service . vulns . find_by_name ( name )
234+ end
198235 else
199- vuln = host . vulns . find_by_name ( name )
236+ if opts [ :resource ]
237+ vuln = host . vulns . find_by ( name : name , resource : opts [ :resource ] )
238+ else
239+ vuln = host . vulns . find_by_name ( name )
240+ end
200241 end
201242
202243 unless vuln
@@ -208,6 +249,7 @@ def report_vuln(opts)
208249 }
209250
210251 vinf [ :service_id ] = service . id if service
252+ vinf [ :resource ] = opts [ :resource ] if opts [ :resource ]
211253 vuln = Mdm ::Vuln . create ( vinf )
212254
213255 begin
0 commit comments