fix: Audit-Findings — Theme-Validierung, locale-unabhängige Tests

- Theme-Name aus settings.ini gegen Regex validiert (nur [A-Za-z0-9_-]),
  verhindert Path-Traversal über GTK-Theme-Loading (S-05)
- Faillock-Tests nutzen expliziten strings-Parameter statt System-Locale,
  Tests laufen jetzt auch auf EN-Systemen (MAINT-4)
- Test für Path-Traversal im Theme-Namen ergänzt
This commit is contained in:
nevaforget 2026-03-26 12:36:16 +01:00
parent 65d3ba64f9
commit 8b1608f99d
3 changed files with 31 additions and 9 deletions

View File

@ -2,9 +2,12 @@
# ABOUTME: Provides User dataclass and helper functions for the greeter UI.
import configparser
import re
from dataclasses import dataclass
from pathlib import Path
VALID_THEME_NAME = re.compile(r"^[A-Za-z0-9_-]+$")
NOLOGIN_SHELLS = {"/usr/sbin/nologin", "/sbin/nologin", "/bin/false", "/usr/bin/nologin"}
MIN_UID = 1000
MAX_UID = 65533
@ -102,6 +105,9 @@ def get_user_gtk_theme(config_dir: Path | None = None) -> str | None:
return None
if config.has_option("Settings", "gtk-theme-name"):
return config.get("Settings", "gtk-theme-name")
theme = config.get("Settings", "gtk-theme-name")
# Validate against path traversal — only allow safe theme names
if theme and VALID_THEME_NAME.match(theme):
return theme
return None

View File

@ -11,6 +11,7 @@ from pathlib import Path
import pytest
from moongreet.greeter import faillock_warning, FAILLOCK_MAX_ATTEMPTS
from moongreet.i18n import load_strings
from moongreet.ipc import create_session, post_auth_response, start_session, cancel_session
@ -182,22 +183,26 @@ class TestFaillockWarning:
"""Tests for the faillock warning message logic."""
def test_no_warning_on_first_attempt(self) -> None:
assert faillock_warning(1) is None
strings = load_strings("de")
assert faillock_warning(1, strings) is None
def test_warning_on_second_attempt(self) -> None:
warning = faillock_warning(2)
strings = load_strings("de")
warning = faillock_warning(2, strings)
assert warning is not None
assert "1" in warning # 1 Versuch übrig
def test_warning_on_third_attempt(self) -> None:
warning = faillock_warning(3)
strings = load_strings("de")
warning = faillock_warning(3, strings)
assert warning is not None
assert "gesperrt" in warning.lower()
assert warning == strings.faillock_locked
def test_warning_beyond_max_attempts(self) -> None:
warning = faillock_warning(4)
strings = load_strings("de")
warning = faillock_warning(4, strings)
assert warning is not None
assert "gesperrt" in warning.lower()
assert warning == strings.faillock_locked
def test_max_attempts_constant_is_three(self) -> None:
assert FAILLOCK_MAX_ATTEMPTS == 3

View File

@ -187,7 +187,7 @@ class TestGetUserGtkTheme:
assert result is None
def test_handles_interpolation_characters(self, tmp_path: Path) -> None:
"""Theme names with % characters should not trigger interpolation errors."""
"""Theme names with % characters are rejected by validation."""
gtk_dir = tmp_path / ".config" / "gtk-4.0"
gtk_dir.mkdir(parents=True)
settings = gtk_dir / "settings.ini"
@ -195,7 +195,18 @@ class TestGetUserGtkTheme:
result = get_user_gtk_theme(config_dir=gtk_dir)
assert result == "My%Theme"
assert result is None
def test_rejects_path_traversal_theme_name(self, tmp_path: Path) -> None:
"""Theme names with path traversal characters should be rejected."""
gtk_dir = tmp_path / ".config" / "gtk-4.0"
gtk_dir.mkdir(parents=True)
settings = gtk_dir / "settings.ini"
settings.write_text("[Settings]\ngtk-theme-name=../../../../etc/evil\n")
result = get_user_gtk_theme(config_dir=gtk_dir)
assert result is None
def test_ignores_symlinked_accountsservice_icon(self, tmp_path: Path) -> None:
"""AccountsService icon as symlink should be ignored to prevent traversal."""