Add Java Spring Boot web service sandbox example#755
Conversation
PR Review: Java Spring Boot Web Service Sandbox ExampleOverall, this is a well-structured example that demonstrates the CubeSandbox snapshot/fork workflow with a realistic Spring Boot service. The code is clean, the tests cover the core behaviors, and the documentation is thorough. Below are the notable findings I'd recommend addressing. 🔴 High Severity1. Unvalidated user input → resource exhaustion (TaskState.java:60) 2. Filesystem path disclosure in API response (TaskState.java:61) 3. Docker image is 400–600 MB larger than necessary (Dockerfile)
🟡 Medium Severity4. 5. Stale temp files on crash (TaskState.java:118) 6. Hardcoded file list in demo upload (run_demo.py:100) 7. Documentation: incomplete manifest example (README.md) 8. Documentation: missing constraints (README.md) 🔵 Low Severity
✅ Positive Highlights
|
| this.objectMapper = objectMapper; | ||
| } | ||
|
|
||
| public synchronized Map<String, Object> createTask(Map<String, Object> request) { |
There was a problem hiding this comment.
All public methods (createTask, getTask) are synchronized on the instance, serializing even read-only lookups. Each call also reads/writes the entire JSON file from disk. This means two concurrent getTask() calls for different IDs still block each other. Consider using a ReentrantReadWriteLock for concurrent reads, or at minimum avoid serializing reads behind the same lock as writes.
Additionally, tasks are only ever appended — there's no eviction or TTL, so /tmp/cubesandbox-spring/state/tasks.json grows unboundedly. In a tmpfs-backed /tmp, this can silently exhaust memory over longer sessions.
| RUN mvn -B -DskipTests dependency:go-offline | ||
|
|
||
| COPY src ./src | ||
| RUN chown -R user:user /workspace && chmod -R 0777 /workspace |
There was a problem hiding this comment.
Container runs as root with chmod -R 0777 /workspace (world-writable). No USER directive is set. While sandbox isolation at the platform layer is the primary defense, running as root with 0777 eliminates defense-in-depth filesystem isolation inside the sandbox. Consider adding a USER user:user directive and using more restrictive permissions (e.g., 0755 on directories, 0644 on files).
| @RequestMapping("/api") | ||
| public class InfoController { | ||
| @GetMapping("/info") | ||
| public Map<String, Object> info() throws Exception { |
There was a problem hiding this comment.
throws Exception is a blanket checked-exception clause that propagates all checked exceptions to Spring's error handler. The only checked path here is InetAddress.getLocalHost() which throws UnknownHostException. Catching that specific exception and handling it gracefully (e.g., returning "hostname": "unknown") would be more idiomatic and make exception handling explicit.
| primary_deployment = correlated[-1] if correlated else None | ||
|
|
||
| incident_minutes = int((last_breach - first_breach).total_seconds() / 60) + METRIC_GRANULARITY_MINUTES | ||
| latency_delta = baseline["incident_peak_p95_latency_ms"] / baseline["baseline_p95_latency_ms"] |
There was a problem hiding this comment.
latency_delta and error_delta divide incident_peak_p95_latency_ms and incident_peak_error_rate by their baseline counterparts (baseline_p95_latency_ms, baseline_error_rate). If the pre-breach metrics window contains all-zero rows, these baselines could be zero, causing a ZeroDivisionError crash. Consider a safe-divide helper that guards against zero (e.g., lambda a, b: round(a / b, 2) if b else 0).
| this.objectMapper = objectMapper; | ||
| } | ||
|
|
||
| public synchronized Map<String, Object> createTask(Map<String, Object> request) { |
There was a problem hiding this comment.
All public methods (createTask, getTask) are synchronized on the instance, serializing even read-only lookups. Each call also reads/writes the entire JSON file from disk. This means two concurrent getTask() calls for different IDs still block each other. Consider using a ReentrantReadWriteLock for concurrent reads, or at minimum avoid serializing reads behind the same lock as writes.
Additionally, tasks are only ever appended — there's no eviction or TTL, so /tmp/cubesandbox-spring/state/tasks.json grows unboundedly. In a tmpfs-backed /tmp, this can silently exhaust memory over longer sessions.
| RUN mvn -B -DskipTests dependency:go-offline | ||
|
|
||
| COPY src ./src | ||
| RUN chown -R user:user /workspace && chmod -R 0777 /workspace |
There was a problem hiding this comment.
Container runs as root with chmod -R 0777 /workspace (world-writable). No USER directive is set. While sandbox isolation at the platform layer is the primary defense, running as root with 0777 eliminates defense-in-depth filesystem isolation inside the sandbox. Consider adding a USER user:user directive and using more restrictive permissions (e.g., 0755 on directories, 0644 on files).
| @RequestMapping("/api") | ||
| public class InfoController { | ||
| @GetMapping("/info") | ||
| public Map<String, Object> info() throws Exception { |
There was a problem hiding this comment.
throws Exception is a blanket checked-exception clause that propagates all checked exceptions to Spring's error handler. The only checked path here is InetAddress.getLocalHost() which throws UnknownHostException. Catching that specific exception and handling it gracefully (e.g., returning "hostname": "unknown") would be more idiomatic and make exception handling explicit.
| primary_deployment = correlated[-1] if correlated else None | ||
|
|
||
| incident_minutes = int((last_breach - first_breach).total_seconds() / 60) + METRIC_GRANULARITY_MINUTES | ||
| latency_delta = baseline["incident_peak_p95_latency_ms"] / baseline["baseline_p95_latency_ms"] |
There was a problem hiding this comment.
latency_delta and error_delta divide incident_peak_p95_latency_ms and incident_peak_error_rate by their baseline counterparts (baseline_p95_latency_ms, baseline_error_rate). If the pre-breach metrics window contains all-zero rows, these baselines could be zero, causing a ZeroDivisionError crash. Consider a safe-divide helper that guards against zero (e.g., lambda a, b: round(a / b, 2) if b else 0).
Add a Java 21 Spring Boot web service template example with health, info, task APIs, persisted sandbox state, offline Maven demo flow, and bilingual docs/index entries. Assisted-by: Codex:GPT-5 Signed-off-by: Sam-Hui-dot <19303092837@163.com>
Add Spring Boot tests, run the template as a non-root user, make task state bounded and read/write locked, and handle hostname lookup failures explicitly. Assisted-by: Codex:GPT-5 Signed-off-by: Sam-Hui-dot <19303092837@163.com>
f10e891 to
c65a5ad
Compare
|
Related to #645 |
Summary
This PR adds a Java 21 Spring Boot web service example for CubeSandbox.
It demonstrates:
/tmp/cubesandbox-spring/state/tasks.jsonChanges
examples/java-springboot-webGET /healthGET /api/infoPOST /api/tasksGET /api/tasks/{id}scripts/run_demo.pyfor the CubeSandbox demo flowVerification
scripts/run_demo.pygit diff --checkLive CubeSandbox demo script is included; local validation covered Docker build, offline Maven package, and Java tests.
Assisted-by: Codex:GPT-5