1# -*- coding: utf-8 -*- 2"""Tests for the validators module.""" 3import rfc3986 4from rfc3986 import exceptions 5from rfc3986 import validators 6 7import pytest 8 9 10def test_defaults(): 11 """Verify the default Validator settings.""" 12 validator = validators.Validator() 13 14 assert validator.required_components == { 15 c: False for c in validator.COMPONENT_NAMES 16 } 17 assert validator.allow_password is True 18 assert validator.allowed_schemes == set() 19 assert validator.allowed_hosts == set() 20 assert validator.allowed_ports == set() 21 22 23def test_allowing_schemes(): 24 """Verify the ability to select schemes to be allowed.""" 25 validator = validators.Validator().allow_schemes("http", "https") 26 27 assert "http" in validator.allowed_schemes 28 assert "https" in validator.allowed_schemes 29 30 31def test_allowing_hosts(): 32 """Verify the ability to select hosts to be allowed.""" 33 validator = validators.Validator().allow_hosts( 34 "pypi.python.org", 35 "pypi.org", 36 ) 37 38 assert "pypi.python.org" in validator.allowed_hosts 39 assert "pypi.org" in validator.allowed_hosts 40 41 42def test_allowing_ports(): 43 """Verify the ability select ports to be allowed.""" 44 validator = validators.Validator().allow_ports("80", "100") 45 46 assert "80" in validator.allowed_ports 47 assert "100" in validator.allowed_ports 48 49 50def test_requiring_invalid_component(): 51 """Verify that we validate required component names.""" 52 with pytest.raises(ValueError): 53 validators.Validator().require_presence_of("frob") 54 55 56def test_checking_validity_of_component(): 57 """Verify that we validate components we're validating.""" 58 with pytest.raises(ValueError): 59 validators.Validator().check_validity_of("frob") 60 61 62def test_use_of_password(): 63 """Verify the behaviour of {forbid,allow}_use_of_password.""" 64 validator = validators.Validator() 65 assert validator.allow_password is True 66 67 validator.forbid_use_of_password() 68 assert validator.allow_password is False 69 70 validator.allow_use_of_password() 71 assert validator.allow_password is True 72 73 74@pytest.mark.parametrize( 75 "uri", 76 [ 77 rfc3986.uri_reference("https://user:password@github.com"), 78 rfc3986.uri_reference("https://user:password@github.com/path"), 79 rfc3986.uri_reference("https://user:password@github.com/path?query"), 80 rfc3986.uri_reference( 81 "https://user:password@github.com/path?query#frag" 82 ), 83 rfc3986.uri_reference("//user:password@github.com"), 84 ], 85) 86def test_forbidden_passwords(uri): 87 """Verify that passwords are disallowed.""" 88 validator = validators.Validator().forbid_use_of_password() 89 with pytest.raises(exceptions.PasswordForbidden): 90 validator.validate(uri) 91 92 93@pytest.mark.parametrize( 94 "uri", 95 [ 96 rfc3986.uri_reference("https://user@github.com"), 97 rfc3986.uri_reference("https://user@github.com/path"), 98 rfc3986.uri_reference("https://user@github.com/path?query"), 99 rfc3986.uri_reference("https://user@github.com/path?query#frag"), 100 rfc3986.uri_reference("//user@github.com"), 101 rfc3986.uri_reference("//github.com"), 102 rfc3986.uri_reference("https://github.com"), 103 ], 104) 105def test_passwordless_uris_pass_validation(uri): 106 """Verify password-less URLs validate properly.""" 107 validator = validators.Validator().forbid_use_of_password() 108 validator.validate(uri) 109 110 111@pytest.mark.parametrize( 112 "uri", 113 [ 114 rfc3986.uri_reference("https://"), 115 rfc3986.uri_reference("/path/to/resource"), 116 ], 117) 118def test_missing_host_component(uri): 119 """Verify that missing host components cause errors.""" 120 validators.Validator().validate(uri) 121 122 validator = validators.Validator().require_presence_of("host") 123 with pytest.raises(exceptions.MissingComponentError): 124 validator.validate(uri) 125 126 127@pytest.mark.parametrize( 128 "uri", 129 [ 130 rfc3986.uri_reference("https://"), 131 rfc3986.uri_reference("//google.com"), 132 rfc3986.uri_reference("//google.com?query=value"), 133 rfc3986.uri_reference("//google.com#fragment"), 134 rfc3986.uri_reference("https://google.com"), 135 rfc3986.uri_reference("https://google.com#fragment"), 136 rfc3986.uri_reference("https://google.com?query=value"), 137 ], 138) 139def test_missing_path_component(uri): 140 """Verify that missing path components cause errors.""" 141 validator = validators.Validator().require_presence_of("path") 142 with pytest.raises(exceptions.MissingComponentError): 143 validator.validate(uri) 144 145 146@pytest.mark.parametrize( 147 "uri", 148 [ 149 rfc3986.uri_reference("//google.com"), 150 rfc3986.uri_reference("//google.com?query=value"), 151 rfc3986.uri_reference("//google.com#fragment"), 152 ], 153) 154def test_multiple_missing_components(uri): 155 """Verify that multiple missing components are caught.""" 156 validator = validators.Validator().require_presence_of("scheme", "path") 157 with pytest.raises(exceptions.MissingComponentError) as captured_exc: 158 validator.validate(uri) 159 exception = captured_exc.value 160 assert 2 == len(exception.args[-1]) 161 162 163@pytest.mark.parametrize( 164 "uri", 165 [ 166 rfc3986.uri_reference("smtp://"), 167 rfc3986.uri_reference("telnet://"), 168 ], 169) 170def test_ensure_uri_has_a_scheme(uri): 171 """Verify validation with allowed schemes.""" 172 validator = validators.Validator().allow_schemes("https", "http") 173 with pytest.raises(exceptions.UnpermittedComponentError): 174 validator.validate(uri) 175 176 177@pytest.mark.parametrize( 178 "uri, failed_component", 179 [ 180 (rfc3986.uri_reference("git://github.com"), "scheme"), 181 (rfc3986.uri_reference("http://github.com"), "scheme"), 182 (rfc3986.uri_reference("ssh://gitlab.com"), "host"), 183 (rfc3986.uri_reference("https://gitlab.com"), "host"), 184 ], 185) 186def test_allowed_hosts_and_schemes(uri, failed_component): 187 """Verify each of these fails.""" 188 validator = ( 189 validators.Validator() 190 .allow_schemes( 191 "https", 192 "ssh", 193 ) 194 .allow_hosts( 195 "github.com", 196 "git.openstack.org", 197 ) 198 ) 199 with pytest.raises(exceptions.UnpermittedComponentError) as caught_exc: 200 validator.validate(uri) 201 202 exc = caught_exc.value 203 assert exc.component_name == failed_component 204 205 206@pytest.mark.parametrize( 207 "uri", 208 [ 209 rfc3986.uri_reference("https://github.com/sigmavirus24"), 210 rfc3986.uri_reference("ssh://github.com/sigmavirus24"), 211 rfc3986.uri_reference("ssh://ssh@github.com:22/sigmavirus24"), 212 rfc3986.uri_reference("https://github.com:443/sigmavirus24"), 213 rfc3986.uri_reference("https://gitlab.com/sigmavirus24"), 214 rfc3986.uri_reference("ssh://gitlab.com/sigmavirus24"), 215 rfc3986.uri_reference("ssh://ssh@gitlab.com:22/sigmavirus24"), 216 rfc3986.uri_reference("https://gitlab.com:443/sigmavirus24"), 217 rfc3986.uri_reference("https://bitbucket.org/sigmavirus24"), 218 rfc3986.uri_reference("ssh://bitbucket.org/sigmavirus24"), 219 rfc3986.uri_reference("ssh://ssh@bitbucket.org:22/sigmavirus24"), 220 rfc3986.uri_reference("https://bitbucket.org:443/sigmavirus24"), 221 rfc3986.uri_reference("https://git.openstack.org/sigmavirus24"), 222 rfc3986.uri_reference("ssh://git.openstack.org/sigmavirus24"), 223 rfc3986.uri_reference("ssh://ssh@git.openstack.org:22/sigmavirus24"), 224 rfc3986.uri_reference("https://git.openstack.org:443/sigmavirus24"), 225 rfc3986.uri_reference( 226 "ssh://ssh@git.openstack.org:22/sigmavirus24?foo=bar#fragment" 227 ), 228 rfc3986.uri_reference( 229 "ssh://git.openstack.org:22/sigmavirus24?foo=bar#fragment" 230 ), 231 rfc3986.uri_reference("ssh://git.openstack.org:22/?foo=bar#fragment"), 232 rfc3986.uri_reference( 233 "ssh://git.openstack.org:22/sigmavirus24#fragment" 234 ), 235 rfc3986.uri_reference("ssh://git.openstack.org:22/#fragment"), 236 rfc3986.uri_reference("ssh://git.openstack.org:22/"), 237 rfc3986.uri_reference( 238 "ssh://ssh@git.openstack.org:22/?foo=bar#fragment" 239 ), 240 rfc3986.uri_reference( 241 "ssh://ssh@git.openstack.org:22/sigmavirus24#fragment" 242 ), 243 rfc3986.uri_reference("ssh://ssh@git.openstack.org:22/#fragment"), 244 rfc3986.uri_reference("ssh://ssh@git.openstack.org:22/"), 245 ], 246) 247def test_successful_complex_validation(uri): 248 """Verify we do not raise ValidationErrors for good URIs.""" 249 validators.Validator().allow_schemes("https", "ssh",).allow_hosts( 250 "github.com", 251 "bitbucket.org", 252 "gitlab.com", 253 "git.openstack.org", 254 ).allow_ports("22", "443",).require_presence_of( 255 "scheme", 256 "host", 257 "path", 258 ).check_validity_of( 259 "scheme", 260 "userinfo", 261 "host", 262 "port", 263 "path", 264 "query", 265 "fragment", 266 ).validate( 267 uri 268 ) 269 270 271def test_invalid_uri_generates_error(invalid_uri): 272 """Verify we catch invalid URIs.""" 273 uri = rfc3986.uri_reference(invalid_uri) 274 with pytest.raises(exceptions.InvalidComponentsError): 275 validators.Validator().check_validity_of("host").validate(uri) 276 277 278def test_invalid_uri_with_invalid_path(invalid_uri): 279 """Verify we catch multiple invalid components.""" 280 uri = rfc3986.uri_reference(invalid_uri) 281 uri = uri.copy_with(path="#foobar") 282 with pytest.raises(exceptions.InvalidComponentsError): 283 validators.Validator().check_validity_of( 284 "host", 285 "path", 286 ).validate(uri) 287 288 289def test_validating_rfc_4007_ipv6_zone_ids(): 290 """Verify that RFC 4007 IPv6 Zone IDs are invalid 291 host/authority but after normalization are valid 292 """ 293 uri = rfc3986.uri_reference("http://[::1%eth0]") 294 with pytest.raises(exceptions.InvalidComponentsError): 295 validators.Validator().check_validity_of("host").validate(uri) 296 297 uri = uri.normalize() 298 assert uri.host == "[::1%25eth0]" 299 300 validators.Validator().check_validity_of("host").validate(uri) 301