@cloudflare/sandbox 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/Dockerfile +1 -14
  3. package/container_src/handler/exec.ts +337 -0
  4. package/container_src/handler/file.ts +844 -0
  5. package/container_src/handler/git.ts +182 -0
  6. package/container_src/handler/ports.ts +314 -0
  7. package/container_src/handler/process.ts +640 -0
  8. package/container_src/index.ts +82 -2973
  9. package/container_src/types.ts +103 -0
  10. package/dist/chunk-6THNBO4S.js +46 -0
  11. package/dist/chunk-6THNBO4S.js.map +1 -0
  12. package/dist/chunk-6UAWTJ5S.js +85 -0
  13. package/dist/chunk-6UAWTJ5S.js.map +1 -0
  14. package/dist/chunk-G4XT4SP7.js +638 -0
  15. package/dist/chunk-G4XT4SP7.js.map +1 -0
  16. package/dist/chunk-ISFOIYQC.js +585 -0
  17. package/dist/chunk-ISFOIYQC.js.map +1 -0
  18. package/dist/chunk-NNGBXDMY.js +89 -0
  19. package/dist/chunk-NNGBXDMY.js.map +1 -0
  20. package/dist/client-Da-mLX4p.d.ts +210 -0
  21. package/dist/client.d.ts +2 -1
  22. package/dist/client.js +3 -37
  23. package/dist/index.d.ts +3 -1
  24. package/dist/index.js +13 -3
  25. package/dist/request-handler.d.ts +2 -1
  26. package/dist/request-handler.js +4 -2
  27. package/dist/sandbox.d.ts +2 -1
  28. package/dist/sandbox.js +4 -2
  29. package/dist/security.d.ts +30 -0
  30. package/dist/security.js +13 -0
  31. package/dist/security.js.map +1 -0
  32. package/dist/sse-parser.d.ts +28 -0
  33. package/dist/sse-parser.js +11 -0
  34. package/dist/sse-parser.js.map +1 -0
  35. package/dist/types.d.ts +284 -0
  36. package/dist/types.js +19 -0
  37. package/dist/types.js.map +1 -0
  38. package/package.json +2 -7
  39. package/src/client.ts +235 -1286
  40. package/src/index.ts +6 -0
  41. package/src/request-handler.ts +69 -20
  42. package/src/sandbox.ts +463 -70
  43. package/src/security.ts +113 -0
  44. package/src/sse-parser.ts +147 -0
  45. package/src/types.ts +386 -0
  46. package/README.md +0 -65
  47. package/dist/chunk-4J5LQCCN.js +0 -1446
  48. package/dist/chunk-4J5LQCCN.js.map +0 -1
  49. package/dist/chunk-5SZ3RVJZ.js +0 -250
  50. package/dist/chunk-5SZ3RVJZ.js.map +0 -1
  51. package/dist/client-BuVjqV00.d.ts +0 -247
  52. package/tests/client.example.ts +0 -308
  53. package/tests/connection-test.ts +0 -81
  54. package/tests/simple-test.ts +0 -81
  55. package/tests/test1.ts +0 -281
  56. package/tests/test2.ts +0 -929
@@ -0,0 +1,844 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import type {
5
+ DeleteFileRequest,
6
+ MkdirRequest,
7
+ MoveFileRequest,
8
+ ReadFileRequest,
9
+ RenameFileRequest,
10
+ SessionData,
11
+ WriteFileRequest
12
+ } from "../types";
13
+
14
+ function executeMkdir(
15
+ sessions: Map<string, SessionData>,
16
+ path: string,
17
+ recursive: boolean,
18
+ sessionId?: string
19
+ ): Promise<{
20
+ success: boolean;
21
+ stdout: string;
22
+ stderr: string;
23
+ exitCode: number;
24
+ }> {
25
+ return new Promise((resolve, reject) => {
26
+ const args = `${recursive ? "-p " : ""} ${path}`;
27
+ const mkdirChild = spawn(`mkdir ${args}`, {
28
+ shell: true,
29
+ stdio: ["pipe", "pipe", "pipe"],
30
+ });
31
+
32
+ // Store the process reference for cleanup if sessionId is provided
33
+ if (sessionId && sessions.has(sessionId)) {
34
+ const session = sessions.get(sessionId)!;
35
+ session.activeProcess = mkdirChild;
36
+ }
37
+
38
+ let stdout = "";
39
+ let stderr = "";
40
+
41
+ mkdirChild.stdout?.on("data", (data) => {
42
+ stdout += data.toString();
43
+ });
44
+
45
+ mkdirChild.stderr?.on("data", (data) => {
46
+ stderr += data.toString();
47
+ });
48
+
49
+ mkdirChild.on("close", (code) => {
50
+ // Clear the active process reference
51
+ if (sessionId && sessions.has(sessionId)) {
52
+ const session = sessions.get(sessionId)!;
53
+ session.activeProcess = null;
54
+ }
55
+
56
+ if (code === 0) {
57
+ console.log(`[Server] Directory created successfully: ${path}`);
58
+ resolve({
59
+ exitCode: code || 0,
60
+ stderr,
61
+ stdout,
62
+ success: true,
63
+ });
64
+ } else {
65
+ console.error(
66
+ `[Server] Failed to create directory: ${path}, Exit code: ${code}`
67
+ );
68
+ resolve({
69
+ exitCode: code || 1,
70
+ stderr,
71
+ stdout,
72
+ success: false,
73
+ });
74
+ }
75
+ });
76
+
77
+ mkdirChild.on("error", (error) => {
78
+ // Clear the active process reference
79
+ if (sessionId && sessions.has(sessionId)) {
80
+ const session = sessions.get(sessionId)!;
81
+ session.activeProcess = null;
82
+ }
83
+
84
+ console.error(`[Server] Error creating directory: ${path}`, error);
85
+ reject(error);
86
+ });
87
+ });
88
+ }
89
+
90
+ export async function handleMkdirRequest(
91
+ sessions: Map<string, SessionData>,
92
+ req: Request,
93
+ corsHeaders: Record<string, string>
94
+ ): Promise<Response> {
95
+ try {
96
+ const body = (await req.json()) as MkdirRequest;
97
+ const { path, recursive = false, sessionId } = body;
98
+
99
+ if (!path || typeof path !== "string") {
100
+ return new Response(
101
+ JSON.stringify({
102
+ error: "Path is required and must be a string",
103
+ }),
104
+ {
105
+ headers: {
106
+ "Content-Type": "application/json",
107
+ ...corsHeaders,
108
+ },
109
+ status: 400,
110
+ }
111
+ );
112
+ }
113
+
114
+ // Basic safety check - prevent dangerous paths
115
+ const dangerousPatterns = [
116
+ /^\/$/, // Root directory
117
+ /^\/etc/, // System directories
118
+ /^\/var/, // System directories
119
+ /^\/usr/, // System directories
120
+ /^\/bin/, // System directories
121
+ /^\/sbin/, // System directories
122
+ /^\/boot/, // System directories
123
+ /^\/dev/, // System directories
124
+ /^\/proc/, // System directories
125
+ /^\/sys/, // System directories
126
+ /^\/tmp\/\.\./, // Path traversal attempts
127
+ /\.\./, // Path traversal attempts
128
+ ];
129
+
130
+ if (dangerousPatterns.some((pattern) => pattern.test(path))) {
131
+ return new Response(
132
+ JSON.stringify({
133
+ error: "Dangerous path not allowed",
134
+ }),
135
+ {
136
+ headers: {
137
+ "Content-Type": "application/json",
138
+ ...corsHeaders,
139
+ },
140
+ status: 400,
141
+ }
142
+ );
143
+ }
144
+
145
+ console.log(
146
+ `[Server] Creating directory: ${path} (recursive: ${recursive})`
147
+ );
148
+
149
+ const result = await executeMkdir(sessions, path, recursive, sessionId);
150
+
151
+ return new Response(
152
+ JSON.stringify({
153
+ exitCode: result.exitCode,
154
+ path,
155
+ recursive,
156
+ stderr: result.stderr,
157
+ stdout: result.stdout,
158
+ success: result.success,
159
+ timestamp: new Date().toISOString(),
160
+ }),
161
+ {
162
+ headers: {
163
+ "Content-Type": "application/json",
164
+ ...corsHeaders,
165
+ },
166
+ }
167
+ );
168
+ } catch (error) {
169
+ console.error("[Server] Error in handleMkdirRequest:", error);
170
+ return new Response(
171
+ JSON.stringify({
172
+ error: "Failed to create directory",
173
+ message: error instanceof Error ? error.message : "Unknown error",
174
+ }),
175
+ {
176
+ headers: {
177
+ "Content-Type": "application/json",
178
+ ...corsHeaders,
179
+ },
180
+ status: 500,
181
+ }
182
+ );
183
+ }
184
+ }
185
+
186
+
187
+ function executeWriteFile(
188
+ path: string,
189
+ content: string,
190
+ encoding: string,
191
+ sessionId?: string
192
+ ): Promise<{
193
+ success: boolean;
194
+ exitCode: number;
195
+ }> {
196
+ return new Promise((resolve, reject) => {
197
+ (async () => {
198
+ try {
199
+ // Ensure the directory exists
200
+ const dir = dirname(path);
201
+ if (dir !== ".") {
202
+ await mkdir(dir, { recursive: true });
203
+ }
204
+
205
+ // Write the file
206
+ await writeFile(path, content, {
207
+ encoding: encoding as BufferEncoding,
208
+ });
209
+
210
+ console.log(`[Server] File written successfully: ${path}`);
211
+ resolve({
212
+ exitCode: 0,
213
+ success: true,
214
+ });
215
+ } catch (error) {
216
+ console.error(`[Server] Error writing file: ${path}`, error);
217
+ reject(error);
218
+ }
219
+ })();
220
+ });
221
+ }
222
+
223
+ export async function handleWriteFileRequest(
224
+ req: Request,
225
+ corsHeaders: Record<string, string>
226
+ ): Promise<Response> {
227
+ try {
228
+ const body = (await req.json()) as WriteFileRequest;
229
+ const { path, content, encoding = "utf-8", sessionId } = body;
230
+
231
+ if (!path || typeof path !== "string") {
232
+ return new Response(
233
+ JSON.stringify({
234
+ error: "Path is required and must be a string",
235
+ }),
236
+ {
237
+ headers: {
238
+ "Content-Type": "application/json",
239
+ ...corsHeaders,
240
+ },
241
+ status: 400,
242
+ }
243
+ );
244
+ }
245
+
246
+ // Basic safety check - prevent dangerous paths
247
+ const dangerousPatterns = [
248
+ /^\/$/, // Root directory
249
+ /^\/etc/, // System directories
250
+ /^\/var/, // System directories
251
+ /^\/usr/, // System directories
252
+ /^\/bin/, // System directories
253
+ /^\/sbin/, // System directories
254
+ /^\/boot/, // System directories
255
+ /^\/dev/, // System directories
256
+ /^\/proc/, // System directories
257
+ /^\/sys/, // System directories
258
+ /^\/tmp\/\.\./, // Path traversal attempts
259
+ /\.\./, // Path traversal attempts
260
+ ];
261
+
262
+ if (dangerousPatterns.some((pattern) => pattern.test(path))) {
263
+ return new Response(
264
+ JSON.stringify({
265
+ error: "Dangerous path not allowed",
266
+ }),
267
+ {
268
+ headers: {
269
+ "Content-Type": "application/json",
270
+ ...corsHeaders,
271
+ },
272
+ status: 400,
273
+ }
274
+ );
275
+ }
276
+
277
+ console.log(
278
+ `[Server] Writing file: ${path} (content length: ${content.length})`
279
+ );
280
+
281
+ const result = await executeWriteFile(path, content, encoding, sessionId);
282
+
283
+ return new Response(
284
+ JSON.stringify({
285
+ exitCode: result.exitCode,
286
+ path,
287
+ success: result.success,
288
+ timestamp: new Date().toISOString(),
289
+ }),
290
+ {
291
+ headers: {
292
+ "Content-Type": "application/json",
293
+ ...corsHeaders,
294
+ },
295
+ }
296
+ );
297
+ } catch (error) {
298
+ console.error("[Server] Error in handleWriteFileRequest:", error);
299
+ return new Response(
300
+ JSON.stringify({
301
+ error: "Failed to write file",
302
+ message: error instanceof Error ? error.message : "Unknown error",
303
+ }),
304
+ {
305
+ headers: {
306
+ "Content-Type": "application/json",
307
+ ...corsHeaders,
308
+ },
309
+ status: 500,
310
+ }
311
+ );
312
+ }
313
+ }
314
+
315
+
316
+ function executeReadFile(
317
+ path: string,
318
+ encoding: string,
319
+ sessionId?: string
320
+ ): Promise<{
321
+ success: boolean;
322
+ exitCode: number;
323
+ content: string;
324
+ }> {
325
+ return new Promise((resolve, reject) => {
326
+ (async () => {
327
+ try {
328
+ // Read the file
329
+ const content = await readFile(path, {
330
+ encoding: encoding as BufferEncoding,
331
+ });
332
+
333
+ console.log(`[Server] File read successfully: ${path}`);
334
+ resolve({
335
+ content,
336
+ exitCode: 0,
337
+ success: true,
338
+ });
339
+ } catch (error) {
340
+ console.error(`[Server] Error reading file: ${path}`, error);
341
+ reject(error);
342
+ }
343
+ })();
344
+ });
345
+ }
346
+
347
+ export async function handleReadFileRequest(
348
+ req: Request,
349
+ corsHeaders: Record<string, string>
350
+ ): Promise<Response> {
351
+ try {
352
+ const body = (await req.json()) as ReadFileRequest;
353
+ const { path, encoding = "utf-8", sessionId } = body;
354
+
355
+ if (!path || typeof path !== "string") {
356
+ return new Response(
357
+ JSON.stringify({
358
+ error: "Path is required and must be a string",
359
+ }),
360
+ {
361
+ headers: {
362
+ "Content-Type": "application/json",
363
+ ...corsHeaders,
364
+ },
365
+ status: 400,
366
+ }
367
+ );
368
+ }
369
+
370
+ // Basic safety check - prevent dangerous paths
371
+ const dangerousPatterns = [
372
+ /^\/$/, // Root directory
373
+ /^\/etc/, // System directories
374
+ /^\/var/, // System directories
375
+ /^\/usr/, // System directories
376
+ /^\/bin/, // System directories
377
+ /^\/sbin/, // System directories
378
+ /^\/boot/, // System directories
379
+ /^\/dev/, // System directories
380
+ /^\/proc/, // System directories
381
+ /^\/sys/, // System directories
382
+ /^\/tmp\/\.\./, // Path traversal attempts
383
+ /\.\./, // Path traversal attempts
384
+ ];
385
+
386
+ if (dangerousPatterns.some((pattern) => pattern.test(path))) {
387
+ return new Response(
388
+ JSON.stringify({
389
+ error: "Dangerous path not allowed",
390
+ }),
391
+ {
392
+ headers: {
393
+ "Content-Type": "application/json",
394
+ ...corsHeaders,
395
+ },
396
+ status: 400,
397
+ }
398
+ );
399
+ }
400
+
401
+ console.log(`[Server] Reading file: ${path}`);
402
+
403
+ const result = await executeReadFile(path, encoding, sessionId);
404
+
405
+ return new Response(
406
+ JSON.stringify({
407
+ content: result.content,
408
+ exitCode: result.exitCode,
409
+ path,
410
+ success: result.success,
411
+ timestamp: new Date().toISOString(),
412
+ }),
413
+ {
414
+ headers: {
415
+ "Content-Type": "application/json",
416
+ ...corsHeaders,
417
+ },
418
+ }
419
+ );
420
+ } catch (error) {
421
+ console.error("[Server] Error in handleReadFileRequest:", error);
422
+ return new Response(
423
+ JSON.stringify({
424
+ error: "Failed to read file",
425
+ message: error instanceof Error ? error.message : "Unknown error",
426
+ }),
427
+ {
428
+ headers: {
429
+ "Content-Type": "application/json",
430
+ ...corsHeaders,
431
+ },
432
+ status: 500,
433
+ }
434
+ );
435
+ }
436
+ }
437
+
438
+
439
+ function executeDeleteFile(
440
+ path: string,
441
+ sessionId?: string
442
+ ): Promise<{
443
+ success: boolean;
444
+ exitCode: number;
445
+ }> {
446
+ return new Promise((resolve, reject) => {
447
+ (async () => {
448
+ try {
449
+ // Delete the file
450
+ await unlink(path);
451
+
452
+ console.log(`[Server] File deleted successfully: ${path}`);
453
+ resolve({
454
+ exitCode: 0,
455
+ success: true,
456
+ });
457
+ } catch (error) {
458
+ console.error(`[Server] Error deleting file: ${path}`, error);
459
+ reject(error);
460
+ }
461
+ })();
462
+ });
463
+ }
464
+
465
+ export async function handleDeleteFileRequest(
466
+ req: Request,
467
+ corsHeaders: Record<string, string>
468
+ ): Promise<Response> {
469
+ try {
470
+ const body = (await req.json()) as DeleteFileRequest;
471
+ const { path, sessionId } = body;
472
+
473
+ if (!path || typeof path !== "string") {
474
+ return new Response(
475
+ JSON.stringify({
476
+ error: "Path is required and must be a string",
477
+ }),
478
+ {
479
+ headers: {
480
+ "Content-Type": "application/json",
481
+ ...corsHeaders,
482
+ },
483
+ status: 400,
484
+ }
485
+ );
486
+ }
487
+
488
+ // Basic safety check - prevent dangerous paths
489
+ const dangerousPatterns = [
490
+ /^\/$/, // Root directory
491
+ /^\/etc/, // System directories
492
+ /^\/var/, // System directories
493
+ /^\/usr/, // System directories
494
+ /^\/bin/, // System directories
495
+ /^\/sbin/, // System directories
496
+ /^\/boot/, // System directories
497
+ /^\/dev/, // System directories
498
+ /^\/proc/, // System directories
499
+ /^\/sys/, // System directories
500
+ /^\/tmp\/\.\./, // Path traversal attempts
501
+ /\.\./, // Path traversal attempts
502
+ ];
503
+
504
+ if (dangerousPatterns.some((pattern) => pattern.test(path))) {
505
+ return new Response(
506
+ JSON.stringify({
507
+ error: "Dangerous path not allowed",
508
+ }),
509
+ {
510
+ headers: {
511
+ "Content-Type": "application/json",
512
+ ...corsHeaders,
513
+ },
514
+ status: 400,
515
+ }
516
+ );
517
+ }
518
+
519
+ console.log(`[Server] Deleting file: ${path}`);
520
+
521
+ const result = await executeDeleteFile(path, sessionId);
522
+
523
+ return new Response(
524
+ JSON.stringify({
525
+ exitCode: result.exitCode,
526
+ path,
527
+ success: result.success,
528
+ timestamp: new Date().toISOString(),
529
+ }),
530
+ {
531
+ headers: {
532
+ "Content-Type": "application/json",
533
+ ...corsHeaders,
534
+ },
535
+ }
536
+ );
537
+ } catch (error) {
538
+ console.error("[Server] Error in handleDeleteFileRequest:", error);
539
+ return new Response(
540
+ JSON.stringify({
541
+ error: "Failed to delete file",
542
+ message: error instanceof Error ? error.message : "Unknown error",
543
+ }),
544
+ {
545
+ headers: {
546
+ "Content-Type": "application/json",
547
+ ...corsHeaders,
548
+ },
549
+ status: 500,
550
+ }
551
+ );
552
+ }
553
+ }
554
+
555
+
556
+ function executeRenameFile(
557
+ oldPath: string,
558
+ newPath: string,
559
+ sessionId?: string
560
+ ): Promise<{
561
+ success: boolean;
562
+ exitCode: number;
563
+ }> {
564
+ return new Promise((resolve, reject) => {
565
+ (async () => {
566
+ try {
567
+ // Rename the file
568
+ await rename(oldPath, newPath);
569
+
570
+ console.log(
571
+ `[Server] File renamed successfully: ${oldPath} -> ${newPath}`
572
+ );
573
+ resolve({
574
+ exitCode: 0,
575
+ success: true,
576
+ });
577
+ } catch (error) {
578
+ console.error(
579
+ `[Server] Error renaming file: ${oldPath} -> ${newPath}`,
580
+ error
581
+ );
582
+ reject(error);
583
+ }
584
+ })();
585
+ });
586
+ }
587
+
588
+ export async function handleRenameFileRequest(
589
+ req: Request,
590
+ corsHeaders: Record<string, string>
591
+ ): Promise<Response> {
592
+ try {
593
+ const body = (await req.json()) as RenameFileRequest;
594
+ const { oldPath, newPath, sessionId } = body;
595
+
596
+ if (!oldPath || typeof oldPath !== "string") {
597
+ return new Response(
598
+ JSON.stringify({
599
+ error: "Old path is required and must be a string",
600
+ }),
601
+ {
602
+ headers: {
603
+ "Content-Type": "application/json",
604
+ ...corsHeaders,
605
+ },
606
+ status: 400,
607
+ }
608
+ );
609
+ }
610
+
611
+ if (!newPath || typeof newPath !== "string") {
612
+ return new Response(
613
+ JSON.stringify({
614
+ error: "New path is required and must be a string",
615
+ }),
616
+ {
617
+ headers: {
618
+ "Content-Type": "application/json",
619
+ ...corsHeaders,
620
+ },
621
+ status: 400,
622
+ }
623
+ );
624
+ }
625
+
626
+ // Basic safety check - prevent dangerous paths
627
+ const dangerousPatterns = [
628
+ /^\/$/, // Root directory
629
+ /^\/etc/, // System directories
630
+ /^\/var/, // System directories
631
+ /^\/usr/, // System directories
632
+ /^\/bin/, // System directories
633
+ /^\/sbin/, // System directories
634
+ /^\/boot/, // System directories
635
+ /^\/dev/, // System directories
636
+ /^\/proc/, // System directories
637
+ /^\/sys/, // System directories
638
+ /^\/tmp\/\.\./, // Path traversal attempts
639
+ /\.\./, // Path traversal attempts
640
+ ];
641
+
642
+ if (
643
+ dangerousPatterns.some(
644
+ (pattern) => pattern.test(oldPath) || pattern.test(newPath)
645
+ )
646
+ ) {
647
+ return new Response(
648
+ JSON.stringify({
649
+ error: "Dangerous path not allowed",
650
+ }),
651
+ {
652
+ headers: {
653
+ "Content-Type": "application/json",
654
+ ...corsHeaders,
655
+ },
656
+ status: 400,
657
+ }
658
+ );
659
+ }
660
+
661
+ console.log(`[Server] Renaming file: ${oldPath} -> ${newPath}`);
662
+
663
+ const result = await executeRenameFile(oldPath, newPath, sessionId);
664
+
665
+ return new Response(
666
+ JSON.stringify({
667
+ exitCode: result.exitCode,
668
+ newPath,
669
+ oldPath,
670
+ success: result.success,
671
+ timestamp: new Date().toISOString(),
672
+ }),
673
+ {
674
+ headers: {
675
+ "Content-Type": "application/json",
676
+ ...corsHeaders,
677
+ },
678
+ }
679
+ );
680
+ } catch (error) {
681
+ console.error("[Server] Error in handleRenameFileRequest:", error);
682
+ return new Response(
683
+ JSON.stringify({
684
+ error: "Failed to rename file",
685
+ message: error instanceof Error ? error.message : "Unknown error",
686
+ }),
687
+ {
688
+ headers: {
689
+ "Content-Type": "application/json",
690
+ ...corsHeaders,
691
+ },
692
+ status: 500,
693
+ }
694
+ );
695
+ }
696
+ }
697
+
698
+
699
+ function executeMoveFile(
700
+ sourcePath: string,
701
+ destinationPath: string,
702
+ sessionId?: string
703
+ ): Promise<{
704
+ success: boolean;
705
+ exitCode: number;
706
+ }> {
707
+ return new Promise((resolve, reject) => {
708
+ (async () => {
709
+ try {
710
+ // Move the file
711
+ await rename(sourcePath, destinationPath);
712
+
713
+ console.log(
714
+ `[Server] File moved successfully: ${sourcePath} -> ${destinationPath}`
715
+ );
716
+ resolve({
717
+ exitCode: 0,
718
+ success: true,
719
+ });
720
+ } catch (error) {
721
+ console.error(
722
+ `[Server] Error moving file: ${sourcePath} -> ${destinationPath}`,
723
+ error
724
+ );
725
+ reject(error);
726
+ }
727
+ })();
728
+ });
729
+ }
730
+
731
+ export async function handleMoveFileRequest(
732
+ req: Request,
733
+ corsHeaders: Record<string, string>
734
+ ): Promise<Response> {
735
+ try {
736
+ const body = (await req.json()) as MoveFileRequest;
737
+ const { sourcePath, destinationPath, sessionId } = body;
738
+
739
+ if (!sourcePath || typeof sourcePath !== "string") {
740
+ return new Response(
741
+ JSON.stringify({
742
+ error: "Source path is required and must be a string",
743
+ }),
744
+ {
745
+ headers: {
746
+ "Content-Type": "application/json",
747
+ ...corsHeaders,
748
+ },
749
+ status: 400,
750
+ }
751
+ );
752
+ }
753
+
754
+ if (!destinationPath || typeof destinationPath !== "string") {
755
+ return new Response(
756
+ JSON.stringify({
757
+ error: "Destination path is required and must be a string",
758
+ }),
759
+ {
760
+ headers: {
761
+ "Content-Type": "application/json",
762
+ ...corsHeaders,
763
+ },
764
+ status: 400,
765
+ }
766
+ );
767
+ }
768
+
769
+ // Basic safety check - prevent dangerous paths
770
+ const dangerousPatterns = [
771
+ /^\/$/, // Root directory
772
+ /^\/etc/, // System directories
773
+ /^\/var/, // System directories
774
+ /^\/usr/, // System directories
775
+ /^\/bin/, // System directories
776
+ /^\/sbin/, // System directories
777
+ /^\/boot/, // System directories
778
+ /^\/dev/, // System directories
779
+ /^\/proc/, // System directories
780
+ /^\/sys/, // System directories
781
+ /^\/tmp\/\.\./, // Path traversal attempts
782
+ /\.\./, // Path traversal attempts
783
+ ];
784
+
785
+ if (
786
+ dangerousPatterns.some(
787
+ (pattern) => pattern.test(sourcePath) || pattern.test(destinationPath)
788
+ )
789
+ ) {
790
+ return new Response(
791
+ JSON.stringify({
792
+ error: "Dangerous path not allowed",
793
+ }),
794
+ {
795
+ headers: {
796
+ "Content-Type": "application/json",
797
+ ...corsHeaders,
798
+ },
799
+ status: 400,
800
+ }
801
+ );
802
+ }
803
+
804
+ console.log(`[Server] Moving file: ${sourcePath} -> ${destinationPath}`);
805
+
806
+ const result = await executeMoveFile(
807
+ sourcePath,
808
+ destinationPath,
809
+ sessionId
810
+ );
811
+
812
+ return new Response(
813
+ JSON.stringify({
814
+ destinationPath,
815
+ exitCode: result.exitCode,
816
+ sourcePath,
817
+ success: result.success,
818
+ timestamp: new Date().toISOString(),
819
+ }),
820
+ {
821
+ headers: {
822
+ "Content-Type": "application/json",
823
+ ...corsHeaders,
824
+ },
825
+ }
826
+ );
827
+ } catch (error) {
828
+ console.error("[Server] Error in handleMoveFileRequest:", error);
829
+ return new Response(
830
+ JSON.stringify({
831
+ error: "Failed to move file",
832
+ message: error instanceof Error ? error.message : "Unknown error",
833
+ }),
834
+ {
835
+ headers: {
836
+ "Content-Type": "application/json",
837
+ ...corsHeaders,
838
+ },
839
+ status: 500,
840
+ }
841
+ );
842
+ }
843
+ }
844
+