Add backend MFM support
This commit is contained in:
parent
ebcc7684c1
commit
6b86e31e5d
15 changed files with 423 additions and 10 deletions
|
|
@ -144,7 +144,13 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
quarantined_instances: [],
|
||||
managed_config: true,
|
||||
static_dir: "instance/static/",
|
||||
allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
|
||||
allowed_post_formats: [
|
||||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode",
|
||||
"text/x.misskeymarkdown"
|
||||
],
|
||||
autofollowed_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
attachment_links: false,
|
||||
|
|
|
|||
|
|
@ -149,6 +149,171 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a Misskey MFM note is rendered from source content" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/alice")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/1",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => "$[spin.speed=1s mfm goes here] <script>alert('xss')</script>",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source["mediaType"] == "text/x.misskeymarkdown"
|
||||
assert content =~ ~s(class="mfm-spin")
|
||||
assert content =~ ~s(data-mfm-speed="1s")
|
||||
assert content =~ "mfm goes here"
|
||||
refute content =~ "original content"
|
||||
refute content =~ "<script"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note resolves only cached AP mention tags" do
|
||||
remote_user = insert(:user, ap_id: "https://misskey.example/users/carol")
|
||||
local_user = insert(:user, nickname: "local_user")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/3",
|
||||
"type" => "Note",
|
||||
"actor" => remote_user.ap_id,
|
||||
"attributedTo" => remote_user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"tag" => [
|
||||
%{
|
||||
"type" => "Mention",
|
||||
"name" => "@local_user",
|
||||
"href" => local_user.ap_id
|
||||
},
|
||||
%{
|
||||
"type" => "Mention",
|
||||
"name" => "@uncached",
|
||||
"href" => "https://misskey.example/users/uncached"
|
||||
}
|
||||
],
|
||||
"source" => %{
|
||||
"content" => "@local_user @uncached $[spin hello]",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content =~ local_user.ap_id
|
||||
assert content =~ "@uncached"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note drops oversized source content instead of parsing it" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/oversized")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/4",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "<span class=\"mfm-spin\">safe fallback</span>",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content == "<span class=\"mfm-spin\">safe fallback</span>"
|
||||
refute Map.has_key?(source, "content")
|
||||
end
|
||||
|
||||
test "a note drops oversized non-MFM source content" do
|
||||
user = insert(:user, ap_id: "https://example.com/users/source")
|
||||
|
||||
note = %{
|
||||
"id" => "https://example.com/notes/1",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "regular content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/markdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{source: source}} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source == %{"mediaType" => "text/markdown"}
|
||||
end
|
||||
|
||||
test "a Misskey MFM note with legacy _misskey_content is rendered" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/legacy")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/5",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"_misskey_content" => "$[spin legacy]"
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source == %{"content" => "$[spin legacy]", "mediaType" => "text/x.misskeymarkdown"}
|
||||
assert content =~ ~s(class="mfm-spin")
|
||||
assert content =~ "legacy"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note with htmlMfm is scrubbed but not rendered from source content" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/bob")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/2",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" =>
|
||||
"<span class=\"mfm-spin\">already rendered</span><script>alert('xss')</script>",
|
||||
"htmlMfm" => true,
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, htmlMfm: true, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content == "<span class=\"mfm-spin\">already rendered</span>alert('xss')"
|
||||
refute Map.has_key?(source, "content")
|
||||
end
|
||||
|
||||
test "a Note with validated likes collection validates" do
|
||||
insert(:user, ap_id: "https://pol.social/users/mkljczk")
|
||||
|
||||
|
|
|
|||
|
|
@ -180,7 +180,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
"@language" => "und",
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -192,7 +193,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "pl"
|
||||
"@language" => "pl",
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -709,6 +709,47 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
assert object.data["source"]["content"] == post
|
||||
end
|
||||
|
||||
test "it renders MFM posts and marks their ActivityPub representation" do
|
||||
user = insert(:user)
|
||||
|
||||
post = "<p class='scrub-this'>$[spin.speed=1s 13:37]</p>"
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: post,
|
||||
content_type: "text/x.misskeymarkdown"
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
assert object.data["htmlMfm"] == true
|
||||
|
||||
assert object.data["source"] == %{
|
||||
"content" => post,
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
|
||||
assert object.data["content"] =~ ~s(class="mfm-spin")
|
||||
assert object.data["content"] =~ ~s(data-mfm-speed="1s")
|
||||
assert object.data["content"] =~ "13:37"
|
||||
refute object.data["content"] =~ "scrub-this"
|
||||
end
|
||||
|
||||
test "it falls back safely for malformed MFM" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "$[spin.speed=1s=boom malformed]",
|
||||
content_type: "text/x.misskeymarkdown"
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
refute object.data["content"] =~ ~s(class="mfm-spin")
|
||||
assert object.data["content"] =~ "malformed"
|
||||
end
|
||||
|
||||
test "it does not allow replies to direct messages that are not direct messages themselves" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue