fix(dotenv): unknown vars should expand to empty

This commit is contained in:
Carlo Sala 2026-04-16 20:09:26 +02:00
commit 9b19287c88
No known key found for this signature in database
GPG key ID: DA6FB450C1A4FE9A
2 changed files with 55 additions and 7 deletions

View file

@ -97,6 +97,7 @@ _parse_dotenv_content() {
key="${match[1]}"
value="${match[2]}"
local raw_value="$value"
# Filter out variables to be ignored for security reasons (best effort)
if [[ "$key" == (${~forbidden}) ]]; then
@ -145,14 +146,39 @@ _parse_dotenv_content() {
# Unquote the value to handle special characters and multiline values
value="${(Q)value}"
# Expand variables from in-file parsed vars (same as double-quoted)
local expanded="$value"
for var_name in "${(@k)parsed_vars}"; do
local var_value="${parsed_vars[$var_name]}"
expanded="${expanded//\$\{${var_name}\}/${var_value}}"
expanded="${expanded//\$${var_name}/${var_value}}"
# Single-quoted values are fully literal and must not participate in expansion.
if [[ "$raw_value" == \'*\' ]]; then
parsed_vars[$key]="$value"
if [[ "$mode" == "export" ]]; then
typeset -x "$key"="$value"
fi
continue
fi
# Expand previously parsed in-file variables without partial name matches.
local expanded="" prefix remainder="$value" var_name
while [[ "$remainder" == *'$'* ]]; do
prefix="${remainder%%\$*}"
expanded+="$prefix"
remainder="${remainder#$prefix}"
if [[ "$remainder" =~ '^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}(.*)$' ]]; then
var_name="${match[1]}"
remainder="${match[2]}"
elif [[ "$remainder" =~ '^\$([a-zA-Z_][a-zA-Z0-9_]*)(.*)$' ]]; then
var_name="${match[1]}"
remainder="${match[2]}"
else
expanded+='$'
remainder="${remainder#?}"
continue
fi
if [[ -v "parsed_vars[$var_name]" ]]; then
expanded+="${parsed_vars[$var_name]}"
fi
done
value="$expanded"
value="${expanded}${remainder}"
# Store in parsed vars (for in-file expansion)
parsed_vars[$key]="$value"

View file

@ -305,3 +305,25 @@ EOF
assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
}
@test 'parse in-file variable expansion prefers the longest matching variable name' {
> "$fixture" <<'EOF'
A=1
ABC=2
X=$ABC
Y=${ABC}
Z=$ABCD
EOF
expected_vars=(
A '1'
ABC '2'
X '2'
Y '2'
Z ''
)
_parse_dotenv_test "$fixture"
assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
}