tainted = identity source("tainted")
sink tainted # $ hasValueFlow=tainted

tainted2 = apply_block tainted do |x|
  sink x # $ hasValueFlow=tainted
  x
end

sink tainted2 # $ hasValueFlow=tainted

my_lambda = -> (x) {
  sink x # $ hasValueFlow=tainted
  x
}

tainted3 = apply_lambda(my_lambda, tainted)

sink(tainted3) # $ hasValueFlow=tainted

tainted4 = Foo.firstArg(tainted)
sink(tainted4) # $ hasTaintFlow=tainted

notTainted = Foo.firstArg(nil, tainted)
sink(notTainted)

tainted5 = Foo.secondArg(nil, tainted)
sink(tainted5) # $ hasTaintFlow=tainted

sink(Foo.onlyWithBlock(tainted))
sink(Foo.onlyWithBlock(tainted) do |x| end) # $ hasTaintFlow=tainted
sink(Foo.onlyWithoutBlock(tainted)) # $ hasTaintFlow=tainted
sink(Foo.onlyWithoutBlock(tainted) do |x| end)

Foo.new.method(tainted) # $ hasValueFlow=tainted
Bar.new.method(tainted) # $ hasValueFlow=tainted
Bar.new.next.method(tainted) # $ hasValueFlow=tainted
Bar.new.next.next.next.next.method(tainted) # $ hasValueFlow=tainted

def userDefinedFunction(x, y)
  t = source("t")
  sink(x.matchedByName(t)) # $ hasTaintFlow=t
  sink(y.matchedByName(t)) # $ hasTaintFlow=t
  sink(x.unmatchedName(t))
  sink(t.matchedByNameRcv()) # $ hasTaintFlow=t
end

Foo.blockArg do |x|
  sink(x.preserveTaint(source("blockArg"))) # $ hasTaintFlow=blockArg
end

sink(Foo.namedArg(foo: tainted)) # $ hasTaintFlow=tainted
sink(Foo.namedArg(tainted))
args = { foo: source("tainted") }
sink(Foo.namedArg(**args)) # $ hasTaintFlow=tainted

sink(Foo.anyArg(foo: tainted)) # $ hasTaintFlow=tainted
sink(Foo.anyArg(tainted)) # $ hasTaintFlow=tainted

sink(Foo.anyNamedArg(foo: tainted)) # $ hasTaintFlow=tainted
sink(Foo.anyNamedArg(tainted))

sink(Foo.anyPositionFromOne(tainted))
sink(Foo.anyPositionFromOne(0, tainted)) # $ hasTaintFlow=tainted

Foo.intoNamedCallback(tainted, foo: ->(x) {
  sink(x) # $ hasTaintFlow=tainted
})
Foo.intoNamedParameter(tainted, ->(foo:) {
  sink(foo) # $ MISSING: hasTaintFlow=tainted
})

Foo.startInNamedCallback(foo: ->(x) {
  sink(x.preserveTaint(source("startInNamedCallback"))) # $ hasTaintFlow=startInNamedCallback
})
Foo.startInNamedParameter(->(foo:) {
  sink(foo.preserveTaint(source("startInNamedParameter"))) # $ hasTaintFlow=startInNamedParameter
})

a = ["elem0", source("elem1"), source("elem2")]
d = a
a[rand()] = source("elem3")
sink(a.readElementOne(1)) # $ hasValueFlow=elem1 $ hasValueFlow=elem3
sink(a.readExactlyElementOne(1)) # $ hasValueFlow=elem1
sink(a[0]) # $ hasValueFlow=elem3
sink(a[1]) # $ hasValueFlow=elem1 $ hasValueFlow=elem3
sink(a[2]) # $ hasValueFlow=elem2 $ hasValueFlow=elem3
b = a.withElementOne()
sink(b[0]) # $ hasValueFlow=elem3
sink(b[1]) # $ hasValueFlow=elem1 $ hasValueFlow=elem3
sink(b[2]) # $ hasValueFlow=elem3
c = a.withExactlyElementOne()
sink(c[0])
sink(c[1]) # $ hasValueFlow=elem1
sink(c[2])
a.withoutExactlyElementOne()
sink(a[0]) # $ hasValueFlow=elem3
sink(a[1]) # $ hasValueFlow=elem3
sink(a[2]) # $ hasValueFlow=elem2 $ hasValueFlow=elem3
a.withoutElementOne()
sink(a[0])
sink(a[1])
sink(a[2]) # $ hasValueFlow=elem2
d[3] = source("elem3")
d.withoutElementOneAndTwo()
sink(d[0])
sink(d[1])
sink(d[2])
sink(d[3]) # $ hasValueFlow=elem3

x = Foo.new
x.set_value(source("attr"))
sink(x.get_value) # $ hasValueFlow=attr

x = Foo.new
y = []
z = []
# This just highlights that none of x,y,z was tainted before
sink(x)
sink(y)
sink(z)

x.flowToAnyArg(tainted, y, key: z)
sink(x)
sink(y) # $ hasTaintFlow=tainted
sink(z) # $ hasTaintFlow=tainted

x = Foo.new
x.flowToSelf(tainted)
sink(x) # $ hasTaintFlow=tainted

Foo.sinkAnyArg(tainted) # $ hasValueFlow=tainted $ hasTaintFlow=tainted
Foo.sinkAnyArg(key: tainted) # $ hasValueFlow=tainted

Foo.sinkAnyNamedArg(tainted)
Foo.sinkAnyNamedArg(key: tainted) # $ hasValueFlow=tainted

"magic_string".method(tainted) # $ hasValueFlow=tainted
"magic_string2".method(tainted)

Alias::Foo.method(tainted) # $ hasValueFlow=tainted
Alias::Bar.method(tainted) # $ hasValueFlow=tainted
Something::Foo.method(tainted)
Alias::Something.method(tainted)

Foo.getSinks()[0].mySink(tainted) # $ hasValueFlow=tainted
Foo.arraySink(tainted)
Foo.arraySink([tainted]) # $ hasValueFlow=tainted

Foo.secondArrayElementIsSink([tainted, "safe", "safe"])
Foo.secondArrayElementIsSink(["safe", tainted, "safe"]) # $ hasValueFlow=tainted
Foo.secondArrayElementIsSink(["safe", "safe", tainted])
Foo.secondArrayElementIsSink([tainted] * 10) # $ MISSING: hasValueFlow=tainted

FuzzyLib.fuzzyCall(tainted) # $ hasValueFlow=tainted
FuzzyLib.foo.bar.fuzzyCall(tainted) # $ hasValueFlow=tainted
FuzzyLib.foo[0].fuzzyCall(tainted) # $ hasValueFlow=tainted
FuzzyLib.foo do |x|
  x.fuzzyCall(tainted) # $ hasValueFlow=tainted
  x.otherCall(tainted)
end
class FuzzySub < FuzzyLib::Foo
  def blah
    self.fuzzyCall(source("tainted")) # $ hasValueFlow=tainted
  end
  def self.blah
    self.fuzzyCall(source("tainted")) # $ hasValueFlow=tainted
  end
end

class SynthGlobalTest
  def store
    @x.someField = source("tainted")
    @x.saveToDatabase()
  end

  def read
    @x = readFromDatabase()
    sink(@x)
    sink(@x.someField) # $ hasValueFlow=tainted
    sink(@x.someOtherField)
  end
end