diff --git a/mews/pages.py b/mews/pages.py index f3db756..eed72a5 100644 --- a/mews/pages.py +++ b/mews/pages.py @@ -210,6 +210,10 @@ def write_directory(target: str, rows: list[sqlite3.Row]) -> list[str]: ) temporary = destination.with_name("." + destination.name + ".tmp") temporary.write_bytes(text) + # The web server reads this file straight off disk as another user, so + # it is made readable on purpose. The service runs with a private umask + # to keep the database to itself, and that would otherwise apply here. + temporary.chmod(0o644) os.replace(temporary, destination) warnings = [str(f) for f in result.warnings] diff --git a/tests/test_service.py b/tests/test_service.py index f83755f..7f96a50 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -344,6 +344,20 @@ def test_a_hostile_title_is_escaped_and_stripped(connection, tmp_path): assert validate_bytes(target.read_bytes()).conforms +def test_the_directory_page_is_readable_by_the_web_server(connection, tmp_path): + """It is served off disk by another user, whatever umask the service has.""" + import os + import stat + + target = tmp_path / "directory.html" + old_umask = os.umask(0o077) + try: + pages.write_directory(str(target), db.listed(connection)) + finally: + os.umask(old_umask) + assert stat.S_IMODE(target.stat().st_mode) == 0o644 + + def test_an_empty_directory_still_conforms(connection, tmp_path): from mews.lint import validate_bytes