# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Agent module Telemetry module TelemetryException # Here will be all known gems to obfuscate during the building of TelemetryExceptions module Obfuscate # List of known gems to be third parties not containing any # user sensitive data. KNOWN_GEMS = %w[ activesupport redis-activesupport redis builder dry-types rails rails-html-sanitizer rails-observers sinatra benchmark-ips bundler climate_control execjs factory_bot debride fake_ftp fasterer flay openssl parallel_tests contrast contrast-agent ruby pry-byebug byebug pry resolv ougai protobuf async nokogiri benchmark grape-order grape-activerecord newrelic newrelic-grape grape-rails-cache grape-msgpack grape-batch rspec rake rubocop grape-jbuilder rack-test rack-protection minitest grape-instrumentation minitest-global_expectations rack-proxy rack-attack rack-ssl grape-cancan grape-attack grape-rake-tasks grape-route-helpers benchmark-memory grape-rabl grape-kaminari grape-path-helpers grape-swagger-entity grape-swagger-rails grape-roar newrelic-rake grapes-wagger-representative grape-forgery_protection grape-papertrail grape-app grape-middleware-logger rack-protection rake-compile rhino rspec-benchmark rspec_junit_formatter rspec-rails rake-performance rubocop-rails rubocop-rake rubocop-rspec ruby-debug-ide simplecov sqlite steep tilt tzinfo-data warning xpath sinatra-activerecord sinatra-param sinatra-partial sinatra-flash sinatra-reloader sinatra-sinatra rake_fly rack rack-accept grape grape-entity sinatra-advanced-routes sinatra-warden grape_logging grape-swagger sinatra-namespace sinatra-resource sinatra-hashfix sinatra-instrumentation sinatra-helpers redis-rack sinatra-support sinatra-assetpack sinatra-router sinatra-cross_origin sinatra_more sinatra-jsonp faraday-rack zlib mustermann mustermann-grape rspec-expectations rspec-core rspec-mocks rspec-sidekiq ].cs__freeze KNOWN_ERRORS = %w[ ActiveModel Error Errors Resolv Rspec ActiveRecord nil NilClass NoMemoryError ScriptError LoadError NotImplementedError SyntaxError SecurityError SignalException Interrupt StandardError ArgumentError UncaughtThrowError FiberError IOError EOFError IndexError KeyError StopIteration LocalJumpError NameError NoMethodError RangeError FloatDomainError RegexpError RuntimeError FrozenError SystemCallError ThreadError TypeError ZeroDivisionError SystemExit SystemStackError fatal Warning buffer OpenSSL SSL SSLError Errno Timeout Exception EOFError ECONNRESET ECONNREFUSED ETIMEDOUT EINVAL ESHUTDOWN EHOSTDOWN EHOSTUNREACH EISCONN ECONNABORTED ENETRESET ENETUNREACH Net HTTPBadResponse HTTPHeaderSyntaxError ProtocolError Faraday BadRequestError UnauthorizedError ForbiddenError ResourceNotFound ProxyAuthError ConflictError UnprocessableEntityError ClientError ].cs__freeze CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.cs__freeze # This will generate different chars for each agent startup but will be constant # for current run and will make identical obfuscation to be compared if given type # is the same. CYPHER = CHARS.chars.shuffle.join class << self # Returns paths for known gems. # # @return [Array] known paths def known_gem_paths @_known_gem_paths ||= KNOWN_GEMS.map do |gem_name| to_regexp(File.join( 'gems', gem_name.tr('-', ''), 'lib')) end end # Returns known modules names. # # @return [Array] known modules def known_modules @_known_modules ||= KNOWN_ERRORS.map { |module_name| to_regexp(module_name) } end # Obfuscate a type and replace it with random characters. # # @param type [String] the StackFrame type to obfuscate # @return [String] obfuscated type def obfuscate_type type return unless type check = type.tr('[^0-9].-', '') type = cypher(type) unless match_known(known_gem_paths, check) type end # Obfuscate a type and replace it with random characters. # # @param exception_type [String] the exception type to obfuscate # @return [String] obfuscated exception type def obfuscate_exception_type exception_type return unless exception_type exceptions = exception_type.split('::').each do |exception| cypher(exception) unless match_known(known_modules, exception) end exceptions.join('::') end private # Transforms string to regexp # # @param string [String] def to_regexp string /#{ string }/ end # Add cypher to path to make it obscure, but unique enough for # comparisons. Mutates original or duplicate if frozen string. # # @param string [String] string to be transformed. def cypher string string = string.dup if string.cs__frozen? string.to_s.tr!(CHARS, CYPHER) end # @param known [Array] Array of regexp to match againts # @param type [String] type to check def match_known known, type known.any? { |regexp| type =~ regexp } end end end end end end end