Class: WSDL::Limits

Inherits:
Object
  • Object
show all
Defined in:
lib/wsdl/limits.rb

Overview

Configuration for resource limits to prevent denial-of-service attacks.

This class provides sensible defaults that work for most WSDL documents while protecting against malicious or malformed documents that could exhaust system resources.

The library already protects against XXE, SSRF, and path traversal attacks. These limits add protection against resource exhaustion attacks.

Examples:

Using default limits

definition = WSDL.parse('http://example.com/service?wsdl')

Customizing limits

definition = WSDL.parse(url, limits: { max_schemas: 100 })

Customizing with a Limits object

custom = WSDL::Limits.new(max_document_size: 20 * 1024 * 1024)
definition = WSDL.parse(url, limits: custom)

Disabling a specific limit

definition = WSDL.parse(url, limits: { max_schemas: nil })

Constant Summary collapse

DEFAULT_MAX_DOCUMENT_SIZE =

Default maximum size for a single WSDL or schema document (10 MB).

10 * 1024 * 1024
DEFAULT_MAX_TOTAL_DOWNLOAD_SIZE =

Default maximum cumulative bytes downloaded across all documents (50 MB).

50 * 1024 * 1024
DEFAULT_MAX_SCHEMAS =

Default maximum number of schema definitions allowed.

50
DEFAULT_MAX_ELEMENTS_PER_TYPE =

Default maximum child elements in a complex type.

500
DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT =

Default maximum attributes on an XML element.

100
DEFAULT_MAX_TYPE_NESTING_DEPTH =

Default maximum depth of type inheritance/nesting.

50
DEFAULT_MAX_REQUEST_ELEMENTS =

Default maximum total elements in request envelope construction.

10_000
DEFAULT_MAX_REQUEST_DEPTH =

Default maximum request envelope nesting depth.

100
DEFAULT_MAX_REQUEST_ATTRIBUTES =

Default maximum total attributes in request envelope construction.

1_000
DEFAULT_MAX_SCHEMA_IMPORT_ITERATIONS =

Default maximum iterations for resolving schema imports and includes.

100
DEFAULT_MAX_RESPONSE_SIZE =

Default maximum size for a SOAP response body (10 MB).

10 * 1024 * 1024

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_document_size: DEFAULT_MAX_DOCUMENT_SIZE, max_total_download_size: DEFAULT_MAX_TOTAL_DOWNLOAD_SIZE, max_schemas: DEFAULT_MAX_SCHEMAS, max_elements_per_type: DEFAULT_MAX_ELEMENTS_PER_TYPE, max_attributes_per_element: DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT, max_type_nesting_depth: DEFAULT_MAX_TYPE_NESTING_DEPTH, max_request_elements: DEFAULT_MAX_REQUEST_ELEMENTS, max_request_depth: DEFAULT_MAX_REQUEST_DEPTH, max_request_attributes: DEFAULT_MAX_REQUEST_ATTRIBUTES, max_schema_import_iterations: DEFAULT_MAX_SCHEMA_IMPORT_ITERATIONS, max_response_size: DEFAULT_MAX_RESPONSE_SIZE) ⇒ Limits

Creates a new Limits instance with the specified resource limits.

rubocop:disable Metrics/ParameterLists

Parameters:

  • max_document_size (Integer, nil) (defaults to: DEFAULT_MAX_DOCUMENT_SIZE)

    maximum size in bytes for a single WSDL/schema document. Set to nil to disable this limit. Default: 10 MB.

  • max_total_download_size (Integer, nil) (defaults to: DEFAULT_MAX_TOTAL_DOWNLOAD_SIZE)

    maximum cumulative bytes downloaded across all WSDL and schema documents. Set to nil to disable. Default: 50 MB.

  • max_schemas (Integer, nil) (defaults to: DEFAULT_MAX_SCHEMAS)

    maximum number of schema definitions allowed. Set to nil to disable. Default: 50.

  • max_elements_per_type (Integer, nil) (defaults to: DEFAULT_MAX_ELEMENTS_PER_TYPE)

    maximum child elements in a complex type. Set to nil to disable. Default: 500.

  • max_attributes_per_element (Integer, nil) (defaults to: DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT)

    maximum attributes on an XML element. Set to nil to disable. Default: 100.

  • max_type_nesting_depth (Integer, nil) (defaults to: DEFAULT_MAX_TYPE_NESTING_DEPTH)

    maximum depth of type inheritance/nesting. Set to nil to disable. Default: 50.

  • max_request_elements (Integer, nil) (defaults to: DEFAULT_MAX_REQUEST_ELEMENTS)

    maximum total elements allowed in request envelope. Set to nil to disable. Default: 10,000.

  • max_request_depth (Integer, nil) (defaults to: DEFAULT_MAX_REQUEST_DEPTH)

    maximum request envelope nesting depth. Set to nil to disable. Default: 100.

  • max_request_attributes (Integer, nil) (defaults to: DEFAULT_MAX_REQUEST_ATTRIBUTES)

    maximum total attributes in request envelope. Set to nil to disable. Default: 1,000.

  • max_schema_import_iterations (Integer, nil) (defaults to: DEFAULT_MAX_SCHEMA_IMPORT_ITERATIONS)

    maximum iterations for resolving schema imports and includes. Set to nil to disable. Default: 100.

  • max_response_size (Integer, nil) (defaults to: DEFAULT_MAX_RESPONSE_SIZE)

    maximum size in bytes for a SOAP response body. Set to nil to disable this limit. Default: 10 MB.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/wsdl/limits.rb', line 103

def initialize(
  max_document_size: DEFAULT_MAX_DOCUMENT_SIZE,
  max_total_download_size: DEFAULT_MAX_TOTAL_DOWNLOAD_SIZE,
  max_schemas: DEFAULT_MAX_SCHEMAS,
  max_elements_per_type: DEFAULT_MAX_ELEMENTS_PER_TYPE,
  max_attributes_per_element: DEFAULT_MAX_ATTRIBUTES_PER_ELEMENT,
  max_type_nesting_depth: DEFAULT_MAX_TYPE_NESTING_DEPTH,
  max_request_elements: DEFAULT_MAX_REQUEST_ELEMENTS,
  max_request_depth: DEFAULT_MAX_REQUEST_DEPTH,
  max_request_attributes: DEFAULT_MAX_REQUEST_ATTRIBUTES,
  max_schema_import_iterations: DEFAULT_MAX_SCHEMA_IMPORT_ITERATIONS,
  max_response_size: DEFAULT_MAX_RESPONSE_SIZE
)
  # rubocop:enable Metrics/ParameterLists
  @max_document_size = max_document_size
  @max_total_download_size = max_total_download_size
  @max_schemas = max_schemas
  @max_elements_per_type = max_elements_per_type
  @max_attributes_per_element = max_attributes_per_element
  @max_type_nesting_depth = max_type_nesting_depth
  @max_request_elements = max_request_elements
  @max_request_depth = max_request_depth
  @max_request_attributes = max_request_attributes
  @max_schema_import_iterations = max_schema_import_iterations
  @max_response_size = max_response_size

  freeze
end

Instance Attribute Details

#max_attributes_per_elementInteger? (readonly)

Returns maximum attributes on an XML element.

Returns:

  • (Integer, nil)

    maximum attributes on an XML element



145
146
147
# File 'lib/wsdl/limits.rb', line 145

def max_attributes_per_element
  @max_attributes_per_element
end

#max_document_sizeInteger? (readonly)

Returns maximum size in bytes for a single WSDL/schema document.

Returns:

  • (Integer, nil)

    maximum size in bytes for a single WSDL/schema document



133
134
135
# File 'lib/wsdl/limits.rb', line 133

def max_document_size
  @max_document_size
end

#max_elements_per_typeInteger? (readonly)

Returns maximum child elements in a complex type.

Returns:

  • (Integer, nil)

    maximum child elements in a complex type



142
143
144
# File 'lib/wsdl/limits.rb', line 142

def max_elements_per_type
  @max_elements_per_type
end

#max_request_attributesInteger? (readonly)

Returns maximum total attributes in request envelope.

Returns:

  • (Integer, nil)

    maximum total attributes in request envelope



157
158
159
# File 'lib/wsdl/limits.rb', line 157

def max_request_attributes
  @max_request_attributes
end

#max_request_depthInteger? (readonly)

Returns maximum request envelope nesting depth.

Returns:

  • (Integer, nil)

    maximum request envelope nesting depth



154
155
156
# File 'lib/wsdl/limits.rb', line 154

def max_request_depth
  @max_request_depth
end

#max_request_elementsInteger? (readonly)

Returns maximum total elements in request envelope.

Returns:

  • (Integer, nil)

    maximum total elements in request envelope



151
152
153
# File 'lib/wsdl/limits.rb', line 151

def max_request_elements
  @max_request_elements
end

#max_response_sizeInteger? (readonly)

Returns maximum size in bytes for a SOAP response body.

Returns:

  • (Integer, nil)

    maximum size in bytes for a SOAP response body



163
164
165
# File 'lib/wsdl/limits.rb', line 163

def max_response_size
  @max_response_size
end

#max_schema_import_iterationsInteger? (readonly)

Returns maximum iterations for resolving schema imports and includes.

Returns:

  • (Integer, nil)

    maximum iterations for resolving schema imports and includes



160
161
162
# File 'lib/wsdl/limits.rb', line 160

def max_schema_import_iterations
  @max_schema_import_iterations
end

#max_schemasInteger? (readonly)

Returns maximum number of schema definitions.

Returns:

  • (Integer, nil)

    maximum number of schema definitions



139
140
141
# File 'lib/wsdl/limits.rb', line 139

def max_schemas
  @max_schemas
end

#max_total_download_sizeInteger? (readonly)

Returns maximum cumulative bytes downloaded.

Returns:

  • (Integer, nil)

    maximum cumulative bytes downloaded



136
137
138
# File 'lib/wsdl/limits.rb', line 136

def max_total_download_size
  @max_total_download_size
end

#max_type_nesting_depthInteger? (readonly)

Returns maximum depth of type inheritance/nesting.

Returns:

  • (Integer, nil)

    maximum depth of type inheritance/nesting



148
149
150
# File 'lib/wsdl/limits.rb', line 148

def max_type_nesting_depth
  @max_type_nesting_depth
end

Class Method Details

.resolve(value) ⇒ Limits?

Coerces a value into a Limits instance.

Accepts a Limits object (returned as-is), a Hash of settings (forwarded to new), or nil.

Parameters:

  • value (Limits, Hash, nil)

    the value to coerce

Returns:

  • (Limits, nil)

    the resolved Limits, or nil if value is nil

Raises:

  • (ArgumentError)

    if the value type is not recognized



68
69
70
71
72
73
74
75
# File 'lib/wsdl/limits.rb', line 68

def self.resolve(value)
  case value
  when Limits then value
  when Hash then new(**value)
  when nil then nil
  else raise ArgumentError, "Cannot coerce #{value.inspect} into a Limits"
  end
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Checks equality with another Limits instance.

Parameters:

  • other (Object)

    the object to compare

Returns:

  • (Boolean)

    true if equal



250
251
252
253
254
# File 'lib/wsdl/limits.rb', line 250

def ==(other)
  return false unless other.is_a?(Limits)

  to_h == other.to_h
end

#hashInteger

Returns a hash code for use in Hash keys.

Returns:

  • (Integer)

    the hash code



262
263
264
# File 'lib/wsdl/limits.rb', line 262

def hash
  to_h.hash
end

#inspectString

Returns a human-readable string representation.

Returns:

  • (String)

    the limits formatted for display



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/wsdl/limits.rb', line 227

def inspect
  parts = {
    max_document_size: Formatting.format_bytes(@max_document_size),
    max_total_download_size: Formatting.format_bytes(@max_total_download_size),
    max_schemas: limit_value(@max_schemas),
    max_elements_per_type: limit_value(@max_elements_per_type),
    max_attributes_per_element: limit_value(@max_attributes_per_element),
    max_type_nesting_depth: limit_value(@max_type_nesting_depth),
    max_request_elements: limit_value(@max_request_elements),
    max_request_depth: limit_value(@max_request_depth),
    max_request_attributes: limit_value(@max_request_attributes),
    max_schema_import_iterations: limit_value(@max_schema_import_iterations),
    max_response_size: Formatting.format_bytes(@max_response_size)
  }.map { |key, value| "#{key}=#{value}" }.join(' ')

  "#<#{self.class.name} #{parts}>"
end

#to_hHash{Symbol => Integer, nil}

Returns a hash representation of the limits.

Returns:

  • (Hash{Symbol => Integer, nil})

    the limits as a hash



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/wsdl/limits.rb', line 207

def to_h
  {
    max_document_size: @max_document_size,
    max_total_download_size: @max_total_download_size,
    max_schemas: @max_schemas,
    max_elements_per_type: @max_elements_per_type,
    max_attributes_per_element: @max_attributes_per_element,
    max_type_nesting_depth: @max_type_nesting_depth,
    max_request_elements: @max_request_elements,
    max_request_depth: @max_request_depth,
    max_request_attributes: @max_request_attributes,
    max_schema_import_iterations: @max_schema_import_iterations,
    max_response_size: @max_response_size
  }
end

#with(**options) ⇒ Limits

Creates a new Limits instance with some values changed.

Examples:

Increase document size limit

new_limits = limits.with(max_document_size: 20 * 1024 * 1024)

Disable schema count limit

new_limits = limits.with(max_schemas: nil)

Parameters:

  • options (Hash)

    the limits to override

Options Hash (**options):

  • :max_document_size (Integer, nil)
  • :max_total_download_size (Integer, nil)
  • :max_schemas (Integer, nil)
  • :max_elements_per_type (Integer, nil)
  • :max_attributes_per_element (Integer, nil)
  • :max_type_nesting_depth (Integer, nil)
  • :max_request_elements (Integer, nil)
  • :max_request_depth (Integer, nil)
  • :max_request_attributes (Integer, nil)
  • :max_schema_import_iterations (Integer, nil)
  • :max_response_size (Integer, nil)

Returns:

  • (Limits)

    a new Limits instance with the specified changes



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/wsdl/limits.rb', line 187

def with(**options)
  self.class.new(
    max_document_size: options.fetch(:max_document_size, @max_document_size),
    max_total_download_size: options.fetch(:max_total_download_size, @max_total_download_size),
    max_schemas: options.fetch(:max_schemas, @max_schemas),
    max_elements_per_type: options.fetch(:max_elements_per_type, @max_elements_per_type),
    max_attributes_per_element: options.fetch(:max_attributes_per_element, @max_attributes_per_element),
    max_type_nesting_depth: options.fetch(:max_type_nesting_depth, @max_type_nesting_depth),
    max_request_elements: options.fetch(:max_request_elements, @max_request_elements),
    max_request_depth: options.fetch(:max_request_depth, @max_request_depth),
    max_request_attributes: options.fetch(:max_request_attributes, @max_request_attributes),
    max_schema_import_iterations: options.fetch(:max_schema_import_iterations, @max_schema_import_iterations),
    max_response_size: options.fetch(:max_response_size, @max_response_size)
  )
end