Skip to content

Commit d95a4a7

Browse files
committed
add a second example of how to use module_eval without constructing a code-string
1 parent ccf520a commit d95a4a7

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

ruby/ql/src/queries/security/cwe-094/UnsafeCodeConstruction.qhelp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ inputs, or avoid constructing code in the first place.
2525

2626
<example>
2727
<p>
28-
The following example shows two methods implemented using `eval`: a simple
28+
The following example shows two methods implemented using <code>eval</code>: a simple
2929
deserialization routine and a getter method.
3030
If untrusted inputs are used with these methods,
3131
then an attacker might be able to execute arbitrary code on the system.
@@ -35,14 +35,37 @@ then an attacker might be able to execute arbitrary code on the system.
3535

3636
<p>
3737
To avoid this problem, either properly document that the function is potentially
38-
unsafe, or use an alternative solution such as `JSON.parse` or another library, like in the examples below,
38+
unsafe, or use an alternative solution such as <code>JSON.parse</code> or another library, like in the examples below,
3939
that does not allow arbitrary code to be executed.
4040
</p>
4141

4242
<sample src="examples/UnsafeCodeConstructionSafe.rb" />
4343

4444
</example>
4545

46+
<example>
47+
<p>
48+
As another example, consider the below code which dynamically constructs
49+
a class that has a getter method with a custom name.
50+
</p>
51+
52+
<sample src="examples/UnsafeCodeConstruction2.rb" />
53+
54+
<p>
55+
The example dynamically constructs a string which is then executed using <code>module_eval</code>.
56+
This code will break if the specified name is not a valid Ruby identifier, and
57+
if the value is controlled by an attacker, then this could lead to code injection.
58+
</p>
59+
60+
<p>
61+
A more robust implementation, that is also immune to code injection,
62+
can be made by using <code>module_eval</code> with a block and using <code>define_method</code>
63+
to define the getter method.
64+
</p>
65+
66+
<sample src="examples/UnsafeCodeConstruction2Safe.rb" />
67+
</example>
68+
4669
<references>
4770
<li>
4871
OWASP:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require 'json'
2+
3+
module BadMakeGetter
4+
# Makes a class with a method named `getter_name` that returns `val`
5+
def self.define_getter_class(getter_name, val)
6+
new_class = Class.new
7+
new_class.module_eval <<-END
8+
def #{getter_name}
9+
#{JSON.dump(val)}
10+
end
11+
END
12+
new_class
13+
end
14+
end
15+
16+
one = BadMakeGetter.define_getter_class(:one, "foo")
17+
puts "One is #{one.new.one}"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Uses `define_method` instead of constructing a string
2+
module GoodMakeGetter
3+
def self.define_getter_class(getter_name, val)
4+
new_class = Class.new
5+
new_class.module_eval do
6+
define_method(getter_name) { val }
7+
end
8+
new_class
9+
end
10+
end
11+
12+
two = GoodMakeGetter.define_getter_class(:two, "bar")
13+
puts "Two is #{two.new.two}"

0 commit comments

Comments
 (0)