Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/vite/src/node/__tests__/dev.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,35 @@ describe('the dev server', () => {
expect(server.config.safeModulePaths).not.toContain(externalEntry)
})

test('does not mark unresolved SSR imports as safe', async () => {
const root = path.join(import.meta.dirname, 'fixtures', 'input-option')
const entry = normalizePath(path.join(root, 'unresolved-entry.js'))
const unresolvedImport = '/safe-module-path-unresolved'

server = await createServer({
configFile: false,
root,
optimizeDeps: { noDiscovery: true },
server: { middlewareMode: true, ws: false },
plugins: [
{
name: 'unresolved-ssr-import',
resolveId(id) {
if (id === '/unresolved-entry.js') return entry
},
load(id) {
if (id === entry)
return `import ${JSON.stringify(unresolvedImport)}`
},
},
],
})

await server.environments.ssr.transformRequest('/unresolved-entry.js')

expect(server.config.safeModulePaths).not.toContain(unresolvedImport)
})

test('silently resolves index.html as the fallback for every environment', async () => {
const root = path.join(import.meta.dirname, 'fixtures', 'input-option')
const clientEntry = normalizePath(path.join(root, 'client-index.html'))
Expand Down
9 changes: 3 additions & 6 deletions packages/vite/src/node/plugins/importAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import {
import type { TransformPluginContext } from '../server/pluginContainer'
import {
createDebugger,
fsPathFromUrl,
generateCodeFrame,
getFileStartIndex,
getHash,
Expand Down Expand Up @@ -587,13 +586,11 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {

// normalize
let [url, resolvedId] = await normalizeUrl(specifier, start)
if (resolvedId) {
config.safeModulePaths.add(cleanUrl(resolvedId))
}
resolvedId = resolvedId || url

// record as safe modules
// safeModulesPath should not include the base prefix.
// See https://github.com/vitejs/vite/issues/9438#issuecomment-1465270409
config.safeModulePaths.add(fsPathFromUrl(stripBase(url, base)))

if (url !== specifier) {
let rewriteDone = false
// optimizer-emitted imports resolve to the sibling file they
Expand Down
10 changes: 9 additions & 1 deletion playground/fs-serve/__tests__/fs-serve.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import net from 'node:net'
import path from 'node:path'
import { describe, expect, test } from 'vitest'
import { isBundledDev, isServe, isWindows, viteTestUrl } from '~utils'
import { isBundledDev, isServe, isWindows, page, viteTestUrl } from '~utils'
import './commonTests'

describe.runIf(isServe)('safe module paths', () => {
test('keeps root-relative module URLs separate from absolute filesystem paths', async () => {
await expect
.poll(() => page.textContent('.safe-module-path-status'))
.toBe(isBundledDev || isWindows ? '404' : '403')
})
})

describe.runIf(isServe)('invalid request', () => {
const sendRawRequest = async (baseUrl: string, requestTarget: string) => {
return new Promise<string>((resolve, reject) => {
Expand Down
24 changes: 24 additions & 0 deletions playground/fs-serve/root/safeModulePathPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import path from 'node:path'
import type { Plugin } from 'vite'

const playgroundRoot = path.dirname(import.meta.dirname).replace(/\\/g, '/')
const targetRoot = playgroundRoot.replace(/^[A-Z]:/i, '')
const target = path.posix.join(targetRoot, 'root/unsafe.txt')
const decoyId = path.posix.join(import.meta.dirname.replace(/\\/g, '/'), target)

export default function safeModulePathPlugin(): Plugin {
return {
name: 'safe-module-path',
enforce: 'pre',
resolveId(id) {
if (id === 'virtual:safe-module-path-confusion') {
return decoyId
}
},
load(id) {
if (id === decoyId) {
return 'export {}'
}
},
}
}
13 changes: 13 additions & 0 deletions playground/fs-serve/root/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ <h2>Normal Import</h2>
<pre class="virtual-svg"></pre>
</div>

<div>
<h2>Safe Module Path Access Isolation</h2>
<pre class="safe-module-path-status"></pre>
</div>

<!-- REPLACE WITH MATRIX TEST RESULT -->

<script type="module">
import '../../entry'
import json, { msg } from '../../safe.json'
import 'virtual:safe-module-path-confusion'
import './code.js'

// Check virtual svg module still works
Expand All @@ -23,6 +29,13 @@ <h2>Normal Import</h2>
text('.named', msg)
text('.virtual-svg', fooSvg)

if (import.meta.env.DEV) {
const base = typeof BASE !== 'undefined' ? BASE : '/'
const hostFileUrl = `${base}@fs${ROOT}/root/unsafe.txt`
const response = await fetch(hostFileUrl)
text('.safe-module-path-status', response.status)
}

function text(sel, text) {
document.querySelector(sel).textContent = text
}
Expand Down
7 changes: 6 additions & 1 deletion playground/fs-serve/root/vite.config-base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'node:path'
import { defineConfig } from 'vite'
import matrixTestResultPlugin from './matrixTestResultPlugin.ts'
import safeModulePathPlugin from './safeModulePathPlugin.ts'
import svgVirtualModulePlugin from './svgVirtualModulePlugin.ts'
import { getWindows83ShortNameForDotEnv } from './windows83Filename.ts'

Expand Down Expand Up @@ -36,5 +37,9 @@ export default defineConfig({
BASE: JSON.stringify(BASE),
DOTENV83SHORTNAME: JSON.stringify(getWindows83ShortNameForDotEnv()),
},
plugins: [svgVirtualModulePlugin(), matrixTestResultPlugin()],
plugins: [
svgVirtualModulePlugin(),
safeModulePathPlugin(),
matrixTestResultPlugin(),
],
})
3 changes: 2 additions & 1 deletion playground/fs-serve/root/vite.config-deny.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path'
import { defineConfig } from 'vite'
import safeModulePathPlugin from './safeModulePathPlugin.ts'
import svgVirtualModulePlugin from './svgVirtualModulePlugin.ts'

export default defineConfig({
Expand All @@ -19,5 +20,5 @@ export default defineConfig({
define: {
ROOT: JSON.stringify(path.dirname(import.meta.dirname).replace(/\\/g, '/')),
},
plugins: [svgVirtualModulePlugin()],
plugins: [svgVirtualModulePlugin(), safeModulePathPlugin()],
})
7 changes: 6 additions & 1 deletion playground/fs-serve/root/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'node:path'
import { defineConfig } from 'vite'
import matrixTestResultPlugin from './matrixTestResultPlugin.ts'
import safeModulePathPlugin from './safeModulePathPlugin.ts'
import svgVirtualModulePlugin from './svgVirtualModulePlugin.ts'
import { getWindows83ShortNameForDotEnv } from './windows83Filename.ts'

Expand Down Expand Up @@ -32,5 +33,9 @@ export default defineConfig({
ROOT: JSON.stringify(path.dirname(import.meta.dirname).replace(/\\/g, '/')),
DOTENV83SHORTNAME: JSON.stringify(getWindows83ShortNameForDotEnv()),
},
plugins: [svgVirtualModulePlugin(), matrixTestResultPlugin()],
plugins: [
svgVirtualModulePlugin(),
safeModulePathPlugin(),
matrixTestResultPlugin(),
],
})
Loading