nene2-python 1.8.7__tar.gz → 1.8.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nene2_python-1.8.7 → nene2_python-1.8.8}/CHANGELOG.md +12 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/PKG-INFO +1 -1
- nene2_python-1.8.8/docs/field-trials/2026-05-field-trial-45.md +108 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/pyproject.toml +1 -1
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/security_headers.py +1 -1
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_security_headers.py +13 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/uv.lock +1 -1
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.env.example +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.github/workflows/ci.yml +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.github/workflows/docs.yml +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.github/workflows/publish.yml +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.gitignore +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.vitepress/config.mts +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.vitepress/theme/custom.css +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/.vitepress/theme/index.ts +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/AGENTS.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/CLAUDE.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/Dockerfile +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/LICENSE +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/README.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/alembic/README +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/alembic/env.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/alembic/script.py.mako +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/alembic.ini +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/compose.yaml +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0001-toolchain.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0002-clean-architecture.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0003-security-first.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0004-ai-first-design.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0005-logging.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0006-rate-limiting.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0009-mcp-design.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0010-async-use-case.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/de/index.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/de/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/explanation/architecture.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-1.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-10.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-11.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-12.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-13.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-14.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-15.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-16.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-17.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-18.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-19.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-2.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-20.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-21.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-22.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-23.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-24.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-25.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-26.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-27.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-28.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-29.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-3.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-30.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-31.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-32.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-33.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-34.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-35.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-36.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-37.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-38.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-39.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-4.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-40.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-41.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-42.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-43.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-44.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-5.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-6.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-7.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-8.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/field-trials/2026-05-field-trial-9.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/fr/index.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/fr/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/async-use-case.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/new-project.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/problem-details.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/run-tests.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/how-to/validation.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/index.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/explanation/architecture.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/explanation/design-philosophy.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/how-to/add-new-domain.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/how-to/configure-auth.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/how-to/new-project.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/how-to/run-tests.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/howto/mcp-setup.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/index.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/reference/api.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/reference/configuration.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/reference/framework-modules.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/ja/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/pt-br/index.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/pt-br/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/reference/api.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/reference/configuration.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/reference/framework-modules.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/roadmap.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/todo/current.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/tutorials/first-domain.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/zh/index.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/docs/zh/tutorials/getting-started.md +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/package-lock.json +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/package.json +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/__main__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/app.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/entity.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/handler.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/comment/use_case.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/mcp.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/async_use_case.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/entity.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/handler.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/note/use_case.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/schema.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/entity.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/handler.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/sqlalchemy_repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/example/tag/use_case.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/auth/api_key.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/auth/bearer_token.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/auth/exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/auth/interfaces.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/auth/local_verifier.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/config/settings.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/database/exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/database/health.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/database/interfaces.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/database/sqlalchemy_executor.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/database/utils.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/http/health.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/http/pagination.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/http/problem_details.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/log/setup.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/mcp/http_client.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/mcp/server.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/domain_exception.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/error_handler.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/request_id.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/request_logging.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/request_size_limit.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/middleware/throttle.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/py.typed +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/use_case/protocols.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/nene2/validation/exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/scripts/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/src/scripts/export_openapi.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/comment/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/comment/test_comment_http.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/comment/test_comment_repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/comment/test_comment_use_case.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/conftest.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/note/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/note/test_async_note_use_case.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/note/test_list_notes.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/note/test_note_repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/tag/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/tag/test_tag_repository.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/tag/test_tags.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/test_cors.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/example/test_mcp.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/auth/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/auth/test_api_key.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/auth/test_bearer_token.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/auth/test_token_issuer.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/config/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/config/test_settings.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/database/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/database/test_transaction.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/database/test_utils.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/http/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/http/test_health.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/http/test_pagination.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/http/test_problem_details.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/log/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/log/test_setup.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/mcp/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/mcp/test_http_client.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_error_handler.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_request_id.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_request_logging.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_request_size_limit.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_throttle.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/use_case/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/use_case/test_protocols.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/validation/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/validation/test_exceptions.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/scripts/__init__.py +0 -0
- {nene2_python-1.8.7 → nene2_python-1.8.8}/tests/scripts/test_export_openapi.py +0 -0
|
@@ -5,6 +5,18 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [1.8.8] — 2026-05-20
|
|
9
|
+
|
|
10
|
+
FT45 フィールドトライアル — SecurityHeadersMiddleware CSP バグ修正。
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `SecurityHeadersMiddleware` — `csp=""` を渡したとき空の `Content-Security-Policy` ヘッダーが付与される問題を修正。`csp=""` の場合は CSP ヘッダーを付与しないよう変更 (#271) (FT45)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Field trial report: `docs/field-trials/2026-05-field-trial-45.md`
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
8
20
|
## [1.8.7] — 2026-05-20
|
|
9
21
|
|
|
10
22
|
FT43〜FT44 フィールドトライアル — ThrottleMiddleware path_limits 確認・PaginationQueryParser バリデーション改善。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nene2-python
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.8
|
|
4
4
|
Summary: NENE2 Python — minimal API framework following NENE2's design philosophy
|
|
5
5
|
Project-URL: Homepage, https://github.com/hideyukiMORI/nene2-python
|
|
6
6
|
Project-URL: Repository, https://github.com/hideyukiMORI/nene2-python
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Field Trial 45: SecurityHeadersMiddleware 詳細カスタマイズ実運用検証
|
|
2
|
+
|
|
3
|
+
**日付**: 2026-05-20
|
|
4
|
+
**バージョン**: v1.8.7 時点
|
|
5
|
+
**テーマ**: `SecurityHeadersMiddleware` の全オプション(CSP・HSTS・Permissions-Policy・extra_no_csp_paths)を組み合わせた実運用確認
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 概要
|
|
10
|
+
|
|
11
|
+
`SecurityHeadersMiddleware` の CSP カスタマイズ・HSTS 設定・Permissions-Policy カスタマイズ・
|
|
12
|
+
カスタム docs_url との組み合わせを実装した。
|
|
13
|
+
2 つの摩擦点を発見し、うち 1 件 (FP45-3) を修正した。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 実装内容
|
|
18
|
+
|
|
19
|
+
`/home/xi/docker/nene2-python-FT/ft45-security-headers/` に以下を作成:
|
|
20
|
+
|
|
21
|
+
- **`app.py`** — カスタム `docs_url`/`openapi_url` + `SecurityHeadersMiddleware` 全オプション
|
|
22
|
+
- **`test_app.py`** — 静的ヘッダー・CSP・HSTS・Permissions-Policy・extra_no_csp_paths (12 件)
|
|
23
|
+
- **`test_friction.py`** — 摩擦点の確認テスト (4 件)
|
|
24
|
+
|
|
25
|
+
**テスト結果**: 16 件全通過 ✅
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 摩擦点
|
|
30
|
+
|
|
31
|
+
### FP45-1: カスタム docs_url 使用時に extra_no_csp_paths の設定を忘れやすい
|
|
32
|
+
|
|
33
|
+
**分類**: 軽微な摩擦(ドキュメント追記で対応)
|
|
34
|
+
|
|
35
|
+
FastAPI の `docs_url="/api/docs"` のようにカスタム URL を使う場合、
|
|
36
|
+
`SecurityHeadersMiddleware` のデフォルト `no_csp_paths` には
|
|
37
|
+
`/docs`・`/redoc`・`/openapi.json` しか含まれない。
|
|
38
|
+
`/api/docs` には CSP が付いてしまい、Swagger UI の CDN アセットが CSP でブロックされる。
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
# カスタム docs_url 使用時の必須設定
|
|
42
|
+
app.add_middleware(
|
|
43
|
+
SecurityHeadersMiddleware,
|
|
44
|
+
extra_no_csp_paths=["/api/docs", "/api/redoc", "/api/openapi.json"],
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**判断**: FT15 で実装した `extra_no_csp_paths` で対応可能だが、
|
|
49
|
+
デフォルトの `docs_url` を変更したときに `extra_no_csp_paths` も更新する必要があることを
|
|
50
|
+
ドキュメントで注意喚起する価値がある。
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### FP45-2: HSTS は本番環境以外で有効にしてはならない
|
|
55
|
+
|
|
56
|
+
**分類**: 注意喚起(設計通り・ドキュメント記載済み)
|
|
57
|
+
|
|
58
|
+
`hsts` パラメータを設定すると、`Strict-Transport-Security` ヘッダーが付与される。
|
|
59
|
+
http:// でアクセスすると次回以降 https:// を強制するため、開発環境での誤設定に注意が必要。
|
|
60
|
+
`hsts=None` のデフォルトは意図的な安全設計。
|
|
61
|
+
|
|
62
|
+
**判断**: 設計通り。ドキュメントの Warning で注意喚起済み。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### FP45-3: csp="" のとき空の CSP ヘッダーが付与されていた
|
|
67
|
+
|
|
68
|
+
**分類**: バグ(#271 で修正)
|
|
69
|
+
|
|
70
|
+
`csp=""` を渡すと `Content-Security-Policy: ` という空ヘッダーが付与されていた。
|
|
71
|
+
ユーザーが CSP を無効化しようとして `csp=""` を渡すと、空 CSP が付く予期しない動作になる。
|
|
72
|
+
|
|
73
|
+
**修正**: `dispatch()` を `self._csp` が truthy のときのみ CSP ヘッダーを付与するよう変更。
|
|
74
|
+
`csp=""` は「CSP ヘッダーを付けない」として扱われる。
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# 修正前
|
|
78
|
+
if request.url.path not in self._no_csp_paths:
|
|
79
|
+
response.headers["Content-Security-Policy"] = self._csp
|
|
80
|
+
|
|
81
|
+
# 修正後
|
|
82
|
+
if request.url.path not in self._no_csp_paths and self._csp:
|
|
83
|
+
response.headers["Content-Security-Policy"] = self._csp
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### FP45-4: 全オプション組み合わせは問題なく動作する
|
|
89
|
+
|
|
90
|
+
**分類**: 摩擦なし(設計の確認)
|
|
91
|
+
|
|
92
|
+
`csp`・`permissions_policy`・`hsts`・`extra_no_csp_paths` の全オプションを同時に指定しても
|
|
93
|
+
正常に動作することを確認した。
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## フレームワーク変更
|
|
98
|
+
|
|
99
|
+
- `SecurityHeadersMiddleware.dispatch()` — `csp=""` のとき CSP ヘッダーを付与しないよう修正 (#271)
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 関連
|
|
104
|
+
|
|
105
|
+
- `nene2.middleware.SecurityHeadersMiddleware`
|
|
106
|
+
- FT15 (CSP カスタマイズ, v1.7.0)
|
|
107
|
+
- FT32 (HSTS・Permissions-Policy 追加, v1.8.3)
|
|
108
|
+
- Issue #271 (csp="" バグ修正)
|
|
@@ -63,6 +63,6 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
|
|
63
63
|
response.headers["Permissions-Policy"] = self._permissions_policy
|
|
64
64
|
if self._hsts:
|
|
65
65
|
response.headers["Strict-Transport-Security"] = self._hsts
|
|
66
|
-
if request.url.path not in self._no_csp_paths:
|
|
66
|
+
if request.url.path not in self._no_csp_paths and self._csp:
|
|
67
67
|
response.headers["Content-Security-Policy"] = self._csp
|
|
68
68
|
return response
|
|
@@ -130,3 +130,16 @@ def test_default_no_csp_paths_still_work_with_extra_paths() -> None:
|
|
|
130
130
|
assert "Content-Security-Policy" not in client.get("/docs").headers
|
|
131
131
|
assert "Content-Security-Policy" not in client.get("/custom").headers
|
|
132
132
|
assert "Content-Security-Policy" in client.get("/ping").headers
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_csp_empty_string_disables_csp_header() -> None:
|
|
136
|
+
app = FastAPI()
|
|
137
|
+
app.add_middleware(SecurityHeadersMiddleware, csp="")
|
|
138
|
+
|
|
139
|
+
@app.get("/ping")
|
|
140
|
+
async def ping() -> JSONResponse:
|
|
141
|
+
return JSONResponse({"ok": True})
|
|
142
|
+
|
|
143
|
+
client = TestClient(app)
|
|
144
|
+
r = client.get("/ping")
|
|
145
|
+
assert "Content-Security-Policy" not in r.headers
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nene2_python-1.8.7 → nene2_python-1.8.8}/alembic/versions/001_create_notes_and_tags_tables.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nene2_python-1.8.7 → nene2_python-1.8.8}/tests/nene2/middleware/test_simple_domain_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|