22 * Security-focused tests: path traversal prevention and source-path containment.
33 */
44import { describe , expect , test , beforeEach , afterEach } from "bun:test" ;
5- import { resolve , join } from "path" ;
5+ import { resolve , join , sep } from "path" ;
66import { isPathWithin } from "../src/paths" ;
77import { resolveLmPath , resolveDitPath } from "../src/worker" ;
88
@@ -25,10 +25,12 @@ function audioFilePath(audioStorageDir: string, pathParam: string): string | nul
2525// ---------------------------------------------------------------------------
2626
2727describe ( "path traversal prevention in /v1/audio" , ( ) => {
28- const storageDir = "/storage/audio" ;
28+ // Use path.resolve so the base is always an absolute, platform-native path.
29+ // On Windows, resolve("/storage/audio") → "C:\storage\audio"; on Unix it stays "/storage/audio".
30+ const storageDir = resolve ( "/storage/audio" ) ;
2931
3032 test ( "valid path inside storage dir is allowed" , ( ) => {
31- expect ( audioFilePath ( storageDir , "/abc123.mp3" ) ) . toBe ( "/storage/audio/ abc123.mp3") ;
33+ expect ( audioFilePath ( storageDir , "/abc123.mp3" ) ) . toBe ( join ( storageDir , " abc123.mp3") ) ;
3234 } ) ;
3335
3436 test ( "explicit ../ traversal is blocked" , ( ) => {
@@ -44,19 +46,19 @@ describe("path traversal prevention in /v1/audio", () => {
4446 // resolve treats it as such, so the result is still within the storage dir.
4547 const result = audioFilePath ( storageDir , "....//....//etc/passwd" ) ;
4648 expect ( result ) . not . toBeNull ( ) ;
47- expect ( result ! . startsWith ( storageDir ) ) . toBe ( true ) ;
49+ expect ( result ! . startsWith ( storageDir + sep ) ) . toBe ( true ) ;
4850 } ) ;
4951
5052 test ( "absolute path in query param is contained within storage dir" , ( ) => {
5153 // requestedPath strips the leading / so /etc/passwd becomes etc/passwd,
5254 // which is then joined with storageDir to give storageDir/etc/passwd.
5355 const result = audioFilePath ( storageDir , "/etc/passwd" ) ;
5456 expect ( result ) . not . toBeNull ( ) ;
55- expect ( result ! . startsWith ( storageDir ) ) . toBe ( true ) ;
57+ expect ( result ! . startsWith ( storageDir + sep ) ) . toBe ( true ) ;
5658 } ) ;
5759
5860 test ( "nested valid path is allowed" , ( ) => {
59- expect ( audioFilePath ( storageDir , "/sub/file.wav" ) ) . toBe ( "/storage/audio/ sub/ file.wav") ;
61+ expect ( audioFilePath ( storageDir , "/sub/file.wav" ) ) . toBe ( join ( storageDir , " sub" , " file.wav") ) ;
6062 } ) ;
6163
6264 test ( "prefix-only directory name is not confused with parent" , ( ) => {
0 commit comments