]>
Commit | Line | Data |
---|---|---|
0383bbb9 JK |
1 | #!/bin/sh |
2 | ||
43a2220f | 3 | test_description='check broken or malicious patterns in .git* files |
0383bbb9 | 4 | |
43a2220f JK |
5 | Such as: |
6 | ||
7 | - presence of .. in submodule names; | |
8 | Exercise the name-checking function on a variety of names, and then give a | |
9 | real-world setup that confirms we catch this in practice. | |
10 | ||
11 | - nested submodule names | |
12 | ||
13 | - symlinked .gitmodules, etc | |
0383bbb9 | 14 | ' |
c65d18cb ÆAB |
15 | |
16 | TEST_PASSES_SANITIZE_LEAK=true | |
0383bbb9 | 17 | . ./test-lib.sh |
73c3f0f7 | 18 | . "$TEST_DIRECTORY"/lib-pack.sh |
0383bbb9 | 19 | |
0d3beb71 TB |
20 | test_expect_success 'setup' ' |
21 | git config --global protocol.file.allow always | |
22 | ' | |
23 | ||
0383bbb9 JK |
24 | test_expect_success 'check names' ' |
25 | cat >expect <<-\EOF && | |
26 | valid | |
27 | valid/with/paths | |
28 | EOF | |
29 | ||
85321a34 | 30 | test-tool submodule check-name >actual <<-\EOF && |
0383bbb9 JK |
31 | valid |
32 | valid/with/paths | |
33 | ||
34 | ../foo | |
35 | /../foo | |
36 | ..\foo | |
37 | \..\foo | |
38 | foo/.. | |
39 | foo/../ | |
40 | foo\.. | |
41 | foo\..\ | |
42 | foo/../bar | |
43 | EOF | |
44 | ||
45 | test_cmp expect actual | |
46 | ' | |
47 | ||
48 | test_expect_success 'create innocent subrepo' ' | |
49 | git init innocent && | |
50 | git -C innocent commit --allow-empty -m foo | |
51 | ' | |
52 | ||
53 | test_expect_success 'submodule add refuses invalid names' ' | |
54 | test_must_fail \ | |
55 | git submodule add --name ../../modules/evil "$PWD/innocent" evil | |
56 | ' | |
57 | ||
58 | test_expect_success 'add evil submodule' ' | |
59 | git submodule add "$PWD/innocent" evil && | |
60 | ||
61 | mkdir modules && | |
62 | cp -r .git/modules/evil modules && | |
63 | write_script modules/evil/hooks/post-checkout <<-\EOF && | |
64 | echo >&2 "RUNNING POST CHECKOUT" | |
65 | EOF | |
66 | ||
67 | git config -f .gitmodules submodule.evil.update checkout && | |
68 | git config -f .gitmodules --rename-section \ | |
69 | submodule.evil submodule.../../modules/evil && | |
70 | git add modules && | |
71 | git commit -am evil | |
72 | ' | |
73 | ||
74 | # This step seems like it shouldn't be necessary, since the payload is | |
75 | # contained entirely in the evil submodule. But due to the vagaries of the | |
76 | # submodule code, checking out the evil module will fail unless ".git/modules" | |
77 | # exists. Adding another submodule (with a name that sorts before "evil") is an | |
78 | # easy way to make sure this is the case in the victim clone. | |
79 | test_expect_success 'add other submodule' ' | |
80 | git submodule add "$PWD/innocent" another-module && | |
81 | git add another-module && | |
82 | git commit -am another | |
83 | ' | |
84 | ||
85 | test_expect_success 'clone evil superproject' ' | |
86 | git clone --recurse-submodules . victim >output 2>&1 && | |
87 | ! grep "RUNNING POST CHECKOUT" output | |
88 | ' | |
89 | ||
1995b5e0 JK |
90 | test_expect_success 'fsck detects evil superproject' ' |
91 | test_must_fail git fsck | |
92 | ' | |
93 | ||
6e328d6c JK |
94 | test_expect_success 'transfer.fsckObjects detects evil superproject (unpack)' ' |
95 | rm -rf dst.git && | |
96 | git init --bare dst.git && | |
97 | git -C dst.git config transfer.fsckObjects true && | |
98 | test_must_fail git push dst.git HEAD | |
99 | ' | |
100 | ||
73c3f0f7 JK |
101 | test_expect_success 'transfer.fsckObjects detects evil superproject (index)' ' |
102 | rm -rf dst.git && | |
103 | git init --bare dst.git && | |
104 | git -C dst.git config transfer.fsckObjects true && | |
105 | git -C dst.git config transfer.unpackLimit 1 && | |
106 | test_must_fail git push dst.git HEAD | |
107 | ' | |
108 | ||
109 | # Normally our packs contain commits followed by trees followed by blobs. This | |
110 | # reverses the order, which requires backtracking to find the context of a | |
111 | # blob. We'll start with a fresh gitmodules-only tree to make it simpler. | |
112 | test_expect_success 'create oddly ordered pack' ' | |
113 | git checkout --orphan odd && | |
114 | git rm -rf --cached . && | |
115 | git add .gitmodules && | |
116 | git commit -m odd && | |
117 | { | |
118 | pack_header 3 && | |
119 | pack_obj $(git rev-parse HEAD:.gitmodules) && | |
120 | pack_obj $(git rev-parse HEAD^{tree}) && | |
121 | pack_obj $(git rev-parse HEAD) | |
122 | } >odd.pack && | |
123 | pack_trailer odd.pack | |
124 | ' | |
125 | ||
126 | test_expect_success 'transfer.fsckObjects handles odd pack (unpack)' ' | |
127 | rm -rf dst.git && | |
128 | git init --bare dst.git && | |
129 | test_must_fail git -C dst.git unpack-objects --strict <odd.pack | |
130 | ' | |
131 | ||
132 | test_expect_success 'transfer.fsckObjects handles odd pack (index)' ' | |
133 | rm -rf dst.git && | |
134 | git init --bare dst.git && | |
135 | test_must_fail git -C dst.git index-pack --strict --stdin <odd.pack | |
136 | ' | |
137 | ||
368b4e59 JK |
138 | test_expect_success 'index-pack --strict works for non-repo pack' ' |
139 | rm -rf dst.git && | |
140 | git init --bare dst.git && | |
141 | cp odd.pack dst.git && | |
142 | test_must_fail git -C dst.git index-pack --strict odd.pack 2>output && | |
143 | # Make sure we fail due to bad gitmodules content, not because we | |
144 | # could not read the blob in the first place. | |
145 | grep gitmodulesName output | |
146 | ' | |
147 | ||
1cb12f33 | 148 | check_dotx_symlink () { |
bb6832d5 JK |
149 | fsck_must_fail=test_must_fail |
150 | fsck_prefix=error | |
151 | refuse_index=t | |
152 | case "$1" in | |
153 | --warning) | |
154 | fsck_must_fail= | |
155 | fsck_prefix=warning | |
156 | refuse_index= | |
157 | shift | |
158 | ;; | |
159 | esac | |
160 | ||
1cb12f33 JK |
161 | name=$1 |
162 | type=$2 | |
163 | path=$3 | |
164 | dir=symlink-$name-$type | |
165 | ||
166 | test_expect_success "set up repo with symlinked $name ($type)" ' | |
167 | git init $dir && | |
168 | ( | |
169 | cd $dir && | |
170 | ||
171 | # Make the tree directly to avoid index restrictions. | |
172 | # | |
173 | # Because symlinks store the target as a blob, choose | |
174 | # a pathname that could be parsed as a .gitmodules file | |
175 | # to trick naive non-symlink-aware checking. | |
176 | tricky="[foo]bar=true" && | |
177 | content=$(git hash-object -w ../.gitmodules) && | |
178 | target=$(printf "$tricky" | git hash-object -w --stdin) && | |
179 | { | |
180 | printf "100644 blob $content\t$tricky\n" && | |
181 | printf "120000 blob $target\t$path\n" | |
182 | } >bad-tree | |
183 | ) && | |
184 | tree=$(git -C $dir mktree <$dir/bad-tree) | |
185 | ' | |
186 | ||
187 | test_expect_success "fsck detects symlinked $name ($type)" ' | |
188 | ( | |
189 | cd $dir && | |
190 | ||
191 | # Check not only that we fail, but that it is due to the | |
192 | # symlink detector | |
bb6832d5 JK |
193 | $fsck_must_fail git fsck 2>output && |
194 | grep "$fsck_prefix.*tree $tree: ${name}Symlink" output | |
1cb12f33 JK |
195 | ) |
196 | ' | |
197 | ||
bb6832d5 | 198 | test -n "$refuse_index" && |
1cb12f33 JK |
199 | test_expect_success "refuse to load symlinked $name into index ($type)" ' |
200 | test_must_fail \ | |
201 | git -C $dir \ | |
202 | -c core.protectntfs \ | |
203 | -c core.protecthfs \ | |
204 | read-tree $tree 2>err && | |
205 | grep "invalid path.*$name" err && | |
206 | git -C $dir ls-files -s >out && | |
207 | test_must_be_empty out | |
208 | ' | |
209 | } | |
210 | ||
211 | check_dotx_symlink gitmodules vanilla .gitmodules | |
212 | check_dotx_symlink gitmodules ntfs ".gitmodules ." | |
213 | check_dotx_symlink gitmodules hfs ".${u200c}gitmodules" | |
a1ca398b | 214 | |
bb6832d5 JK |
215 | check_dotx_symlink --warning gitattributes vanilla .gitattributes |
216 | check_dotx_symlink --warning gitattributes ntfs ".gitattributes ." | |
217 | check_dotx_symlink --warning gitattributes hfs ".${u200c}gitattributes" | |
218 | ||
219 | check_dotx_symlink --warning gitignore vanilla .gitignore | |
220 | check_dotx_symlink --warning gitignore ntfs ".gitignore ." | |
221 | check_dotx_symlink --warning gitignore hfs ".${u200c}gitignore" | |
222 | ||
223 | check_dotx_symlink --warning mailmap vanilla .mailmap | |
224 | check_dotx_symlink --warning mailmap ntfs ".mailmap ." | |
225 | check_dotx_symlink --warning mailmap hfs ".${u200c}mailmap" | |
b7b1fca1 | 226 | |
47cc9131 JK |
227 | test_expect_success 'fsck detects non-blob .gitmodules' ' |
228 | git init non-blob && | |
229 | ( | |
230 | cd non-blob && | |
231 | ||
232 | # As above, make the funny tree directly to avoid index | |
233 | # restrictions. | |
234 | mkdir subdir && | |
235 | cp ../.gitmodules subdir/file && | |
236 | git add subdir/file && | |
237 | git commit -m ok && | |
238 | git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree && | |
239 | ||
240 | test_must_fail git fsck 2>output && | |
6789275d | 241 | test_grep gitmodulesBlob output |
47cc9131 JK |
242 | ) |
243 | ' | |
244 | ||
de6bd9e3 JK |
245 | test_expect_success 'fsck detects corrupt .gitmodules' ' |
246 | git init corrupt && | |
247 | ( | |
248 | cd corrupt && | |
249 | ||
250 | echo "[broken" >.gitmodules && | |
251 | git add .gitmodules && | |
252 | git commit -m "broken gitmodules" && | |
253 | ||
64eb14d3 | 254 | git fsck 2>output && |
6789275d JH |
255 | test_grep gitmodulesParse output && |
256 | test_grep ! "bad config" output | |
de6bd9e3 JK |
257 | ) |
258 | ' | |
259 | ||
bccc37fd | 260 | test_expect_success WINDOWS 'prevent git~1 squatting on Windows' ' |
0060fd15 JS |
261 | git init squatting && |
262 | ( | |
263 | cd squatting && | |
264 | mkdir a && | |
265 | touch a/..git && | |
266 | git add a/..git && | |
267 | test_tick && | |
268 | git commit -m initial && | |
269 | ||
270 | modules="$(test_write_lines \ | |
271 | "[submodule \"b.\"]" "url = ." "path = c" \ | |
272 | "[submodule \"b\"]" "url = ." "path = d\\\\a" | | |
273 | git hash-object -w --stdin)" && | |
274 | rev="$(git rev-parse --verify HEAD)" && | |
275 | hash="$(echo x | git hash-object -w --stdin)" && | |
224c7d70 JS |
276 | test_must_fail git update-index --add \ |
277 | --cacheinfo 160000,$rev,d\\a 2>err && | |
6789275d | 278 | test_grep "Invalid path" err && |
e1d911dd | 279 | git -c core.protectNTFS=false update-index --add \ |
0060fd15 JS |
280 | --cacheinfo 100644,$modules,.gitmodules \ |
281 | --cacheinfo 160000,$rev,c \ | |
282 | --cacheinfo 160000,$rev,d\\a \ | |
283 | --cacheinfo 100644,$hash,d./a/x \ | |
284 | --cacheinfo 100644,$hash,d./a/..git && | |
285 | test_tick && | |
224c7d70 | 286 | git -c core.protectNTFS=false commit -m "module" |
0060fd15 | 287 | ) && |
bccc37fd AD |
288 | if test_have_prereq MINGW |
289 | then | |
290 | test_must_fail git -c core.protectNTFS=false \ | |
291 | clone --recurse-submodules squatting squatting-clone 2>err && | |
6789275d | 292 | test_grep -e "directory not empty" -e "not an empty directory" err && |
bccc37fd AD |
293 | ! grep gitdir squatting-clone/d/a/git~2 |
294 | fi | |
0060fd15 JS |
295 | ' |
296 | ||
a8dee3ca JS |
297 | test_expect_success 'git dirs of sibling submodules must not be nested' ' |
298 | git init nested && | |
299 | test_commit -C nested nested && | |
300 | ( | |
301 | cd nested && | |
302 | cat >.gitmodules <<-EOF && | |
303 | [submodule "hippo"] | |
304 | url = . | |
305 | path = thing1 | |
306 | [submodule "hippo/hooks"] | |
307 | url = . | |
308 | path = thing2 | |
309 | EOF | |
310 | git clone . thing1 && | |
311 | git clone . thing2 && | |
312 | git add .gitmodules thing1 thing2 && | |
313 | test_tick && | |
314 | git commit -m nested | |
315 | ) && | |
316 | test_must_fail git clone --recurse-submodules nested clone 2>err && | |
6789275d | 317 | test_grep "is inside git dir" err |
a8dee3ca JS |
318 | ' |
319 | ||
0383bbb9 | 320 | test_done |