@cloudflare/sandbox 0.7.1 → 0.7.2
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/Dockerfile +30 -0
- package/dist/index.js +16 -9
- package/dist/index.js.map +1 -1
- package/dist/sandbox-CgjQQZGw.d.ts.map +1 -1
- package/package.json +2 -2
package/Dockerfile
CHANGED
|
@@ -201,3 +201,33 @@ ENV TYPESCRIPT_POOL_MIN_SIZE=3
|
|
|
201
201
|
EXPOSE 4096
|
|
202
202
|
|
|
203
203
|
ENTRYPOINT ["/container-server/sandbox"]
|
|
204
|
+
|
|
205
|
+
# ============================================================================
|
|
206
|
+
# Stage 5d: Musl image - Alpine-based with musl-linked binary
|
|
207
|
+
# ============================================================================
|
|
208
|
+
FROM alpine:3.21 AS musl
|
|
209
|
+
|
|
210
|
+
ARG SANDBOX_VERSION=unknown
|
|
211
|
+
|
|
212
|
+
ENV SANDBOX_VERSION=${SANDBOX_VERSION}
|
|
213
|
+
|
|
214
|
+
RUN apk add --no-cache bash file git ca-certificates curl libstdc++ libgcc s3fs-fuse fuse
|
|
215
|
+
|
|
216
|
+
RUN sed -i 's/#user_allow_other/user_allow_other/' /etc/fuse.conf
|
|
217
|
+
|
|
218
|
+
COPY --from=builder /app/packages/sandbox-container/dist/sandbox-musl /container-server/sandbox
|
|
219
|
+
|
|
220
|
+
WORKDIR /container-server
|
|
221
|
+
|
|
222
|
+
COPY --from=builder /app/packages/sandbox-container/dist/runtime/executors/javascript/node_executor.js ./dist/runtime/executors/javascript/
|
|
223
|
+
COPY --from=builder /app/packages/sandbox-container/dist/index.js ./dist/
|
|
224
|
+
|
|
225
|
+
RUN mkdir -p /workspace
|
|
226
|
+
|
|
227
|
+
EXPOSE 3000
|
|
228
|
+
|
|
229
|
+
ENV PYTHON_POOL_MIN_SIZE=0
|
|
230
|
+
ENV JAVASCRIPT_POOL_MIN_SIZE=0
|
|
231
|
+
ENV TYPESCRIPT_POOL_MIN_SIZE=0
|
|
232
|
+
|
|
233
|
+
ENTRYPOINT ["/container-server/sandbox"]
|
package/dist/index.js
CHANGED
|
@@ -2089,13 +2089,16 @@ var SecurityError = class extends Error {
|
|
|
2089
2089
|
}
|
|
2090
2090
|
};
|
|
2091
2091
|
/**
|
|
2092
|
-
* Validates port numbers for sandbox services
|
|
2093
|
-
*
|
|
2092
|
+
* Validates port numbers for sandbox services.
|
|
2093
|
+
*
|
|
2094
|
+
* Rules:
|
|
2095
|
+
* - Range: 1024-65535 (privileged ports require root, which containers don't have)
|
|
2096
|
+
* - Reserved: 3000 (sandbox control plane)
|
|
2094
2097
|
*/
|
|
2095
2098
|
function validatePort(port) {
|
|
2096
2099
|
if (!Number.isInteger(port)) return false;
|
|
2097
2100
|
if (port < 1024 || port > 65535) return false;
|
|
2098
|
-
if ([3e3
|
|
2101
|
+
if ([3e3].includes(port)) return false;
|
|
2099
2102
|
return true;
|
|
2100
2103
|
}
|
|
2101
2104
|
/**
|
|
@@ -2564,7 +2567,7 @@ function buildS3fsSource(bucket, prefix) {
|
|
|
2564
2567
|
* This file is auto-updated by .github/changeset-version.ts during releases
|
|
2565
2568
|
* DO NOT EDIT MANUALLY - Changes will be overwritten on the next version bump
|
|
2566
2569
|
*/
|
|
2567
|
-
const SDK_VERSION = "0.7.
|
|
2570
|
+
const SDK_VERSION = "0.7.2";
|
|
2568
2571
|
|
|
2569
2572
|
//#endregion
|
|
2570
2573
|
//#region src/sandbox.ts
|
|
@@ -2603,7 +2606,7 @@ function enhanceSession(stub, rpcSession) {
|
|
|
2603
2606
|
}
|
|
2604
2607
|
function connect(stub) {
|
|
2605
2608
|
return async (request, port) => {
|
|
2606
|
-
if (!validatePort(port)) throw new SecurityError(`Invalid
|
|
2609
|
+
if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`);
|
|
2607
2610
|
const portSwitchedRequest = switchPort(request, port);
|
|
2608
2611
|
return await stub.fetch(portSwitchedRequest);
|
|
2609
2612
|
};
|
|
@@ -3668,6 +3671,7 @@ var Sandbox = class extends Container {
|
|
|
3668
3671
|
* // url: https://8080-sandbox-id-my-token-v1.example.com
|
|
3669
3672
|
*/
|
|
3670
3673
|
async exposePort(port, options) {
|
|
3674
|
+
if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`);
|
|
3671
3675
|
if (options.hostname.endsWith(".workers.dev")) throw new CustomDomainRequiredError({
|
|
3672
3676
|
code: ErrorCode.CUSTOM_DOMAIN_REQUIRED,
|
|
3673
3677
|
message: `Port exposure requires a custom domain. .workers.dev domains do not support wildcard subdomains required for port proxying.`,
|
|
@@ -3695,7 +3699,7 @@ var Sandbox = class extends Container {
|
|
|
3695
3699
|
};
|
|
3696
3700
|
}
|
|
3697
3701
|
async unexposePort(port) {
|
|
3698
|
-
if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be
|
|
3702
|
+
if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`);
|
|
3699
3703
|
const sessionId = await this.ensureDefaultSession();
|
|
3700
3704
|
await this.client.ports.unexposePort(port, sessionId);
|
|
3701
3705
|
const tokens = await this.ctx.storage.get("portTokens") || {};
|
|
@@ -3735,11 +3739,14 @@ var Sandbox = class extends Container {
|
|
|
3735
3739
|
this.logger.error("Port is exposed but has no token - bug detected", void 0, { port });
|
|
3736
3740
|
return false;
|
|
3737
3741
|
}
|
|
3738
|
-
if (storedToken.length !== token.length) return false;
|
|
3739
3742
|
const encoder = new TextEncoder();
|
|
3740
3743
|
const a = encoder.encode(storedToken);
|
|
3741
3744
|
const b = encoder.encode(token);
|
|
3742
|
-
|
|
3745
|
+
try {
|
|
3746
|
+
return crypto.subtle.timingSafeEqual(a, b);
|
|
3747
|
+
} catch {
|
|
3748
|
+
return false;
|
|
3749
|
+
}
|
|
3743
3750
|
}
|
|
3744
3751
|
validateCustomToken(token) {
|
|
3745
3752
|
if (token.length === 0) throw new SecurityError(`Custom token cannot be empty.`);
|
|
@@ -3752,7 +3759,7 @@ var Sandbox = class extends Container {
|
|
|
3752
3759
|
return btoa(String.fromCharCode(...array)).replace(/\+/g, "_").replace(/\//g, "_").replace(/=/g, "").toLowerCase();
|
|
3753
3760
|
}
|
|
3754
3761
|
constructPreviewUrl(port, sandboxId, hostname, token) {
|
|
3755
|
-
if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be
|
|
3762
|
+
if (!validatePort(port)) throw new SecurityError(`Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`);
|
|
3756
3763
|
const effectiveId = this.sandboxName || sandboxId;
|
|
3757
3764
|
const hasUppercase = /[A-Z]/.test(effectiveId);
|
|
3758
3765
|
if (!this.normalizeId && hasUppercase) throw new SecurityError(`Preview URLs require lowercase sandbox IDs. Your ID "${effectiveId}" contains uppercase letters.\n\nTo fix this:\n1. Create a new sandbox with: getSandbox(ns, "${effectiveId}", { normalizeId: true })\n2. This will create a sandbox with ID: "${effectiveId.toLowerCase()}"\n\nNote: Due to DNS case-insensitivity, IDs with uppercase letters cannot be used with preview URLs.`);
|