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