fix: sanitize asset filenames (#9737)

This commit is contained in:
Dominik G 2022-08-19 14:50:38 +02:00 committed by GitHub
parent 518bc6ce99
commit 2f468bb33c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 1 deletions

View File

@ -342,7 +342,7 @@ export function assetFileNamesToFileName(
return hash
case '[name]':
return name
return sanitizeFileName(name)
}
throw new Error(
`invalid placeholder ${placeholder} in assetFileNames "${assetFileNames}"`
@ -353,6 +353,23 @@ export function assetFileNamesToFileName(
return fileName
}
// taken from https://github.com/rollup/rollup/blob/a8647dac0fe46c86183be8596ef7de25bc5b4e4b/src/utils/sanitizeFileName.ts
// https://datatracker.ietf.org/doc/html/rfc2396
// eslint-disable-next-line no-control-regex
const INVALID_CHAR_REGEX = /[\x00-\x1F\x7F<>*#"{}|^[\]`;?:&=+$,]/g
const DRIVE_LETTER_REGEX = /^[a-z]:/i
function sanitizeFileName(name: string): string {
const match = DRIVE_LETTER_REGEX.exec(name)
const driveLetter = match ? match[0] : ''
// A `:` is only allowed as part of a windows drive letter (ex: C:\foo)
// Otherwise, avoid them because they can refer to NTFS alternate data streams.
return (
driveLetter +
name.substr(driveLetter.length).replace(INVALID_CHAR_REGEX, '_')
)
}
export const publicAssetUrlCache = new WeakMap<
ResolvedConfig,
// hash -> url

View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="#000"></circle>
</svg>

After

Width:  |  Height:  |  Size: 135 B

View File

@ -0,0 +1,27 @@
import { expect, test } from 'vitest'
import { getBg, isBuild, page, readManifest } from '~utils'
if (!isBuild) {
test('importing asset with special char in filename works in dev', async () => {
expect(await getBg('.plus-circle')).toContain('+circle.svg')
expect(await page.textContent('.plus-circle')).toMatch('+circle.svg')
expect(await getBg('.underscore-circle')).toContain('_circle.svg')
expect(await page.textContent('.underscore-circle')).toMatch('_circle.svg')
})
} else {
test('importing asset with special char in filename works in build', async () => {
const manifest = readManifest()
const plusCircleAsset = manifest['+circle.svg'].file
const underscoreCircleAsset = manifest['_circle.svg'].file
expect(await getBg('.plus-circle')).toMatch(plusCircleAsset)
expect(await page.textContent('.plus-circle')).toMatch(plusCircleAsset)
expect(await getBg('.underscore-circle')).toMatch(underscoreCircleAsset)
expect(await page.textContent('.underscore-circle')).toMatch(
underscoreCircleAsset
)
expect(plusCircleAsset).toMatch('/_circle')
expect(underscoreCircleAsset).toMatch('/_circle')
expect(plusCircleAsset).not.toEqual(underscoreCircleAsset)
expect(Object.keys(manifest).length).toBe(3) // 2 svg, 1 index.js
})
}

View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="red"></circle>
</svg>

After

Width:  |  Height:  |  Size: 134 B

View File

@ -0,0 +1,11 @@
<script type="module" src="./index.js"></script>
<style>
.test-el {
background-repeat: no-repeat;
padding-left: 2rem;
margin-bottom: 1rem;
}
</style>
<h1>test elements below should show circles and their url</h1>
<div class="test-el plus-circle"></div>
<div class="test-el underscore-circle"></div>

View File

@ -0,0 +1,9 @@
import plusCircle from './+circle.svg'
import underscoreCircle from './_circle.svg'
function setData(classname, file) {
const el = document.body.querySelector(classname)
el.style.backgroundImage = `url(${file})`
el.textContent = file
}
setData('.plus-circle', plusCircle)
setData('.underscore-circle', underscoreCircle)

View File

@ -0,0 +1,11 @@
{
"name": "test-assets-sanitize",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
"preview": "vite preview"
}
}

View File

@ -0,0 +1,11 @@
const { defineConfig } = require('vite')
module.exports = defineConfig({
build: {
//speed up build
minify: false,
target: 'esnext',
assetsInlineLimit: 0,
manifest: true
}
})