| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='test fetching over git protocol' |
| 4 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 5 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 6 | |
| 7 | . ./test-lib.sh |
| 8 | |
| 9 | . "$TEST_DIRECTORY"/lib-git-daemon.sh |
| 10 | |
| 11 | test_expect_success 'daemon rejects invalid --init-timeout values' ' |
| 12 | for arg in "3a" "-3" |
| 13 | do |
| 14 | test_must_fail git daemon --init-timeout="$arg" 2>err && |
| 15 | test_grep "fatal: invalid init-timeout ${SQ}$arg${SQ}, expecting a non-negative integer" err || |
| 16 | return 1 |
| 17 | done |
| 18 | ' |
| 19 | |
| 20 | test_expect_success 'daemon rejects invalid --timeout values' ' |
| 21 | for arg in "3a" "-3" |
| 22 | do |
| 23 | test_must_fail git daemon --timeout="$arg" 2>err && |
| 24 | test_grep "fatal: invalid timeout ${SQ}$arg${SQ}, expecting a non-negative integer" err || |
| 25 | return 1 |
| 26 | done |
| 27 | ' |
| 28 | |
| 29 | test_expect_success 'daemon rejects invalid --max-connections values' ' |
| 30 | arg='3a' && |
| 31 | test_must_fail git daemon --max-connections=3a 2>err && |
| 32 | test_grep "fatal: invalid max-connections ${SQ}$arg${SQ}, expecting an integer" err |
| 33 | ' |
| 34 | |
| 35 | start_git_daemon |
| 36 | |
| 37 | check_verbose_connect () { |
| 38 | test_grep -F "Looking up 127.0.0.1 ..." stderr && |
| 39 | test_grep -F "Connecting to 127.0.0.1 (port " stderr && |
| 40 | test_grep -F "done." stderr |
| 41 | } |
| 42 | |
| 43 | test_expect_success 'setup repository' ' |
| 44 | git config push.default matching && |
| 45 | echo content >file && |
| 46 | git add file && |
| 47 | git commit -m one |
| 48 | ' |
| 49 | |
| 50 | test_expect_success 'create git-accessible bare repository' ' |
| 51 | mkdir "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && |
| 52 | (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && |
| 53 | git --bare init && |
| 54 | : >git-daemon-export-ok |
| 55 | ) && |
| 56 | git remote add public "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && |
| 57 | git push public main:main |
| 58 | ' |
| 59 | |
| 60 | test_expect_success 'clone git repository' ' |
| 61 | git clone -v "$GIT_DAEMON_URL/repo.git" clone 2>stderr && |
| 62 | check_verbose_connect && |
| 63 | test_cmp file clone/file |
| 64 | ' |
| 65 | |
| 66 | test_expect_success 'fetch changes via git protocol' ' |
| 67 | echo content >>file && |
| 68 | git commit -a -m two && |
| 69 | git push public && |
| 70 | (cd clone && git pull -v) 2>stderr && |
| 71 | check_verbose_connect && |
| 72 | test_cmp file clone/file |
| 73 | ' |
| 74 | |
| 75 | test_expect_success 'no-op fetch -v stderr is as expected' ' |
| 76 | (cd clone && git fetch -v) 2>stderr && |
| 77 | check_verbose_connect |
| 78 | ' |
| 79 | |
| 80 | test_expect_success 'no-op fetch without "-v" is quiet' ' |
| 81 | (cd clone && git fetch 2>../stderr) && |
| 82 | test_must_be_empty stderr |
| 83 | ' |
| 84 | |
| 85 | test_expect_success 'remote detects correct HEAD' ' |
| 86 | git push public main:other && |
| 87 | (cd clone && |
| 88 | git remote set-head -d origin && |
| 89 | git remote set-head -a origin && |
| 90 | git symbolic-ref refs/remotes/origin/HEAD > output && |
| 91 | echo refs/remotes/origin/main > expect && |
| 92 | test_cmp expect output |
| 93 | ) |
| 94 | ' |
| 95 | |
| 96 | test_expect_success 'prepare pack objects' ' |
| 97 | cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git && |
| 98 | (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git && |
| 99 | git --bare repack -a -d |
| 100 | ) |
| 101 | ' |
| 102 | |
| 103 | test_expect_success 'fetch notices corrupt pack' ' |
| 104 | cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git && |
| 105 | (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git && |
| 106 | p=$(ls objects/pack/pack-*.pack) && |
| 107 | chmod u+w $p && |
| 108 | printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc |
| 109 | ) && |
| 110 | mkdir repo_bad1.git && |
| 111 | (cd repo_bad1.git && |
| 112 | git --bare init && |
| 113 | test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad1.git" && |
| 114 | test 0 = $(ls objects/pack/pack-*.pack | wc -l) |
| 115 | ) |
| 116 | ' |
| 117 | |
| 118 | test_expect_success 'fetch notices corrupt idx' ' |
| 119 | cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git && |
| 120 | (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git && |
| 121 | rm -f objects/pack/multi-pack-index && |
| 122 | p=$(ls objects/pack/pack-*.idx) && |
| 123 | chmod u+w $p && |
| 124 | printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc |
| 125 | ) && |
| 126 | mkdir repo_bad2.git && |
| 127 | (cd repo_bad2.git && |
| 128 | git --bare init && |
| 129 | test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad2.git" && |
| 130 | test 0 = $(ls objects/pack | wc -l) |
| 131 | ) |
| 132 | ' |
| 133 | |
| 134 | test_expect_success 'client refuses to ask for repo with newline' ' |
| 135 | test_must_fail git clone "$GIT_DAEMON_URL/repo$LF.git" dst 2>stderr && |
| 136 | test_grep newline.is.forbidden stderr |
| 137 | ' |
| 138 | |
| 139 | test_remote_error() |
| 140 | { |
| 141 | do_export=YesPlease |
| 142 | while test $# -gt 0 |
| 143 | do |
| 144 | case $1 in |
| 145 | -x) |
| 146 | shift |
| 147 | chmod -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" |
| 148 | ;; |
| 149 | -n) |
| 150 | shift |
| 151 | do_export= |
| 152 | ;; |
| 153 | *) |
| 154 | break |
| 155 | esac |
| 156 | done |
| 157 | |
| 158 | msg=$1 |
| 159 | shift |
| 160 | cmd=$1 |
| 161 | shift |
| 162 | repo=$1 |
| 163 | shift || error "invalid number of arguments" |
| 164 | |
| 165 | if test -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo" |
| 166 | then |
| 167 | if test -n "$do_export" |
| 168 | then |
| 169 | : >"$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok" |
| 170 | else |
| 171 | rm -f "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok" |
| 172 | fi |
| 173 | fi |
| 174 | |
| 175 | test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output && |
| 176 | test_grep "fatal: remote error: $msg: /$repo" output && |
| 177 | ret=$? |
| 178 | chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" |
| 179 | (exit $ret) |
| 180 | } |
| 181 | |
| 182 | msg="access denied or repository not exported" |
| 183 | test_expect_success 'clone non-existent' "test_remote_error '$msg' clone nowhere.git" |
| 184 | test_expect_success 'push disabled' "test_remote_error '$msg' push repo.git main" |
| 185 | test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git" |
| 186 | test_expect_success 'not exported' "test_remote_error -n '$msg' fetch repo.git" |
| 187 | |
| 188 | stop_git_daemon |
| 189 | start_git_daemon --informative-errors |
| 190 | |
| 191 | test_expect_success 'clone non-existent' "test_remote_error 'no such repository' clone nowhere.git" |
| 192 | test_expect_success 'push disabled' "test_remote_error 'service not enabled' push repo.git main" |
| 193 | test_expect_success 'read access denied' "test_remote_error -x 'no such repository' fetch repo.git" |
| 194 | test_expect_success 'not exported' "test_remote_error -n 'repository not exported' fetch repo.git" |
| 195 | |
| 196 | stop_git_daemon |
| 197 | start_git_daemon --interpolated-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH/%H%D" |
| 198 | |
| 199 | test_expect_success 'access repo via interpolated hostname' ' |
| 200 | repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/localhost/interp.git" && |
| 201 | git init --bare "$repo" && |
| 202 | git push "$repo" HEAD && |
| 203 | >"$repo"/git-daemon-export-ok && |
| 204 | GIT_OVERRIDE_VIRTUAL_HOST=localhost \ |
| 205 | git ls-remote "$GIT_DAEMON_URL/interp.git" && |
| 206 | GIT_OVERRIDE_VIRTUAL_HOST=LOCALHOST \ |
| 207 | git ls-remote "$GIT_DAEMON_URL/interp.git" |
| 208 | ' |
| 209 | |
| 210 | test_expect_success 'hostname cannot break out of directory' ' |
| 211 | repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/../escape.git" && |
| 212 | git init --bare "$repo" && |
| 213 | git push "$repo" HEAD && |
| 214 | >"$repo"/git-daemon-export-ok && |
| 215 | test_must_fail \ |
| 216 | env GIT_OVERRIDE_VIRTUAL_HOST=.. \ |
| 217 | git ls-remote "$GIT_DAEMON_URL/escape.git" |
| 218 | ' |
| 219 | |
| 220 | test_expect_success FAKENC 'hostname interpolation works after LF-stripping' ' |
| 221 | { |
| 222 | printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw && |
| 223 | printf "0000" |
| 224 | } >input && |
| 225 | fake_nc "$GIT_DAEMON_HOST_PORT" <input >output && |
| 226 | depacketize <output >output.raw && |
| 227 | |
| 228 | # just pick out the value of main, which avoids any protocol |
| 229 | # particulars |
| 230 | perl -lne "print \$1 if m{^(\\S+) refs/heads/main}" <output.raw >actual && |
| 231 | git -C "$repo" rev-parse main >expect && |
| 232 | test_cmp expect actual |
| 233 | ' |
| 234 | |
| 235 | test_done |