Skip to content

Commit

Permalink
Avoid arbitrary code executation by allowing dynamic tasks only dispa…
Browse files Browse the repository at this point in the history
…tch to method missing cases [#4 status:resolved]
  • Loading branch information
josevalim committed Aug 30, 2009
1 parent 0229237 commit 5f8eb9f
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 13 deletions.
2 changes: 1 addition & 1 deletion lib/thor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def start(given_args=ARGV, config={})
args, opts = given_args, {}
end

task ||= Task.dynamic(meth)
task ||= Thor::Task::Dynamic.new(meth)
trailing = args[Range.new(arguments.size, -1)]
new(args, opts, config).invoke(task, trailing || [])
end
Expand Down
2 changes: 1 addition & 1 deletion lib/thor/invocation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def _validate_task(object, task) #:nodoc:
raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base

task ||= klass.default_task if klass <= Thor
task = klass.all_tasks[task.to_s] || Task.dynamic(task) if task && !task.is_a?(Thor::Task)
task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task)
task
end

Expand Down
20 changes: 13 additions & 7 deletions lib/thor/task.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
class Thor
class Task < Struct.new(:name, :description, :usage, :options)

# Creates a dynamic task. Dynamic tasks are created on demand to allow method
# missing calls (since a method missing does not have a task object for it).
# A dynamic task that handles method missing scenarios.
#
def self.dynamic(name)
new(name, "A dynamically-generated task", name.to_s)
class Dynamic < Task
def initialize(name)
super(name.to_s, "A dynamically-generated task", name.to_s)
end

def run(instance, args=[])
unless (instance.methods & [name.to_s, name.to_sym]).empty?
raise Error, "could not find Thor class or task '#{name}'"
end
super
end
end

def initialize(name, description, usage, options=nil)
Expand Down Expand Up @@ -37,8 +45,6 @@ def run(instance, args=[])
# injected in the usage.
#
def formatted_usage(klass=nil, namespace=false, show_options=true)
formatted = ''

formatted = if namespace.is_a?(String)
"#{namespace}:"
elsif klass && namespace
Expand Down Expand Up @@ -77,7 +83,7 @@ def formatted_options
#
def public_method?(instance) #:nodoc:
collection = instance.private_methods + instance.protected_methods
!(collection).include?(name.to_s) && !(collection).include?(name.to_sym) # For Ruby 1.9
(collection & [name.to_s, name.to_sym]).empty?
end

# Clean everything that comes from the Thor gempath and remove the caller.
Expand Down
14 changes: 10 additions & 4 deletions spec/task_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ def task(options={})

describe "#dynamic" do
it "creates a dynamic task with the given name" do
Thor::Task.dynamic('task').name.must == 'task'
Thor::Task.dynamic('task').description.must == 'A dynamically-generated task'
Thor::Task.dynamic('task').usage.must == 'task'
Thor::Task.dynamic('task').options.must == {}
Thor::Task::Dynamic.new('task').name.must == 'task'
Thor::Task::Dynamic.new('task').description.must == 'A dynamically-generated task'
Thor::Task::Dynamic.new('task').usage.must == 'task'
Thor::Task::Dynamic.new('task').options.must == {}
end

it "does not invoke an existing method" do
lambda {
Thor::Task::Dynamic.new('to_s').run([])
}.must raise_error(Thor::Error, "could not find Thor class or task 'to_s'")
end
end

Expand Down

0 comments on commit 5f8eb9f

Please sign in to comment.