diff --git a/docs_src/security/tutorial004_an_py310.py b/docs_src/security/tutorial004_an_py310.py index 7c7d6d1c6..ebc6a90d9 100644 --- a/docs_src/security/tutorial004_an_py310.py +++ b/docs_src/security/tutorial004_an_py310.py @@ -12,7 +12,9 @@ from pydantic import BaseModel # to get a string like this run: # openssl rand -hex 32 # then set it as an environment variable: export SECRET_KEY="your-generated-key" -SECRET_KEY = os.environ.get("SECRET_KEY", "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7") +SECRET_KEY = os.environ.get( + "SECRET_KEY", "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" +) ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 diff --git a/tests/test_invariant_tutorial004_an_py310.py b/tests/test_invariant_tutorial004_an_py310.py index e4845f3eb..9fcd48fe5 100644 --- a/tests/test_invariant_tutorial004_an_py310.py +++ b/tests/test_invariant_tutorial004_an_py310.py @@ -1,44 +1,52 @@ -import pytest -import sys import os +import sys from importlib import util +import pytest + # Add the project root to sys.path to import the tutorial module -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))) # Import the actual production module spec = util.spec_from_file_location( - "tutorial004_an_py310", - "docs_src/security/tutorial004_an_py310.py" + "tutorial004_an_py310", "docs_src/security/tutorial004_an_py310.py" ) tutorial = util.module_from_spec(spec) spec.loader.exec_module(tutorial) -@pytest.mark.parametrize("secret_key", [ - # Exact exploit case: hardcoded secret from the tutorial - "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7", - # Boundary case: empty secret (should not be allowed) - "", - # Valid input: a different secret (should be accepted but not match hardcoded) - "a1b2c3d4e5f67890abcdef1234567890fedcba0987654321abcdef0123456789", -]) +@pytest.mark.parametrize( + "secret_key", + [ + # Exact exploit case: hardcoded secret from the tutorial + "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7", + # Boundary case: empty secret (should not be allowed) + "", + # Valid input: a different secret (should be accepted but not match hardcoded) + "a1b2c3d4e5f67890abcdef1234567890fedcba0987654321abcdef0123456789", + ], +) def test_secret_key_not_hardcoded_in_production(secret_key): """Invariant: Production secret keys must not match the tutorial's hardcoded example.""" # Access the module's SECRET_KEY constant module_secret = tutorial.SECRET_KEY - + # The security property: the actual secret must NOT be the tutorial's hardcoded value # This ensures the example isn't accidentally used in production - assert module_secret != "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7", \ + assert ( + module_secret + != "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" + ), ( "SECRET_KEY matches the tutorial's hardcoded example - this is unsafe for production!" - + ) + # Additional invariant: secret must be non-empty and sufficiently long for HS256 - assert len(module_secret) >= 32, \ + assert len(module_secret) >= 32, ( "SECRET_KEY is too short for secure HS256 usage (minimum 32 hex chars)" - + ) + # Verify the secret is a valid hex string (optional but good practice) try: int(module_secret, 16) except ValueError: - pytest.fail("SECRET_KEY is not a valid hexadecimal string") \ No newline at end of file + pytest.fail("SECRET_KEY is not a valid hexadecimal string")