lib/contrast/agent/assess/policy/trigger_node.rb in contrast-agent-3.8.5 vs lib/contrast/agent/assess/policy/trigger_node.rb in contrast-agent-3.9.0
- old
+ new
@@ -99,17 +99,17 @@
# this condition may not hold true forever, but for now it's
# a nice optimization
return false unless source.cs__tracked?
# find the ranges that violate the rule (untrusted, etc)
- vulnerable_ranges = find_ranges_by_tag(source.cs__properties, required_tags)
+ vulnerable_ranges = find_ranges_by_all_tags(Contrast::Utils::StringUtils.ret_length(source), source.cs__properties, required_tags)
# if there aren't any vulnerable ranges, nope out
return false if vulnerable_ranges.empty?
# find the ranges that are exempt from the rule
# (validated, sanitized, etc)
- secure_ranges = find_ranges_by_tag(source.cs__properties, disallowed_tags)
+ secure_ranges = find_ranges_by_any_tag(source.cs__properties, disallowed_tags)
# if there are vulnerable ranges and no secure, report
return true if secure_ranges.empty?
# figure out if there are any vulnerable ranges that aren't
# covered by a secure one. if there are, the rule was violated
@@ -133,11 +133,11 @@
UNTRUSTED = 'UNTRUSTED'
def populate_tags required_tags
return unless dataflow?
validate_rule_tags(required_tags)
- @required_tags = required_tags || []
+ @required_tags = Set.new(required_tags)
@required_tags << UNTRUSTED
end
ENCODER_START = 'CUSTOM_ENCODED_'
VALIDATOR_START = 'CUSTOM_VALIDATED_'
@@ -151,11 +151,11 @@
CUSTOM_VALIDATED = 'CUSTOM_VALIDATED'
def populate_disallowed disallowed_tags
return unless dataflow?
validate_rule_tags(disallowed_tags)
- @disallowed_tags = disallowed_tags || []
+ @disallowed_tags = Set.new(disallowed_tags)
@disallowed_tags << LIMITED_CHARS
@disallowed_tags << CUSTOM_ENCODED
@disallowed_tags << CUSTOM_VALIDATED
@disallowed_tags << ENCODER_START + loud_name
@disallowed_tags << VALIDATOR_START + loud_name
@@ -170,26 +170,71 @@
Contrast::Agent::Assess::Policy::PolicyNode::VALID_TAGS.include?(tag) ||
Contrast::Agent::Assess::Policy::PolicyNode::VALID_SOURCE_TAGS.include?(tag)
end
end
- def find_ranges_by_tag cs__properties, tags
+ # Find the ranges that satisfy all of the given tags.
+ #
+ # @param length [Integer] the length of the object which may have the
+ # given tags -- used as the maximum index to search for all of the
+ # tags.
+ # @param cs__properties [Contrast::Agent::Assess::Properties] the
+ # properties to check for the tags
+ # @param tags [Set<String>] the list of tags on which to match
+ # @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
+ # by the given conditions
+ def find_ranges_by_all_tags length, cs__properties, tags
+ # if there aren't any all_tags or tags, break early
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless cs__properties.tracked?
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tags&.any?
+
+ # :zap: faster to treat all as any if there's only one tag
+ return find_ranges_by_any_tag(cs__properties, tags) if tags.length == 1
+
ranges = []
+ # find all the indicies on the source that have all the given tags
+ (0..length).each do |idx|
+ tags_at = cs__properties.tag_names_at(idx)
+ ranges << idx if tags.all? { |tag| tags_at.include?(tag) }
+ end
+ # break early if no indicies satisfy all the tags
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY if ranges.empty?
+ # chunk all the adjacent ranges
+ chunked = ranges.chunk_while { |i, j| i + 1 == j }
+ tag_ranges = []
+ # and convert them into Tags
+ chunked.each do |join|
+ start = join[0]
+ stop = join[-1]
+ # add the 1 to account for end index being exclusive
+ tag_length = stop - start + 1
+ tag_ranges = Contrast::Utils::TagUtil.ordered_merge(tag_ranges, Tag.new(tag_length, start))
+ end
+ tag_ranges
+ end
+
+ # Find the ranges that satisfy any of the given tags.
+ #
+ # @param cs__properties [Contrast::Agent::Assess::Properties] the
+ # properties to check for the tags
+ # @param tags [Set<String>] the list of tags on which to match
+ # @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
+ # by the given conditions
+ def find_ranges_by_any_tag cs__properties, tags
# if there aren't any all_tags or tags, break early
- return ranges unless cs__properties.tracked?
- return ranges unless tags&.any?
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless cs__properties.tracked?
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tags&.any?
- # find all tags that match the desired ones
+ ranges = []
tags.each do |desired|
found = cs__properties.fetch_tag(desired)
next unless found
# we need to dup here so that we don't change the tags if target is
# used in another trace
ranges = Contrast::Utils::TagUtil.ordered_merge(ranges, found.dup)
end
-
ranges
end
end
end
end