diff --git a/env b/env index 0be062740..60295303f 100644 --- a/env +++ b/env @@ -34,3 +34,4 @@ CF_KEY=blahblahblah CF_ZONE=blahblahblah DEBIAN_FRONTEND=noninteractive MENTION_LIMIT=100 +MULTIMEDIA_EMBEDDING_ENABLED=False diff --git a/files/__main__.py b/files/__main__.py index 9cc2a4cfc..762f0de8d 100644 --- a/files/__main__.py +++ b/files/__main__.py @@ -61,6 +61,7 @@ app.config['DESCRIPTION'] = environ.get("DESCRIPTION", "DESCRIPTION GOES HERE"). app.config['SETTINGS'] = {} app.config['SQLALCHEMY_DATABASE_URI'] = app.config['DATABASE_URL'] app.config['MENTION_LIMIT'] = int(environ.get('MENTION_LIMIT', 100)) +app.config['MULTIMEDIA_EMBEDDING_ENABLED'] = environ.get('MULTIMEDIA_EMBEDDING_ENABLED', "false").lower() == "true" r=redis.Redis(host=environ.get("REDIS_URL", "redis://localhost"), decode_responses=True, ssl_cert_reqs=None) diff --git a/files/assets/js/gif_modal.js b/files/assets/js/gif_modal.js index ba6d57470..94746bf09 100644 --- a/files/assets/js/gif_modal.js +++ b/files/assets/js/gif_modal.js @@ -43,7 +43,7 @@ async function getGif(searchTerm) { let response = await fetch("/giphy?searchTerm=" + searchTerm + "&limit=48"); let data = await response.json() - var max = data.length - 1 + var max = data.data?.length === undefined ? 0 : data.data.length - 1 data = data.data var gifURL = []; @@ -70,7 +70,10 @@ async function getGif(searchTerm) { function insertGIF(url,form) { - var gif = "\n\n"; + // https://github.com/themotte/rDrama/issues/139 + // when MULTIMEDIA_EMBEDDING_ENABLED == False, we want to insert an anchor, NOT an img + //var gif = "\n\n"; + var gif = '\n\n[' + url + '](' + url + ')'; var commentBox = document.getElementById(form); diff --git a/files/assets/js/marked.custom.js b/files/assets/js/marked.custom.js index 53a83b9ef..4f8d9df6f 100644 --- a/files/assets/js/marked.custom.js +++ b/files/assets/js/marked.custom.js @@ -65,7 +65,9 @@ function markdown(first, second) { dest.removeChild(dest.children[i]); } const html = marked.parse(input.value); - dest.innerHTML = DOMPurify.sanitize(html); + // https://github.com/themotte/rDrama/issues/139 + // Remove disallowed tags completely. + dest.innerHTML = DOMPurify.sanitize(html, {FORBID_TAGS: ['img', 'video', 'source']}); } } @@ -87,4 +89,4 @@ function charLimit(form, content) { text.innerText = length + ' / ' + maxLength; } -setTimeout(() => markdown('post-text','preview'), 200); \ No newline at end of file +setTimeout(() => markdown('post-text','preview'), 200); diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py index 8fb933a16..a56249e1d 100644 --- a/files/helpers/sanitize.py +++ b/files/helpers/sanitize.py @@ -13,9 +13,12 @@ import time import requests from files.__main__ import app -TLDS = ('ac','ad','ae','aero','af','ag','ai','al','am','an','ao','aq','ar','arpa','as','asia','at','au','aw','ax','az','ba','bb','bd','be','bf','bg','bh','bi','biz','bj','bm','bn','bo','br','bs','bt','bv','bw','by','bz','ca','cafe','cat','cc','cd','cf','cg','ch','ci','ck','cl','club','cm','cn','co','com','coop','cr','cu','cv','cx','cy','cz','de','dj','dk','dm','do','dz','ec','edu','ee','eg','er','es','et','eu','fi','fj','fk','fm','fo','fr','ga','gb','gd','ge','gf','gg','gh','gi','gl','gm','gn','gov','gp','gq','gr','gs','gt','gu','gw','gy','hk','hm','hn','hr','ht','hu','id','ie','il','im','in','info','int','io','iq','ir','is','it','je','jm','jo','jobs','jp','ke','kg','kh','ki','km','kn','kp','kr','kw','ky','kz','la','lb','lc','li','lk','lr','ls','lt','lu','lv','ly','ma','mc','md','me','mg','mh','mil','mk','ml','mm','mn','mo','mobi','mp','mq','mr','ms','mt','mu','museum','mv','mw','mx','my','mz','na','name','nc','ne','net','nf','ng','ni','nl','no','np','nr','nu','nz','om','org','pa','pe','pf','pg','ph','pk','pl','pm','pn','post','pr','pro','ps','pt','pw','py','qa','re','ro','rs','ru','rw','sa','sb','sc','sd','se','sg','sh','si','sj','sk','sl','sm','sn','so','social','sr','ss','st','su','sv','sx','sy','sz','tc','td','tel','tf','tg','th','tj','tk','tl','tm','tn','to','tp','tr','travel','tt','tv','tw','tz','ua','ug','uk','us','uy','uz','va','vc','ve','vg','vi','vn','vu','wf','win','ws','xn','xxx','xyz','ye','yt','yu','za','zm','zw') +TLDS = ('ac','ad','ae','aero','af','ag','ai','al','am','an','ao','aq','ar','arpa','as','asia','at','au','aw','ax','az','ba','bb','bd','be','bf','bg','bh','bi','biz','bj','bm','bn','bo','br','bs','bt','bv','bw','by','bz','ca','cafe','cat','cc','cd','cf','cg','ch','ci','ck','cl','club','cm','cn','co','com','coop','cr','cu','cv','cx','cy','cz','de','dj','dk','dm','do','dz','ec','edu','ee','eg','er','es','et','eu','fi','fj','fk','fm','fo','fr','ga','gb','gd','ge','gf','gg','gh','gi','gl','gm','gn','gov','gp','gq','gr','gs','gt','gu','gw','gy','hk','hm','hn','hr','ht','hu','id','ie','il','im','in','info','int','io','iq','ir','is','it','je','jm','jo','jobs','jp','ke','kg','kh','ki','km','kn','kp','kr','kw','ky','kz','la','lb','lc','li','lk','lr','ls','lt','lu','lv','ly','ma','mc','md','me','mg','mh','mil','mk','ml','mm','mn','mo','mobi','mp','mq','mr','ms','mt','mu','museum','mv','mw','mx','my','mz','na','name','nc','ne','net','nf','ng','ni','nl','no','np','nr','nu','nz','om','org','pa','pe','pf','pg','ph','pk','pl','pm','pn','post','pr','pro','ps','pt','pw','py','qa','re','ro','rs','ru','rw','sa','sb','sc','sd','se','sg','sh','si','sj','sk','sl','sm','sn','so','social','sr','ss','st','su','sv','sx','sy','sz','tc','td','tel','tf','tg','th','tj','tk','tl','tm','tn','to','tp','tr','travel','tt','tv','tw','tz','ua','ug','uk','us','uy','uz','va','vc','ve','vg','vi','vn','vu','wf','win','ws','xn','xxx','xyz','ye','yt','yu','za','zm','zw', 'moe') -allowed_tags = ('b','blockquote','br','code','del','em','h1','h2','h3','h4','h5','h6','hr','i','li','ol','p','pre','strong','sub','sup','table','tbody','th','thead','td','tr','ul','marquee','a','span','ruby','rp','rt','spoiler','img','lite-youtube','video','source') +allowed_tags = ('b','blockquote','br','code','del','em','h1','h2','h3','h4','h5','h6','hr','i','li','ol','p','pre','strong','sub','sup','table','tbody','th','thead','td','tr','ul','marquee','a','span','ruby','rp','rt','spoiler',) + +if app.config['MULTIMEDIA_EMBEDDING_ENABLED']: + allowed_tags += ('img', 'lite-youtube', 'video', 'source',) def allowed_attributes(tag, name, value): @@ -132,16 +135,26 @@ def sanitize(sanitized, alert=False, comment=False, edit=False): signal.signal(signal.SIGALRM, handler) signal.alarm(1) + # double newlines, eg. hello\nworld becomes hello\n\nworld, which later becomes
hello
world
sanitized = linefeeds_regex.sub(r'\1\n\n\2', sanitized) - sanitized = image_regex.sub(r'\1\4', sanitized) + if app.config['MULTIMEDIA_EMBEDDING_ENABLED']: + # turn eg. https://wikipedia.org/someimage.jpg into  + sanitized = image_regex.sub(r'\1\4', sanitized) + # if image url in whitelist, do nothing + # eg.  turns into  + # but if not, then extract url + # eg  turns into https://example.org/someimage.jpg sanitized = image_check_regex.sub(r'\1', sanitized) + # transform markdown into html sanitized = markdown(sanitized) + # turn ~something~ or ~~something~~ into