Generating HTML from strings rather than templates

Fairly new to elixir and phoenix, but am really enjoying how it all fits together. I have a question about templates, is there a way to run a render function from a string rather than a file? Say I have a string that includes <%= function_call() %> and have it generate the html that I can send to another render function that uses a template?

Obviously this would have security issues, because I would be taking strings from a database that I entered into an admin input, and if someone got access they could do a <%= destroy_database() %> like command. So a second question is can I limit the modules that can be called from a template? Then the template would only be able to run functions I have supplied.

So two questions, can I limit modules to a template, and can I generate code from a string from a database rather than a template, and also limit modules to that string?

If not I think it would be a nice feature if security issues are sorted out.

Hello, there is a special sigil to represent template in Phoenix…

https://hexdocs.pm/phoenix_html/Phoenix.HTML.html#sigil_E/2

No, EEX is not safe to use for user supplied templates. Consider using something like mustache.

2 Likes

~e should be safe ?!

All of those operate at compile time on compile time values. If you have code that accepts a user supplied string and evaluates it at runtime, it’s exactly the same as calling Code.eval_string, it’s wildly unsafe.

4 Likes

My bad… I did not read the part concerning security correctly. in particular user facing template.

I would be nice if there was a way to limit an eval_string to only allow certain modules, and just throw exceptions for functions not within that module.

Mustache might work… Mainly I wanted to be able to do an image tag like this

~E"""
<img source="<%= Routes.static_path("/image.png") =>" />
“”"

Then be able to change the static path to cdn or whatever.

I guess Mustache could look like this, I’ll look into it.
“”"
<img source=""{{full_path}}/image.png"" />
“”"

EDIT: ignore the double quote, it was trying to be an image in the post.

Maybe a module limited eval_string would be a good thing to put in suggestions, could be used as a user scripting language that only allows very specific functions without having to introduce a new syntax.

For some reason I get “undefined function sigil_E/2” when I try to use the ~E syntax, do I have to include something?

Making EEX safe is as exactly as impossible as making .exs safe. Here are some past threads on the issue:

4 Likes

Adding to this, because I abandoned work on my module-white-listing sandboxing project after related research and feedback from BEAM creators, and am linked in these links:

There is no reliable way to prevent arbitrary user-supplied code from invoking functions inside particular modules on the BEAM, and simply parsing the AST of user-supplied code cannot protect you from all attack vectors.

There are simply too many M/F/A calling eval equivalents that power essential spawning mechanics to be able to enforce that user code not access the host’s file system, network, or compute resources.

The BEAM was designed to execute functions against any connected node’s resources by design, and so arbitrary-calling-mechanics are core to its function. As such, running any untrusted Elixir/erlang/BEAM code must be done in resource-constrained environments to ensure hygiene and safety.

For my use case eval/exs files will be fine, since I will be the only person updating the app. Mustache looks like it will do for outside users. I can’t seem to get the sigil_E function to work, iex claims that it doesn’t even exist in the Kernel.

$ mix phx.new --no-webpack --no-ecto test_sigil
(with latest phx.new installed)

$ elixir --version
Erlang/OTP 22 [erts-10.5.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.9.1 (compiled with Erlang/OTP 21)

in mix.exs
def project do
[
app: :test_sigil,
version: “0.1.0”,
elixir: “~> 1.9.1”,
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

defp deps do
[
{:phoenix, “~> 1.4.10”},
{:phoenix_pubsub, “~> 1.1.2”},
{:phoenix_html, “~> 2.13”},
{:phoenix_live_reload, “~> 1.2”, only: :dev},
{:gettext, “~> 0.11”},
{:jason, “~> 1.0”},
{:plug_cowboy, “~> 2.0”}
]
end

iex(1)> h sigil_r

                   defmacro sigil_r(term, modifiers)                        

Handles the sigil ~r for regular expressions.

It returns a regular expression pattern, unescaping characters and replacing
interpolations.

More information on regular expressions can be found in the Regex module.

Examples

iex> Regex.match?(~r(foo), "foo")
true

iex> Regex.match?(~r/abc/, "abc")
true

iex(2)> h sigil_E
No documentation for Kernel.sigil_E was found
iex(3)> test_sigil = ~E"BOB"
** (CompileError) iex:3: undefined function sigil_E/2

~E is not part of the standard library, it’s provided by Phoenix HTML https://hexdocs.pm/phoenix_html/Phoenix.HTML.html#sigil_E/2

2 Likes