# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Agent module Assess module Policy module TriggerValidation # Validator used to assert a SSRF finding is actually vulnerable # before serializing that finding as a DTM to report to the service. module SSRFValidator SSRF_RULE = 'ssrf' URL_PATTERN = %r{(?http|https|ftp|sftp|telnet|gopher|rtsp|rtsps|ssh|svn)://(?[^/?]+)(?/?[^?]*)(?\?.*)?}i.cs__freeze # The Net::HTTP class validates host format on instantiation. Since # our triggers for that class are on the instance, they already # have this validation done for them. We do not need to apply the # validation in this case. PATH_ONLY_PATCH_MARKER = 'Assess:Trigger:Net::HTTP#' # A finding is valid for SSRF if the source of the trigger event is # a valid URL in which the User controls a section prior to the # querystring # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/server_side_request_forgery.md def self.valid? patcher, _object, _ret, args return true unless SSRF_RULE == patcher&.rule_id return true if patcher.id.to_s.start_with?(PATH_ONLY_PATCH_MARKER) url = args[0].to_s match = url.match(URL_PATTERN) return false unless match # It is dangerous for the user to control a section of the URL # between the start of the protocol and the beginning of the # querystring. If there is no path, then the entire URL is # dangerous for the User to control. start = match.begin(:protocol) finish = match.begin(:path) finish ||= url.length properties = Contrast::Agent::Assess::Tracker.properties(args[0]) properties.any_tags_between?(start, finish) end end end end end end end