From 47f4bf20c7f2e24bf7b72aee5a53fd7daa05a8fe Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Mon, 26 Jan 2026 15:02:53 +0530 Subject: [PATCH] cleanup precommit hooks --- .github/ISSUE_TEMPLATE/bug_report.yaml | 12 ++++---- .github/SECURITY.md | 2 +- .github/workflows/auto-comment.yml | 26 ++++++++-------- .github/workflows/build-pr.yml | 2 +- .github/workflows/build.yml | 2 +- .gitignore | 2 +- .vscode/settings.json | 8 ++--- CHANGELOG.md | 28 ++++++++--------- README.md | 2 +- VERSION | 2 +- backend/.gitignore | 2 +- backend/Dockerfile.dev | 2 +- backend/app/api/deps.py | 11 +++---- backend/app/api/router.py | 1 + backend/app/api/v1/auth.py | 19 ++++++------ backend/app/api/v1/users.py | 22 ++++++------- backend/app/config.py | 34 +++++++++++++++++++-- backend/app/core/database.py | 3 +- backend/app/core/logging.py | 9 ++++++ backend/app/core/security.py | 14 ++++----- backend/app/main.py | 19 +++++------- backend/app/models/project.py | 3 +- backend/app/models/user.py | 26 ++++++++-------- backend/app/utils/helpers.py | 10 +++--- frontend/src/lib/assets/favicon.svg | 2 +- frontend/src/lib/components/layout/index.ts | 2 +- frontend/src/lib/components/ui/index.ts | 2 +- frontend/src/routes/layout.css | 2 +- frontend/svelte.config.js | 2 +- shared/.gitignore | 2 +- shared/logging.py | 22 ++++++------- 31 files changed, 162 insertions(+), 133 deletions(-) create mode 100644 backend/app/core/logging.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 2b6a5696..d03fe720 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -45,12 +45,12 @@ body: - **Docker compose**: 2.15.1 - **Browser**: Microsoft Edge 112.0.1722.58 value: | - - reNgine: - - OS: - - Python: - - Docker Engine: - - Docker Compose: - - Browser: + - reNgine: + - OS: + - Python: + - Docker Engine: + - Docker Compose: + - Browser: render: markdown validations: required: true diff --git a/.github/SECURITY.md b/.github/SECURITY.md index ea6331dd..70339f54 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,7 +1,7 @@ # Security Policy -> **[IMPORTANT NOTICE - February 9, 2025]** +> **[IMPORTANT NOTICE - February 9, 2025]** > reNgine is currently undergoing a major refactoring to address all XSS-related vulnerabilities. While we are committed to security, we are temporarily suspending new XSS vulnerability reports until this refactoring is complete. We will continue to accept and investigate all other types of security vulnerabilities. Thank you for your understanding and continued support in making reNgine more secure. > > Please note that most reported XSS vulnerabilities in reNgine affect on-premise installations with limited exploitability. Nevertheless, we are committed to fixing these issues systematically through our ongoing refactoring effort. diff --git a/.github/workflows/auto-comment.yml b/.github/workflows/auto-comment.yml index e5fa9e7c..bf39897f 100644 --- a/.github/workflows/auto-comment.yml +++ b/.github/workflows/auto-comment.yml @@ -23,35 +23,35 @@ jobs: script: | const { owner, repo } = context.repo; const author = context.payload.sender.login; - + if (context.eventName === 'issues' && context.payload.action === 'opened') { const issueTitle = context.payload.issue.title.toLowerCase(); let commentBody; - + if (issueTitle.includes('feat')) { commentBody = `Hey @${author}! πŸš€ Thanks for this exciting feature idea! We love seeing fresh concepts that could take reNgine to the next level. 🌟 - + To help us understand your vision better, could you: - + πŸ“ Provide a detailed description of the feature 🎯 Explain the problem it solves or the value it adds πŸ’‘ Share any implementation ideas you might have - + Your input is invaluable in shaping the future of reNgine. Let's innovate together! πŸ’ͺ`; } else { commentBody = `Hey @${author}! πŸ‘‹ Thanks for flagging this bug! πŸ›πŸ” You're our superhero bug hunter! πŸ¦Έβ€β™‚οΈπŸ¦Έβ€β™€οΈ Before we suit up to squash this bug, could you please: - + πŸ“š Double-check our documentation: https://rengine.wiki πŸ•΅οΈ Make sure it's not a known issue πŸ“ Provide all the juicy details about this sneaky bug - + Once again - thanks for your vigilance! πŸ› οΈπŸš€`; } - + github.rest.issues.createComment({ issue_number: context.issue.number, owner, @@ -74,8 +74,8 @@ jobs: if (isPRMerged) { commentBody = `Holy smokes! 🀯 You've just made reNgine even more awesome! - Your code is now part of the reNgine hall of fame. πŸ† - + Your code is now part of the reNgine hall of fame. πŸ† + Keep the cool ideas coming - maybe next time you'll break the internet! πŸ’»πŸ’₯ Virtual high fives all around! πŸ™Œ`; @@ -83,9 +83,9 @@ jobs: commentBody = `Hey, thanks for your contribution! πŸ™ We appreciate the time and effort you put into this PR. Sadly this is not the right fit for reNgine at the moment. - + While we couldn't merge it this time, we value your interest in improving reNgine. - + Feel free to reach out if you have any questions. Thanks again!`; } @@ -95,4 +95,4 @@ jobs: repo, body: commentBody }); - } \ No newline at end of file + } diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index d5cdad0b..14810dbb 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -2,7 +2,7 @@ name: πŸ—οΈ Build Docker image for pull request on: pull_request: - branches: + branches: - master - release/* diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df11eb55..ed804bc4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,4 +55,4 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha - cache-to: type=gha,mode=max \ No newline at end of file + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index aa57340d..d1445da9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ docker-compose.override.yml # Temporary files *.tmp *.temp -.cache/ \ No newline at end of file +.cache/ diff --git a/.vscode/settings.json b/.vscode/settings.json index e8dc0efd..d77201dc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,12 +3,12 @@ "**/__pycache__": true, "**/*.pyc": true, "**/*.pyo": true, - ".ruff_cache": true, + ".ruff_cache": true }, "search.exclude": { "**/__pycache__": true, ".venv": true, "venv": true, - ".ruff_cache": true, - }, -} \ No newline at end of file + ".ruff_cache": true + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 19fb9a98..294e4d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -202,7 +202,7 @@ * Fix uninitialised variable cmd in custom_subdomain_tools by @cpandya2909 in https://github.com/yogeshojha/rengine/pull/1207 * [FIX] security: OS Command Injection vulnerability (x2) #1219 by @0xtejas in https://github.com/yogeshojha/rengine/pull/1227 -### New Contributors :rocket: +### New Contributors :rocket: * @yarysp made their first contribution in https://github.com/yogeshojha/rengine/pull/1199 * @jostasik made their first contribution in https://github.com/yogeshojha/rengine/pull/1226 * @cpandya2909 made their first contribution in https://github.com/yogeshojha/rengine/pull/1207 @@ -278,19 +278,19 @@ * fix(tool_arsenal): incorrect regex version numbers by @AnonymousWP in https://github.com/yogeshojha/rengine/pull/1086 ### New Contributors -* @luizmlo made their first contribution in https://github.com/yogeshojha/rengine/pull/1029 :partying_face: -* @aqhmal made their first contribution in https://github.com/yogeshojha/rengine/pull/1021 :partying_face: -* @C0wnuts made their first contribution in https://github.com/yogeshojha/rengine/pull/973 :partying_face: -* @ocervell made their first contribution in https://github.com/yogeshojha/rengine/pull/1058 :partying_face: -* @Vijayragha1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1074 :partying_face: -* @jxdv made their first contribution in https://github.com/yogeshojha/rengine/pull/1081 :partying_face: -* @SeanOverton made their first contribution in https://github.com/yogeshojha/rengine/pull/1106 :partying_face: -* @SubGlitch1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1107 :partying_face: -* @Linuxinet made their first contribution in https://github.com/yogeshojha/rengine/pull/1112 :partying_face: +* @luizmlo made their first contribution in https://github.com/yogeshojha/rengine/pull/1029 :partying_face: +* @aqhmal made their first contribution in https://github.com/yogeshojha/rengine/pull/1021 :partying_face: +* @C0wnuts made their first contribution in https://github.com/yogeshojha/rengine/pull/973 :partying_face: +* @ocervell made their first contribution in https://github.com/yogeshojha/rengine/pull/1058 :partying_face: +* @Vijayragha1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1074 :partying_face: +* @jxdv made their first contribution in https://github.com/yogeshojha/rengine/pull/1081 :partying_face: +* @SeanOverton made their first contribution in https://github.com/yogeshojha/rengine/pull/1106 :partying_face: +* @SubGlitch1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1107 :partying_face: +* @Linuxinet made their first contribution in https://github.com/yogeshojha/rengine/pull/1112 :partying_face: **Full Changelog**: https://github.com/yogeshojha/rengine/compare/v2.0.1...v2.0.2 -Once again excellent work on reNgine v2.0.2 by @AnonymousWP, @psyray, @ocervell and everybody else! :rocket: +Once again excellent work on reNgine v2.0.2 by @AnonymousWP, @psyray, @ocervell and everybody else! :rocket: ## 2.0.1 @@ -299,7 +299,7 @@ Once again excellent work on reNgine v2.0.2 by @AnonymousWP, @psyray, @ocervell 2.0.1 fixes a ton of issues in reNgine 2.0. -Fixes: +Fixes: 1. Prevent duplicating Nuclei vulns for subdomain #1012 @psyray 2. Fixes for empty subdomain returned during nuclei scan #1011 @psyray 3. Add all the missing slug in scanEngine view & other places #1005 @psyray @@ -314,13 +314,13 @@ Fixes: 11. Add stack trace into make logs if DEBUG True #994 @psyray 12. Fix dirfuzz base64 name display #993 #992 @psyray 13. Fix target subdomains list not loading #991 @psyray -14. Change WORDLIST constant value #987, fixes #986@psyray +14. Change WORDLIST constant value #987, fixes #986@psyray 15. fix(notification_settings): submitting results in error 502 #981 fixes #970 @psyray 16. Fixes with documentation and installation/update/uninstall scripts @anonymousWP 17. Fix file directory popup not showing in detailed scan #912 @psyray -@AnonymousWP and @psyray have been phenomenal in fixing these bugs. Thanks to both of you! :heart: :rocket: +@AnonymousWP and @psyray have been phenomenal in fixing these bugs. Thanks to both of you! :heart: :rocket: ## 2.0.0 diff --git a/README.md b/README.md index 5b029848..4ce5a227 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# rengine 3.0 \ No newline at end of file +# rengine 3.0 diff --git a/VERSION b/VERSION index 56fea8a0..4a36342f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.0 \ No newline at end of file +3.0.0 diff --git a/backend/.gitignore b/backend/.gitignore index eb068ff4..fe09b614 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -126,4 +126,4 @@ dmypy.json .DS_Store # FastAPI specific -.uvicorn_cache/ \ No newline at end of file +.uvicorn_cache/ diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev index 9ab843a2..1d47c0d5 100644 --- a/backend/Dockerfile.dev +++ b/backend/Dockerfile.dev @@ -15,4 +15,4 @@ RUN uv sync --no-install-project # Expose port EXPOSE 8000 -CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] \ No newline at end of file +CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index 3825668a..1ce5ff26 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -1,16 +1,15 @@ from typing import Annotated from uuid import UUID -from fastapi import Depends, HTTPException, status, Request -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from fastapi import Depends, HTTPException, Request, status +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select -from app.core.security import decode_token from app.core.database import get_session +from app.core.security import decode_token from app.models.user import User - security = HTTPBearer(auto_error=False) @@ -70,12 +69,12 @@ async def get_current_user( try: user_id = UUID(user_id_str) - except ValueError: + except ValueError as e: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid user ID in token", headers={"WWW-Authenticate": "Bearer"}, - ) + ) from e # Fetch user result = await session.execute(select(User).where(User.id == user_id)) diff --git a/backend/app/api/router.py b/backend/app/api/router.py index e2c4a844..5103bb12 100644 --- a/backend/app/api/router.py +++ b/backend/app/api/router.py @@ -1,4 +1,5 @@ from fastapi import APIRouter + from app.api.v1 import auth, users router = APIRouter() diff --git a/backend/app/api/v1/auth.py b/backend/app/api/v1/auth.py index 69034882..dea68e60 100644 --- a/backend/app/api/v1/auth.py +++ b/backend/app/api/v1/auth.py @@ -1,23 +1,22 @@ from typing import Annotated from uuid import UUID -from fastapi import APIRouter, Depends, HTTPException, status, Response, Request +from fastapi import APIRouter, Depends, HTTPException, Request, Response, status from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select +from app.api.deps import CurrentSuperuser, CurrentUser +from app.config import settings +from app.core.database import get_session from app.core.security import ( - verify_password, - hash_password, create_access_token, create_refresh_token, decode_token, + hash_password, + verify_password, ) -from app.config import settings -from app.core.database import get_session from app.models.user import User, UserCreate, UserRead from app.schemas.auth import LoginRequest, TokenResponse -from app.api.deps import CurrentUser, CurrentSuperuser - router = APIRouter(prefix="/auth", tags=["authentication"]) @@ -127,12 +126,12 @@ async def refresh_access_token( try: user_id = UUID(user_id_str) - except ValueError: + except ValueError as e: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid user ID in token", headers={"WWW-Authenticate": "Bearer"}, - ) + ) from e result = await session.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() @@ -180,7 +179,7 @@ async def get_current_user_info(current_user: CurrentUser): async def register_user( user_in: UserCreate, session: Annotated[AsyncSession, Depends(get_session)], - current_user: CurrentSuperuser, + current_user: CurrentSuperuser, # noqa: ARG001 ): """ Register a new user. **Admin only**. diff --git a/backend/app/api/v1/users.py b/backend/app/api/v1/users.py index a14673df..2b52fa07 100644 --- a/backend/app/api/v1/users.py +++ b/backend/app/api/v1/users.py @@ -1,13 +1,13 @@ from typing import Annotated +from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select +from app.api.deps import CurrentSuperuser from app.core.database import get_session from app.models.user import User, UserRead -from app.api.deps import CurrentSuperuser - router = APIRouter(prefix="/users", tags=["users"]) @@ -15,7 +15,7 @@ router = APIRouter(prefix="/users", tags=["users"]) @router.get("/", response_model=list[UserRead]) async def list_users( session: Annotated[AsyncSession, Depends(get_session)], - current_user: CurrentSuperuser, # Only superusers can list all users + current_user: CurrentSuperuser, # noqa: ARG001 skip: int = 0, limit: int = 100, ): @@ -26,29 +26,26 @@ async def list_users( # Get paginated users query = select(User).offset(skip).limit(limit) result = await session.execute(query) - users = result.scalars().all() - - return users + return result.scalars().all() @router.get("/{user_id}", response_model=UserRead) async def get_user( user_id: str, session: Annotated[AsyncSession, Depends(get_session)], - current_user: CurrentSuperuser, # superuser only + current_user: CurrentSuperuser, # noqa: ARG001 ): """ Get user by ID. **Admin only**. """ - from uuid import UUID try: uuid_id = UUID(user_id) - except ValueError: + except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid user ID format", - ) + ) from e result = await session.execute(select(User).where(User.id == uuid_id)) user = result.scalar_one_or_none() @@ -71,15 +68,14 @@ async def delete_user( """ Delete user by ID. **Admin only**. """ - from uuid import UUID try: uuid_id = UUID(user_id) - except ValueError: + except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid user ID format", - ) + ) from e # Prevent self-deletion if uuid_id == current_user.id: diff --git a/backend/app/config.py b/backend/app/config.py index 078ffbd6..f39aba1f 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -1,6 +1,8 @@ -from pydantic_settings import BaseSettings from functools import lru_cache +from pydantic import field_validator +from pydantic_settings import BaseSettings + class Settings(BaseSettings): ################################################################ @@ -29,7 +31,7 @@ class Settings(BaseSettings): ############################################################### # Auth configs ############################################################### - SECRET_KEY: str = "change-me-in-production-use-openssl-rand-hex-32" + SECRET_KEY: str = "change-me-in-production-use-openssl-rand-hex-32" # noqa: S105 ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 REFRESH_TOKEN_EXPIRE_DAYS: int = 7 @@ -39,7 +41,33 @@ class Settings(BaseSettings): ############################################################### ADMIN_EMAIL: str = "admin@rengine.local" ADMIN_USERNAME: str = "rengine" - ADMIN_PASSWORD: str = "rengine@123" + ADMIN_PASSWORD: str = "rengine@123" # noqa: S105 + + @field_validator("SECRET_KEY") + @classmethod + def validate_secret_key(cls, v: str, info) -> str: + """Ensure SECRET_KEY is not using default value in production.""" + if ( + not info.data.get("DEBUG", False) + and v == "change-me-in-production-use-openssl-rand-hex-32" + ): + return_error = ( + "SECRET_KEY must be set in production. " + "Generate one with: openssl rand -hex 32" + ) + raise ValueError(return_error) + return v + + @field_validator("ADMIN_PASSWORD") + @classmethod + def validate_admin_password(cls, v: str, info) -> str: + """Ensure ADMIN_PASSWORD is not using default value in production.""" + if not info.data.get("DEBUG", False) and v == "rengine@123": + return_error = ( + "ADMIN_PASSWORD must be changed from default value in production" + ) + raise ValueError(return_error) + return v class Config: env_file = ".env" diff --git a/backend/app/core/database.py b/backend/app/core/database.py index f88c3d5f..4d31e606 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -1,5 +1,6 @@ +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from sqlmodel import SQLModel -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker + from app.config import settings engine = create_async_engine( diff --git a/backend/app/core/logging.py b/backend/app/core/logging.py new file mode 100644 index 00000000..88259028 --- /dev/null +++ b/backend/app/core/logging.py @@ -0,0 +1,9 @@ +from shared.logging import setup_logging + +from app.config import settings + +logger = setup_logging( + name="rengine.backend", + level=settings.LOG_LEVEL if hasattr(settings, "LOG_LEVEL") else "INFO", + colored=True, +) diff --git a/backend/app/core/security.py b/backend/app/core/security.py index cdb4dc26..f2b18420 100644 --- a/backend/app/core/security.py +++ b/backend/app/core/security.py @@ -1,13 +1,12 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError -from jose import jwt, JWTError +from jose import JWTError, jwt from app.config import settings - ph = PasswordHasher( time_cost=2, memory_cost=65536, @@ -54,7 +53,7 @@ def create_token( expires_delta: timedelta, ) -> str: """Create a JWT token with the given subject and expiration.""" - expire = datetime.now(timezone.utc) + expires_delta + expire = datetime.now(UTC) + expires_delta to_encode = { "exp": expire, "sub": str(subject), @@ -67,7 +66,7 @@ def create_access_token(subject: str | Any) -> str: """Create an access token for the given subject.""" return create_token( subject=subject, - token_type="access", + token_type="access", # noqa: S106 expires_delta=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES), ) @@ -76,7 +75,7 @@ def create_refresh_token(subject: str | Any) -> str: """Create a refresh token for the given subject.""" return create_token( subject=subject, - token_type="refresh", + token_type="refresh", # noqa: S106 expires_delta=timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS), ) @@ -89,11 +88,10 @@ def decode_token(token: str) -> dict | None: Decoded payload dict if valid, None otherwise """ try: - payload = jwt.decode( + return jwt.decode( token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM], ) - return payload except JWTError: return None diff --git a/backend/app/main.py b/backend/app/main.py index 778fe1af..af75bded 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,22 +1,17 @@ from contextlib import asynccontextmanager + from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from app.config import settings -from app.utils.helpers import create_initial_admin -from app.core.database import init_db + from app.api.router import router as api_router -from shared.logging import setup_logging, get_logger - - -logger = setup_logging( - name="rengine.backend", - level=settings.LOG_LEVEL if hasattr(settings, "LOG_LEVEL") else "INFO", - colored=True, -) +from app.config import settings +from app.core.database import init_db +from app.core.logging import logger +from app.utils.helpers import create_initial_admin @asynccontextmanager -async def lifespan(app: FastAPI): +async def lifespan(_app: FastAPI): # app starts up logger.info("Starting Backend...") try: diff --git a/backend/app/models/project.py b/backend/app/models/project.py index de431081..194b8aa2 100644 --- a/backend/app/models/project.py +++ b/backend/app/models/project.py @@ -1,5 +1,6 @@ import uuid -from sqlmodel import SQLModel, Field + +from sqlmodel import Field, SQLModel class ProjectBase(SQLModel): diff --git a/backend/app/models/user.py b/backend/app/models/user.py index 66a6ee4b..c93c5d75 100644 --- a/backend/app/models/user.py +++ b/backend/app/models/user.py @@ -1,16 +1,13 @@ import uuid -from datetime import datetime, timezone -from typing import Optional import uuid as uuid_pkg +from datetime import UTC, datetime -from pydantic import field_validator, ValidationInfo -from sqlalchemy.dialects.postgresql import UUID -from sqlmodel import SQLModel, Field +from pydantic import ValidationInfo, field_validator +from sqlmodel import Field, SQLModel from zxcvbn import zxcvbn from app.config import settings - # Password policy MIN_PASSWORD_SCORE = 3 MIN_PASSWORD_LENGTH = 10 @@ -31,9 +28,9 @@ class User(UserBase, table=True): id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) hashed_password: str created_at: datetime = Field( - default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None) + default_factory=lambda: datetime.now(UTC).replace(tzinfo=None) ) - updated_at: Optional[datetime] = Field(default=None) + updated_at: datetime | None = Field(default=None) class UserCreate(SQLModel): @@ -49,9 +46,10 @@ class UserCreate(SQLModel): return password if len(password) < MIN_PASSWORD_LENGTH: - raise ValueError( + return_error = ( f"Password must be at least {MIN_PASSWORD_LENGTH} characters long" ) + raise ValueError(return_error) result = zxcvbn( password, @@ -59,13 +57,15 @@ class UserCreate(SQLModel): ) if result["score"] < MIN_PASSWORD_SCORE: - raise ValueError( + return_error = ( "Password is too weak. Consider using a stronger password with a " "mix of uppercase, lowercase, numbers, and special characters." ) + raise ValueError(return_error) - if result["guesses_log10"] < 3: - raise ValueError("This password is too common or easily guessable.") + if result["guesses_log10"] < MIN_PASSWORD_SCORE: + return_error = "Password is too guessable. Choose a less common password." + raise ValueError(return_error) return password @@ -73,4 +73,4 @@ class UserCreate(SQLModel): class UserRead(UserBase): id: uuid_pkg.UUID created_at: datetime - updated_at: Optional[datetime] = None + updated_at: datetime | None = None diff --git a/backend/app/utils/helpers.py b/backend/app/utils/helpers.py index ca2b9962..3fc71af9 100644 --- a/backend/app/utils/helpers.py +++ b/backend/app/utils/helpers.py @@ -1,8 +1,10 @@ from sqlmodel import select -from app.core.database import async_db_session -from app.models.user import User -from app.core.security import hash_password + from app.config import settings +from app.core.database import async_db_session +from app.core.logger import logger +from app.core.security import hash_password +from app.models.user import User async def create_initial_admin() -> None: @@ -20,4 +22,4 @@ async def create_initial_admin() -> None: ) session.add(admin) await session.commit() - print(f"Initial admin created: {settings.ADMIN_USERNAME}") + logger.info(f"Initial admin created: {settings.ADMIN_USERNAME}") diff --git a/frontend/src/lib/assets/favicon.svg b/frontend/src/lib/assets/favicon.svg index cc5dc66a..fc8ed39d 100644 --- a/frontend/src/lib/assets/favicon.svg +++ b/frontend/src/lib/assets/favicon.svg @@ -1 +1 @@ -svelte-logo \ No newline at end of file +svelte-logo diff --git a/frontend/src/lib/components/layout/index.ts b/frontend/src/lib/components/layout/index.ts index 81a888ef..4d9c864c 100644 --- a/frontend/src/lib/components/layout/index.ts +++ b/frontend/src/lib/components/layout/index.ts @@ -1 +1 @@ -export { default as Navbar } from './navbar.svelte'; \ No newline at end of file +export { default as Navbar } from './navbar.svelte'; diff --git a/frontend/src/lib/components/ui/index.ts b/frontend/src/lib/components/ui/index.ts index 344cc423..83addf61 100644 --- a/frontend/src/lib/components/ui/index.ts +++ b/frontend/src/lib/components/ui/index.ts @@ -1,3 +1,3 @@ export * from './button'; export * from './input'; -export * from './card'; \ No newline at end of file +export * from './card'; diff --git a/frontend/src/routes/layout.css b/frontend/src/routes/layout.css index e48f5f09..c6510746 100644 --- a/frontend/src/routes/layout.css +++ b/frontend/src/routes/layout.css @@ -118,4 +118,4 @@ body { @apply bg-background text-foreground; } -} \ No newline at end of file +} diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 8fffb076..b77f9b71 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -15,4 +15,4 @@ const config = { } }; -export default config; \ No newline at end of file +export default config; diff --git a/shared/.gitignore b/shared/.gitignore index eb068ff4..fe09b614 100644 --- a/shared/.gitignore +++ b/shared/.gitignore @@ -126,4 +126,4 @@ dmypy.json .DS_Store # FastAPI specific -.uvicorn_cache/ \ No newline at end of file +.uvicorn_cache/ diff --git a/shared/logging.py b/shared/logging.py index c3188af1..9089ea0b 100644 --- a/shared/logging.py +++ b/shared/logging.py @@ -21,11 +21,11 @@ class ColoredFormatter(logging.Formatter): 'CRITICAL': '\033[35m', 'RESET': '\033[0m', } - + def format(self, record: logging.LogRecord) -> str: color = self.COLORS.get(record.levelname, self.COLORS['RESET']) reset = self.COLORS['RESET'] - record.levelname = f"{color}{record.levelname}{reset}" + record.levelname = f"{color}{record.levelname}{reset}" return super().format(record) @@ -36,24 +36,24 @@ def setup_logging( ) -> logging.Logger: """ Set up logging configuration. - + Args: name: Logger name (e.g., "rengine.backend", "rengine.worker") level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) colored: Whether to use colored output for console - + Returns: Configured logger instance - + """ logger = logging.getLogger(name) logger.setLevel(getattr(logging, level.upper())) - + logger.handlers.clear() - + console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.DEBUG) - + if colored: console_format = ColoredFormatter( fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", @@ -64,12 +64,12 @@ def setup_logging( fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) - + console_handler.setFormatter(console_format) logger.addHandler(console_handler) - + return logger def get_logger(name: str) -> logging.Logger: - return logging.getLogger(name) \ No newline at end of file + return logging.getLogger(name)