check if data is visible before embedding it in OG tags
Some checks are pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-amd64 Pipeline was successful
ci/woodpecker/push/build-arm64 Pipeline was successful
ci/woodpecker/push/docs Pipeline was successful
Some checks are pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-amd64 Pipeline was successful
ci/woodpecker/push/build-arm64 Pipeline was successful
ci/woodpecker/push/docs Pipeline was successful
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:
parent
1135935cbe
commit
05f8179d08
6 changed files with 115 additions and 25 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"})
|
||||
|
|
Loading…
Reference in a new issue