sabri
February 23, 2017, 11:22am
1
I am trying to implement several queries against Cassandra DB, here is my attempt:
def last_locations(conn, params) do
{user_ids, _}= Code.eval_string(params["user_ids"])
locations= user_ids
|> Enum.map(&Task.async(fn -> last_location(&1, params) end))
|> Enum.map(&Task.await/1)
json(conn, locations)
end
defp last_location(user_id, params) do
client = CQEx.Client.new!
organization_id= params["organization_id"]
user_id= params["user_id"]
query= "SELECT * from myapp.locations WHERE organization_id=#{organization_id} and user_id=#{user_id} LIMIT 1;"
result= for [organization_id: _, user_id: _, date: _, unix_time: _, lat: lat, long: long] <- Q.call!(client, query)
|> Enum.to_list,
do: [lat, long]
result
end
So, basically, I get user ids, and pass them to last_location function through Task.async, but when I call last_locations I get error:
Is there something I am missing?
EDIT
I have tested the function last_locations to return user_ids via json(conn, user_ids) without calling Enum.map and I got them back, so the function last_locations is being called for sure.
Only when executing:
locations= user_ids
|> Enum.map(&Task.async(fn -> last_location(&1, params) end))
|> Enum.map(&Task.await/1)
I will never get a response back.
I will never get a response back.
Task.await/1 has a timeout of 5 seconds, so if you were not really getting anything back, you would eventually trigger a timeout. You need to investigate a bit more what is happening.
{user_ids, _}= Code.eval_string(params["user_ids"])
Please do not ever evaluate user input . The snippet above will allow anyone to execute any code they want in your server. If you want to convert from strings to integers, use String.to_integer or Integer.parse while traversing the list.
sabri
February 23, 2017, 1:01pm
3
Thanks, but how would I convert "[1, 2, 3, 4]" to [1, 2, 3, 4] without using eval ?
NobbZ
February 23, 2017, 1:18pm
4
By parsing it yourself. (lexx and yeec may help here)
Or at least by prechecking if it really does it match this regex ~r/\[[0-9, ]\]/ before evaluating.
Golden rule of userinput: don’t trust it without serverside validation.
Just hint:
"1, 2, 3, 4" |> String.split(",") |> Enum.map(&(String.trim(&1) |> String.to_integer))
results: [1, 2, 3, 4]
Just to make sure you understand the severity of the risk with eval: What if user_ids was code to send all of your application passwords to another website? Or install malware on your server? or delete specific data? You’d have just run it.
sabri:
“[1, 2, 3, 4]”
oh, well why implement own parser when you can:
iex(2)> Poison.decode!("[1, 2, 3, 4]")
[1, 2, 3, 4]
Most likely he uses Poison already.
NobbZ
February 23, 2017, 1:43pm
9
Thats of course is true.
The suggestion to write a parser was more for even more complex stuff one wants to “convert”.
sabri
February 23, 2017, 2:57pm
10
Yes, I’ve got the point, very dangerous indeed.
sabri
February 23, 2017, 3:22pm
11
Thank you, worked perfectly
Or via using ExSpirit :
input = "[1, 2, 3, 4, 5]"
%{error: nil, result: output} = parse(input, seq([lit(\[), uint(), repeat(lit(?,) |> uint()), lit(\])]), skipper: repeat(lit(?\s)))
# output = [1, 2, 3, 4, 5]
And that is if you want the [ and ] there for sure, with , as separators, and optional any amount of spacing.
^.^
NobbZ
February 23, 2017, 6:12pm
13
OvermindDL1:
Or via using ExSpirit:
Which is a parser combinator…