Fix some tests and fix code that was failing tests. Catch HTML signatures with invalid padding.

ap-processing-improvements
Alain St-Denis 2023-07-10 14:39:55 -04:00
rodzic 1f8d4ac93f
commit 24f5bb21a9
9 zmienionych plików z 68 dodań i 86 usunięć

Wyświetl plik

@ -75,8 +75,8 @@ def verify_ld_signature(payload):
obj_digest = hash(obj)
digest = (sig_digest + obj_digest).encode('utf-8')
sig_value = b64decode(signature.get('signatureValue'))
try:
sig_value = b64decode(signature.get('signatureValue'))
verifier.verify(SHA256.new(digest), sig_value)
logger.debug('ld_signature - %s has a valid signature', payload.get("id"))
return profile.id

Wyświetl plik

@ -4,7 +4,7 @@ import logging
import re
import traceback
import uuid
from datetime import timedelta
from operator import attrgetter
from typing import List, Dict, Union
from urllib.parse import urlparse
@ -801,8 +801,9 @@ class Note(Object, RawContentMixin):
for el in self._soup('a', attrs={'class':'hashtag'}):
self.tag_objects.append(Hashtag(
href = el.attrs['href'],
name = el.text.lstrip('#')
name = el.text
))
self.tag_objects = sorted(self.tag_objects, key=attrgetter('name'))
if el.text == '#nsfw': self.sensitive = True
# Add Mention objects

Wyświetl plik

@ -237,8 +237,8 @@ class RawContentMixin(BaseEntity):
@property
def tags(self) -> List[str]:
if not self.raw_content:
return
return find_tags(self.raw_content)
return []
return sorted(find_tags(self.raw_content))
def extract_mentions(self):
if not self.raw_content:

Wyświetl plik

@ -1,3 +1,4 @@
import commonmark
import pytest
from unittest.mock import patch
from pprint import pprint
@ -9,6 +10,7 @@ from federation.entities.activitypub.models import context_manager
from federation.entities.activitypub.models import Accept
from federation.tests.fixtures.keys import PUBKEY
from federation.types import UserType
from federation.utils.text import process_text_links
class TestEntitiesConvertToAS2:
@ -65,6 +67,8 @@ class TestEntitiesConvertToAS2:
def test_comment_to_as2__url_in_raw_content(self, activitypubcomment):
activitypubcomment.raw_content = 'raw_content http://example.com'
activitypubcomment.rendered_content = process_text_links(
commonmark.commonmark(activitypubcomment.raw_content).strip())
activitypubcomment.pre_send()
result = activitypubcomment.to_as2()
assert result == {
@ -118,6 +122,7 @@ class TestEntitiesConvertToAS2:
}
def test_post_to_as2(self, activitypubpost):
activitypubpost.rendered_content = commonmark.commonmark(activitypubpost.raw_content).strip()
activitypubpost.pre_send()
result = activitypubpost.to_as2()
assert result == {
@ -191,6 +196,15 @@ class TestEntitiesConvertToAS2:
}
def test_post_to_as2__with_tags(self, activitypubpost_tags):
activitypubpost_tags.rendered_content = '<h1>raw_content</h1>\n' \
'<p><a class="hashtag" ' \
'href="https://example.com/tag/foobar/" rel="noopener ' \
'noreferrer nofollow" ' \
'target="_blank">#<span>foobar</span></a>\n' \
'<a class="hashtag" ' \
'href="https://example.com/tag/barfoo/" rel="noopener ' \
'noreferrer nofollow" ' \
'target="_blank">#<span>barfoo</span></a></p>'
activitypubpost_tags.pre_send()
result = activitypubpost_tags.to_as2()
assert result == {
@ -204,11 +218,11 @@ class TestEntitiesConvertToAS2:
'url': 'http://127.0.0.1:8000/post/123456/',
'attributedTo': 'http://127.0.0.1:8000/profile/123456/',
'content': '<h1>raw_content</h1>\n'
'<p><a class="mention hashtag" '
'<p><a class="hashtag" '
'href="https://example.com/tag/foobar/" rel="noopener '
'noreferrer nofollow" '
'target="_blank">#<span>foobar</span></a>\n'
'<a class="mention hashtag" '
'<a class="hashtag" '
'href="https://example.com/tag/barfoo/" rel="noopener '
'noreferrer nofollow" '
'target="_blank">#<span>barfoo</span></a></p>',
@ -235,6 +249,7 @@ class TestEntitiesConvertToAS2:
}
def test_post_to_as2__with_images(self, activitypubpost_images):
activitypubpost_images.rendered_content = '<p>raw_content</p>'
activitypubpost_images.pre_send()
result = activitypubpost_images.to_as2()
assert result == {
@ -274,6 +289,7 @@ class TestEntitiesConvertToAS2:
}
def test_post_to_as2__with_diaspora_guid(self, activitypubpost_diaspora_guid):
activitypubpost_diaspora_guid.rendered_content = '<p>raw_content</p>'
activitypubpost_diaspora_guid.pre_send()
result = activitypubpost_diaspora_guid.to_as2()
assert result == {
@ -418,17 +434,6 @@ class TestEntitiesPostReceive:
"public": False,
}]
@patch("federation.entities.activitypub.models.bleach.linkify", autospec=True)
def test_post_post_receive__linkifies_if_not_markdown(self, mock_linkify, activitypubpost):
activitypubpost._media_type = 'text/html'
activitypubpost.post_receive()
mock_linkify.assert_called_once()
@patch("federation.entities.activitypub.models.bleach.linkify", autospec=True)
def test_post_post_receive__skips_linkify_if_markdown(self, mock_linkify, activitypubpost):
activitypubpost.post_receive()
mock_linkify.assert_not_called()
class TestEntitiesPreSend:
def test_post_inline_images_are_attached(self, activitypubpost_embedded_images):

Wyświetl plik

@ -4,6 +4,9 @@ from unittest.mock import patch, Mock, DEFAULT
import json
import pytest
from federation.entities.activitypub.models import Person
#from federation.entities.activitypub.entities import (
# models.Follow, models.Accept, models.Person, models.Note, models.Note,
# models.Delete, models.Announce)
@ -70,9 +73,7 @@ class TestActivitypubEntityMappersReceive:
post = entities[0]
assert isinstance(post, models.Note)
assert isinstance(post, Post)
assert post.raw_content == '<p><span class="h-card"><a class="u-url mention" ' \
'href="https://dev.jasonrobinson.me/u/jaywink/">' \
'@<span>jaywink</span></a></span> boom</p>'
assert post.raw_content == ''
assert post.rendered_content == '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/">' \
'@<span>jaywink</span></a></span> boom</p>'
assert post.id == "https://diaspodon.fr/users/jaywink/statuses/102356911717767237"
@ -87,40 +88,41 @@ class TestActivitypubEntityMappersReceive:
post = entities[0]
assert isinstance(post, models.Note)
assert isinstance(post, Post)
assert post.raw_content == '<p>boom #test</p>'
assert post.raw_content == ''
assert post.rendered_content == '<p>boom <a class="mention hashtag" data-hashtag="test" href="https://mastodon.social/tags/test" rel="tag">#<span>test</span></a></p>'
# TODO: fix this test
@pytest.mark.skip
def test_message_to_objects_simple_post__with_mentions(self):
@patch("federation.entities.activitypub.models.get_profile_or_entity", return_value=Person(finger="jaywink@dev3.jasonrobinson.me"))
def test_message_to_objects_simple_post__with_mentions(self, mock_get):
entities = message_to_objects(ACTIVITYPUB_POST_WITH_MENTIONS, "https://mastodon.social/users/jaywink")
assert len(entities) == 1
post = entities[0]
assert isinstance(post, models.Note)
assert isinstance(post, Post)
assert len(post._mentions) == 1
assert list(post._mentions)[0] == "https://dev3.jasonrobinson.me/u/jaywink/"
assert list(post._mentions)[0] == "jaywink@dev3.jasonrobinson.me"
def test_message_to_objects_simple_post__with_source__bbcode(self):
@patch("federation.entities.activitypub.models.get_profile_or_entity", return_value=Person(finger="jaywink@dev.jasonrobinson.me"))
def test_message_to_objects_simple_post__with_source__bbcode(self, mock_get):
entities = message_to_objects(ACTIVITYPUB_POST_WITH_SOURCE_BBCODE, "https://diaspodon.fr/users/jaywink")
assert len(entities) == 1
post = entities[0]
assert isinstance(post, models.Note)
assert isinstance(post, Post)
assert post.rendered_content == '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/">' \
assert post.rendered_content == '<p><span class="h-card"><a class="u-url mention" data-mention="jaywink@dev.jasonrobinson.me" href="https://dev.jasonrobinson.me/u/jaywink/">' \
'@<span>jaywink</span></a></span> boom</p>'
assert post.raw_content == '<p><span class="h-card"><a class="u-url mention" ' \
'href="https://dev.jasonrobinson.me/u/jaywink/">' \
'@<span>jaywink</span></a></span> boom</p>'
assert post.raw_content == ''
def test_message_to_objects_simple_post__with_source__markdown(self):
@patch("federation.entities.activitypub.models.get_profile_or_entity", return_value=Person(finger="jaywink@dev.jasonrobinson.me"))
def test_message_to_objects_simple_post__with_source__markdown(self, mock_get):
entities = message_to_objects(ACTIVITYPUB_POST_WITH_SOURCE_MARKDOWN, "https://diaspodon.fr/users/jaywink")
assert len(entities) == 1
post = entities[0]
assert isinstance(post, models.Note)
assert isinstance(post, Post)
assert post.rendered_content == '<p><span class="h-card"><a href="https://dev.jasonrobinson.me/u/jaywink/" ' \
'class="u-url mention">@<span>jaywink</span></a></span> boom</p>'
assert post.raw_content == "@jaywink boom"
assert post.rendered_content == '<p><span class="h-card"><a class="u-url mention" ' \
'href="https://dev.jasonrobinson.me/u/jaywink/">@<span>jaywink</span></a></span> boom</p>'
assert post.raw_content == "@jaywink@dev.jasonrobinson.me boom"
assert post.id == "https://diaspodon.fr/users/jaywink/statuses/102356911717767237"
assert post.actor_id == "https://diaspodon.fr/users/jaywink"
assert post.public is True
@ -145,15 +147,17 @@ class TestActivitypubEntityMappersReceive:
assert photo.guid == ""
assert photo.handle == ""
def test_message_to_objects_comment(self):
@patch("federation.entities.activitypub.models.get_profile_or_entity", return_value=Person(finger="jaywink@dev.jasonrobinson.me"))
def test_message_to_objects_comment(self, mock_get):
entities = message_to_objects(ACTIVITYPUB_COMMENT, "https://diaspodon.fr/users/jaywink")
assert len(entities) == 1
comment = entities[0]
assert isinstance(comment, models.Note)
assert isinstance(comment, Comment)
assert comment.raw_content == '<p><span class="h-card"><a class="u-url mention" ' \
assert comment.rendered_content == '<p><span class="h-card"><a class="u-url mention" data-mention="jaywink@dev.jasonrobinson.me" ' \
'href="https://dev.jasonrobinson.me/u/jaywink/">' \
'@<span>jaywink</span></a></span> boom</p>'
assert comment.raw_content == ''
assert comment.id == "https://diaspodon.fr/users/jaywink/statuses/102356911717767237"
assert comment.actor_id == "https://diaspodon.fr/users/jaywink"
assert comment.target_id == "https://dev.jasonrobinson.me/content/653bad70-41b3-42c9-89cb-c4ee587e68e4/"

Wyświetl plik

@ -30,6 +30,7 @@ def activitypubcomment():
with freeze_time("2019-04-27"):
obj = models.Comment(
raw_content="raw_content",
rendered_content="<p>raw_content</p>",
public=True,
provider_display_name="Socialhome",
id=f"http://127.0.0.1:8000/post/123456/",

Wyświetl plik

@ -35,7 +35,7 @@ ACTIVITYPUB_COMMENT = {
'contentMap': {'en': '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/">@<span>jaywink</span></a></span> boom</p>'},
'attachment': [],
'tag': [{'type': 'Mention',
'href': 'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
'href': 'https://dev.jasonrobinson.me/u/jaywink/',
'name': '@jaywink@dev.jasonrobinson.me'}],
'replies': {'id': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237/replies',
'type': 'Collection',
@ -459,9 +459,9 @@ ACTIVITYPUB_POST_WITH_TAGS = {
'conversation': 'tag:diaspodon.fr,2019-06-28:objectId=2347687:objectType=Conversation',
'content': '<p>boom <a href="https://mastodon.social/tags/test" class="mention hashtag" rel="tag">#<span>test</span></a></p>',
'attachment': [],
'tag': [{'type': 'Mention',
'href': 'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
'name': '@jaywink@dev.jasonrobinson.me'}],
'tag': [{'type': 'Hashtag',
'href': 'https://mastodon.social/tags/test',
'name': '#test'}],
'replies': {'id': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237/replies',
'type': 'Collection',
'first': {'type': 'CollectionPage',
@ -552,13 +552,13 @@ ACTIVITYPUB_POST_WITH_SOURCE_MARKDOWN = {
'conversation': 'tag:diaspodon.fr,2019-06-28:objectId=2347687:objectType=Conversation',
'content': '<p><span class="h-card"><a href="https://dev.jasonrobinson.me/u/jaywink/" class="u-url mention">@<span>jaywink</span></a></span> boom</p>',
'source': {
'content': "@jaywink boom",
'content': "@{jaywink@dev.jasonrobinson.me} boom",
'mediaType': "text/markdown",
},
'contentMap': {'en': '<p><span class="h-card"><a href="https://dev.jasonrobinson.me/u/jaywink/" class="u-url mention">@<span>jaywink</span></a></span> boom</p>'},
'attachment': [],
'tag': [{'type': 'Mention',
'href': 'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
'href': 'https://dev.jasonrobinson.me/u/jaywink/',
'name': '@jaywink@dev.jasonrobinson.me'}],
'replies': {'id': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237/replies',
'type': 'Collection',
@ -612,7 +612,7 @@ ACTIVITYPUB_POST_WITH_SOURCE_BBCODE = {
'contentMap': {'en': '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/">@<span>jaywink</span></a></span> boom</p>'},
'attachment': [],
'tag': [{'type': 'Mention',
'href': 'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
'href': 'https://dev.jasonrobinson.me/u/jaywink/',
'name': '@jaywink@dev.jasonrobinson.me'}],
'replies': {'id': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237/replies',
'type': 'Collection',

Wyświetl plik

@ -18,78 +18,49 @@ class TestFindTags:
def test_all_tags_are_parsed_from_text(self):
source = "#starting and #MixED with some #line\nendings also tags can\n#start on new line"
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"starting", "mixed", "line", "start"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "#starting/starting and #MixED/mixed with some #line/line\nendings also tags can\n" \
"#start/start on new line"
def test_code_block_tags_ignored(self):
source = "foo\n```\n#code\n```\n#notcode\n\n #alsocode\n"
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"notcode"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "foo\n```\n#code\n```\n#notcode/notcode\n\n #alsocode\n"
def test_endings_are_filtered_out(self):
source = "#parenthesis) #exp! #list] *#doh* _#bah_ #gah% #foo/#bar"
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"parenthesis", "exp", "list", "doh", "bah", "gah", "foo", "bar"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "#parenthesis/parenthesis) #exp/exp! #list/list] *#doh/doh* _#bah/bah_ #gah/gah% " \
"#foo/foo/#bar/bar"
def test_finds_tags(self):
source = "#post **Foobar** #tag #OtherTag #third\n#fourth"
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"third", "fourth", "post", "othertag", "tag"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "#post/post **Foobar** #tag/tag #OtherTag/othertag #third/third\n#fourth/fourth"
def test_ok_with_html_tags_in_text(self):
source = "<p>#starting and <span>#MixED</span> however not <#>this</#> or <#/>that"
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"starting", "mixed"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "<p>#starting/starting and <span>#MixED/mixed</span> however not <#>this</#> or <#/>that"
def test_postfixed_tags(self):
source = "#foo) #bar] #hoo, #hee."
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"foo", "bar", "hoo", "hee"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "#foo/foo) #bar/bar] #hoo/hoo, #hee/hee."
def test_prefixed_tags(self):
source = "(#foo [#bar"
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"foo", "bar"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == "(#foo/foo [#bar/bar"
def test_invalid_text_returns_no_tags(self):
source = "#a!a #a#a #a$a #a%a #a^a #a&a #a*a #a+a #a.a #a,a #a@a #a£a #a(a #a)a #a=a " \
"#a?a #a`a #a'a #a\\a #a{a #a[a #a]a #a}a #a~a #a;a #a:a #a\"a #aa #a”a #\xa0cd"
tags, text = find_tags(source)
assert tags == set()
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == source
tags = find_tags(source)
assert tags == {'a'}
def test_start_of_paragraph_in_html_content(self):
source = '<p>First line</p><p>#foobar #barfoo</p>'
tags, text = find_tags(source)
tags = find_tags(source)
assert tags == {"foobar", "barfoo"}
assert text == source
tags, text = find_tags(source, replacer=self._replacer)
assert text == '<p>First line</p><p>#foobar/foobar #barfoo/barfoo</p>'
class TestProcessTextLinks:

Wyświetl plik

@ -27,7 +27,7 @@ def encode_if_text(text):
return text
def find_tags(text: str) -> List[str]:
def find_tags(text: str) -> Set[str]:
"""Find tags in text.
Ignore tags inside code blocks.
@ -37,7 +37,7 @@ def find_tags(text: str) -> List[str]:
"""
tags = find_elements(BeautifulSoup(commonmark(text, ignore_html_blocks=True), 'html.parser'),
TAG_PATTERN)
return sorted([tag.text.lstrip('#').lower() for tag in tags])
return set([tag.text.lstrip('#').lower() for tag in tags])
def find_elements(soup: BeautifulSoup, pattern: re.Pattern) -> List[NavigableString]:
@ -54,7 +54,7 @@ def find_elements(soup: BeautifulSoup, pattern: re.Pattern) -> List[NavigableStr
if candidate.parent.name == 'code': continue
ns = [NavigableString(r) for r in re.split(pattern, candidate.text)]
candidate.replace_with(*ns)
return list(soup.find_all(string=re.compile(r'^'+pattern.pattern)))
return list(soup.find_all(string=re.compile(r'\A'+pattern.pattern+r'\Z')))
def get_path_from_url(url: str) -> str: