fix(dotenv): reject extremely large named pipes

This commit is contained in:
Carlo Sala 2026-04-16 20:33:43 +02:00
commit a30c63e011
No known key found for this signature in database
GPG key ID: DA6FB450C1A4FE9A
2 changed files with 75 additions and 2 deletions

View file

@ -202,7 +202,7 @@ parse_dotenv() {
# Fail if file is too large to avoid DoS
zmodload -F zsh/stat b:zstat
local -i file_size max_size=10485760 # 10MiB
if ! file_size=$(zstat -L +size "$filename" 2>/dev/null); then
if ! file_size=$(zstat +size "$filename" 2>/dev/null); then
echo "dotenv: unable to determine size of file '$filename'" >&2
return 1
fi
@ -216,6 +216,39 @@ parse_dotenv() {
_parse_dotenv_content "$content" "$mode"
}
_dotenv_read_limited() {
local filename="$1"
local chunk content=""
local -i max_size=10485760 total=0 read_size=0 fd read_status
zmodload zsh/system || return 1
exec {fd}<"$filename" || return 1
while true; do
sysread -i $fd -s 65536 -c read_size chunk
read_status=$?
if (( read_status == 5 )); then
break
elif (( read_status != 0 )); then
exec {fd}<&-
return 1
fi
(( total += read_size ))
if (( total > max_size )); then
exec {fd}<&-
echo "dotenv: file '$filename' is too large to parse (size: more than $max_size bytes)" >&2
return 1
fi
content+="$chunk"
done
exec {fd}<&-
REPLY="$content"
}
_dotenv_check_syntax() {
local filename="$1"
@ -272,7 +305,8 @@ source_env() {
local content
if [[ -p "$ZSH_DOTENV_FILE" ]]; then
content="$(<"$ZSH_DOTENV_FILE")" || return 1
_dotenv_read_limited "$ZSH_DOTENV_FILE" || return 1
content="$REPLY"
_dotenv_check_syntax "$ZSH_DOTENV_FILE" "$content" || return 1
setopt localoptions allexport

View file

@ -82,6 +82,45 @@
assert "$result" equals 'secret'
}
@test 'source_env rejects oversized named pipes' {
run zsh -fc '
source ./dotenv.plugin.zsh
tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/dotenv.XXXXXX")" || exit 1
fifo="$tmpdir/.env"
command mkfifo "$fifo" || exit 1
cleanup() {
kill $killer_pid 2>/dev/null || true
kill $writer_pid 2>/dev/null || true
wait $writer_pid 2>/dev/null || true
command rm -rf "$tmpdir"
}
trap cleanup EXIT
(
{
print -rn -- "BIG="
command dd if=/dev/zero bs=10485761 count=1 2>/dev/null | tr "\0" a
} > "$fifo"
) &
writer_pid=$!
(
sleep 2
kill -0 $$ 2>/dev/null || exit 0
kill $$ 2>/dev/null || exit 0
) &
killer_pid=$!
ZSH_DOTENV_PROMPT=false
ZSH_DOTENV_FILE="$fifo"
source_env >/dev/null 2>&1
'
assert $state equals 1
}
@test 'parse basic variable assignment' {
> "$fixture" <<'EOF'
# Basic assignments