Class: Msf::DBManager::Import::MarshalValidator
- Inherits:
-
Object
- Object
- Msf::DBManager::Import::MarshalValidator
- Defined in:
- lib/msf/core/db_manager/import/marshal_validator.rb
Overview
Walks a Marshal byte stream structurally, reading type bytes only in type positions and skipping over data payloads. Rejects any stream that attempts to instantiate a named class (object, struct, custom marshal, module extension, etc.).
This runs BEFORE Marshal.load so no objects are ever instantiated from an unsafe payload.
Reference: https://ruby-doc.org/3.3/Marshal.html Reference: https://github.com/ruby/ruby/blob/master/doc/marshal/marshal.md
Constant Summary collapse
- MarshalValidationError =
Msf::DBManager::Import::MarshalValidationError
- UNSAFE_TYPES =
Type bytes that always instantiate named classes — unconditionally blocked.
Set.new(%w[o c m C S e U d].map(&:ord)).freeze
- DEFAULT_PERMITTED_CLASSES =
Default classes permitted for the 'u' (_dump/_load) serialization type.
%w[].freeze
- MAX_NESTING_DEPTH =
Prevent deeply nested input from exhausting the Ruby call stack while the validator recursively walks compound values.
512
Class Method Summary collapse
-
.marshalled_data?(data) ⇒ Boolean
Check whether the given data starts with the Marshal 4.8 version header, indicating it is a Marshal-serialized payload.
-
.safe_load(data, permitted_classes: DEFAULT_PERMITTED_CLASSES) ⇒ Object
Convenience method: validate and then load.
Instance Method Summary collapse
-
#initialize(data, permitted_classes: DEFAULT_PERMITTED_CLASSES) ⇒ MarshalValidator
constructor
A new instance of MarshalValidator.
-
#validate! ⇒ true
Validate the entire stream.
Constructor Details
#initialize(data, permitted_classes: DEFAULT_PERMITTED_CLASSES) ⇒ MarshalValidator
Returns a new instance of MarshalValidator.
38 39 40 41 42 43 |
# File 'lib/msf/core/db_manager/import/marshal_validator.rb', line 38 def initialize(data, permitted_classes: DEFAULT_PERMITTED_CLASSES) @bytes = String.new(data).bytes @pos = 0 @object_count = 0 @permitted_classes = Set.new(permitted_classes) end |
Class Method Details
.marshalled_data?(data) ⇒ Boolean
Check whether the given data starts with the Marshal 4.8 version header, indicating it is a Marshal-serialized payload.
73 74 75 |
# File 'lib/msf/core/db_manager/import/marshal_validator.rb', line 73 def self.marshalled_data?(data) data.length >= 2 && data.getbyte(0) == 4 && data.getbyte(1) == 8 end |
.safe_load(data, permitted_classes: DEFAULT_PERMITTED_CLASSES) ⇒ Object
Convenience method: validate and then load.
59 60 61 62 63 64 65 66 |
# File 'lib/msf/core/db_manager/import/marshal_validator.rb', line 59 def self.safe_load(data, permitted_classes: DEFAULT_PERMITTED_CLASSES) # Validate and load the same plain String snapshot. In particular, do # not let a String subclass supply different data through #bytes, and # do not leave a window in which another thread can mutate the input. payload = String.new(data).freeze new(payload, permitted_classes: permitted_classes).validate! Marshal.load(payload) # rubocop:disable Security/MarshalLoad -- payload has been structurally validated above end |
Instance Method Details
#validate! ⇒ true
Validate the entire stream. Raises MarshalValidationError if unsafe.
47 48 49 50 51 |
# File 'lib/msf/core/db_manager/import/marshal_validator.rb', line 47 def validate! read_version validate_value true end |