aboutsummaryrefslogtreecommitdiffstats
path: root/auth_backend.py
diff options
context:
space:
mode:
authorkj_sh6042026-04-03 02:09:14 -0400
committerkj_sh6042026-04-03 02:09:14 -0400
commitd6458885e00788e1dec779b6807a79646aac6ed6 (patch)
tree242f105571de42ab2a0a810ca79d41de5921d20c /auth_backend.py
parent90ba16a3b4d1dfc29a614522824d87c5cae14bf8 (diff)
refactor: xss and sql injection security hardening
Diffstat (limited to 'auth_backend.py')
-rw-r--r--auth_backend.py22
1 files changed, 20 insertions, 2 deletions
diff --git a/auth_backend.py b/auth_backend.py
index a8ad782..3902d1e 100644
--- a/auth_backend.py
+++ b/auth_backend.py
@@ -14,6 +14,9 @@ from typing import Callable, Optional, Protocol
# we store encrypted challenge output instead of storing passwords.
AUTH_CHALLENGE = "SHIM_AUTH_VALID"
USERNAME_RE = re.compile(r"^[a-z0-9_.-]{2,64}$")
+UUID_RE = re.compile(
+ r"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+)
ConnectFn = Callable[[], sqlite3.Connection]
@@ -39,6 +42,13 @@ def normalize_username(username: str) -> str:
return username.strip().lower()
+def normalize_uuid(value: str) -> Optional[str]:
+ candidate = (value or "").strip().lower()
+ if not UUID_RE.fullmatch(candidate):
+ return None
+ return candidate
+
+
def looks_like_python_script(path: Path) -> bool:
try:
with open(path, "r", encoding="utf-8", errors="ignore") as f:
@@ -93,6 +103,10 @@ class LocalMojicryptAuthBackend:
return False, "username already exists"
def update_username(self, user_uuid: str, new_username: str) -> tuple[bool, str]:
+ normalized_user_uuid = normalize_uuid(user_uuid)
+ if normalized_user_uuid is None:
+ return False, "invalid user id"
+
normalized = normalize_username(new_username)
username_error = self._validate_username(normalized)
if username_error:
@@ -102,7 +116,7 @@ class LocalMojicryptAuthBackend:
with self.connect_db() as conn:
cursor = conn.execute(
"UPDATE users SET username = ? WHERE user_uuid = ?",
- (normalized, user_uuid),
+ (normalized, normalized_user_uuid),
)
if cursor.rowcount == 0:
return False, "user not found"
@@ -112,6 +126,10 @@ class LocalMojicryptAuthBackend:
return True, "username updated"
def update_password(self, user_uuid: str, new_password: str) -> tuple[bool, str]:
+ normalized_user_uuid = normalize_uuid(user_uuid)
+ if normalized_user_uuid is None:
+ return False, "invalid user id"
+
password_error = self._validate_password(new_password)
if password_error:
return False, password_error
@@ -124,7 +142,7 @@ class LocalMojicryptAuthBackend:
with self.connect_db() as conn:
cursor = conn.execute(
"UPDATE users SET encrypted_challenge = ? WHERE user_uuid = ?",
- (encrypted, user_uuid),
+ (encrypted, normalized_user_uuid),
)
if cursor.rowcount == 0:
return False, "user not found"