fix(core): in `isInternalUrl()`, URI protocol scheme detection should implement the spec more strictly (#11579)
Some checks failed
Argos CI / take-screenshots (push) Has been cancelled
Build Hash Router / Build Hash Router (push) Has been cancelled
Canary Release / Publish Canary (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Continuous Releases / Continuous Releases (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (20) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (20.0) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (22) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (24) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (25.1) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 Windows (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -st) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -st) (push) Has been cancelled
E2E Tests / E2E — npm (push) Has been cancelled
E2E Tests / E2E — pnpm (push) Has been cancelled

This commit is contained in:
Sébastien Lorber 2025-11-27 17:33:12 +01:00 committed by GitHub
parent c32ed21431
commit c6c0f636a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 10 deletions

View File

@ -28,7 +28,7 @@ describe('isInternalUrl', () => {
expect(isInternalUrl('https://foo.com')).toBeFalsy();
});
it('returns false for whatever protocol links', () => {
it('returns false for relative protocol links', () => {
expect(isInternalUrl('//foo.com')).toBeFalsy();
});
@ -43,4 +43,50 @@ describe('isInternalUrl', () => {
it('returns false for undefined links', () => {
expect(isInternalUrl(undefined)).toBeFalsy();
});
describe('custom scheme links', () => {
it('returns true for invalid protocol schemes', () => {
expect(isInternalUrl('+customScheme://')).toBeTruthy();
expect(isInternalUrl('+customScheme://whatever')).toBeTruthy();
expect(isInternalUrl('+customScheme:whatever')).toBeTruthy();
expect(isInternalUrl('.customScheme://')).toBeTruthy();
expect(isInternalUrl('.customScheme://whatever')).toBeTruthy();
expect(isInternalUrl('.customScheme:whatever')).toBeTruthy();
expect(isInternalUrl('-customScheme://')).toBeTruthy();
expect(isInternalUrl('-customScheme://whatever')).toBeTruthy();
expect(isInternalUrl('-customScheme:whatever')).toBeTruthy();
expect(isInternalUrl('custom_scheme://')).toBeTruthy();
expect(isInternalUrl('custom_scheme://whatever')).toBeTruthy();
expect(isInternalUrl('custom_scheme:whatever')).toBeTruthy();
expect(isInternalUrl('custom scheme://')).toBeTruthy();
expect(isInternalUrl('custom scheme://whatever')).toBeTruthy();
expect(isInternalUrl('custom scheme:whatever')).toBeTruthy();
expect(isInternalUrl('custom$scheme://')).toBeTruthy();
expect(isInternalUrl('custom$scheme://whatever')).toBeTruthy();
expect(isInternalUrl('custom$scheme:whatever')).toBeTruthy();
});
it('returns false valid protocol schemes', () => {
expect(isInternalUrl('customScheme://')).toBeFalsy();
expect(isInternalUrl('customScheme://whatever')).toBeFalsy();
expect(isInternalUrl('customScheme:whatever')).toBeFalsy();
expect(isInternalUrl('custom-scheme://')).toBeFalsy();
expect(isInternalUrl('custom-scheme://whatever')).toBeFalsy();
expect(isInternalUrl('custom-scheme:whatever')).toBeFalsy();
expect(isInternalUrl('custom.scheme://')).toBeFalsy();
expect(isInternalUrl('custom.scheme://whatever')).toBeFalsy();
expect(isInternalUrl('custom.scheme:whatever')).toBeFalsy();
expect(isInternalUrl('custom-sch.eme+-.://')).toBeFalsy();
expect(isInternalUrl('custom-sch.eme+-.://whatever')).toBeFalsy();
expect(isInternalUrl('custom-sch.eme+-.:whatever')).toBeFalsy();
});
});
});

View File

@ -5,8 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/
// Poor man's protocol detection
// Spec: https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
// In particular: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
export function hasProtocol(url: string): boolean {
return /^(?:\w*:|\/\/)/.test(url);
return /^(?:[A-Za-z][A-Za-z\d+.-]*:|\/\/)/.test(url);
}
export default function isInternalUrl(url?: string): boolean {

View File

@ -360,15 +360,10 @@ This is a test page to see if Docusaurus Markdown features are working properly
See [#3337](https://github.com/facebook/docusaurus/issues/3337)
- [/someFile.pdf](/someFile.pdf)
- [/someFile.xyz](/someFile.xyz)
- [/image with space.png](</image with spaces.png>)
- [@site/\_dogfooding/\_asset-tests/someFile.pdf](@site/_dogfooding/_asset-tests/someFile.pdf)
- [@site/\_dogfooding/\_asset-tests/someFile.xyz](@site/_dogfooding/_asset-tests/someFile.xyz)
- [@site/\_dogfooding/\_asset-tests/image with space.png](<@site/_dogfooding/_asset-tests/image with spaces.png>)
### Linking to non-SPA page hosted within website
@ -376,13 +371,16 @@ See [#3337](https://github.com/facebook/docusaurus/issues/3337)
See [#3309](https://github.com/facebook/docusaurus/issues/3309)
- [pathname:///dogfooding/javadoc](pathname:///dogfooding/javadoc)
- [pathname:///dogfooding/javadoc/index.html](pathname:///dogfooding/javadoc/index.html)
- [pathname://../dogfooding/javadoc](pathname://../dogfooding/javadoc)
- [pathname://../dogfooding/javadoc/index.html](pathname://../dogfooding/javadoc/index.html)
### Linking to non-SPA paths with custom schemes
- [customScheme://whatever](customScheme://whatever)
- [custom-scheme://whatever](custom-scheme://whatever)
- [custom-sch.eme+-.://whatever](custom-sch.eme+-.://whatever)
### Linking to non-SPA page with Link component
See [#9758](https://github.com/facebook/docusaurus/issues/9758), these external urls should not be reported by the broken links checker: