Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:python-dynaconf
User: [email protected]
Usertags: pu

Fixes a minor security issue, all tests in debusine were fine.
debdiff below.

Cheers,
        Moritz

diff -Nru python-dynaconf-3.1.7/debian/changelog 
python-dynaconf-3.1.7/debian/changelog
--- python-dynaconf-3.1.7/debian/changelog      2022-10-28 19:43:27.000000000 
+0200
+++ python-dynaconf-3.1.7/debian/changelog      2026-06-21 23:39:27.000000000 
+0200
@@ -1,3 +1,9 @@
+python-dynaconf (3.1.7-2+deb13u1) trixie; urgency=medium
+
+  * CVE-2026-33154 (Closes: #1131476)
+
+ -- Moritz Mühlenhoff <[email protected]>  Sun, 21 Jun 2026 23:39:27 +0200
+
 python-dynaconf (3.1.7-2) unstable; urgency=medium
 
   [ Debian Janitor ]
diff -Nru python-dynaconf-3.1.7/debian/patches/CVE-2026-33154.patch 
python-dynaconf-3.1.7/debian/patches/CVE-2026-33154.patch
--- python-dynaconf-3.1.7/debian/patches/CVE-2026-33154.patch   1970-01-01 
01:00:00.000000000 +0100
+++ python-dynaconf-3.1.7/debian/patches/CVE-2026-33154.patch   2026-06-21 
23:39:21.000000000 +0200
@@ -0,0 +1,127 @@
+From 2fbb45ee36b8c0caa5b924fe19f3c1a5e8603fa7 Mon Sep 17 00:00:00 2001
+From: Pedro Brochado <[email protected]>
+Date: Fri, 13 Mar 2026 16:24:45 -0300
+Subject: [PATCH] Fix @jinja and @format templating vulnerabilities
+
+--- python-dynaconf-3.1.7.orig/dynaconf/base.py
++++ python-dynaconf-3.1.7/dynaconf/base.py
+@@ -368,6 +368,13 @@ class Settings:
+                 return parse_conf_data(result, tomlfy=True, box_settings=self)
+             return result
+ 
++        # Still keys left, but current result/parent is not a data container
++        if keys and not isinstance(result, (dict, list)):
++            result_type = type(result).__name__
++            raise AttributeError(
++                f"Invalid dotted lookup in {dotted_key}. {name} is a 
{result_type}"
++            )
++
+         # If we've still got key elements to traverse, let's do that.
+         return self._dotted_get(
+             ".".join(keys), default=default, parent=result, cast=cast, 
**kwargs
+@@ -429,7 +436,7 @@ class Settings:
+             self.unset(key)
+             self.execute_loaders(key=key)
+ 
+-        data = (parent or self.store).get(key, default)
++        data = _get_with_default(parent or self.store, key, default)
+         if cast:
+             data = get_converter(cast, data, box_settings=self)
+         return data
+@@ -1216,3 +1223,21 @@ RESERVED_ATTRS = (
+         "_validate_exclude",
+     ]
+ )
++
++# These are special fields defined by Dynaconf, but users can access it
++_PUBLIC_PROPERTIES = [name for name, _ in inspect.getmembers(Settings, lambda
++  x: isinstance(x, property))]
++
++
++def _get_with_default(data: dict | list, key: str, default):
++    if isinstance(data, dict):
++        return data.get(key, default)
++    elif isinstance(data, list):
++        if not key.isdigit():
++            raise ValueError(f"Expected integer, got: {key}")
++        try:
++            return data[int(key)]
++        except KeyError:
++            return default
++    else:
++        raise AttributeError(f"Unknown data container type: {type(data)}")
+--- python-dynaconf-3.1.7.orig/dynaconf/utils/parse_conf.py
++++ python-dynaconf-3.1.7/dynaconf/utils/parse_conf.py
+@@ -1,6 +1,7 @@
+ import json
+ import os
+ import re
++import string
+ import warnings
+ from functools import wraps
+ 
+@@ -12,9 +13,10 @@ from dynaconf.utils.boxing import DynaBo
+ from dynaconf.vendor import toml
+ 
+ try:
+-    from jinja2 import Environment
++    import jinja2
++    from jinja2.sandbox import SandboxedEnvironment
+ 
+-    jinja_env = Environment()
++    jinja_env = SandboxedEnvironment()
+     for p_method in ("abspath", "realpath", "relpath", "dirname", "basename"):
+         jinja_env.filters[p_method] = getattr(os.path, p_method)
+ except ImportError:  # pragma: no cover
+@@ -142,18 +144,48 @@ class BaseFormatter:
+         return str(self.token)
+ 
+ 
+-def _jinja_formatter(value, **context):
++def _jinja_formatter(value: str, **context) -> str:
+     if jinja_env is None:  # pragma: no cover
+         raise ImportError(
+             "jinja2 must be installed to enable '@jinja' settings in dynaconf"
+         )
+-    return jinja_env.from_string(value).render(**context)
++    try:
++        return jinja_env.from_string(value).render(**context)
++    except jinja2.exceptions.SecurityError:
++        warnings.warn(f"Unsafe access attempt to: {value}")
++        return ""
++
++
++class SafeFormatter(string.Formatter):
++    def get_field(self, field_name, args, context):
++        self._validate_key_exists(field_name, context)
++        return super().get_field(field_name, args, context)
++
++    def _validate_key_exists(self, field_name: str, context):
++        if not field_name.lower().startswith("this"):
++            return
++        from dynaconf.base import _PUBLIC_PROPERTIES
++
++        field_name = field_name.replace("[", ".")
++        field_name = field_name.replace("]", "")
++        context_name, _, key = field_name.partition(".")
++        # these are accesible by the user, but are not considered setting keys
++        # e.g, settings.current_env
++        if key in _PUBLIC_PROPERTIES:
++            return
++        # allow only existing setting keys
++        if key not in context[context_name]:
++            raise AttributeError(key)
++
++
++def _format_formatter(input: str, **context) -> str:
++    return SafeFormatter().format(input, **context)
+ 
+ 
+ class Formatters:
+     """Dynaconf builtin formatters"""
+ 
+-    python_formatter = BaseFormatter(str.format, "format")
++    python_formatter = BaseFormatter(_format_formatter, "format")
+     jinja_formatter = BaseFormatter(_jinja_formatter, "jinja")
+ 
+ 
diff -Nru python-dynaconf-3.1.7/debian/patches/series 
python-dynaconf-3.1.7/debian/patches/series
--- python-dynaconf-3.1.7/debian/patches/series 1970-01-01 01:00:00.000000000 
+0100
+++ python-dynaconf-3.1.7/debian/patches/series 2026-06-21 23:39:10.000000000 
+0200
@@ -0,0 +1 @@
+CVE-2026-33154.patch

Reply via email to