Option to spoof image metadata

merge-requests/30/head
Bob Mottram 2021-05-09 13:17:55 +01:00
parent 572d338943
commit d8b882c10f
5 changed files with 93 additions and 16 deletions

View File

@ -239,7 +239,7 @@ from content import addHtmlTags
from content import extractMediaInFormPOST from content import extractMediaInFormPOST
from content import saveMediaInFormPOST from content import saveMediaInFormPOST
from content import extractTextFieldsInPOST from content import extractTextFieldsInPOST
from media import removeMetaData from media import processMetaData
from cache import checkForChangedActor from cache import checkForChangedActor
from cache import storePersonInCache from cache import storePersonInCache
from cache import getPersonFromCache from cache import getPersonFromCache
@ -4075,7 +4075,8 @@ class PubServer(BaseHTTPRequestHandler):
os.remove(postImageFilename + '.etag') os.remove(postImageFilename + '.etag')
except BaseException: except BaseException:
pass pass
removeMetaData(filename, postImageFilename) processMetaData(baseDir, nickname, domain,
filename, postImageFilename)
if os.path.isfile(postImageFilename): if os.path.isfile(postImageFilename):
print('profile update POST ' + mType + print('profile update POST ' + mType +
' image or font saved to ' + postImageFilename) ' image or font saved to ' + postImageFilename)
@ -13053,7 +13054,9 @@ class PubServer(BaseHTTPRequestHandler):
filename.endswith('.gif'): filename.endswith('.gif'):
postImageFilename = filename.replace('.temp', '') postImageFilename = filename.replace('.temp', '')
print('Removing metadata from ' + postImageFilename) print('Removing metadata from ' + postImageFilename)
removeMetaData(filename, postImageFilename) processMetaData(self.server.baseDir,
nickname, self.server.domain,
filename, postImageFilename)
if os.path.isfile(postImageFilename): if os.path.isfile(postImageFilename):
print('POST media saved to ' + postImageFilename) print('POST media saved to ' + postImageFilename)
else: else:
@ -13307,6 +13310,7 @@ class PubServer(BaseHTTPRequestHandler):
postJsonObject['object'] = \ postJsonObject['object'] = \
attachMedia(self.server.baseDir, attachMedia(self.server.baseDir,
self.server.httpPrefix, self.server.httpPrefix,
nickname,
self.server.domain, self.server.domain,
self.server.port, self.server.port,
postJsonObject['object'], postJsonObject['object'],

View File

@ -8,6 +8,7 @@ __status__ = "Production"
import os import os
import datetime import datetime
from random import randint
from hashlib import sha1 from hashlib import sha1
from auth import createPassword from auth import createPassword
from utils import getFullDomain from utils import getFullDomain
@ -37,7 +38,7 @@ def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None:
replacementDomain) replacementDomain)
def removeMetaData(imageFilename: str, outputFilename: str) -> None: def _removeMetaData(imageFilename: str, outputFilename: str) -> None:
"""Attempts to do this with pure python didn't work well, """Attempts to do this with pure python didn't work well,
so better to use a dedicated tool if one is installed so better to use a dedicated tool if one is installed
""" """
@ -53,6 +54,68 @@ def removeMetaData(imageFilename: str, outputFilename: str) -> None:
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
def _spoofMetaData(imageFilename: str, outputFilename: str,
spoofFilename: str) -> None:
"""Use reference images to spoof the metadata
"""
copyfile(imageFilename, outputFilename)
if not os.path.isfile(outputFilename):
print('ERROR: unable to spoof metadata from ' + imageFilename)
return
if not os.path.isfile(spoofFilename):
print('ERROR: No spoof reference image ' + spoofFilename)
return
if os.path.isfile('/usr/bin/exiftool'):
print('Spoofing metadata in ' + outputFilename + ' using exiftool')
os.system('exiftool -TagsFromFile ' +
spoofFilename + ' ' + outputFilename) # nosec
else:
print('ERROR: exiftool is not installed')
return
def processMetaData(baseDir: str, nickname: str, domain: str,
imageFilename: str, outputFilename: str) -> None:
"""Handles image metadata. This tries to spoof the metadata
if possible, but otherwise just removes it
"""
accountDir = baseDir + '/accounts/' + nickname + '@' + domain
spoofImagesDir = accountDir + '/ref/images'
if os.path.isdir(spoofImagesDir):
imageTypes = getImageExtensions()
# get the format of the target image
ext = None
for mType in imageTypes:
if outputFilename.endswith('.' + mType):
ext = mType
break
if ext:
spoofList = []
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for f in files:
filename = os.path.join(spoofImagesDir, f)
# what is the format of this file?
currExt = None
for mType in imageTypes:
if filename.endswith('.' + mType):
currExt = mType
break
# if this the same format as the target?
if currExt:
if currExt == ext:
spoofList.append(filename)
break
if spoofList:
# choose a reference at random
index = randint(0, len(spoofList))
spoofFilename = spoofList[index]
_spoofMetaData(imageFilename, outputFilename,
spoofFilename)
return
# if we can't spoof then just remove metadata
_removeMetaData(imageFilename, outputFilename)
def _isMedia(imageFilename: str) -> bool: def _isMedia(imageFilename: str) -> bool:
"""Is the given file a media file? """Is the given file a media file?
""" """
@ -131,7 +194,8 @@ def _updateEtag(mediaFilename: str) -> None:
pass pass
def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int, def attachMedia(baseDir: str, httpPrefix: str,
nickname: str, domain: str, port: int,
postJson: {}, imageFilename: str, postJson: {}, imageFilename: str,
mediaType: str, description: str) -> {}: mediaType: str, description: str) -> {}:
"""Attaches media to a json object post """Attaches media to a json object post
@ -179,7 +243,8 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
if baseDir: if baseDir:
if mediaType.startswith('image/'): if mediaType.startswith('image/'):
removeMetaData(imageFilename, mediaFilename) processMetaData(baseDir, nickname, domain,
imageFilename, mediaFilename)
else: else:
copyfile(imageFilename, mediaFilename) copyfile(imageFilename, mediaFilename)
_updateEtag(mediaFilename) _updateEtag(mediaFilename)

View File

@ -34,7 +34,7 @@ from posts import createModeration
from auth import storeBasicCredentials from auth import storeBasicCredentials
from auth import removePassword from auth import removePassword
from roles import setRole from roles import setRole
from media import removeMetaData from media import processMetaData
from utils import getStatusNumber from utils import getStatusNumber
from utils import getFullDomain from utils import getFullDomain
from utils import validNickname from utils import validNickname
@ -135,7 +135,8 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
'/usr/bin/convert ' + imageFilename + ' -size ' + \ '/usr/bin/convert ' + imageFilename + ' -size ' + \
resolution + ' -quality 50 ' + profileFilename resolution + ' -quality 50 ' + profileFilename
subprocess.call(cmd, shell=True) subprocess.call(cmd, shell=True)
removeMetaData(profileFilename, profileFilename) processMetaData(baseDir, nickname, domain,
profileFilename, profileFilename)
return True return True
return False return False

View File

@ -1059,6 +1059,10 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
idStr = \ idStr = \
httpPrefix + '://' + domain + '/users/' + nickname + \ httpPrefix + '://' + domain + '/users/' + nickname + \
'/statuses/' + statusNumber + '/replies' '/statuses/' + statusNumber + '/replies'
newPostUrl = \
httpPrefix + '://' + domain + '/@' + nickname + '/'+statusNumber
newPostAttributedTo = \
httpPrefix + '://' + domain + '/users/' + nickname
newPost = { newPost = {
'@context': postContext, '@context': postContext,
'id': newPostId + '/activity', 'id': newPostId + '/activity',
@ -1073,8 +1077,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'summary': summary, 'summary': summary,
'inReplyTo': inReplyTo, 'inReplyTo': inReplyTo,
'published': published, 'published': published,
'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber, 'url': newPostUrl,
'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname, 'attributedTo': newPostAttributedTo,
'to': toRecipients, 'to': toRecipients,
'cc': toCC, 'cc': toCC,
'sensitive': sensitive, 'sensitive': sensitive,
@ -1101,7 +1105,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
} }
if attachImageFilename: if attachImageFilename:
newPost['object'] = \ newPost['object'] = \
attachMedia(baseDir, httpPrefix, domain, port, attachMedia(baseDir, httpPrefix, nickname, domain, port,
newPost['object'], attachImageFilename, newPost['object'], attachImageFilename,
mediaType, imageDescription) mediaType, imageDescription)
_appendEventFields(newPost['object'], eventUUID, eventStatus, _appendEventFields(newPost['object'], eventUUID, eventStatus,
@ -1115,6 +1119,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
idStr = \ idStr = \
httpPrefix + '://' + domain + '/users/' + nickname + \ httpPrefix + '://' + domain + '/users/' + nickname + \
'/statuses/' + statusNumber + '/replies' '/statuses/' + statusNumber + '/replies'
newPostUrl = \
httpPrefix + '://' + domain + '/@' + nickname+'/' + statusNumber
newPost = { newPost = {
"@context": postContext, "@context": postContext,
'id': newPostId, 'id': newPostId,
@ -1122,8 +1128,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'summary': summary, 'summary': summary,
'inReplyTo': inReplyTo, 'inReplyTo': inReplyTo,
'published': published, 'published': published,
'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber, 'url': newPostUrl,
'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname, 'attributedTo': httpPrefix + '://' + domain + '/users/' + nickname,
'to': toRecipients, 'to': toRecipients,
'cc': toCC, 'cc': toCC,
'sensitive': sensitive, 'sensitive': sensitive,
@ -1149,7 +1155,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
} }
if attachImageFilename: if attachImageFilename:
newPost = \ newPost = \
attachMedia(baseDir, httpPrefix, domain, port, attachMedia(baseDir, httpPrefix, nickname, domain, port,
newPost, attachImageFilename, newPost, attachImageFilename,
mediaType, imageDescription) mediaType, imageDescription)
_appendEventFields(newPost, eventUUID, eventStatus, _appendEventFields(newPost, eventUUID, eventStatus,

View File

@ -18,7 +18,7 @@ from utils import validNickname
from utils import loadJson from utils import loadJson
from utils import saveJson from utils import saveJson
from utils import getImageExtensions from utils import getImageExtensions
from media import removeMetaData from media import processMetaData
def getValidSharedItemID(displayName: str) -> str: def getValidSharedItemID(displayName: str) -> str:
@ -129,7 +129,8 @@ def addShare(baseDir: str,
formats = getImageExtensions() formats = getImageExtensions()
for ext in formats: for ext in formats:
if imageFilename.endswith('.' + ext): if imageFilename.endswith('.' + ext):
removeMetaData(imageFilename, itemIDfile + '.' + ext) processMetaData(baseDir, nickname, domain,
imageFilename, itemIDfile + '.' + ext)
if moveImage: if moveImage:
os.remove(imageFilename) os.remove(imageFilename)
imageUrl = \ imageUrl = \