Skip to content

Commit

Permalink
Only add schemes to URLs with hostnames
Browse files Browse the repository at this point in the history
Fixes: #1250

This changes the way a scheme is prefixed to a URL when linkValidation
is on. Previously any URL that did not match the scheme regex would be
prefixed with 'http://' which meant that an absolute path such as '/'
would be changed to 'http:///'

This changes it so that the 'http://' is only prefixed if the first part
of the path looks like a hostname, which allows absolute paths to be
unchanged and most relative paths to be unchanged.

I say most as a path such as "test.txt" would be matched as a hostname and
converted to 'http://test.txt' but this seems an acceptable trade off
and definitely better than trying to store a list of valid TLDs so
filenames and hostnames can be determined.
  • Loading branch information
kevindew committed Dec 10, 2016
1 parent 34f070f commit 3dfc087
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 7 deletions.
57 changes: 57 additions & 0 deletions spec/anchor.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,63 @@ describe('Anchor Button TestCase', function () {
expect(link.getAttribute('href')).toBe(validHashLink);
});

it('should not add a scheme to an absolute path', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
absolutePath = '/test',
link,
anchorExtension = editor.getExtensionByName('anchor');

selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(absolutePath);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');

link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(absolutePath);
});

it('should not add a scheme to an obviously relative path', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
relativePath = 'test/file.html',
link,
anchorExtension = editor.getExtensionByName('anchor');

selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(relativePath);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');

link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(relativePath);
});

it('should add a scheme to a localhost url', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
localhostUrl = 'http://localhost',
link,
anchorExtension = editor.getExtensionByName('anchor');

selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('localhost');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');

link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(localhostUrl);
});

it('should change spaces to %20 for a valid url if linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
Expand Down
23 changes: 16 additions & 7 deletions src/js/extensions/anchor.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@
// Matches common external protocols "mailto:" "tel:" "maps:"
// Matches relative hash link, begins with "#"
var urlSchemeRegex = /^([a-z]+:)?\/\/|^(mailto|tel|maps):|^\#/i,
hasScheme = urlSchemeRegex.test(value),
scheme = '',
// telRegex is a regex for checking if the string is a telephone number
telRegex = /^\+?\s?\(?(?:\d\s?\-?\)?){3,20}$/,
split = value.split('?'),
Expand All @@ -265,14 +267,21 @@

if (telRegex.test(value)) {
return 'tel:' + value;
} else {
// Check for URL scheme and default to http:// if none found
return (urlSchemeRegex.test(value) ? '' : 'http://') +
// Ensure path is encoded
this.ensureEncodedUri(path) +
// Ensure query is encoded
(query === undefined ? '' : '?' + this.ensureEncodedQuery(query));
}

if (!hasScheme) {
var host = path.split('/')[0];
// if the host part of the path looks like a hostname
if (host.match(/.+(\.|:).+/) || host === 'localhost') {
scheme = 'http://';
}
}

return scheme +
// Ensure path is encoded
this.ensureEncodedUri(path) +
// Ensure query is encoded
(query === undefined ? '' : '?' + this.ensureEncodedQuery(query));
},

doFormCancel: function () {
Expand Down

0 comments on commit 3dfc087

Please sign in to comment.