From 9113442af352e5a35f917139f7640408043e124c Mon Sep 17 00:00:00 2001 From: kevin9327 <5299031+kevin9327@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:41:17 +0900 Subject: [PATCH 1/2] fix(utils): recognize SCP-style SSH remotes without a user in hasSSHProtocol --- .../src/__tests__/urlUtils.test.ts | 11 +++++++++++ packages/docusaurus-utils/src/urlUtils.ts | 14 +++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts index 64c22ced0528..3d0d42878a09 100644 --- a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts @@ -625,6 +625,17 @@ describe('hasSSHProtocol', () => { expect(hasSSHProtocol(url)).toBe(true); }); + it('recognizes implied SSH protocol without a user', () => { + expect(hasSSHProtocol('github.com:facebook/docusaurus.git')).toBe(true); + // An SSH config host alias, as used for several GitHub accounts + expect(hasSSHProtocol('github-work:facebook/docusaurus.git')).toBe(true); + }); + + it('does not recognize a Windows drive path', () => { + expect(hasSSHProtocol('C:/projects/docusaurus.git')).toBe(false); + expect(hasSSHProtocol('C:\\projects\\docusaurus.git')).toBe(false); + }); + it('does not recognize HTTPS with credentials', () => { const url = 'https://user:pass@github.com/facebook/docusaurus.git'; expect(hasSSHProtocol(url)).toBe(false); diff --git a/packages/docusaurus-utils/src/urlUtils.ts b/packages/docusaurus-utils/src/urlUtils.ts index 0b661ce3d6cc..88335e5aabd8 100644 --- a/packages/docusaurus-utils/src/urlUtils.ts +++ b/packages/docusaurus-utils/src/urlUtils.ts @@ -314,14 +314,22 @@ export function buildHttpsUrl( /** * Whether the current URL is an SSH protocol. In addition to looking for * `ssh:`, it will also allow protocol-less URLs like - * `git@github.com:facebook/docusaurus.git`. + * `git@github.com:facebook/docusaurus.git`, with or without the user part + * (`github.com:facebook/docusaurus.git`, or an SSH config host alias). */ export function hasSSHProtocol(sourceRepoUrl: string): boolean { const url = URL.parse(sourceRepoUrl); - if (url === null) { + // Without "://", git reads `[user@]host:path` as an SCP-style address. When + // the user part is left out, URL.parse() takes the host for a URL scheme + // (`github.com:` is a valid one), so it is checked here as well. + if (url === null || !sourceRepoUrl.includes('://')) { // Recognizes SCP-style addresses, implying SSH // Example: git@github.com:facebook/docusaurus.git - return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl); + // A Windows drive path like C:/repo is not one. + return ( + /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl) && + !/^[a-z]:[\\/]/i.test(sourceRepoUrl) + ); } return url.protocol === 'ssh:'; } From e3e2d2816fe4286e372f62b20a317900505d2faa Mon Sep 17 00:00:00 2001 From: kevin <5299031+kevin9327@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:26:59 +0900 Subject: [PATCH 2/2] fix(utils): accept ~ paths and reject C:repo drive paths in hasSSHProtocol --- packages/docusaurus-utils/src/__tests__/urlUtils.test.ts | 6 ++++++ packages/docusaurus-utils/src/urlUtils.ts | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts index 3d0d42878a09..9105e474d84e 100644 --- a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts @@ -631,9 +631,15 @@ describe('hasSSHProtocol', () => { expect(hasSSHProtocol('github-work:facebook/docusaurus.git')).toBe(true); }); + it('recognizes implied SSH protocol with a path relative to a home directory', () => { + expect(hasSSHProtocol('git@example.com:~git/docusaurus.git')).toBe(true); + expect(hasSSHProtocol('example.com:~/docusaurus.git')).toBe(true); + }); + it('does not recognize a Windows drive path', () => { expect(hasSSHProtocol('C:/projects/docusaurus.git')).toBe(false); expect(hasSSHProtocol('C:\\projects\\docusaurus.git')).toBe(false); + expect(hasSSHProtocol('C:projects/docusaurus.git')).toBe(false); }); it('does not recognize HTTPS with credentials', () => { diff --git a/packages/docusaurus-utils/src/urlUtils.ts b/packages/docusaurus-utils/src/urlUtils.ts index 88335e5aabd8..35f290130c9a 100644 --- a/packages/docusaurus-utils/src/urlUtils.ts +++ b/packages/docusaurus-utils/src/urlUtils.ts @@ -325,10 +325,12 @@ export function hasSSHProtocol(sourceRepoUrl: string): boolean { if (url === null || !sourceRepoUrl.includes('://')) { // Recognizes SCP-style addresses, implying SSH // Example: git@github.com:facebook/docusaurus.git - // A Windows drive path like C:/repo is not one. + // Example: github.com:~user/docusaurus.git + // A Windows drive path like C:/repo or C:repo is not one: like git on + // Windows, a single-letter host is read as a drive letter. return ( - /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl) && - !/^[a-z]:[\\/]/i.test(sourceRepoUrl) + /^(?:[\w-]+@)?[\w.-]+:[\w./~-]+/.test(sourceRepoUrl) && + !/^[a-z]:/i.test(sourceRepoUrl) ); } return url.protocol === 'ssh:';