69 lines
3.6 KiB
TypeScript
69 lines
3.6 KiB
TypeScript
// file: 앱 오리진 판정을 렌더러 디렉터리로 제한하는 회귀 테스트(드롭한 파일이 앱 창을 대체하던 문제).
|
|
import { describe, expect, it } from 'vitest'
|
|
import { isAppNavigationTarget, isAppOrigin, isFileUrlWithinRoot } from '../src/url-policy'
|
|
|
|
const ROOT = 'file:///C:/Program%20Files/D3RO%20Voice/resources/app.asar/out/renderer/'
|
|
const ORIGINS = ['file://', 'http://localhost:5173']
|
|
const OPTIONS = { fileRoot: ROOT }
|
|
|
|
describe('isFileUrlWithinRoot', () => {
|
|
it('accepts files under the renderer root (encoding, drive case, hash/query ignored)', () => {
|
|
expect(isFileUrlWithinRoot(`${ROOT}index.html#/meetings`, ROOT)).toBe(true)
|
|
expect(isFileUrlWithinRoot(`${ROOT}popups/result-popup/index.html?x=1`, ROOT)).toBe(true)
|
|
expect(isFileUrlWithinRoot('file:///c:/program files/d3ro voice/resources/app.asar/out/renderer/index.html', ROOT)).toBe(true)
|
|
expect(isFileUrlWithinRoot(`${ROOT}index.html`, ROOT.slice(0, -1))).toBe(true)
|
|
})
|
|
|
|
it('accepts a non-ASCII install path encoded the way Chromium encodes it', () => {
|
|
const root = 'file:///C:/Users/%EC%9C%A4%EC%B0%AC/AppData/Local/Programs/d3ro-voice/resources/app.asar/out/renderer/'
|
|
expect(isFileUrlWithinRoot('file:///C:/Users/윤찬/AppData/Local/Programs/d3ro-voice/resources/app.asar/out/renderer/index.html', root)).toBe(true)
|
|
})
|
|
|
|
it.each([
|
|
'file:///C:/Users/me/Downloads/x.html',
|
|
'file:///C:/Program%20Files/D3RO%20Voice/resources/app.asar/out/renderer-evil/index.html',
|
|
'file:///C:/Program%20Files/D3RO%20Voice/resources/app.asar/out/index.html',
|
|
'file://evil-host/share/x.html',
|
|
'file://evil-host/C:/Program%20Files/D3RO%20Voice/resources/app.asar/out/renderer/index.html',
|
|
`${ROOT}..%2F..%2Fx.html`,
|
|
`${ROOT}..%5C..%5Cx.html`,
|
|
`${ROOT}%2e%2e/%2e%2e/x.html`,
|
|
'http://localhost:5173/index.html',
|
|
'not a url',
|
|
])('rejects %s', (url) => {
|
|
expect(isFileUrlWithinRoot(url, ROOT)).toBe(false)
|
|
})
|
|
|
|
it('rejects everything when the root itself is not a file URL', () => {
|
|
expect(isFileUrlWithinRoot(`${ROOT}index.html`, 'https://example.com/')).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('isAppOrigin with fileRoot', () => {
|
|
it('trusts only the renderer bundle and the dev server', () => {
|
|
expect(isAppOrigin(`${ROOT}index.html#/settings`, ORIGINS, OPTIONS)).toBe(true)
|
|
expect(isAppOrigin('http://localhost:5173/#/settings', ORIGINS, OPTIONS)).toBe(true)
|
|
expect(isAppOrigin('file:///C:/Users/me/Downloads/evil.html', ORIGINS, OPTIONS)).toBe(false)
|
|
expect(isAppOrigin('file://host/share/evil.html', ORIGINS, OPTIONS)).toBe(false)
|
|
expect(isAppOrigin('https://evil.example/', ORIGINS, OPTIONS)).toBe(false)
|
|
})
|
|
|
|
it('keeps the dev server origin exact even with a fileRoot', () => {
|
|
expect(isAppOrigin('http://localhost:5174/', ORIGINS, OPTIONS)).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('isAppNavigationTarget', () => {
|
|
it('allows app HTML pages and dev server routes', () => {
|
|
expect(isAppNavigationTarget(`${ROOT}index.html`, ORIGINS, OPTIONS)).toBe(true)
|
|
expect(isAppNavigationTarget(`${ROOT}popups/command-popup/index.html#x`, ORIGINS, OPTIONS)).toBe(true)
|
|
expect(isAppNavigationTarget('http://localhost:5173/popups/result-popup/index.html', ORIGINS, OPTIONS)).toBe(true)
|
|
})
|
|
|
|
it('blocks dropped files and non-HTML files inside the bundle', () => {
|
|
expect(isAppNavigationTarget('file:///C:/Users/me/Music/meeting.mp3', ORIGINS, OPTIONS)).toBe(false)
|
|
expect(isAppNavigationTarget('file:///C:/Users/me/Downloads/x.html', ORIGINS, OPTIONS)).toBe(false)
|
|
expect(isAppNavigationTarget(`${ROOT}assets/index-abc.js`, ORIGINS, OPTIONS)).toBe(false)
|
|
expect(isAppNavigationTarget(`${ROOT}`, ORIGINS, OPTIONS)).toBe(false)
|
|
})
|
|
})
|