From 142f5af3c9397b82b94a35de9893304d67ffa2c5 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Mon, 6 Jan 2025 10:00:27 +0100 Subject: [PATCH 1/5] fix: open redirect issues --- src/server/routes/contentful.js | 13 +++++++++++-- src/server/services/contentful.js | 1 + src/shared/components/TopcoderHeader/index.jsx | 11 ++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/server/routes/contentful.js b/src/server/routes/contentful.js index 1084af62ba..b61469b9c7 100644 --- a/src/server/routes/contentful.js +++ b/src/server/routes/contentful.js @@ -12,6 +12,7 @@ import { getService, getSpaceId, articleVote, + ALLOWED_DOMAINS, } from '../services/contentful'; const cors = require('cors'); @@ -37,7 +38,11 @@ routes.use( version, } = req.params; const spaceId = getSpaceId(spaceName); - res.redirect(`https://${ASSETS_DOMAIN}/spaces/${spaceId}/environments/${environment}/${id}/${version}/${name}`); + if (!ALLOWED_DOMAINS.includes(ASSETS_DOMAIN)) { + throw new Error('Invalid domain detected!'); + } + const url = new URL(`https://${ASSETS_DOMAIN}/spaces/${spaceId}/environments/${environment}/${id}/${version}/${name}`); + res.redirect(url.href); }, ); @@ -52,8 +57,12 @@ routes.use( spaceName, version, } = req.params; + if (!ALLOWED_DOMAINS.includes(IMAGES_DOMAIN)) { + throw new Error('Invalid domain detected!'); + } const spaceId = getSpaceId(spaceName); - res.redirect(`https://${IMAGES_DOMAIN}/spaces/${spaceId}/environments/${environment}/${id}/${version}/${name}`); + const url = new URL(`https://${IMAGES_DOMAIN}/spaces/${spaceId}/environments/${environment}/${id}/${version}/${name}`); + res.redirect(url.href); }, ); diff --git a/src/server/services/contentful.js b/src/server/services/contentful.js index 2525696f30..3a2cf3136c 100644 --- a/src/server/services/contentful.js +++ b/src/server/services/contentful.js @@ -21,6 +21,7 @@ const PREVIEW_URL = 'https://preview.contentful.com/spaces'; export const ASSETS_DOMAIN = 'assets.ctfassets.net'; export const IMAGES_DOMAIN = 'images.ctfassets.net'; +export const ALLOWED_DOMAINS = ['assets.ctfassets.net', 'images.ctfassets.net']; const MAX_FETCH_RETRIES = 5; /** diff --git a/src/shared/components/TopcoderHeader/index.jsx b/src/shared/components/TopcoderHeader/index.jsx index 5e8f5d73f4..85c9648e09 100644 --- a/src/shared/components/TopcoderHeader/index.jsx +++ b/src/shared/components/TopcoderHeader/index.jsx @@ -44,6 +44,7 @@ import './style.scss'; /* global window, document */ const BASE_URL = config.URL.BASE; +const VALID_BASE_URLS = ['https://www.topcoder-dev.com', 'https://www.topcoder.com']; const MENU = [{ title: 'Compete', @@ -426,9 +427,13 @@ export default class TopcoderHeader extends React.Component { ref={(input) => { this.searchInput = input; }} onKeyPress={(event) => { if (event.key === 'Enter') { - window.location = `${BASE_URL}/search/members?q=${ - encodeURIComponent(event.target.value) - }`; + if (!VALID_BASE_URLS.includes(BASE_URL)) { + return; + } + const query = event.target.value.trim(); + const url = new URL(`${BASE_URL}/search/members`); + url.searchParams.append('q', query); + window.location = url.href; } }} onBlur={closeSearch} From 3d1eb071dffa898cf35a3296292b03629cf0497a Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Mon, 6 Jan 2025 10:08:50 +0100 Subject: [PATCH 2/5] fix: lint --- src/shared/components/TopcoderHeader/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/TopcoderHeader/index.jsx b/src/shared/components/TopcoderHeader/index.jsx index 85c9648e09..c9403050d4 100644 --- a/src/shared/components/TopcoderHeader/index.jsx +++ b/src/shared/components/TopcoderHeader/index.jsx @@ -432,7 +432,7 @@ export default class TopcoderHeader extends React.Component { } const query = event.target.value.trim(); const url = new URL(`${BASE_URL}/search/members`); - url.searchParams.append('q', query); + url.searchParams.append('q', query); window.location = url.href; } }} From 03e711435e1c130367c7eb57a9390416173d1c35 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Mon, 6 Jan 2025 10:17:38 +0100 Subject: [PATCH 3/5] fix: lint --- src/shared/components/TopcoderHeader/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/TopcoderHeader/index.jsx b/src/shared/components/TopcoderHeader/index.jsx index c9403050d4..f56c16f024 100644 --- a/src/shared/components/TopcoderHeader/index.jsx +++ b/src/shared/components/TopcoderHeader/index.jsx @@ -432,7 +432,7 @@ export default class TopcoderHeader extends React.Component { } const query = event.target.value.trim(); const url = new URL(`${BASE_URL}/search/members`); - url.searchParams.append('q', query); + url.searchParams.append('q', query); window.location = url.href; } }} From 713e7307178e72216297b32ad151513db8e10b70 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Mon, 6 Jan 2025 10:18:00 +0100 Subject: [PATCH 4/5] fix: lint --- src/shared/components/TopcoderHeader/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/TopcoderHeader/index.jsx b/src/shared/components/TopcoderHeader/index.jsx index f56c16f024..761d17d4fa 100644 --- a/src/shared/components/TopcoderHeader/index.jsx +++ b/src/shared/components/TopcoderHeader/index.jsx @@ -432,7 +432,7 @@ export default class TopcoderHeader extends React.Component { } const query = event.target.value.trim(); const url = new URL(`${BASE_URL}/search/members`); - url.searchParams.append('q', query); + url.searchParams.append('q', query); window.location = url.href; } }} From b3fa4b4b42121816f9f4c32a45c31468f1116415 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 7 Jan 2025 16:57:52 +0100 Subject: [PATCH 5/5] fix: review comment --- src/server/services/contentful.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/services/contentful.js b/src/server/services/contentful.js index 3a2cf3136c..1091759c59 100644 --- a/src/server/services/contentful.js +++ b/src/server/services/contentful.js @@ -21,7 +21,7 @@ const PREVIEW_URL = 'https://preview.contentful.com/spaces'; export const ASSETS_DOMAIN = 'assets.ctfassets.net'; export const IMAGES_DOMAIN = 'images.ctfassets.net'; -export const ALLOWED_DOMAINS = ['assets.ctfassets.net', 'images.ctfassets.net']; +export const ALLOWED_DOMAINS = [ASSETS_DOMAIN, IMAGES_DOMAIN]; const MAX_FETCH_RETRIES = 5; /**