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