Skip to content

Commit

Permalink
Prefer to transform mxc:// URIs to URLs to the HS rather than the sen…
Browse files Browse the repository at this point in the history
…der's server

Otherwise it is too easy to crash M51, eg. by sending a mxc:// URI with
a misbehaved server (eg. invalid TLS cert)
  • Loading branch information
progval committed Jun 6, 2022
1 parent 1b61870 commit e1b664a
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 19 deletions.
7 changes: 5 additions & 2 deletions lib/format/common.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ defmodule M51.Format do
iex> M51.Format.matrix2irc(~s(foo <font data-mx-color="FF0000">bar</font> baz))
"foo \x04FF0000,FFFFFFbar\x0399,99 baz"
"""
def matrix2irc(html) do
def matrix2irc(html, homeserver \\ nil) do
tree = :mochiweb_html.parse("<html>" <> html <> "</html>")
String.trim(M51.Format.Matrix2Irc.transform(tree, %M51.Format.Matrix2Irc.State{}))

String.trim(
M51.Format.Matrix2Irc.transform(tree, %M51.Format.Matrix2Irc.State{homeserver: homeserver})
)
end

@doc ~S"""
Expand Down
17 changes: 11 additions & 6 deletions lib/format/matrix2irc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
###

defmodule M51.Format.Matrix2Irc.State do
defstruct preserve_whitespace: false,
defstruct homeserver: nil,
preserve_whitespace: false,
color: {nil, nil}
end

Expand Down Expand Up @@ -80,9 +81,9 @@ defmodule M51.Format.Matrix2Irc do
{nil, nil, nil} -> transform_children(children, state)
{nil, nil, title} -> title
{nil, alt, _} -> alt
{link, nil, nil} -> format_url(link)
{link, nil, title} -> "#{title} <#{format_url(link)}>"
{link, alt, _} -> "#{alt} <#{format_url(link)}>"
{link, nil, nil} -> format_url(link, state.homeserver)
{link, nil, title} -> "#{title} <#{format_url(link, state.homeserver)}>"
{link, alt, _} -> "#{alt} <#{format_url(link, state.homeserver)}>"
end
end

Expand Down Expand Up @@ -175,10 +176,14 @@ defmodule M51.Format.Matrix2Irc do
end

@doc "Transforms a mxc:// \"URL\" into an actually usable URL."
def format_url(url, filename \\ nil) do
def format_url(url, homeserver \\ nil, filename \\ nil) do
case URI.parse(url) do
%{scheme: "mxc", host: host, path: path} ->
base_url = M51.MatrixClient.Client.get_base_url(host, M51.Config.httpoison())
# prefer the homeserver when available, it is more reliable than arbitrary
# hosts chosen by message senders
homeserver = homeserver || host

base_url = M51.MatrixClient.Client.get_base_url(homeserver, M51.Config.httpoison())

case filename do
nil ->
Expand Down
13 changes: 13 additions & 0 deletions lib/matrix_client/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,19 @@ defmodule M51.MatrixClient.Client do
end
end

def hostname(pid) do
case GenServer.call(pid, {:dump_state}) do
%M51.MatrixClient.Client{
state: :connected,
hostname: hostname
} ->
hostname

%M51.MatrixClient.Client{state: :initial_state} ->
nil
end
end

def register(pid, local_name, hostname, password) do
GenServer.call(pid, {:register, local_name, hostname, password}, @timeout)
end
Expand Down
27 changes: 16 additions & 11 deletions lib/matrix_client/poller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,11 @@ defmodule M51.MatrixClient.Poller do
state = M51.IrcConn.Supervisor.matrix_state(sup_pid)
channel = M51.MatrixClient.State.room_irc_channel(state, room_id)
member = M51.MatrixClient.State.room_member(state, room_id, sender)
client = M51.IrcConn.Supervisor.matrix_client(sup_pid)
send = make_send_function(sup_pid, event, write)

homeserver = M51.MatrixClient.Client.hostname(client)

tags = %{"account" => sender}

# TODO: dedup this with m.reaction handler
Expand Down Expand Up @@ -498,7 +501,7 @@ defmodule M51.MatrixClient.Poller do
body
end

{"PRIVMSG", M51.Format.matrix2irc(formatted_body) || body}
{"PRIVMSG", M51.Format.matrix2irc(formatted_body, homeserver) || body}

%{"msgtype" => "m.text", "body" => body} when is_binary(body) ->
body =
Expand All @@ -522,7 +525,8 @@ defmodule M51.MatrixClient.Poller do
"formatted_body" => formatted_body,
"body" => body
} ->
{"PRIVMSG", "\x01ACTION " <> (M51.Format.matrix2irc(formatted_body) || body) <> "\x01"}
{"PRIVMSG",
"\x01ACTION " <> (M51.Format.matrix2irc(formatted_body, homeserver) || body) <> "\x01"}

%{"msgtype" => "m.emote", "body" => body} when is_binary(body) ->
# TODO: ditto
Expand All @@ -534,7 +538,7 @@ defmodule M51.MatrixClient.Poller do
"formatted_body" => formatted_body,
"body" => body
} ->
{"NOTICE", M51.Format.matrix2irc(formatted_body) || body}
{"NOTICE", M51.Format.matrix2irc(formatted_body, homeserver) || body}

%{"msgtype" => "m.notice", "body" => body} when is_binary(body) ->
# TODO: ditto
Expand All @@ -543,38 +547,39 @@ defmodule M51.MatrixClient.Poller do
%{"msgtype" => "m.image", "body" => body, "url" => url, "filename" => filename}
when is_binary(body) and is_binary(url) and is_binary(filename) ->
if M51.Format.Matrix2Irc.useless_img_alt?(body) or body == filename do
{"PRIVMSG", M51.Format.Matrix2Irc.format_url(url, filename)}
{"PRIVMSG", M51.Format.Matrix2Irc.format_url(url, homeserver, filename)}
else
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, filename)}
{"PRIVMSG",
body <> " " <> M51.Format.Matrix2Irc.format_url(url, homeserver, filename)}
end

%{"msgtype" => "m.image", "body" => body, "url" => url}
when is_binary(body) and is_binary(url) ->
if M51.Format.Matrix2Irc.useless_img_alt?(body) do
{"PRIVMSG", M51.Format.Matrix2Irc.format_url(url)}
{"PRIVMSG", M51.Format.Matrix2Irc.format_url(url, homeserver)}
else
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url)}
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, homeserver)}
end

%{"msgtype" => "m.file", "body" => body, "url" => url, "filename" => filename}
when is_binary(body) and is_binary(url) and is_binary(filename) ->
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, filename)}
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, homeserver, filename)}

%{"msgtype" => "m.file", "body" => body, "url" => url}
when is_binary(body) and is_binary(url) ->
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url)}
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, homeserver)}

%{"msgtype" => "m.audio", "body" => body, "url" => url}
when is_binary(body) and is_binary(url) ->
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url)}
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, homeserver)}

%{"msgtype" => "m.location", "body" => body, "geo_uri" => geo_uri}
when is_binary(body) and is_binary(geo_uri) ->
{"PRIVMSG", body <> " (" <> geo_uri <> ")"}

%{"msgtype" => "m.video", "body" => body, "url" => url}
when is_binary(body) and is_binary(url) ->
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url)}
{"PRIVMSG", body <> " " <> M51.Format.Matrix2Irc.format_url(url, homeserver)}

%{"body" => body} when is_binary(body) ->
# fallback
Expand Down
14 changes: 14 additions & 0 deletions test/format/common_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ defmodule M51.FormatTest do
body: ~s({"m.homeserver": {"base_url": "https://api.example.org"}})
}
end)
|> expect(:get!, 1, fn url ->
assert url == "https://homeserver.org/.well-known/matrix/client"

%HTTPoison.Response{
status_code: 200,
body: ~s({"m.homeserver": {"base_url": "https://api.homeserver.org"}})
}
end)

assert M51.Format.matrix2irc(~s(<a href="https://example.org">foo</a>)) ==
"foo <https://example.org>"
Expand All @@ -145,6 +153,12 @@ defmodule M51.FormatTest do
) ==
"an image <https://api.example.org/_matrix/media/r0/download/example.org/foo>"

assert M51.Format.matrix2irc(
~s(<img src="mxc://example.org/foo" alt="an image" title="blah"/>),
"homeserver.org"
) ==
"an image <https://api.homeserver.org/_matrix/media/r0/download/example.org/foo>"

assert M51.Format.matrix2irc(~s(<img" />)) ==
""

Expand Down

0 comments on commit e1b664a

Please sign in to comment.