Alternatives to Eval
The eval method executes arbitrary Ruby code from a string. This is unsafe and can be used for code injection attacks if data from a SketchUp model, a server or other source outside of the extension code is inserted into the string.
In SketchUp extension development, there is almost always a better alternative. This article covers common scenarios where developers may reach for eval and what to use instead.
Server Side Model Generation (AI)
When creating geometry outside of SketchUp, e.g. by an LLM, don't generate Ruby code you then evaluate in SketchUp. It allows arbitrary code execution — anything the server sends will run with full access to the user's system.
# Bad
code_from_server = "Sketchup.active_model...."
eval(code_from_server)Instead generate the geometry on the server, e.g. using the SketchUp C API, or to let the LLM create a text based model format such as STL or OBJ.
# Good
model_from_server = "some_path.skp"
definition = Sketchup.active_model.definitions.import(model_from_server)
Sketchup.active_model.place_component(definition)Mathematical Expressions
If you need to evaluate user-entered mathematical expressions, don't pass them to eval. Instead, parse them yourself. A simple recursive descent parser can handle basic arithmetic safely.
# Bad - evaluates arbitrary Ruby code
result = eval(user_input)
# Good - only recognizes numbers and basic operators
def evaluate(expression)
tokens = expression.scan(/\d+\.?\d*|[+\-*\/()]/)
parse_expression(tokens)
end
def parse_expression(tokens)
left = parse_term(tokens)
while tokens.first == "+" || tokens.first == "-"
op = tokens.shift
right = parse_term(tokens)
left = op == "+" ? left + right : left - right
end
left
end
def parse_term(tokens)
left = parse_factor(tokens)
while tokens.first == "*" || tokens.first == "/"
op = tokens.shift
right = parse_factor(tokens)
left = op == "*" ? left * right : left / right
end
left
end
def parse_factor(tokens)
token = tokens.shift
if token == "("
result = parse_expression(tokens)
tokens.shift # consume ")"
result
else
token.to_f
end
endThis parser only understands numbers and arithmetic operators. Malicious input like system('rm -rf /') would simply fail to parse rather than execute.
Dynamic Method Calls
If you need to call methods based on user input, use send with a whitelist of allowed methods.
# Bad - allows calling any method
method_name = user_input
object.send(method_name)
# Good - only allows specific methods
ALLOWED_METHODS = %w[width height depth].freeze
method_name = user_input
if ALLOWED_METHODS.include?(method_name)
object.send(method_name)
else
raise "Unknown method: #{method_name}"
endConfiguration and Data
For configuration files or data exchange, use structured formats like JSON or YAML instead of executable code.
# Bad - executes Ruby code from a file
config = eval(File.read("config.rb"))
# Good - parses data, doesn't execute code
require "json"
config = JSON.parse(File.read("config.json"))
