Hellthread mitigation

main2
Bob Mottram 2019-09-30 11:15:20 +01:00
parent 11f6655530
commit 865b4c3ce9
5 changed files with 46 additions and 17 deletions

View File

@ -3939,7 +3939,8 @@ def runDaemon(projectVersion, \
instanceId,clientToServer: bool, \ instanceId,clientToServer: bool, \
baseDir: str,domain: str, \ baseDir: str,domain: str, \
port=80,proxyPort=80,httpPrefix='https', \ port=80,proxyPort=80,httpPrefix='https', \
fedList=[],authenticatedFetch=False, \ fedList=[],maxMentions=10, \
authenticatedFetch=False, \
noreply=False,nolike=False,nopics=False, \ noreply=False,nolike=False,nopics=False, \
noannounce=False,cw=False,ocapAlways=False, \ noannounce=False,cw=False,ocapAlways=False, \
useTor=False,maxReplies=64, \ useTor=False,maxReplies=64, \
@ -4078,7 +4079,8 @@ def runDaemon(projectVersion, \
domain,port,useTor,httpd.federationList, \ domain,port,useTor,httpd.federationList, \
httpd.ocapAlways,maxReplies, \ httpd.ocapAlways,maxReplies, \
domainMaxPostsPerDay,accountMaxPostsPerDay, \ domainMaxPostsPerDay,accountMaxPostsPerDay, \
allowDeletion,debug,httpd.acceptedCaps),daemon=True) allowDeletion,debug,maxMentions, \
httpd.acceptedCaps),daemon=True)
if not unitTest: if not unitTest:
httpd.thrWatchdog= \ httpd.thrWatchdog= \
threadWithTrace(target=runInboxQueueWatchdog, \ threadWithTrace(target=runInboxQueueWatchdog, \

View File

@ -223,6 +223,8 @@ parser.add_argument("-c","--client", type=str2bool, nargs='?', \
help="Use as an ActivityPub client") help="Use as an ActivityPub client")
parser.add_argument('--maxreplies', dest='maxReplies', type=int,default=64, \ parser.add_argument('--maxreplies', dest='maxReplies', type=int,default=64, \
help='Maximum number of replies to a post') help='Maximum number of replies to a post')
parser.add_argument('--maxMentions','--hellthread', dest='maxMentions', type=int,default=10, \
help='Maximum number of mentions within a post')
parser.add_argument('--role', dest='role', type=str,default=None, \ parser.add_argument('--role', dest='role', type=str,default=None, \
help='Set a role for a person') help='Set a role for a person')
parser.add_argument('--organization','--project', dest='project', type=str,default=None, \ parser.add_argument('--organization','--project', dest='project', type=str,default=None, \
@ -1335,10 +1337,16 @@ if args.testdata:
followerOfPerson(baseDir,nickname,domain,'maxboardroom',domainFull,federationList,False) followerOfPerson(baseDir,nickname,domain,'maxboardroom',domainFull,federationList,False)
setConfigParam(baseDir,'admin',nickname) setConfigParam(baseDir,'admin',nickname)
# set a lower bound to the maximum mentions
# so that it can't be accidentally set to zero and disable replies
if args.maxMentions<4:
args.maxMentions=4
runDaemon(__version__, \ runDaemon(__version__, \
instanceId,args.client,baseDir, \ instanceId,args.client,baseDir, \
domain,port,proxyPort,httpPrefix, \ domain,port,proxyPort,httpPrefix, \
federationList,args.authenticatedFetch, \ federationList,args.maxMentions, \
args.authenticatedFetch, \
args.noreply,args.nolike,args.nopics, \ args.noreply,args.nolike,args.nopics, \
args.noannounce,args.cw,ocapAlways, \ args.noannounce,args.cw,ocapAlways, \
useTor,args.maxReplies, \ useTor,args.maxReplies, \

View File

@ -1096,8 +1096,20 @@ def populateReplies(baseDir :str,httpPrefix :str,domain :str, \
repliesFile.close() repliesFile.close()
return True return True
def validPostContent(messageJson: {}) -> bool: def estimateNumberOfMentions(content: str) -> int:
"""Returns a rough estimate of the number of mentions
"""
words=content.split(' ')
ctr=0
for word in words:
if word.startswith('@') or '>@' in word:
ctr+=1
return ctr
def validPostContent(messageJson: {},maxMentions: int) -> bool:
"""Is the content of a received post valid? """Is the content of a received post valid?
Check for bad html
Check for hellthreads
""" """
if not messageJson.get('object'): if not messageJson.get('object'):
return True return True
@ -1112,6 +1124,9 @@ def validPostContent(messageJson: {}) -> bool:
print('REJECT: '+messageJson['object']['id']) print('REJECT: '+messageJson['object']['id'])
print('REJECT: bad string in post - '+messageJson['object']['content']) print('REJECT: bad string in post - '+messageJson['object']['content'])
return False return False
if estimateNumberOfMentions(messageJson['object']['content'])>maxMentions:
print('REJECT: Too many mentions in post - '+messageJson['object']['content'])
return False
print('ACCEPT: post content is valid') print('ACCEPT: post content is valid')
return True return True
@ -1120,9 +1135,10 @@ def inboxAfterCapabilities(session,keyId: str,handle: str,messageJson: {}, \
postLog: [],cachedWebfingers: {},personCache: {}, \ postLog: [],cachedWebfingers: {},personCache: {}, \
queue: [],domain: str,port: int,useTor: bool, \ queue: [],domain: str,port: int,useTor: bool, \
federationList: [],ocapAlways: bool,debug: bool, \ federationList: [],ocapAlways: bool,debug: bool, \
acceptedCaps: [], acceptedCaps: [], \
queueFilename :str,destinationFilename :str, queueFilename :str,destinationFilename :str, \
maxReplies: int,allowDeletion: bool) -> bool: maxReplies: int,allowDeletion: bool, \
maxMentions: int) -> bool:
""" Anything which needs to be done after capabilities checks have passed """ Anything which needs to be done after capabilities checks have passed
""" """
actor=keyId actor=keyId
@ -1203,11 +1219,11 @@ def inboxAfterCapabilities(session,keyId: str,handle: str,messageJson: {}, \
return True return True
if messageJson.get('postNickname'): if messageJson.get('postNickname'):
if validPostContent(messageJson['post']): if validPostContent(messageJson['post'],maxMentions):
with open(destinationFilename, 'w+') as fp: with open(destinationFilename, 'w+') as fp:
commentjson.dump(messageJson['post'], fp, indent=4, sort_keys=False) commentjson.dump(messageJson['post'], fp, indent=4, sort_keys=False)
else: else:
if validPostContent(messageJson): if validPostContent(messageJson,maxMentions):
with open(destinationFilename, 'w+') as fp: with open(destinationFilename, 'w+') as fp:
commentjson.dump(messageJson, fp, indent=4, sort_keys=False) commentjson.dump(messageJson, fp, indent=4, sort_keys=False)
@ -1251,7 +1267,7 @@ def runInboxQueue(projectVersion: str, \
domain: str,port: int,useTor: bool,federationList: [], \ domain: str,port: int,useTor: bool,federationList: [], \
ocapAlways: bool,maxReplies: int, \ ocapAlways: bool,maxReplies: int, \
domainMaxPostsPerDay: int,accountMaxPostsPerDay: int, \ domainMaxPostsPerDay: int,accountMaxPostsPerDay: int, \
allowDeletion: bool,debug: bool, \ allowDeletion: bool,debug: bool,maxMentions: int, \
acceptedCaps=["inbox:write","objects:read"]) -> None: acceptedCaps=["inbox:write","objects:read"]) -> None:
"""Processes received items and moves them to """Processes received items and moves them to
the appropriate directories the appropriate directories
@ -1578,7 +1594,8 @@ def runInboxQueue(projectVersion: str, \
federationList,ocapAlways, \ federationList,ocapAlways, \
debug,acceptedCaps, \ debug,acceptedCaps, \
queueFilename,destination, \ queueFilename,destination, \
maxReplies,allowDeletion) maxReplies,allowDeletion, \
maxMentions)
else: else:
if debug: if debug:
print('DEBUG: object capabilities check has failed') print('DEBUG: object capabilities check has failed')
@ -1595,7 +1612,8 @@ def runInboxQueue(projectVersion: str, \
federationList,ocapAlways, \ federationList,ocapAlways, \
debug,acceptedCaps, \ debug,acceptedCaps, \
queueFilename,destination, \ queueFilename,destination, \
maxReplies,allowDeletion) maxReplies,allowDeletion, \
maxMentions)
if debug: if debug:
pprint(queueJson['post']) pprint(queueJson['post'])
print('No capability list within post') print('No capability list within post')

View File

@ -461,7 +461,6 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
nickname,domain,content, \ nickname,domain,content, \
mentionedRecipients, \ mentionedRecipients, \
hashtagsDict) hashtagsDict)
print('Content tagging stage 1: '+content)
statusNumber,published = getStatusNumber() statusNumber,published = getStatusNumber()
postTo='https://www.w3.org/ns/activitystreams#Public' postTo='https://www.w3.org/ns/activitystreams#Public'
@ -504,7 +503,6 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
updateHashtagsIndex(baseDir,tag,newPostId) updateHashtagsIndex(baseDir,tag,newPostId)
print('Content tags: '+str(tags)) print('Content tags: '+str(tags))
content=replaceEmojiFromTags(content,tags,'content') content=replaceEmojiFromTags(content,tags,'content')
print('Content tagging stage 2: '+content)
if not clientToServer: if not clientToServer:
actorUrl=httpPrefix+'://'+domain+'/users/'+nickname actorUrl=httpPrefix+'://'+domain+'/users/'+nickname

View File

@ -216,9 +216,10 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [], \
False, True, clientToServer,None,None,useBlurhash) False, True, clientToServer,None,None,useBlurhash)
global testServerAliceRunning global testServerAliceRunning
testServerAliceRunning = True testServerAliceRunning = True
maxMentions=10
print('Server running: Alice') print('Server running: Alice')
runDaemon(__version__,"instanceId",False,path,domain,port,port, \ runDaemon(__version__,"instanceId",False,path,domain,port,port, \
httpPrefix,federationList,False, \ httpPrefix,federationList,maxMentions,False, \
noreply,nolike,nopics,noannounce,cw,ocapAlways, \ noreply,nolike,nopics,noannounce,cw,ocapAlways, \
useTor,maxReplies, \ useTor,maxReplies, \
domainMaxPostsPerDay,accountMaxPostsPerDay, \ domainMaxPostsPerDay,accountMaxPostsPerDay, \
@ -269,9 +270,10 @@ def createServerBob(path: str,domain: str,port: int,federationList: [], \
False, True, clientToServer,None,None,useBlurhash) False, True, clientToServer,None,None,useBlurhash)
global testServerBobRunning global testServerBobRunning
testServerBobRunning = True testServerBobRunning = True
maxMentions=10
print('Server running: Bob') print('Server running: Bob')
runDaemon(__version__,"instanceId",False,path,domain,port,port, \ runDaemon(__version__,"instanceId",False,path,domain,port,port, \
httpPrefix,federationList,False, \ httpPrefix,federationList,maxMentions,False, \
noreply,nolike,nopics,noannounce,cw,ocapAlways, \ noreply,nolike,nopics,noannounce,cw,ocapAlways, \
useTor,maxReplies, \ useTor,maxReplies, \
domainMaxPostsPerDay,accountMaxPostsPerDay, \ domainMaxPostsPerDay,accountMaxPostsPerDay, \
@ -302,9 +304,10 @@ def createServerEve(path: str,domain: str,port: int,federationList: [], \
deleteAllPosts(path,nickname,domain,'outbox') deleteAllPosts(path,nickname,domain,'outbox')
global testServerEveRunning global testServerEveRunning
testServerEveRunning = True testServerEveRunning = True
maxMentions=10
print('Server running: Eve') print('Server running: Eve')
runDaemon(__version__,"instanceId",False,path,domain,port,port, \ runDaemon(__version__,"instanceId",False,path,domain,port,port, \
httpPrefix,federationList,False, \ httpPrefix,federationList,maxMentions,False, \
noreply,nolike,nopics,noannounce,cw,ocapAlways, \ noreply,nolike,nopics,noannounce,cw,ocapAlways, \
useTor,maxReplies,allowDeletion,True,True,False) useTor,maxReplies,allowDeletion,True,True,False)