Skip to content

Importing repo by url fails with `?` at the end of the password, and other character combinations.

What

Urls such as https://abc:123!?@gitlab.example.com/engine/engine.git fail to import because Addressable::URI.parse raises an error when the url is passed in with symbols in the password segment.

According to a user, encoding the ? as %3F works, but encoding ! as %21 doesn't, which means the password can't be url-encoded automatically as a workaround.

When attempting with url "https://abc:%3F%21%23%2F%5C@gitlab.example.com/engine/engine.git" the credentials will be returned unchanged as {:user=>"abc", :password=>"%3F%21%23%2F%5C"} from Gitlab::UrlSanitizer#credentials, which I'm unsure if is intended.

Steps to reproduce

  1. Visit https://gitlab.com/projects/new
  2. Click Repo by URL and fill in https://abc:123!?@gitlab.example.com/path/to/repo.git
  3. Submitting will result in Import url must be a valid URL and Unable to save project. Error: Import url must be a valid URL`

Screenshots

Screen_Shot_2016-12-16_at_23.09.05 Screen_Shot_2016-12-16_at_23.09.34

Details

The import_url is validated with AddressableUrlValidator which gets passed the url in full. This in turn calls Gitlab::UrlSanitizer.valid?(url), which hands the input to Addressable::URI.parse. This method is fine with !?!?!? at the end of a normal URL but errors out when certain combinations are passed to it in the password segment. Its documentation suggests that the regex used is /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/, which made me try combinations of \, /, ?, and #.

pry(main)> Addressable::URI.parse('https://abc:123!?@gitlab.example.com/path/to/repo.git')
Addressable::URI::InvalidURIError: Invalid port number: "123!"
Addressable::URI::InvalidURIError: Invalid port number: "123!"
# from /Users/myuser/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/addressable-2.3.8/lib/addressable/uri.rb:1283:in `port='
pry(main)> Addressable::URI.parse('https://abc:123!@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61cff06c8 URI:https://abc:123!@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:123!\@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61d3a393c URI:https://abc:123!\@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:123!/\@gitlab.example.com/path/to/repo.git')
Addressable::URI::InvalidURIError: Invalid port number: "123!"
# from /Users/myuser/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/addressable-2.3.8/lib/addressable/uri.rb:1283:in `port='
pry(main)> Addressable::URI.parse('https://abc:123!/@gitlab.example.com/path/to/repo.git')
Addressable::URI::InvalidURIError: Invalid port number: "123!"
# from /Users/myuser/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/addressable-2.3.8/lib/addressable/uri.rb:1283:in `port='
pry(main)> Addressable::URI.parse('https://abc:12/3!@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61d325fa0 URI:https://abc:12/3!@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:12/?3!@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61a2edab8 URI:https://abc:12/?3!@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:12/?3!\@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61a2c2110 URI:https://abc:12/?3!\@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:12/?3!/@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61cefe738 URI:https://abc:12/?3!/@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:12/3!/@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61d2b1e84 URI:https://abc:12/3!/@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:123!/@gitlab.example.com/path/to/repo.git')
Addressable::URI::InvalidURIError: Invalid port number: "123!"
# from /Users/myuser/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/addressable-2.3.8/lib/addressable/uri.rb:1283:in `port='
pry(main)> Addressable::URI.parse('https://abc:123@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61d24ccc8 URI:https://abc:123@gitlab.example.com/path/to/repo.git>
pry(main)> Addressable::URI.parse('https://abc:123#@gitlab.example.com/path/to/repo.git')
=> #<Addressable::URI:0x3fc61d221f8c URI:https://abc:123#@gitlab.example.com/path/to/repo.git>

Related links

Edited by 🤖 GitLab Bot 🤖