Skip to content

Commit 3eb8dd5

Browse files
committed
Introduce UPLOAD_FILES_WHITELIST
- this file paths will be ignored during files extensions and mime type checks
1 parent 2b2d9ae commit 3eb8dd5

3 files changed

Lines changed: 63 additions & 0 deletions

File tree

server/mergin/sync/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,5 @@ class Configuration(object):
7878
EXCLUDED_CLONE_FILENAMES = config(
7979
"EXCLUDED_CLONE_FILENAMES", default="qgis_cfg.xml", cast=Csv()
8080
)
81+
# files that should be ignored during extension and mime type check
82+
UPLOAD_FILES_WHITELIST = config("UPLOAD_FILES_WHITELIST", default="", cast=Csv())

server/mergin/sync/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
from flask import current_app
2828
from pathlib import Path
2929

30+
from .config import Configuration
31+
3032

3133
def generate_checksum(file, chunk_size=4096):
3234
"""
@@ -349,6 +351,8 @@ def has_trailing_space(filepath: str) -> bool:
349351

350352
def is_supported_extension(filepath) -> bool:
351353
"""Check whether file's extension is supported."""
354+
if check_skip_validation(filepath):
355+
return True
352356
ext = os.path.splitext(filepath)[1].lower()
353357
return ext and ext not in FORBIDDEN_EXTENSIONS
354358

@@ -491,6 +495,15 @@ def is_supported_extension(filepath) -> bool:
491495
".xnk",
492496
}
493497

498+
499+
def check_skip_validation(file_path: str) -> bool:
500+
"""
501+
Check if we can skip validation for this file path.
502+
Some files are allowed even if they have forbidden extension or mime type.
503+
"""
504+
return file_path in Configuration.UPLOAD_FILES_WHITELIST
505+
506+
494507
FORBIDDEN_MIME_TYPES = {
495508
"application/x-msdownload",
496509
"application/x-sh",
@@ -515,6 +528,8 @@ def is_supported_extension(filepath) -> bool:
515528

516529
def is_supported_type(filepath) -> bool:
517530
"""Check whether the file mimetype is supported."""
531+
if check_skip_validation(filepath):
532+
return True
518533
mime_type = get_mimetype(filepath)
519534
return mime_type.startswith("image/") or mime_type not in FORBIDDEN_MIME_TYPES
520535

server/mergin/tests/test_utils.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
has_valid_characters,
2323
has_valid_first_character,
2424
check_filename,
25+
is_supported_extension,
26+
is_supported_type,
2527
is_valid_path,
2628
get_x_accel_uri,
2729
wkb2wkt,
2830
has_trailing_space,
31+
check_skip_validation,
2932
)
3033
from ..auth.models import LoginHistory, User
3134
from . import json_headers
@@ -322,3 +325,46 @@ class TestSchema(Schema):
322325
"size": "disk_usage",
323326
}
324327
assert schema_map == expected_map
328+
329+
330+
def test_check_skip_validation():
331+
ALLOWED_FILES = ["script.js", "config/script.js"]
332+
333+
# We patch the Configuration class attribute directly
334+
with patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES):
335+
336+
# Test allowed files
337+
for file_path in ALLOWED_FILES:
338+
assert check_skip_validation(file_path)
339+
340+
# Test not allowed files
341+
assert not check_skip_validation("test.py")
342+
assert not check_skip_validation("/some/path/test.py")
343+
assert not check_skip_validation("image.png")
344+
345+
346+
def test_is_supported_extension():
347+
ALLOWED_FILES = ["script.js", "config/script.js"]
348+
349+
with patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES):
350+
for file_path in ALLOWED_FILES:
351+
assert is_supported_extension(file_path)
352+
353+
# Allowed normal file
354+
assert is_supported_extension("image.png")
355+
356+
# Forbidden file
357+
assert not is_supported_extension("test.js")
358+
359+
360+
def test_mime_type_validation_skip():
361+
ALLOWED_FILES = ["script.js", "config/script.js"]
362+
# Mocking get_mimetype to return forbidden mime type
363+
with patch(
364+
"mergin.sync.utils.get_mimetype", return_value="application/x-python-code"
365+
), patch("mergin.sync.utils.Configuration.UPLOAD_FILES_WHITELIST", ALLOWED_FILES):
366+
for file_path in ALLOWED_FILES:
367+
assert is_supported_extension(file_path)
368+
369+
# Should be forbidden
370+
assert not is_supported_type("other.js")

0 commit comments

Comments
 (0)