Skip to content

Commit c012ddd

Browse files
Shivanshu-07claude
andcommitted
fix(core): treat [::1] IPv6 literal as loopback origin (PER-8600)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 69036bf commit c012ddd

2 files changed

Lines changed: 8 additions & 2 deletions

File tree

packages/core/src/server.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import {
1717
export function isLoopbackOrigin(origin) {
1818
if (!origin || typeof origin !== 'string') return false;
1919
try {
20-
// URL.hostname strips the brackets from an IPv6 literal, so `[::1]`
21-
// normalises to `::1` here — no need to match the bracketed form.
20+
// URL.hostname KEEPS the brackets around an IPv6 literal, so `[::1]`
21+
// stays `[::1]` here and must be unwrapped before matching `::1`.
2222
let host = new URL(origin).hostname.toLowerCase();
23+
if (host.startsWith('[') && host.endsWith(']')) host = host.slice(1, -1);
2324
return host === 'localhost' || host === '127.0.0.1' ||
2425
host === '::1' || host.endsWith('.localhost');
2526
} catch {

packages/core/test/unit/server.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,11 @@ describe('Unit / Server', () => {
323323
expect(isLoopbackOrigin('http://app.localhost')).toBe(true);
324324
});
325325

326+
it('returns true for the bracketed IPv6 loopback literal', () => {
327+
expect(isLoopbackOrigin('http://[::1]:5338')).toBe(true);
328+
expect(isLoopbackOrigin('http://[::1]')).toBe(true);
329+
});
330+
326331
it('returns false for non-loopback and missing origins', () => {
327332
expect(isLoopbackOrigin('https://evil.example.com')).toBe(false);
328333
expect(isLoopbackOrigin('')).toBe(false);

0 commit comments

Comments
 (0)