4
votes

I have a map of the form:

%{"browser_name" => "Chrome", "platform" => "linux"}

and I need to convert it to a keyword list:

[browser_name: "Chrome", platform: "linux"]

What's the best way of achieving this?

3

3 Answers

11
votes

Wouldn't this work:

 def convert_to_klist(map) do
   Enum.map(map, fn({key, value}) -> {String.to_existing_atom(key), value} end)
 end
7
votes

I would put it here for the sake of future readers.

While one surely might blindly call String.to_atom/1 here and there, that approach is strongly discouraged due to its vulnerability to atom DoS attacks.

Here is the excerpt from Erlang docs:

Atoms are not garbage-collected. Once an atom is created, it is never removed. The emulator terminates if the limit for the number of atoms (1,048,576 by default) is reached.

Therefore, converting arbitrary input strings to atoms can be dangerous in a system that runs continuously. If only certain well-defined atoms are allowed as input, list_to_existing_atom/1 can be used to guard against a denial-of-service attack. (All atoms that are allowed must have been created earlier, for example, by simply using all of them in a module and loading that module.)

http://erlang.org/doc/efficiency_guide/commoncaveats.html#list_to_atom-1

That said, whether you receive the map from the external untrusted source (e. g. it’s the parameters in the call to your API or something,) you must not use String.to_atom/1 and should use String.to_existing_atom/1 instead.

Otherwise, the intruder with a simple random generator for keys would blow up your ErlangVM without any issue.

3
votes

You can use the Keyword.new function and pass in a transform function as the second argument.

Keyword.new(%{"browser_name" => "Chrome", "platform" => "linux"}, fn {k,v} -> {String.to_existing_atom(k), v} end)
> [browser_name: "Chrome", platform: "linux"]

Although this is basically equivalent to the accepted answer from @NiteRain, I think it's a bit more explicit and shows future readers of your code your intentions.

See: https://hexdocs.pm/elixir/Keyword.html#new/2