class Rack::Prevoty::ContentMiddleware
Public Class Methods
build_result(mode, request, input, result)
click to toggle source
# File lib/rack/content_middleware.rb, line 109 def self.build_result(mode, request, input, result) data = { product: 'content', mode: mode, version: '1', input: input, timestamp: Time.now.utc.strftime('%b %d %Y %H:%M:%S %Z'), request_url: request.path, session_id: request.session["session_id"], cookies: request.cookies, http_method: request.request_method, src_ip: request.ip, dest_host: request.host, dest_port: request.port } # these are hacks due to differences between protect and monitor if mode === 'protect' data[:statistics] = result.statistics data[:output] = CGI::unescape(result.output) else data[:statistics] = result end ::Prevoty::ContentPayload.new(data) end
new(app, opts)
click to toggle source
# File lib/rack/content_middleware.rb, line 9 def initialize(app, opts) @app = app @base = opts[:api_base] ||= 'https://api.prevoty.com' @client = ::Prevoty::Client.new(opts[:api_key], @base) @policy_key = opts[:policy_key] ||= '' @mode = opts[:mode] ||= 'monitor' @log_verbosity = opts[:log_verbosity] ||= 'incident' @paths = opts[:paths] ||= ['/'] @blacklist = opts[:blacklist] ||= [] @traffic_percentage = opts[:traffic_percentage] ||= 100 if @mode === 'monitor' @monitor = ::Prevoty::ContentMonitor.new(@client, opts) end end
Public Instance Methods
call(env)
click to toggle source
# File lib/rack/content_middleware.rb, line 24 def call(env) req = Rack::Request.new(env) # Thread local storage for communicating between content and query Thread.current[:request_storage] = {} # only wrapping this in a begin so we can use ensure to clean out # thread storage begin if rand(100) > @traffic_percentage Thread.current[:request_storage][:skip_processing] = true return @app.call(env) end # passthru if not listed in paths return @app.call(env) if @paths.detect {|p| req.path.start_with?(p)}.nil? # passthru if blacklisted return @app.call(env) unless @blacklist.detect {|p| req.path.start_with?(p)}.nil? # TODO: implement support for multipart. The Rack multipart # implementation doesn't support parsing and re-creating the # mutlipart data so a custom implementation needs to be written return @app.call(env) if req.media_type === 'multipart/form-data' unless env['QUERY_STRING'].empty? querystring = env['QUERY_STRING'] if @mode === 'protect' begin Timeout::timeout(@timeout) do resp = nil if defined? ActiveSupport::Notifications ActiveSupport::Notifications.instrument('prevoty:content:protect') do |payload| resp = payload[:response] = @client.bulk_filter(querystring, @policy_key) end end env['QUERY_STRING'] = resp.output result = self.class.build_result(@mode, req, querystring, resp) if resp.statistics.is_significant? || @log_verbosity === 'all' ::Prevoty::LOGGER << result.to_json + "\n" end end rescue Exception => e env['QUERY_STRING'] = escape_query(CGI::parse(env['QUERY_STRING'])) Rails.logger.warn e.message end else @monitor.process({mode: @mode, input: env['QUERY_STRING'], request: req}) end end if ['POST', 'PUT', 'PATCH'].member?(req.request_method) body = URI.unescape(req.body.read.encode('utf-8')) unless body.empty? if @mode === 'protect' begin resp = nil Timeout::timeout(@timeout) do if defined? ActiveSupport::Notifications ActiveSupport::Notifications.instrument('prevoty:content:protect') do |payload| resp = payload[:response] = @client.bulk_filter(body, @policy_key) end end env['rack.input'] = StringIO.new(resp.output) result = self.class.build_result(@mode, req, body, resp) if resp.statistics.is_significant? || @log_verbosity === 'all' ::Prevoty::LOGGER << result.to_json + "\n" end end rescue Exception => e env['rack.input'] = StringIO.new(escape_query(CGI::parse(body))) Rails.logger.warn e.message end else @monitor.process({mode: @mode, input: body, request: req}) end end end @app.call(env) ensure Thread.current[:request_storage] = {} end end
Protected Instance Methods
escape_query(params)
click to toggle source
# File lib/rack/content_middleware.rb, line 137 def escape_query(params) params.map do |name,values| values.map do |value| "#{CGI.escape name}=#{CGI.escape CGI.escapeHTML value}" end end.flatten.join("&") end