diff --git a/.changeset/shiny-grapes-tan.md b/.changeset/shiny-grapes-tan.md new file mode 100644 index 0000000000..b7361bb970 --- /dev/null +++ b/.changeset/shiny-grapes-tan.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Validate that integration config contains a valid host diff --git a/packages/integration/src/azure/config.ts b/packages/integration/src/azure/config.ts index 27b73ed707..5550e2faf5 100644 --- a/packages/integration/src/azure/config.ts +++ b/packages/integration/src/azure/config.ts @@ -15,6 +15,7 @@ */ import { Config } from '@backstage/config'; +import { isValidHost } from '../helpers'; const AZURE_HOST = 'dev.azure.com'; @@ -47,6 +48,13 @@ export function readAzureIntegrationConfig( ): AzureIntegrationConfig { const host = config.getOptionalString('host') ?? AZURE_HOST; const token = config.getOptionalString('token'); + + if (!isValidHost(host)) { + throw new Error( + `Invalid Azure integration config, '${host}' is not a valid host`, + ); + } + return { host, token }; } diff --git a/packages/integration/src/bitbucket/config.ts b/packages/integration/src/bitbucket/config.ts index 1997a5597d..b7ad8684a9 100644 --- a/packages/integration/src/bitbucket/config.ts +++ b/packages/integration/src/bitbucket/config.ts @@ -15,6 +15,7 @@ */ import { Config } from '@backstage/config'; +import { isValidHost } from '../helpers'; const BITBUCKET_HOST = 'bitbucket.org'; const BITBUCKET_API_BASE_URL = 'https://api.bitbucket.org/2.0'; @@ -75,6 +76,12 @@ export function readBitbucketIntegrationConfig( const username = config.getOptionalString('username'); const appPassword = config.getOptionalString('appPassword'); + if (!isValidHost(host)) { + throw new Error( + `Invalid Bitbucket integration config, '${host}' is not a valid host`, + ); + } + if (apiBaseUrl) { apiBaseUrl = apiBaseUrl.replace(/\/+$/, ''); } else if (host === BITBUCKET_HOST) { diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts index f646acc53b..22e8ad57d8 100644 --- a/packages/integration/src/github/config.ts +++ b/packages/integration/src/github/config.ts @@ -15,6 +15,7 @@ */ import { Config } from '@backstage/config'; +import { isValidHost } from '../helpers'; const GITHUB_HOST = 'github.com'; const GITHUB_API_BASE_URL = 'https://api.github.com'; @@ -72,6 +73,12 @@ export function readGitHubIntegrationConfig( let rawBaseUrl = config.getOptionalString('rawBaseUrl'); const token = config.getOptionalString('token'); + if (!isValidHost(host)) { + throw new Error( + `Invalid GitHub integration config, '${host}' is not a valid host`, + ); + } + if (apiBaseUrl) { apiBaseUrl = apiBaseUrl.replace(/\/+$/, ''); } else if (host === GITHUB_HOST) { diff --git a/packages/integration/src/gitlab/config.ts b/packages/integration/src/gitlab/config.ts index 97c948c999..a38312d01a 100644 --- a/packages/integration/src/gitlab/config.ts +++ b/packages/integration/src/gitlab/config.ts @@ -15,6 +15,7 @@ */ import { Config } from '@backstage/config'; +import { isValidHost } from '../helpers'; const GITLAB_HOST = 'gitlab.com'; @@ -45,6 +46,13 @@ export function readGitLabIntegrationConfig( ): GitLabIntegrationConfig { const host = config.getOptionalString('host') ?? GITLAB_HOST; const token = config.getOptionalString('token'); + + if (!isValidHost(host)) { + throw new Error( + `Invalid GitLab integration config, '${host}' is not a valid host`, + ); + } + return { host, token }; } diff --git a/packages/integration/src/helpers.test.ts b/packages/integration/src/helpers.test.ts new file mode 100644 index 0000000000..9f4c531ba6 --- /dev/null +++ b/packages/integration/src/helpers.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { isValidHost } from './helpers'; + +describe('isValidHost', () => { + it.each([ + ['example.com', true], + ['foo', true], + ['foo:1', true], + ['foo:10000', true], + ['foo.bar', true], + ['foo.bar.baz', true], + ['1.2.3.4', true], + ['[::]', true], + ['[::1]', true], + ['[1:2:3:4:5:6:7:8]', true], + ['1.2.3.4.5.6.7.8', true], + ['https://example.com', false], + ['foo:100000', false], + ['FOO', false], + ['Foo', false], + ['foo/bar', false], + ['//foo', false], + ['foo:bar', false], + ['foo?', false], + ['foo?bar', false], + ['foo#', false], + ['foo#bar', false], + ['::', false], + ['::1', false], + ['1:2:3:4:5:6:7:8', false], + ['???????', false], + ['€&()=)&(', false], + ['höst', false], + ['πœπœfiπœ', false], + ])('Should check whether %s is a valid host', (str, expected) => { + expect(isValidHost(str)).toBe(expected); + }); +}); diff --git a/packages/integration/src/helpers.ts b/packages/integration/src/helpers.ts new file mode 100644 index 0000000000..02393f99e1 --- /dev/null +++ b/packages/integration/src/helpers.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Checks whether the given url is a valid host */ +export function isValidHost(url: string): boolean { + const check = new URL('http://example.com'); + check.host = url; + return check.host === url; +}