@@ -111,10 +111,11 @@ describe("validateCommand", () => {
111111
112112describe ( "handleLocalOp" , ( ) => {
113113 let testDir : string ;
114- const options = makeOptions ( ) ;
114+ let options : WizardOptions ;
115115
116116 beforeEach ( ( ) => {
117117 testDir = mkdtempSync ( join ( "/tmp" , "local-ops-test-" ) ) ;
118+ options = makeOptions ( { directory : testDir } ) ;
118119 } ) ;
119120
120121 afterEach ( ( ) => {
@@ -182,6 +183,50 @@ describe("handleLocalOp", () => {
182183 } ) ;
183184 } ) ;
184185
186+ describe ( "cwd sandboxing" , ( ) => {
187+ test ( "rejects cwd outside project directory" , async ( ) => {
188+ const payload : ListDirPayload = {
189+ type : "local-op" ,
190+ operation : "list-dir" ,
191+ cwd : "/" ,
192+ params : { path : "." } ,
193+ } ;
194+
195+ const result = await handleLocalOp ( payload , options ) ;
196+ expect ( result . ok ) . toBe ( false ) ;
197+ expect ( result . error ) . toContain ( "outside project directory" ) ;
198+ } ) ;
199+
200+ test ( "allows cwd equal to project directory" , async ( ) => {
201+ writeFileSync ( join ( testDir , "file.txt" ) , "x" ) ;
202+
203+ const payload : ListDirPayload = {
204+ type : "local-op" ,
205+ operation : "list-dir" ,
206+ cwd : testDir ,
207+ params : { path : "." } ,
208+ } ;
209+
210+ const result = await handleLocalOp ( payload , options ) ;
211+ expect ( result . ok ) . toBe ( true ) ;
212+ } ) ;
213+
214+ test ( "allows cwd that is a subdirectory of project directory" , async ( ) => {
215+ mkdirSync ( join ( testDir , "sub" ) ) ;
216+ writeFileSync ( join ( testDir , "sub" , "file.txt" ) , "x" ) ;
217+
218+ const payload : ListDirPayload = {
219+ type : "local-op" ,
220+ operation : "list-dir" ,
221+ cwd : join ( testDir , "sub" ) ,
222+ params : { path : "." } ,
223+ } ;
224+
225+ const result = await handleLocalOp ( payload , options ) ;
226+ expect ( result . ok ) . toBe ( true ) ;
227+ } ) ;
228+ } ) ;
229+
185230 describe ( "list-dir" , ( ) => {
186231 test ( "lists files and directories with correct types" , async ( ) => {
187232 writeFileSync ( join ( testDir , "file1.txt" ) , "a" ) ;
@@ -489,7 +534,7 @@ describe("handleLocalOp", () => {
489534 params : { commands : [ "rm -rf /" , "echo hello" ] } ,
490535 } ;
491536
492- const dryRunOptions = makeOptions ( { dryRun : true } ) ;
537+ const dryRunOptions = makeOptions ( { dryRun : true , directory : testDir } ) ;
493538 const result = await handleLocalOp ( payload , dryRunOptions ) ;
494539 expect ( result . ok ) . toBe ( true ) ;
495540 const results = (
@@ -657,7 +702,7 @@ describe("handleLocalOp", () => {
657702 } ,
658703 } ;
659704
660- const dryRunOptions = makeOptions ( { dryRun : true } ) ;
705+ const dryRunOptions = makeOptions ( { dryRun : true , directory : testDir } ) ;
661706 const result = await handleLocalOp ( payload , dryRunOptions ) ;
662707 expect ( result . ok ) . toBe ( true ) ;
663708
@@ -681,7 +726,7 @@ describe("handleLocalOp", () => {
681726 } ,
682727 } ;
683728
684- const dryRunOptions = makeOptions ( { dryRun : true } ) ;
729+ const dryRunOptions = makeOptions ( { dryRun : true , directory : testDir } ) ;
685730 const result = await handleLocalOp ( payload , dryRunOptions ) ;
686731 expect ( result . ok ) . toBe ( false ) ;
687732 expect ( result . error ) . toContain ( "outside project directory" ) ;
0 commit comments