| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 |
- /**
- * Containment tests for lexical canonical paths and filesystem-identity aliases.
- */
- import { afterEach, beforeEach, describe, expect, it } from 'vitest'
- import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises'
- import { tmpdir } from 'node:os'
- import { join, parse } from 'node:path'
- import { isPathUnder } from '../src/containment.ts'
- let base: string
- beforeEach(async () => {
- base = await mkdtemp(join(tmpdir(), 'dsh-fssbx-containment-'))
- })
- afterEach(async () => {
- await rm(base, { recursive: true, force: true })
- })
- describe('filesystem sandbox containment', () => {
- it('accepts equal paths, descendants, and a filesystem-root boundary', async () => {
- expect(await isPathUnder(base, base)).toBe(true)
- expect(await isPathUnder(join(base, 'child'), base)).toBe(true)
- expect(await isPathUnder(base, parse(base).root)).toBe(true)
- })
- it('uses case-insensitive lexical comparison for Windows-style containment', async () => {
- expect(await isPathUnder(join(base.toUpperCase(), 'child'), base.toLowerCase(), false)).toBe(true)
- expect(await isPathUnder(join(base, 'case-sensitive-child'), base, true)).toBe(true)
- })
- it('recognizes an alias-equivalent root by filesystem identity for a missing target', async () => {
- const realRoot = join(base, 'real')
- const aliasRoot = join(base, 'alias')
- await mkdir(realRoot)
- await symlink(realRoot, aliasRoot)
- expect(await isPathUnder(join(await realpath(realRoot), 'missing', 'file.txt'), aliasRoot)).toBe(true)
- })
- it('denies unrelated and missing roots', async () => {
- const allowed = join(base, 'allowed')
- const outside = join(base, 'outside')
- await mkdir(allowed)
- await mkdir(outside)
- expect(await isPathUnder(join(outside, 'file.txt'), allowed)).toBe(false)
- expect(await isPathUnder(join(outside, 'file.txt'), join(base, 'missing-root'))).toBe(false)
- })
- it('treats a regular-file path segment as a missing target, not containment', async () => {
- const allowed = join(base, 'allowed')
- const blocker = join(base, 'blocker')
- await mkdir(allowed)
- await writeFile(blocker, 'not a directory')
- expect(await isPathUnder(join(blocker, 'child.txt'), allowed)).toBe(false)
- })
- })
|