Maximum size for rss/atom feeds

Prevents a hacked news source from implementing a denial of service
main
Bob Mottram 2020-10-16 12:40:01 +01:00
parent 257a2d1e88
commit c3dbec6181
5 changed files with 34 additions and 10 deletions

View File

@ -11951,7 +11951,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
tokensLookup[token] = nickname tokensLookup[token] = nickname
def runDaemon(maxNewswirePostsPerSource: int, def runDaemon(maxNewswireFeedSizeKb: int,
maxNewswirePostsPerSource: int,
showPublishedDateOnly: bool, showPublishedDateOnly: bool,
votingTimeMins: int, votingTimeMins: int,
positiveVoting: bool, positiveVoting: bool,
@ -12072,6 +12073,9 @@ def runDaemon(maxNewswirePostsPerSource: int,
# number of votes needed to remove a newswire item from the news timeline # number of votes needed to remove a newswire item from the news timeline
# or if positive voting is anabled to add the item to the news timeline # or if positive voting is anabled to add the item to the news timeline
httpd.newswireVotesThreshold = newswireVotesThreshold httpd.newswireVotesThreshold = newswireVotesThreshold
# maximum overall size of an rss/atom feed read by the newswire daemon
# If the feed is too large then this is probably a DoS attempt
httpd.maxNewswireFeedSizeKb = maxNewswireFeedSizeKb
# For each newswire source (account or rss feed) # For each newswire source (account or rss feed)
# this is the maximum number of posts to show for each. # this is the maximum number of posts to show for each.

View File

@ -116,6 +116,10 @@ parser.add_argument('--postsPerSource',
dest='maxNewswirePostsPerSource', type=int, dest='maxNewswirePostsPerSource', type=int,
default=5, default=5,
help='Maximum newswire posts per feed or account') help='Maximum newswire posts per feed or account')
parser.add_argument('--maxFeedSize',
dest='maxNewswireFeedSizeKb', type=int,
default=2048,
help='Maximum newswire rss/atom feed size in K')
parser.add_argument('--postcache', dest='maxRecentPosts', type=int, parser.add_argument('--postcache', dest='maxRecentPosts', type=int,
default=512, default=512,
help='The maximum number of recent posts to store in RAM') help='The maximum number of recent posts to store in RAM')
@ -1936,6 +1940,13 @@ if maxNewswirePostsPerSource:
if maxNewswirePostsPerSource.isdigit(): if maxNewswirePostsPerSource.isdigit():
args.maxNewswirePostsPerSource = maxNewswirePostsPerSource args.maxNewswirePostsPerSource = maxNewswirePostsPerSource
# set the maximum size of a newswire rss/atom feed in Kilobytes
maxNewswireFeedSizeKb = \
getConfigParam(baseDir, 'maxNewswireFeedSizeKb')
if maxNewswireFeedSizeKb:
if maxNewswireFeedSizeKb.isdigit():
args.maxNewswireFeedSizeKb = maxNewswireFeedSizeKb
YTDomain = getConfigParam(baseDir, 'youtubedomain') YTDomain = getConfigParam(baseDir, 'youtubedomain')
if YTDomain: if YTDomain:
if '://' in YTDomain: if '://' in YTDomain:
@ -1949,7 +1960,8 @@ if setTheme(baseDir, themeName, domain):
print('Theme set to ' + themeName) print('Theme set to ' + themeName)
if __name__ == "__main__": if __name__ == "__main__":
runDaemon(args.maxNewswirePostsPerSource, runDaemon(args.maxNewswireFeedSizeKb,
args.maxNewswirePostsPerSource,
args.dateonly, args.dateonly,
args.votingtime, args.votingtime,
args.positivevoting, args.positivevoting,

View File

@ -227,7 +227,8 @@ def runNewswireDaemon(baseDir: str, httpd,
try: try:
newNewswire = \ newNewswire = \
getDictFromNewswire(httpd.session, baseDir, getDictFromNewswire(httpd.session, baseDir,
httpd.maxNewswirePostsPerSource) httpd.maxNewswirePostsPerSource,
httpd.maxNewswireFeedSizeKb)
except Exception as e: except Exception as e:
print('WARN: unable to update newswire ' + str(e)) print('WARN: unable to update newswire ' + str(e))
time.sleep(120) time.sleep(120)

View File

@ -196,7 +196,8 @@ def xmlStrToDict(xmlStr: str, moderated: bool,
def getRSS(session, url: str, moderated: bool, def getRSS(session, url: str, moderated: bool,
maxPostsPerSource: int) -> {}: maxPostsPerSource: int,
maxFeedSizeKb: int) -> {}:
"""Returns an RSS url as a dict """Returns an RSS url as a dict
""" """
if not isinstance(url, str): if not isinstance(url, str):
@ -219,7 +220,11 @@ def getRSS(session, url: str, moderated: bool,
print('WARN: no session specified for getRSS') print('WARN: no session specified for getRSS')
try: try:
result = session.get(url, headers=sessionHeaders, params=sessionParams) result = session.get(url, headers=sessionHeaders, params=sessionParams)
return xmlStrToDict(result.text, moderated, maxPostsPerSource) if result:
if int(len(result) / 1024) < maxFeedSizeKb:
return xmlStrToDict(result.text, moderated, maxPostsPerSource)
else:
print('WARN: feed is too large: ' + url)
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
print('ERROR: getRSS failed\nurl: ' + str(url) + '\n' + print('ERROR: getRSS failed\nurl: ' + str(url) + '\n' +
'headers: ' + str(sessionHeaders) + '\n' + 'headers: ' + str(sessionHeaders) + '\n' +
@ -396,7 +401,8 @@ def addBlogsToNewswire(baseDir: str, newswire: {},
os.remove(newswireModerationFilename) os.remove(newswireModerationFilename)
def getDictFromNewswire(session, baseDir: str, maxPostsPerSource: int) -> {}: def getDictFromNewswire(session, baseDir: str,
maxPostsPerSource: int, maxFeedSizeKb: int) -> {}:
"""Gets rss feeds as a dictionary from newswire file """Gets rss feeds as a dictionary from newswire file
""" """
subscriptionsFilename = baseDir + '/accounts/newswire.txt' subscriptionsFilename = baseDir + '/accounts/newswire.txt'
@ -427,7 +433,8 @@ def getDictFromNewswire(session, baseDir: str, maxPostsPerSource: int) -> {}:
moderated = True moderated = True
url = url.replace('*', '').strip() url = url.replace('*', '').strip()
itemsList = getRSS(session, url, moderated, maxPostsPerSource) itemsList = getRSS(session, url, moderated,
maxPostsPerSource, maxFeedSizeKb)
for dateStr, item in itemsList.items(): for dateStr, item in itemsList.items():
result[dateStr] = item result[dateStr] = item

View File

@ -288,7 +288,7 @@ def createServerAlice(path: str, domain: str, port: int,
onionDomain = None onionDomain = None
i2pDomain = None i2pDomain = None
print('Server running: Alice') print('Server running: Alice')
runDaemon(5, False, 0, False, 1, False, False, False, runDaemon(1024, 5, False, 0, False, 1, False, False, False,
5, True, True, 'en', __version__, 5, True, True, 'en', __version__,
"instanceId", False, path, domain, "instanceId", False, path, domain,
onionDomain, i2pDomain, None, port, port, onionDomain, i2pDomain, None, port, port,
@ -351,7 +351,7 @@ def createServerBob(path: str, domain: str, port: int,
onionDomain = None onionDomain = None
i2pDomain = None i2pDomain = None
print('Server running: Bob') print('Server running: Bob')
runDaemon(5, False, 0, False, 1, False, False, False, runDaemon(1024, 5, False, 0, False, 1, False, False, False,
5, True, True, 'en', __version__, 5, True, True, 'en', __version__,
"instanceId", False, path, domain, "instanceId", False, path, domain,
onionDomain, i2pDomain, None, port, port, onionDomain, i2pDomain, None, port, port,
@ -388,7 +388,7 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
onionDomain = None onionDomain = None
i2pDomain = None i2pDomain = None
print('Server running: Eve') print('Server running: Eve')
runDaemon(5, False, 0, False, 1, False, False, False, runDaemon(1024, 5, False, 0, False, 1, False, False, False,
5, True, True, 'en', __version__, 5, True, True, 'en', __version__,
"instanceId", False, path, domain, "instanceId", False, path, domain,
onionDomain, i2pDomain, None, port, port, onionDomain, i2pDomain, None, port, port,