forked from indymedia/epicyon
Option to allow access to the local network
This might be useful for mesh networks or private networksmain
parent
b889c8dfdd
commit
5364b71616
10
content.py
10
content.py
|
@ -151,7 +151,7 @@ def htmlReplaceQuoteMarks(content: str) -> str:
|
||||||
return newContent
|
return newContent
|
||||||
|
|
||||||
|
|
||||||
def dangerousMarkup(content: str) -> bool:
|
def dangerousMarkup(content: str, allowLocalNetworkAccess: bool) -> bool:
|
||||||
"""Returns true if the given content contains dangerous html markup
|
"""Returns true if the given content contains dangerous html markup
|
||||||
"""
|
"""
|
||||||
if '<' not in content:
|
if '<' not in content:
|
||||||
|
@ -159,7 +159,9 @@ def dangerousMarkup(content: str) -> bool:
|
||||||
if '>' not in content:
|
if '>' not in content:
|
||||||
return False
|
return False
|
||||||
contentSections = content.split('<')
|
contentSections = content.split('<')
|
||||||
invalidPartials = ('127.0.', '192.168', '10.0.')
|
invalidPartials = ()
|
||||||
|
if not allowLocalNetworkAccess:
|
||||||
|
invalidPartials = ('127.0.', '192.168', '10.0.')
|
||||||
invalidStrings = ('script', 'canvas', 'style', 'abbr',
|
invalidStrings = ('script', 'canvas', 'style', 'abbr',
|
||||||
'frame', 'iframe', 'html', 'body',
|
'frame', 'iframe', 'html', 'body',
|
||||||
'hr')
|
'hr')
|
||||||
|
@ -181,7 +183,7 @@ def dangerousMarkup(content: str) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def dangerousCSS(filename: str) -> bool:
|
def dangerousCSS(filename: str, allowLocalNetworkAccess: bool) -> bool:
|
||||||
"""Returns true is the css file contains code which
|
"""Returns true is the css file contains code which
|
||||||
can create security problems
|
can create security problems
|
||||||
"""
|
"""
|
||||||
|
@ -199,7 +201,7 @@ def dangerousCSS(filename: str) -> bool:
|
||||||
|
|
||||||
# an attacker can include html inside of the css
|
# an attacker can include html inside of the css
|
||||||
# file as a comment and this may then be run from the html
|
# file as a comment and this may then be run from the html
|
||||||
if dangerousMarkup(content):
|
if dangerousMarkup(content, allowLocalNetworkAccess):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
26
daemon.py
26
daemon.py
|
@ -2917,7 +2917,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if nickname == adminNickname:
|
if nickname == adminNickname:
|
||||||
if fields.get('editedAbout'):
|
if fields.get('editedAbout'):
|
||||||
aboutStr = fields['editedAbout']
|
aboutStr = fields['editedAbout']
|
||||||
if not dangerousMarkup(aboutStr):
|
allowLocalNetworkAccess = \
|
||||||
|
self.server.allowLocalNetworkAccess
|
||||||
|
if not dangerousMarkup(aboutStr,
|
||||||
|
allowLocalNetworkAccess):
|
||||||
aboutFile = open(aboutFilename, "w+")
|
aboutFile = open(aboutFilename, "w+")
|
||||||
if aboutFile:
|
if aboutFile:
|
||||||
aboutFile.write(aboutStr)
|
aboutFile.write(aboutStr)
|
||||||
|
@ -2928,7 +2931,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
if fields.get('editedTOS'):
|
if fields.get('editedTOS'):
|
||||||
TOSStr = fields['editedTOS']
|
TOSStr = fields['editedTOS']
|
||||||
if not dangerousMarkup(TOSStr):
|
allowLocalNetworkAccess = \
|
||||||
|
self.server.allowLocalNetworkAccess
|
||||||
|
if not dangerousMarkup(TOSStr,
|
||||||
|
allowLocalNetworkAccess):
|
||||||
TOSFile = open(TOSFilename, "w+")
|
TOSFile = open(TOSFilename, "w+")
|
||||||
if TOSFile:
|
if TOSFile:
|
||||||
TOSFile.write(TOSStr)
|
TOSFile.write(TOSStr)
|
||||||
|
@ -3655,7 +3661,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if fields.get('themeDropdown'):
|
if fields.get('themeDropdown'):
|
||||||
setTheme(baseDir,
|
setTheme(baseDir,
|
||||||
fields['themeDropdown'],
|
fields['themeDropdown'],
|
||||||
domain)
|
domain.
|
||||||
|
self.server.allowLocalNetworkAccess)
|
||||||
self.server.showPublishAsIcon = \
|
self.server.showPublishAsIcon = \
|
||||||
getConfigParam(self.server.baseDir,
|
getConfigParam(self.server.baseDir,
|
||||||
'showPublishAsIcon')
|
'showPublishAsIcon')
|
||||||
|
@ -4014,7 +4021,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'.etag')
|
'.etag')
|
||||||
currTheme = getTheme(baseDir)
|
currTheme = getTheme(baseDir)
|
||||||
if currTheme:
|
if currTheme:
|
||||||
setTheme(baseDir, currTheme, domain)
|
setTheme(baseDir, currTheme, domain,
|
||||||
|
self.server.allowLocalNetworkAccess)
|
||||||
self.server.showPublishAsIcon = \
|
self.server.showPublishAsIcon = \
|
||||||
getConfigParam(self.server.baseDir,
|
getConfigParam(self.server.baseDir,
|
||||||
'showPublishAsIcon')
|
'showPublishAsIcon')
|
||||||
|
@ -12374,7 +12382,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
|
||||||
tokensLookup[token] = nickname
|
tokensLookup[token] = nickname
|
||||||
|
|
||||||
|
|
||||||
def runDaemon(maxFeedItemSizeKb: int,
|
def runDaemon(allowLocalNetworkAccess: bool,
|
||||||
|
maxFeedItemSizeKb: int,
|
||||||
publishButtonAtTop: bool,
|
publishButtonAtTop: bool,
|
||||||
rssIconAtTop: bool,
|
rssIconAtTop: bool,
|
||||||
iconsAsButtons: bool,
|
iconsAsButtons: bool,
|
||||||
|
@ -12439,6 +12448,10 @@ def runDaemon(maxFeedItemSizeKb: int,
|
||||||
return False
|
return False
|
||||||
|
|
||||||
httpd.unitTest = unitTest
|
httpd.unitTest = unitTest
|
||||||
|
httpd.allowLocalNetworkAccess = allowLocalNetworkAccess
|
||||||
|
if unitTest:
|
||||||
|
# unit tests are run on the local network with LAN addresses
|
||||||
|
httpd.allowLocalNetworkAccess = True
|
||||||
httpd.YTReplacementDomain = YTReplacementDomain
|
httpd.YTReplacementDomain = YTReplacementDomain
|
||||||
|
|
||||||
# newswire storing rss feeds
|
# newswire storing rss feeds
|
||||||
|
@ -12702,7 +12715,8 @@ def runDaemon(maxFeedItemSizeKb: int,
|
||||||
httpd.YTReplacementDomain,
|
httpd.YTReplacementDomain,
|
||||||
httpd.showPublishedDateOnly,
|
httpd.showPublishedDateOnly,
|
||||||
httpd.allowNewsFollowers,
|
httpd.allowNewsFollowers,
|
||||||
httpd.maxFollowers), daemon=True)
|
httpd.maxFollowers,
|
||||||
|
httpd.allowLocalNetworkAccess), daemon=True)
|
||||||
|
|
||||||
print('Creating scheduled post thread')
|
print('Creating scheduled post thread')
|
||||||
httpd.thrPostSchedule = \
|
httpd.thrPostSchedule = \
|
||||||
|
|
12
epicyon.py
12
epicyon.py
|
@ -249,6 +249,13 @@ parser.add_argument("--publishButtonAtTop",
|
||||||
const=True, default=False,
|
const=True, default=False,
|
||||||
help="Whether to show the publish button at the top of " +
|
help="Whether to show the publish button at the top of " +
|
||||||
"the newswire column")
|
"the newswire column")
|
||||||
|
parser.add_argument("--allowLocalNetworkAccess",
|
||||||
|
dest='allowLocalNetworkAccess',
|
||||||
|
type=str2bool, nargs='?',
|
||||||
|
const=True, default=False,
|
||||||
|
help="Whether to allow access to local network " +
|
||||||
|
"addresses. This might be useful when deploying in " +
|
||||||
|
"a mesh network")
|
||||||
parser.add_argument("--noapproval", type=str2bool, nargs='?',
|
parser.add_argument("--noapproval", type=str2bool, nargs='?',
|
||||||
const=True, default=False,
|
const=True, default=False,
|
||||||
help="Allow followers without approval")
|
help="Allow followers without approval")
|
||||||
|
@ -2059,11 +2066,12 @@ if YTDomain:
|
||||||
if '.' in YTDomain:
|
if '.' in YTDomain:
|
||||||
args.YTReplacementDomain = YTDomain
|
args.YTReplacementDomain = YTDomain
|
||||||
|
|
||||||
if setTheme(baseDir, themeName, domain):
|
if setTheme(baseDir, themeName, domain, args.allowLocalNetworkAccess):
|
||||||
print('Theme set to ' + themeName)
|
print('Theme set to ' + themeName)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
runDaemon(args.maxFeedItemSizeKb,
|
runDaemon(args.allowLocalNetworkAccess,
|
||||||
|
args.maxFeedItemSizeKb,
|
||||||
args.publishButtonAtTop,
|
args.publishButtonAtTop,
|
||||||
args.rssIconAtTop,
|
args.rssIconAtTop,
|
||||||
args.iconsAsButtons,
|
args.iconsAsButtons,
|
||||||
|
|
17
inbox.py
17
inbox.py
|
@ -1565,7 +1565,8 @@ def estimateNumberOfEmoji(content: str) -> int:
|
||||||
|
|
||||||
|
|
||||||
def validPostContent(baseDir: str, nickname: str, domain: str,
|
def validPostContent(baseDir: str, nickname: str, domain: str,
|
||||||
messageJson: {}, maxMentions: int, maxEmoji: int) -> bool:
|
messageJson: {}, maxMentions: int, maxEmoji: int,
|
||||||
|
allowLocalNetworkAccess: bool) -> bool:
|
||||||
"""Is the content of a received post valid?
|
"""Is the content of a received post valid?
|
||||||
Check for bad html
|
Check for bad html
|
||||||
Check for hellthreads
|
Check for hellthreads
|
||||||
|
@ -1600,7 +1601,8 @@ def validPostContent(baseDir: str, nickname: str, domain: str,
|
||||||
messageJson['object']['content']):
|
messageJson['object']['content']):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if dangerousMarkup(messageJson['object']['content']):
|
if dangerousMarkup(messageJson['object']['content'],
|
||||||
|
allowLocalNetworkAccess):
|
||||||
if messageJson['object'].get('id'):
|
if messageJson['object'].get('id'):
|
||||||
print('REJECT ARBITRARY HTML: ' + messageJson['object']['id'])
|
print('REJECT ARBITRARY HTML: ' + messageJson['object']['id'])
|
||||||
print('REJECT ARBITRARY HTML: bad string in post - ' +
|
print('REJECT ARBITRARY HTML: bad string in post - ' +
|
||||||
|
@ -2030,7 +2032,8 @@ def inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
maxReplies: int, allowDeletion: bool,
|
maxReplies: int, allowDeletion: bool,
|
||||||
maxMentions: int, maxEmoji: int, translate: {},
|
maxMentions: int, maxEmoji: int, translate: {},
|
||||||
unitTest: bool, YTReplacementDomain: str,
|
unitTest: bool, YTReplacementDomain: str,
|
||||||
showPublishedDateOnly: bool) -> bool:
|
showPublishedDateOnly: bool,
|
||||||
|
allowLocalNetworkAccess: bool) -> bool:
|
||||||
""" Anything which needs to be done after initial checks have passed
|
""" Anything which needs to be done after initial checks have passed
|
||||||
"""
|
"""
|
||||||
actor = keyId
|
actor = keyId
|
||||||
|
@ -2155,7 +2158,8 @@ def inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
|
|
||||||
nickname = handle.split('@')[0]
|
nickname = handle.split('@')[0]
|
||||||
if validPostContent(baseDir, nickname, domain,
|
if validPostContent(baseDir, nickname, domain,
|
||||||
postJsonObject, maxMentions, maxEmoji):
|
postJsonObject, maxMentions, maxEmoji,
|
||||||
|
allowLocalNetworkAccess):
|
||||||
|
|
||||||
if postJsonObject.get('object'):
|
if postJsonObject.get('object'):
|
||||||
jsonObj = postJsonObject['object']
|
jsonObj = postJsonObject['object']
|
||||||
|
@ -2438,7 +2442,7 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
YTReplacementDomain: str,
|
YTReplacementDomain: str,
|
||||||
showPublishedDateOnly: bool,
|
showPublishedDateOnly: bool,
|
||||||
allowNewsFollowers: bool,
|
allowNewsFollowers: bool,
|
||||||
maxFollowers: int) -> None:
|
maxFollowers: int, allowLocalNetworkAccess: bool) -> None:
|
||||||
"""Processes received items and moves them to the appropriate
|
"""Processes received items and moves them to the appropriate
|
||||||
directories
|
directories
|
||||||
"""
|
"""
|
||||||
|
@ -2853,7 +2857,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
maxMentions, maxEmoji,
|
maxMentions, maxEmoji,
|
||||||
translate, unitTest,
|
translate, unitTest,
|
||||||
YTReplacementDomain,
|
YTReplacementDomain,
|
||||||
showPublishedDateOnly)
|
showPublishedDateOnly,
|
||||||
|
allowLocalNetworkAccess)
|
||||||
if debug:
|
if debug:
|
||||||
pprint(queueJson['post'])
|
pprint(queueJson['post'])
|
||||||
|
|
||||||
|
|
|
@ -469,7 +469,8 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
||||||
personCache: {},
|
personCache: {},
|
||||||
federationList: [],
|
federationList: [],
|
||||||
sendThreads: [], postLog: [],
|
sendThreads: [], postLog: [],
|
||||||
maxMirroredArticles: int) -> None:
|
maxMirroredArticles: int,
|
||||||
|
allowLocalNetworkAccess: bool) -> None:
|
||||||
"""Converts rss items in a newswire into posts
|
"""Converts rss items in a newswire into posts
|
||||||
"""
|
"""
|
||||||
if not newswire:
|
if not newswire:
|
||||||
|
@ -512,7 +513,8 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
||||||
|
|
||||||
rssTitle = removeControlCharacters(item[0])
|
rssTitle = removeControlCharacters(item[0])
|
||||||
url = item[1]
|
url = item[1]
|
||||||
if dangerousMarkup(url) or dangerousMarkup(rssTitle):
|
if dangerousMarkup(url, allowLocalNetworkAccess) or \
|
||||||
|
dangerousMarkup(rssTitle, allowLocalNetworkAccess):
|
||||||
continue
|
continue
|
||||||
rssDescription = ''
|
rssDescription = ''
|
||||||
|
|
||||||
|
@ -537,7 +539,8 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
||||||
postUrl += '/index.html'
|
postUrl += '/index.html'
|
||||||
|
|
||||||
# add the off-site link to the description
|
# add the off-site link to the description
|
||||||
if rssDescription and not dangerousMarkup(rssDescription):
|
if rssDescription and \
|
||||||
|
not dangerousMarkup(rssDescription, allowLocalNetworkAccess):
|
||||||
rssDescription += \
|
rssDescription += \
|
||||||
'<br><a href="' + postUrl + '">' + \
|
'<br><a href="' + postUrl + '">' + \
|
||||||
translate['Read more...'] + '</a>'
|
translate['Read more...'] + '</a>'
|
||||||
|
@ -743,7 +746,8 @@ def runNewswireDaemon(baseDir: str, httpd,
|
||||||
httpd.federationList,
|
httpd.federationList,
|
||||||
httpd.sendThreads,
|
httpd.sendThreads,
|
||||||
httpd.postLog,
|
httpd.postLog,
|
||||||
httpd.maxMirroredArticles)
|
httpd.maxMirroredArticles,
|
||||||
|
httpd.allowLocalNetworkAccess)
|
||||||
print('Newswire feed converted to ActivityPub')
|
print('Newswire feed converted to ActivityPub')
|
||||||
|
|
||||||
if httpd.maxNewsPosts > 0:
|
if httpd.maxNewsPosts > 0:
|
||||||
|
|
39
tests.py
39
tests.py
|
@ -291,8 +291,10 @@ def createServerAlice(path: str, domain: str, port: int,
|
||||||
maxEmoji = 10
|
maxEmoji = 10
|
||||||
onionDomain = None
|
onionDomain = None
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
|
allowLocalNetworkAccess = True
|
||||||
print('Server running: Alice')
|
print('Server running: Alice')
|
||||||
runDaemon(2048, False, True, False, False, True, 10, False,
|
runDaemon(allowLocalNetworkAccess,
|
||||||
|
2048, False, True, False, False, True, 10, False,
|
||||||
0, 100, 1024, 5, False,
|
0, 100, 1024, 5, False,
|
||||||
0, False, 1, False, False, False,
|
0, False, 1, False, False, False,
|
||||||
5, True, True, 'en', __version__,
|
5, True, True, 'en', __version__,
|
||||||
|
@ -356,8 +358,10 @@ def createServerBob(path: str, domain: str, port: int,
|
||||||
maxEmoji = 10
|
maxEmoji = 10
|
||||||
onionDomain = None
|
onionDomain = None
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
|
allowLocalNetworkAccess = True
|
||||||
print('Server running: Bob')
|
print('Server running: Bob')
|
||||||
runDaemon(2048, False, True, False, False, True, 10, False,
|
runDaemon(allowLocalNetworkAccess,
|
||||||
|
2048, False, True, False, False, True, 10, False,
|
||||||
0, 100, 1024, 5, False, 0,
|
0, 100, 1024, 5, False, 0,
|
||||||
False, 1, False, False, False,
|
False, 1, False, False, False,
|
||||||
5, True, True, 'en', __version__,
|
5, True, True, 'en', __version__,
|
||||||
|
@ -395,8 +399,10 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
|
||||||
maxEmoji = 10
|
maxEmoji = 10
|
||||||
onionDomain = None
|
onionDomain = None
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
|
allowLocalNetworkAccess = True
|
||||||
print('Server running: Eve')
|
print('Server running: Eve')
|
||||||
runDaemon(2048, False, True, False, False, True, 10, False,
|
runDaemon(allowLocalNetworkAccess,
|
||||||
|
2048, False, True, False, False, True, 10, False,
|
||||||
0, 100, 1024, 5, False, 0,
|
0, 100, 1024, 5, False, 0,
|
||||||
False, 1, False, False, False,
|
False, 1, False, False, False,
|
||||||
5, True, True, 'en', __version__,
|
5, True, True, 'en', __version__,
|
||||||
|
@ -1941,58 +1947,59 @@ def testRemoveHtml():
|
||||||
|
|
||||||
def testDangerousMarkup():
|
def testDangerousMarkup():
|
||||||
print('testDangerousMarkup')
|
print('testDangerousMarkup')
|
||||||
|
allowLocalNetworkAccess = False
|
||||||
content = '<p>This is a valid message</p>'
|
content = '<p>This is a valid message</p>'
|
||||||
assert(not dangerousMarkup(content))
|
assert(not dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = 'This is a valid message without markup'
|
content = 'This is a valid message without markup'
|
||||||
assert(not dangerousMarkup(content))
|
assert(not dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This is a valid-looking message. But wait... ' + \
|
content = '<p>This is a valid-looking message. But wait... ' + \
|
||||||
'<script>document.getElementById("concentrated")' + \
|
'<script>document.getElementById("concentrated")' + \
|
||||||
'.innerHTML = "evil";</script></p>'
|
'.innerHTML = "evil";</script></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This html contains more than you expected... ' + \
|
content = '<p>This html contains more than you expected... ' + \
|
||||||
'<script language="javascript">document.getElementById("abc")' + \
|
'<script language="javascript">document.getElementById("abc")' + \
|
||||||
'.innerHTML = "def";</script></p>'
|
'.innerHTML = "def";</script></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This is a valid-looking message. But wait... ' + \
|
content = '<p>This is a valid-looking message. But wait... ' + \
|
||||||
'<script src="https://evilsite/payload.js" /></p>'
|
'<script src="https://evilsite/payload.js" /></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message embeds an evil frame.' + \
|
content = '<p>This message embeds an evil frame.' + \
|
||||||
'<iframe src="somesite"></iframe></p>'
|
'<iframe src="somesite"></iframe></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message tries to obfuscate an evil frame.' + \
|
content = '<p>This message tries to obfuscate an evil frame.' + \
|
||||||
'< iframe src = "somesite"></ iframe ></p>'
|
'< iframe src = "somesite"></ iframe ></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message is not necessarily evil, but annoying.' + \
|
content = '<p>This message is not necessarily evil, but annoying.' + \
|
||||||
'<hr><br><br><br><br><br><br><br><hr><hr></p>'
|
'<hr><br><br><br><br><br><br><br><hr><hr></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message contans a ' + \
|
content = '<p>This message contans a ' + \
|
||||||
'<a href="https://validsite/index.html">valid link.</a></p>'
|
'<a href="https://validsite/index.html">valid link.</a></p>'
|
||||||
assert(not dangerousMarkup(content))
|
assert(not dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message contans a ' + \
|
content = '<p>This message contans a ' + \
|
||||||
'<a href="https://validsite/iframe.html">' + \
|
'<a href="https://validsite/iframe.html">' + \
|
||||||
'valid link having invalid but harmless name.</a></p>'
|
'valid link having invalid but harmless name.</a></p>'
|
||||||
assert(not dangerousMarkup(content))
|
assert(not dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message which <a href="127.0.0.1:8736">' + \
|
content = '<p>This message which <a href="127.0.0.1:8736">' + \
|
||||||
'tries to access the local network</a></p>'
|
'tries to access the local network</a></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>This message which <a href="http://192.168.5.10:7235">' + \
|
content = '<p>This message which <a href="http://192.168.5.10:7235">' + \
|
||||||
'tries to access the local network</a></p>'
|
'tries to access the local network</a></p>'
|
||||||
assert(dangerousMarkup(content))
|
assert(dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
content = '<p>127.0.0.1 This message which does not access ' + \
|
content = '<p>127.0.0.1 This message which does not access ' + \
|
||||||
'the local network</a></p>'
|
'the local network</a></p>'
|
||||||
assert(not dangerousMarkup(content))
|
assert(not dangerousMarkup(content, allowLocalNetworkAccess))
|
||||||
|
|
||||||
|
|
||||||
def runHtmlReplaceQuoteMarks():
|
def runHtmlReplaceQuoteMarks():
|
||||||
|
|
30
theme.py
30
theme.py
|
@ -183,7 +183,8 @@ def setCSSparam(css: str, param: str, value: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def setThemeFromDict(baseDir: str, name: str,
|
def setThemeFromDict(baseDir: str, name: str,
|
||||||
themeParams: {}, bgParams: {}) -> None:
|
themeParams: {}, bgParams: {},
|
||||||
|
allowLocalNetworkAccess: bool) -> None:
|
||||||
"""Uses a dictionary to set a theme
|
"""Uses a dictionary to set a theme
|
||||||
"""
|
"""
|
||||||
if name:
|
if name:
|
||||||
|
@ -198,7 +199,7 @@ def setThemeFromDict(baseDir: str, name: str,
|
||||||
|
|
||||||
# Ensure that any custom CSS is mostly harmless.
|
# Ensure that any custom CSS is mostly harmless.
|
||||||
# If not then just use the defaults
|
# If not then just use the defaults
|
||||||
if dangerousCSS(templateFilename) or \
|
if dangerousCSS(templateFilename, allowLocalNetworkAccess) or \
|
||||||
not os.path.isfile(templateFilename):
|
not os.path.isfile(templateFilename):
|
||||||
# use default css
|
# use default css
|
||||||
templateFilename = baseDir + '/epicyon-' + filename
|
templateFilename = baseDir + '/epicyon-' + filename
|
||||||
|
@ -355,7 +356,8 @@ def setCustomFont(baseDir: str):
|
||||||
|
|
||||||
|
|
||||||
def readVariablesFile(baseDir: str, themeName: str,
|
def readVariablesFile(baseDir: str, themeName: str,
|
||||||
variablesFile: str) -> None:
|
variablesFile: str,
|
||||||
|
allowLocalNetworkAccess: bool) -> None:
|
||||||
"""Reads variables from a file in the theme directory
|
"""Reads variables from a file in the theme directory
|
||||||
"""
|
"""
|
||||||
themeParams = loadJson(variablesFile, 0)
|
themeParams = loadJson(variablesFile, 0)
|
||||||
|
@ -367,10 +369,11 @@ def readVariablesFile(baseDir: str, themeName: str,
|
||||||
"options": "jpg",
|
"options": "jpg",
|
||||||
"search": "jpg"
|
"search": "jpg"
|
||||||
}
|
}
|
||||||
setThemeFromDict(baseDir, themeName, themeParams, bgParams)
|
setThemeFromDict(baseDir, themeName, themeParams, bgParams,
|
||||||
|
allowLocalNetworkAccess)
|
||||||
|
|
||||||
|
|
||||||
def setThemeDefault(baseDir: str):
|
def setThemeDefault(baseDir: str, allowLocalNetworkAccess: bool):
|
||||||
name = 'default'
|
name = 'default'
|
||||||
removeTheme(baseDir)
|
removeTheme(baseDir)
|
||||||
setThemeInConfig(baseDir, name)
|
setThemeInConfig(baseDir, name)
|
||||||
|
@ -390,10 +393,11 @@ def setThemeDefault(baseDir: str):
|
||||||
"banner-height-mobile": "10vh",
|
"banner-height-mobile": "10vh",
|
||||||
"search-banner-height-mobile": "15vh"
|
"search-banner-height-mobile": "15vh"
|
||||||
}
|
}
|
||||||
setThemeFromDict(baseDir, name, themeParams, bgParams)
|
setThemeFromDict(baseDir, name, themeParams, bgParams,
|
||||||
|
allowLocalNetworkAccess)
|
||||||
|
|
||||||
|
|
||||||
def setThemeHighVis(baseDir: str):
|
def setThemeHighVis(baseDir: str, allowLocalNetworkAccess: bool):
|
||||||
name = 'highvis'
|
name = 'highvis'
|
||||||
themeParams = {
|
themeParams = {
|
||||||
"newswire-publish-icon": True,
|
"newswire-publish-icon": True,
|
||||||
|
@ -422,7 +426,8 @@ def setThemeHighVis(baseDir: str):
|
||||||
"options": "jpg",
|
"options": "jpg",
|
||||||
"search": "jpg"
|
"search": "jpg"
|
||||||
}
|
}
|
||||||
setThemeFromDict(baseDir, name, themeParams, bgParams)
|
setThemeFromDict(baseDir, name, themeParams, bgParams,
|
||||||
|
allowLocalNetworkAccess)
|
||||||
|
|
||||||
|
|
||||||
def setThemeFonts(baseDir: str, themeName: str) -> None:
|
def setThemeFonts(baseDir: str, themeName: str) -> None:
|
||||||
|
@ -578,7 +583,8 @@ def setNewsAvatar(baseDir: str, name: str,
|
||||||
nickname + '@' + domain + '/avatar.png')
|
nickname + '@' + domain + '/avatar.png')
|
||||||
|
|
||||||
|
|
||||||
def setTheme(baseDir: str, name: str, domain: str) -> bool:
|
def setTheme(baseDir: str, name: str, domain: str,
|
||||||
|
allowLocalNetworkAccess: bool) -> bool:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
prevThemeName = getTheme(baseDir)
|
prevThemeName = getTheme(baseDir)
|
||||||
|
@ -589,7 +595,8 @@ def setTheme(baseDir: str, name: str, domain: str) -> bool:
|
||||||
themeNameLower = themeName.lower()
|
themeNameLower = themeName.lower()
|
||||||
if name == themeNameLower:
|
if name == themeNameLower:
|
||||||
try:
|
try:
|
||||||
globals()['setTheme' + themeName](baseDir)
|
globals()['setTheme' + themeName](baseDir,
|
||||||
|
allowLocalNetworkAccess)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -608,7 +615,8 @@ def setTheme(baseDir: str, name: str, domain: str) -> bool:
|
||||||
|
|
||||||
variablesFile = baseDir + '/theme/' + name + '/theme.json'
|
variablesFile = baseDir + '/theme/' + name + '/theme.json'
|
||||||
if os.path.isfile(variablesFile):
|
if os.path.isfile(variablesFile):
|
||||||
readVariablesFile(baseDir, name, variablesFile)
|
readVariablesFile(baseDir, name, variablesFile,
|
||||||
|
allowLocalNetworkAccess)
|
||||||
|
|
||||||
setCustomFont(baseDir)
|
setCustomFont(baseDir)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue