fix: critical security hardening (Phase A permissions audit)
- Remove role field from UserCreate schema, hardcode 'engineer' at registration
- Escape all user content in HTML export with html.escape() (XSS fix)
- Add field_validator to reject default SECRET_KEY when DEBUG=False
- Add CHECK constraint on users.role ('engineer'|'viewer') + migration 011
- Fix test_admin fixture to properly grant is_super_admin via ORM
- Fix circular FK (users↔invite_codes) in test DB setup with DROP SCHEMA CASCADE
- Add 5 new security tests (role validation + XSS prevention)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,7 @@ class TestAuthentication:
|
||||
user_data = {
|
||||
"email": "newuser@example.com",
|
||||
"password": "SecurePass123!",
|
||||
"name": "New User",
|
||||
"role": "engineer"
|
||||
"name": "New User"
|
||||
}
|
||||
|
||||
response = await client.post("/api/v1/auth/register", json=user_data)
|
||||
@@ -23,7 +22,7 @@ class TestAuthentication:
|
||||
data = response.json()
|
||||
assert data["email"] == user_data["email"]
|
||||
assert data["name"] == user_data["name"]
|
||||
assert data["role"] == user_data["role"]
|
||||
assert data["role"] == "engineer"
|
||||
assert "id" in data
|
||||
assert "password" not in data # Password should not be returned
|
||||
|
||||
@@ -35,8 +34,7 @@ class TestAuthentication:
|
||||
user_data = {
|
||||
"email": test_user["email"], # Use existing email
|
||||
"password": "AnotherPass123!",
|
||||
"name": "Another User",
|
||||
"role": "engineer"
|
||||
"name": "Another User"
|
||||
}
|
||||
|
||||
response = await client.post("/api/v1/auth/register", json=user_data)
|
||||
@@ -93,3 +91,33 @@ class TestAuthentication:
|
||||
response = await client.get("/api/v1/auth/me")
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_with_role_field_ignored(self, client: AsyncClient):
|
||||
"""Test that sending a role field at registration is ignored — always engineer."""
|
||||
user_data = {
|
||||
"email": "hacker@example.com",
|
||||
"password": "HackerPass123!",
|
||||
"name": "Hacker",
|
||||
"role": "admin"
|
||||
}
|
||||
|
||||
response = await client.post("/api/v1/auth/register", json=user_data)
|
||||
|
||||
assert response.status_code == 201
|
||||
data = response.json()
|
||||
assert data["role"] == "engineer"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_default_role_is_engineer(self, client: AsyncClient):
|
||||
"""Test that omitting role defaults to engineer."""
|
||||
user_data = {
|
||||
"email": "default@example.com",
|
||||
"password": "DefaultPass123!",
|
||||
"name": "Default User"
|
||||
}
|
||||
|
||||
response = await client.post("/api/v1/auth/register", json=user_data)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert response.json()["role"] == "engineer"
|
||||
|
||||
Reference in New Issue
Block a user