Module: Contrast::Agent::Assess::Property::Tagged
- Included in:
- Contrast::Agent::Assess::Properties
- Defined in:
- lib/contrast/agent/assess/property/tagged.rb
Overview
This module serves to hold the functionality required for the management of our dataflow tags.
Instance Method Summary collapse
-
#add_tag(label, range) ⇒ Object
Given a tag name and range object, add a new tag to this collection.
-
#any_tags_between?(start, finish) ⇒ Boolean
Similar to #tracked?, but limited to a given range.
-
#cleanup_tags ⇒ Object
Calls merge to combine touching or overlapping tags Deletes empty tags.
-
#clear_tags ⇒ Object
Reset the tag hash.
-
#delete_tags(label) ⇒ Object
Remove all tags with a given label.
-
#delete_tags_at_ranges(ranges, shift = true) ⇒ Object
Remove all tags within the given ranges.
-
#fetch_tag(label) ⇒ Array<Contrast::Agent::Assess::Tag>
We’ll use this as a helper method to retrieve tags from the hash.
-
#remove_tags(range) ⇒ Object
Remove the tag ranges covering the given range.
- #set_tags(label, tag_ranges) ⇒ Object
-
#shift_tags(ranges) ⇒ Object
Shift all the tags in this object by the given ranges.
-
#shift_tags_for_deletion(range, shift) ⇒ Object
Shift the tag ranges covering the given range We assume this is for a deletion, meaning we have to move tags to the left.
-
#shift_tags_for_insertion(range) ⇒ Object
Shift the tag ranges covering the given range We assume this is for a insertion, meaning we have to move tags to the right.
-
#tag_keys ⇒ Object
Returns a list of all current tag labels, most likely to be used for a splat operation.
-
#tagged?(label) ⇒ Boolean
Is the given tag present? Used in testing, so found by ‘be_tagged`, if you’re grepping for it.
-
#tags ⇒ Object
Because of the auto-fill thing, we should not allow direct access to the tags hash.
-
#tags_at(idx) ⇒ Array<Contrast::Agent::Assess::Tag>
Find all of the ranges that span a given index.
-
#tags_at_range(range) ⇒ Hash{String => Contrast::Agent::Assess::Tag}
given a range, select all tags in that range the selected tags are shifted such that the start index of the new tag (0) aligns with the given start index in the range.
-
#tags_to_dtm ⇒ Array<Contrast::Api::Dtm::TraceTaintRange>
Convert the tags of this object into the TraceTaintRange required to be sent to the service.
-
#tracked? ⇒ Boolean
Is any tag present? Creating Tags is expensive and we check for Tags all the time on untracked things.
Instance Method Details
#add_tag(label, range) ⇒ Object
Given a tag name and range object, add a new tag to this collection. If the given range touches an existing tag, we’ll combine the two, adjusting the existing one and dropping this new one.
126 127 128 129 130 131 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 126 def add_tag label, range length = range.end - range.begin tag = Contrast::Agent::Assess::Tag.new(label, length, range.begin) existing = fetch_tag(label) [label] = Contrast::Utils::TagUtil.ordered_merge(existing, tag) end |
#any_tags_between?(start, finish) ⇒ Boolean
Similar to #tracked?, but limited to a given range.
39 40 41 42 43 44 45 46 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 39 def start, finish return false unless tracked? .each_value do |tag_array| return true if tag_array.any? { |tag| tag.overlaps?(start, finish) } end false end |
#cleanup_tags ⇒ Object
Calls merge to combine touching or overlapping tags Deletes empty tags
157 158 159 160 161 162 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 157 def return unless tracked? Contrast::Utils::TagUtil.() .delete_if { |_, value| value.empty? } end |
#clear_tags ⇒ Object
Reset the tag hash
143 144 145 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 143 def .clear if tracked? end |
#delete_tags(label) ⇒ Object
Remove all tags with a given label
138 139 140 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 138 def label .delete(label) if tracked? end |
#delete_tags_at_ranges(ranges, shift = true) ⇒ Object
Remove all tags within the given ranges. This does not delete an entire tag if part of that tag is outside this range, meaning we may reduce sizes of tags or split them.
If shift is true, it is assumed the characters at those ranges were removed. If shift is false, it is assumed those ranges were replaced by the same number of characters and no shift is needed.
current tags: 0-15 range: 5-10 result: 0-5, 10-15
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 200 def ranges, shift = true return unless tracked? # Stage one - delete the tags w/o changing their # location. ranges.each do |range| (range) end return unless shift # the amount we've already removed from the string shift = 0 # Stage two - shift the tags to the left to account # for the sections that were deleted. ranges.each do |range| (range, shift) shift += (range.end - range.begin) end # Clean up and merge any touching tags Contrast::Utils::TagUtil.() end |
#fetch_tag(label) ⇒ Array<Contrast::Agent::Assess::Tag>
We’ll use this as a helper method to retrieve tags from the hash. Because the hash auto-populates an empty array when we try to access a tag in it, we cannot use the [] method without side effect. To get around this, we’ll use a fetch work around.
172 173 174 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 172 def fetch_tag label .fetch(label, nil) if tracked? end |
#remove_tags(range) ⇒ Object
Remove the tag ranges covering the given range
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 252 def range return unless tracked? full_delete = [] .each_pair do |key, value| remove = [] add = [] value.each do |tag| comparison = tag.compare_range(range.begin, range.end) # ABOVE and BELOW are not affected by this check case comparison when Contrast::Agent::Assess::Tag::LOW_SPAN tag.update_end(range.begin) when Contrast::Agent::Assess::Tag::WITHIN remove << tag when Contrast::Agent::Assess::Tag::WITHOUT new_tag = tag.clone new_tag.update_start(range.end) add << new_tag tag.update_end(range.begin) when Contrast::Agent::Assess::Tag::HIGH_SPAN tag.update_start(range.end) end end value.delete_if { |tag| remove.include?(tag) } Contrast::Utils::TagUtil.ordered_merge(value, add) full_delete << key if value.empty? end full_delete.each { |key| .delete(key) } end |
#set_tags(label, tag_ranges) ⇒ Object
133 134 135 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 133 def label, tag_ranges [label] = tag_ranges end |
#shift_tags(ranges) ⇒ Object
Shift all the tags in this object by the given ranges. This method assumes the ranges are sorted, meaning the leftmost (lowest) range is first
current tags: 0-15 range: 5-10 result: 0-5, 10-20
230 231 232 233 234 235 236 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 230 def ranges return unless tracked? ranges.each do |range| (range) end end |
#shift_tags_for_deletion(range, shift) ⇒ Object
Shift the tag ranges covering the given range We assume this is for a deletion, meaning we have to move tags to the left
289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 289 def range, shift return unless tracked? .each_value do |value| value.each do |tag| comparison = tag.compare_range(range.begin - shift, range.end - shift) # this is really the only thing we need to shift next unless comparison == Contrast::Agent::Assess::Tag::ABOVE length = range.end - range.begin tag.shift(0 - length) end end end |
#shift_tags_for_insertion(range) ⇒ Object
Shift the tag ranges covering the given range We assume this is for a insertion, meaning we have to move tags to the right
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 307 def range return unless tracked? .each_value do |value| add = [] value.each do |tag| comparison = tag.compare_range(range.begin, range.end) length = range.end - range.begin # BELOW is not affected by this check case comparison # part of the tag is being inserted on when Contrast::Agent::Assess::Tag::LOW_SPAN new_tag = tag.clone new_tag.update_start(range.begin) new_tag.shift(length) add << new_tag tag.update_end(range.begin) # the tag exists in the inserted range. it is partially shifted when Contrast::Agent::Assess::Tag::WITHIN tag.shift(length) # the tag spans the range. leave the part below alone when Contrast::Agent::Assess::Tag::WITHOUT new_tag = tag.clone new_tag.update_start(range.begin) new_tag.shift(length) add << new_tag tag.update_end(range.begin) when Contrast::Agent::Assess::Tag::HIGH_SPAN, Contrast::Agent::Assess::Tag::ABOVE tag.shift(length) end end Contrast::Utils::TagUtil.ordered_merge(value, add) end end |
#tag_keys ⇒ Object
Returns a list of all current tag labels, most likely to be used for a splat operation
149 150 151 152 153 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 149 def tag_keys return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tracked? .keys end |
#tagged?(label) ⇒ Boolean
Is the given tag present? Used in testing, so found by ‘be_tagged`, if you’re grepping for it
30 31 32 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 30 def tagged? label tracked? && .key?(label) end |
#tags ⇒ Object
Because of the auto-fill thing, we should not allow direct access to the tags hash. Instead, the methods above should be used to do operations like add, delete, and fetch.
CONTRAST-22914 please do NOT expose this w/ an attr_reader / accessor. there are helper methods in this class that safely access the hash. the tags method is private to avoid the side effect of a direct lookup with ‘[]` adding an empty array to the hash.
247 248 249 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 247 def @_tags ||= Hash.new { |h, k| h[k] = [] } end |
#tags_at(idx) ⇒ Array<Contrast::Agent::Assess::Tag>
Find all of the ranges that span a given index. This is used in propagation when we need to shift tags about. For instance, in the append method when we need to see if any tag at the end needs to be expanded out to the size of the new String.
Note: Tags do not know their key, so this is only the range covered
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 58 def idx return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tracked? at = [] .each_value do |tag_array| tag_array.each do |tag| if tag.covers?(idx) at << tag elsif tag.above?(idx) break end end end at end |
#tags_at_range(range) ⇒ Hash{String => Contrast::Agent::Assess::Tag}
given a range, select all tags in that range the selected tags are shifted such that the start index of the new tag (0) aligns with the given start index in the range
current tags: 5-15 range : 5-10 result : 0-05
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 85 def range return Contrast::Utils::ObjectShare::EMPTY_HASH unless tracked? at = Hash.new { |h, k| h[k] = [] } .each_pair do |key, value| add = [] value.each do |tag| comparison = tag.compare_range(range.begin, range.end) # BELOW and ABOVE are applicable to this check and are removed. case comparison # part of the tag is being selected when Contrast::Agent::Assess::Tag::LOW_SPAN add << Contrast::Agent::Assess::Tag.new(tag.label, range.size) # the tag exists in the requested range, figure out the boundaries when Contrast::Agent::Assess::Tag::WITHIN start = tag.start_idx - range.begin finish = range.size - start add << Contrast::Agent::Assess::Tag.new(tag.label, finish, start) # the tag spans the requested range. when Contrast::Agent::Assess::Tag::WITHOUT add << Contrast::Agent::Assess::Tag.new(tag.label, range.size) # part of the tag is being selected when Contrast::Agent::Assess::Tag::HIGH_SPAN add << Contrast::Agent::Assess::Tag.new(tag.label, range.size) end end next if add.empty? at[key] = add end at end |
#tags_to_dtm ⇒ Array<Contrast::Api::Dtm::TraceTaintRange>
Convert the tags of this object into the TraceTaintRange required to be sent to the service
180 181 182 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 180 def Contrast::Api::Dtm::TraceTaintRange.build_for_event() end |
#tracked? ⇒ Boolean
Is any tag present? Creating Tags is expensive and we check for Tags all the time on untracked things. ALWAYS!!! call this method before checking if an object has tags
21 22 23 |
# File 'lib/contrast/agent/assess/property/tagged.rb', line 21 def tracked? instance_variable_defined?(:@_tags) && .any? end |