Download tests/fuzzing/test_jwt_validation_fuzzing.py from techprotrade/annator-command-center: direct link, hf CLI and curl.
- Browser
- Download file 11.5 kB
-
https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/fuzzing/test_jwt_validation_fuzzing.py
- Command line
-
hf download hf://spaces/techprotrade/annator-command-center/tests/fuzzing/test_jwt_validation_fuzzing.py
-
curl -L -o test_jwt_validation_fuzzing.py https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/fuzzing/test_jwt_validation_fuzzing.py
11.5 kB
| """ | |
| JWT validation fuzzing harnesses. | |
| This module uses Atheris to discover crashes in JWT parsing and | |
| validation code through coverage-guided fuzzing. | |
| Fuzzing Targets: | |
| - Authorization header parsing | |
| - JWT expiry validation | |
| - JWT signature validation | |
| - Authorization header format validation | |
| Usage: | |
| FUZZ_ITERATIONS=10000 pytest tests/fuzzing/test_jwt_validation_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 | |
| 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 JWT validation fuzzing", allow_module_level=True) | |
| # ============================================================================ | |
| # FUZZING TEST 1: AUTHORIZATION HEADER VALIDATION | |
| # ============================================================================ | |
| def test_jwt_validation_fuzzing(db_session: Session): | |
| """ | |
| Fuzz Authorization header parsing in protected endpoints. | |
| PROPERTY: JWT validation should not crash on malformed tokens. | |
| STRATEGY: FuzzedDataProvider generates random token strings. | |
| INVARIANT: Response status code in [200, 401, 403, 422] (no 500 errors). | |
| RADII: 10000 iterations sufficient for token input space. | |
| Fuzzed fields: | |
| - Authorization header: Random string up to 500 chars | |
| - Token format: Bearer {token} with fuzzed token | |
| Tests GET /api/agents (protected endpoint) with fuzzed JWT tokens. | |
| """ | |
| # Override database dependency | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| client = TestClient(app) | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for Authorization header parsing.""" | |
| try: | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Consume random token string | |
| token = fdp.ConsumeRandomLengthString(500) | |
| # Create Authorization header with fuzzed token | |
| headers = {"Authorization": f"Bearer {token}"} | |
| # Call protected endpoint with fuzzed token | |
| response = client.get("/api/agents", headers=headers) | |
| # Assert no crashes (401/403 expected for invalid tokens) | |
| assert response.status_code in [200, 401, 403, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except (ValueError, json.decoder.JSONDecodeError): | |
| pass | |
| except Exception as e: | |
| raise Exception(f"JWT validation 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 2: JWT EXPIRY VALIDATION | |
| # ============================================================================ | |
| def test_jwt_expiry_fuzzing(db_session: Session, authenticated_user): | |
| """ | |
| Fuzz JWT expiry timestamp handling. | |
| PROPERTY: Expired token validation should not crash. | |
| STRATEGY: Direct function fuzzing of JWT verification with fuzzed expiry. | |
| INVARIANT: Function returns False or raises ValidationError (no crashes). | |
| RADII: 10000 iterations sufficient for timestamp space. | |
| Fuzzed fields: | |
| - Expiry timestamp: Negative values, huge numbers, strings | |
| - Token payload: Malformed expiry claims | |
| Uses core.auth.verify_token for direct function fuzzing. | |
| """ | |
| from core.auth import verify_token | |
| from jose import jwt | |
| from datetime import datetime, timedelta | |
| # Override database dependency | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| client = TestClient(app) | |
| # Get user for valid token generation | |
| user, _ = authenticated_user | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for JWT expiry validation.""" | |
| try: | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Generate JWT with fuzzed expiry | |
| # Use fuzzed data to create potentially invalid expiry | |
| fuzzed_expiry = fdp.ConsumeIntToIntInRange(-1000000, 1000000) | |
| # Create token with fuzzed expiry | |
| payload = { | |
| "sub": str(user.id), | |
| "exp": fuzzed_expiry, | |
| "iat": int(datetime.utcnow().timestamp()) | |
| } | |
| # Sign with test secret (may be invalid) | |
| try: | |
| token = jwt.encode(payload, "test_secret", algorithm="HS256") | |
| # Try to verify token | |
| headers = {"Authorization": f"Bearer {token}"} | |
| response = client.get("/api/agents", headers=headers) | |
| # Assert no crashes | |
| assert response.status_code in [200, 401, 403, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except Exception as jwt_error: | |
| # JWT encoding errors are OK (fuzzed input) | |
| pass | |
| except (ValueError, json.decoder.JSONDecodeError): | |
| pass | |
| except Exception as e: | |
| raise Exception(f"JWT expiry validation 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: JWT SIGNATURE VALIDATION | |
| # ============================================================================ | |
| def test_jwt_signature_fuzzing(db_session: Session, authenticated_user): | |
| """ | |
| Fuzz JWT signature validation. | |
| PROPERTY: Signature validation should not crash on invalid signatures. | |
| STRATEGY: FuzzedDataProvider generates random signature strings. | |
| INVARIANT: Response status code in [200, 401, 403, 422] (no 500 errors). | |
| RADII: 10000 iterations sufficient for signature space. | |
| Fuzzed fields: | |
| - Signature: Invalid length, invalid chars, None, empty strings | |
| - Token segments: Malformed JWT structure (header.payload.signature) | |
| Tests protected endpoint with tokens containing fuzzed signatures. | |
| """ | |
| # Override database dependency | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| client = TestClient(app) | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for JWT signature validation.""" | |
| try: | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Generate fuzzed JWT segments | |
| header = fdp.ConsumeRandomLengthString(100) | |
| payload = fdp.ConsumeRandomLengthString(100) | |
| signature = fdp.ConsumeRandomLengthString(100) | |
| # Create malformed JWT token | |
| fuzzed_token = f"{header}.{payload}.{signature}" | |
| # Call protected endpoint with fuzzed token | |
| headers = {"Authorization": f"Bearer {fuzzed_token}"} | |
| response = client.get("/api/agents", headers=headers) | |
| # Assert no crashes | |
| assert response.status_code in [200, 401, 403, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except (ValueError, json.decoder.JSONDecodeError): | |
| pass | |
| except Exception as e: | |
| raise Exception(f"JWT signature validation 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 4: AUTHORIZATION HEADER FORMAT | |
| # ============================================================================ | |
| def test_jwt_header_fuzzing(db_session: Session, authenticated_user): | |
| """ | |
| Fuzz Authorization header format variations. | |
| PROPERTY: Authorization header parser should not crash on malformed headers. | |
| STRATEGY: FuzzedDataProvider generates random header formats. | |
| INVARIANT: Response status code in [200, 401, 403, 422] (no 500 errors). | |
| RADII: 10000 iterations sufficient for header format space. | |
| Fuzzed fields: | |
| - Header prefix: "Bearer", "bearer", lowercase, no prefix | |
| - Spacing: No space, multiple spaces, random whitespace | |
| - Token: Random string, None, empty | |
| Tests various malformed Authorization header formats: | |
| - Missing "Bearer" prefix | |
| - Lowercase "bearer" | |
| - No space after "Bearer" | |
| - Multiple spaces | |
| - Empty token | |
| - None token | |
| """ | |
| import random | |
| # Override database dependency | |
| app.dependency_overrides[get_db] = lambda: db_session | |
| client = TestClient(app) | |
| def fuzz_one_input(data: bytes): | |
| """Fuzzing target for Authorization header format.""" | |
| try: | |
| fdp = fp.FuzzedDataProvider(data) | |
| # Generate fuzzed token | |
| token = fdp.ConsumeRandomLengthString(500) | |
| # Generate fuzzed header format | |
| header_formats = [ | |
| f"Bearer {token}", # Standard format | |
| f"bearer {token}", # Lowercase | |
| f"BEARER {token}", # Uppercase | |
| f"{token}", # Missing prefix | |
| f"Bearer{token}", # No space | |
| f"Bearer {token}", # Multiple spaces | |
| f"Bearer {token} ", # Trailing spaces | |
| f"", # Empty header | |
| f"Bearer ", # Empty token | |
| f" {token}", # Leading space | |
| ] | |
| # Select random header format | |
| if len(data) > 0: | |
| header_value = header_formats[ord(data[0]) % len(header_formats)] | |
| else: | |
| header_value = f"Bearer {token}" | |
| # Call protected endpoint with fuzzed header | |
| headers = {"Authorization": header_value} | |
| response = client.get("/api/agents", headers=headers) | |
| # Assert no crashes | |
| assert response.status_code in [200, 401, 403, 422], \ | |
| f"Unexpected status {response.status_code}: {response.text[:200]}" | |
| except (ValueError, json.decoder.JSONDecodeError): | |
| pass | |
| except Exception as e: | |
| raise Exception(f"Authorization header parsing 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 = {} | |