Add profile feature, input sanitization, and stronger security checks

Backend:
- app/core/sanitize.py: shared sanitize_str, normalize_email, validate_phone,
  validate_date_of_birth — applied to every user-supplied DB-bound input
- app/schemas/user.py: sanitize full_name, normalize email on UserCreate
- app/models/profile.py: profiles table (position, phone, dob, address, updated_at)
- app/models/user.py: Profile back-ref, is_superuser admin-role comment
- app/schemas/profile.py: ProfileRead/ProfileUpdate with full sanitization
- app/routers/profile.py: GET+PUT /api/profile/me (lazy profile creation)
- app/main.py: register /api/profile router
- alembic migration 676084df61d1: create profiles table

Frontend:
- components/Nav.tsx: shared nav (Dashboard | Profile | Logout)
- pages/ProfilePage.tsx: profile view + inline edit form with error handling
- pages/DashboardPage.tsx: use Nav component
- api/client.ts: ProfileData type, getProfile, updateProfile
- App.tsx: /profile private route

Security:
- scripts/security_check.py: tighter SQL injection patterns (f-string/format/%
  in execute/query/text()), new SANIT category for raw request→DB patterns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-04-13 18:15:47 +02:00
parent e117a33a73
commit 343f12259c
18 changed files with 547 additions and 16 deletions
+28 -5
View File
@@ -7,9 +7,10 @@ Checks:
1. Hardcoded secrets / credentials in staged files
2. Dangerous patterns (eval, exec, shell=True, pickle)
3. Weak cryptography (MD5, SHA1 for passwords, DES)
4. SQL injection risk (raw string formatting into queries)
5. Debug/development flags left in code
6. bandit static analysis on Python files
4. SQL injection risk (f-strings / .format() / % in execute/query/text())
5. Missing input sanitization (raw request attributes passed to DB)
6. Debug/development flags left in code
7. bandit static analysis on Python files
"""
import os
@@ -48,8 +49,29 @@ WEAK_CRYPTO_PATTERNS = [
]
SQL_INJECTION_PATTERNS = [
(r'(execute|query)\s*\(\s*[f"\'].*%s.*["\']\.format|f".*SELECT.*{',
"potential SQL injection via string formatting"),
# f-string or .format() passed directly to execute/query
(r'(execute|query)\s*\(\s*f["\']',
"potential SQL injection: f-string passed to execute/query"),
(r'(execute|query)\s*\(.*\.format\s*\(',
"potential SQL injection: .format() passed to execute/query"),
(r'(execute|query)\s*\(.*%\s*[({]',
"potential SQL injection: %-formatting passed to execute/query"),
# SQLAlchemy text() used without bindparam / colon-style params
(r'\btext\s*\(\s*f["\']',
"SQLAlchemy text() with f-string — use bindparam() instead"),
(r'\btext\s*\(.*\.format\s*\(',
"SQLAlchemy text() with .format() — use bindparam() instead"),
# String concatenation into a variable named *query* or *sql*
(r'(sql|query)\s*[+]=\s*["\']',
"possible SQL string concatenation — use ORM or bindparam()"),
]
SANITIZATION_PATTERNS = [
# Pydantic str field without a validator on the same or adjacent line
# Flags 'str' fields in BaseModel subclasses that look unvalidated.
# Heuristic: detects direct assignment to session/db without going through a schema.
(r'\bdb\.(add|execute)\s*\(.*request\.',
"raw request attribute passed to DB — route through a Pydantic schema first"),
]
DEBUG_PATTERNS = [
@@ -62,6 +84,7 @@ ALL_PATTERNS = (
+ [("DANGER", p, m) for p, m in DANGEROUS_PATTERNS]
+ [("CRYPTO", p, m) for p, m in WEAK_CRYPTO_PATTERNS]
+ [("SQLINJ", p, m) for p, m in SQL_INJECTION_PATTERNS]
+ [("SANIT", p, m) for p, m in SANITIZATION_PATTERNS]
+ [("DEBUG", p, m) for p, m in DEBUG_PATTERNS]
)