Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 6610ae8

Browse files
committed
fix(input): fix URL validation being too strict
Background: Prior to ffb6b2f, there was a bug in `URL_REGEXP`, trying to match the hostname as `\S+` (meaning any non-space character). This resulted in never actually validating the structure of the URL (e.g. segments such as port, path, query, fragment). Then ffb6b2f and subsequently e4bb838 fixed that bug, but revealed `URL_REGEXP`'s "strictness" wrt certain parts of the URL. Since browsers are too lenient when it comes to URL validation anyway, it doesn't make sense for Angular to be much stricter, so this commit relaxes the "strictness" of `URL_REGEXP`, focusing more on the general structure, than on the specific characters allowed in each segment. Note 1: `URL_REGEXP` still seems to be stricter than browsers in some cases. Note 2: Browsers don't always agree on what is a valid URL and what isn't. Fixes #13528 Closes #13544
1 parent fa56f8e commit 6610ae8

File tree

2 files changed

+121
-17
lines changed

2 files changed

+121
-17
lines changed

src/ng/directive/input.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,18 @@
1212
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
1313
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
1414
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
15-
var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/[\]$'()*,;~]*)?$/;
15+
// Note: We are being more lenient, because browsers are too.
16+
// 1. Scheme
17+
// 2. Slashes
18+
// 3. Username
19+
// 4. Password
20+
// 5. Hostname
21+
// 6. Port
22+
// 7. Path
23+
// 8. Query
24+
// 9. Fragment
25+
// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
26+
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
1627
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
1728
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
1829
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;

test/ng/directive/inputSpec.js

+109-16
Original file line numberDiff line numberDiff line change
@@ -2535,22 +2535,115 @@ describe('input', function() {
25352535

25362536

25372537
describe('URL_REGEXP', function() {
2538-
/* global URL_REGEXP: false */
2539-
it('should validate url', function() {
2540-
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
2541-
expect(URL_REGEXP.test('http://server:123/path')).toBe(true);
2542-
expect(URL_REGEXP.test('https://server:123/path')).toBe(true);
2543-
expect(URL_REGEXP.test('file:///home/user')).toBe(true);
2544-
expect(URL_REGEXP.test('mailto:user@example.com?subject=Foo')).toBe(true);
2545-
expect(URL_REGEXP.test('r2-d2.c3-p0://localhost/foo')).toBe(true);
2546-
expect(URL_REGEXP.test('abc:/foo')).toBe(true);
2547-
expect(URL_REGEXP.test('http://example.com/path;path')).toBe(true);
2548-
expect(URL_REGEXP.test('http://example.com/[]$\'()*,~)')).toBe(true);
2549-
expect(URL_REGEXP.test('http:')).toBe(false);
2550-
expect(URL_REGEXP.test('a@B.c')).toBe(false);
2551-
expect(URL_REGEXP.test('a_B.c')).toBe(false);
2552-
expect(URL_REGEXP.test('0scheme://example.com')).toBe(false);
2553-
expect(URL_REGEXP.test('http://example.com:9999/``')).toBe(false);
2538+
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
2539+
// Note: We are being more lenient, because browsers are too.
2540+
var urls = [
2541+
['scheme://hostname', true],
2542+
['scheme://username:password@host.name:7678/pa/t.h?q=u&e=r&y#fragment', true],
2543+
2544+
// Validating `scheme`
2545+
['://example.com', false],
2546+
['0scheme://example.com', false],
2547+
['.scheme://example.com', false],
2548+
['+scheme://example.com', false],
2549+
['-scheme://example.com', false],
2550+
['_scheme://example.com', false],
2551+
['scheme0://example.com', true],
2552+
['scheme.://example.com', true],
2553+
['scheme+://example.com', true],
2554+
['scheme-://example.com', true],
2555+
['scheme_://example.com', false],
2556+
2557+
// Vaidating `:` and `/` after `scheme`
2558+
['scheme//example.com', false],
2559+
['scheme:example.com', true],
2560+
['scheme:/example.com', true],
2561+
['scheme:///example.com', true],
2562+
2563+
// Validating `username` and `password`
2564+
['scheme://@example.com', true],
2565+
['scheme://username@example.com', true],
2566+
['scheme://u0s.e+r-n_a~m!e@example.com', true],
2567+
['scheme://u#s$e%r^n&a*m;e@example.com', true],
2568+
['scheme://:password@example.com', true],
2569+
['scheme://username:password@example.com', true],
2570+
['scheme://username:pass:word@example.com', true],
2571+
['scheme://username:p0a.s+s-w_o~r!d@example.com', true],
2572+
['scheme://username:p#a$s%s^w&o*r;d@example.com', true],
2573+
2574+
// Validating `hostname`
2575+
['scheme:', false], // Chrome, FF: true
2576+
['scheme://', false], // Chrome, FF: true
2577+
['scheme:// example.com:', false], // Chrome, FF: true
2578+
['scheme://example com:', false], // Chrome, FF: true
2579+
['scheme://:', false], // Chrome, FF: true
2580+
['scheme://?', false], // Chrome, FF: true
2581+
['scheme://#', false], // Chrome, FF: true
2582+
['scheme://username:password@:', false], // Chrome, FF: true
2583+
['scheme://username:password@/', false], // Chrome, FF: true
2584+
['scheme://username:password@?', false], // Chrome, FF: true
2585+
['scheme://username:password@#', false], // Chrome, FF: true
2586+
['scheme://host.name', true],
2587+
['scheme://123.456.789.10', true],
2588+
['scheme://[1234:0000:0000:5678:9abc:0000:0000:def]', true],
2589+
['scheme://[1234:0000:0000:5678:9abc:0000:0000:def]:7678', true],
2590+
['scheme://[1234:0:0:5678:9abc:0:0:def]', true],
2591+
['scheme://[1234::5678:9abc::def]', true],
2592+
['scheme://~`!@$%^&*-_=+|\\;\'",.()[]{}<>', true],
2593+
2594+
// Validating `port`
2595+
['scheme://example.com/no-port', true],
2596+
['scheme://example.com:7678', true],
2597+
['scheme://example.com:76T8', false], // Chrome, FF: true
2598+
['scheme://example.com:port', false], // Chrome, FF: true
2599+
2600+
// Validating `path`
2601+
['scheme://example.com/', true],
2602+
['scheme://example.com/path', true],
2603+
['scheme://example.com/path/~`!@$%^&*-_=+|\\;:\'",./()[]{}<>', true],
2604+
2605+
// Validating `query`
2606+
['scheme://example.com?query', true],
2607+
['scheme://example.com/?query', true],
2608+
['scheme://example.com/path?query', true],
2609+
['scheme://example.com/path?~`!@$%^&*-_=+|\\;:\'",.?/()[]{}<>', true],
2610+
2611+
// Validating `fragment`
2612+
['scheme://example.com#fragment', true],
2613+
['scheme://example.com/#fragment', true],
2614+
['scheme://example.com/path#fragment', true],
2615+
['scheme://example.com/path/#fragment', true],
2616+
['scheme://example.com/path?query#fragment', true],
2617+
['scheme://example.com/path?query#~`!@#$%^&*-_=+|\\;:\'",.?/()[]{}<>', true],
2618+
2619+
// Validating miscellaneous
2620+
['scheme://☺.✪.⌘.➡/䨹', true],
2621+
['scheme://مثال.إختبار', true],
2622+
['scheme://例子.测试', true],
2623+
['scheme://उदाहरण.परीक्षा', true],
2624+
2625+
// Legacy tests
2626+
['http://server:123/path', true],
2627+
['https://server:123/path', true],
2628+
['file:///home/user', true],
2629+
['mailto:user@example.com?subject=Foo', true],
2630+
['r2-d2.c3-p0://localhost/foo', true],
2631+
['abc:/foo', true],
2632+
['http://example.com/path;path', true],
2633+
['http://example.com/[]$\'()*,~)', true],
2634+
['http:', false], // FF: true
2635+
['a@B.c', false],
2636+
['a_B.c', false],
2637+
['0scheme://example.com', false],
2638+
['http://example.com:9999/``', true]
2639+
];
2640+
2641+
they('should validate url: $prop', urls, function(item) {
2642+
var url = item[0];
2643+
var valid = item[1];
2644+
2645+
/* global URL_REGEXP: false */
2646+
expect(URL_REGEXP.test(url)).toBe(valid);
25542647
});
25552648
});
25562649
});

0 commit comments

Comments
 (0)