fix(env): preserve '=' in complex dict cast values (#565)#618
Open
jbbqqf wants to merge 1 commit into
Open
Conversation
The complex-dict cast path used ``val.split('=')`` (no maxsplit), so a
value containing an additional ``=`` (e.g. base64 padding, query strings,
JWT segments) was silently truncated. The simple-dict cast path already
used ``split('=', 1)``; align the complex path with the same single
split.
Adds a parametrised regression test covering the
``a=1;b=v=more`` shape that fails on develop and passes after the fix.
Fixes joke2k#565.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The complex-dict cast path (
cast={'value': ...}) usedval.split('=')with nomaxsplit, so a value that itself contains=(e.g. base64 padding, query strings, JWT segments) was silently truncated at the second=. The simple-dict cast path (cast=dict) already usedsplit('=', 1). This PR aligns the two paths.Fixes #565.
Context
Issue #565 noticed the inconsistency in
environ/environ.py:val.split('=')— drops the rest of the value after the first internal=v.split('=', 1)— preserves itChanges
environ/environ.py— single-line change:val.split('=')→val.split('=', 1)in the complex-dict branch ofparse_value. Added a code comment pointing at the simple-dict line and at issue Complex dict value cannot have equal sign, but simple dict can. #565 so a reviewer doesn't have to re-derive the reasoning.tests/test_env.py— extends the existing parametrisedtest_dict_parsingmatrix with adict_value_contains_equal_signcase ('a=1;b=v=more'withdict(value=str)).Reproduce BEFORE/AFTER yourself (copy-paste)
What I ran locally
The new test was also verified to FAIL on
develop(only theenviron.pychange reverted):assert {'a': '1', 'b': 'v'} == {'a': '1', 'b': 'v=more'}.Edge cases
'a=1'withdict(value=int){'a': 1}dict_int(unchanged)'a=1,2,3'withdict(value=[int]){'a': [1, 2, 3]}dict_int_list(unchanged)='a=1;b=v=more'withdict(value=str){'a': '1', 'b': 'v=more'}dict_value_contains_equal_sign=in value'k=a=b=c'withdict(value=str){'k': 'a=b=c'}Risk / blast radius
The change touches a single line in
Env.parse_value's complex-dict branch. The previous behaviour silently truncated values at the first internal=, so any user actively relying on that truncation would have been doing so unintentionally — they would have been getting partial values without warning. All existingtest_dict_parsingcases pass unchanged (none of them placed=inside a value), and the broader test suite (458 tests) is green.PR drafted with assistance from Claude Code (Anthropic). The change was reviewed manually against django-environ's source. The reproducer block above is the one I used during development; reviewers can paste it verbatim.