Class: Html2rss::RequestService::Policy

Inherits:
Object
  • Object
show all
Defined in:
lib/html2rss/request_service/policy.rb

Overview

Describes the runtime request envelope for a single feed build.

Constant Summary collapse

MAX_REQUESTS_CEILING =

rubocop:disable Metrics/ClassLength

10
LOCAL_HOSTS =
%w[localhost localhost.localdomain metadata.google.internal].to_set.freeze
BLOCKED_IP_RANGES =
[
  IPAddr.new('0.0.0.0/8'),
  IPAddr.new('10.0.0.0/8'),
  IPAddr.new('127.0.0.0/8'),
  IPAddr.new('169.254.0.0/16'),
  IPAddr.new('172.16.0.0/12'),
  IPAddr.new('192.168.0.0/16'),
  IPAddr.new('224.0.0.0/4'),
  IPAddr.new('::/128'),
  IPAddr.new('::1/128'),
  IPAddr.new('fe80::/10'),
  IPAddr.new('fc00::/7'),
  IPAddr.new('ff00::/8')
].freeze
DEFAULTS =
{
  connect_timeout_seconds: 5,
  read_timeout_seconds: 10,
  total_timeout_seconds: 30,
  max_redirects: 3,
  max_response_bytes: 5_242_880,
  max_decompressed_bytes: 10_485_760,
  max_requests: 1,
  allow_private_networks: false,
  allow_cross_origin_followups: false
}.freeze
DEFAULT_POLICY =
Policy.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connect_timeout_seconds: , read_timeout_seconds: , total_timeout_seconds: , max_redirects: , max_response_bytes: , max_decompressed_bytes: , max_requests: , allow_private_networks: , allow_cross_origin_followups: , resolver: Socket) ⇒ Policy

Returns a new instance of Policy.

Parameters:

  • connect_timeout_seconds (Integer) (defaults to: )

    maximum connection setup time

  • read_timeout_seconds (Integer) (defaults to: )

    maximum read stall time

  • total_timeout_seconds (Integer) (defaults to: )

    maximum total request time

  • max_redirects (Integer) (defaults to: )

    maximum redirect count

  • max_response_bytes (Integer) (defaults to: )

    maximum streamed response bytes

  • max_decompressed_bytes (Integer) (defaults to: )

    maximum final body size

  • max_requests (Integer) (defaults to: )

    maximum requests per feed build

  • allow_private_networks (Boolean) (defaults to: )

    whether private network targets are allowed

  • allow_cross_origin_followups (Boolean) (defaults to: )

    whether follow-up requests may leave the origin host

  • resolver (#each_address) (defaults to: Socket)

    DNS resolver used for hostname classification



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/html2rss/request_service/policy.rb', line 52

def initialize(connect_timeout_seconds: DEFAULTS[:connect_timeout_seconds], # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
               read_timeout_seconds: DEFAULTS[:read_timeout_seconds],
               total_timeout_seconds: DEFAULTS[:total_timeout_seconds],
               max_redirects: DEFAULTS[:max_redirects],
               max_response_bytes: DEFAULTS[:max_response_bytes],
               max_decompressed_bytes: DEFAULTS[:max_decompressed_bytes],
               max_requests: DEFAULTS[:max_requests],
               allow_private_networks: DEFAULTS[:allow_private_networks],
               allow_cross_origin_followups: DEFAULTS[:allow_cross_origin_followups],
               resolver: Socket)
  @connect_timeout_seconds = validate_positive_integer!(:connect_timeout_seconds, connect_timeout_seconds)
  @read_timeout_seconds = validate_positive_integer!(:read_timeout_seconds, read_timeout_seconds)
  @total_timeout_seconds = validate_positive_integer!(:total_timeout_seconds, total_timeout_seconds)
  @max_redirects = validate_non_negative_integer!(:max_redirects, max_redirects)
  @max_response_bytes = validate_positive_integer!(:max_response_bytes, max_response_bytes)
  @max_decompressed_bytes = validate_positive_integer!(:max_decompressed_bytes, max_decompressed_bytes)
  @max_requests = [validate_positive_integer!(:max_requests, max_requests), MAX_REQUESTS_CEILING].min
  @allow_private_networks = allow_private_networks ? true : false
  @allow_cross_origin_followups = allow_cross_origin_followups ? true : false
  @resolver = resolver
  freeze
end

Instance Attribute Details

#connect_timeout_secondsObject (readonly)

Returns the value of attribute connect_timeout_seconds.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def connect_timeout_seconds
  @connect_timeout_seconds
end

#max_decompressed_bytesObject (readonly)

Returns the value of attribute max_decompressed_bytes.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def max_decompressed_bytes
  @max_decompressed_bytes
end

#max_redirectsObject (readonly)

Returns the value of attribute max_redirects.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def max_redirects
  @max_redirects
end

#max_requestsObject (readonly)

Returns the value of attribute max_requests.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def max_requests
  @max_requests
end

#max_response_bytesObject (readonly)

Returns the value of attribute max_response_bytes.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def max_response_bytes
  @max_response_bytes
end

#read_timeout_secondsObject (readonly)

Returns the value of attribute read_timeout_seconds.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def read_timeout_seconds
  @read_timeout_seconds
end

#total_timeout_secondsObject (readonly)

Returns the value of attribute total_timeout_seconds.



75
76
77
# File 'lib/html2rss/request_service/policy.rb', line 75

def total_timeout_seconds
  @total_timeout_seconds
end

Class Method Details

.defaultPolicy

Returns the default request policy.

rubocop:disable Layout/ClassStructure

Returns:

  • (Policy)

    a default, frozen policy instance



100
101
102
# File 'lib/html2rss/request_service/policy.rb', line 100

def self.default
  new
end

Instance Method Details

#allow_cross_origin_followups?Boolean

Returns whether follow-up requests may leave the initial origin.

Returns:

  • (Boolean)

    whether follow-up requests may leave the initial origin



91
92
93
# File 'lib/html2rss/request_service/policy.rb', line 91

def allow_cross_origin_followups?
  @allow_cross_origin_followups
end

#allow_private_networks?Boolean

Returns whether private network targets may be requested.

Returns:

  • (Boolean)

    whether private network targets may be requested



85
86
87
# File 'lib/html2rss/request_service/policy.rb', line 85

def allow_private_networks?
  @allow_private_networks
end

#validate_redirect!(from_url:, to_url:, origin_url:, relation:) ⇒ void

This method returns an undefined value.

Validates a redirect hop before it is followed.

Parameters:

  • from_url (Html2rss::Url)

    URL that produced the redirect

  • to_url (Html2rss::Url)

    redirect destination

  • origin_url (Html2rss::Url)

    initial URL of the feed build

  • relation (Symbol)

    logical reason for the request

Raises:



128
129
130
131
132
133
134
# File 'lib/html2rss/request_service/policy.rb', line 128

def validate_redirect!(from_url:, to_url:, origin_url:, relation:)
  if from_url.scheme == 'https' && to_url.scheme == 'http'
    raise UnsupportedUrlScheme, 'Redirect downgraded from https to http'
  end

  validate_request!(url: to_url, origin_url:, relation:)
end

#validate_remote_ip!(ip:, url:) ⇒ void

This method returns an undefined value.

Validates the resolved remote IP for a completed request.

Parameters:

  • ip (String, nil)

    remote IP address reported by the client

  • url (Html2rss::Url)

    URL associated with the response

Raises:



143
144
145
146
147
148
149
150
151
152
# File 'lib/html2rss/request_service/policy.rb', line 143

def validate_remote_ip!(ip:, url:)
  return if allow_private_networks?
  return if ip.nil? || ip.empty?

  parsed_ip = parse_ip(ip)
  raise PrivateNetworkDenied, "Remote IP could not be validated for #{url}" unless parsed_ip
  return unless blocked_ip?(parsed_ip)

  raise PrivateNetworkDenied, "Private network target denied for #{url}"
end

#validate_request!(url:, origin_url:, relation:) ⇒ void

This method returns an undefined value.

Validates whether a request target is permitted for the given context.

Parameters:

  • url (Html2rss::Url)

    destination URL

  • origin_url (Html2rss::Url)

    initial URL of the feed build

  • relation (Symbol)

    logical reason for the request

Raises:



114
115
116
117
# File 'lib/html2rss/request_service/policy.rb', line 114

def validate_request!(url:, origin_url:, relation:)
  enforce_same_origin!(url, origin_url, relation)
  enforce_public_network!(url)
end