check if data is visible before embedding it in OG tags

previously we would uncritically take data and format it into
tags for static-fe and the like - however, instances can be
configured to disallow unauthenticated access to these resources.

this means that OG tags as a vector for information leakage.

_technically_ this should only occur if you have both
restrict_unauthenticated *AND* you run static-fe, which makes no
sense since static-fe is for unauthenticated people in particular,
but hey ho.
This commit is contained in:
Floatingghost 2024-04-12 05:16:47 +01:00
parent 1135935cbe
commit 05f8179d08
6 changed files with 115 additions and 25 deletions

View file

@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Fixed
- Issue preventing fetching anything from IPv6-only instances
- Issue allowing post content to leak via opengraph tags despite :estrict\_unauthenticated being set
## 2024.03

View file

@ -12,14 +12,38 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
@behaviour Provider
@media_types ["image", "audio", "video"]
defp user_avatar_tags(user) do
if Utils.visible?(user) do
[
{:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
[]},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
]
else
[]
end
end
@impl Provider
def build_tags(%{
object: object,
url: url,
user: user
}) do
attachments = build_attachments(object)
scrubbed_content = Utils.scrub_html_and_truncate(object)
attachments =
if Utils.visible?(object) do
build_attachments(object)
else
[]
end
scrubbed_content =
if Utils.visible?(object) do
Utils.scrub_html_and_truncate(object)
else
"Content cannot be displayed."
end
[
{:meta,
@ -36,12 +60,7 @@ def build_tags(%{
{:meta, [property: "og:type", content: "article"], []}
] ++
if attachments == [] or Metadata.activity_nsfw?(object) do
[
{:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
[]},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
]
user_avatar_tags(user)
else
attachments
end
@ -49,7 +68,9 @@ def build_tags(%{
@impl Provider
def build_tags(%{user: user}) do
with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
if Utils.visible?(user) do
truncated_bio = Utils.scrub_html_and_truncate(user.bio)
[
{:meta,
[
@ -58,12 +79,10 @@ def build_tags(%{user: user}) do
], []},
{:meta, [property: "og:url", content: user.uri || user.ap_id], []},
{:meta, [property: "og:description", content: truncated_bio], []},
{:meta, [property: "og:type", content: "article"], []},
{:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
[]},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
]
{:meta, [property: "og:type", content: "article"], []}
] ++ user_avatar_tags(user)
else
[]
end
end

View file

@ -17,8 +17,19 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
@impl Provider
def build_tags(%{activity_id: id, object: object, user: user}) do
attachments = build_attachments(id, object)
scrubbed_content = Utils.scrub_html_and_truncate(object)
attachments =
if Utils.visible?(object) do
build_attachments(id, object)
else
[]
end
scrubbed_content =
if Utils.visible?(object) do
Utils.scrub_html_and_truncate(object)
else
"Content cannot be displayed."
end
[
title_tag(user),
@ -36,13 +47,17 @@ def build_tags(%{activity_id: id, object: object, user: user}) do
@impl Provider
def build_tags(%{user: user}) do
with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
[
title_tag(user),
{:meta, [name: "twitter:description", content: truncated_bio], []},
image_tag(user),
{:meta, [name: "twitter:card", content: "summary"], []}
]
if Utils.visible?(user) do
with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
[
title_tag(user),
{:meta, [name: "twitter:description", content: truncated_bio], []},
image_tag(user),
{:meta, [name: "twitter:card", content: "summary"], []}
]
end
else
[]
end
end
@ -51,7 +66,11 @@ defp title_tag(user) do
end
def image_tag(user) do
{:meta, [name: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))], []}
if Utils.visible?(user) do
{:meta, [name: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))], []}
else
{:meta, [name: "twitter:image", content: ""], []}
end
end
defp build_attachments(id, %{data: %{"attachment" => attachments}}) do

View file

@ -7,6 +7,15 @@ defmodule Pleroma.Web.Metadata.Utils do
alias Pleroma.Emoji
alias Pleroma.Formatter
alias Pleroma.HTML
alias Pleroma.Web.ActivityPub.Visibility
def visible?(%Pleroma.User{} = object) do
Visibility.restrict_unauthenticated_access?(object) == :visible
end
def visible?(object) do
Visibility.visible_for_user?(object, nil)
end
defp scrub_html_and_truncate_object_field(field, object) do
field

View file

@ -9,6 +9,8 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
setup do: clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
setup do: clear_config([:restrict_unauthenticated, :profiles, :local])
setup do: clear_config([:restrict_unauthenticated, :activities, :local])
test "it renders all supported types of attachments and skips unknown types" do
user = insert(:user)
@ -188,4 +190,24 @@ test "video attachments have no image thumbnail with Preview Proxy disabled" do
"http://localhost:4001/proxy/preview/LzAnlke-l5oZbNzWsrHfprX1rGw/aHR0cHM6Ly9wbGVyb21hLmdvdi9hYm91dC9qdWNoZS53ZWJt/juche.webm"
], []} in result
end
test "it does not render users if profiles are marked as restricted" do
clear_config([:restrict_unauthenticated, :profiles, :local], true)
user = insert(:user)
result = OpenGraph.build_tags(%{user: user})
assert Enum.empty?(result)
end
test "it does not activities users if they are marked as restricted" do
clear_config([:restrict_unauthenticated, :activities, :local], true)
user = insert(:user)
note = insert(:note, data: %{"actor" => user.ap_id})
result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
assert {:meta, [property: "og:description", content: "Content cannot be displayed."], []} in result
end
end

View file

@ -14,6 +14,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
alias Pleroma.Web.Metadata.Utils
setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
setup do: clear_config([:restrict_unauthenticated, :profiles, :local])
setup do: clear_config([:restrict_unauthenticated, :activities, :local])
test "it renders twitter card for user info" do
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
@ -28,6 +30,14 @@ test "it renders twitter card for user info" do
]
end
test "it does not render twitter card for user info if it is restricted" do
clear_config([:restrict_unauthenticated, :profiles, :local], true)
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
res = TwitterCard.build_tags(%{user: user})
assert Enum.empty?(res)
end
test "it uses summary twittercard if post has no attachment" do
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
{:ok, activity} = CommonAPI.post(user, %{status: "HI"})
@ -54,6 +64,16 @@ test "it uses summary twittercard if post has no attachment" do
] == result
end
test "it does not summarise activities if they are marked as restricted" do
clear_config([:restrict_unauthenticated, :activities, :local], true)
user = insert(:user)
note = insert(:note, data: %{"actor" => user.ap_id})
result = TwitterCard.build_tags(%{object: note, activity_id: note.data["id"], user: user})
assert {:meta, [name: "twitter:description", content: "Content cannot be displayed."], []} in result
end
test "it uses summary as description if post has one" do
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
{:ok, activity} = CommonAPI.post(user, %{status: "HI"})