
* upgrade to latest pip for in_docker_test_script.sh * Bump gunicorn to 22.0.0 (security) Changelog: https://docs.gunicorn.org/en/stable/news.html#id1 use utime to notify workers liveness migrate setup to pyproject.toml fix numerous security vulnerabilities in HTTP parser (closing some request smuggling vectors) parsing additional requests is no longer attempted past unsupported request framing on HTTP versions < 1.1 support for chunked transfer is refused (only used in exploits) requests conflicting configured or passed SCRIPT_NAME now produce a verbose error Trailer fields are no longer inspected for headers indicating secure scheme support Python 3.12 ** Breaking changes ** minimum version is Python 3.7 the limitations on valid characters in the HTTP method have been bounded to Internet Standards requests specifying unsupported transfer coding (order) are refused by default (rare) HTTP methods are no longer casefolded by default (IANA method registry contains none affected) HTTP methods containing the number sign (#) are no longer accepted by default (rare) HTTP versions < 1.0 or >= 2.0 are no longer accepted by default (rare, only HTTP/1.1 is supported) HTTP versions consisting of multiple digits or containing a prefix/suffix are no longer accepted HTTP header field names Gunicorn cannot safely map to variables are silently dropped, as in other software HTTP headers with empty field name are refused by default (no legitimate use cases, used in exploits) requests with both Transfer-Encoding and Content-Length are refused by default (such a message might indicate an attempt to perform request smuggling) empty transfer codings are no longer permitted (reportedly seen with really old & broken proxies) ** SECURITY ** fix CVE-2024-1135 * Remove TYPE_CHECKING guard for pydantic v1 imports Retain TYPE_CHECKING guard in v1 fallback to force pyright into pydantic.v1 namespace * Run unit tests with pydantic v1 now that v2 is installed via poetry
142 lines
4.1 KiB
Python
142 lines
4.1 KiB
Python
"""Define the base Reflex class."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import TYPE_CHECKING, Any, List, Type
|
|
|
|
try:
|
|
import pydantic.v1 as pydantic
|
|
from pydantic.v1 import BaseModel
|
|
from pydantic.v1.fields import ModelField
|
|
except ModuleNotFoundError:
|
|
if not TYPE_CHECKING:
|
|
import pydantic
|
|
from pydantic import BaseModel
|
|
from pydantic.fields import ModelField # type: ignore
|
|
|
|
|
|
from reflex import constants
|
|
|
|
|
|
def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None:
|
|
"""Ensure that the field's name does not shadow an existing attribute of the model.
|
|
|
|
Args:
|
|
bases: List of base models to check for shadowed attrs.
|
|
field_name: name of attribute
|
|
|
|
Raises:
|
|
NameError: If state var field shadows another in its parent state
|
|
"""
|
|
reload = os.getenv(constants.RELOAD_CONFIG) == "True"
|
|
for base in bases:
|
|
try:
|
|
if not reload and getattr(base, field_name, None):
|
|
pass
|
|
except TypeError as te:
|
|
raise NameError(
|
|
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
|
|
f'use a different field name instead".'
|
|
) from te
|
|
|
|
|
|
# monkeypatch pydantic validate_field_name method to skip validating
|
|
# shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
|
|
pydantic.main.validate_field_name = validate_field_name # type: ignore
|
|
|
|
|
|
class Base(pydantic.BaseModel): # pyright: ignore [reportUnboundVariable]
|
|
"""The base class subclassed by all Reflex classes.
|
|
|
|
This class wraps Pydantic and provides common methods such as
|
|
serialization and setting fields.
|
|
|
|
Any data structure that needs to be transferred between the
|
|
frontend and backend should subclass this class.
|
|
"""
|
|
|
|
class Config:
|
|
"""Pydantic config."""
|
|
|
|
arbitrary_types_allowed = True
|
|
use_enum_values = True
|
|
extra = "allow"
|
|
|
|
def json(self) -> str:
|
|
"""Convert the object to a json string.
|
|
|
|
Returns:
|
|
The object as a json string.
|
|
"""
|
|
from reflex.utils.serializers import serialize
|
|
|
|
return self.__config__.json_dumps( # type: ignore
|
|
self.dict(),
|
|
default=serialize,
|
|
)
|
|
|
|
def set(self, **kwargs):
|
|
"""Set multiple fields and return the object.
|
|
|
|
Args:
|
|
**kwargs: The fields and values to set.
|
|
|
|
Returns:
|
|
The object with the fields set.
|
|
"""
|
|
for key, value in kwargs.items():
|
|
setattr(self, key, value)
|
|
return self
|
|
|
|
@classmethod
|
|
def get_fields(cls) -> dict[str, Any]:
|
|
"""Get the fields of the object.
|
|
|
|
Returns:
|
|
The fields of the object.
|
|
"""
|
|
return cls.__fields__
|
|
|
|
@classmethod
|
|
def add_field(cls, var: Any, default_value: Any):
|
|
"""Add a pydantic field after class definition.
|
|
|
|
Used by State.add_var() to correctly handle the new variable.
|
|
|
|
Args:
|
|
var: The variable to add a pydantic field for.
|
|
default_value: The default value of the field
|
|
"""
|
|
new_field = ModelField.infer(
|
|
name=var._var_name,
|
|
value=default_value,
|
|
annotation=var._var_type,
|
|
class_validators=None,
|
|
config=cls.__config__, # type: ignore
|
|
)
|
|
cls.__fields__.update({var._var_name: new_field})
|
|
|
|
def get_value(self, key: str) -> Any:
|
|
"""Get the value of a field.
|
|
|
|
Args:
|
|
key: The key of the field.
|
|
|
|
Returns:
|
|
The value of the field.
|
|
"""
|
|
if isinstance(key, str) and key in self.__fields__:
|
|
# Seems like this function signature was wrong all along?
|
|
# If the user wants a field that we know of, get it and pass it off to _get_value
|
|
key = getattr(self, key)
|
|
return self._get_value(
|
|
key,
|
|
to_dict=True,
|
|
by_alias=False,
|
|
include=None,
|
|
exclude=None,
|
|
exclude_unset=False,
|
|
exclude_defaults=False,
|
|
exclude_none=False,
|
|
)
|