plain 0.57.0__py3-none-any.whl → 0.59.0__py3-none-any.whl
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.
- plain/CHANGELOG.md +28 -0
- plain/cli/agent.py +6 -0
- plain/cli/upgrade.py +1 -0
- plain/csrf/README.md +61 -9
- plain/csrf/middleware.py +95 -406
- plain/exceptions.py +29 -13
- plain/forms/README.md +0 -2
- plain/internal/middleware/https.py +5 -3
- plain/runtime/global_settings.py +51 -52
- plain/runtime/utils.py +20 -0
- plain/templates/README.md +0 -1
- plain/test/client.py +2 -10
- plain/views/README.md +0 -2
- plain/views/objects.py +39 -66
- plain/views/templates.py +6 -18
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/METADATA +1 -1
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/RECORD +20 -20
- plain/views/csrf.py +0 -4
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/WHEEL +0 -0
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/entry_points.txt +0 -0
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/licenses/LICENSE +0 -0
plain/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# plain changelog
|
2
2
|
|
3
|
+
## [0.59.0](https://github.com/dropseed/plain/releases/plain@0.59.0) (2025-08-22)
|
4
|
+
|
5
|
+
### What's changed
|
6
|
+
|
7
|
+
- Added new `APP_NAME` setting that defaults to the project name from `pyproject.toml` ([1a4d60e](https://github.com/dropseed/plain/commit/1a4d60e787))
|
8
|
+
- Template views now validate that `get_template_names()` returns a list instead of a string ([428a64f](https://github.com/dropseed/plain/commit/428a64f8cc))
|
9
|
+
- Object views now use cached properties for `.object` and `.objects` to improve performance ([bd0507a](https://github.com/dropseed/plain/commit/bd0507a72c))
|
10
|
+
- Improved `plain upgrade` command to suggest using subagents when there are more than 3 package updates ([497c30d](https://github.com/dropseed/plain/commit/497c30d445))
|
11
|
+
|
12
|
+
### Upgrade instructions
|
13
|
+
|
14
|
+
- In object views, `self.load_object()` is no longer necessary as `self.object` is now a cached property.
|
15
|
+
|
16
|
+
## [0.58.0](https://github.com/dropseed/plain/releases/plain@0.58.0) (2025-08-19)
|
17
|
+
|
18
|
+
### What's changed
|
19
|
+
|
20
|
+
- Complete rewrite of CSRF protection using modern Sec-Fetch-Site headers and origin validation ([955150800c](https://github.com/dropseed/plain/commit/955150800c))
|
21
|
+
- Replaced CSRF view mixin with path-based exemptions using `CSRF_EXEMPT_PATHS` setting ([2a50a9154e](https://github.com/dropseed/plain/commit/2a50a9154e))
|
22
|
+
- Renamed `HTTPS_REDIRECT_EXEMPT` to `HTTPS_REDIRECT_EXEMPT_PATHS` with leading slash requirement ([b53d3bb7a7](https://github.com/dropseed/plain/commit/b53d3bb7a7))
|
23
|
+
- Agent commands now print prompts directly when running in Claude Code or Codex Sandbox environments ([6eaed8ae3b](https://github.com/dropseed/plain/commit/6eaed8ae3b))
|
24
|
+
|
25
|
+
### Upgrade instructions
|
26
|
+
|
27
|
+
- Remove any usage of `CsrfExemptViewMixin` and `request.csrf_exempt` and add exempt paths to the `CSRF_EXEMPT_PATHS` setting instead (ex. `CSRF_EXEMPT_PATHS = [r"^/api/", r"/webhooks/.*"]` -- but consider first whether the view still needs CSRF exemption under the new implementation)
|
28
|
+
- Replace `HTTPS_REDIRECT_EXEMPT` with `HTTPS_REDIRECT_EXEMPT_PATHS` and ensure patterns include leading slash (ex. `[r"^/health$", r"/api/internal/.*"]`)
|
29
|
+
- Remove all CSRF cookie and token related settings - the new implementation doesn't use cookies or tokens (ex. `{{ csrf_input }}` and `{{ csrf_token }}`)
|
30
|
+
|
3
31
|
## [0.57.0](https://github.com/dropseed/plain/releases/plain@0.57.0) (2025-08-15)
|
4
32
|
|
5
33
|
### What's changed
|
plain/cli/agent.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import os
|
1
2
|
import shlex
|
2
3
|
import subprocess
|
3
4
|
|
@@ -20,6 +21,11 @@ def prompt_agent(
|
|
20
21
|
True if the agent command succeeded (or no agent command was provided),
|
21
22
|
False if the agent command failed.
|
22
23
|
"""
|
24
|
+
# Check if running inside an agent and just print the prompt if so
|
25
|
+
if os.environ.get("CLAUDECODE") or os.environ.get("CODEX_SANDBOX"):
|
26
|
+
click.echo(prompt)
|
27
|
+
return True
|
28
|
+
|
23
29
|
if print_only or not agent_command:
|
24
30
|
click.echo(prompt)
|
25
31
|
if not print_only:
|
plain/cli/upgrade.py
CHANGED
@@ -158,6 +158,7 @@ def build_prompt(before_after: dict[str, tuple[str | None, str | None]]) -> str:
|
|
158
158
|
"",
|
159
159
|
"3. **Available tools:**",
|
160
160
|
" - Python shell: `uv run python`",
|
161
|
+
" - If you have a subagents feature and there are more than three packages here, use subagents",
|
161
162
|
"",
|
162
163
|
"4. **Workflow:**",
|
163
164
|
" - Review changelog for each package → Apply changes → Move to next package",
|
plain/csrf/README.md
CHANGED
@@ -1,23 +1,75 @@
|
|
1
1
|
# CSRF
|
2
2
|
|
3
|
-
**Cross-Site Request Forgery (CSRF) protection.**
|
3
|
+
**Cross-Site Request Forgery (CSRF) protection using modern request headers.**
|
4
4
|
|
5
5
|
- [Overview](#overview)
|
6
6
|
- [Usage](#usage)
|
7
|
+
- [CSRF Exempt Paths](#csrf-exempt-paths)
|
8
|
+
- [Trusted Origins](#trusted-origins)
|
7
9
|
|
8
10
|
## Overview
|
9
11
|
|
10
|
-
Plain
|
12
|
+
Plain provides modern CSRF protection based on [Filippo Valsorda's 2025 research](https://words.filippo.io/csrf/) using `Sec-Fetch-Site` headers and origin validation.
|
11
13
|
|
12
14
|
## Usage
|
13
15
|
|
14
|
-
The `CsrfViewMiddleware` is [automatically installed](../internal/handlers/base.py#BUILTIN_BEFORE_MIDDLEWARE)
|
16
|
+
The `CsrfViewMiddleware` is [automatically installed](../internal/handlers/base.py#BUILTIN_BEFORE_MIDDLEWARE) and works transparently. **No changes to your forms or templates are needed.**
|
15
17
|
|
16
|
-
|
18
|
+
## CSRF Exempt Paths
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
In some cases, you may need to disable CSRF protection for specific paths (like API endpoints or webhooks). Configure exempt paths using regex patterns in your settings:
|
21
|
+
|
22
|
+
```python
|
23
|
+
# settings.py
|
24
|
+
CSRF_EXEMPT_PATHS = [
|
25
|
+
r"^/api/", # All API endpoints
|
26
|
+
r"^/api/v\d+/", # Versioned APIs: /api/v1/, /api/v2/, etc.
|
27
|
+
r"/webhooks/.*", # All webhook paths
|
28
|
+
r"/webhooks/github/", # Specific webhook
|
29
|
+
r"/health$", # Exact match for /health endpoint
|
30
|
+
]
|
23
31
|
```
|
32
|
+
|
33
|
+
**Pattern Matching**: Exempt paths use Python regex patterns with `re.search()` against the full URL path including the leading slash.
|
34
|
+
|
35
|
+
**Examples:**
|
36
|
+
|
37
|
+
- `r"^/api/"` - matches `/api/users/`, `/api/posts/`
|
38
|
+
- `r"/webhooks/.*"` - matches `/webhooks/github/push`, `/webhooks/stripe/payment`
|
39
|
+
- `r"/health$"` - matches `/health` but not `/health-check`
|
40
|
+
- `r"^/api/v\d+/"` - matches `/api/v1/users/`, `/api/v2/posts/`
|
41
|
+
|
42
|
+
**Common Use Cases:**
|
43
|
+
|
44
|
+
```python
|
45
|
+
CSRF_EXEMPT_PATHS = [
|
46
|
+
# API endpoints (often consumed by JavaScript/mobile apps)
|
47
|
+
r"^/api/",
|
48
|
+
|
49
|
+
# Webhooks (external services posting data)
|
50
|
+
r"/webhooks/.*",
|
51
|
+
|
52
|
+
# Health checks and monitoring
|
53
|
+
r"/health$",
|
54
|
+
r"/status$",
|
55
|
+
r"/metrics$",
|
56
|
+
|
57
|
+
# File uploads (if using direct POST)
|
58
|
+
r"/upload/",
|
59
|
+
]
|
60
|
+
```
|
61
|
+
|
62
|
+
## Trusted Origins
|
63
|
+
|
64
|
+
In some cases, you may need to allow requests from specific external origins (like API clients or mobile apps). You can configure trusted origins in your settings:
|
65
|
+
|
66
|
+
```python
|
67
|
+
# settings.py
|
68
|
+
CSRF_TRUSTED_ORIGINS = [
|
69
|
+
"https://api.example.com",
|
70
|
+
"https://mobile.example.com:8443",
|
71
|
+
"https://trusted-partner.com",
|
72
|
+
]
|
73
|
+
```
|
74
|
+
|
75
|
+
**Important**: Trusted origins bypass **all** CSRF protection. Only add origins you completely trust, as they can make requests that appear to come from your users.
|