# frozen_string_literal: true

require “digest/md5”

# Configuration for rack_attack class Rack::Attack # rubocop:disable Style/ClassAndModuleChildren <%- if devise? -%>

LOGIN_PATH = "/users/sign_in"
REGISTRATION_PATH = "/users"

<%- end -%>

### Configure Cache ###

# If you don't want to use Rails.cache (Rack::Attack's default), then
# configure it here.
#
# Note: The store is only used for throttling (not blocklisting and
# safelisting). It must implement .increment and .write like
# ActiveSupport::Cache::Store

# Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new

### Throttle Spammy Clients ###

# If any single client IP is making tons of requests, then they're
# probably malicious or a poorly-configured scraper. Either way, they
# don't deserve to hog all of the app server's CPU. Cut them off!
#
# Note: If you're serving assets through rack, those requests may be
# counted by rack-attack and this throttle may be activated too
# quickly. If so, enable the condition to exclude them from tracking.

# Throttle all requests by IP (60rpm)
#
# Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
throttle("req/ip", limit: 300, period: 5.minutes, &:ip)

def self.pentesting_request?(path, query_string)
  CGI.unescape(query_string) =~ %r{/etc/passwd} ||
    path.include?("/etc/passwd") ||
    path.include?("wp-admin") ||
    path.include?("wp-login") ||
    /\.php$/.match?(path)
end

# Block suspicious requests for '/etc/passwd' or wordpress specific paths.
# After 3 blocked requests in 10 minutes, block all requests from that IP for 5 minutes.
blocklist("fail2ban/pentesters") do |req|
  # `filter` returns truthy value if request fails, or if it's from a previously banned IP
  # so the request is blocked
  Rack::Attack::Fail2Ban.filter("pentesters-#{req.ip}", maxretry: 3, findtime: 10.minutes, bantime: 5.minutes) do
    # The count for the IP is incremented if the return value is truthy
    pentesting_request?(req.path, req.query_string)
  end
end

<%- if devise? -%>

### Prevent Brute-Force Login Attacks ###

# The most common brute-force login attack is a brute-force password
# attack where an attacker simply tries a large number of emails and
# passwords to see if any credentials match.
#
# Another common method of attack is to use a swarm of computers with
# different IPs to try brute-forcing a password for a specific account.

# Throttle POST requests to /users/sign_in by IP address
#
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
throttle("logins/ip", limit: 5, period: 20.seconds) do |req|
  req.ip if req.path == LOGIN_PATH && req.post?
end

# Throttle POST requests to /users/sign_in by email param
#
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}"
#
# Note: This creates a problem where a malicious user could intentionally
# throttle logins for another user and force their login requests to be
# denied, but that's not very common and shouldn't happen to you. (Knock
# on wood!)
throttle("logins/email", limit: 5, period: 20.seconds) do |req|
  if req.path == LOGIN_PATH && req.post?
    # return a filtered email if present, nil otherwise
    email_address = req.params.dig("user", "email").presence
    email_address && Digest::MD5.hexdigest(email_address)
  end
end

### Prevent Brute-Force Email Harvesting ###

# For most authentication endpoints, we attempt to hide the presence of an
# email address from any outside users.
#
# However our registration form will not allow known email addresses to be used.
# Which can be used as a potential method of verifying email addresses.
#
# Throttling this endpoint is an attempt to limit the effectiveness of this farming.

# Throttle POST requests to /users by IP address
#
# Key: "rack::attack:#{Time.now.to_i/:period}:registrations/ip:#{req.ip}"
throttle("registrations/ip", limit: 5, period: 20.seconds) do |req|
  req.ip if req.path == REGISTRATION_PATH && (req.post? || req.put? || req.patch?)
end

<%- end -%>

### Custom Blocklist Response ###

self.blocklisted_response = lambda do |request|
  if pentesting_request?(request.fetch("PATH_INFO"), request.fetch("QUERY_STRING"))
    [301, { "Location" => "/" }, []]
  else
    [302, { "Location" => "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }, []]
  end
end

### Custom Throttle Response ###

# By default, Rack::Attack returns an HTTP 429 for throttled responses,
# which is just fine.
#
# If you want to return 503 so that the attacker might be fooled into
# believing that they've successfully broken your app (or you just want to
# customize the response), then uncomment these lines.
# self.throttled_response = lambda do |env|
#  [ 503,  # status
#    {},   # headers
#    ['']] # body
# end

end