Class: Rhales::SafeInjectionValidator
- Inherits:
-
Object
- Object
- Rhales::SafeInjectionValidator
- Defined in:
- lib/rhales/safe_injection_validator.rb
Overview
Validates whether a hydration injection point is safe within HTML context Prevents injection inside script tags, style tags, comments, or other unsafe locations
Constant Summary collapse
- UNSAFE_CONTEXTS =
[ { start: /<script\b[^>]*>/i, end: /<\/script>/i }, { start: /<style\b[^>]*>/i, end: /<\/style>/i }, { start: /<!--/, end: /-->/ }, { start: /<!\[CDATA\[/, end: /\]\]>/ } ].freeze
Instance Method Summary collapse
-
#at_tag_boundary?(position) ⇒ Boolean
private
Check if position is at a tag boundary (before < or after >).
-
#calculate_unsafe_ranges ⇒ Object
private
-
#initialize(html) ⇒ SafeInjectionValidator
constructor
A new instance of SafeInjectionValidator.
-
#nearest_safe_point_after(position) ⇒ Object
Find the nearest safe injection point after the given position.
-
#nearest_safe_point_before(position) ⇒ Object
Find the nearest safe injection point before the given position.
-
#next_non_whitespace_is_tag?(position) ⇒ Boolean
private
-
#safe_injection_point?(position) ⇒ Boolean
Check if the given position is safe for injection.
Constructor Details
#initialize(html) ⇒ SafeInjectionValidator
Returns a new instance of SafeInjectionValidator.
14 15 16 17 |
# File 'lib/rhales/safe_injection_validator.rb', line 14 def initialize(html) @html = html @unsafe_ranges = calculate_unsafe_ranges end |
Instance Method Details
#at_tag_boundary?(position) ⇒ Boolean (private)
Check if position is at a tag boundary (before < or after >)
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/rhales/safe_injection_validator.rb', line 77 def at_tag_boundary?(position) return true if position == 0 || position == @html.length char_before = position > 0 ? @html[position - 1] : nil char_at = @html[position] # Safe positions: # - Right after a closing > # - Right before an opening < # - At whitespace boundaries between tags char_before == '>' || char_at == '<' || (char_at&.match?(/\s/) && next_non_whitespace_is_tag?(position)) end |
#calculate_unsafe_ranges ⇒ Object (private)
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/rhales/safe_injection_validator.rb', line 51 def calculate_unsafe_ranges ranges = [] scanner = StringScanner.new(@html) UNSAFE_CONTEXTS.each do |context| scanner.pos = 0 while scanner.scan_until(context[:start]) start_pos = scanner.pos - scanner.matched.length # Find the corresponding end tag if scanner.scan_until(context[:end]) end_pos = scanner.pos ranges << (start_pos...end_pos) else # If no closing tag found, consider rest of document unsafe ranges << (start_pos...@html.length) break end end end ranges end |
#nearest_safe_point_after(position) ⇒ Object
Find the nearest safe injection point after the given position
39 40 41 42 43 44 45 46 47 |
# File 'lib/rhales/safe_injection_validator.rb', line 39 def nearest_safe_point_after(position) # Work forwards from position to find a safe point (position...@html.length).each do |pos| return pos if safe_injection_point?(pos) && at_tag_boundary?(pos) end # If no safe point found after, return nil nil end |
#nearest_safe_point_before(position) ⇒ Object
Find the nearest safe injection point before the given position
28 29 30 31 32 33 34 35 36 |
# File 'lib/rhales/safe_injection_validator.rb', line 28 def nearest_safe_point_before(position) # Work backwards from position to find a safe point (0...position).reverse_each do |pos| return pos if safe_injection_point?(pos) && at_tag_boundary?(pos) end # If no safe point found before, return nil nil end |
#next_non_whitespace_is_tag?(position) ⇒ Boolean (private)
90 91 92 93 94 95 96 97 |
# File 'lib/rhales/safe_injection_validator.rb', line 90 def next_non_whitespace_is_tag?(position) pos = position while pos < @html.length && @html[pos].match?(/\s/) pos += 1 end pos < @html.length && @html[pos] == '<' end |
#safe_injection_point?(position) ⇒ Boolean
Check if the given position is safe for injection
20 21 22 23 24 25 |
# File 'lib/rhales/safe_injection_validator.rb', line 20 def safe_injection_point?(position) return false if position < 0 || position > @html.length # Check if position falls within any unsafe range @unsafe_ranges.none? { |range| range.cover?(position) } end |