@cloudflare/sandbox 0.4.12 → 0.4.15
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.
- package/.turbo/turbo-build.log +13 -47
- package/CHANGELOG.md +46 -16
- package/Dockerfile +78 -31
- package/README.md +9 -2
- package/dist/index.d.ts +1889 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3144 -65
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/clients/base-client.ts +39 -24
- package/src/clients/command-client.ts +8 -8
- package/src/clients/file-client.ts +31 -26
- package/src/clients/git-client.ts +3 -4
- package/src/clients/index.ts +12 -16
- package/src/clients/interpreter-client.ts +51 -47
- package/src/clients/port-client.ts +10 -10
- package/src/clients/process-client.ts +11 -8
- package/src/clients/sandbox-client.ts +2 -4
- package/src/clients/types.ts +6 -2
- package/src/clients/utility-client.ts +10 -6
- package/src/errors/adapter.ts +90 -32
- package/src/errors/classes.ts +189 -64
- package/src/errors/index.ts +9 -5
- package/src/file-stream.ts +11 -6
- package/src/index.ts +22 -15
- package/src/interpreter.ts +50 -41
- package/src/request-handler.ts +24 -21
- package/src/sandbox.ts +339 -149
- package/src/security.ts +21 -6
- package/src/sse-parser.ts +4 -3
- package/src/version.ts +1 -1
- package/tests/base-client.test.ts +116 -80
- package/tests/command-client.test.ts +149 -112
- package/tests/file-client.test.ts +309 -197
- package/tests/file-stream.test.ts +24 -20
- package/tests/get-sandbox.test.ts +10 -10
- package/tests/git-client.test.ts +188 -101
- package/tests/port-client.test.ts +100 -108
- package/tests/process-client.test.ts +204 -179
- package/tests/request-handler.test.ts +117 -65
- package/tests/sandbox.test.ts +219 -67
- package/tests/sse-parser.test.ts +17 -16
- package/tests/utility-client.test.ts +79 -72
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +6 -6
- package/dist/chunk-BFVUNTP4.js +0 -104
- package/dist/chunk-BFVUNTP4.js.map +0 -1
- package/dist/chunk-EKSWCBCA.js +0 -86
- package/dist/chunk-EKSWCBCA.js.map +0 -1
- package/dist/chunk-JXZMAU2C.js +0 -559
- package/dist/chunk-JXZMAU2C.js.map +0 -1
- package/dist/chunk-UJ3TV4M6.js +0 -7
- package/dist/chunk-UJ3TV4M6.js.map +0 -1
- package/dist/chunk-YE265ASX.js +0 -2484
- package/dist/chunk-YE265ASX.js.map +0 -1
- package/dist/chunk-Z532A7QC.js +0 -78
- package/dist/chunk-Z532A7QC.js.map +0 -1
- package/dist/file-stream.d.ts +0 -43
- package/dist/file-stream.js +0 -9
- package/dist/file-stream.js.map +0 -1
- package/dist/interpreter.d.ts +0 -33
- package/dist/interpreter.js +0 -8
- package/dist/interpreter.js.map +0 -1
- package/dist/request-handler.d.ts +0 -18
- package/dist/request-handler.js +0 -13
- package/dist/request-handler.js.map +0 -1
- package/dist/sandbox-CLZWpfGc.d.ts +0 -613
- package/dist/sandbox.d.ts +0 -4
- package/dist/sandbox.js +0 -13
- package/dist/sandbox.js.map +0 -1
- package/dist/security.d.ts +0 -31
- package/dist/security.js +0 -13
- package/dist/security.js.map +0 -1
- package/dist/sse-parser.d.ts +0 -28
- package/dist/sse-parser.js +0 -11
- package/dist/sse-parser.js.map +0 -1
- package/dist/version.d.ts +0 -8
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
|
@@ -7,7 +7,7 @@ vi.mock('../src/sandbox', () => {
|
|
|
7
7
|
const mockFn = vi.fn();
|
|
8
8
|
return {
|
|
9
9
|
getSandbox: mockFn,
|
|
10
|
-
Sandbox: vi.fn()
|
|
10
|
+
Sandbox: vi.fn()
|
|
11
11
|
};
|
|
12
12
|
});
|
|
13
13
|
|
|
@@ -25,11 +25,11 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
25
25
|
mockSandbox = {
|
|
26
26
|
validatePortToken: vi.fn().mockResolvedValue(true),
|
|
27
27
|
fetch: vi.fn().mockResolvedValue(new Response('WebSocket response')),
|
|
28
|
-
containerFetch: vi.fn().mockResolvedValue(new Response('HTTP response'))
|
|
28
|
+
containerFetch: vi.fn().mockResolvedValue(new Response('HTTP response'))
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
mockEnv = {
|
|
32
|
-
Sandbox: {} as any
|
|
32
|
+
Sandbox: {} as any
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
vi.mocked(getSandbox).mockReturnValue(mockSandbox as Sandbox);
|
|
@@ -37,12 +37,15 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
37
37
|
|
|
38
38
|
describe('WebSocket detection and routing', () => {
|
|
39
39
|
it('should detect WebSocket upgrade header (case-insensitive)', async () => {
|
|
40
|
-
const request = new Request(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
const request = new Request(
|
|
41
|
+
'https://8080-sandbox-token12345678901.example.com/ws',
|
|
42
|
+
{
|
|
43
|
+
headers: {
|
|
44
|
+
Upgrade: 'websocket',
|
|
45
|
+
Connection: 'Upgrade'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
);
|
|
46
49
|
|
|
47
50
|
await proxyToSandbox(request, mockEnv);
|
|
48
51
|
|
|
@@ -52,32 +55,40 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
52
55
|
});
|
|
53
56
|
|
|
54
57
|
it('should set cf-container-target-port header for WebSocket', async () => {
|
|
55
|
-
const request = new Request(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const request = new Request(
|
|
59
|
+
'https://8080-sandbox-token12345678901.example.com/ws',
|
|
60
|
+
{
|
|
61
|
+
headers: {
|
|
62
|
+
Upgrade: 'websocket'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
);
|
|
60
66
|
|
|
61
67
|
await proxyToSandbox(request, mockEnv);
|
|
62
68
|
|
|
63
69
|
expect(mockSandbox.fetch).toHaveBeenCalledTimes(1);
|
|
64
|
-
const fetchCall = vi.mocked(mockSandbox.fetch as any).mock
|
|
70
|
+
const fetchCall = vi.mocked(mockSandbox.fetch as any).mock
|
|
71
|
+
.calls[0][0] as Request;
|
|
65
72
|
expect(fetchCall.headers.get('cf-container-target-port')).toBe('8080');
|
|
66
73
|
});
|
|
67
74
|
|
|
68
75
|
it('should preserve original headers for WebSocket', async () => {
|
|
69
|
-
const request = new Request(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
const request = new Request(
|
|
77
|
+
'https://8080-sandbox-token12345678901.example.com/ws',
|
|
78
|
+
{
|
|
79
|
+
headers: {
|
|
80
|
+
Upgrade: 'websocket',
|
|
81
|
+
'Sec-WebSocket-Key': 'test-key-123',
|
|
82
|
+
'Sec-WebSocket-Version': '13',
|
|
83
|
+
'User-Agent': 'test-client'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
);
|
|
77
87
|
|
|
78
88
|
await proxyToSandbox(request, mockEnv);
|
|
79
89
|
|
|
80
|
-
const fetchCall = vi.mocked(mockSandbox.fetch as any).mock
|
|
90
|
+
const fetchCall = vi.mocked(mockSandbox.fetch as any).mock
|
|
91
|
+
.calls[0][0] as Request;
|
|
81
92
|
expect(fetchCall.headers.get('Upgrade')).toBe('websocket');
|
|
82
93
|
expect(fetchCall.headers.get('Sec-WebSocket-Key')).toBe('test-key-123');
|
|
83
94
|
expect(fetchCall.headers.get('Sec-WebSocket-Version')).toBe('13');
|
|
@@ -87,9 +98,12 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
87
98
|
|
|
88
99
|
describe('HTTP routing (existing behavior)', () => {
|
|
89
100
|
it('should route HTTP requests through containerFetch', async () => {
|
|
90
|
-
const request = new Request(
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
const request = new Request(
|
|
102
|
+
'https://8080-sandbox-token12345678901.example.com/api/data',
|
|
103
|
+
{
|
|
104
|
+
method: 'GET'
|
|
105
|
+
}
|
|
106
|
+
);
|
|
93
107
|
|
|
94
108
|
await proxyToSandbox(request, mockEnv);
|
|
95
109
|
|
|
@@ -99,13 +113,16 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
99
113
|
});
|
|
100
114
|
|
|
101
115
|
it('should route POST requests through containerFetch', async () => {
|
|
102
|
-
const request = new Request(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
const request = new Request(
|
|
117
|
+
'https://8080-sandbox-token12345678901.example.com/api/data',
|
|
118
|
+
{
|
|
119
|
+
method: 'POST',
|
|
120
|
+
body: JSON.stringify({ data: 'test' }),
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json'
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
109
126
|
|
|
110
127
|
await proxyToSandbox(request, mockEnv);
|
|
111
128
|
|
|
@@ -114,11 +131,14 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
114
131
|
});
|
|
115
132
|
|
|
116
133
|
it('should not detect SSE as WebSocket', async () => {
|
|
117
|
-
const request = new Request(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
const request = new Request(
|
|
135
|
+
'https://8080-sandbox-token12345678901.example.com/events',
|
|
136
|
+
{
|
|
137
|
+
headers: {
|
|
138
|
+
Accept: 'text/event-stream'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
);
|
|
122
142
|
|
|
123
143
|
await proxyToSandbox(request, mockEnv);
|
|
124
144
|
|
|
@@ -130,26 +150,40 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
130
150
|
|
|
131
151
|
describe('Token validation', () => {
|
|
132
152
|
it('should validate token for both WebSocket and HTTP requests', async () => {
|
|
133
|
-
const wsRequest = new Request(
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
const wsRequest = new Request(
|
|
154
|
+
'https://8080-sandbox-token12345678901.example.com/ws',
|
|
155
|
+
{
|
|
156
|
+
headers: { Upgrade: 'websocket' }
|
|
157
|
+
}
|
|
158
|
+
);
|
|
136
159
|
|
|
137
160
|
await proxyToSandbox(wsRequest, mockEnv);
|
|
138
|
-
expect(mockSandbox.validatePortToken).toHaveBeenCalledWith(
|
|
161
|
+
expect(mockSandbox.validatePortToken).toHaveBeenCalledWith(
|
|
162
|
+
8080,
|
|
163
|
+
'token12345678901'
|
|
164
|
+
);
|
|
139
165
|
|
|
140
166
|
vi.clearAllMocks();
|
|
141
167
|
|
|
142
|
-
const httpRequest = new Request(
|
|
168
|
+
const httpRequest = new Request(
|
|
169
|
+
'https://8080-sandbox-token12345678901.example.com/api'
|
|
170
|
+
);
|
|
143
171
|
await proxyToSandbox(httpRequest, mockEnv);
|
|
144
|
-
expect(mockSandbox.validatePortToken).toHaveBeenCalledWith(
|
|
172
|
+
expect(mockSandbox.validatePortToken).toHaveBeenCalledWith(
|
|
173
|
+
8080,
|
|
174
|
+
'token12345678901'
|
|
175
|
+
);
|
|
145
176
|
});
|
|
146
177
|
|
|
147
178
|
it('should reject requests with invalid token', async () => {
|
|
148
179
|
vi.mocked(mockSandbox.validatePortToken as any).mockResolvedValue(false);
|
|
149
180
|
|
|
150
|
-
const request = new Request(
|
|
151
|
-
|
|
152
|
-
|
|
181
|
+
const request = new Request(
|
|
182
|
+
'https://8080-sandbox-invalidtoken1234.example.com/ws',
|
|
183
|
+
{
|
|
184
|
+
headers: { Upgrade: 'websocket' }
|
|
185
|
+
}
|
|
186
|
+
);
|
|
153
187
|
|
|
154
188
|
const response = await proxyToSandbox(request, mockEnv);
|
|
155
189
|
|
|
@@ -159,15 +193,18 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
159
193
|
const body = await response?.json();
|
|
160
194
|
expect(body).toMatchObject({
|
|
161
195
|
error: 'Access denied: Invalid token or port not exposed',
|
|
162
|
-
code: 'INVALID_TOKEN'
|
|
196
|
+
code: 'INVALID_TOKEN'
|
|
163
197
|
});
|
|
164
198
|
});
|
|
165
199
|
|
|
166
200
|
it('should reject reserved port 3000', async () => {
|
|
167
201
|
// Port 3000 is reserved as control plane port and rejected by validatePort()
|
|
168
|
-
const request = new Request(
|
|
169
|
-
|
|
170
|
-
|
|
202
|
+
const request = new Request(
|
|
203
|
+
'https://3000-sandbox-anytoken12345678.example.com/status',
|
|
204
|
+
{
|
|
205
|
+
method: 'GET'
|
|
206
|
+
}
|
|
207
|
+
);
|
|
171
208
|
|
|
172
209
|
const response = await proxyToSandbox(request, mockEnv);
|
|
173
210
|
|
|
@@ -180,13 +217,19 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
180
217
|
|
|
181
218
|
describe('Port routing', () => {
|
|
182
219
|
it('should route to correct port from subdomain', async () => {
|
|
183
|
-
const request = new Request(
|
|
184
|
-
|
|
185
|
-
|
|
220
|
+
const request = new Request(
|
|
221
|
+
'https://9000-sandbox-token12345678901.example.com/api',
|
|
222
|
+
{
|
|
223
|
+
method: 'GET'
|
|
224
|
+
}
|
|
225
|
+
);
|
|
186
226
|
|
|
187
227
|
await proxyToSandbox(request, mockEnv);
|
|
188
228
|
|
|
189
|
-
expect(mockSandbox.validatePortToken).toHaveBeenCalledWith(
|
|
229
|
+
expect(mockSandbox.validatePortToken).toHaveBeenCalledWith(
|
|
230
|
+
9000,
|
|
231
|
+
'token12345678901'
|
|
232
|
+
);
|
|
190
233
|
});
|
|
191
234
|
});
|
|
192
235
|
|
|
@@ -212,13 +255,18 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
212
255
|
|
|
213
256
|
describe('Error handling', () => {
|
|
214
257
|
it('should handle errors during WebSocket routing', async () => {
|
|
215
|
-
(mockSandbox.fetch as any).mockImplementation(() =>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
258
|
+
(mockSandbox.fetch as any).mockImplementation(() =>
|
|
259
|
+
Promise.reject(new Error('Connection failed'))
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const request = new Request(
|
|
263
|
+
'https://8080-sandbox-token12345678901.example.com/ws',
|
|
264
|
+
{
|
|
265
|
+
headers: {
|
|
266
|
+
Upgrade: 'websocket'
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
);
|
|
222
270
|
|
|
223
271
|
const response = await proxyToSandbox(request, mockEnv);
|
|
224
272
|
|
|
@@ -228,9 +276,13 @@ describe('proxyToSandbox - WebSocket Support', () => {
|
|
|
228
276
|
});
|
|
229
277
|
|
|
230
278
|
it('should handle errors during HTTP routing', async () => {
|
|
231
|
-
(mockSandbox.containerFetch as any).mockImplementation(() =>
|
|
279
|
+
(mockSandbox.containerFetch as any).mockImplementation(() =>
|
|
280
|
+
Promise.reject(new Error('Service error'))
|
|
281
|
+
);
|
|
232
282
|
|
|
233
|
-
const request = new Request(
|
|
283
|
+
const request = new Request(
|
|
284
|
+
'https://8080-sandbox-token12345678901.example.com/api'
|
|
285
|
+
);
|
|
234
286
|
|
|
235
287
|
const response = await proxyToSandbox(request, mockEnv);
|
|
236
288
|
|