Class: Arachni::Page::DOM
- Defined in:
- lib/arachni/page/dom.rb,
lib/arachni/page/dom/transition.rb
Overview
Static DOM snapshot as computed by a real browser.
Defined Under Namespace
Classes: Error, Transition
Constant Summary collapse
Instance Attribute Summary collapse
- #data_flow_sinks ⇒ Array
-
#digest ⇒ String
String digest of the DOM tree.
- #execution_flow_sinks ⇒ Array
-
#page ⇒ Page
readonly
Page to which this DOM state is attached.
- #skip_states ⇒ Support::LookUp::HashSet
- #transitions ⇒ Array<Transition>
-
#url ⇒ String
URL of the page as seen by the user-agent, fragments and all.
Class Method Summary collapse
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#===(other) ⇒ Bool
‘true` if the compared DOM trees are effectively the same, `false` otherwise.
-
#depth ⇒ Integer
Depth of the current DOM – sum of #transitions Transition#depths that had to be triggered to reach the current state.
- #hash ⇒ Object
-
#initialize(options) ⇒ DOM
constructor
A new instance of DOM.
- #playable_transitions ⇒ Object
- #print_transitions(printer, indent = '') ⇒ Object
- #push_transition(transition) ⇒ Object
-
#restore(browser, take_snapshot = true) ⇒ Browser?
Loads the page and restores it to its captured state.
- #to_h ⇒ Hash
- #to_hash ⇒ Object
-
#to_rpc_data ⇒ Hash
Data representing this instance that are suitable the RPC transmission.
- #to_s ⇒ Object
Constructor Details
#initialize(options) ⇒ DOM
Returns a new instance of DOM.
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/arachni/page/dom.rb', line 57 def initialize( ) @page = [:page] self.url = [:url] || @page.url self.digest = [:digest] @transitions = [:transitions] || [] @data_flow_sinks = [:data_flow_sinks] || [] @execution_flow_sinks = [:execution_flow_sinks] || [] @skip_states = [:skip_states] || Support::LookUp::HashSet.new( hasher: :persistent_hash ) end |
Instance Attribute Details
#data_flow_sinks ⇒ Array
Returns Browser::Javascript::TaintTracer#data_flow_sinks data.
36 37 38 |
# File 'lib/arachni/page/dom.rb', line 36 def data_flow_sinks @data_flow_sinks end |
#digest ⇒ String
Returns String digest of the DOM tree.
44 45 46 |
# File 'lib/arachni/page/dom.rb', line 44 def digest @digest end |
#execution_flow_sinks ⇒ Array
Returns Browser::Javascript::TaintTracer#execution_flow_sinks data.
40 41 42 |
# File 'lib/arachni/page/dom.rb', line 40 def execution_flow_sinks @execution_flow_sinks end |
#page ⇒ Page (readonly)
Returns Page to which this DOM state is attached.
52 53 54 |
# File 'lib/arachni/page/dom.rb', line 52 def page @page end |
#skip_states ⇒ Support::LookUp::HashSet
27 28 29 |
# File 'lib/arachni/page/dom.rb', line 27 def skip_states @skip_states end |
#transitions ⇒ Array<Transition>
Returns Transitions representing the steps required to convert a Arachni::Page::DOM snapshot to a live Browser page.
32 33 34 |
# File 'lib/arachni/page/dom.rb', line 32 def transitions @transitions end |
#url ⇒ String
Returns URL of the page as seen by the user-agent, fragments and all.
48 49 50 |
# File 'lib/arachni/page/dom.rb', line 48 def url @url end |
Class Method Details
.from_rpc_data(data) ⇒ DOM
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/arachni/page/dom.rb', line 228 def self.from_rpc_data( data ) instance = allocate data.each do |name, value| value = case name when 'transitions' value.map { |t| Transition.from_rpc_data t } when 'data_flow_sinks' value.map do |entry| Browser::Javascript::TaintTracer::Sink::DataFlow.from_rpc_data( entry ) end.to_a when 'execution_flow_sinks' value.map do |entry| Browser::Javascript::TaintTracer::Sink::ExecutionFlow.from_rpc_data( entry ) end.to_a when 'skip_states' skip_states = Support::LookUp::HashSet.new( hasher: :persistent_hash ) skip_states.collection.merge( value || [] ) skip_states else value end instance.instance_variable_set( "@#{name}", value ) end instance end |
Instance Method Details
#==(other) ⇒ Object
267 268 269 |
# File 'lib/arachni/page/dom.rb', line 267 def ==( other ) hash == other.hash end |
#===(other) ⇒ Bool
Removes the URL strings of both DOMs from each other’s document before comparing.
Returns ‘true` if the compared DOM trees are effectively the same, `false` otherwise.
277 278 279 |
# File 'lib/arachni/page/dom.rb', line 277 def ===( other ) digest_without_urls( other ) == other.digest_without_urls( self ) end |
#depth ⇒ Integer
Returns Depth of the current DOM – sum of #transitions Arachni::Page::DOM::Transition#depths that had to be triggered to reach the current state.
93 94 95 |
# File 'lib/arachni/page/dom.rb', line 93 def depth @transitions.map { |t| t.depth }.inject(&:+).to_i end |
#hash ⇒ Object
262 263 264 265 |
# File 'lib/arachni/page/dom.rb', line 262 def hash # TODO: Maybe raise error if #digest is not set? digest.persistent_hash end |
#playable_transitions ⇒ Object
97 98 99 |
# File 'lib/arachni/page/dom.rb', line 97 def playable_transitions transitions.select { |t| t.playable? } end |
#print_transitions(printer, indent = '') ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/arachni/page/dom.rb', line 101 def print_transitions( printer, indent = '' ) longest_event_size = 0 page.dom.transitions.each do |t| longest_event_size = [t.event.to_s.size, longest_event_size].max end page.dom.transitions.map do |t| padding = longest_event_size - t.event.to_s.size + 1 time = sprintf( '%.4f', t.time.to_f ) if t.event == :request printer.call "#{indent * 2}* [#{time}s] #{t.event}#{' ' * padding} => #{t.element}" else url = nil if t.[:url] url = "(#{t.[:url]})" end printer.call "#{indent}-- [#{time}s] #{t.event}#{' ' * padding} => #{t.element} #{url}" if t.[:cookies] && t.[:cookies].any? printer.call "#{indent * 2}-- Cookies:" t.[:cookies].each do |name, value| printer.call "#{indent * 3}* #{name}\t=> #{value}\n" end end if t.[:inputs] && t.[:inputs].any? t.[:inputs].each do |name, value| printer.call "#{indent * 2}* #{name}\t=> #{value}\n" end end end end end |
#push_transition(transition) ⇒ Object
86 87 88 |
# File 'lib/arachni/page/dom.rb', line 86 def push_transition( transition ) @transitions << transition end |
#restore(browser, take_snapshot = true) ⇒ Browser?
Loads the page and restores it to its captured state.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/arachni/page/dom.rb', line 145 def restore( browser, take_snapshot = true ) # Preload the associated HTTP response since we've already got it. browser.preload( page ) # First, try to load the page via its DOM#url in case it can restore # itself via its URL fragments and whatnot. browser.goto url, take_snapshot: take_snapshot playables = playable_transitions # If we've got no playable transitions then we're done. return browser if playables.empty? browser_page = browser.to_page # We were probably led to an out-of-scope page via a JS redirect, # bail out. return if !browser_page # Check to see if just loading the DOM URL was enough. # # Of course, this check will fail some of the time because even if the # page can restore itself via its URL (using fragment data most probably), # the document may still be different from when our snapshot was captured. # # However, this check doesn't cost us much so it's worth a shot. if browser_page.dom === self return browser end # The URL restore failed, so, navigate to the pure version of the URL and # replay its transitions. browser.preload( page ) playables.each do |transition| next if transition.play( browser ) browser.print_debug "Could not replay transition for: #{url}" playables.each do |t| browser.print_debug "-#{t == transition ? '>' : '-'} #{transition}" end return end browser end |
#to_h ⇒ Hash
194 195 196 197 198 199 200 201 202 203 |
# File 'lib/arachni/page/dom.rb', line 194 def to_h { url: url, transitions: transitions.map(&:to_hash), digest: digest, skip_states: skip_states, data_flow_sinks: data_flow_sinks.map(&:to_hash), execution_flow_sinks: execution_flow_sinks.map(&:to_hash) } end |
#to_hash ⇒ Object
204 205 206 |
# File 'lib/arachni/page/dom.rb', line 204 def to_hash to_h end |
#to_rpc_data ⇒ Hash
Returns Data representing this instance that are suitable the RPC transmission.
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/arachni/page/dom.rb', line 214 def to_rpc_data { 'url' => url, 'transitions' => transitions.map(&:to_rpc_data), 'digest' => digest, 'skip_states' => skip_states ? skip_states.collection.to_a : [], 'data_flow_sinks' => data_flow_sinks.map(&:to_rpc_data), 'execution_flow_sinks' => execution_flow_sinks.map(&:to_rpc_data) } end |
#to_s ⇒ Object
208 209 210 |
# File 'lib/arachni/page/dom.rb', line 208 def to_s "#<#{self.class}:#{object_id} @url=#{@url.inspect}>" end |