From 28a02ca94dd48d78b8e73ecf7f5f037ad068504a Mon Sep 17 00:00:00 2001 From: monam2 Date: Mon, 1 Sep 2025 02:28:43 +0900 Subject: [PATCH 1/8] feat(utils): support multiple certificates in resolveServerUrls --- .../vite/src/node/__tests__/utils.spec.ts | 172 ++++++++++++++++++ packages/vite/src/node/utils.ts | 28 ++- 2 files changed, 192 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index 519d179143b4ff..603ec4c98aa92d 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -22,6 +22,7 @@ import { posToNumber, processSrcSetSync, resolveHostname, + resolveServerUrls, } from '../utils' import { isWindows } from '../../shared/utils' import type { CommonServerOptions, ResolvedServerUrls } from '..' @@ -908,3 +909,174 @@ describe('getServerUrlByHost', () => { }) } }) + +describe('resolveServerUrls', () => { + const createMockServer = ( + family: 'IPv4' | 'IPv6' = 'IPv4', + address: string = '127.0.0.1', + ) => + ({ + address: () => ({ port: 3000, address, family }), + }) as any + + const createTestConfig = () => ({ + options: { https: true } as any, + hostname: { host: '127.0.0.1', name: 'localhost' } as any, + config: { rawBase: '/' } as any, + }) + + // Test certificate containing domains: localhost, foo.localhost, *.vite.localhost + const createWorkingCert = `-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV +BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx +EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl +c3QwHhcNMjUwMTMwMDQxNTI1WhcNMjUwMzAxMDQxNTI1WjBpMRQwEgYDVQQDEwtl +eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD +VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNPlCqTmUZ7/F7GyFWDopqZ6 +w19Y7/98B10JEeFGTAQIj/RP2UgZNcTABQDUvtkF7y+bOeoVJW7Zz8ozQYhRaDp8 +CN2gXMcYeTUku/pKLXyCzHHVrOPAXTeU7sMRgLvPCrrJtx5OjvndW+O/PhohPRi3 +iEpPvpM8gi7MVRGhnWVSx0/Ynx5c0+/vqyBTzrM2OX7Ufg8Nv7LaTXpCAnmIQp+f +Sqq7HZ7t6Y7laS4RApityvlnFHZ4f2cEibAKv/vXLED7bgAlGb8R1viPRdMtAPuI +MYvHBgGFjyX1fmq6Mz3aqlAscJILtbQlwty1oYyaENE0lq8+nZXQ+t6I+CIVLQID +AQABo4GZMIGWMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB +BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBUBgNVHREETTBLgglsb2NhbGhvc3SC +DWZvby5sb2NhbGhvc3SCECoudml0ZS5sb2NhbGhvc3SCBVs6OjFdhwR/AAABhxD+ +gAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBi302qLCgxWsUalgc2 +olFxVKob1xCciS8yUVX6HX0vza0WJ7oGW6qZsBbQtfgDwB/dHv7rwsfpjRWvFhmq +gEUrewa1h0TIC+PPTYYz4M0LOwcLIWZLZr4am1eI7YP9NDgRdhfAfM4hw20vjf2a +kYLKyRTC5+3/ly5opMq+CGLQ8/gnFxhP3ho8JYrRnqLeh3KCTGen3kmbAhD4IOJ9 +lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh +WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkwbvxBar +X2JN +-----END CERTIFICATE----- + `.trim() as any + + test('should handle no certificate', () => { + const mockServer = createMockServer() + const { options, hostname, config } = createTestConfig() + const httpsOptions = {} as never + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + }) + + test('should handle IPv4 single certificate', () => { + const mockServer = createMockServer() + const { options, hostname, config } = createTestConfig() + const httpsOptions = { cert: [createWorkingCert] } + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + expect(result.local).toContain('https://foo.localhost:3000/') + expect(result.local).toContain('https://vite.vite.localhost:3000/') + }) + + test('should handle IPv4 multiple certificates', () => { + const mockServer = createMockServer() + const { options, hostname, config } = createTestConfig() + const httpsOptions = { cert: [createWorkingCert] } + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + expect(result.local).toContain('https://foo.localhost:3000/') + expect(result.local).toContain('https://vite.vite.localhost:3000/') + }) + + test('should handle IPv6 single certificate', () => { + const mockServer = createMockServer('IPv6', '::1') + const { options, hostname, config } = createTestConfig() + const httpsOptions = { + cert: [createWorkingCert], + } + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + expect(result.local).toContain('https://foo.localhost:3000/') + expect(result.local).toContain('https://vite.vite.localhost:3000/') + }) + + test('should handle IPv6 multiple certificates', () => { + const mockServer = createMockServer('IPv6', '::1') + const { options, hostname, config } = createTestConfig() + const httpsOptions = { + cert: [createWorkingCert], + } + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + expect(result.local).toContain('https://foo.localhost:3000/') + expect(result.local).toContain('https://vite.vite.localhost:3000/') + }) + + test('should handle mixed IPv4 and IPv6', () => { + const mockServer = createMockServer('IPv4', '0.0.0.0') + const { options, hostname, config } = createTestConfig() + const httpsOptions = { + cert: [createWorkingCert], + } as any + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + expect(result.local).toContain('https://foo.localhost:3000/') + expect(result.local).toContain('https://vite.vite.localhost:3000/') + }) + + test('should handle invalid certificate', () => { + const mockServer = createMockServer() + const { options, hostname, config } = createTestConfig() + const httpsOptions = { cert: ['invalid-cert'] } + + const result = resolveServerUrls( + mockServer, + options, + hostname, + httpsOptions, + config, + ) + + expect(result.local).toContain('https://localhost:3000/') + }) +}) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 937c4cc8c2300d..3134600af0c2d5 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -993,6 +993,10 @@ export async function resolveHostname( return { host, name } } +export function bufferify(buffer: string | Buffer): Buffer { + return Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer) +} + export function resolveServerUrls( server: Server, options: CommonServerOptions, @@ -1045,19 +1049,27 @@ export function resolveServerUrls( }) } - const cert = - httpsOptions?.cert && !Array.isArray(httpsOptions.cert) - ? new crypto.X509Certificate(httpsOptions.cert) - : undefined - const hostnameFromCert = cert?.subjectAltName - ? extractHostnamesFromSubjectAltName(cert.subjectAltName) - : [] + const certs = httpsOptions?.cert ? arraify(httpsOptions.cert) : [] + const hostnameFromCert = certs + .map((cert) => { + try { + const buf = bufferify(cert) + return new crypto.X509Certificate(buf) + } catch { + return null + } + }) + .flatMap((cert) => + cert && cert.subjectAltName + ? extractHostnamesFromSubjectAltName(cert.subjectAltName) + : [], + ) if (hostnameFromCert.length > 0) { const existings = new Set([...local, ...network]) local.push( ...hostnameFromCert - .map((hostname) => `https://${hostname}:${port}${base}`) + .map((hostname) => `${protocol}://${hostname}:${port}${base}`) .filter((url) => !existings.has(url)), ) } From 6611611fcfad7f9ce602adaa9c09549e8272fb94 Mon Sep 17 00:00:00 2001 From: monam2 Date: Tue, 2 Sep 2025 00:17:58 +0900 Subject: [PATCH 2/8] test: update multiple certs handling tests for IPv4 and IPv6 --- .../vite/src/node/__tests__/utils.spec.ts | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index 603ec4c98aa92d..ff9c6de9eef8b6 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -989,7 +989,7 @@ X2JN test('should handle IPv4 multiple certificates', () => { const mockServer = createMockServer() const { options, hostname, config } = createTestConfig() - const httpsOptions = { cert: [createWorkingCert] } + const httpsOptions = { cert: [createWorkingCert, createWorkingCert] } const result = resolveServerUrls( mockServer, @@ -1007,9 +1007,7 @@ X2JN test('should handle IPv6 single certificate', () => { const mockServer = createMockServer('IPv6', '::1') const { options, hostname, config } = createTestConfig() - const httpsOptions = { - cert: [createWorkingCert], - } + const httpsOptions = { cert: [createWorkingCert] } const result = resolveServerUrls( mockServer, @@ -1027,29 +1025,7 @@ X2JN test('should handle IPv6 multiple certificates', () => { const mockServer = createMockServer('IPv6', '::1') const { options, hostname, config } = createTestConfig() - const httpsOptions = { - cert: [createWorkingCert], - } - - const result = resolveServerUrls( - mockServer, - options, - hostname, - httpsOptions, - config, - ) - - expect(result.local).toContain('https://localhost:3000/') - expect(result.local).toContain('https://foo.localhost:3000/') - expect(result.local).toContain('https://vite.vite.localhost:3000/') - }) - - test('should handle mixed IPv4 and IPv6', () => { - const mockServer = createMockServer('IPv4', '0.0.0.0') - const { options, hostname, config } = createTestConfig() - const httpsOptions = { - cert: [createWorkingCert], - } as any + const httpsOptions = { cert: [createWorkingCert, createWorkingCert] } const result = resolveServerUrls( mockServer, From 9effbca53272b72bbc2a677017358f71b9a3d8fa Mon Sep 17 00:00:00 2001 From: monam2 Date: Sun, 14 Sep 2025 22:10:12 +0900 Subject: [PATCH 3/8] feat(utils): extract hostnames from certs via helper and use in resolveServerUrls --- packages/vite/src/node/utils.ts | 43 ++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 3134600af0c2d5..55261addc3bf62 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -997,6 +997,29 @@ export function bufferify(buffer: string | Buffer): Buffer { return Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer) } +export function extractHostnamesFromCerts( + certs: HttpsServerOptions['cert'] | undefined, +): string[] { + const certList = certs ? arraify(certs) : [] + if (certList.length === 0) return [] + + const hostnames = certList + .map((cert) => { + try { + return new crypto.X509Certificate(cert) + } catch { + return null + } + }) + .flatMap((cert) => + cert?.subjectAltName + ? extractHostnamesFromSubjectAltName(cert.subjectAltName) + : [], + ) + + return hostnames +} + export function resolveServerUrls( server: Server, options: CommonServerOptions, @@ -1049,26 +1072,12 @@ export function resolveServerUrls( }) } - const certs = httpsOptions?.cert ? arraify(httpsOptions.cert) : [] - const hostnameFromCert = certs - .map((cert) => { - try { - const buf = bufferify(cert) - return new crypto.X509Certificate(buf) - } catch { - return null - } - }) - .flatMap((cert) => - cert && cert.subjectAltName - ? extractHostnamesFromSubjectAltName(cert.subjectAltName) - : [], - ) + const hostnames = extractHostnamesFromCerts(httpsOptions?.cert) - if (hostnameFromCert.length > 0) { + if (hostnames.length > 0) { const existings = new Set([...local, ...network]) local.push( - ...hostnameFromCert + ...hostnames .map((hostname) => `${protocol}://${hostname}:${port}${base}`) .filter((url) => !existings.has(url)), ) From 2353f2eba9b8e4248179acf89f2f937a486c2d29 Mon Sep 17 00:00:00 2001 From: monam2 Date: Thu, 18 Sep 2025 23:52:30 +0900 Subject: [PATCH 4/8] test(utils): cover extractHostnamesFromCerts with single and multiple certs --- .../vite/src/node/__tests__/utils.spec.ts | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index ff9c6de9eef8b6..c4505819480a57 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -7,6 +7,7 @@ import { asyncFlatten, bareImportRE, combineSourcemaps, + extractHostnamesFromCerts, extractHostnamesFromSubjectAltName, flattenId, generateCodeFrame, @@ -910,6 +911,60 @@ describe('getServerUrlByHost', () => { } }) +describe('extractHostnamesFromCerts', () => { + // Test certificate containing domains: 'localhost', 'foo.localhost', 'vite.vite.localhost', + const createWorkingCert = `-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV +BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx +EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl +c3QwHhcNMjUwMTMwMDQxNTI1WhcNMjUwMzAxMDQxNTI1WjBpMRQwEgYDVQQDEwtl +eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD +VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNPlCqTmUZ7/F7GyFWDopqZ6 +w19Y7/98B10JEeFGTAQIj/RP2UgZNcTABQDUvtkF7y+bOeoVJW7Zz8ozQYhRaDp8 +CN2gXMcYeTUku/pKLXyCzHHVrOPAXTeU7sMRgLvPCrrJtx5OjvndW+O/PhohPRi3 +iEpPvpM8gi7MVRGhnWVSx0/Ynx5c0+/vqyBTzrM2OX7Ufg8Nv7LaTXpCAnmIQp+f +Sqq7HZ7t6Y7laS4RApityvlnFHZ4f2cEibAKv/vXLED7bgAlGb8R1viPRdMtAPuI +MYvHBgGFjyX1fmq6Mz3aqlAscJILtbQlwty1oYyaENE0lq8+nZXQ+t6I+CIVLQID +AQABo4GZMIGWMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB +BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBUBgNVHREETTBLgglsb2NhbGhvc3SC +DWZvby5sb2NhbGhvc3SCECoudml0ZS5sb2NhbGhvc3SCBVs6OjFdhwR/AAABhxD+ +gAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBi302qLCgxWsUalgc2 +olFxVKob1xCciS8yUVX6HX0vza0WJ7oGW6qZsBbQtfgDwB/dHv7rwsfpjRWvFhmq +gEUrewa1h0TIC+PPTYYz4M0LOwcLIWZLZr4am1eI7YP9NDgRdhfAfM4hw20vjf2a +kYLKyRTC5+3/ly5opMq+CGLQ8/gnFxhP3ho8JYrRnqLeh3KCTGen3kmbAhD4IOJ9 +lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh +WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkwbvxBar +X2JN +-----END CERTIFICATE----- + `.trim() as any + + test('should extract hostnames from certificate', () => { + const httpsOptions = { cert: [createWorkingCert] } as any + const result = extractHostnamesFromCerts(httpsOptions.cert) + + expect(result).toStrictEqual([ + 'localhost', + 'foo.localhost', + 'vite.vite.localhost', + ]) + }) + + test('should extract hostnames from multiple certificates', () => { + const httpsOptions = { cert: [createWorkingCert, createWorkingCert] } as any + const result = extractHostnamesFromCerts(httpsOptions.cert) + + expect(result).toStrictEqual([ + 'localhost', + 'foo.localhost', + 'vite.vite.localhost', + 'localhost', + 'foo.localhost', + 'vite.vite.localhost', + ]) + }) +}) + describe('resolveServerUrls', () => { const createMockServer = ( family: 'IPv4' | 'IPv6' = 'IPv4', @@ -925,7 +980,7 @@ describe('resolveServerUrls', () => { config: { rawBase: '/' } as any, }) - // Test certificate containing domains: localhost, foo.localhost, *.vite.localhost + // Test certificate containing domains: 'localhost', 'foo.localhost', 'vite.vite.localhost', const createWorkingCert = `-----BEGIN CERTIFICATE----- MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx From 758b48143e36a58cb8505a080cbc1b06223fce32 Mon Sep 17 00:00:00 2001 From: monam2 Date: Sat, 20 Sep 2025 21:57:46 +0900 Subject: [PATCH 5/8] fix(utils): dedupe hostnames returned by extractHostnamesFromCerts --- packages/vite/src/node/__tests__/utils.spec.ts | 3 --- packages/vite/src/node/utils.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index c4505819480a57..ee93dfb84fc5fb 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -958,9 +958,6 @@ X2JN 'localhost', 'foo.localhost', 'vite.vite.localhost', - 'localhost', - 'foo.localhost', - 'vite.vite.localhost', ]) }) }) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 55261addc3bf62..f9cd8c9139d10f 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -1017,7 +1017,7 @@ export function extractHostnamesFromCerts( : [], ) - return hostnames + return unique(hostnames) } export function resolveServerUrls( From 82693307ed5bab4d4b8f9337a7671b8470b6400f Mon Sep 17 00:00:00 2001 From: monam2 Date: Sun, 21 Sep 2025 00:22:05 +0900 Subject: [PATCH 6/8] test(utils): hoist WORKING_TEST_CERT and update cert-related tests --- .../vite/src/node/__tests__/utils.spec.ts | 127 ++++++------------ 1 file changed, 40 insertions(+), 87 deletions(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index ee93dfb84fc5fb..88b40c2eb6f5a2 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -28,6 +28,39 @@ import { import { isWindows } from '../../shared/utils' import type { CommonServerOptions, ResolvedServerUrls } from '..' +// Test certificate for SAN parsing (localhost, foo.localhost, *.vite.localhost) +// Generate once: +// openssl req -x509 -nodes -newkey rsa:2048 -days 365 -subj "/CN=example.org" \ +// -addext "subjectAltName=DNS:localhost,DNS:foo.localhost,DNS:*.vite.localhost" \ +// -keyout /tmp/test.key -out /tmp/test.crt +// Paste /tmp/test.crt below. +const WORKING_TEST_CERT = ` +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV +BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx +EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl +c3QwHhcNMjUwMTMwMDQxNTI1WhcNMjUwMzAxMDQxNTI1WjBpMRQwEgYDVQQDEwtl +eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD +VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNPlCqTmUZ7/F7GyFWDopqZ6 +w19Y7/98B10JEeFGTAQIj/RP2UgZNcTABQDUvtkF7y+bOeoVJW7Zz8ozQYhRaDp8 +CN2gXMcYeTUku/pKLXyCzHHVrOPAXTeU7sMRgLvPCrrJtx5OjvndW+O/PhohPRi3 +iEpPvpM8gi7MVRGhnWVSx0/Ynx5c0+/vqyBTzrM2OX7Ufg8Nv7LaTXpCAnmIQp+f +Sqq7HZ7t6Y7laS4RApityvlnFHZ4f2cEibAKv/vXLED7bgAlGb8R1viPRdMtAPuI +MYvHBgGFjyX1fmq6Mz3aqlAscJILtbQlwty1oYyaENE0lq8+nZXQ+t6I+CIVLQID +AQABo4GZMIGWMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB +BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBUBgNVHREETTBLgglsb2NhbGhvc3SC +DWZvby5sb2NhbGhvc3SCECoudml0ZS5sb2NhbGhvc3SCBVs6OjFdhwR/AAABhxD+ +gAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBi302qLCgxWsUalgc2 +olFxVKob1xCciS8yUVX6HX0vza0WJ7oGW6qZsBbQtfgDwB/dHv7rwsfpjRWvFhmq +gEUrewa1h0TIC+PPTYYz4M0LOwcLIWZLZr4am1eI7YP9NDgRdhfAfM4hw20vjf2a +kYLKyRTC5+3/ly5opMq+CGLQ8/gnFxhP3ho8JYrRnqLeh3KCTGen3kmbAhD4IOJ9 +lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh +WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkqbvxBar +X2JN +-----END CERTIFICATE----- +`.trim() as any + describe('bareImportRE', () => { test('should work with normal package name', () => { expect(bareImportRE.test('vite')).toBe(true) @@ -220,33 +253,7 @@ describe('extractHostnamesFromSubjectAltName', () => { } test('should extract names from actual certificate', () => { - const certText = ` ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV -BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx -EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl -c3QwHhcNMjUwMTMwMDQxNTI1WhcNMjUwMzAxMDQxNTI1WjBpMRQwEgYDVQQDEwtl -eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD -VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNPlCqTmUZ7/F7GyFWDopqZ6 -w19Y7/98B10JEeFGTAQIj/RP2UgZNcTABQDUvtkF7y+bOeoVJW7Zz8ozQYhRaDp8 -CN2gXMcYeTUku/pKLXyCzHHVrOPAXTeU7sMRgLvPCrrJtx5OjvndW+O/PhohPRi3 -iEpPvpM8gi7MVRGhnWVSx0/Ynx5c0+/vqyBTzrM2OX7Ufg8Nv7LaTXpCAnmIQp+f -Sqq7HZ7t6Y7laS4RApityvlnFHZ4f2cEibAKv/vXLED7bgAlGb8R1viPRdMtAPuI -MYvHBgGFjyX1fmq6Mz3aqlAscJILtbQlwty1oYyaENE0lq8+nZXQ+t6I+CIVLQID -AQABo4GZMIGWMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB -BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBUBgNVHREETTBLgglsb2NhbGhvc3SC -DWZvby5sb2NhbGhvc3SCECoudml0ZS5sb2NhbGhvc3SCBVs6OjFdhwR/AAABhxD+ -gAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBi302qLCgxWsUalgc2 -olFxVKob1xCciS8yUVX6HX0vza0WJ7oGW6qZsBbQtfgDwB/dHv7rwsfpjRWvFhmq -gEUrewa1h0TIC+PPTYYz4M0LOwcLIWZLZr4am1eI7YP9NDgRdhfAfM4hw20vjf2a -kYLKyRTC5+3/ly5opMq+CGLQ8/gnFxhP3ho8JYrRnqLeh3KCTGen3kmbAhD4IOJ9 -lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh -WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkqbvxBar -X2JN ------END CERTIFICATE----- - `.trim() - const cert = new crypto.X509Certificate(certText) + const cert = new crypto.X509Certificate(WORKING_TEST_CERT) expect( extractHostnamesFromSubjectAltName(cert.subjectAltName ?? ''), ).toStrictEqual([ @@ -912,35 +919,8 @@ describe('getServerUrlByHost', () => { }) describe('extractHostnamesFromCerts', () => { - // Test certificate containing domains: 'localhost', 'foo.localhost', 'vite.vite.localhost', - const createWorkingCert = `-----BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV -BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx -EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl -c3QwHhcNMjUwMTMwMDQxNTI1WhcNMjUwMzAxMDQxNTI1WjBpMRQwEgYDVQQDEwtl -eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD -VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNPlCqTmUZ7/F7GyFWDopqZ6 -w19Y7/98B10JEeFGTAQIj/RP2UgZNcTABQDUvtkF7y+bOeoVJW7Zz8ozQYhRaDp8 -CN2gXMcYeTUku/pKLXyCzHHVrOPAXTeU7sMRgLvPCrrJtx5OjvndW+O/PhohPRi3 -iEpPvpM8gi7MVRGhnWVSx0/Ynx5c0+/vqyBTzrM2OX7Ufg8Nv7LaTXpCAnmIQp+f -Sqq7HZ7t6Y7laS4RApityvlnFHZ4f2cEibAKv/vXLED7bgAlGb8R1viPRdMtAPuI -MYvHBgGFjyX1fmq6Mz3aqlAscJILtbQlwty1oYyaENE0lq8+nZXQ+t6I+CIVLQID -AQABo4GZMIGWMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB -BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBUBgNVHREETTBLgglsb2NhbGhvc3SC -DWZvby5sb2NhbGhvc3SCECoudml0ZS5sb2NhbGhvc3SCBVs6OjFdhwR/AAABhxD+ -gAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBi302qLCgxWsUalgc2 -olFxVKob1xCciS8yUVX6HX0vza0WJ7oGW6qZsBbQtfgDwB/dHv7rwsfpjRWvFhmq -gEUrewa1h0TIC+PPTYYz4M0LOwcLIWZLZr4am1eI7YP9NDgRdhfAfM4hw20vjf2a -kYLKyRTC5+3/ly5opMq+CGLQ8/gnFxhP3ho8JYrRnqLeh3KCTGen3kmbAhD4IOJ9 -lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh -WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkwbvxBar -X2JN ------END CERTIFICATE----- - `.trim() as any - test('should extract hostnames from certificate', () => { - const httpsOptions = { cert: [createWorkingCert] } as any + const httpsOptions = { cert: [WORKING_TEST_CERT] } as any const result = extractHostnamesFromCerts(httpsOptions.cert) expect(result).toStrictEqual([ @@ -951,7 +931,7 @@ X2JN }) test('should extract hostnames from multiple certificates', () => { - const httpsOptions = { cert: [createWorkingCert, createWorkingCert] } as any + const httpsOptions = { cert: [WORKING_TEST_CERT, WORKING_TEST_CERT] } as any const result = extractHostnamesFromCerts(httpsOptions.cert) expect(result).toStrictEqual([ @@ -977,33 +957,6 @@ describe('resolveServerUrls', () => { config: { rawBase: '/' } as any, }) - // Test certificate containing domains: 'localhost', 'foo.localhost', 'vite.vite.localhost', - const createWorkingCert = `-----BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIJS9D2rIN7tA8mMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV -BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx -EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl -c3QwHhcNMjUwMTMwMDQxNTI1WhcNMjUwMzAxMDQxNTI1WjBpMRQwEgYDVQQDEwtl -eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD -VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxNPlCqTmUZ7/F7GyFWDopqZ6 -w19Y7/98B10JEeFGTAQIj/RP2UgZNcTABQDUvtkF7y+bOeoVJW7Zz8ozQYhRaDp8 -CN2gXMcYeTUku/pKLXyCzHHVrOPAXTeU7sMRgLvPCrrJtx5OjvndW+O/PhohPRi3 -iEpPvpM8gi7MVRGhnWVSx0/Ynx5c0+/vqyBTzrM2OX7Ufg8Nv7LaTXpCAnmIQp+f -Sqq7HZ7t6Y7laS4RApityvlnFHZ4f2cEibAKv/vXLED7bgAlGb8R1viPRdMtAPuI -MYvHBgGFjyX1fmq6Mz3aqlAscJILtbQlwty1oYyaENE0lq8+nZXQ+t6I+CIVLQID -AQABo4GZMIGWMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB -BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBUBgNVHREETTBLgglsb2NhbGhvc3SC -DWZvby5sb2NhbGhvc3SCECoudml0ZS5sb2NhbGhvc3SCBVs6OjFdhwR/AAABhxD+ -gAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBi302qLCgxWsUalgc2 -olFxVKob1xCciS8yUVX6HX0vza0WJ7oGW6qZsBbQtfgDwB/dHv7rwsfpjRWvFhmq -gEUrewa1h0TIC+PPTYYz4M0LOwcLIWZLZr4am1eI7YP9NDgRdhfAfM4hw20vjf2a -kYLKyRTC5+3/ly5opMq+CGLQ8/gnFxhP3ho8JYrRnqLeh3KCTGen3kmbAhD4IOJ9 -lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh -WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkwbvxBar -X2JN ------END CERTIFICATE----- - `.trim() as any - test('should handle no certificate', () => { const mockServer = createMockServer() const { options, hostname, config } = createTestConfig() @@ -1023,7 +976,7 @@ X2JN test('should handle IPv4 single certificate', () => { const mockServer = createMockServer() const { options, hostname, config } = createTestConfig() - const httpsOptions = { cert: [createWorkingCert] } + const httpsOptions = { cert: [WORKING_TEST_CERT] } const result = resolveServerUrls( mockServer, @@ -1041,7 +994,7 @@ X2JN test('should handle IPv4 multiple certificates', () => { const mockServer = createMockServer() const { options, hostname, config } = createTestConfig() - const httpsOptions = { cert: [createWorkingCert, createWorkingCert] } + const httpsOptions = { cert: [WORKING_TEST_CERT, WORKING_TEST_CERT] } const result = resolveServerUrls( mockServer, @@ -1059,7 +1012,7 @@ X2JN test('should handle IPv6 single certificate', () => { const mockServer = createMockServer('IPv6', '::1') const { options, hostname, config } = createTestConfig() - const httpsOptions = { cert: [createWorkingCert] } + const httpsOptions = { cert: [WORKING_TEST_CERT] } const result = resolveServerUrls( mockServer, @@ -1077,7 +1030,7 @@ X2JN test('should handle IPv6 multiple certificates', () => { const mockServer = createMockServer('IPv6', '::1') const { options, hostname, config } = createTestConfig() - const httpsOptions = { cert: [createWorkingCert, createWorkingCert] } + const httpsOptions = { cert: [WORKING_TEST_CERT, WORKING_TEST_CERT] } const result = resolveServerUrls( mockServer, From 1d5618abc26655459c38bf57d051d96e942200e4 Mon Sep 17 00:00:00 2001 From: monam2 Date: Sun, 21 Sep 2025 02:45:28 +0900 Subject: [PATCH 7/8] refactor(utils): remove unused bufferify function --- packages/vite/src/node/utils.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index f9cd8c9139d10f..dd2a685602c25a 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -993,10 +993,6 @@ export async function resolveHostname( return { host, name } } -export function bufferify(buffer: string | Buffer): Buffer { - return Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer) -} - export function extractHostnamesFromCerts( certs: HttpsServerOptions['cert'] | undefined, ): string[] { From 6ef0e81c148d51664fe1862080f41ac8357a5605 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:14:22 +0900 Subject: [PATCH 8/8] chore: update --- packages/vite/src/node/__tests__/utils.spec.ts | 14 ++++++-------- packages/vite/src/node/utils.ts | 7 +++---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index 88b40c2eb6f5a2..bd52e6b13d65e4 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -59,7 +59,7 @@ lxMwFPTTWLFFjxbXjXmt5cEiL2mpcq13VCF2HmheCen37CyYIkrwK9IfLhBd5QQh WEIBLwjKCAscrtyayXWp6zUTmgvb8PQf//3Mh2DiEngAi3WI/nL+8Y0RkqbvxBar X2JN -----END CERTIFICATE----- -`.trim() as any +`.trim() describe('bareImportRE', () => { test('should work with normal package name', () => { @@ -920,9 +920,8 @@ describe('getServerUrlByHost', () => { describe('extractHostnamesFromCerts', () => { test('should extract hostnames from certificate', () => { - const httpsOptions = { cert: [WORKING_TEST_CERT] } as any - const result = extractHostnamesFromCerts(httpsOptions.cert) - + const certs = [WORKING_TEST_CERT] + const result = extractHostnamesFromCerts(certs) expect(result).toStrictEqual([ 'localhost', 'foo.localhost', @@ -931,9 +930,8 @@ describe('extractHostnamesFromCerts', () => { }) test('should extract hostnames from multiple certificates', () => { - const httpsOptions = { cert: [WORKING_TEST_CERT, WORKING_TEST_CERT] } as any - const result = extractHostnamesFromCerts(httpsOptions.cert) - + const certs = [WORKING_TEST_CERT, WORKING_TEST_CERT] + const result = extractHostnamesFromCerts(certs) expect(result).toStrictEqual([ 'localhost', 'foo.localhost', @@ -960,7 +958,7 @@ describe('resolveServerUrls', () => { test('should handle no certificate', () => { const mockServer = createMockServer() const { options, hostname, config } = createTestConfig() - const httpsOptions = {} as never + const httpsOptions = {} const result = resolveServerUrls( mockServer, diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index dd2a685602c25a..d9981e72c20757 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -1068,12 +1068,11 @@ export function resolveServerUrls( }) } - const hostnames = extractHostnamesFromCerts(httpsOptions?.cert) - - if (hostnames.length > 0) { + const hostnamesFromCert = extractHostnamesFromCerts(httpsOptions?.cert) + if (hostnamesFromCert.length > 0) { const existings = new Set([...local, ...network]) local.push( - ...hostnames + ...hostnamesFromCert .map((hostname) => `${protocol}://${hostname}:${port}${base}`) .filter((url) => !existings.has(url)), )