From ea23f01df26f32201e96ab1554f922209a9b12c8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 15 Feb 2021 10:06:49 +0000 Subject: [PATCH 01/25] Actor validation for arriving posts --- outbox.py | 18 ++++++++++++++++++ utils.py | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/outbox.py b/outbox.py index 2c3fda855..11d595c6e 100644 --- a/outbox.py +++ b/outbox.py @@ -14,6 +14,7 @@ from posts import outboxMessageCreateWrap from posts import savePostToBox from posts import sendToFollowersThread from posts import sendToNamedAddresses +from utils import getLocalNetworkAddresses from utils import getFullDomain from utils import removeIdEnding from utils import getDomainFromActor @@ -114,6 +115,23 @@ def postMessageToOutbox(messageJson: {}, postToNickname: str, 'Create does not have the "to" parameter ' + str(messageJson)) return False + + # actor should be a string + if not isinstance(messageJson['actor'], str): + return False + + # actor should look like a url + if '://' not in messageJson['actor'] or \ + '.' not in messageJson['actor']: + return False + + # sent by an actor on a local network address? + if not allowLocalNetworkAccess: + localNetworkPatternList = getLocalNetworkAddresses() + for localNetworkPattern in localNetworkPatternList: + if localNetworkPattern in messageJson['actor']: + return False + testDomain, testPort = getDomainFromActor(messageJson['actor']) testDomain = getFullDomain(testDomain, testPort) if isBlockedDomain(baseDir, testDomain): diff --git a/utils.py b/utils.py index f1247e898..8f2348062 100644 --- a/utils.py +++ b/utils.py @@ -605,6 +605,12 @@ def urlPermitted(url: str, federationList: []): return False +def getLocalNetworkAddresses() -> []: + """Returns patterns for local network address detection + """ + return ('localhost', '127.0.', '192.168', '10.0.') + + def dangerousMarkup(content: str, allowLocalNetworkAccess: bool) -> bool: """Returns true if the given content contains dangerous html markup """ @@ -615,7 +621,7 @@ def dangerousMarkup(content: str, allowLocalNetworkAccess: bool) -> bool: contentSections = content.split('<') invalidPartials = () if not allowLocalNetworkAccess: - invalidPartials = ('localhost', '127.0.', '192.168', '10.0.') + invalidPartials = getLocalNetworkAddresses() invalidStrings = ('script', 'canvas', 'style', 'abbr', 'frame', 'iframe', 'html', 'body', 'hr', 'allow-popups', 'allow-scripts') From 5285c11b70331c4cdb01fb940703b76aeafb7708 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 15 Feb 2021 10:24:31 +0000 Subject: [PATCH 02/25] More validation on the actor of incoming posts --- daemon.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/daemon.py b/daemon.py index 577afe37f..e87ce93e3 100644 --- a/daemon.py +++ b/daemon.py @@ -185,6 +185,7 @@ from shares import addShare from shares import removeShare from shares import expireShares from categories import setHashtagCategory +from utils import getLocalNetworkAddresses from utils import decodedHost from utils import isPublicPost from utils import getLockedAccount @@ -1154,6 +1155,32 @@ class PubServer(BaseHTTPRequestHandler): # check for blocked domains so that they can be rejected early messageDomain = None if messageJson.get('actor'): + # actor should be a string + if not isinstance(messageJson['actor'], str): + self._400() + self.server.POSTbusy = False + return 3 + + # actor should look like a url + if '://' not in messageJson['actor'] or \ + '.' not in messageJson['actor']: + print('POST actor does not look like a url ' + + messageJson['actor']) + self._400() + self.server.POSTbusy = False + return 3 + + # sent by an actor on a local network address? + if not self.server.allowLocalNetworkAccess: + localNetworkPatternList = getLocalNetworkAddresses() + for localNetworkPattern in localNetworkPatternList: + if localNetworkPattern in messageJson['actor']: + print('POST actor contains local network address ' + + messageJson['actor']) + self._400() + self.server.POSTbusy = False + return 3 + messageDomain, messagePort = \ getDomainFromActor(messageJson['actor']) if isBlockedDomain(self.server.baseDir, messageDomain): From 30474d19c215ec45503a93cb48d34fe6eeb43c5b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 15 Feb 2021 10:30:53 +0000 Subject: [PATCH 03/25] Less indentation --- daemon.py | 70 +++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/daemon.py b/daemon.py index e87ce93e3..320f3fa78 100644 --- a/daemon.py +++ b/daemon.py @@ -1154,46 +1154,46 @@ class PubServer(BaseHTTPRequestHandler): # check for blocked domains so that they can be rejected early messageDomain = None - if messageJson.get('actor'): - # actor should be a string - if not isinstance(messageJson['actor'], str): - self._400() - self.server.POSTbusy = False - return 3 - - # actor should look like a url - if '://' not in messageJson['actor'] or \ - '.' not in messageJson['actor']: - print('POST actor does not look like a url ' + - messageJson['actor']) - self._400() - self.server.POSTbusy = False - return 3 - - # sent by an actor on a local network address? - if not self.server.allowLocalNetworkAccess: - localNetworkPatternList = getLocalNetworkAddresses() - for localNetworkPattern in localNetworkPatternList: - if localNetworkPattern in messageJson['actor']: - print('POST actor contains local network address ' + - messageJson['actor']) - self._400() - self.server.POSTbusy = False - return 3 - - messageDomain, messagePort = \ - getDomainFromActor(messageJson['actor']) - if isBlockedDomain(self.server.baseDir, messageDomain): - print('POST from blocked domain ' + messageDomain) - self._400() - self.server.POSTbusy = False - return 3 - else: + if not messageJson.get('actor'): print('Message arriving at inbox queue has no actor') self._400() self.server.POSTbusy = False return 3 + # actor should be a string + if not isinstance(messageJson['actor'], str): + self._400() + self.server.POSTbusy = False + return 3 + + # actor should look like a url + if '://' not in messageJson['actor'] or \ + '.' not in messageJson['actor']: + print('POST actor does not look like a url ' + + messageJson['actor']) + self._400() + self.server.POSTbusy = False + return 3 + + # sent by an actor on a local network address? + if not self.server.allowLocalNetworkAccess: + localNetworkPatternList = getLocalNetworkAddresses() + for localNetworkPattern in localNetworkPatternList: + if localNetworkPattern in messageJson['actor']: + print('POST actor contains local network address ' + + messageJson['actor']) + self._400() + self.server.POSTbusy = False + return 3 + + messageDomain, messagePort = \ + getDomainFromActor(messageJson['actor']) + if isBlockedDomain(self.server.baseDir, messageDomain): + print('POST from blocked domain ' + messageDomain) + self._400() + self.server.POSTbusy = False + return 3 + # if the inbox queue is full then return a busy code if len(self.server.inboxQueue) >= self.server.maxQueueLength: if messageDomain: From 3303840318e45b2023f67893043eab7149aa6563 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 15 Feb 2021 11:53:19 +0000 Subject: [PATCH 04/25] Column icon size --- epicyon-profile.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/epicyon-profile.css b/epicyon-profile.css index 473fbdb37..f5af28671 100644 --- a/epicyon-profile.css +++ b/epicyon-profile.css @@ -106,11 +106,11 @@ --column-left-icons-margin: 0; --column-right-border-width: 0; --column-left-border-color: black; - --column-left-icon-size: 20%; + --column-left-icon-size: 2.1vw; --column-left-icon-size-mobile: 10%; --column-left-image-width-mobile: 40vw; --column-right-image-width-mobile: 100vw; - --column-right-icon-size: 20%; + --column-right-icon-size: 2.1vw; --column-right-icon-size-mobile: 10%; --newswire-date-color: white; --newswire-voted-background-color: black; From 0f95961eaedbd1bb7b8accfe44bde62c5a4efc89 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 15 Feb 2021 12:09:21 +0000 Subject: [PATCH 05/25] Line endings to more easily read html source --- theme.py | 2 +- webapp_utils.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/theme.py b/theme.py index 9a27ec6e6..676ff42c1 100644 --- a/theme.py +++ b/theme.py @@ -438,7 +438,7 @@ def getTextModeBanner(baseDir: str) -> str: with open(textModeBannerFilename, 'r') as fp: bannerStr = fp.read() if bannerStr: - return bannerStr.replace('\n', '
') + return bannerStr.replace('\n', '
\n') return None diff --git a/webapp_utils.py b/webapp_utils.py index 014b42a19..2bc847e4f 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -892,26 +892,26 @@ def htmlKeyboardNavigation(banner: str, links: {}, followApprovals=False) -> str: """Given a set of links return the html for keyboard navigation """ - htmlStr = '
    ' + htmlStr = '
      \n' if banner: - htmlStr += '
      ' + banner + '

      ' + htmlStr += '
      ' + banner + '\n

      \n' if subHeading: htmlStr += '
      ' + subHeading + '
      \n' # show new follower approvals if usersPath and translate and followApprovals: htmlStr += '

      ' + '

      \n' # show the list of links for title, url in links.items(): htmlStr += '
    • ' - htmlStr += '
    ' + str(title) + '\n' + htmlStr += '
\n' return htmlStr From e094e8d6028711e0741986632dcc647ca86975db Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 15 Feb 2021 12:13:31 +0000 Subject: [PATCH 06/25] Extra newline --- webapp_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_utils.py b/webapp_utils.py index 2bc847e4f..64b1cae89 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -895,7 +895,7 @@ def htmlKeyboardNavigation(banner: str, links: {}, htmlStr = '
    \n' if banner: - htmlStr += '
    ' + banner + '\n

    \n' + htmlStr += '
    \n' + banner + '\n

    \n' if subHeading: htmlStr += '