Spaces:
Sleeping
Sleeping
Download tests/fuzzing/test_auth_api_fuzzing.py from techprotrade/annator-command-center: direct link, hf CLI and curl.
- Browser
- Download file 9.18 kB
-
https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/fuzzing/test_auth_api_fuzzing.py
- Command line
-
hf download hf://spaces/techprotrade/annator-command-center/tests/fuzzing/test_auth_api_fuzzing.py
-
curl -L -o test_auth_api_fuzzing.py https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/fuzzing/test_auth_api_fuzzing.py
9.18 kB
| """ | |
| Authentication endpoint fuzzing harnesses. | |
| This module uses Atheris to discover crashes in authentication endpoint | |
| parsing and validation code through coverage-guided fuzzing. | |
| Fuzzing Targets: | |
| - POST /api/auth/login - Login endpoint | |
| - POST /api/auth/signup - Signup endpoint | |
| - POST /api/auth/mobile/login - Mobile login endpoint | |
| Usage: | |
| FUZZ_ITERATIONS=10000 pytest tests/fuzzing/test_auth_api_fuzzing.py -v -m fuzzing | |
| """ | |
| import os | |
| import sys | |
| import json | |
| import pytest | |
| from typing import Dict, Any | |
| # Add backend to path | |
| backend_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| if backend_dir not in sys.path: | |
| sys.path.insert(0, backend_dir) | |
| from fastapi.testclient import TestClient | |
| from sqlalchemy.orm import Session | |
| # Import fixtures and app | |
| from tests.fuzzing.conftest import ATHERIS_AVAILABLE | |
| from tests.e2e_ui.fixtures.database_fixtures import db_session | |
| from tests.e2e_ui.fixtures.auth_fixtures import authenticated_user, test_user | |
| from main_api_app import app | |
| from core.database import get_db | |
| # Try to import Atheris (graceful degradation) | |
| try: | |
| import atheris | |
| from atheris import fp | |
| except ImportError: | |
| ATHERIS_AVAILABLE = False | |
| pytest.skip("Atheris not installed - skipping auth endpoint fuzzing", allow_module_level=True) | |
| # ============================================================================ | |
| # FUZZING TEST 1: LOGIN ENDPOINT | |
| # ============================================================================ | |
| def test_login_endpoint_fuzzing(db_session: Session): | |
| """ | |
| Fuzz POST /api/auth/login endpoint. | |
| PROPERTY: Login endpoint should not crash on malformed input. | |
| STRATEGY: FuzzedDataProvider generates random email/password combinations. | |
| INVARIANT: Response status code in [200, 400, 401, 422] (no 500 errors). | |
| RADII: 10000 iterations sufficient for login input space (email + password). | |
| Fuzzed fields: | |
| - email: Random string up to 100 chars | |
| - password: Random string up to 100 chars | |
| """ | |
| # Override database dependency for test isolation | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| # Create TestClient for fuzzing | |
| client = TestClient(app) | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for login endpoint.""" | |
| try: | |
| # Generate fuzzed payload using FuzzedDataProvider | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Consume random strings for email and password | |
| email = fdp.ConsumeRandomLengthString(100) | |
| password = fdp.ConsumeRandomLengthString(100) | |
| # Call login endpoint with fuzzed data | |
| response = client.post( | |
| "/api/auth/login", | |
| json={"email": email, "password": password} | |
| ) | |
| # Assert no crashes (status in expected range) | |
| assert response.status_code in [200, 400, 401, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except (ValueError, json.decoder.JSONDecodeError) as e: | |
| # Expected errors (malformed JSON) | |
| pass | |
| except Exception as e: | |
| # Potential bug - re-raise for Atheris to capture | |
| raise Exception(f"Login endpoint crashed: {e}") | |
| # Setup Atheris with libFuzzer | |
| atheris.Setup( | |
| sys.argv + [atheris.FuzzedDataProviderFlag], | |
| fuzz_one_input | |
| ) | |
| # Run fuzzing (default 10000 iterations or FUZZ_ITERATIONS env var) | |
| iterations = int(os.getenv("FUZZ_ITERATIONS", "10000")) | |
| atheris.Fuzz(iterations=iterations) | |
| # Clean up dependency override | |
| app.dependency_overrides = {} | |
| # ============================================================================ | |
| # FUZZING TEST 2: SIGNUP ENDPOINT | |
| # ============================================================================ | |
| def test_signup_endpoint_fuzzing(db_session: Session): | |
| """ | |
| Fuzz POST /api/auth/signup endpoint. | |
| PROPERTY: Signup endpoint should not crash on malformed input. | |
| STRATEGY: FuzzedDataProvider generates random signup payloads. | |
| INVARIANT: Response status code in [200, 400, 409, 422] (no 500 errors). | |
| RADII: 10000 iterations sufficient for signup input space (email + password + confirm + name). | |
| Fuzzed fields: | |
| - email: Random string up to 100 chars | |
| - password: Random string up to 100 chars | |
| - confirm_password: Random string up to 100 chars | |
| - name: Random string up to 100 chars | |
| """ | |
| import json | |
| # Override database dependency | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| client = TestClient(app) | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for signup endpoint.""" | |
| try: | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Consume random strings for signup fields | |
| email = fdp.ConsumeRandomLengthString(100) | |
| password = fdp.ConsumeRandomLengthString(100) | |
| confirm_password = fdp.ConsumeRandomLengthString(100) | |
| name = fdp.ConsumeRandomLengthString(100) | |
| # Call signup endpoint with fuzzed data | |
| response = client.post( | |
| "/api/auth/signup", | |
| json={ | |
| "email": email, | |
| "password": password, | |
| "confirm_password": confirm_password, | |
| "name": name | |
| } | |
| ) | |
| # Assert no crashes | |
| assert response.status_code in [200, 400, 409, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except (ValueError, json.decoder.JSONDecodeError): | |
| pass | |
| except Exception as e: | |
| raise Exception(f"Signup endpoint crashed: {e}") | |
| atheris.Setup( | |
| sys.argv + [atheris.FuzzedDataProviderFlag], | |
| fuzz_one_input | |
| ) | |
| iterations = int(os.getenv("FUZZ_ITERATIONS", "10000")) | |
| atheris.Fuzz(iterations=iterations) | |
| app.dependency_overrides = {} | |
| # ============================================================================ | |
| # FUZZING TEST 3: MOBILE LOGIN ENDPOINT | |
| # ============================================================================ | |
| def test_mobile_login_fuzzing(db_session: Session, authenticated_user): | |
| """ | |
| Fuzz POST /api/auth/mobile/login endpoint. | |
| PROPERTY: Mobile login endpoint should not crash on malformed device tokens. | |
| STRATEGY: FuzzedDataProvider generates random device tokens and platforms. | |
| INVARIANT: Response status code in [200, 400, 401, 422] (no 500 errors). | |
| RADII: 10000 iterations sufficient for device token/platform space. | |
| Fuzzed fields: | |
| - device_token: Random string up to 100 chars | |
| - platform: Random string up to 20 chars (ios, android, or random) | |
| - device_info: Random dictionary with nested fuzzed data | |
| Uses authenticated_user fixture for valid credentials baseline. | |
| """ | |
| import json | |
| # Override database dependency | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| client = TestClient(app) | |
| # Get valid credentials from authenticated_user | |
| user, _ = authenticated_user | |
| valid_email = user.email | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for mobile login endpoint.""" | |
| try: | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Consume random strings for device fields | |
| device_token = fdp.ConsumeRandomLengthString(100) | |
| platform = fdp.ConsumeRandomLengthString(20) | |
| # Generate fuzzed device_info dict | |
| device_info: Dict[str, Any] = {} | |
| if fdp.ConsumeBool(): | |
| device_info["device_id"] = fdp.ConsumeRandomLengthString(50) | |
| if fdp.ConsumeBool(): | |
| device_info["app_version"] = fdp.ConsumeRandomLengthString(20) | |
| if fdp.ConsumeBool(): | |
| device_info["os_version"] = fdp.ConsumeRandomLengthString(20) | |
| # Call mobile login endpoint with fuzzed device data | |
| response = client.post( | |
| "/api/auth/mobile/login", | |
| json={ | |
| "email": valid_email, | |
| "password": "TestPassword123!", # Valid password from test_user fixture | |
| "device_token": device_token, | |
| "platform": platform, | |
| "device_info": device_info | |
| } | |
| ) | |
| # Assert no crashes | |
| assert response.status_code in [200, 400, 401, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except (ValueError, json.decoder.JSONDecodeError): | |
| pass | |
| except Exception as e: | |
| raise Exception(f"Mobile login endpoint crashed: {e}") | |
| atheris.Setup( | |
| sys.argv + [atheris.FuzzedDataProviderFlag], | |
| fuzz_one_input | |
| ) | |
| iterations = int(os.getenv("FUZZ_ITERATIONS", "10000")) | |
| atheris.Fuzz(iterations=iterations) | |
| app.dependency_overrides = {} | |