Skip to content

Commit 5a62fb5

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, 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 angular#13528
1 parent 031303d commit 5a62fb5

File tree

2 files changed

+123
-19
lines changed

2 files changed

+123
-19
lines changed

Diff for: src/ng/directive/input.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@
1010
*/
1111

1212
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
13-
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)/;
1413
// 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#!:.?+=&%@\-/[\]$'()*,;~]*)?$/;
14+
// Note: We are being more lenient, because browsers are too.
15+
// 1. Scheme
16+
// 2. Slashes
17+
// 3. Username
18+
// 4. Password
19+
// 5. Hostname
20+
// 6. Port
21+
// 7. Path
22+
// 8. Query
23+
// 9. Fragment
24+
// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
25+
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
26+
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)/;
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})$/;

Diff for: test/ng/directive/inputSpec.js

+110-17
Original file line numberDiff line numberDiff line change
@@ -2534,23 +2534,116 @@ describe('input', function() {
25342534
});
25352535

25362536

2537-
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:[email protected]?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('[email protected]')).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);
2537+
ddescribe('URL_REGEXP', function() {
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:[email protected]: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://[email protected]', true],
2566+
['scheme://[email protected]', true],
2567+
['scheme://u#s$e%r^n&a*m;[email protected]', true],
2568+
['scheme://:[email protected]', true],
2569+
['scheme://username:[email protected]', true],
2570+
['scheme://username:pass:[email protected]', true],
2571+
['scheme://username:[email protected]', true],
2572+
['scheme://username:p#a$s%s^w&o*r;[email protected]', 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:[email protected]?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+
['[email protected]', 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)