-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Lazlo Westerhof <[email protected]>
- Loading branch information
1 parent
6b55514
commit 4fc21f0
Showing
7 changed files
with
170 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
__copyright__ = 'Copyright (c) 2023, Utrecht University' | ||
__license__ = 'GPLv3, see LICENSE' | ||
__license__ = 'GPLv3, see LICENSE' | ||
|
||
import string | ||
from unittest.mock import patch | ||
|
||
from yoda_eus.mail import is_email_valid | ||
from yoda_eus.password_complexity import check_password_complexity | ||
from yoda_eus.util import get_validated_static_path | ||
|
||
|
||
class TestMain: | ||
|
||
def test_password_validation_ok(self): | ||
result = check_password_complexity("Test123456789!") | ||
assert len(result) == 0 | ||
|
@@ -50,10 +51,92 @@ def test_password_validation_multiple(self): | |
assert len(result) == 3 | ||
assert "Password is too short: it needs to be at least 10 characters." in result | ||
assert "Password needs to contain at least one digit." in result | ||
assert "Password needs to contain at least one punctuation character ({})".format(string.punctuation) in result | ||
assert ( | ||
"Password needs to contain at least one punctuation character ({})".format( | ||
string.punctuation | ||
) | ||
in result | ||
) | ||
|
||
def is_email_valid_yes(self): | ||
assert is_email_valid("[email protected]") | ||
|
||
def is_email_valid_no(self): | ||
assert not is_email_valid("this is not a valid email address") | ||
|
||
def exists_return_value(self, pathname): | ||
""" Mock path.exists function. True if path does not contain "theme" and "uu" """ | ||
return not ("theme" in pathname and "uu" in pathname) | ||
|
||
@patch("os.path.exists") | ||
def test_static_loader_valid_path(self, mock_exists): | ||
mock_exists.side_effect = self.exists_return_value | ||
# uu theme | ||
static_dir, asset_name = get_validated_static_path( | ||
"/assets/img/logo.svg?wekr", | ||
"/assets/img/logo.svg", | ||
"/var/www/yoda/themes", | ||
"uu", | ||
) | ||
assert static_dir == "/var/www/yoda/static/img" | ||
assert asset_name == "logo.svg" | ||
# other theme | ||
static_dir, asset_name = get_validated_static_path( | ||
"/assets/img/logo.svg?wekr", | ||
"/assets/img/logo.svg", | ||
"/var/www/yoda/themes", | ||
"wur", | ||
) | ||
assert static_dir == "/var/www/yoda/themes/wur/static/img" | ||
assert asset_name == "logo.svg" | ||
|
||
@patch("os.path.exists") | ||
def test_static_loader_invalid_path(self, mock_exists): | ||
mock_exists.side_effect = self.exists_return_value | ||
# Too short | ||
assert ( | ||
get_validated_static_path("/?sawerw", "/", "/var/www/yoda/themes", "uu") | ||
is None | ||
) | ||
# Path traversal attack | ||
assert ( | ||
get_validated_static_path( | ||
"/assets/../../../../etc/passwd?werwrwr", | ||
"/assets/../../../../etc/passwd", | ||
"/var/www/yoda/themes", | ||
"uu", | ||
) | ||
is None | ||
) | ||
# non-printable characters | ||
full_path = "/assets/" + chr(13) + "img/logo.svg?werwer" | ||
path = "/assets/" + chr(13) + "img/logo.svg" | ||
assert ( | ||
get_validated_static_path(full_path, path, "/var/www/yoda/themes", "uu") | ||
is None | ||
) | ||
assert ( | ||
get_validated_static_path(full_path, path, "/var/www/yoda/themes", "wur") | ||
is None | ||
) | ||
# non-printable characters in asset name | ||
full_path = "/assets/img/l" + chr(13) + "ogo.svg?werwer" | ||
path = "/assets/img/l" + chr(13) + "ogo.svg" | ||
assert ( | ||
get_validated_static_path(full_path, path, "/var/www/yoda/themes", "uu") | ||
is None | ||
) | ||
assert ( | ||
get_validated_static_path(full_path, path, "/var/www/yoda/themes", "wur") | ||
is None | ||
) | ||
# .. in file name | ||
assert ( | ||
get_validated_static_path( | ||
"/assets/img/lo..go.svg?sklaerw", | ||
"/assets/img/lo..go.svg?sklaerw", | ||
"/var/www/yoda/themes", | ||
"uu", | ||
) | ||
is None | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#!/usr/bin/env python3 | ||
|
||
__copyright__ = 'Copyright (c) 2021-2023, Utrecht University' | ||
__license__ = 'GPLv3, see LICENSE' | ||
|
||
from os import path | ||
from re import fullmatch | ||
from typing import Optional, Tuple | ||
|
||
from werkzeug.security import safe_join | ||
from werkzeug.utils import secure_filename | ||
|
||
|
||
def get_validated_static_path( | ||
full_path: str, request_path: str, yoda_theme_path: str, yoda_theme: str | ||
) -> Optional[Tuple[str, str]]: | ||
""" | ||
Static files handling - recognisable through '/assets/' | ||
Confirms that input path is valid and return corresponding static path | ||
:param full_path: Full path of request | ||
:param request_path: Short path of request | ||
:param yoda_theme_path: Path to the yoda themes | ||
:param yoda_theme: Name of the chosen theme | ||
:returns: Tuple of static directory and filename for correct path, None for incorrect path | ||
""" | ||
parts = full_path.split("/") | ||
|
||
if ( | ||
len(parts) > 2 | ||
and fullmatch("[ -~]*", full_path) is not None | ||
and parts[1] == "assets" | ||
): | ||
parts = parts[2:-1] | ||
user_static_area = path.join(yoda_theme_path, yoda_theme) | ||
_, asset_name = path.split(request_path) | ||
# Confirm that asset_name is safe | ||
if asset_name != secure_filename(asset_name): | ||
return None | ||
|
||
static_dir = safe_join(user_static_area + "/static", *parts) | ||
if not static_dir: | ||
return None | ||
user_static_filename = path.join(static_dir, asset_name) | ||
|
||
if not path.exists(user_static_filename): | ||
static_dir = safe_join("/var/www/yoda/static", *parts) | ||
|
||
return static_dir, asset_name | ||
|
||
return None |