cello-framework 1.2.1__tar.gz → 1.2.2__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.
- {cello_framework-1.2.1 → cello_framework-1.2.2}/CLAUDE.md +3 -2
- {cello_framework-1.2.1 → cello_framework-1.2.2}/Cargo.lock +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.2}/Cargo.toml +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.2}/PKG-INFO +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v1.2.1.md +58 -0
- cello_framework-1.2.2/docs/releases/v1.2.2.md +131 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/mkdocs.yml +1 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/pyproject.toml +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/__init__.py +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/auth.rs +4 -4
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/csrf.rs +4 -3
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/guards.rs +2 -2
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/mod.rs +13 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/rate_limit.rs +5 -5
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/session.rs +3 -3
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/minijinja_engine.rs +5 -5
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/test_cello.py +3 -3
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/test_minijinja.py +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.2}/.github/workflows/ci.yml +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/.github/workflows/docs.yml +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/.github/workflows/publish.yml +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/.gitignore +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/CONTRIBUTING.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/ENTERPRISE_ROADMAP.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/LICENSE +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/PUBLISHING.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/README.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/README.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/benchmark.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/README.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/apps/__init__.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/apps/blacksheep_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/apps/cello_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/apps/fastapi_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/apps/robyn_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/requirements.txt +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/compare/run_benchmarks.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/benchmarks/quick_bench.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/cello.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/community/code-of-conduct.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/community/contributing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/community/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/community/support.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/deployment/docker.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/deployment/kubernetes.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/deployment/service-mesh.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/integration/database.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/integration/graphql.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/integration/grpc.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/integration/message-queues.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/observability/health-checks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/observability/metrics.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/observability/opentelemetry.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/observability/tracing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/roadmap.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/background-tasks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/file-storage.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/fullstack.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/graphql.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/microservices.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/realtime-dashboard.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/redis-caching.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/basic/database.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/basic/forms.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/basic/hello-world.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/basic/jwt-auth.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/basic/query-params.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/basic/rest-api.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/enterprise/api-gateway.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/enterprise/event-sourcing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/enterprise/health-checks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/enterprise/multi-tenant.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/enterprise/oauth2.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/enterprise/rate-limiting.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/adaptive-rate-limiting.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/advanced-middleware.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/advanced-patterns.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/all-features.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/api-protocols.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/async-handlers.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/auto-openapi.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/blueprints-advanced.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/circuit-breaker.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/cluster-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/complete-showcase.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/comprehensive-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/database-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/dto-validation.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/enterprise-config.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/guards.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/hello-world.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/lifecycle-hooks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/middleware-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/minijinja-advanced.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/minijinja-basic.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/minijinja-blog.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/minijinja-emails.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/minijinja-forms.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/minijinja-macros.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/security.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/simple-api.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/smart-caching.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/real/streaming-sse.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/advanced/background-tasks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/advanced/dependency-injection.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/advanced/dto-validation.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/advanced/file-uploads.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/advanced/static-files.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/advanced/templates.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/core/async.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/core/blueprints.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/core/requests.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/core/responses.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/core/routing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/caching.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/circuit-breaker.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/compression.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/cors.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/logging.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/overview.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/middleware/rate-limiting.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/minijinja-templates.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/realtime/sse.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/realtime/websocket.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/authentication.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/csrf.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/guards.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/headers.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/jwt.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/overview.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/security/sessions.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/features/templates.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/getting-started/configuration.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/getting-started/first-app.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/getting-started/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/getting-started/installation.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/getting-started/project-structure.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/getting-started/quickstart.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/includes/abbreviations.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/javascripts/extra.js +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/guides/best-practices.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/guides/deployment.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/guides/error-handling.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/guides/performance.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/guides/testing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/patterns/cqrs.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/patterns/event-driven.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/patterns/repository.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/patterns/service-layer.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/tutorials/auth-system.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/tutorials/chat-app.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/tutorials/microservices.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/learn/tutorials/rest-api.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/logo-full.png +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/logo-icon.svg +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/logo.jpg +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/logo.svg +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/overrides/main.html +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/app.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/blueprint.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/context.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/guards.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/middleware.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/request.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/api/response.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/cli.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/config/middleware.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/config/security.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/config/server.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/errors.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/reference/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/changelog.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/migration.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.10.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.3.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.4.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.5.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.6.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.7.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.8.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v0.9.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v1.0.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v1.0.1.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v1.1.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/releases/v1.2.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/requirements.txt +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/stylesheets/extra.css +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/docs/tags.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/adaptive_rate_limit.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/advanced.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/advanced_middleware.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/advanced_patterns_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/all_features_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/api_protocols_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/async_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/auto_openapi_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/circuit_breaker.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/cluster_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/complete_showcase.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/comprehensive_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/database_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/dto_validation.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/enterprise.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/guards.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/hello.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/lifecycle_hooks.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/middleware_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/minijinja_advanced.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/minijinja_basic.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/minijinja_blog.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/minijinja_emails.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/minijinja_forms.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/minijinja_macros.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/security.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/simple_api.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/smart_caching.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/examples/streaming_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/cqrs.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/database.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/eventsourcing.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/graphql.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/grpc.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/guards.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/messaging.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/saga.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/python/cello/validation.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/arena.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/background.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/blueprint.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/context.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/dependency.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/dto.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/error.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/handler.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/http_client.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/json.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/lib.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/lifecycle.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/body_limit.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/cache.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/circuit_breaker.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/cors.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/cqrs.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/database.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/etag.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/eventsourcing.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/exception_handler.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/graphql.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/grpc.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/health.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/messaging.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/prometheus.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/redis.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/request_id.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/saga.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/security.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/static_files.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/middleware/telemetry.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/multipart.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/openapi.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/request/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/request/multipart_streaming.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/request/parsing.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/response/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/response/streaming.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/response/xml.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/router.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/routing/constraints.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/routing/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/server/cluster.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/server/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/server/protocols.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/sse.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/template.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/timeout.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/src/websocket.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/verify_adaptive.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/verify_caching.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/verify_circuit_breaker.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/verify_dto.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/verify_guards_impl.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.2}/tests/verify_lifecycle.py +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Cello** is an ultra-fast, Rust-powered Python async web framework designed to achieve C-level performance on the hot path while maintaining Python's developer experience. It's the successor to frameworks like FastAPI, Robyn, and Litestar, combining their best features with pure Rust implementation for maximum performance.
|
|
6
6
|
|
|
7
|
-
**Version:** 1.
|
|
7
|
+
**Version:** 1.2.2
|
|
8
8
|
**License:** MIT
|
|
9
9
|
**Python Requirement:** 3.12+
|
|
10
10
|
**Author:** Jagadeesh Katla
|
|
@@ -346,7 +346,8 @@ def handle_value_error(request, exc):
|
|
|
346
346
|
|
|
347
347
|
## Version History
|
|
348
348
|
|
|
349
|
-
- **v1.2.
|
|
349
|
+
- **v1.2.2**: Security & bug fixes — CSRF `HttpOnly` on double-submit cookie (critical, broke all AJAX CSRF); all middleware `skip_path` prefix bypass fixed via `path_matches_skip()` helper; `FixedWindowStore` window_start never updated after reset; unused `mut` cleaned in minijinja tests
|
|
350
|
+
- **v1.2.1**: Bug fixes — server port never bound (`pyo3_asyncio` replaced with native `py.allow_threads` + `tokio::block_on`); `ProblemDetails` was missing from Python module export; `And`/`Or` guards now accept both `*args` and list styles; CSRF `HttpOnly` removed from double-submit cookie (JS must read it); `FixedWindowStore` window_start never updated after reset; all middleware `skip_path` used raw `starts_with` allowing prefix bypass; doc corrections (`type_uri` not `type_url`)
|
|
350
351
|
- **v1.2.0**: Bug fixes (shutdown coroutine never awaited, KeyboardInterrupt in shutdown handler, `request.redis` AttributeError); Redis Lua scripting (`eval`, `evalsha`, `script_load`); Rust-native `AsyncClient` backed by `reqwest + Tokio` — GIL never held during HTTP I/O, HTTP/2, gzip, rustls
|
|
351
352
|
- **v1.1.0**: MiniJinja Jinja2-compatible template engine (`MiniJinjaEngine`, `App.enable_templates()`, `App.render()`, `App.render_string()`); minijinja 2 Rust crate; HTML auto-escaping; globals; 47 new tests; 6 examples
|
|
352
353
|
- **v1.0.1**: Cross-platform fixes (Windows multi-worker, signal handling, UNC paths; ARM JSON fallback; Linux-only CPU affinity), async compatibility fixes (handler validation, guards, cache decorator, blueprints), guards and database exports in `__all__`
|
|
@@ -100,6 +100,64 @@ admin_or_editor = Or(RoleGuard(["admin"]), RoleGuard(["editor"]))
|
|
|
100
100
|
|
|
101
101
|
---
|
|
102
102
|
|
|
103
|
+
### CSRF Cookie Readable by JavaScript (Security)
|
|
104
|
+
|
|
105
|
+
**Symptom:** AJAX/SPA applications using CSRF protection failed all POST/PUT/DELETE requests with 403.
|
|
106
|
+
|
|
107
|
+
**Root cause:** The CSRF double-submit cookie was set with the `HttpOnly` flag. This prevents
|
|
108
|
+
JavaScript from reading the cookie value, making it impossible for the frontend to copy it into
|
|
109
|
+
the `X-CSRF-Token` header — which is the entire point of the double-submit pattern.
|
|
110
|
+
|
|
111
|
+
**Fix:** Removed `HttpOnly` from the CSRF cookie. The CSRF cookie is intentionally readable by
|
|
112
|
+
JavaScript; the session cookie (where sensitive data lives) remains `HttpOnly`.
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Now works — JS can read the CSRF cookie and send it back
|
|
116
|
+
const csrf = document.cookie.match(/_csrf=([^;]+)/)?.[1];
|
|
117
|
+
fetch("/api/data", {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: { "X-CSRF-Token": csrf },
|
|
120
|
+
body: JSON.stringify(data),
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Rate Limiter Fixed-Window Counter Reset (Bug)
|
|
127
|
+
|
|
128
|
+
**Symptom:** After the first rate-limit window expired, the fixed-window counter reset to zero
|
|
129
|
+
on every single subsequent request, making rate limiting completely ineffective.
|
|
130
|
+
|
|
131
|
+
**Root cause:** When the time window rolled over, `entry.count` was reset to 0 but
|
|
132
|
+
`entry.window_start` was never updated to the new window. Every request in the new window
|
|
133
|
+
saw `window_start != current_window` and reset the counter again.
|
|
134
|
+
|
|
135
|
+
**Fix:** Update `entry.window_start = current_window` alongside the count reset.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### Auth Skip-Path Prefix Bypass (Security)
|
|
140
|
+
|
|
141
|
+
**Symptom:** Calling `skip_path("/health")` to exclude the health endpoint from authentication
|
|
142
|
+
also excluded `/healthz`, `/healthy`, and any other path starting with `/health`.
|
|
143
|
+
|
|
144
|
+
**Root cause:** Path matching used `request.path.starts_with(skip_path)` without checking that
|
|
145
|
+
the following character is `/` or end-of-string.
|
|
146
|
+
|
|
147
|
+
**Fix:** Path is now only skipped when it equals the pattern exactly or starts with
|
|
148
|
+
`{pattern}/`. Affects `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `RateLimitMiddleware`,
|
|
149
|
+
`SessionMiddleware`, `CsrfMiddleware`, and `GuardsMiddleware`.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Before: /healthz would also be skipped (security bypass)
|
|
153
|
+
jwt.skip_path("/health")
|
|
154
|
+
|
|
155
|
+
# After: only /health and /health/* are skipped
|
|
156
|
+
jwt.skip_path("/health")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
103
161
|
## Upgrade
|
|
104
162
|
|
|
105
163
|
```bash
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: v1.2.2 Release Notes
|
|
3
|
+
description: Cello Framework v1.2.2 — Security & Bug Fixes
|
|
4
|
+
tags:
|
|
5
|
+
- v1.2.2
|
|
6
|
+
- Release Notes
|
|
7
|
+
- Security
|
|
8
|
+
- Bug Fixes
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Cello v1.2.2 — Security & Bug Fixes
|
|
12
|
+
|
|
13
|
+
**Release Date:** June 14, 2026
|
|
14
|
+
**License:** MIT
|
|
15
|
+
**Python:** 3.12+
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Cello v1.2.2 is a patch release that fixes one critical security bug, one security
|
|
22
|
+
vulnerability, and two correctness bugs discovered during a thorough code review of v1.2.1.
|
|
23
|
+
All changes are fully backwards-compatible.
|
|
24
|
+
|
|
25
|
+
**Upgrade immediately** if you use CSRF protection or authentication middleware skip-paths.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Security Fixes
|
|
30
|
+
|
|
31
|
+
### CSRF Double-Submit Cookie Was `HttpOnly` (Critical)
|
|
32
|
+
|
|
33
|
+
**Symptom:** AJAX applications and SPAs using `CsrfMiddleware` received 403 on every
|
|
34
|
+
POST/PUT/DELETE request, even when the CSRF token was correctly set.
|
|
35
|
+
|
|
36
|
+
**Root cause:** The CSRF double-submit cookie was set with `HttpOnly`, which prevents
|
|
37
|
+
JavaScript from reading cookie values. The double-submit pattern *requires* JavaScript
|
|
38
|
+
to read the cookie and copy its value into the `X-CSRF-Token` request header. With
|
|
39
|
+
`HttpOnly` set, this was impossible — every state-changing request failed CSRF validation.
|
|
40
|
+
|
|
41
|
+
**Fix:** Removed `HttpOnly` from the CSRF token cookie. The CSRF cookie is intentionally
|
|
42
|
+
readable by JavaScript (that is the design). Your session cookie, which holds sensitive
|
|
43
|
+
data, remains `HttpOnly`.
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// Now works — JavaScript can read the CSRF cookie
|
|
47
|
+
const csrf = document.cookie.match(/_csrf=([^;]+)/)?.[1];
|
|
48
|
+
|
|
49
|
+
fetch("/api/submit", {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "X-CSRF-Token": csrf, "Content-Type": "application/json" },
|
|
52
|
+
body: JSON.stringify({ name: "Alice" }),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### Auth Middleware `skip_path` Allowed Prefix Bypass (Security)
|
|
59
|
+
|
|
60
|
+
**Symptom:** Calling `jwt.skip_path("/health")` to exclude the health-check endpoint
|
|
61
|
+
from authentication also excluded `/healthz`, `/healthy`, `/health-admin`, and any
|
|
62
|
+
other path whose string representation started with `/health`.
|
|
63
|
+
|
|
64
|
+
**Root cause:** All middleware used `request.path.starts_with(skip_path)` with no
|
|
65
|
+
boundary check. An attacker aware of a skipped path could craft a URL that bypasses
|
|
66
|
+
authentication.
|
|
67
|
+
|
|
68
|
+
**Affected middleware:** `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `RateLimitMiddleware`,
|
|
69
|
+
`SessionMiddleware`, `CsrfMiddleware`, `GuardsMiddleware`.
|
|
70
|
+
|
|
71
|
+
**Fix:** Paths are now only skipped when:
|
|
72
|
+
|
|
73
|
+
- The path **exactly equals** the pattern, or
|
|
74
|
+
- The path **starts with** `{pattern}/` (sub-path match).
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# Before: /healthz was also skipped — security bypass
|
|
78
|
+
jwt_auth.skip_path("/health")
|
|
79
|
+
|
|
80
|
+
# After: only /health and /health/* are skipped
|
|
81
|
+
jwt_auth.skip_path("/health")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Bug Fixes
|
|
87
|
+
|
|
88
|
+
### Rate Limiter Fixed-Window Counter Broke After First Window
|
|
89
|
+
|
|
90
|
+
**Symptom:** After the first time-window expired, the `FixedWindowStore` rate limiter
|
|
91
|
+
stopped enforcing limits entirely — every request was allowed regardless of how many
|
|
92
|
+
had been made.
|
|
93
|
+
|
|
94
|
+
**Root cause:** When the window rolled over, the per-client entry's `count` was reset
|
|
95
|
+
to 0 but `window_start` was never updated to the new window ID. Every subsequent
|
|
96
|
+
request also saw `window_start != current_window` and reset the counter to 0 again,
|
|
97
|
+
making the limiter permanently ineffective after the first window.
|
|
98
|
+
|
|
99
|
+
**Fix:** `window_start` is now updated to `current_window` alongside the count reset.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### Unused `mut` Warnings in Template Engine Tests
|
|
104
|
+
|
|
105
|
+
Five test functions in `minijinja_engine.rs` declared `let mut env = Environment::new()`
|
|
106
|
+
where `mut` was not needed (the variable was never mutated after construction). These
|
|
107
|
+
were cleaned up to eliminate Clippy warnings.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Files Changed
|
|
112
|
+
|
|
113
|
+
| File | Change |
|
|
114
|
+
|------|--------|
|
|
115
|
+
| `src/middleware/csrf.rs` | Remove `HttpOnly` from CSRF cookie |
|
|
116
|
+
| `src/middleware/mod.rs` | Add `path_matches_skip()` shared helper |
|
|
117
|
+
| `src/middleware/auth.rs` | Use `path_matches_skip()` in JWT/Basic/ApiKey |
|
|
118
|
+
| `src/middleware/session.rs` | Use `path_matches_skip()` |
|
|
119
|
+
| `src/middleware/rate_limit.rs` | Use `path_matches_skip()`; fix `window_start` reset |
|
|
120
|
+
| `src/middleware/guards.rs` | Use `path_matches_skip()` |
|
|
121
|
+
| `src/minijinja_engine.rs` | Remove unused `mut` from test bindings |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Upgrade
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install --upgrade cello-framework==1.2.2
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Drop-in replacement for v1.2.1. No API changes.
|
|
@@ -14,7 +14,7 @@ use std::sync::Arc;
|
|
|
14
14
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
15
15
|
use subtle::ConstantTimeEq;
|
|
16
16
|
|
|
17
|
-
use super::{Middleware, MiddlewareAction, MiddlewareError, MiddlewareResult};
|
|
17
|
+
use super::{path_matches_skip, Middleware, MiddlewareAction, MiddlewareError, MiddlewareResult};
|
|
18
18
|
use crate::request::Request;
|
|
19
19
|
use crate::response::Response;
|
|
20
20
|
|
|
@@ -299,7 +299,7 @@ impl Middleware for JwtAuth {
|
|
|
299
299
|
fn before(&self, request: &mut Request) -> MiddlewareResult {
|
|
300
300
|
// Check if path should be skipped
|
|
301
301
|
for skip_path in &self.skip_paths {
|
|
302
|
-
if request.path
|
|
302
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
303
303
|
return Ok(MiddlewareAction::Continue);
|
|
304
304
|
}
|
|
305
305
|
}
|
|
@@ -421,7 +421,7 @@ impl Middleware for BasicAuth {
|
|
|
421
421
|
fn before(&self, request: &mut Request) -> MiddlewareResult {
|
|
422
422
|
// Check if path should be skipped
|
|
423
423
|
for skip_path in &self.skip_paths {
|
|
424
|
-
if request.path
|
|
424
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
425
425
|
return Ok(MiddlewareAction::Continue);
|
|
426
426
|
}
|
|
427
427
|
}
|
|
@@ -591,7 +591,7 @@ impl Middleware for ApiKeyAuth {
|
|
|
591
591
|
fn before(&self, request: &mut Request) -> MiddlewareResult {
|
|
592
592
|
// Check if path should be skipped
|
|
593
593
|
for skip_path in &self.skip_paths {
|
|
594
|
-
if request.path
|
|
594
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
595
595
|
return Ok(MiddlewareAction::Continue);
|
|
596
596
|
}
|
|
597
597
|
}
|
|
@@ -13,7 +13,7 @@ use sha2::Sha256;
|
|
|
13
13
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
14
14
|
use subtle::ConstantTimeEq;
|
|
15
15
|
|
|
16
|
-
use super::{Middleware, MiddlewareAction, MiddlewareError, MiddlewareResult};
|
|
16
|
+
use super::{path_matches_skip, Middleware, MiddlewareAction, MiddlewareError, MiddlewareResult};
|
|
17
17
|
use crate::request::Request;
|
|
18
18
|
use crate::response::Response;
|
|
19
19
|
|
|
@@ -316,7 +316,7 @@ impl CsrfMiddleware {
|
|
|
316
316
|
|
|
317
317
|
/// Check if path should skip CSRF.
|
|
318
318
|
fn should_skip(&self, path: &str) -> bool {
|
|
319
|
-
self.config.skip_paths.iter().any(|p| path
|
|
319
|
+
self.config.skip_paths.iter().any(|p| path_matches_skip(path, p))
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
/// Extract token from cookie.
|
|
@@ -389,7 +389,8 @@ impl CsrfMiddleware {
|
|
|
389
389
|
cookie.push_str("; Secure");
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
cookie
|
|
392
|
+
// CSRF double-submit cookie must NOT be HttpOnly — JavaScript needs to
|
|
393
|
+
// read this value to send it back in the X-CSRF-Token request header.
|
|
393
394
|
cookie.push_str(&format!("; SameSite={}", self.config.cookie_same_site));
|
|
394
395
|
|
|
395
396
|
cookie
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
use std::collections::HashSet;
|
|
11
11
|
use std::sync::Arc;
|
|
12
12
|
|
|
13
|
-
use super::{Middleware, MiddlewareAction, MiddlewareError, MiddlewareResult};
|
|
13
|
+
use super::{path_matches_skip, Middleware, MiddlewareAction, MiddlewareError, MiddlewareResult};
|
|
14
14
|
use crate::request::Request;
|
|
15
15
|
use pyo3::prelude::*;
|
|
16
16
|
|
|
@@ -547,7 +547,7 @@ impl Middleware for GuardsMiddleware {
|
|
|
547
547
|
|
|
548
548
|
// Check if path should be skipped
|
|
549
549
|
for skip_path in &self.skip_paths {
|
|
550
|
-
if request.path
|
|
550
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
551
551
|
return Ok(MiddlewareAction::Continue);
|
|
552
552
|
}
|
|
553
553
|
}
|
|
@@ -201,6 +201,19 @@ impl MiddlewareError {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/// Check if a request path matches a skip pattern.
|
|
205
|
+
///
|
|
206
|
+
/// Matches when:
|
|
207
|
+
/// - `path == pattern` (exact match), or
|
|
208
|
+
/// - `path` starts with `pattern/` (sub-path match)
|
|
209
|
+
///
|
|
210
|
+
/// Without the trailing-slash check, a pattern like `/health` would also
|
|
211
|
+
/// skip `/healthz`, allowing an auth bypass.
|
|
212
|
+
#[inline]
|
|
213
|
+
pub(super) fn path_matches_skip(path: &str, pattern: &str) -> bool {
|
|
214
|
+
path == pattern || path.starts_with(&format!("{}/", pattern))
|
|
215
|
+
}
|
|
216
|
+
|
|
204
217
|
impl std::fmt::Display for MiddlewareError {
|
|
205
218
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
206
219
|
if let Some(code) = &self.code {
|
|
@@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|
|
11
11
|
use std::sync::Arc;
|
|
12
12
|
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|
13
13
|
|
|
14
|
-
use super::{Middleware, MiddlewareAction, MiddlewareResult};
|
|
14
|
+
use super::{path_matches_skip, Middleware, MiddlewareAction, MiddlewareResult};
|
|
15
15
|
use crate::request::Request;
|
|
16
16
|
use crate::response::Response;
|
|
17
17
|
|
|
@@ -563,7 +563,7 @@ impl RateLimitStore for FixedWindowStore {
|
|
|
563
563
|
let current_window = self.current_window();
|
|
564
564
|
let reset_time = (current_window + 1) * self.window_seconds;
|
|
565
565
|
|
|
566
|
-
let entry = self
|
|
566
|
+
let mut entry = self
|
|
567
567
|
.windows
|
|
568
568
|
.entry(key.to_string())
|
|
569
569
|
.or_insert_with(|| FixedWindowState {
|
|
@@ -571,10 +571,10 @@ impl RateLimitStore for FixedWindowStore {
|
|
|
571
571
|
window_start: current_window,
|
|
572
572
|
});
|
|
573
573
|
|
|
574
|
-
//
|
|
574
|
+
// Reset counter when window rolls over
|
|
575
575
|
if entry.window_start != current_window {
|
|
576
576
|
entry.count.store(0, Ordering::SeqCst);
|
|
577
|
-
|
|
577
|
+
entry.window_start = current_window;
|
|
578
578
|
}
|
|
579
579
|
|
|
580
580
|
let count = entry.count.fetch_add(1, Ordering::SeqCst);
|
|
@@ -848,7 +848,7 @@ impl Middleware for RateLimitMiddleware {
|
|
|
848
848
|
fn before(&self, request: &mut Request) -> MiddlewareResult {
|
|
849
849
|
// Check if path should be skipped
|
|
850
850
|
for skip_path in &self.skip_paths {
|
|
851
|
-
if request.path
|
|
851
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
852
852
|
return Ok(MiddlewareAction::Continue);
|
|
853
853
|
}
|
|
854
854
|
}
|
|
@@ -14,7 +14,7 @@ use std::collections::HashMap;
|
|
|
14
14
|
use std::sync::Arc;
|
|
15
15
|
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|
16
16
|
|
|
17
|
-
use super::{Middleware, MiddlewareAction, MiddlewareResult};
|
|
17
|
+
use super::{path_matches_skip, Middleware, MiddlewareAction, MiddlewareResult};
|
|
18
18
|
use crate::request::Request;
|
|
19
19
|
use crate::response::Response;
|
|
20
20
|
|
|
@@ -486,7 +486,7 @@ impl Middleware for SessionMiddleware {
|
|
|
486
486
|
fn before(&self, request: &mut Request) -> MiddlewareResult {
|
|
487
487
|
// Check if path should be skipped
|
|
488
488
|
for skip_path in &self.skip_paths {
|
|
489
|
-
if request.path
|
|
489
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
490
490
|
return Ok(MiddlewareAction::Continue);
|
|
491
491
|
}
|
|
492
492
|
}
|
|
@@ -513,7 +513,7 @@ impl Middleware for SessionMiddleware {
|
|
|
513
513
|
fn after(&self, request: &Request, response: &mut Response) -> MiddlewareResult {
|
|
514
514
|
// Check if path was skipped
|
|
515
515
|
for skip_path in &self.skip_paths {
|
|
516
|
-
if request.path
|
|
516
|
+
if path_matches_skip(&request.path, skip_path) {
|
|
517
517
|
return Ok(MiddlewareAction::Continue);
|
|
518
518
|
}
|
|
519
519
|
}
|
|
@@ -361,7 +361,7 @@ mod tests {
|
|
|
361
361
|
|
|
362
362
|
#[test]
|
|
363
363
|
fn test_render_if_block() {
|
|
364
|
-
let
|
|
364
|
+
let env = Environment::new();
|
|
365
365
|
let tmpl = "{% if active %}yes{% else %}no{% endif %}";
|
|
366
366
|
let result = env
|
|
367
367
|
.render_str(tmpl, serde_json::json!({ "active": true }))
|
|
@@ -371,7 +371,7 @@ mod tests {
|
|
|
371
371
|
|
|
372
372
|
#[test]
|
|
373
373
|
fn test_render_for_loop() {
|
|
374
|
-
let
|
|
374
|
+
let env = Environment::new();
|
|
375
375
|
let tmpl = "{% for i in items %}{{ i }},{% endfor %}";
|
|
376
376
|
let result = env
|
|
377
377
|
.render_str(tmpl, serde_json::json!({ "items": [1, 2, 3] }))
|
|
@@ -381,7 +381,7 @@ mod tests {
|
|
|
381
381
|
|
|
382
382
|
#[test]
|
|
383
383
|
fn test_render_filters() {
|
|
384
|
-
let
|
|
384
|
+
let env = Environment::new();
|
|
385
385
|
let result = env
|
|
386
386
|
.render_str("{{ name | upper }}", serde_json::json!({ "name": "cello" }))
|
|
387
387
|
.unwrap();
|
|
@@ -390,7 +390,7 @@ mod tests {
|
|
|
390
390
|
|
|
391
391
|
#[test]
|
|
392
392
|
fn test_render_nested_dict() {
|
|
393
|
-
let
|
|
393
|
+
let env = Environment::new();
|
|
394
394
|
let ctx = serde_json::json!({
|
|
395
395
|
"user": { "name": "Alice", "age": 30 }
|
|
396
396
|
});
|
|
@@ -445,7 +445,7 @@ mod tests {
|
|
|
445
445
|
|
|
446
446
|
#[test]
|
|
447
447
|
fn test_json_filter() {
|
|
448
|
-
let
|
|
448
|
+
let env = Environment::new();
|
|
449
449
|
let result = env
|
|
450
450
|
.render_str(
|
|
451
451
|
"{{ data | tojson }}",
|
|
@@ -1042,7 +1042,7 @@ def test_version():
|
|
|
1042
1042
|
"""Test that version is 1.2.0."""
|
|
1043
1043
|
import cello
|
|
1044
1044
|
|
|
1045
|
-
assert cello.__version__ == "1.2.
|
|
1045
|
+
assert cello.__version__ == "1.2.2"
|
|
1046
1046
|
|
|
1047
1047
|
|
|
1048
1048
|
def test_all_exports():
|
|
@@ -2203,7 +2203,7 @@ def test_version_v090():
|
|
|
2203
2203
|
"""Test that version is 1.2.0 (updated from 0.9.0)."""
|
|
2204
2204
|
import cello
|
|
2205
2205
|
|
|
2206
|
-
assert cello.__version__ == "1.2.
|
|
2206
|
+
assert cello.__version__ == "1.2.2"
|
|
2207
2207
|
|
|
2208
2208
|
|
|
2209
2209
|
def test_v090_exports_in_all():
|
|
@@ -3989,7 +3989,7 @@ def test_version_v0100():
|
|
|
3989
3989
|
"""Test that version is 1.2.0."""
|
|
3990
3990
|
import cello
|
|
3991
3991
|
|
|
3992
|
-
assert cello.__version__ == "1.2.
|
|
3992
|
+
assert cello.__version__ == "1.2.2"
|
|
3993
3993
|
|
|
3994
3994
|
|
|
3995
3995
|
def test_v0100_exports_in_all():
|
|
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
|
{cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/integration/message-queues.md
RENAMED
|
File without changes
|
{cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/observability/health-checks.md
RENAMED
|
File without changes
|
|
File without changes
|
{cello_framework-1.2.1 → cello_framework-1.2.2}/docs/enterprise/observability/opentelemetry.md
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
|
{cello_framework-1.2.1 → cello_framework-1.2.2}/docs/examples/advanced/realtime-dashboard.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|