diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..7bbc7a7c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +.env +.env.* +!.env.example +.git +.gitea +.gitlab-ci.yml +__pycache__ +*.pyc +*.pyo +site/ +docs/ +exports/ +alembic/versions_backup/ +*.csv +*.md +!requirements.txt +.pre-commit-config.yaml +.architecture-rules/ +.performance-rules/ +.security-rules/ +mkdocs.yml diff --git a/app/modules/customers/routes/api/storefront.py b/app/modules/customers/routes/api/storefront.py index d10ca502..7d3c0d87 100644 --- a/app/modules/customers/routes/api/storefront.py +++ b/app/modules/customers/routes/api/storefront.py @@ -317,7 +317,7 @@ def forgot_password(request: Request, email: str, db: Session = Depends(get_db)) ) except Exception as e: db.rollback() - logger.error(f"Failed to send password reset email: {e}") + logger.error(f"Failed to send password reset email: {e}") # noqa: SEC-021 else: logger.info( f"Password reset requested for non-existent email {email} (store: {store.subdomain})" diff --git a/app/modules/customers/services/customer_service.py b/app/modules/customers/services/customer_service.py index 1dbc75ef..765bf904 100644 --- a/app/modules/customers/services/customer_service.py +++ b/app/modules/customers/services/customer_service.py @@ -570,7 +570,7 @@ class CustomerService: # Mark token as used token_record.mark_used(db) - logger.info(f"Password reset completed for customer {customer.id}") + logger.info(f"Password reset completed for customer {customer.id}") # noqa: SEC-021 return customer diff --git a/app/modules/customers/tests/unit/test_admin_customer_service.py b/app/modules/customers/tests/unit/test_admin_customer_service.py index 175d5ee8..19cfc86e 100644 --- a/app/modules/customers/tests/unit/test_admin_customer_service.py +++ b/app/modules/customers/tests/unit/test_admin_customer_service.py @@ -36,7 +36,7 @@ def multiple_customers(db, test_store): customer = Customer( store_id=test_store.id, email=f"customer{i}@example.com", - hashed_password="hashed_password_placeholder", + hashed_password="hashed_password_placeholder", # noqa: SEC-001 first_name=f"First{i}", last_name=f"Last{i}", customer_number=f"CUST-00{i}", diff --git a/app/modules/customers/tests/unit/test_customer_model.py b/app/modules/customers/tests/unit/test_customer_model.py index a1c936d6..28d24e53 100644 --- a/app/modules/customers/tests/unit/test_customer_model.py +++ b/app/modules/customers/tests/unit/test_customer_model.py @@ -16,7 +16,7 @@ class TestCustomerModel: customer = Customer( store_id=test_store.id, email="customer@example.com", - hashed_password="hashed_password", + hashed_password="hashed_password", # noqa: SEC-001 first_name="John", last_name="Doe", customer_number="CUST001", @@ -40,7 +40,7 @@ class TestCustomerModel: customer = Customer( store_id=test_store.id, email="defaults@example.com", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 customer_number="CUST_DEFAULTS", ) db.add(customer) @@ -57,7 +57,7 @@ class TestCustomerModel: customer = Customer( store_id=test_store.id, email="fullname@example.com", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 customer_number="CUST_FULLNAME", first_name="Jane", last_name="Smith", @@ -73,7 +73,7 @@ class TestCustomerModel: customer = Customer( store_id=test_store.id, email="noname@example.com", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 customer_number="CUST_NONAME", ) db.add(customer) @@ -87,7 +87,7 @@ class TestCustomerModel: customer = Customer( store_id=test_store.id, email="optional@example.com", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 customer_number="CUST_OPT", phone="+352123456789", preferences={"language": "en", "currency": "EUR"}, @@ -106,7 +106,7 @@ class TestCustomerModel: customer = Customer( store_id=test_store.id, email="relationship@example.com", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 customer_number="CUST_REL", ) db.add(customer) diff --git a/app/modules/customers/tests/unit/test_customer_schema.py b/app/modules/customers/tests/unit/test_customer_schema.py index c2d7cded..8fb10c28 100644 --- a/app/modules/customers/tests/unit/test_customer_schema.py +++ b/app/modules/customers/tests/unit/test_customer_schema.py @@ -24,7 +24,7 @@ class TestCustomerRegisterSchema: """Test valid registration data.""" customer = CustomerRegister( email="customer@example.com", - password="Password123", + password="Password123", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -36,7 +36,7 @@ class TestCustomerRegisterSchema: """Test email is normalized to lowercase.""" customer = CustomerRegister( email="Customer@Example.COM", - password="Password123", + password="Password123", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -47,7 +47,7 @@ class TestCustomerRegisterSchema: with pytest.raises(ValidationError) as exc_info: CustomerRegister( email="not-an-email", - password="Password123", + password="Password123", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -58,7 +58,7 @@ class TestCustomerRegisterSchema: with pytest.raises(ValidationError) as exc_info: CustomerRegister( email="customer@example.com", - password="Pass1", + password="Pass1", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -69,7 +69,7 @@ class TestCustomerRegisterSchema: with pytest.raises(ValidationError) as exc_info: CustomerRegister( email="customer@example.com", - password="Password", + password="Password", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -80,7 +80,7 @@ class TestCustomerRegisterSchema: with pytest.raises(ValidationError) as exc_info: CustomerRegister( email="customer@example.com", - password="12345678", + password="12345678", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -91,7 +91,7 @@ class TestCustomerRegisterSchema: with pytest.raises(ValidationError) as exc_info: CustomerRegister( email="customer@example.com", - password="Password123", + password="Password123", # noqa: SEC-001 last_name="Doe", ) assert "first_name" in str(exc_info.value).lower() @@ -100,7 +100,7 @@ class TestCustomerRegisterSchema: """Test marketing_consent defaults to False.""" customer = CustomerRegister( email="customer@example.com", - password="Password123", + password="Password123", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -110,7 +110,7 @@ class TestCustomerRegisterSchema: """Test optional phone field.""" customer = CustomerRegister( email="customer@example.com", - password="Password123", + password="Password123", # noqa: SEC-001 first_name="John", last_name="Doe", phone="+352 123 456", diff --git a/app/modules/messaging/static/admin/js/email-templates.js b/app/modules/messaging/static/admin/js/email-templates.js index f8791fa0..176f7d8f 100644 --- a/app/modules/messaging/static/admin/js/email-templates.js +++ b/app/modules/messaging/static/admin/js/email-templates.js @@ -224,7 +224,7 @@ function emailTemplatesPage() { }, 'password_reset': { customer_name: 'John Doe', - reset_link: 'https://example.com/reset?token=abc123', + reset_link: 'https://example.com/reset?token=abc123', // # noqa: SEC-022 expiry_hours: '1' }, 'team_invite': { diff --git a/app/modules/messaging/tests/unit/test_store_email_settings_service.py b/app/modules/messaging/tests/unit/test_store_email_settings_service.py index 0ff14aa3..38bcec20 100644 --- a/app/modules/messaging/tests/unit/test_store_email_settings_service.py +++ b/app/modules/messaging/tests/unit/test_store_email_settings_service.py @@ -33,7 +33,7 @@ def test_email_settings(db, test_store): smtp_host="smtp.example.com", smtp_port=587, smtp_username="testuser", - smtp_password="testpass", + smtp_password="testpass", # noqa: SEC-001 smtp_use_tls=True, smtp_use_ssl=False, is_configured=True, @@ -56,7 +56,7 @@ def test_verified_email_settings(db, test_store): smtp_host="smtp.example.com", smtp_port=587, smtp_username="testuser", - smtp_password="testpass", + smtp_password="testpass", # noqa: SEC-001 smtp_use_tls=True, is_configured=True, is_verified=True, @@ -155,7 +155,7 @@ class TestStoreEmailSettingsWrite: "smtp_host": "smtp.example.com", "smtp_port": 587, "smtp_username": "user", - "smtp_password": "pass", + "smtp_password": "pass", # noqa: SEC-001 } settings = store_email_settings_service.create_or_update( diff --git a/app/modules/monitoring/static/admin/js/logs.js b/app/modules/monitoring/static/admin/js/logs.js index ddeb5631..544033ff 100644 --- a/app/modules/monitoring/static/admin/js/logs.js +++ b/app/modules/monitoring/static/admin/js/logs.js @@ -197,7 +197,7 @@ function adminLogs() { const token = localStorage.getItem('admin_token'); // Note: window.open bypasses apiClient, so we need the full path const url = `/api/v1/admin/logs/files/${this.selectedFile}/download`; - window.open(`${url}?token=${token}`, '_blank'); // noqa: sec-022 + window.open(`${url}?token=${token}`, '_blank'); // # noqa: SEC-022 } catch (error) { logsLog.error('Failed to download log file:', error); this.error = 'Failed to download log file'; diff --git a/app/modules/tenancy/tests/unit/test_admin_platform_service.py b/app/modules/tenancy/tests/unit/test_admin_platform_service.py index 637ab122..a412a5e9 100644 --- a/app/modules/tenancy/tests/unit/test_admin_platform_service.py +++ b/app/modules/tenancy/tests/unit/test_admin_platform_service.py @@ -255,7 +255,7 @@ class TestAdminPlatformServiceQueries: another_admin = User( email="another_padmin@example.com", username="another_padmin", - hashed_password=auth_manager.hash_password("pass"), + hashed_password=auth_manager.hash_password("pass"), # noqa: SEC-001 role="admin", is_active=True, is_super_admin=False, @@ -342,7 +342,7 @@ class TestAdminPlatformServiceSuperAdmin: another_super = User( email="another_super@example.com", username="another_super", - hashed_password=auth_manager.hash_password("pass"), + hashed_password=auth_manager.hash_password("pass"), # noqa: SEC-001 role="admin", is_active=True, is_super_admin=True, @@ -416,7 +416,7 @@ class TestAdminPlatformServiceCreatePlatformAdmin: db=db, email="new_padmin@example.com", username="new_padmin", - password="securepass123", + password="securepass123", # noqa: SEC-001 platform_ids=[test_platform.id, another_platform.id], created_by_user_id=test_super_admin.id, first_name="New", @@ -444,7 +444,7 @@ class TestAdminPlatformServiceCreatePlatformAdmin: db=db, email=test_platform_admin.email, # Duplicate username="unique_username", - password="securepass123", + password="securepass123", # noqa: SEC-001 platform_ids=[test_platform.id], created_by_user_id=test_super_admin.id, ) @@ -461,7 +461,7 @@ class TestAdminPlatformServiceCreatePlatformAdmin: db=db, email="unique@example.com", username=test_platform_admin.username, # Duplicate - password="securepass123", + password="securepass123", # noqa: SEC-001 platform_ids=[test_platform.id], created_by_user_id=test_super_admin.id, ) diff --git a/app/modules/tenancy/tests/unit/test_store_team_service.py b/app/modules/tenancy/tests/unit/test_store_team_service.py index 10b4ec85..1fad399d 100644 --- a/app/modules/tenancy/tests/unit/test_store_team_service.py +++ b/app/modules/tenancy/tests/unit/test_store_team_service.py @@ -87,7 +87,7 @@ def pending_invitation(db, team_store, test_user, auth_manager): new_user = User( email=f"pending_{unique_id}@example.com", username=f"pending_{unique_id}", - hashed_password=auth_manager.hash_password("temppass"), + hashed_password=auth_manager.hash_password("temppass"), # noqa: SEC-001 role="store", is_active=False, ) @@ -129,7 +129,7 @@ def expired_invitation(db, team_store, test_user, auth_manager): new_user = User( email=f"expired_{unique_id}@example.com", username=f"expired_{unique_id}", - hashed_password=auth_manager.hash_password("temppass"), + hashed_password=auth_manager.hash_password("temppass"), # noqa: SEC-001 role="store", is_active=False, ) @@ -186,7 +186,7 @@ class TestStoreTeamServiceAccept: result = store_team_service.accept_invitation( db=db, invitation_token=pending_invitation.invitation_token, - password="newpassword123", + password="newpassword123", # noqa: SEC-001 first_name="John", last_name="Doe", ) @@ -203,7 +203,7 @@ class TestStoreTeamServiceAccept: store_team_service.accept_invitation( db=db, invitation_token="invalid_token_12345", - password="password123", + password="password123", # noqa: SEC-001 ) def test_accept_invitation_already_accepted(self, db, team_member): @@ -213,7 +213,7 @@ class TestStoreTeamServiceAccept: store_team_service.accept_invitation( db=db, invitation_token="some_token", # team_member has no token - password="password123", + password="password123", # noqa: SEC-001 ) def test_accept_invitation_expired(self, db, expired_invitation): @@ -222,7 +222,7 @@ class TestStoreTeamServiceAccept: store_team_service.accept_invitation( db=db, invitation_token=expired_invitation.invitation_token, - password="password123", + password="password123", # noqa: SEC-001 ) assert "expired" in str(exc_info.value).lower() diff --git a/app/modules/tenancy/tests/unit/test_user_model.py b/app/modules/tenancy/tests/unit/test_user_model.py index 56ec7619..d9c0dc26 100644 --- a/app/modules/tenancy/tests/unit/test_user_model.py +++ b/app/modules/tenancy/tests/unit/test_user_model.py @@ -17,7 +17,7 @@ class TestUserModel: user = User( email="db_test@example.com", username="dbtest", - hashed_password="hashed_password_123", + hashed_password="hashed_password_123", # noqa: SEC-001 role="user", is_active=True, ) @@ -39,7 +39,7 @@ class TestUserModel: user1 = User( email="unique@example.com", username="user1", - hashed_password="hash1", + hashed_password="hash1", # noqa: SEC-001 ) db.add(user1) db.commit() @@ -49,7 +49,7 @@ class TestUserModel: user2 = User( email="unique@example.com", username="user2", - hashed_password="hash2", + hashed_password="hash2", # noqa: SEC-001 ) db.add(user2) db.commit() @@ -59,7 +59,7 @@ class TestUserModel: user1 = User( email="user1@example.com", username="sameusername", - hashed_password="hash1", + hashed_password="hash1", # noqa: SEC-001 ) db.add(user1) db.commit() @@ -69,7 +69,7 @@ class TestUserModel: user2 = User( email="user2@example.com", username="sameusername", - hashed_password="hash2", + hashed_password="hash2", # noqa: SEC-001 ) db.add(user2) db.commit() @@ -79,7 +79,7 @@ class TestUserModel: user = User( email="defaults@example.com", username="defaultuser", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 ) db.add(user) db.commit() @@ -93,7 +93,7 @@ class TestUserModel: user = User( email="optional@example.com", username="optionaluser", - hashed_password="hash", + hashed_password="hash", # noqa: SEC-001 first_name="John", last_name="Doe", ) diff --git a/conftest.py b/conftest.py index 4820a03b..8ad4e585 100644 --- a/conftest.py +++ b/conftest.py @@ -128,7 +128,7 @@ def db(engine, testing_session_local): # Disable FK checks temporarily for fast truncation conn.execute(text("SET session_replication_role = 'replica'")) for table in reversed(Base.metadata.sorted_tables): - conn.execute(text(f'TRUNCATE TABLE "{table.name}" CASCADE')) + conn.execute(text(f'TRUNCATE TABLE "{table.name}" CASCADE')) # noqa: SEC-011 conn.execute(text("SET session_replication_role = 'origin'")) conn.commit() diff --git a/docker-compose.yml b/docker-compose.yml index 367ec500..efb7506d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,7 @@ services: - full # Only start with: docker compose --profile full up -d ports: - "8001:8000" # Use 8001 to avoid conflict with local dev server + env_file: .env environment: DATABASE_URL: postgresql://orion_user:secure_password@db:5432/orion_db JWT_SECRET_KEY: ${JWT_SECRET_KEY:-your-super-secret-key} @@ -62,6 +63,7 @@ services: profiles: - full # Only start with: docker compose --profile full up -d command: celery -A app.core.celery_config worker --loglevel=info -Q default,long_running,scheduled + env_file: .env environment: DATABASE_URL: postgresql://orion_user:secure_password@db:5432/orion_db REDIS_URL: redis://redis:6379/0 diff --git a/scripts/seed/install.py b/scripts/seed/install.py index ad383d8a..2e3fda39 100755 --- a/scripts/seed/install.py +++ b/scripts/seed/install.py @@ -576,10 +576,10 @@ def main(): admin_email = env_vars.get("ADMIN_EMAIL", "admin@orion.lu") print(" URL: /admin/login") print(f" Email: {admin_email}") - print(f" Password: {'(configured in .env)' if env_vars.get('ADMIN_PASSWORD') else 'admin123'}") + print(f" Password: {'(configured in .env)' if env_vars.get('ADMIN_PASSWORD') else 'admin123'}") # noqa: SEC-021 if not env_vars.get("ADMIN_PASSWORD"): - print(f"\n {Colors.WARNING}⚠ CHANGE DEFAULT PASSWORD IMMEDIATELY!{Colors.ENDC}") + print(f"\n {Colors.WARNING}⚠ CHANGE DEFAULT PASSWORD IMMEDIATELY!{Colors.ENDC}") # noqa: SEC-021 print(f"\n {Colors.BOLD}For demo data (development only):{Colors.ENDC}") print(" make seed-demo") diff --git a/scripts/seed/seed_demo.py b/scripts/seed/seed_demo.py index 8d497fe6..f8cb8149 100644 --- a/scripts/seed/seed_demo.py +++ b/scripts/seed/seed_demo.py @@ -103,7 +103,7 @@ DEMO_COMPANIES = [ "name": "WizaCorp Ltd.", "description": "Leading technology and electronics distributor", "owner_email": "john.owner@wizacorp.com", - "owner_password": "password123", + "owner_password": "password123", # noqa: SEC-001 "owner_first_name": "John", "owner_last_name": "Smith", "contact_email": "info@wizacorp.com", @@ -116,7 +116,7 @@ DEMO_COMPANIES = [ "name": "Fashion Group S.A.", "description": "International fashion and lifestyle retailer", "owner_email": "jane.owner@fashiongroup.com", - "owner_password": "password123", + "owner_password": "password123", # noqa: SEC-001 "owner_first_name": "Jane", "owner_last_name": "Merchant", "contact_email": "contact@fashiongroup.com", @@ -129,7 +129,7 @@ DEMO_COMPANIES = [ "name": "BookWorld Publishing", "description": "Books, education, and media content provider", "owner_email": "bob.owner@bookworld.com", - "owner_password": "password123", + "owner_password": "password123", # noqa: SEC-001 "owner_first_name": "Bob", "owner_last_name": "Seller", "contact_email": "support@bookworld.com", @@ -213,7 +213,7 @@ DEMO_TEAM_MEMBERS = [ { "merchant_index": 0, "email": "alice.manager@wizacorp.com", - "password": "password123", + "password": "password123", # noqa: SEC-001 "first_name": "Alice", "last_name": "Manager", "store_codes": ["ORION", "WIZAGADGETS"], # manages two stores @@ -222,7 +222,7 @@ DEMO_TEAM_MEMBERS = [ { "merchant_index": 0, "email": "charlie.staff@wizacorp.com", - "password": "password123", + "password": "password123", # noqa: SEC-001 "first_name": "Charlie", "last_name": "Staff", "store_codes": ["WIZAHOME"], @@ -232,7 +232,7 @@ DEMO_TEAM_MEMBERS = [ { "merchant_index": 1, "email": "diana.stylist@fashiongroup.com", - "password": "password123", + "password": "password123", # noqa: SEC-001 "first_name": "Diana", "last_name": "Stylist", "store_codes": ["FASHIONHUB", "FASHIONOUTLET"], @@ -241,7 +241,7 @@ DEMO_TEAM_MEMBERS = [ { "merchant_index": 1, "email": "eric.sales@fashiongroup.com", - "password": "password123", + "password": "password123", # noqa: SEC-001 "first_name": "Eric", "last_name": "Sales", "store_codes": ["FASHIONOUTLET"], @@ -251,7 +251,7 @@ DEMO_TEAM_MEMBERS = [ { "merchant_index": 2, "email": "fiona.editor@bookworld.com", - "password": "password123", + "password": "password123", # noqa: SEC-001 "first_name": "Fiona", "last_name": "Editor", "store_codes": ["BOOKSTORE", "BOOKDIGITAL"], @@ -615,7 +615,7 @@ def create_demo_merchants(db: Session, auth_manager: AuthManager) -> list[Mercha owner_user = User( username=merchant_data["owner_email"].split("@")[0], email=merchant_data["owner_email"], - hashed_password=auth_manager.hash_password( + hashed_password=auth_manager.hash_password( # noqa: SEC-001 merchant_data["owner_password"] ), role="store", @@ -780,7 +780,7 @@ def create_demo_team_members( user = User( username=member_data["email"].split("@")[0], email=member_data["email"], - hashed_password=auth_manager.hash_password(member_data["password"]), + hashed_password=auth_manager.hash_password(member_data["password"]), # noqa: SEC-001 role="store", first_name=member_data["first_name"], last_name=member_data["last_name"], @@ -838,7 +838,7 @@ def create_demo_customers( customers = [] # Use a simple demo password for all customers - demo_password = "customer123" + demo_password = "customer123" # noqa: SEC-001 for i in range(1, count + 1): email = f"customer{i}@{store.subdomain}.example.com" @@ -858,7 +858,7 @@ def create_demo_customers( customer = Customer( store_id=store.id, email=email, - hashed_password=auth_manager.hash_password(demo_password), + hashed_password=auth_manager.hash_password(demo_password), # noqa: SEC-001 first_name=f"Customer{i}", last_name="Test", phone=f"+352123456{i:03d}", @@ -1178,7 +1178,7 @@ def print_summary(db: Session): merchant = merchants[i - 1] if i <= len(merchants) else None print(f" Merchant {i}: {merchant_data['name']}") print(f" Email: {merchant_data['owner_email']}") - print(f" Password: {merchant_data['owner_password']}") + print(f" Password: {merchant_data['owner_password']}") # noqa: SEC-021 if merchant and merchant.stores: for store in merchant.stores: print( @@ -1196,7 +1196,7 @@ def print_summary(db: Session): store_codes = ", ".join(member_data["store_codes"]) print(f" {member_data['first_name']} {member_data['last_name']} ({merchant_name})") print(f" Email: {member_data['email']}") - print(f" Password: {member_data['password']}") + print(f" Password: {member_data['password']}") # noqa: SEC-021 print(f" Stores: {store_codes}") print() @@ -1204,7 +1204,7 @@ def print_summary(db: Session): print("─" * 70) print(" All customers:") print(" Email: customer1@{subdomain}.example.com") - print(" Password: customer123") + print(" Password: customer123") # noqa: SEC-021 print(" (Replace {subdomain} with store subdomain, e.g., orion)") print() @@ -1228,7 +1228,7 @@ def print_summary(db: Session): print(" 3. Visit store shop: http://localhost:8000/stores/ORION/shop/") print(" 4. Admin panel: http://localhost:8000/admin/login") print(f" Username: {settings.admin_username}") - print(f" Password: {settings.admin_password}") + print(f" Password: {settings.admin_password}") # noqa: SEC-021 # ============================================================================= diff --git a/scripts/test_store_management.py b/scripts/test_store_management.py index 60f69e94..5d88f692 100644 --- a/scripts/test_store_management.py +++ b/scripts/test_store_management.py @@ -72,7 +72,7 @@ def test_create_store_with_both_emails(): print(f" Contact Email: {data['contact_email']}") print("\nšŸ”‘ Credentials:") print(f" Username: {data['owner_username']}") - print(f" Password: {data['temporary_password']}") + print(f" Password: {data['temporary_password']}") # noqa: SEC-021 print(f"\nšŸ”— Login URL: {data['login_url']}") return data["id"] print(f"āŒ Failed: {response.status_code}") diff --git a/scripts/validate/base_validator.py b/scripts/validate/base_validator.py index 139fe60b..9a860173 100755 --- a/scripts/validate/base_validator.py +++ b/scripts/validate/base_validator.py @@ -4,6 +4,7 @@ Base Validator Class Shared functionality for all validators. """ +import re from abc import ABC from dataclasses import dataclass, field from enum import Enum @@ -62,8 +63,18 @@ class BaseValidator(ABC): ".venv", "venv", "node_modules", "__pycache__", ".git", ".pytest_cache", ".mypy_cache", "dist", "build", "*.egg-info", "migrations", "alembic/versions", ".tox", "htmlcov", + "site", # mkdocs build output ] + # Regex for noqa comments: # noqa, # noqa: RULE-001, # noqa: RULE-001, RULE-002 + _NOQA_PATTERN = re.compile( + r"#\s*noqa(?::\s*([A-Z]+-\d+(?:\s*,\s*[A-Z]+-\d+)*))?", + ) + # Same for HTML comments: + _NOQA_HTML_PATTERN = re.compile( + r"", + ) + def __init__( self, rules_dir: str = "", @@ -180,6 +191,26 @@ class BaseValidator(ABC): path_str = str(file_path) return any(pattern in path_str for pattern in self.IGNORE_PATTERNS) + def _is_noqa_suppressed(self, line: str, rule_id: str) -> bool: + """Check if a line has a noqa comment suppressing the given rule. + + Supports: + - ``# noqa`` — suppresses all rules + - ``# noqa: SEC-001`` — suppresses specific rule + - ``# noqa: SEC-001, SEC-002`` — suppresses multiple rules + - ```` — HTML comment variant + """ + for pattern in (self._NOQA_PATTERN, self._NOQA_HTML_PATTERN): + match = pattern.search(line) + if match: + rule_list = match.group(1) + if not rule_list: + return True # bare # noqa → suppress everything + suppressed = [r.strip() for r in rule_list.split(",")] + if rule_id in suppressed: + return True + return False + def _add_violation( self, rule_id: str, diff --git a/scripts/validate/validate_audit.py b/scripts/validate/validate_audit.py index d4d864c9..d7648d53 100644 --- a/scripts/validate/validate_audit.py +++ b/scripts/validate/validate_audit.py @@ -196,7 +196,7 @@ class AuditValidator(BaseValidator): r"logger\.\w+\(.*password\s*[=:]\s*['\"]?%", # password=%s r"logger\.\w+\(.*password\s*[=:]\s*\{", # password={var} r"logging\.\w+\(.*password\s*[=:]\s*['\"]?%", # password=%s - r"print\(.*password\s*=", # print(password=xxx) + r"print\(.*password\s*=", # print(password=xxx) # noqa: SEC-021 r"logger.*credit.*card.*\d", # credit card with numbers r"logger.*\bssn\b.*\d", # SSN with numbers ], diff --git a/scripts/validate/validate_performance.py b/scripts/validate/validate_performance.py index 348b79fa..c0241817 100755 --- a/scripts/validate/validate_performance.py +++ b/scripts/validate/validate_performance.py @@ -199,6 +199,8 @@ class PerformanceValidator(BaseValidator): if re.search(r"\.\w+\.\w+", line) and "(" not in line: # Could be accessing a relationship if any(rel in line for rel in [".customer.", ".store.", ".order.", ".product.", ".user."]): + if self._is_noqa_suppressed(line, "PERF-001"): + continue self._add_violation( rule_id="PERF-001", rule_name="N+1 query detection", @@ -225,7 +227,7 @@ class PerformanceValidator(BaseValidator): context_text = "\n".join(context_lines) if "limit" not in context_text.lower() and "filter" not in context_text.lower(): - if "# noqa" in line or "# bounded" in line: + if self._is_noqa_suppressed(line, "PERF-003") or "# bounded" in line: continue self._add_violation( rule_id="PERF-003", @@ -256,6 +258,8 @@ class PerformanceValidator(BaseValidator): if current_indent <= for_indent and stripped: in_for_loop = False elif "db.add(" in line or ".save(" in line: + if self._is_noqa_suppressed(line, "PERF-006"): + continue self._add_violation( rule_id="PERF-006", rule_name="Bulk operations for multiple records", @@ -278,6 +282,8 @@ class PerformanceValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern, issue in patterns: if re.search(pattern, line): + if self._is_noqa_suppressed(line, "PERF-008"): + continue self._add_violation( rule_id="PERF-008", rule_name="Use EXISTS for existence checks", @@ -311,17 +317,18 @@ class PerformanceValidator(BaseValidator): in_for_loop = False elif loop_var and f"{loop_var}." in line and "=" in line and "==" not in line: # Attribute assignment in loop - if "# noqa" not in line: - self._add_violation( - rule_id="PERF-009", - rule_name="Batch updates instead of loops", - severity=Severity.INFO, - file_path=file_path, - line_number=i, - message="Individual updates in loop - consider batch update", - context=line.strip()[:80], - suggestion="Use .update({...}) with filters for batch updates", - ) + if self._is_noqa_suppressed(line, "PERF-009"): + continue + self._add_violation( + rule_id="PERF-009", + rule_name="Batch updates instead of loops", + severity=Severity.INFO, + file_path=file_path, + line_number=i, + message="Individual updates in loop - consider batch update", + context=line.strip()[:80], + suggestion="Use .update({...}) with filters for batch updates", + ) # ========================================================================= # API Performance Checks @@ -349,17 +356,18 @@ class PerformanceValidator(BaseValidator): in_endpoint = False # Check for .all() without pagination if ".all()" in line and not has_pagination: - if "# noqa" not in line: - self._add_violation( - rule_id="PERF-026", - rule_name="Pagination required for list endpoints", - severity=Severity.WARNING, - file_path=file_path, - line_number=i, - message="List endpoint may lack pagination", - context=line.strip()[:80], - suggestion="Add skip/limit parameters for pagination", - ) + if self._is_noqa_suppressed(line, "PERF-026"): + continue + self._add_violation( + rule_id="PERF-026", + rule_name="Pagination required for list endpoints", + severity=Severity.WARNING, + file_path=file_path, + line_number=i, + message="List endpoint may lack pagination", + context=line.strip()[:80], + suggestion="Add skip/limit parameters for pagination", + ) # ========================================================================= # Async Performance Checks @@ -381,6 +389,10 @@ class PerformanceValidator(BaseValidator): if await_count >= 3: # Verify they're sequential (within 5 lines of each other) if all(await_lines[j+1] - await_lines[j] <= 2 for j in range(len(await_lines)-1)): + if self._is_noqa_suppressed(line, "PERF-037"): + await_count = 0 + await_lines = [] + continue self._add_violation( rule_id="PERF-037", rule_name="Parallel independent operations", @@ -412,6 +424,8 @@ class PerformanceValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern in patterns: if re.search(pattern, line) and "timeout" not in line: + if self._is_noqa_suppressed(line, "PERF-040"): + continue self._add_violation( rule_id="PERF-040", rule_name="Timeout configuration", @@ -436,22 +450,25 @@ class PerformanceValidator(BaseValidator): if i < len(lines): next_lines = "\n".join(lines[i:min(i+3, len(lines))]) if "for " in next_lines and "in" in next_lines: - if "# noqa" not in line: - self._add_violation( - rule_id="PERF-046", - rule_name="Generators for large datasets", - severity=Severity.INFO, - file_path=file_path, - line_number=i, - message=".all() loads everything into memory before iteration", - context=line.strip()[:80], - suggestion="Use .yield_per(100) for large result sets", - ) + if self._is_noqa_suppressed(line, "PERF-046"): + continue + self._add_violation( + rule_id="PERF-046", + rule_name="Generators for large datasets", + severity=Severity.INFO, + file_path=file_path, + line_number=i, + message=".all() loads everything into memory before iteration", + context=line.strip()[:80], + suggestion="Use .yield_per(100) for large result sets", + ) def _check_file_streaming(self, file_path: Path, content: str, lines: list[str]): """PERF-047: Check for loading entire files into memory""" for i, line in enumerate(lines, 1): if re.search(r"await\s+\w+\.read\(\)", line) and "chunk" not in line: + if self._is_noqa_suppressed(line, "PERF-047"): + continue self._add_violation( rule_id="PERF-047", rule_name="Stream large file uploads", @@ -468,6 +485,9 @@ class PerformanceValidator(BaseValidator): if "chunk" not in content.lower() and "batch" not in content.lower(): # Check if file processes multiple records if "for " in content and ("csv" in content.lower() or "import" in content.lower()): + first_line = lines[0] if lines else "" + if self._is_noqa_suppressed(first_line, "PERF-048"): + return self._add_violation( rule_id="PERF-048", rule_name="Chunked processing for imports", @@ -484,17 +504,18 @@ class PerformanceValidator(BaseValidator): for i, line in enumerate(lines, 1): # Check for file open without 'with' if re.search(r"^\s*\w+\s*=\s*open\s*\(", line): - if "# noqa" not in line: - self._add_violation( - rule_id="PERF-049", - rule_name="Context managers for resources", - severity=Severity.WARNING, - file_path=file_path, - line_number=i, - message="File opened without context manager", - context=line.strip()[:80], - suggestion="Use 'with open(...) as f:' to ensure cleanup", - ) + if self._is_noqa_suppressed(line, "PERF-049"): + continue + self._add_violation( + rule_id="PERF-049", + rule_name="Context managers for resources", + severity=Severity.WARNING, + file_path=file_path, + line_number=i, + message="File opened without context manager", + context=line.strip()[:80], + suggestion="Use 'with open(...) as f:' to ensure cleanup", + ) def _check_string_concatenation(self, file_path: Path, content: str, lines: list[str]): """PERF-051: Check for inefficient string concatenation in loops""" @@ -513,17 +534,18 @@ class PerformanceValidator(BaseValidator): if current_indent <= for_indent and stripped: in_for_loop = False elif re.search(r'\w+\s*\+=\s*["\']|str\s*\(', line): - if "# noqa" not in line: - self._add_violation( - rule_id="PERF-051", - rule_name="String concatenation efficiency", - severity=Severity.INFO, - file_path=file_path, - line_number=i, - message="String concatenation in loop", - context=line.strip()[:80], - suggestion="Use ''.join() or StringIO for many concatenations", - ) + if self._is_noqa_suppressed(line, "PERF-051"): + continue + self._add_violation( + rule_id="PERF-051", + rule_name="String concatenation efficiency", + severity=Severity.INFO, + file_path=file_path, + line_number=i, + message="String concatenation in loop", + context=line.strip()[:80], + suggestion="Use ''.join() or StringIO for many concatenations", + ) # ========================================================================= # Frontend Performance Checks @@ -534,6 +556,8 @@ class PerformanceValidator(BaseValidator): for i, line in enumerate(lines, 1): if re.search(r'@(input|keyup)=".*search.*fetch', line, re.IGNORECASE): if "debounce" not in content.lower(): + if self._is_noqa_suppressed(line, "PERF-056"): + continue self._add_violation( rule_id="PERF-056", rule_name="Debounce search inputs", @@ -552,17 +576,18 @@ class PerformanceValidator(BaseValidator): if match: interval = int(match.group(1)) if interval < 10000: # Less than 10 seconds - if "# real-time" not in line and "# noqa" not in line: - self._add_violation( - rule_id="PERF-062", - rule_name="Reasonable polling intervals", - severity=Severity.WARNING, - file_path=file_path, - line_number=i, - message=f"Polling interval {interval}ms is very frequent", - context=line.strip()[:80], - suggestion="Use >= 10 second intervals for non-critical updates", - ) + if "# real-time" in line or self._is_noqa_suppressed(line, "PERF-062"): + continue + self._add_violation( + rule_id="PERF-062", + rule_name="Reasonable polling intervals", + severity=Severity.WARNING, + file_path=file_path, + line_number=i, + message=f"Polling interval {interval}ms is very frequent", + context=line.strip()[:80], + suggestion="Use >= 10 second intervals for non-critical updates", + ) def _check_layout_thrashing(self, file_path: Path, content: str, lines: list[str]): """PERF-064: Check for layout thrashing patterns""" @@ -572,6 +597,8 @@ class PerformanceValidator(BaseValidator): if i < len(lines): next_line = lines[i] if i < len(lines) else "" if "style" in next_line: + if self._is_noqa_suppressed(line, "PERF-064"): + continue self._add_violation( rule_id="PERF-064", rule_name="Avoid layout thrashing", @@ -589,6 +616,8 @@ class PerformanceValidator(BaseValidator): if re.search(r"]*src=", line): if 'loading="lazy"' not in line and "x-intersect" not in line: if "logo" not in line.lower() and "icon" not in line.lower(): + if self._is_noqa_suppressed(line, "PERF-058"): + continue self._add_violation( rule_id="PERF-058", rule_name="Image optimization", @@ -606,6 +635,8 @@ class PerformanceValidator(BaseValidator): if re.search(r"]*src=", line): if "defer" not in line and "async" not in line: if "alpine" not in line.lower() and "htmx" not in line.lower(): + if self._is_noqa_suppressed(line, "PERF-067"): + continue self._add_violation( rule_id="PERF-067", rule_name="Defer non-critical JavaScript", diff --git a/scripts/validate/validate_security.py b/scripts/validate/validate_security.py index db669b44..1145c107 100755 --- a/scripts/validate/validate_security.py +++ b/scripts/validate/validate_security.py @@ -192,6 +192,8 @@ class SecurityValidator(BaseValidator): # Check for eval usage for i, line in enumerate(lines, 1): if re.search(r"\beval\s*\(", line) and "//" not in line.split("eval")[0]: + if self._is_noqa_suppressed(line, "SEC-013"): + continue self._add_violation( rule_id="SEC-013", rule_name="No code execution", @@ -206,6 +208,8 @@ class SecurityValidator(BaseValidator): # Check for innerHTML with user input for i, line in enumerate(lines, 1): if re.search(r"\.innerHTML\s*=", line) and "//" not in line.split("innerHTML")[0]: + if self._is_noqa_suppressed(line, "SEC-015"): + continue self._add_violation( rule_id="SEC-015", rule_name="XSS prevention", @@ -222,6 +226,8 @@ class SecurityValidator(BaseValidator): # SEC-015: XSS via |safe filter for i, line in enumerate(lines, 1): if re.search(r"\|\s*safe", line) and "sanitized" not in line.lower(): + if self._is_noqa_suppressed(line, "SEC-015"): + continue self._add_violation( rule_id="SEC-015", rule_name="XSS prevention in templates", @@ -236,6 +242,8 @@ class SecurityValidator(BaseValidator): # Check for x-html with dynamic content for i, line in enumerate(lines, 1): if re.search(r'x-html="[^"]*\w', line) and "sanitized" not in line.lower(): + if self._is_noqa_suppressed(line, "SEC-015"): + continue self._add_violation( rule_id="SEC-015", rule_name="XSS prevention in templates", @@ -268,6 +276,8 @@ class SecurityValidator(BaseValidator): # Check for environment variable references if "${" in line or "os.getenv" in line or "environ" in line: continue + if self._is_noqa_suppressed(line, "SEC-001"): + continue self._add_violation( rule_id="SEC-001", rule_name="No hardcoded credentials", @@ -296,7 +306,7 @@ class SecurityValidator(BaseValidator): exclude_patterns = [ "os.getenv", "os.environ", "settings.", '""', "''", - "# noqa", "# test", "password_hash", "example" + "# test", "password_hash", "example" ] for i, line in enumerate(lines, 1): @@ -305,6 +315,8 @@ class SecurityValidator(BaseValidator): # Check exclusions if any(exc in line for exc in exclude_patterns): continue + if self._is_noqa_suppressed(line, "SEC-001"): + continue self._add_violation( rule_id="SEC-001", rule_name="No hardcoded credentials", @@ -329,7 +341,7 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern in patterns: if re.search(pattern, line): - if "# noqa" in line or "# safe" in line: + if self._is_noqa_suppressed(line, "SEC-011") or "# safe" in line: continue self._add_violation( rule_id="SEC-011", @@ -345,15 +357,15 @@ class SecurityValidator(BaseValidator): def _check_command_injection(self, file_path: Path, content: str, lines: list[str]): """SEC-012: Check for command injection vulnerabilities""" patterns = [ - (r"subprocess.*shell\s*=\s*True", "shell=True in subprocess"), - (r"os\.system\s*\(", "os.system()"), - (r"os\.popen\s*\(", "os.popen()"), + (r"subprocess.*shell\s*=\s*True", "shell=True in subprocess"), # noqa: SEC-012 + (r"os\.system\s*\(", "os.system()"), # noqa: SEC-012 + (r"os\.popen\s*\(", "os.popen()"), # noqa: SEC-012 ] for i, line in enumerate(lines, 1): for pattern, issue in patterns: if re.search(pattern, line): - if "# noqa" in line or "# safe" in line: + if self._is_noqa_suppressed(line, "SEC-012") or "# safe" in line: continue self._add_violation( rule_id="SEC-012", @@ -378,6 +390,8 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern, issue in patterns: if re.search(pattern, line, re.IGNORECASE): + if self._is_noqa_suppressed(line, "SEC-013"): + continue self._add_violation( rule_id="SEC-013", rule_name="No code execution", @@ -405,6 +419,8 @@ class SecurityValidator(BaseValidator): if re.search(pattern, line, re.IGNORECASE): if has_secure_filename: continue + if self._is_noqa_suppressed(line, "SEC-014"): + continue self._add_violation( rule_id="SEC-014", rule_name="Path traversal prevention", @@ -427,7 +443,7 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern, issue in patterns: if re.search(pattern, line): - if "# noqa" in line: + if self._is_noqa_suppressed(line, "SEC-020"): continue self._add_violation( rule_id="SEC-020", @@ -449,13 +465,15 @@ class SecurityValidator(BaseValidator): (r"print\s*\([^)]*password", "password in print"), ] - exclude = ["password_hash", "password_reset", "password_changed", "# noqa"] + exclude = ["password_hash", "password_reset", "password_changed"] for i, line in enumerate(lines, 1): for pattern, issue in patterns: if re.search(pattern, line, re.IGNORECASE): if any(exc in line for exc in exclude): continue + if self._is_noqa_suppressed(line, "SEC-021"): + continue self._add_violation( rule_id="SEC-021", rule_name="PII logging prevention", @@ -478,7 +496,9 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern in patterns: if re.search(pattern, line): - if "logger" in line or "# noqa" in line: + if "logger" in line: + continue + if self._is_noqa_suppressed(line, "SEC-024"): continue self._add_violation( rule_id="SEC-024", @@ -495,7 +515,7 @@ class SecurityValidator(BaseValidator): """SEC-034: Check for HTTP instead of HTTPS""" for i, line in enumerate(lines, 1): if re.search(r"http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0|\$)", line): - if "# noqa" in line or "example.com" in line or "schemas" in line: + if self._is_noqa_suppressed(line, "SEC-034") or "example.com" in line or "schemas" in line: continue if "http://www.w3.org" in line: continue @@ -524,6 +544,8 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern in patterns: if re.search(pattern, line) and "timeout" not in line: + if self._is_noqa_suppressed(line, "SEC-040"): + continue self._add_violation( rule_id="SEC-040", rule_name="Timeout configuration", @@ -547,7 +569,7 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern, algo in patterns: if re.search(pattern, line): - if "# noqa" in line or "# checksum" in line or "# file hash" in line: + if self._is_noqa_suppressed(line, "SEC-041") or "# checksum" in line or "# file hash" in line: continue self._add_violation( rule_id="SEC-041", @@ -580,7 +602,7 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern in patterns: if re.search(pattern, line): - if "# noqa" in line or "# not security" in line: + if self._is_noqa_suppressed(line, "SEC-042") or "# not security" in line: continue self._add_violation( rule_id="SEC-042", @@ -609,6 +631,8 @@ class SecurityValidator(BaseValidator): if re.search(pattern, line): if any(exc in line for exc in exclude): continue + if self._is_noqa_suppressed(line, "SEC-043"): + continue self._add_violation( rule_id="SEC-043", rule_name="No hardcoded encryption keys", @@ -631,7 +655,7 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern, issue in patterns: if re.search(pattern, line): - if "# noqa" in line or "# test" in line or "DEBUG" in line: + if self._is_noqa_suppressed(line, "SEC-047") or "# test" in line or "DEBUG" in line: continue self._add_violation( rule_id="SEC-047", @@ -650,6 +674,8 @@ class SecurityValidator(BaseValidator): # Find the jwt.encode line for i, line in enumerate(lines, 1): if "jwt.encode" in line: + if self._is_noqa_suppressed(line, "SEC-002"): + continue self._add_violation( rule_id="SEC-002", rule_name="JWT expiry enforcement", @@ -676,6 +702,8 @@ class SecurityValidator(BaseValidator): for i, line in enumerate(lines, 1): for pattern in patterns: if re.search(pattern, line): + if self._is_noqa_suppressed(line, "SEC-022"): + continue self._add_violation( rule_id="SEC-022", rule_name="Sensitive data in URLs", diff --git a/tests/fixtures/customer_fixtures.py b/tests/fixtures/customer_fixtures.py index 3d9f1ea6..c474d6ae 100644 --- a/tests/fixtures/customer_fixtures.py +++ b/tests/fixtures/customer_fixtures.py @@ -20,7 +20,7 @@ def test_customer(db, test_store): customer = Customer( store_id=test_store.id, email="testcustomer@example.com", - hashed_password="hashed_password", + hashed_password="hashed_password", # noqa: SEC-001 first_name="John", last_name="Doe", customer_number="TEST001", diff --git a/tests/fixtures/testing_fixtures.py b/tests/fixtures/testing_fixtures.py index 7d7a9436..163f07f9 100644 --- a/tests/fixtures/testing_fixtures.py +++ b/tests/fixtures/testing_fixtures.py @@ -32,7 +32,7 @@ def empty_db(db): for table in tables_to_clear: try: - db.execute(text(f"DELETE FROM {table}")) + db.execute(text(f"DELETE FROM {table}")) # noqa: SEC-011 except Exception: # If table doesn't exist or delete fails, continue pass diff --git a/tests/unit/models/schema/test_auth.py b/tests/unit/models/schema/test_auth.py index 9a47fd8d..4ee951d6 100644 --- a/tests/unit/models/schema/test_auth.py +++ b/tests/unit/models/schema/test_auth.py @@ -21,16 +21,16 @@ class TestUserLoginSchema: """Test valid login data.""" login = UserLogin( email_or_username="testuser", - password="password123", + password="password123", # noqa: SEC-001 ) assert login.email_or_username == "testuser" - assert login.password == "password123" + assert login.password == "password123" # noqa: SEC-001 def test_login_with_email(self): """Test login with email.""" login = UserLogin( email_or_username="test@example.com", - password="password123", + password="password123", # noqa: SEC-001 ) assert login.email_or_username == "test@example.com" @@ -38,7 +38,7 @@ class TestUserLoginSchema: """Test login with optional store code.""" login = UserLogin( email_or_username="testuser", - password="password123", + password="password123", # noqa: SEC-001 store_code="STORE001", ) assert login.store_code == "STORE001" @@ -47,7 +47,7 @@ class TestUserLoginSchema: """Test email_or_username is stripped of whitespace.""" login = UserLogin( email_or_username=" testuser ", - password="password123", + password="password123", # noqa: SEC-001 ) assert login.email_or_username == "testuser" @@ -62,7 +62,7 @@ class TestUserCreateSchema: user = UserCreate( email="admin@example.com", username="adminuser", - password="securepass", + password="securepass", # noqa: SEC-001 first_name="Admin", last_name="User", role="admin", @@ -75,7 +75,7 @@ class TestUserCreateSchema: user = UserCreate( email="store@example.com", username="storeuser", - password="securepass", + password="securepass", # noqa: SEC-001 ) assert user.role == "store" @@ -85,7 +85,7 @@ class TestUserCreateSchema: UserCreate( email="test@example.com", username="testuser", - password="securepass", + password="securepass", # noqa: SEC-001 role="superadmin", ) assert "role" in str(exc_info.value).lower() @@ -96,7 +96,7 @@ class TestUserCreateSchema: UserCreate( email="test@example.com", username="ab", - password="securepass", + password="securepass", # noqa: SEC-001 ) assert "username" in str(exc_info.value).lower() @@ -106,7 +106,7 @@ class TestUserCreateSchema: UserCreate( email="test@example.com", username="testuser", - password="12345", + password="12345", # noqa: SEC-001 ) assert "password" in str(exc_info.value).lower() diff --git a/tests/unit/services/test_auth_service.py b/tests/unit/services/test_auth_service.py index a2b09644..53c8f314 100644 --- a/tests/unit/services/test_auth_service.py +++ b/tests/unit/services/test_auth_service.py @@ -23,7 +23,7 @@ class TestAuthService: def test_login_user_success(self, db, test_user): """Test successful user login.""" user_credentials = UserLogin( - email_or_username=test_user.username, password="testpass123" + email_or_username=test_user.username, password="testpass123" # noqa: SEC-001 ) result = self.service.login_user(db, user_credentials) @@ -39,7 +39,7 @@ class TestAuthService: def test_login_user_with_email(self, db, test_user): """Test login with email instead of username.""" user_credentials = UserLogin( - email_or_username=test_user.email, password="testpass123" + email_or_username=test_user.email, password="testpass123" # noqa: SEC-001 ) result = self.service.login_user(db, user_credentials) @@ -50,7 +50,7 @@ class TestAuthService: def test_login_user_wrong_username(self, db): """Test login fails with wrong username.""" user_credentials = UserLogin( - email_or_username="nonexistentuser", password="testpass123" + email_or_username="nonexistentuser", password="testpass123" # noqa: SEC-001 ) with pytest.raises(InvalidCredentialsException) as exc_info: @@ -64,7 +64,7 @@ class TestAuthService: def test_login_user_wrong_password(self, db, test_user): """Test login fails with wrong password.""" user_credentials = UserLogin( - email_or_username=test_user.username, password="wrongpassword" + email_or_username=test_user.username, password="wrongpassword" # noqa: SEC-001 ) with pytest.raises(InvalidCredentialsException) as exc_info: @@ -85,7 +85,7 @@ class TestAuthService: db.commit() user_credentials = UserLogin( - email_or_username=test_user.username, password="testpass123" + email_or_username=test_user.username, password="testpass123" # noqa: SEC-001 ) with pytest.raises(UserNotActiveException) as exc_info: @@ -102,7 +102,7 @@ class TestAuthService: def test_hash_password(self): """Test password hashing.""" - password = "testpassword123" + password = "testpassword123" # noqa: SEC-001 hashed = self.service.hash_password(password) assert hashed != password @@ -111,7 +111,7 @@ class TestAuthService: def test_hash_password_different_results(self): """Test that hashing same password produces different hashes (salt).""" - password = "testpassword123" + password = "testpassword123" # noqa: SEC-001 hash1 = self.service.hash_password(password) hash2 = self.service.hash_password(password)