Merge branch 'master' into always-nonce

This commit is contained in:
caryoscelus 2023-11-16 14:20:30 +00:00
commit 93ed7418ab
4 changed files with 77 additions and 146 deletions

View file

@ -49,7 +49,7 @@ class SecurityError(Exception):
@PluginManager.acceptPlugins
class UiRequest(object):
class UiRequest:
def __init__(self, server, get, env, start_response):
if server:
@ -99,8 +99,52 @@ class UiRequest(object):
def resolveDomain(self, domain):
return self.server.site_manager.resolveDomainCached(domain)
# Call the request handler function base on path
def isCrossOriginRequest(self):
"""Prevent detecting sites on this 0net instance
In particular, we block non-user requests from other hosts as well as
cross-site
"""
url = self.getRequestUrl()
fetch_mode = self.env.get('HTTP_SEC_FETCH_MODE')
origin = self.env.get('HTTP_ORIGIN')
referer = self.env.get('HTTP_REFERER')
# Allow all user-initiated requests
if fetch_mode == 'navigate':
return False
# Deny requests that cannot be traced
if not origin and not referer:
return True
# Deny requests from non-0net origins
if origin and not self.isSameHost(origin, url):
return True
# Allow non-site specific requests
if self.getRequestSite() == '/':
return False
# Deny cross site requests
if not self.isSameOrigin(referer, url):
return True
return False
def route(self, path):
"""Main routing
If no internal action is performed, calls action[Path] from plugins
This behaviour is not very flexible or easy to follow, so perhaps
we'd want something else..
"""
if self.isCrossOriginRequest():
return self.error404()
# Restict Ui access by ip
if config.ui_restrict and self.env['REMOTE_ADDR'] not in config.ui_restrict:
return self.error403(details=False)
@ -274,25 +318,31 @@ class UiRequest(object):
else:
return referer
def getRequestSite(self):
"""Return 0net site addr associated with current request
If request is site-agnostic, returns /
"""
path = self.env["PATH_INFO"]
match = re.match(r'(/raw)?(?P<site>/1[a-zA-Z0-9]*)', path)
if not match:
match = re.match(r'(/raw)?/(?P<domain>[a-zA-Z0-9\.\-_]*)', path)
if match:
domain = match.group('domain')
if self.isDomain(domain):
addr = self.resolveDomain(domain)
return '/'+addr
return '/'
return match.group('site')
# Send response headers
def sendHeader(self, status=200, content_type="text/html", noscript=False, allow_ajax=False, script_nonce=None, extra_headers=[]):
url = self.getRequestUrl()
referer = self.env.get('HTTP_REFERER')
origin = self.env.get('HTTP_ORIGIN')
fetch_site = self.env.get('HTTP_SEC_FETCH_SITE')
fetch_mode = self.env.get('HTTP_SEC_FETCH_MODE')
not_same_ref = referer and not self.isSameHost(referer, url)
not_same_origin = origin and not self.isSameHost(origin, url)
cross_site_not_navigate = not referer and fetch_site == 'cross-site' and not fetch_mode == 'navigate'
if status != 404 and (not_same_ref or not_same_origin or cross_site_not_navigate):
# pretend nothing is here for third-party access
return self.error404()
headers = {}
headers["Version"] = "HTTP/1.1"
headers["Connection"] = "Keep-Alive"
headers["Keep-Alive"] = "max=25, timeout=30"
headers["X-Frame-Options"] = "SAMEORIGIN"
headers["Referrer-Policy"] = "same-origin"
if noscript:
headers["Content-Security-Policy"] = "default-src 'none'; sandbox allow-top-navigation allow-forms; img-src *; font-src * data:; media-src *; style-src * 'unsafe-inline';"
@ -304,7 +354,7 @@ class UiRequest(object):
if self.env["REQUEST_METHOD"] == "OPTIONS":
# Allow json access
headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept, Cookie, Range"
headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Accept, Cookie, Range, Referer"
headers["Access-Control-Allow-Credentials"] = "true"
# Download instead of display file types that can be dangerous
@ -614,15 +664,18 @@ class UiRequest(object):
if not url_a or not url_b:
return False
url_a = url_a.replace("/raw/", "/")
url_b = url_b.replace("/raw/", "/")
host_pattern = r'(?P<host>http[s]?://.*?)(/|$)'
origin_pattern = "http[s]{0,1}://(.*?/).*"
match_a = re.match(host_pattern, url_a)
match_b = re.match(host_pattern, url_b)
origin_a = re.sub(origin_pattern, "\\1", url_a)
origin_b = re.sub(origin_pattern, "\\1", url_b)
if not match_a or not match_b:
return False
return origin_a == origin_b
host_a = match_a.group('host')
host_b = match_b.group('host')
return host_a == host_b
def isSameOrigin(self, url_a, url_b):
"""Check if 0net origin is the same"""