From 96d7b49621a21e4e9b3eb3c03255f64e1d99fd4d Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 16 Oct 2018 11:20:50 +0200 Subject: [PATCH] SONAR-11265 use valid-url library to validate urls --- server/sonar-web/package.json | 2 + .../organization/OrganizationDetailsStep.tsx | 6 +-- .../webhooks/components/CreateWebhookForm.tsx | 4 +- .../main/js/helpers/__tests__/urls-test.ts | 51 +------------------ server/sonar-web/src/main/js/helpers/urls.ts | 12 ----- server/sonar-web/yarn.lock | 10 ++++ 6 files changed, 18 insertions(+), 67 deletions(-) diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index f0d1f5c219a..c5e2074a0b8 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -43,6 +43,7 @@ "remark-slug": "5.0.0", "remark-toc": "5.0.0", "unist-util-visit": "1.3.1", + "valid-url": "1.0.9", "whatwg-fetch": "2.0.4" }, "devDependencies": { @@ -77,6 +78,7 @@ "@types/react-router": "3.0.13", "@types/react-select": "1.2.6", "@types/react-virtualized": "9.18.3", + "@types/valid-url": "1.0.2", "autoprefixer": "8.4.1", "babel-core": "7.0.0-bridge.0", "babel-jest": "23.6.0", diff --git a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx index 6bacc6727e6..cf5026f87ae 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { isWebUri } from 'valid-url'; import OrganizationDetailsInput from './OrganizationDetailsInput'; import Step from '../../tutorials/components/Step'; import ValidationForm, { ChildrenProps } from '../../../components/controls/ValidationForm'; @@ -25,7 +26,6 @@ import { translate } from '../../../helpers/l10n'; import { ResetButtonLink, SubmitButton } from '../../../components/ui/buttons'; import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon'; import DropdownIcon from '../../../components/icons-components/DropdownIcon'; -import { isUrl } from '../../../helpers/urls'; import { OrganizationBase } from '../../../app/types'; import { getOrganization } from '../../../api/organizations'; @@ -80,7 +80,7 @@ export default class OrganizationDetailsStep extends React.PureComponent { const errors: { [P in keyof Values]?: string } = {}; - if (avatar.length > 0 && !isUrl(avatar)) { + if (avatar.length > 0 && !isWebUri(avatar)) { errors.avatar = translate('onboarding.create_organization.avatar.error'); } @@ -92,7 +92,7 @@ export default class OrganizationDetailsStep extends React.PureComponent 0 && !isUrl(url)) { + if (url.length > 0 && !isWebUri(url)) { errors.url = translate('onboarding.create_organization.url.error'); } diff --git a/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx b/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx index 6a76095990f..462df320caa 100644 --- a/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx +++ b/server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx @@ -19,10 +19,10 @@ */ import * as React from 'react'; import { FormikProps } from 'formik'; +import { isWebUri } from 'valid-url'; import ValidationModal from '../../../components/controls/ValidationModal'; import InputValidationField from '../../../components/controls/InputValidationField'; import { Webhook } from '../../../app/types'; -import { isUrl } from '../../../helpers/urls'; import { translate } from '../../../helpers/l10n'; interface Props { @@ -53,7 +53,7 @@ export default class CreateWebhookForm extends React.PureComponent { errors.url = translate('webhooks.url.required'); } else if (!url.startsWith('http://') && !url.startsWith('https://')) { errors.url = translate('webhooks.url.bad_protocol'); - } else if (!isUrl(url)) { + } else if (!isWebUri(url)) { errors.url = translate('webhooks.url.bad_format'); } return errors; diff --git a/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts index 0dc5a089379..f5c9ef90db2 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts +++ b/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts @@ -23,8 +23,7 @@ import { getPathUrlAsString, getProjectUrl, getQualityGatesUrl, - getQualityGateUrl, - isUrl + getQualityGateUrl } from '../urls'; const SIMPLE_COMPONENT_KEY = 'sonarqube'; @@ -118,51 +117,3 @@ describe('#getQualityGate(s)Url', () => { }); }); }); - -describe('#isUrl', () => { - it('should be valid', () => { - expect(isUrl('https://localhost')).toBeTruthy(); - expect(isUrl('https://localhost/')).toBeTruthy(); - expect(isUrl('https://localhost:9000')).toBeTruthy(); - expect(isUrl('https://localhost:9000/')).toBeTruthy(); - expect(isUrl('https://foo:bar@localhost:9000')).toBeTruthy(); - expect(isUrl('https://foo@localhost')).toBeTruthy(); - expect(isUrl('http://foo.com/blah_blah')).toBeTruthy(); - expect(isUrl('http://foo.com/blah_blah/')).toBeTruthy(); - expect(isUrl('http://www.example.com/wpstyle/?p=364')).toBeTruthy(); - expect(isUrl('https://www.example.com/foo/?bar=baz&inga=42&quux')).toBeTruthy(); - expect(isUrl('http://userid@example.com')).toBeTruthy(); - expect(isUrl('http://userid@example.com/')).toBeTruthy(); - expect(isUrl('http://userid:password@example.com:8080')).toBeTruthy(); - expect(isUrl('http://userid:password@example.com:8080/')).toBeTruthy(); - expect(isUrl('http://userid@example.com:8080')).toBeTruthy(); - expect(isUrl('http://userid@example.com:8080/')).toBeTruthy(); - expect(isUrl('http://userid:password@example.com')).toBeTruthy(); - expect(isUrl('http://userid:password@example.com/')).toBeTruthy(); - expect(isUrl('http://142.42.1.1/')).toBeTruthy(); - expect(isUrl('http://142.42.1.1:8080/')).toBeTruthy(); - expect(isUrl('http://foo.com/blah_(wikipedia)#cite-1')).toBeTruthy(); - expect(isUrl('http://foo.com/blah_(wikipedia)_blah#cite-1')).toBeTruthy(); - expect(isUrl('http://foo.com/(something)?after=parens')).toBeTruthy(); - expect(isUrl('http://code.google.com/events/#&product=browser')).toBeTruthy(); - expect(isUrl('http://j.mp')).toBeTruthy(); - expect(isUrl('http://foo.bar/?q=Test%20URL-encoded%20stuff')).toBeTruthy(); - expect(isUrl('http://1337.net')).toBeTruthy(); - expect(isUrl('http://a.b-c.de')).toBeTruthy(); - expect(isUrl('http://223.255.255.254')).toBeTruthy(); - expect(isUrl('https://foo_bar.example.com/')).toBeTruthy(); - }); - - it('should not be valid', () => { - expect(isUrl('http://')).toBeFalsy(); - expect(isUrl('http://?')).toBeFalsy(); - expect(isUrl('http://??')).toBeFalsy(); - expect(isUrl('http://??/')).toBeFalsy(); - expect(isUrl('http://#')).toBeFalsy(); - expect(isUrl('http://##')).toBeFalsy(); - expect(isUrl('http://##/')).toBeFalsy(); - expect(isUrl('//')).toBeFalsy(); - expect(isUrl('///')).toBeFalsy(); - expect(isUrl('http:// shouldfail.com')).toBeFalsy(); - }); -}); diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts index de21ce40a5e..ecb965a112a 100644 --- a/server/sonar-web/src/main/js/helpers/urls.ts +++ b/server/sonar-web/src/main/js/helpers/urls.ts @@ -252,15 +252,3 @@ export function getHomePageUrl(homepage: HomePage) { // should never happen, but just in case... return '/projects'; } - -export function isUrl(url: string) { - try { - const elem = document.createElement('a'); - elem.href = url; - return !!(elem.host && elem.hostname && elem.protocol); - } catch (error) { - // both IE11 & Edge throw an exception when a url contains credentials - // is this case let's just pretend the url is fine and pass it to the server for the validation - return true; - } -} diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index 279fc9ba4e6..d5faaf6b277 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -894,6 +894,11 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/valid-url@1.0.2": + version "1.0.2" + resolved "http://registry.npmjs.org/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45" + integrity sha1-YPpDXOJL/VuhB7jSqAeWrq86j0U= + "@webassemblyjs/ast@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" @@ -10185,6 +10190,11 @@ uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== +valid-url@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= + validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" -- 2.39.5