@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 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
- * Only allows non-system ports to prevent conflicts and security issues
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, 8787].includes(port)) return false;
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.1";
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 or restricted port: ${port}. Ports must be in range 1024-65535 and not reserved.`);
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 between 1024-65535 and not reserved.`);
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
- return crypto.subtle.timingSafeEqual(a, b);
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 between 1024-65535 and not reserved.`);
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.`);