mirror of
https://github.com/webrecorder/pywb.git
synced 2025-03-15 00:03:28 +01:00
Sanitize static filepaths and prevent path traversal
This commit is contained in:
parent
97fffe3a34
commit
d89a0d3699
@ -1,5 +1,8 @@
|
|||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from pathvalidate import sanitize_filepath
|
||||||
|
|
||||||
from pywb.utils.loaders import LocalFileLoader
|
from pywb.utils.loaders import LocalFileLoader
|
||||||
|
|
||||||
@ -7,6 +10,10 @@ from pywb.apps.wbrequestresponse import WbResponse
|
|||||||
from pywb.utils.wbexception import NotFoundException
|
from pywb.utils.wbexception import NotFoundException
|
||||||
|
|
||||||
|
|
||||||
|
class PathValidationError(Exception):
|
||||||
|
"""Path validation exception"""
|
||||||
|
|
||||||
|
|
||||||
#=================================================================
|
#=================================================================
|
||||||
# Static Content Handler
|
# Static Content Handler
|
||||||
#=================================================================
|
#=================================================================
|
||||||
@ -23,15 +30,29 @@ class StaticHandler(object):
|
|||||||
if url.endswith('/'):
|
if url.endswith('/'):
|
||||||
url += 'index.html'
|
url += 'index.html'
|
||||||
|
|
||||||
full_path = environ.get('pywb.static_dir')
|
url = sanitize_filepath(url)
|
||||||
if full_path:
|
|
||||||
full_path = os.path.join(full_path, url)
|
canonical_static_path = environ.get('pywb.static_dir')
|
||||||
|
if not canonical_static_path:
|
||||||
|
canonical_static_path = self.static_path
|
||||||
|
|
||||||
|
full_static_path = os.path.abspath(canonical_static_path)
|
||||||
|
full_path = None
|
||||||
|
|
||||||
|
if environ.get('pywb.static_dir'):
|
||||||
|
full_path = os.path.join(full_static_path, url)
|
||||||
if not os.path.isfile(full_path):
|
if not os.path.isfile(full_path):
|
||||||
full_path = None
|
full_path = None
|
||||||
|
|
||||||
if not full_path:
|
if not full_path:
|
||||||
full_path = os.path.join(self.static_path, url)
|
full_path = os.path.join(self.static_path, url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate_requested_file_path(full_static_path, full_path)
|
||||||
|
except PathValidationError:
|
||||||
|
raise NotFoundException('Static File Not Found: ' +
|
||||||
|
url_str)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = self.block_loader.load(full_path)
|
data = self.block_loader.load(full_path)
|
||||||
|
|
||||||
@ -65,4 +86,13 @@ class StaticHandler(object):
|
|||||||
raise NotFoundException('Static File Not Found: ' +
|
raise NotFoundException('Static File Not Found: ' +
|
||||||
url_str)
|
url_str)
|
||||||
|
|
||||||
|
def validate_requested_file_path(self, static_dir, requested_path):
|
||||||
|
"""Validate that requested file path is within static dir"""
|
||||||
|
static_dir = Path(static_dir)
|
||||||
|
requested_path = Path(requested_path)
|
||||||
|
|
||||||
|
if static_dir.resolve() not in requested_path.resolve().parents:
|
||||||
|
raise PathValidationError('Requested path forbidden')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,3 +18,4 @@ tldextract
|
|||||||
python-dateutil
|
python-dateutil
|
||||||
markupsafe>=2.1.1
|
markupsafe>=2.1.1
|
||||||
ua_parser
|
ua_parser
|
||||||
|
pathvalidate
|
||||||
|
Loading…
x
Reference in New Issue
Block a user