Module: Hooks::App::Helpers

Included in:
API, CatchallEndpoint
Defined in:
lib/hooks/app/helpers.rb

Instance Method Summary collapse

Instance Method Details

#enforce_request_limits(config, request_context = {}) ⇒ void

Note:

Timeout enforcement should be handled at the server level (e.g., Puma)

This method returns an undefined value.

Enforce request size and timeout limits

Parameters:

  • config (Hash)

    The configuration hash, must include :request_limit

  • request_context (Hash) (defaults to: {})

    Context for the request, e.g. request ID (optional)

Raises:

  • (StandardError)

    Halts with error if request body is too large



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/hooks/app/helpers.rb', line 25

def enforce_request_limits(config, request_context = {})
  # Optimized content length check - check most common sources first
  content_length = request.content_length if respond_to?(:request) && request.respond_to?(:content_length)

  content_length ||= headers["Content-Length"] ||
                    headers["CONTENT_LENGTH"] ||
                    headers["content-length"] ||
                    headers["HTTP_CONTENT_LENGTH"] ||
                    env["CONTENT_LENGTH"] ||
                    env["HTTP_CONTENT_LENGTH"]

  content_length = content_length&.to_i

  if content_length && content_length > config[:request_limit]
    request_id = request_context&.dig(:request_id)
    error!({ error: "request_body_too_large", message: "request body too large", request_id: }, 413)
  end

  # Note: Timeout enforcement would typically be handled at the server level (Puma, etc.)
end

#ip_filtering!(headers, endpoint_config, global_config, request_context, env) ⇒ void

Note:

This method will halt execution with an error if IP filtering rules fail.

This method returns an undefined value.

Verifies the incoming request passes the configured IP filtering rules.

This method assumes that the client IP address is available in the request headers (e.g., ‘X-Forwarded-For`). The headers that is used is configurable via the endpoint configuration. It checks the IP address against the allowed and denied lists defined in the endpoint configuration. If the IP address is not allowed, it instantly returns an error response via the `error!` method. If the IP filtering configuration is missing or invalid, it raises an error. If IP filtering is configured at the global level, it will also check against the global configuration first, and then against the endpoint-specific configuration.

Parameters:

  • headers (Hash)

    The request headers.

  • endpoint_config (Hash)

    The endpoint configuration, must include :ip_filtering key.

  • global_config (Hash)

    The global configuration (optional, for compatibility).

  • request_context (Hash)

    Context for the request, e.g. request ID, path, handler (optional).

  • env (Hash)

    The Rack environment

Raises:

  • (StandardError)

    Raises error if IP filtering fails or is misconfigured.



110
111
112
# File 'lib/hooks/app/helpers.rb', line 110

def ip_filtering!(headers, endpoint_config, global_config, request_context, env)
  Hooks::Core::Network::IpFiltering.ip_filtering!(headers, endpoint_config, global_config, request_context, env)
end

#load_handler(handler_class_name) ⇒ Object

Load handler class

Parameters:

  • handler_class_name (String)

    The name of the handler in snake_case (e.g., “github_handler”)

Returns:

  • (Object)

    An instance of the loaded handler class

Raises:

  • (StandardError)

    If handler cannot be found



81
82
83
84
85
86
87
88
89
90
# File 'lib/hooks/app/helpers.rb', line 81

def load_handler(handler_class_name)
  # Get handler class from loaded plugins registry (the registry is populated at boot time)
  # NOTE: We create a new instance per request (not reuse boot-time instances) because:
  # - Security: Prevents state pollution and information leakage between requests
  # - Thread Safety: Avoids race conditions from shared instance state
  # - Performance: Handler instantiation is fast; reusing instances provides minimal gain
  # - Memory: Allows garbage collection of short-lived objects (Ruby GC optimization)
  handler_class = Core::PluginLoader.get_handler_plugin(handler_class_name)
  return handler_class.new
end

#parse_payload(raw_body, headers, symbolize: false) ⇒ Hash, String

Parse request payload

Parameters:

  • raw_body (String)

    The raw request body

  • headers (Hash)

    The request headers

  • symbolize (Boolean) (defaults to: false)

    Whether to symbolize keys in parsed JSON (default: false)

Returns:

  • (Hash, String)

    Parsed JSON as Hash with string keys, or raw body if not JSON



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

def parse_payload(raw_body, headers, symbolize: false)
  # Optimized content type check - check most common header first
  content_type = headers["Content-Type"] || headers["CONTENT_TYPE"] || headers["content-type"] || headers["HTTP_CONTENT_TYPE"]

  # Try to parse as JSON if content type suggests it or if it looks like JSON
  if content_type&.include?("application/json") || (raw_body.strip.start_with?("{", "[") rescue false)
    begin
      # Security: Limit JSON parsing depth and complexity to prevent JSON bombs
      parsed_payload = safe_json_parse(raw_body)
      # Note: symbolize parameter is kept for backward compatibility but defaults to false
      parsed_payload = parsed_payload.transform_keys(&:to_sym) if symbolize && parsed_payload.is_a?(Hash)
      return parsed_payload
    rescue JSON::ParserError, ArgumentError => e
      # If JSON parsing fails or security limits exceeded, return raw body
      if e.message.include?("nesting") || e.message.include?("depth")
        log.warn("JSON parsing limit exceeded: #{e.message}")
      end
    end
  end

  # Return raw body for all other cases
  raw_body
end

#uuidString

Generate a unique identifier (UUID)

Returns:

  • (String)

    a new UUID string



14
15
16
# File 'lib/hooks/app/helpers.rb', line 14

def uuid
  SecureRandom.uuid
end