Module: Immunio::Context
- Defined in:
- lib/immunio/context.rb
Constant Summary collapse
- RAILS_TEMPLATE_FILTER =
/(.*(_erb|_haml))__+\d+_\d+(.*)/
- ACTIVESUPPORT_FILTER =
/(.+)_run__\d+__(.+)__\d+__callbacks(.*)/
- FILE_CHECKSUM_CACHE =
Hash.new do |cache, filepath| begin contents = IOHooks.paused { File.read(filepath) } cache[filepath] = Digest::SHA1.hexdigest(contents) rescue StandardError cache[filepath] = "" end end
- @@hash_cache =
Cache for contexts (named in tribute to our buddy Adam Back who invented proof of work)
{}
Class Method Summary collapse
-
.context(additional_data = nil) ⇒ Object
Calculate context hashes and a stack trace.
Class Method Details
.context(additional_data = nil) ⇒ Object
Calculate context hashes and a stack trace. Additional data, in the form of a String, may be provided to mix into the strict context hash.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 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 |
# File 'lib/immunio/context.rb', line 23 def self.context(additional_data=nil) # Filter out agent stack frames. This includes string class_evals in `io`. filtered_stack_frames = caller.reject do |frame| frame =~ /lib\/immunio|^\(eval\):\d+:.+`.+_with_immunio'$/ end stack = filtered_stack_frames.join "\n" cache_key = Digest::SHA1.hexdigest stack if @@hash_cache.has_key?(cache_key) then loose_context = @@hash_cache[cache_key]["loose_context"] strict_context = @@hash_cache[cache_key]["strict_context"] if Immunio.agent.config.log_context_data Immunio.logger.info {"Stack contexts from cache"} end else # Use ropes as they're faster than string concatenation loose_context_rope = [] strict_context_rope = [] # drop the top frame as it's us, but retain the rest. Immunio frames # are filtered by the Gem regex. locations = filtered_stack_frames.map do |frame| frame = frame.split(":", 3) { path: frame[0], line: frame[1], label: frame[2] } end locations.each do |frame| # Filter frame names from template rendering to remove generated random bits template_match = RAILS_TEMPLATE_FILTER.match(frame[:label]) frame[:label] = template_match[1] + template_match[3] if template_match # Filter frame names from activesupport generated callback methods callback_match = ACTIVESUPPORT_FILTER.match(frame[:label]) frame[:label] = callback_match[1] + callback_match[2] + callback_match[3] if callback_match # Reduce paths to be relative to root if possible, to allow # relocation. If there's no rails root, or the path doesn't start with # the rails root, just use the filename part. if defined?(Rails) && defined?(Rails.root) && Rails.root && frame[:path].start_with?(Rails.root.to_s) strict_path = frame[:path].sub(Rails.root.to_s, '') else strict_path = File.basename(frame[:path]) end strict_context_rope << "\n" unless strict_context_rope.empty? strict_context_rope << strict_path strict_context_rope << ":" strict_context_rope << frame[:line] strict_context_rope << ":" strict_context_rope << frame[:label] # Include checksums of file contents in the strict context checksum = FILE_CHECKSUM_CACHE[frame[:path]] strict_context_rope << ":#{checksum}" unless checksum.blank? # Remove pathname from the loose context. The goal here is to prevent # upgrading gem versions from changing the loose context key, so for instance # users don't have to rebuild their whitelists every time they update a gem loose_context_rope << "\n" unless loose_context_rope.empty? loose_context_rope << File.basename(frame[:path]) loose_context_rope << ":" loose_context_rope << frame[:label] end strict_stack = strict_context_rope.join() loose_stack = loose_context_rope.join() if Immunio.agent.config.log_context_data Immunio.logger.info {"Strict context stack:\n#{strict_stack}"} Immunio.logger.info {"Loose context stack:\n#{loose_stack}"} end strict_context = Digest::SHA1.hexdigest(strict_stack) loose_context = Digest::SHA1.hexdigest(loose_stack) @@hash_cache[cache_key] = { "strict_context" => strict_context, "loose_context" => loose_context, } end # Mix in additional context data if additional_data if Immunio.agent.config.log_context_data Immunio.logger.info {"Additional context data:\n#{additional_data}"} end strict_context = Digest::SHA1.hexdigest(strict_context + additional_data) end return strict_context, loose_context, stack end |