| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='basic credential helper tests' |
| 4 | |
| 5 | . ./test-lib.sh |
| 6 | . "$TEST_DIRECTORY"/lib-credential.sh |
| 7 | |
| 8 | test_expect_success 'setup helper scripts' ' |
| 9 | cat >dump <<-\EOF && |
| 10 | whoami=$(echo $0 | sed s/.*git-credential-//) |
| 11 | echo >&2 "$whoami: $*" |
| 12 | OIFS=$IFS |
| 13 | IFS== |
| 14 | while read key value; do |
| 15 | echo >&2 "$whoami: $key=$value" |
| 16 | if test -z "${key%%*\[\]}" |
| 17 | then |
| 18 | key=${key%%\[\]} |
| 19 | eval "$key=\"\$$key $value\"" |
| 20 | else |
| 21 | eval "$key=$value" |
| 22 | fi |
| 23 | done |
| 24 | IFS=$OIFS |
| 25 | EOF |
| 26 | |
| 27 | write_script git-credential-useless <<-\EOF && |
| 28 | . ./dump |
| 29 | exit 0 |
| 30 | EOF |
| 31 | |
| 32 | write_script git-credential-quit <<-\EOF && |
| 33 | . ./dump |
| 34 | echo quit=1 |
| 35 | EOF |
| 36 | |
| 37 | write_script git-credential-verbatim <<-\EOF && |
| 38 | user=$1; shift |
| 39 | pass=$1; shift |
| 40 | . ./dump |
| 41 | test -z "$user" || echo username=$user |
| 42 | test -z "$pass" || echo password=$pass |
| 43 | EOF |
| 44 | |
| 45 | write_script git-credential-verbatim-cred <<-\EOF && |
| 46 | authtype=$1; shift |
| 47 | credential=$1; shift |
| 48 | . ./dump |
| 49 | echo capability[]=authtype |
| 50 | echo capability[]=state |
| 51 | test -z "${capability##*authtype*}" || exit 0 |
| 52 | test -z "$authtype" || echo authtype=$authtype |
| 53 | test -z "$credential" || echo credential=$credential |
| 54 | test -z "${capability##*state*}" || exit 0 |
| 55 | echo state[]=verbatim-cred:foo |
| 56 | EOF |
| 57 | |
| 58 | write_script git-credential-verbatim-ephemeral <<-\EOF && |
| 59 | authtype=$1; shift |
| 60 | credential=$1; shift |
| 61 | . ./dump |
| 62 | echo capability[]=authtype |
| 63 | test -z "${capability##*authtype*}" || exit 0 |
| 64 | test -z "$authtype" || echo authtype=$authtype |
| 65 | test -z "$credential" || echo credential=$credential |
| 66 | echo "ephemeral=1" |
| 67 | EOF |
| 68 | |
| 69 | write_script git-credential-verbatim-with-expiry <<-\EOF && |
| 70 | user=$1; shift |
| 71 | pass=$1; shift |
| 72 | pexpiry=$1; shift |
| 73 | . ./dump |
| 74 | test -z "$user" || echo username=$user |
| 75 | test -z "$pass" || echo password=$pass |
| 76 | test -z "$pexpiry" || echo password_expiry_utc=$pexpiry |
| 77 | EOF |
| 78 | |
| 79 | write_script git-credential-cntrl-in-username <<-\EOF && |
| 80 | printf "username=\\007latrix Lestrange\\n" |
| 81 | EOF |
| 82 | |
| 83 | PATH="$PWD:$PATH" |
| 84 | ' |
| 85 | |
| 86 | test_expect_success 'credential_fill invokes helper' ' |
| 87 | check fill "verbatim foo bar" <<-\EOF |
| 88 | protocol=http |
| 89 | host=example.com |
| 90 | -- |
| 91 | protocol=http |
| 92 | host=example.com |
| 93 | username=foo |
| 94 | password=bar |
| 95 | -- |
| 96 | verbatim: get |
| 97 | verbatim: protocol=http |
| 98 | verbatim: host=example.com |
| 99 | EOF |
| 100 | ' |
| 101 | |
| 102 | test_expect_success 'credential_fill invokes helper with credential' ' |
| 103 | check fill "verbatim-cred Bearer token" <<-\EOF |
| 104 | capability[]=authtype |
| 105 | protocol=http |
| 106 | host=example.com |
| 107 | -- |
| 108 | capability[]=authtype |
| 109 | authtype=Bearer |
| 110 | credential=token |
| 111 | protocol=http |
| 112 | host=example.com |
| 113 | -- |
| 114 | verbatim-cred: get |
| 115 | verbatim-cred: capability[]=authtype |
| 116 | verbatim-cred: protocol=http |
| 117 | verbatim-cred: host=example.com |
| 118 | EOF |
| 119 | ' |
| 120 | |
| 121 | test_expect_success 'credential_fill invokes helper with ephemeral credential' ' |
| 122 | check fill "verbatim-ephemeral Bearer token" <<-\EOF |
| 123 | capability[]=authtype |
| 124 | protocol=http |
| 125 | host=example.com |
| 126 | -- |
| 127 | capability[]=authtype |
| 128 | authtype=Bearer |
| 129 | credential=token |
| 130 | ephemeral=1 |
| 131 | protocol=http |
| 132 | host=example.com |
| 133 | -- |
| 134 | verbatim-ephemeral: get |
| 135 | verbatim-ephemeral: capability[]=authtype |
| 136 | verbatim-ephemeral: protocol=http |
| 137 | verbatim-ephemeral: host=example.com |
| 138 | EOF |
| 139 | ' |
| 140 | test_expect_success 'credential_fill invokes helper with credential and state' ' |
| 141 | check fill "verbatim-cred Bearer token" <<-\EOF |
| 142 | capability[]=authtype |
| 143 | capability[]=state |
| 144 | protocol=http |
| 145 | host=example.com |
| 146 | -- |
| 147 | capability[]=authtype |
| 148 | capability[]=state |
| 149 | authtype=Bearer |
| 150 | credential=token |
| 151 | protocol=http |
| 152 | host=example.com |
| 153 | state[]=verbatim-cred:foo |
| 154 | -- |
| 155 | verbatim-cred: get |
| 156 | verbatim-cred: capability[]=authtype |
| 157 | verbatim-cred: capability[]=state |
| 158 | verbatim-cred: protocol=http |
| 159 | verbatim-cred: host=example.com |
| 160 | EOF |
| 161 | ' |
| 162 | |
| 163 | test_expect_success 'credential_fill invokes multiple helpers' ' |
| 164 | check fill useless "verbatim foo bar" <<-\EOF |
| 165 | protocol=http |
| 166 | host=example.com |
| 167 | -- |
| 168 | protocol=http |
| 169 | host=example.com |
| 170 | username=foo |
| 171 | password=bar |
| 172 | -- |
| 173 | useless: get |
| 174 | useless: protocol=http |
| 175 | useless: host=example.com |
| 176 | verbatim: get |
| 177 | verbatim: protocol=http |
| 178 | verbatim: host=example.com |
| 179 | EOF |
| 180 | ' |
| 181 | |
| 182 | test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' ' |
| 183 | check fill useless "verbatim foo bar" <<-\EOF |
| 184 | capability[]=authtype |
| 185 | capability[]=state |
| 186 | protocol=http |
| 187 | host=example.com |
| 188 | -- |
| 189 | protocol=http |
| 190 | host=example.com |
| 191 | username=foo |
| 192 | password=bar |
| 193 | -- |
| 194 | useless: get |
| 195 | useless: capability[]=authtype |
| 196 | useless: capability[]=state |
| 197 | useless: protocol=http |
| 198 | useless: host=example.com |
| 199 | verbatim: get |
| 200 | verbatim: capability[]=authtype |
| 201 | verbatim: capability[]=state |
| 202 | verbatim: protocol=http |
| 203 | verbatim: host=example.com |
| 204 | EOF |
| 205 | ' |
| 206 | |
| 207 | test_expect_success 'credential_fill response does not get capabilities when caller is incapable' ' |
| 208 | check fill "verbatim-cred Bearer token" <<-\EOF |
| 209 | protocol=http |
| 210 | host=example.com |
| 211 | -- |
| 212 | protocol=http |
| 213 | host=example.com |
| 214 | -- |
| 215 | verbatim-cred: get |
| 216 | verbatim-cred: protocol=http |
| 217 | verbatim-cred: host=example.com |
| 218 | EOF |
| 219 | ' |
| 220 | |
| 221 | test_expect_success 'credential_fill stops when we get a full response' ' |
| 222 | check fill "verbatim one two" "verbatim three four" <<-\EOF |
| 223 | protocol=http |
| 224 | host=example.com |
| 225 | -- |
| 226 | protocol=http |
| 227 | host=example.com |
| 228 | username=one |
| 229 | password=two |
| 230 | -- |
| 231 | verbatim: get |
| 232 | verbatim: protocol=http |
| 233 | verbatim: host=example.com |
| 234 | EOF |
| 235 | ' |
| 236 | |
| 237 | test_expect_success 'credential_fill thinks a credential is a full response' ' |
| 238 | check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF |
| 239 | capability[]=authtype |
| 240 | protocol=http |
| 241 | host=example.com |
| 242 | -- |
| 243 | capability[]=authtype |
| 244 | authtype=Bearer |
| 245 | credential=token |
| 246 | protocol=http |
| 247 | host=example.com |
| 248 | -- |
| 249 | verbatim-cred: get |
| 250 | verbatim-cred: capability[]=authtype |
| 251 | verbatim-cred: protocol=http |
| 252 | verbatim-cred: host=example.com |
| 253 | EOF |
| 254 | ' |
| 255 | |
| 256 | test_expect_success 'credential_fill continues through partial response' ' |
| 257 | check fill "verbatim one \"\"" "verbatim two three" <<-\EOF |
| 258 | protocol=http |
| 259 | host=example.com |
| 260 | -- |
| 261 | protocol=http |
| 262 | host=example.com |
| 263 | username=two |
| 264 | password=three |
| 265 | -- |
| 266 | verbatim: get |
| 267 | verbatim: protocol=http |
| 268 | verbatim: host=example.com |
| 269 | verbatim: get |
| 270 | verbatim: protocol=http |
| 271 | verbatim: host=example.com |
| 272 | verbatim: username=one |
| 273 | EOF |
| 274 | ' |
| 275 | |
| 276 | test_expect_success 'credential_fill populates password_expiry_utc' ' |
| 277 | check fill "verbatim-with-expiry one two 9999999999" <<-\EOF |
| 278 | protocol=http |
| 279 | host=example.com |
| 280 | -- |
| 281 | protocol=http |
| 282 | host=example.com |
| 283 | username=one |
| 284 | password=two |
| 285 | password_expiry_utc=9999999999 |
| 286 | -- |
| 287 | verbatim-with-expiry: get |
| 288 | verbatim-with-expiry: protocol=http |
| 289 | verbatim-with-expiry: host=example.com |
| 290 | EOF |
| 291 | ' |
| 292 | |
| 293 | test_expect_success 'credential_fill ignores expired password' ' |
| 294 | check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF |
| 295 | protocol=http |
| 296 | host=example.com |
| 297 | -- |
| 298 | protocol=http |
| 299 | host=example.com |
| 300 | username=three |
| 301 | password=four |
| 302 | -- |
| 303 | verbatim-with-expiry: get |
| 304 | verbatim-with-expiry: protocol=http |
| 305 | verbatim-with-expiry: host=example.com |
| 306 | verbatim: get |
| 307 | verbatim: protocol=http |
| 308 | verbatim: host=example.com |
| 309 | verbatim: username=one |
| 310 | EOF |
| 311 | ' |
| 312 | |
| 313 | test_expect_success 'credential_fill passes along metadata' ' |
| 314 | check fill "verbatim one two" <<-\EOF |
| 315 | protocol=ftp |
| 316 | host=example.com |
| 317 | path=foo.git |
| 318 | -- |
| 319 | protocol=ftp |
| 320 | host=example.com |
| 321 | path=foo.git |
| 322 | username=one |
| 323 | password=two |
| 324 | -- |
| 325 | verbatim: get |
| 326 | verbatim: protocol=ftp |
| 327 | verbatim: host=example.com |
| 328 | verbatim: path=foo.git |
| 329 | EOF |
| 330 | ' |
| 331 | |
| 332 | test_expect_success 'credential_fill produces no credential without capability' ' |
| 333 | check fill "verbatim-cred Bearer token" <<-\EOF |
| 334 | protocol=http |
| 335 | host=example.com |
| 336 | -- |
| 337 | protocol=http |
| 338 | host=example.com |
| 339 | -- |
| 340 | verbatim-cred: get |
| 341 | verbatim-cred: protocol=http |
| 342 | verbatim-cred: host=example.com |
| 343 | EOF |
| 344 | ' |
| 345 | |
| 346 | test_expect_success 'credential_approve calls all helpers' ' |
| 347 | check approve useless "verbatim one two" <<-\EOF |
| 348 | protocol=http |
| 349 | host=example.com |
| 350 | username=foo |
| 351 | password=bar |
| 352 | -- |
| 353 | -- |
| 354 | useless: store |
| 355 | useless: protocol=http |
| 356 | useless: host=example.com |
| 357 | useless: username=foo |
| 358 | useless: password=bar |
| 359 | verbatim: store |
| 360 | verbatim: protocol=http |
| 361 | verbatim: host=example.com |
| 362 | verbatim: username=foo |
| 363 | verbatim: password=bar |
| 364 | EOF |
| 365 | ' |
| 366 | |
| 367 | test_expect_success 'credential_approve stores password expiry' ' |
| 368 | check approve useless <<-\EOF |
| 369 | protocol=http |
| 370 | host=example.com |
| 371 | username=foo |
| 372 | password=bar |
| 373 | password_expiry_utc=9999999999 |
| 374 | -- |
| 375 | -- |
| 376 | useless: store |
| 377 | useless: protocol=http |
| 378 | useless: host=example.com |
| 379 | useless: username=foo |
| 380 | useless: password=bar |
| 381 | useless: password_expiry_utc=9999999999 |
| 382 | EOF |
| 383 | ' |
| 384 | |
| 385 | test_expect_success 'credential_approve stores oauth refresh token' ' |
| 386 | check approve useless <<-\EOF |
| 387 | protocol=http |
| 388 | host=example.com |
| 389 | username=foo |
| 390 | password=bar |
| 391 | oauth_refresh_token=xyzzy |
| 392 | -- |
| 393 | -- |
| 394 | useless: store |
| 395 | useless: protocol=http |
| 396 | useless: host=example.com |
| 397 | useless: username=foo |
| 398 | useless: password=bar |
| 399 | useless: oauth_refresh_token=xyzzy |
| 400 | EOF |
| 401 | ' |
| 402 | |
| 403 | test_expect_success 'do not bother storing password-less credential' ' |
| 404 | check approve useless <<-\EOF |
| 405 | protocol=http |
| 406 | host=example.com |
| 407 | username=foo |
| 408 | -- |
| 409 | -- |
| 410 | EOF |
| 411 | ' |
| 412 | |
| 413 | test_expect_success 'credential_approve does not store expired password' ' |
| 414 | check approve useless <<-\EOF |
| 415 | protocol=http |
| 416 | host=example.com |
| 417 | username=foo |
| 418 | password=bar |
| 419 | password_expiry_utc=5 |
| 420 | -- |
| 421 | -- |
| 422 | EOF |
| 423 | ' |
| 424 | |
| 425 | test_expect_success 'credential_reject calls all helpers' ' |
| 426 | check reject useless "verbatim one two" <<-\EOF |
| 427 | protocol=http |
| 428 | host=example.com |
| 429 | username=foo |
| 430 | password=bar |
| 431 | -- |
| 432 | -- |
| 433 | useless: erase |
| 434 | useless: protocol=http |
| 435 | useless: host=example.com |
| 436 | useless: username=foo |
| 437 | useless: password=bar |
| 438 | verbatim: erase |
| 439 | verbatim: protocol=http |
| 440 | verbatim: host=example.com |
| 441 | verbatim: username=foo |
| 442 | verbatim: password=bar |
| 443 | EOF |
| 444 | ' |
| 445 | |
| 446 | test_expect_success 'credential_reject erases credential regardless of expiry' ' |
| 447 | check reject useless <<-\EOF |
| 448 | protocol=http |
| 449 | host=example.com |
| 450 | username=foo |
| 451 | password=bar |
| 452 | password_expiry_utc=5 |
| 453 | -- |
| 454 | -- |
| 455 | useless: erase |
| 456 | useless: protocol=http |
| 457 | useless: host=example.com |
| 458 | useless: username=foo |
| 459 | useless: password=bar |
| 460 | useless: password_expiry_utc=5 |
| 461 | EOF |
| 462 | ' |
| 463 | |
| 464 | test_expect_success 'usernames can be preserved' ' |
| 465 | check fill "verbatim \"\" three" <<-\EOF |
| 466 | protocol=http |
| 467 | host=example.com |
| 468 | username=one |
| 469 | -- |
| 470 | protocol=http |
| 471 | host=example.com |
| 472 | username=one |
| 473 | password=three |
| 474 | -- |
| 475 | verbatim: get |
| 476 | verbatim: protocol=http |
| 477 | verbatim: host=example.com |
| 478 | verbatim: username=one |
| 479 | EOF |
| 480 | ' |
| 481 | |
| 482 | test_expect_success 'usernames can be overridden' ' |
| 483 | check fill "verbatim two three" <<-\EOF |
| 484 | protocol=http |
| 485 | host=example.com |
| 486 | username=one |
| 487 | -- |
| 488 | protocol=http |
| 489 | host=example.com |
| 490 | username=two |
| 491 | password=three |
| 492 | -- |
| 493 | verbatim: get |
| 494 | verbatim: protocol=http |
| 495 | verbatim: host=example.com |
| 496 | verbatim: username=one |
| 497 | EOF |
| 498 | ' |
| 499 | |
| 500 | test_expect_success 'do not bother completing already-full credential' ' |
| 501 | check fill "verbatim three four" <<-\EOF |
| 502 | protocol=http |
| 503 | host=example.com |
| 504 | username=one |
| 505 | password=two |
| 506 | -- |
| 507 | protocol=http |
| 508 | host=example.com |
| 509 | username=one |
| 510 | password=two |
| 511 | -- |
| 512 | EOF |
| 513 | ' |
| 514 | |
| 515 | # We can't test the basic terminal password prompt here because |
| 516 | # getpass() tries too hard to find the real terminal. But if our |
| 517 | # askpass helper is run, we know the internal getpass is working. |
| 518 | test_expect_success 'empty helper list falls back to internal getpass' ' |
| 519 | check fill <<-\EOF |
| 520 | protocol=http |
| 521 | host=example.com |
| 522 | -- |
| 523 | protocol=http |
| 524 | host=example.com |
| 525 | username=askpass-username |
| 526 | password=askpass-password |
| 527 | -- |
| 528 | askpass: Username for '\''http://example.com'\'': |
| 529 | askpass: Password for '\''http://askpass-username@example.com'\'': |
| 530 | EOF |
| 531 | ' |
| 532 | |
| 533 | test_expect_success 'internal getpass does not ask for known username' ' |
| 534 | check fill <<-\EOF |
| 535 | protocol=http |
| 536 | host=example.com |
| 537 | username=foo |
| 538 | -- |
| 539 | protocol=http |
| 540 | host=example.com |
| 541 | username=foo |
| 542 | password=askpass-password |
| 543 | -- |
| 544 | askpass: Password for '\''http://foo@example.com'\'': |
| 545 | EOF |
| 546 | ' |
| 547 | |
| 548 | test_expect_success 'git-credential respects core.askPass' ' |
| 549 | write_script alternate-askpass <<-\EOF && |
| 550 | echo >&2 "alternate askpass invoked" |
| 551 | echo alternate-value |
| 552 | EOF |
| 553 | test_config core.askpass "$PWD/alternate-askpass" && |
| 554 | ( |
| 555 | # unset GIT_ASKPASS set by lib-credential.sh which would |
| 556 | # override our config, but do so in a subshell so that we do |
| 557 | # not interfere with other tests |
| 558 | sane_unset GIT_ASKPASS && |
| 559 | check fill <<-\EOF |
| 560 | protocol=http |
| 561 | host=example.com |
| 562 | -- |
| 563 | protocol=http |
| 564 | host=example.com |
| 565 | username=alternate-value |
| 566 | password=alternate-value |
| 567 | -- |
| 568 | alternate askpass invoked |
| 569 | alternate askpass invoked |
| 570 | EOF |
| 571 | ) |
| 572 | ' |
| 573 | |
| 574 | HELPER="!f() { |
| 575 | cat >/dev/null |
| 576 | echo username=foo |
| 577 | echo password=bar |
| 578 | }; f" |
| 579 | test_expect_success 'respect configured credentials' ' |
| 580 | test_config credential.helper "$HELPER" && |
| 581 | check fill <<-\EOF |
| 582 | protocol=http |
| 583 | host=example.com |
| 584 | -- |
| 585 | protocol=http |
| 586 | host=example.com |
| 587 | username=foo |
| 588 | password=bar |
| 589 | -- |
| 590 | EOF |
| 591 | ' |
| 592 | |
| 593 | test_expect_success 'match configured credential' ' |
| 594 | test_config credential.https://example.com.helper "$HELPER" && |
| 595 | check fill <<-\EOF |
| 596 | protocol=https |
| 597 | host=example.com |
| 598 | path=repo.git |
| 599 | -- |
| 600 | protocol=https |
| 601 | host=example.com |
| 602 | username=foo |
| 603 | password=bar |
| 604 | -- |
| 605 | EOF |
| 606 | ' |
| 607 | |
| 608 | test_expect_success 'do not match configured credential' ' |
| 609 | test_config credential.https://foo.helper "$HELPER" && |
| 610 | check fill <<-\EOF |
| 611 | protocol=https |
| 612 | host=bar |
| 613 | -- |
| 614 | protocol=https |
| 615 | host=bar |
| 616 | username=askpass-username |
| 617 | password=askpass-password |
| 618 | -- |
| 619 | askpass: Username for '\''https://bar'\'': |
| 620 | askpass: Password for '\''https://askpass-username@bar'\'': |
| 621 | EOF |
| 622 | ' |
| 623 | |
| 624 | test_expect_success 'match multiple configured helpers' ' |
| 625 | test_config credential.helper "verbatim \"\" \"\"" && |
| 626 | test_config credential.https://example.com.helper "$HELPER" && |
| 627 | check fill <<-\EOF |
| 628 | protocol=https |
| 629 | host=example.com |
| 630 | path=repo.git |
| 631 | -- |
| 632 | protocol=https |
| 633 | host=example.com |
| 634 | username=foo |
| 635 | password=bar |
| 636 | -- |
| 637 | verbatim: get |
| 638 | verbatim: protocol=https |
| 639 | verbatim: host=example.com |
| 640 | EOF |
| 641 | ' |
| 642 | |
| 643 | test_expect_success 'match multiple configured helpers with URLs' ' |
| 644 | test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" && |
| 645 | test_config credential.https://example.com.helper "$HELPER" && |
| 646 | check fill <<-\EOF |
| 647 | protocol=https |
| 648 | host=example.com |
| 649 | path=repo.git |
| 650 | -- |
| 651 | protocol=https |
| 652 | host=example.com |
| 653 | username=foo |
| 654 | password=bar |
| 655 | -- |
| 656 | verbatim: get |
| 657 | verbatim: protocol=https |
| 658 | verbatim: host=example.com |
| 659 | EOF |
| 660 | ' |
| 661 | |
| 662 | test_expect_success 'match percent-encoded values' ' |
| 663 | test_config credential.https://example.com/%2566.git.helper "$HELPER" && |
| 664 | check fill <<-\EOF |
| 665 | url=https://example.com/%2566.git |
| 666 | -- |
| 667 | protocol=https |
| 668 | host=example.com |
| 669 | username=foo |
| 670 | password=bar |
| 671 | -- |
| 672 | EOF |
| 673 | ' |
| 674 | |
| 675 | test_expect_success 'match percent-encoded UTF-8 values in path' ' |
| 676 | test_config credential.https://example.com.useHttpPath true && |
| 677 | test_config credential.https://example.com/perú.git.helper "$HELPER" && |
| 678 | # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs |
| 679 | # that contain multibyte chars. |
| 680 | check fill <<-EOF |
| 681 | url=https://example.com/per%C3%BA.git |
| 682 | -- |
| 683 | protocol=https |
| 684 | host=example.com |
| 685 | path=perú.git |
| 686 | username=foo |
| 687 | password=bar |
| 688 | -- |
| 689 | EOF |
| 690 | ' |
| 691 | |
| 692 | test_expect_success 'match percent-encoded values in username' ' |
| 693 | test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" && |
| 694 | check fill <<-\EOF |
| 695 | url=https://user%2fname@example.com/foo/bar.git |
| 696 | -- |
| 697 | protocol=https |
| 698 | host=example.com |
| 699 | username=foo |
| 700 | password=bar |
| 701 | -- |
| 702 | EOF |
| 703 | ' |
| 704 | |
| 705 | test_expect_success 'match percent-encoded values in hostname' ' |
| 706 | test_config "credential.https://a%20b%20c/.helper" "$HELPER" && |
| 707 | check fill <<-\EOF |
| 708 | url=https://a b c/ |
| 709 | -- |
| 710 | protocol=https |
| 711 | host=a b c |
| 712 | username=foo |
| 713 | password=bar |
| 714 | -- |
| 715 | EOF |
| 716 | ' |
| 717 | |
| 718 | test_expect_success 'fetch with multiple path components' ' |
| 719 | test_unconfig credential.helper && |
| 720 | test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" && |
| 721 | check fill <<-\EOF |
| 722 | url=https://example.com/foo/repo.git |
| 723 | -- |
| 724 | protocol=https |
| 725 | host=example.com |
| 726 | username=foo |
| 727 | password=bar |
| 728 | -- |
| 729 | verbatim: get |
| 730 | verbatim: protocol=https |
| 731 | verbatim: host=example.com |
| 732 | EOF |
| 733 | ' |
| 734 | |
| 735 | test_expect_success 'pull username from config' ' |
| 736 | test_config credential.https://example.com.username foo && |
| 737 | check fill <<-\EOF |
| 738 | protocol=https |
| 739 | host=example.com |
| 740 | -- |
| 741 | protocol=https |
| 742 | host=example.com |
| 743 | username=foo |
| 744 | password=askpass-password |
| 745 | -- |
| 746 | askpass: Password for '\''https://foo@example.com'\'': |
| 747 | EOF |
| 748 | ' |
| 749 | |
| 750 | test_expect_success 'honors username from URL over helper (URL)' ' |
| 751 | test_config credential.https://example.com.username bob && |
| 752 | test_config credential.https://example.com.helper "verbatim \"\" bar" && |
| 753 | check fill <<-\EOF |
| 754 | url=https://alice@example.com |
| 755 | -- |
| 756 | protocol=https |
| 757 | host=example.com |
| 758 | username=alice |
| 759 | password=bar |
| 760 | -- |
| 761 | verbatim: get |
| 762 | verbatim: protocol=https |
| 763 | verbatim: host=example.com |
| 764 | verbatim: username=alice |
| 765 | EOF |
| 766 | ' |
| 767 | |
| 768 | test_expect_success 'honors username from URL over helper (components)' ' |
| 769 | test_config credential.https://example.com.username bob && |
| 770 | test_config credential.https://example.com.helper "verbatim \"\" bar" && |
| 771 | check fill <<-\EOF |
| 772 | protocol=https |
| 773 | host=example.com |
| 774 | username=alice |
| 775 | -- |
| 776 | protocol=https |
| 777 | host=example.com |
| 778 | username=alice |
| 779 | password=bar |
| 780 | -- |
| 781 | verbatim: get |
| 782 | verbatim: protocol=https |
| 783 | verbatim: host=example.com |
| 784 | verbatim: username=alice |
| 785 | EOF |
| 786 | ' |
| 787 | |
| 788 | test_expect_success 'last matching username wins' ' |
| 789 | test_config credential.https://example.com/path.git.username bob && |
| 790 | test_config credential.https://example.com.username alice && |
| 791 | test_config credential.https://example.com.helper "verbatim \"\" bar" && |
| 792 | check fill <<-\EOF |
| 793 | url=https://example.com/path.git |
| 794 | -- |
| 795 | protocol=https |
| 796 | host=example.com |
| 797 | username=alice |
| 798 | password=bar |
| 799 | -- |
| 800 | verbatim: get |
| 801 | verbatim: protocol=https |
| 802 | verbatim: host=example.com |
| 803 | verbatim: username=alice |
| 804 | EOF |
| 805 | ' |
| 806 | |
| 807 | test_expect_success 'http paths can be part of context' ' |
| 808 | check fill "verbatim foo bar" <<-\EOF && |
| 809 | protocol=https |
| 810 | host=example.com |
| 811 | path=foo.git |
| 812 | -- |
| 813 | protocol=https |
| 814 | host=example.com |
| 815 | username=foo |
| 816 | password=bar |
| 817 | -- |
| 818 | verbatim: get |
| 819 | verbatim: protocol=https |
| 820 | verbatim: host=example.com |
| 821 | EOF |
| 822 | test_config credential.https://example.com.useHttpPath true && |
| 823 | check fill "verbatim foo bar" <<-\EOF |
| 824 | protocol=https |
| 825 | host=example.com |
| 826 | path=foo.git |
| 827 | -- |
| 828 | protocol=https |
| 829 | host=example.com |
| 830 | path=foo.git |
| 831 | username=foo |
| 832 | password=bar |
| 833 | -- |
| 834 | verbatim: get |
| 835 | verbatim: protocol=https |
| 836 | verbatim: host=example.com |
| 837 | verbatim: path=foo.git |
| 838 | EOF |
| 839 | ' |
| 840 | |
| 841 | test_expect_success 'context uses urlmatch' ' |
| 842 | test_config "credential.https://*.org.useHttpPath" true && |
| 843 | check fill "verbatim foo bar" <<-\EOF |
| 844 | protocol=https |
| 845 | host=example.org |
| 846 | path=foo.git |
| 847 | -- |
| 848 | protocol=https |
| 849 | host=example.org |
| 850 | path=foo.git |
| 851 | username=foo |
| 852 | password=bar |
| 853 | -- |
| 854 | verbatim: get |
| 855 | verbatim: protocol=https |
| 856 | verbatim: host=example.org |
| 857 | verbatim: path=foo.git |
| 858 | EOF |
| 859 | ' |
| 860 | |
| 861 | test_expect_success 'helpers can abort the process' ' |
| 862 | test_must_fail git \ |
| 863 | -c credential.helper=quit \ |
| 864 | -c credential.helper="verbatim foo bar" \ |
| 865 | credential fill >stdout 2>stderr <<-\EOF && |
| 866 | protocol=http |
| 867 | host=example.com |
| 868 | EOF |
| 869 | test_must_be_empty stdout && |
| 870 | cat >expect <<-\EOF && |
| 871 | quit: get |
| 872 | quit: protocol=http |
| 873 | quit: host=example.com |
| 874 | fatal: credential helper '\''quit'\'' told us to quit |
| 875 | EOF |
| 876 | test_cmp expect stderr |
| 877 | ' |
| 878 | |
| 879 | test_expect_success 'empty helper spec resets helper list' ' |
| 880 | test_config credential.helper "verbatim file file" && |
| 881 | check fill "" "verbatim cmdline cmdline" <<-\EOF |
| 882 | protocol=http |
| 883 | host=example.com |
| 884 | -- |
| 885 | protocol=http |
| 886 | host=example.com |
| 887 | username=cmdline |
| 888 | password=cmdline |
| 889 | -- |
| 890 | verbatim: get |
| 891 | verbatim: protocol=http |
| 892 | verbatim: host=example.com |
| 893 | EOF |
| 894 | ' |
| 895 | |
| 896 | test_expect_success 'url parser rejects embedded newlines' ' |
| 897 | test_must_fail git credential fill 2>stderr <<-\EOF && |
| 898 | url=https://one.example.com?%0ahost=two.example.com/ |
| 899 | EOF |
| 900 | cat >expect <<-\EOF && |
| 901 | warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/ |
| 902 | fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/ |
| 903 | EOF |
| 904 | test_cmp expect stderr |
| 905 | ' |
| 906 | |
| 907 | test_expect_success 'url parser rejects embedded carriage returns' ' |
| 908 | test_config credential.helper "!true" && |
| 909 | test_must_fail git credential fill 2>stderr <<-\EOF && |
| 910 | url=https://example%0d.com/ |
| 911 | EOF |
| 912 | cat >expect <<-\EOF && |
| 913 | fatal: credential value for host contains carriage return |
| 914 | If this is intended, set `credential.protectProtocol=false` |
| 915 | EOF |
| 916 | test_cmp expect stderr && |
| 917 | GIT_ASKPASS=true \ |
| 918 | git -c credential.protectProtocol=false credential fill <<-\EOF |
| 919 | url=https://example%0d.com/ |
| 920 | EOF |
| 921 | ' |
| 922 | |
| 923 | test_expect_success 'host-less URLs are parsed as empty host' ' |
| 924 | check fill "verbatim foo bar" <<-\EOF |
| 925 | url=cert:///path/to/cert.pem |
| 926 | -- |
| 927 | protocol=cert |
| 928 | host= |
| 929 | path=path/to/cert.pem |
| 930 | username=foo |
| 931 | password=bar |
| 932 | -- |
| 933 | verbatim: get |
| 934 | verbatim: protocol=cert |
| 935 | verbatim: host= |
| 936 | verbatim: path=path/to/cert.pem |
| 937 | EOF |
| 938 | ' |
| 939 | |
| 940 | test_expect_success 'credential system refuses to work with missing host' ' |
| 941 | test_must_fail git credential fill 2>stderr <<-\EOF && |
| 942 | protocol=http |
| 943 | EOF |
| 944 | cat >expect <<-\EOF && |
| 945 | fatal: refusing to work with credential missing host field |
| 946 | EOF |
| 947 | test_cmp expect stderr |
| 948 | ' |
| 949 | |
| 950 | test_expect_success 'credential system refuses to work with missing protocol' ' |
| 951 | test_must_fail git credential fill 2>stderr <<-\EOF && |
| 952 | host=example.com |
| 953 | EOF |
| 954 | cat >expect <<-\EOF && |
| 955 | fatal: refusing to work with credential missing protocol field |
| 956 | EOF |
| 957 | test_cmp expect stderr |
| 958 | ' |
| 959 | |
| 960 | # usage: check_host_and_path <url> <expected-host> <expected-path> |
| 961 | check_host_and_path () { |
| 962 | # we always parse the path component, but we need this to make sure it |
| 963 | # is passed to the helper |
| 964 | test_config credential.useHTTPPath true && |
| 965 | check fill "verbatim user pass" <<-EOF |
| 966 | url=$1 |
| 967 | -- |
| 968 | protocol=https |
| 969 | host=$2 |
| 970 | path=$3 |
| 971 | username=user |
| 972 | password=pass |
| 973 | -- |
| 974 | verbatim: get |
| 975 | verbatim: protocol=https |
| 976 | verbatim: host=$2 |
| 977 | verbatim: path=$3 |
| 978 | EOF |
| 979 | } |
| 980 | |
| 981 | test_expect_success 'url parser handles bare query marker' ' |
| 982 | check_host_and_path https://example.com?foo.git example.com ?foo.git |
| 983 | ' |
| 984 | |
| 985 | test_expect_success 'url parser handles bare fragment marker' ' |
| 986 | check_host_and_path https://example.com#foo.git example.com "#foo.git" |
| 987 | ' |
| 988 | |
| 989 | test_expect_success 'url parser not confused by encoded markers' ' |
| 990 | check_host_and_path https://example.com%23%3f%2f/foo.git \ |
| 991 | "example.com#?/" foo.git |
| 992 | ' |
| 993 | |
| 994 | test_expect_success 'credential config with partial URLs' ' |
| 995 | echo "echo password=yep" | write_script git-credential-yep && |
| 996 | test_write_lines url=https://user@example.com/org/repo.git >stdin && |
| 997 | for partial in \ |
| 998 | example.com \ |
| 999 | example.com/org/repo.git \ |
| 1000 | user@example.com \ |
| 1001 | user@example.com/org/repo.git \ |
| 1002 | https:// \ |
| 1003 | https://example.com \ |
| 1004 | https://example.com/ \ |
| 1005 | https://example.com/org \ |
| 1006 | https://example.com/org/ \ |
| 1007 | https://example.com/org/repo.git \ |
| 1008 | https://user@example.com \ |
| 1009 | https://user@example.com/ \ |
| 1010 | https://user@example.com/org \ |
| 1011 | https://user@example.com/org/ \ |
| 1012 | https://user@example.com/org/repo.git \ |
| 1013 | /org/repo.git |
| 1014 | do |
| 1015 | git -c credential.$partial.helper=yep \ |
| 1016 | credential fill <stdin >stdout && |
| 1017 | test_grep yep stdout || |
| 1018 | return 1 |
| 1019 | done && |
| 1020 | |
| 1021 | for partial in \ |
| 1022 | dont.use.this \ |
| 1023 | example.com/o \ |
| 1024 | user@example.com/o \ |
| 1025 | http:// \ |
| 1026 | https://example.com/o \ |
| 1027 | https://user@example.com/o \ |
| 1028 | /o \ |
| 1029 | /repo |
| 1030 | do |
| 1031 | git -c credential.$partial.helper=yep \ |
| 1032 | credential fill <stdin >stdout && |
| 1033 | test_grep ! yep stdout || |
| 1034 | return 1 |
| 1035 | done && |
| 1036 | |
| 1037 | git -c credential.$partial.helper=yep \ |
| 1038 | -c credential.with%0anewline.username=uh-oh \ |
| 1039 | credential fill <stdin 2>stderr && |
| 1040 | test_grep "skipping credential lookup for key" stderr |
| 1041 | ' |
| 1042 | |
| 1043 | BEL="$(printf '\007')" |
| 1044 | |
| 1045 | test_expect_success 'interactive prompt is sanitized' ' |
| 1046 | check fill cntrl-in-username <<-EOF |
| 1047 | protocol=https |
| 1048 | host=example.org |
| 1049 | -- |
| 1050 | protocol=https |
| 1051 | host=example.org |
| 1052 | username=${BEL}latrix Lestrange |
| 1053 | password=askpass-password |
| 1054 | -- |
| 1055 | askpass: Password for ${SQ}https://%07latrix%20Lestrange@example.org${SQ}: |
| 1056 | EOF |
| 1057 | ' |
| 1058 | |
| 1059 | test_done |