diff --git a/lib/brakeman/checks/base_check.rb b/lib/brakeman/checks/base_check.rb
index 11c5cd3f6253d5f75bc57c7924432616754dd1dc..6b0120b3ad7e669f5b285a979b59f68e1e791f21 100644
--- a/lib/brakeman/checks/base_check.rb
+++ b/lib/brakeman/checks/base_check.rb
@@ -1,11 +1,10 @@
-require 'sexp_processor'
require 'brakeman/processors/output_processor'
require 'brakeman/processors/lib/processor_helper'
require 'brakeman/warning'
require 'brakeman/util'
#Basis of vulnerability checks.
-class Brakeman::BaseCheck < SexpProcessor
+class Brakeman::BaseCheck < Brakeman::SexpProcessor
include Brakeman::ProcessorHelper
include Brakeman::Util
attr_reader :tracker, :warnings
@@ -24,11 +23,6 @@ class Brakeman::BaseCheck < SexpProcessor
@current_set = nil
@current_template = @current_module = @current_class = @current_method = nil
@mass_assign_disabled = nil
- self.strict = false
- self.auto_shift_type = false
- self.require_empty = false
- self.default_method = :process_default
- self.warn_on_default = false
end
#Add result to result list, which is used to check for duplicates
diff --git a/lib/brakeman/processors/alias_processor.rb b/lib/brakeman/processors/alias_processor.rb
index e1fdef05cca66e318281c3dac89c464bb5bc040d..c9b4291dd9bf085001f8191c30ff4d281766dc98 100644
--- a/lib/brakeman/processors/alias_processor.rb
+++ b/lib/brakeman/processors/alias_processor.rb
@@ -1,12 +1,11 @@
-require 'rubygems'
-require 'sexp_processor'
require 'brakeman/util'
+require 'ruby_parser/bm_sexp_processor'
require 'brakeman/processors/lib/processor_helper'
#Returns an s-expression with aliases replaced with their value.
#This does not preserve semantics (due to side effects, etc.), but it makes
#processing easier when searching for various things.
-class Brakeman::AliasProcessor < SexpProcessor
+class Brakeman::AliasProcessor < Brakeman::SexpProcessor
include Brakeman::ProcessorHelper
include Brakeman::Util
@@ -19,11 +18,6 @@ class Brakeman::AliasProcessor < SexpProcessor
# AliasProcessor.new.process_safely src
def initialize tracker = nil
super()
- self.strict = false
- self.auto_shift_type = false
- self.require_empty = false
- self.default_method = :process_default
- self.warn_on_default = false
@env = SexpProcessor::Environment.new
@inside_if = false
@ignore_ifs = false
diff --git a/lib/brakeman/processors/base_processor.rb b/lib/brakeman/processors/base_processor.rb
index c67c0b43768cc3ba01db29c18302ca90a108288c..53fc26533b03a8ac4577539c7882390fbe332e5d 100644
--- a/lib/brakeman/processors/base_processor.rb
+++ b/lib/brakeman/processors/base_processor.rb
@@ -1,10 +1,8 @@
-require 'rubygems'
-require 'sexp_processor'
require 'brakeman/processors/lib/processor_helper'
require 'brakeman/util'
#Base processor for most processors.
-class Brakeman::BaseProcessor < SexpProcessor
+class Brakeman::BaseProcessor < Brakeman::SexpProcessor
include Brakeman::ProcessorHelper
include Brakeman::Util
@@ -13,11 +11,6 @@ class Brakeman::BaseProcessor < SexpProcessor
#Return a new Processor.
def initialize tracker
super()
- self.strict = false
- self.auto_shift_type = false
- self.require_empty = false
- self.default_method = :process_default
- self.warn_on_default = false
@last = nil
@tracker = tracker
@ignore = Sexp.new :ignore
@@ -50,7 +43,7 @@ class Brakeman::BaseProcessor < SexpProcessor
#Default processing.
def process_default exp
exp = exp.dup
- type = exp.shift
+
exp.each_with_index do |e, i|
if sexp? e and not e.empty?
exp[i] = process e
@@ -58,8 +51,8 @@ class Brakeman::BaseProcessor < SexpProcessor
e
end
end
- ensure
- exp.unshift type
+
+ exp
end
#Process an if statement.
diff --git a/lib/brakeman/processors/template_processor.rb b/lib/brakeman/processors/template_processor.rb
index bda89ed794294135f63649b6d0b4729f9181b70b..0e95fb7a3c1a34df6eaad51a07e601cea4078702 100644
--- a/lib/brakeman/processors/template_processor.rb
+++ b/lib/brakeman/processors/template_processor.rb
@@ -20,7 +20,6 @@ class Brakeman::TemplateProcessor < Brakeman::BaseProcessor
tracker.templates[template_name] = @current_template
@inside_concat = false
- self.warn_on_default = false
end
#Process the template Sexp.
diff --git a/lib/brakeman/scanner.rb b/lib/brakeman/scanner.rb
index b62d3b0e26234728ef4bf6845e2f53ffa18fbae6..b3910eb1ba055674103cd344e8866ddb03b8fc14 100644
--- a/lib/brakeman/scanner.rb
+++ b/lib/brakeman/scanner.rb
@@ -8,6 +8,8 @@ begin
require 'ruby_parser/bm_sexp.rb'
end
+ require 'ruby_parser/bm_sexp_processor.rb'
+
require 'haml'
require 'sass'
require 'erb'
diff --git a/lib/brakeman/util.rb b/lib/brakeman/util.rb
index 63e96b6b04e45a41cd3b7f2bbd428c9734b84022..ddc93a2b2610be5005a80999c4f25eeb5bf140a3 100644
--- a/lib/brakeman/util.rb
+++ b/lib/brakeman/util.rb
@@ -1,4 +1,3 @@
-require 'sexp_processor'
require 'set'
require 'active_support/inflector'
diff --git a/lib/ruby_parser/bm_sexp_processor.rb b/lib/ruby_parser/bm_sexp_processor.rb
new file mode 100644
index 0000000000000000000000000000000000000000..db1588d2cfadb873c88540415619d9a720e468aa
--- /dev/null
+++ b/lib/ruby_parser/bm_sexp_processor.rb
@@ -0,0 +1,231 @@
+##
+# SexpProcessor provides a uniform interface to process Sexps.
+#
+# In order to create your own SexpProcessor subclass you'll need
+# to call super in the initialize method, then set any of the
+# Sexp flags you want to be different from the defaults.
+#
+# SexpProcessor uses a Sexp's type to determine which process method
+# to call in the subclass. For Sexp s(:lit, 1)
+# SexpProcessor will call #process_lit, if it is defined.
+#
+
+class Brakeman::SexpProcessor
+
+ VERSION = 'CUSTOM'
+
+ ##
+ # Return a stack of contexts. Most recent node is first.
+
+ attr_reader :context
+
+ ##
+ # Expected result class
+
+ attr_accessor :expected
+
+ ##
+ # A scoped environment to make you happy.
+
+ attr_reader :env
+
+ ##
+ # Creates a new SexpProcessor. Use super to invoke this
+ # initializer from SexpProcessor subclasses, then use the
+ # attributes above to customize the functionality of the
+ # SexpProcessor
+
+ def initialize
+ @expected = Sexp
+
+ # we do this on an instance basis so we can subclass it for
+ # different processors.
+ @processors = {}
+ @context = []
+
+ public_methods.each do |name|
+ if name.to_s.start_with? "process_" then
+ @processors[name[8..-1].to_sym] = name.to_sym
+ end
+ end
+ end
+
+ ##
+ # Default Sexp processor. Invokes process_ methods matching
+ # the Sexp type given. Performs additional checks as specified by
+ # the initializer.
+
+ def process(exp)
+ return nil if exp.nil?
+
+ result = nil
+
+ type = exp.first
+ raise "type should be a Symbol, not: #{exp.first.inspect}" unless
+ Symbol === type
+
+ in_context type do
+ # now do a pass with the real processor (or generic)
+ meth = @processors[type]
+ if meth then
+ if $DEBUG
+ result = error_handler(type) do
+ self.send(meth, exp)
+ end
+ else
+ result = self.send(meth, exp)
+ end
+
+ else
+ result = self.process_default(exp)
+ end
+ end
+
+ raise SexpTypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result
+
+ result
+ end
+
+ def error_handler(type, exp=nil) # :nodoc:
+ begin
+ return yield
+ rescue StandardError => err
+ warn "#{err.class} Exception thrown while processing #{type} for sexp #{exp.inspect} #{caller.inspect}" if $DEBUG
+ raise
+ end
+ end
+
+ ##
+ # A fairly generic processor for a dummy node. Dummy nodes are used
+ # when your processor is doing a complicated rewrite that replaces
+ # the current sexp with multiple sexps.
+ #
+ # Bogus Example:
+ #
+ # def process_something(exp)
+ # return s(:dummy, process(exp), s(:extra, 42))
+ # end
+
+ def process_dummy(exp)
+ result = @expected.new(:dummy) rescue @expected.new
+
+ until exp.empty? do
+ result << self.process(exp.shift)
+ end
+
+ result
+ end
+
+ ##
+ # Add a scope level to the current env. Eg:
+ #
+ # def process_defn exp
+ # name = exp.shift
+ # args = process(exp.shift)
+ # scope do
+ # body = process(exp.shift)
+ # # ...
+ # end
+ # end
+ #
+ # env[:x] = 42
+ # scope do
+ # env[:x] # => 42
+ # env[:y] = 24
+ # end
+ # env[:y] # => nil
+
+ def scope &block
+ env.scope(&block)
+ end
+
+ def in_context type
+ self.context.unshift type
+
+ yield
+
+ self.context.shift
+ end
+
+ ##
+ # I really hate this here, but I hate subdirs in my lib dir more...
+ # I guess it is kinda like shaving... I'll split this out when it
+ # itches too much...
+
+ class Environment
+ def initialize
+ @env = []
+ @env.unshift({})
+ end
+
+ def all
+ @env.reverse.inject { |env, scope| env.merge scope }
+ end
+
+ def depth
+ @env.length
+ end
+
+ # TODO: depth_of
+
+ def [] name
+ hash = @env.find { |closure| closure.has_key? name }
+ hash[name] if hash
+ end
+
+ def []= name, val
+ hash = @env.find { |closure| closure.has_key? name } || @env.first
+ hash[name] = val
+ end
+
+ def scope
+ @env.unshift({})
+ begin
+ yield
+ ensure
+ @env.shift
+ raise "You went too far unextending env" if @env.empty?
+ end
+ end
+ end
+end
+
+class Object
+
+ ##
+ # deep_clone is the usual Marshalling hack to make a deep copy.
+ # It is rather slow, so use it sparingly. Helps with debugging
+ # SexpProcessors since you usually shift off sexps.
+
+ def deep_clone
+ Marshal.load(Marshal.dump(self))
+ end
+end
+
+##
+# SexpProcessor base exception class.
+
+class SexpProcessorError < StandardError; end
+
+##
+# Raised by SexpProcessor if it sees a node type listed in its
+# unsupported list.
+
+class UnsupportedNodeError < SexpProcessorError; end
+
+##
+# Raised by SexpProcessor if it is in strict mode and sees a node for
+# which there is no processor available.
+
+class UnknownNodeError < SexpProcessorError; end
+
+##
+# Raised by SexpProcessor if a processor did not process every node in
+# a sexp and @require_empty is true.
+
+class NotEmptyError < SexpProcessorError; end
+
+##
+# Raised if assert_type encounters an unexpected sexp type.
+
+class SexpTypeError < SexpProcessorError; end