Class: SchemaGraphy::AstGate
- Inherits:
-
Object
- Object
- SchemaGraphy::AstGate
- Defined in:
- lib/schemagraphy/safe_expression.rb
Overview
Provides a simple, deny-by-exception sandbox for mapping expressions.
It validates code by walking the Abstract Syntax Tree (AST) and blocking
known dangerous operations, rather than attempting to allowlist safe ones.
Constant Summary collapse
- BLOCKED_BAREWORDS =
A list of dangerous bareword methods that are blocked.
%w[ eval instance_eval class_eval module_eval binding require require_relative load autoload system exec spawn fork backtick ` open ObjectSpace GC Thread Process at_exit ].freeze
- DISALLOWED_NODES =
A list of AST node types that are explicitly disallowed.
%i[ # Definitions and meta-programming def_node class_node module_node define_node alias_node undef_node # Globals and constants paths global_variable_read_node constant_path_node # Shell and backticks x_string_node interpolated_x_string_node ].freeze
- DANGEROUS_CONSTANTS =
A list of constants that are considered dangerous and are blocked.
%w[ Kernel Object Module Class File FileUtils IO Dir Process Open3 PTY Thread SystemSignal Signal Gem Net HTTP TCPSocket UDPSocket Socket ObjectSpace GC ].freeze
Class Method Summary collapse
-
.validate!(code, context_keys: []) ⇒ Object
Validates the given code by parsing it and walking the AST.
-
.walk(node, context_keys: []) ⇒ Object
private
Recursively walks the AST, checking for disallowed nodes and operations.
Class Method Details
.validate!(code, context_keys: []) ⇒ Object
Validates the given code by parsing it and walking the AST.
41 42 43 44 45 46 |
# File 'lib/schemagraphy/safe_expression.rb', line 41 def self.validate! code, context_keys: [] result = Prism.parse(code) raise SyntaxError, result.errors.map(&:message).join(', ') if result.errors.any? walk(result.value, context_keys: context_keys) end |
.walk(node, context_keys: []) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Recursively walks the AST, checking for disallowed nodes and operations.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/schemagraphy/safe_expression.rb', line 54 def self.walk node, context_keys: [] return unless node.is_a?(Prism::Node) type = node.type raise SecurityError, "node not allowed: #{type}" if DISALLOWED_NODES.include?(type) case node when Prism::CallNode # Block dangerous barewords (system, eval, etc.) if node.receiver.nil? && BLOCKED_BAREWORDS.include?(node.name.to_s) raise SecurityError, "method not allowed: #{node.name}" end # Block dangerous constants and constant paths if node.receiver.is_a?(Prism::ConstantReadNode) && DANGEROUS_CONSTANTS.include?(node.receiver.name.to_s) raise SecurityError, "unsafe constant: #{node.receiver.name}" end raise SecurityError, 'unsafe constant path' if node.receiver.is_a?(Prism::ConstantPathNode) when Prism::ConstantReadNode # Allow only core Ruby constants defined in SafeTransform const_name = node.name.to_s unless SafeTransform::CORE_CONSTANTS.key?(const_name.to_sym) raise SecurityError, "constant not allowed: #{const_name}" end when Prism::ConstantPathNode, Prism::GlobalVariableReadNode raise SecurityError, 'constant paths and global variables are not allowed' when Prism::DefNode, Prism::ClassNode, Prism::ModuleNode raise SecurityError, 'method, class, and module definitions are not allowed' when Prism::BackReferenceReadNode, Prism::XStringNode, Prism::InterpolatedXStringNode raise SecurityError, 'shell commands and backticks are not allowed' end node.child_nodes.each { |child| walk(child, context_keys: context_keys) if child } end |