From 0c7f9ccb33925cbce66969530a3ab4227a30f021 Mon Sep 17 00:00:00 2001 From: jonhickman Date: Thu, 25 Jun 2026 07:41:51 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20LinkedIn=20company=20and?= =?UTF-8?q?=20school=20URLs=20with=20underscores=20being=20rejected=20as?= =?UTF-8?q?=20invalid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LinkedIn company and school page URLs can contain underscores (e.g. /company/eyeshot_2), but the validator only allowed alphanumeric characters and hyphens, causing valid URLs to be incorrectly rejected. Personal /in/ profile URLs do not allow underscores per LinkedIn's own documentation, so the stricter rule is preserved for those. --- .../src/utils/social-urls/linkedin.ts | 13 ++++++++++--- .../test/unit/utils/linkedin-urls.test.ts | 10 ++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/admin-x-settings/src/utils/social-urls/linkedin.ts b/apps/admin-x-settings/src/utils/social-urls/linkedin.ts index 28388671c6e..5a27e54df07 100644 --- a/apps/admin-x-settings/src/utils/social-urls/linkedin.ts +++ b/apps/admin-x-settings/src/utils/social-urls/linkedin.ts @@ -9,8 +9,10 @@ const PATH_TYPES = ['in', 'pub', 'company', 'school'] as const; type PathType = typeof PATH_TYPES[number]; // validation info: https://www.linkedin.com/help/linkedin/answer/a542685/manage-your-public-profile-url?lang=en -// Alphanumeric, hyphen, 3–100 characters +// Personal profiles (/in/): alphanumeric and hyphen, 3–100 characters const USERNAME_REGEX = /^[a-zA-Z0-9-]{3,100}$/; +// Company and school pages allow underscores in addition to alphanumeric and hyphen +const COMPANY_USERNAME_REGEX = /^[a-zA-Z0-9_-]{3,100}$/; // For /pub/ profiles: username optionally followed by slash-separated segments (i.e. 12/34/567) const PUB_USERNAME_REGEX = /^[a-zA-Z0-9-]{3,100}(?:\/[a-zA-Z0-9-]*)*$/; @@ -67,8 +69,13 @@ const extractInputParts = (input: string) => { }; const isValidUsername = (pathType: PathType, username: string) => { - const pattern = pathType === 'pub' ? PUB_USERNAME_REGEX : USERNAME_REGEX; - return pattern.test(username); + if (pathType === 'pub') { + return PUB_USERNAME_REGEX.test(username); + } + if (pathType === 'company' || pathType === 'school') { + return COMPANY_USERNAME_REGEX.test(username); + } + return USERNAME_REGEX.test(username); }; const buildUrl = ({regional, pathType, username}: {regional?: string; pathType: PathType; username: string}) => { diff --git a/apps/admin-x-settings/test/unit/utils/linkedin-urls.test.ts b/apps/admin-x-settings/test/unit/utils/linkedin-urls.test.ts index 4f72d156cc6..9cd53e40133 100644 --- a/apps/admin-x-settings/test/unit/utils/linkedin-urls.test.ts +++ b/apps/admin-x-settings/test/unit/utils/linkedin-urls.test.ts @@ -43,6 +43,16 @@ describe('LinkedIn URLs', () => { assert.equal(validateLinkedInUrl('linkedin.com/pub/john-smith-abc123'), 'https://www.linkedin.com/pub/john-smith-abc123'); assert.equal(validateLinkedInUrl('linkedin.com/pub/johnsmith/12/34/567'), 'https://www.linkedin.com/pub/johnsmith/12/34/567'); }); + + it('should accept underscores in company and school URLs', () => { + assert.equal(validateLinkedInUrl('https://www.linkedin.com/company/eyeshot_2'), 'https://www.linkedin.com/company/eyeshot_2'); + assert.equal(validateLinkedInUrl('linkedin.com/company/some_company'), 'https://www.linkedin.com/company/some_company'); + assert.equal(validateLinkedInUrl('linkedin.com/school/some_school'), 'https://www.linkedin.com/school/some_school'); + }); + + it('should reject underscores in personal /in/ URLs', () => { + assert.throws(() => validateLinkedInUrl('linkedin.com/in/john_smith'), /Your Username is not a valid LinkedIn Username/); + }); }); describe('Handle to URL conversion', () => {