diff --git a/lib/tainted/static.rb b/lib/tainted/static.rb index 32bf01e..4c6b10c 100644 --- a/lib/tainted/static.rb +++ b/lib/tainted/static.rb @@ -30,10 +30,17 @@ def visit(node) def parse_assign(node) variable_name = node.target.value.value - # pp node.value.class - return unless node.value.is_a?(SyntaxTree::CallNode) - method_name = node.value.message.value + method_name = + case node.value + when SyntaxTree::CallNode + node.value.message.value + when SyntaxTree::ARef + # (aref (vcall (ident ""))) + node.value.collection.value.value + end + + return if method_name.nil? return unless @sources.include?(method_name&.to_sym) State.instance.var_dependencies[variable_name.to_sym][:tainted] = true diff --git a/lib/tainted/version.rb b/lib/tainted/version.rb index dfbd195..7f1e07d 100644 --- a/lib/tainted/version.rb +++ b/lib/tainted/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Tainted - VERSION = "0.1.0" + VERSION = "0.2.0" end diff --git a/spec/fixtures/params.rb b/spec/fixtures/params.rb new file mode 100644 index 0000000..ed57189 --- /dev/null +++ b/spec/fixtures/params.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +a = params[:insecure] +b = a + 1 +c = b + 2 +d = b + c + +sql = "select * from users where age = #{d};" +execute(sql) diff --git a/spec/lib/tainted/lint_spec.rb b/spec/lib/tainted/lint_spec.rb index ccbc4ce..4d62ed9 100644 --- a/spec/lib/tainted/lint_spec.rb +++ b/spec/lib/tainted/lint_spec.rb @@ -14,5 +14,17 @@ ] ) end + + it "returns issue for sql query from unsanitized param" do + file = File.expand_path "#{__dir__}/../../fixtures/params.rb" + lint = Tainted::Lint.new(file, %i[params], %i[execute]) + result = lint.analyze + + expect(result).to eq( + [ + "Method `execute()` consuming tainted variable `sql`", + ] + ) + end end end