1- import { mkdirSync , rmSync , writeFileSync } from 'fs' ;
2- import { tmpdir } from 'os' ;
3- import { join } from 'path' ;
1+ import { mkdirSync , writeFileSync } from 'fs' ;
2+ import { resolve , relative } from 'path' ;
3+
4+ import * as tmp from 'tmp' ;
45
56import { determineCdsFilesToCompile } from '../../../../src/cds/parser/functions' ;
67
8+ /**
9+ * Validates that a path is safe to use within a base directory.
10+ * Prevents path traversal attacks by ensuring the resolved path stays within the base directory.
11+ */
12+ function validateSafePath ( basePath : string , ...pathSegments : string [ ] ) : string {
13+ const resolvedBase = resolve ( basePath ) ;
14+ const targetPath = resolve ( basePath , ...pathSegments ) ;
15+
16+ // Check if the resolved target path is within the base directory
17+ const relativePath = relative ( resolvedBase , targetPath ) ;
18+ if ( relativePath . startsWith ( '..' ) || relativePath . includes ( '..' ) ) {
19+ throw new Error ( `Path traversal detected: ${ pathSegments . join ( '/' ) } ` ) ;
20+ }
21+
22+ return targetPath ;
23+ }
24+
725describe ( 'Project-aware compilation for CAP projects' , ( ) => {
826 let tempDir : string ;
27+ let tmpCleanup : ( ( ) => void ) | undefined ;
928
1029 beforeEach ( ( ) => {
11- // Create a temporary directory for each test
12- tempDir = join ( tmpdir ( ) , `cds-test-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substring ( 7 ) } ` ) ;
13- mkdirSync ( tempDir , { recursive : true } ) ;
30+ // Create a secure temporary directory for each test
31+ const tmpObj = tmp . dirSync ( { unsafeCleanup : true } ) ;
32+ tempDir = tmpObj . name ;
33+ tmpCleanup = tmpObj . removeCallback ;
1434 } ) ;
1535
1636 afterEach ( ( ) => {
17- // Clean up temporary directory
18- try {
19- rmSync ( tempDir , { recursive : true , force : true } ) ;
20- } catch ( error : unknown ) {
21- console . warn ( `Warning: Could not clean up temp directory ${ tempDir } : ${ String ( error ) } ` ) ;
37+ // Clean up temporary directory using tmp library's cleanup function
38+ if ( tmpCleanup ) {
39+ tmpCleanup ( ) ;
2240 }
2341 } ) ;
2442
2543 it ( 'should use project-level compilation for log injection test case structure' , ( ) => {
2644 // Create the log injection test case structure
27- const projectPath = join ( tempDir , 'log-injection-without-protocol-none' ) ;
45+ const projectPath = validateSafePath ( tempDir , 'log-injection-without-protocol-none' ) ;
2846 mkdirSync ( projectPath , { recursive : true } ) ;
29- mkdirSync ( join ( projectPath , 'db' ) , { recursive : true } ) ;
30- mkdirSync ( join ( projectPath , 'srv' ) , { recursive : true } ) ;
47+ mkdirSync ( validateSafePath ( projectPath , 'db' ) , { recursive : true } ) ;
48+ mkdirSync ( validateSafePath ( projectPath , 'srv' ) , { recursive : true } ) ;
3149
3250 // Create package.json with CAP dependencies
3351 writeFileSync (
34- join ( projectPath , 'package.json' ) ,
52+ validateSafePath ( projectPath , 'package.json' ) ,
3553 JSON . stringify ( {
3654 name : 'log-injection-test' ,
3755 dependencies : {
@@ -42,7 +60,7 @@ describe('Project-aware compilation for CAP projects', () => {
4260
4361 // Create CDS files
4462 writeFileSync (
45- join ( projectPath , 'db' , 'schema.cds' ) ,
63+ validateSafePath ( projectPath , 'db' , 'schema.cds' ) ,
4664 `namespace advanced_security.log_injection.sample_entities;
4765
4866entity Entity1 {
@@ -57,7 +75,7 @@ entity Entity2 {
5775 ) ;
5876
5977 writeFileSync (
60- join ( projectPath , 'srv' , 'service1.cds' ) ,
78+ validateSafePath ( projectPath , 'srv' , 'service1.cds' ) ,
6179 `using { advanced_security.log_injection.sample_entities as db_schema } from '../db/schema';
6280
6381service Service1 @(path: '/service-1') {
@@ -70,7 +88,7 @@ service Service1 @(path: '/service-1') {
7088 ) ;
7189
7290 writeFileSync (
73- join ( projectPath , 'srv' , 'service2.cds' ) ,
91+ validateSafePath ( projectPath , 'srv' , 'service2.cds' ) ,
7492 `using { advanced_security.log_injection.sample_entities as db_schema } from '../db/schema';
7593
7694service Service2 @(path: '/service-2') {
@@ -128,12 +146,12 @@ service Service2 @(path: '/service-2') {
128146
129147 it ( 'should still use individual file compilation for simple projects without CAP structure' , ( ) => {
130148 // Create a simple project without typical CAP structure
131- const simpleProjectPath = join ( tempDir , 'simple-project' ) ;
149+ const simpleProjectPath = validateSafePath ( tempDir , 'simple-project' ) ;
132150 mkdirSync ( simpleProjectPath , { recursive : true } ) ;
133151
134152 // Create CDS files in flat structure
135153 writeFileSync (
136- join ( simpleProjectPath , 'main.cds' ) ,
154+ validateSafePath ( simpleProjectPath , 'main.cds' ) ,
137155 `using from "./model";
138156
139157service MainService {
@@ -142,7 +160,7 @@ service MainService {
142160 ) ;
143161
144162 writeFileSync (
145- join ( simpleProjectPath , 'model.cds' ) ,
163+ validateSafePath ( simpleProjectPath , 'model.cds' ) ,
146164 `namespace model;
147165
148166entity Item {
0 commit comments