Skip to content

docs: add CodeBuddy integration guide and example#718

Open
toxitoxi wants to merge 4 commits into
TencentCloud:masterfrom
toxitoxi:docs/codebuddy-integration
Open

docs: add CodeBuddy integration guide and example#718
toxitoxi wants to merge 4 commits into
TencentCloud:masterfrom
toxitoxi:docs/codebuddy-integration

Conversation

@toxitoxi

@toxitoxi toxitoxi commented Jul 2, 2026

Copy link
Copy Markdown

Refs #644

Summary

  • Add bilingual CodeBuddy integration guides under docs/guide/integrations and docs/zh/guide/integrations.
  • Add a runnable CodeBuddy example under examples/codebuddy-integration.
  • Include a CubeSandbox template Dockerfile, template build script, .env.example, Python runner, and bilingual README.
  • Cover credential injection via Sandbox.create(envs=...), egress requirements, troubleshooting, and pause/resume state preservation.

Validation

  • python3 -m py_compile examples/codebuddy-integration/run_codebuddy.py examples/codebuddy-integration/env_utils.py examples/codebuddy-integration/test_run_codebuddy.py
  • cd examples/codebuddy-integration && python3 -m unittest test_run_codebuddy.py
  • bash -n examples/codebuddy-integration/build-template.sh
  • git diff --check
  • cd docs && npm run docs:build
  • Local CodeBuddy CLI headless sanity check
    • CodeBuddy CLI version: 2.103.3
    • --permission-mode bypassPermissions successfully read a temp README and ran python3 hello.py without modifying files
  • Docker template build on x86_64 Ubuntu 22.04 ECS
    • Built cubesandbox-codebuddy:verify with --platform linux/amd64
    • Build-time codebuddy --version: 2.114.2
  • Docker image runtime sanity check
    • cube-entrypoint started envd on port 49983
    • codebuddy --version: 2.114.2
    • node --version: v22.23.1
    • python3 --version: Python 3.10.12
    • rg --version: ripgrep 13.0.0

Not Yet Run

  • Push the image to a registry reachable by Cube nodes
  • cubemastercli tpl create-from-image
  • python run_codebuddy.py against a live CubeSandbox deployment
  • python run_codebuddy.py --pause-resume against a live CubeSandbox deployment

The available ECS can build and run the Docker image, but it does not currently have CubeSandbox deployed: no cubemastercli, no CubeAPI on port 3000, and no /dev/kvm/PVM runtime.

@toxitoxi toxitoxi requested a review from tinklone as a code owner July 2, 2026 17:35
&& codebuddy --version \
&& npm cache clean --force \
&& rm -rf /var/lib/apt/lists/*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing WORKDIR /workspace in this code snippet. The actual Dockerfile at examples/codebuddy-integration/template/Dockerfile:22 has a WORKDIR /workspace instruction between the RUN and ENTRYPOINT directives. A reader who copies this snippet verbatim would produce a functionally different Dockerfile — the shell's working directory would default to /, not /workspace, affecting where codebuddy -p and python3 commands execute.

&& npm cache clean --force \
&& rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["/usr/local/bin/cube-entrypoint.sh"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码片段中缺少 WORKDIR /workspace 实际的 Dockerfile (examples/codebuddy-integration/template/Dockerfile:22) 在 RUN 和 ENTRYPOINT 之间有一行 WORKDIR /workspace。按此片段逐字复制构建出的镜像行为不同——shell 默认工作目录将是 / 而非 /workspace,会影响 codebuddy -ppython3 命令的执行路径。

printf '%s\n' "${create_output}"

if [[ "${WATCH_JOB:-0}" == "1" ]]; then
job_id="$(printf '%s\n' "${create_output}" | sed -n 's/.*job_id[=: ]*\([^ ]*\).*/\1/p' | tail -n 1)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragile job_id extraction from unstructured output. This regex attempts to parse cubemastercli tpl create-from-image output to extract a job ID. Any change to that tool's output format (whitespace, punctuation, ordering) would silently produce an empty job_id, making WATCH_JOB=1 a no-op. Consider requesting structured output (JSON/YAML) if cubemastercli supports it, or at minimum print the raw create_output alongside the failure message so users can debug.

python3 \
python3-pip \
ripgrep \
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider verifying the NodeSource setup script. curl -fsSL ... | bash - pipes the setup script directly into bash without checksum or signature verification. A compromise of the NodeSource CDN or a DNS hijack during build could execute arbitrary code. Other images in this repo (e.g. the base image) use pinned tarballs with GPG verification. Consider downloading the script first and verifying a checksum before execution, or pinning the tarball URL with GPG verification using the NodeSource release keys.

@cubesandboxbot

cubesandboxbot Bot commented Jul 2, 2026

Copy link
Copy Markdown

PR Review: CodeBuddy Integration Guide and Example (#718)

Well-structured PR with thorough validation steps. The code uses modern practices (shlex.quote(), set -euo pipefail, single-layer Dockerfile RUN). Below are the noteworthy issues found.

Documentation

  • Missing WORKDIR in guide's Dockerfile snippet (docs/guide/integrations/codebuddy.md:91, docs/zh/guide/integrations/codebuddy.md:91) — The "Template image" code block omits WORKDIR /workspace that exists in the actual Dockerfile. Readers copying the snippet verbatim get a functionally different image (default working directory / instead of /workspace).

  • Inconsistent working directory in integration steps (docs/guide/integrations/codebuddy.md:59-61, docs/zh/guide/integrations/codebuddy.md:52-54) — Steps 1-4 use repo-root-relative paths (bash examples/codebuddy-integration/build-template.sh) but steps 5-6 use bare python run_codebuddy.py. A user following sequentially from the repo root gets a "file not found" error. Either cd examples/codebuddy-integration first, or use the full repo-relative path.

Code Quality

  • Fragile job_id extraction in build-template.sh:40 — The regex parsing cubemastercli unstructured output for the job ID will silently fail if the output format changes. If cubemastercli supports structured output (JSON/YAML), prefer that; otherwise print the raw output on failure so users can debug.

Security (Low Severity)

  • curl | bash without verification in template/Dockerfile:15 — The NodeSource setup script is piped directly into bash without checksum or signature verification. Consider downloading, verifying, then executing — other images in this repo use pinned tarballs with GPG verification as precedent.

No Action Needed (Noted as Correct)

  • Credential injection via Sandbox.create(envs={...}) is the correct pattern; the .env.example is in .gitignore; bypassPermissions is adequately flagged as a demo-only concern across all four documentation files.
  • shlex.quote() is used consistently for shell command construction — no injection vectors.
  • English and Chinese documentation are in sync with matching sections and accurate translations.

&& codebuddy --version \
&& npm cache clean --force \
&& rm -rf /var/lib/apt/lists/*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing WORKDIR /workspace in this code snippet. The actual Dockerfile at examples/codebuddy-integration/template/Dockerfile:22 has a WORKDIR /workspace instruction between the RUN and ENTRYPOINT directives. A reader who copies this snippet verbatim would produce a functionally different Dockerfile — the shell's working directory would default to /, not /workspace, affecting where codebuddy -p and python3 commands execute.

&& npm cache clean --force \
&& rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["/usr/local/bin/cube-entrypoint.sh"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码片段中缺少 WORKDIR /workspace 实际的 Dockerfile (examples/codebuddy-integration/template/Dockerfile:22) 在 RUN 和 ENTRYPOINT 之间有一行 WORKDIR /workspace。按此片段逐字复制构建出的镜像行为不同——shell 默认工作目录将是 / 而非 /workspace,会影响 codebuddy -ppython3 命令的执行路径。

printf '%s\n' "${create_output}"

if [[ "${WATCH_JOB:-0}" == "1" ]]; then
job_id="$(printf '%s\n' "${create_output}" | sed -n 's/.*job_id[=: ]*\([^ ]*\).*/\1/p' | tail -n 1)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragile job_id extraction from unstructured output. This regex attempts to parse cubemastercli tpl create-from-image output to extract a job ID. Any change to that tool's output format (whitespace, punctuation, ordering) would silently produce an empty job_id, making WATCH_JOB=1 a no-op. Consider requesting structured output (JSON/YAML) if cubemastercli supports it, or at minimum print the raw create_output alongside the failure message so users can debug.

python3 \
python3-pip \
ripgrep \
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider verifying the NodeSource setup script. curl -fsSL ... | bash - pipes the setup script directly into bash without checksum or signature verification. A compromise of the NodeSource CDN or a DNS hijack during build could execute arbitrary code. Other images in this repo use pinned tarballs with GPG verification. Consider downloading the script first and verifying a checksum before execution, or pinning the tarball URL with GPG verification using the NodeSource release keys.

"""


def sandbox_env(config_dir: str) -> Dict[str, str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hidden coupling: sandbox_env() reads CODEBUDDY_API_KEY directly from os.environ (line 78), which means it silently depends on require_env() having been called first. If a caller (or future refactor) calls this before the requirement check, it will raise a KeyError instead of the friendly "Missing required environment variables" message.

For example code that readers will learn from, it would be better to pass the API key as an explicit parameter:

def sandbox_env(api_key: str, config_dir: str) -> Dict[str, str]:

This makes the dependency explicit and makes the function independently testable.

)
parser.add_argument(
"--timeout",
type=int,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validation gap: positive_int() is only applied to the environment-variable fallback default (line 184), not to CLI-provided values. Since type=int is used here, passing --timeout -5 or --timeout 0 on the command line bypasses the validation entirely. Consider using type=positive_int (with the env-var fallback logic moved into the default) so CLI values are also validated.

# Move positive_int to type so it validates both CLI and env-default
parser.add_argument(
    "--timeout",
    type=positive_int,
    default=os.environ.get("CUBE_SANDBOX_TIMEOUT", "600"),
    help="Sandbox timeout in seconds.",
)

And adjust positive_int to accept a default differently, or handle the string parsing within its own logic.

ENV DISABLE_AUTOUPDATER=1 \
CODEBUDDY_CONFIG_DIR=/workspace/.codebuddy

RUN apt-get update \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docker cache efficiency: Everything is chained into one RUN layer, so bumping the CodeBuddy version (line 23) forces re-execution of the entire chain: apt-get update, installing system packages, downloading the NodeSource GPG key, Node.js, etc. (~60-120s of wasted rebuild time during iteration).

Consider splitting into three layers to preserve caching for the stable parts:

  1. System packages + cleanup (rarely changes)
  2. NodeSource repo key + Node.js install + cleanup (changes with Node version)
  3. npm install -g + codebuddy --version + npm cache clean (changes with every CodeBuddy bump)

> /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends nodejs \
&& npm install -g @tencent-ai/codebuddy-code@2.114.2 \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image size: npm install -g installs devDependencies by default, which can add 50-200+ MB of unnecessary packages to the sandbox template image. Consider --omit=dev:

RUN npm install -g --omit=dev @tencent-ai/codebuddy-code@2.114.2 \
    && codebuddy --version \
    && npm cache clean --force

(If codebuddy --version fails without devDependencies, that's a CLI packaging issue worth flagging to the team.)

printf '%s\n' "${create_output}"

if [[ "${WATCH_JOB:-0}" == "1" ]]; then
job_id="$(printf '%s\n' "${create_output}" | sed -n 's/.*job_id[=: ]*\([^ ]*\).*/\1/p' | tail -n 1)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragile parsing: The sed pattern assumes job_id appears in a specific format (job_id=<value> or job_id: <value>). If cubemastercli tpl create-from-image output format changes, this silently produces an empty string. The fallback on lines 44-49 handles the failure gracefully (dumps raw output), but the pattern could be tightened by matching a known prefix/suffix specific to cubemastercli's actual output, or by asking cubemastercli to have a --json / --format json flag.

if not args.template:
raise SystemExit("Missing template: set CUBE_TEMPLATE_ID or pass --template")

from e2b_code_interpreter import Sandbox

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lazy import: Importing Sandbox inside main() means that running --help or any other CLI operation will fail with an ImportError if e2b-code-interpreter is not installed. A top-level import (optionally guarded with a try/except and a helpful message) is the standard Python convention and worth demonstrating in example code that others learn from.

"""


def sandbox_env(api_key: str, config_dir: str) -> Dict[str, str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: Proxy env var forwarding leaks host credentials into sandbox

sandbox_env() propagates HTTP_PROXY, HTTPS_PROXY, and NO_PROXY from the host environment into every sandbox instance. If a proxy URL contains embedded credentials (e.g., http://user:pass@corp-proxy:8080), those credentials are readable by any process inside the sandbox including the CodeBuddy LLM agent. Consider stripping userinfo from proxy URLs before forwarding, or at minimum document this behavior prominently.

permission_mode=args.permission_mode,
)
sandbox.files.write(SCRIPT_PATH, script)
sandbox.commands.run(f"chmod +x {shlex.quote(SCRIPT_PATH)}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: chmod +x is unnecessary here

The script is executed via bash /tmp/run-codebuddy.sh (line 240), which does not require the execute bit. This chmod call is a wasted network round-trip to the CubeAPI. Removing it saves ~50–200ms per run.

from e2b_code_interpreter import Sandbox

print(f"[cube] template: {args.template}")
print(f"[cube] api url: {os.environ['E2B_API_URL']}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality: require_env return value partially ignored

require_env() returns a Dict[str, str] of validated values, but line 218 reads os.environ['E2B_API_URL'] directly instead of using the returned dict. This bypasses the function's abstraction and makes testing harder. Use required_env['E2B_API_URL'] instead for consistency.

return env


def write_demo_workspace(sandbox) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: Three network round-trips for trivial workspace setup

mkdir -p (1 RPC) + two files.write() calls (2 RPCs) = 3 network round-trips to the CubeAPI. These could be combined into a single shell heredoc script executed via sandbox.commands.run(), saving ~60–70% of this step's latency.


def build_codebuddy_script(
prompt: str,
output_format: str,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality: positive_int couples validation to argparse

positive_int raises argparse.ArgumentTypeError, but env_positive_int (line 73) calls it outside argparse and must catch-and-reraise as SystemExit. Consider extracting a pure validate_positive_int(value) -> int that raises ValueError, then wrap it with a thin argparse adapter. This keeps env_positive_int clean and makes validation reusable.

return env


def write_demo_workspace(sandbox) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality: Missing type annotations on core data-flow functions

write_demo_workspace, print_result (line 118), and verify_pause_resume (line 142) omit type annotations for their primary parameters while the rest of the module is fully annotated. Consider annotating sandbox as Sandbox and defining a Protocol for the result type to get IDE/type-checker assistance.

)"
printf '%s\n' "${create_output}"

if [[ "${WATCH_JOB:-0}" == "1" ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragile job_id extraction via regex on unstructured CLI output

The sed command parses human-readable output from cubemastercli tpl create-from-image. If the CLI output format changes (localization, new fields, spacing), the extraction silently fails. If cubemastercli supports --format json, prefer jq -r '.job_id' for robust parsing.

exit 1
fi

docker build --platform "${DOCKER_PLATFORM:-linux/amd64}" -t "${IMAGE_NAME}" "${SCRIPT_DIR}/template"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: docker build missing --pull flag

The base image tag 2026.16 is a rolling weekly tag. Without --pull, Docker uses a locally cached copy even if a newer manifest exists. For iterative local builds, the base image can silently fall behind. Add --pull to always fetch the latest manifest.

Path.cwd() / ".env",
]

seen_paths = set()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Unnecessary deduplication set for two candidate paths

The seen_paths set deduplicates two .env paths, but the function returns on the first match anyway. The set adds complexity without functional benefit — a simple loop suffices.

"""


def strip_url_userinfo(value: str) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security-critical: strip_url_userinfo() has no test coverage ⚠️

This function handles credential stripping from proxy URLs before injecting them into sandboxes. Despite being security-critical, none of its branches are tested in isolation. Consider adding tests for:

  • URL with userinfo (http://user:pass@proxy:8080)
  • URL without @ (http://proxy:8080)
  • URL without scheme (user:pass@proxy:8080)
  • URL with @ only in credentials (http://user:p@ss@proxy:8080)

The test_sandbox_env_strips_proxy_credentials_when_forwarding_is_enabled test validates the integration path through sandbox_env(), but not the utility in isolation.

TRUTHY_ENV_VALUES = {"1", "true", "yes", "on"}


def require_env(keys: Iterable[str]) -> Dict[str, str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterator consumption bug in require_env() 🐛

The keys: Iterable[str] parameter is iterated twice (once for building missing, once for building the return dict). If keys is a one-shot iterator or generator, the second loop silently produces an empty dict — no missing keys reported, no env vars returned.

Fix by iterating once:

def require_env(keys: Iterable[str]) -> Dict[str, str]:
    result = {}
    missing = []
    for key in keys:
        value = os.environ.get(key)
        if not value:
            missing.append(key)
        else:
            result[key] = value
    if missing:
        raise SystemExit(...)
    return result

Current callers pass list literals so this doesn't trigger yet, but the type annotation advertises Iterable.

return parsed


def env_default(key: str, default: str) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env_default() treats empty string as "not set"

return os.environ.get(key) or default — if someone explicitly sets CUBE_SANDBOX_TIMEOUT="" (e.g., in .env), this silently falls through to the default "600" rather than propagating the empty value. Consider using os.environ.get(key, default) if you only want to fall back when the key is absent.

python3-pip \
ripgrep \
&& mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GPG key fingerprint should be verified

The NodeSource GPG key is piped directly into the keyring without fingerprint verification. An attacker compromising Nodesource's infrastructure or performing a transit hijack could serve a different key and sign malicious packages. Consider:

&& gpg --show-keys /etc/apt/keyrings/nodesource.gpg 2>/dev/null \
    | grep -q "9FD3 B784 BC1C 6FC3 1A8A  0A1C 1655 A0AB 6857 3800" \
    || (echo "ERROR: Nodesource key fingerprint mismatch" && exit 1)

Verify the current fingerprint at https://github.com/nodesource/distributions before pinning.

return env


def write_demo_workspace(sandbox) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write_demo_workspace() makes 3 sequential RPC calls

Each sandbox.commands.run() and sandbox.files.write() crosses a network boundary to the sandbox MicroVM. On high-latency links this adds ~150–600ms. Consider batching into a single command:

def write_demo_workspace(sandbox) -> None:
    script = (
        "mkdir -p /tmp/codebuddy-demo\n"
        "cat > /tmp/codebuddy-demo/hello.py <<'EOF'\n"
        "print('hello from Cube Sandbox + CodeBuddy')\n"
        "EOF\n"
        "cat > /tmp/codebuddy-demo/README.md <<'EOF'\n"
        "# CodeBuddy Cube Sandbox Demo\n\n"
        "…\n"
        "EOF\n"
    )
    sandbox.commands.run(script)

Also consider adding error handling — if a file write fails, the script proceeds with a broken workspace.

&& npm cache clean --force \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider running as non-root user

The container runs CodeBuddy (an autonomous code-generation agent with Bash/Write/Edit access) as root. Any vulnerability or prompt injection gives root-level access inside the sandbox. While the MicroVM provides isolation, defense-in-depth suggests:

RUN groupadd -r codebuddy && useradd -r -g codebuddy -d /workspace -s /bin/bash codebuddy \
    && chown codebuddy:codebuddy /workspace
USER codebuddy

Document that this is a hardening recommendation — verify compatibility with cube-entrypoint.sh requirements.

"""


def strip_url_userinfo(value: str) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security-critical: strip_url_userinfo() has no test coverage ⚠️

This function handles credential stripping from proxy URLs before injecting them into sandboxes. Despite being security-critical, none of its branches are tested. Consider adding tests for:

  • URL with userinfo (http://user:pass@proxy:8080)
  • URL without @ (http://proxy:8080)
  • URL without scheme (user:pass@proxy:8080)
  • URL with @ only in credentials (http://user@host:p@ss@proxy:8080)

The test_sandbox_env_strips_proxy_credentials_when_forwarding_is_enabled test only validates the integration path through sandbox_env(), not the utility function in isolation.

TRUTHY_ENV_VALUES = {"1", "true", "yes", "on"}


def require_env(keys: Iterable[str]) -> Dict[str, str]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterator consumption bug in require_env() 🐛

The keys: Iterable[str] parameter is iterated twice (once for building missing, once for building the return dict). If keys is a one-shot iterator (e.g., iter(["E2B_API_URL", "E2B_API_KEY"]) or a generator), the second loop silently produces an empty dict — no missing keys reported, no env vars returned.

Fix: iterate once and build both lists:

def require_env(keys: Iterable[str]) -> Dict[str, str]:
    result = {}
    missing = []
    for key in keys:
        value = os.environ.get(key)
        if not value:
            missing.append(key)
        else:
            result[key] = value
    if missing:
        raise SystemExit(...)
    return result

The current callers pass list literals so this doesn't trigger today, but the type annotation advertises Iterable.

return parsed


def env_default(key: str, default: str) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env_default() treats empty string as "not set"

return os.environ.get(key) or default — If someone explicitly sets CUBE_SANDBOX_TIMEOUT="" (e.g., in .env), this silently falls through to the default "600" rather than propagating the empty value or raising an error. Consider os.environ.get(key, default) if the intent is to only fall back when the key is absent, or strip + check if you want to reject empty values.

python3-pip \
ripgrep \
&& mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GPG key fingerprint should be verified for supply chain security

The NodeSource GPG key is piped directly into the keyring without fingerprint verification. An attacker compromising Nodesource distribution infrastructure or performing a DNS/transit hijack could serve a different key and sign malicious packages. Consider verifying the key fingerprint after import:

&& gpg --show-keys /etc/apt/keyrings/nodesource.gpg 2>/dev/null \
    | grep -q "9FD3 B784 BC1C 6FC3 1A8A  0A1C 1655 A0AB 6857 3800" \
    || (echo "ERROR: Nodesource key fingerprint mismatch" && exit 1)

Current key fingerprint for NodeSource: 9FD3 B784 BC1C 6FC3 1A8A 0A1C 1655 A0AB 6857 3800 (verify before pinning).

return env


def write_demo_workspace(sandbox) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write_demo_workspace() makes 3 sequential sandbox RPC calls

Each sandbox.commands.run() and sandbox.files.write() call crosses a network boundary. On high-latency links this adds 150–600ms. Consider batching into a single shell command:

def write_demo_workspace(sandbox) -> None:
    script = (
        "mkdir -p /tmp/codebuddy-demo\n"
        "cat > /tmp/codebuddy-demo/hello.py <<'EOF'\n"
        "print('hello from Cube Sandbox + CodeBuddy')\n"
        "EOF\n"
        "cat > /tmp/codebuddy-demo/README.md <<'EOF'\n"
        "# CodeBuddy Cube Sandbox Demo\n\n"
        "…\n"
        "EOF\n"
    )
    sandbox.commands.run(script)

Also, consider adding error handling — if a file write fails, the script proceeds with a broken workspace.

&& npm cache clean --force \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider running as non-root user

The container runs CodeBuddy (an autonomous code-generation agent with Bash, Write, and Edit tool access) as root. Any vulnerability or prompt injection enables root-level access inside the sandbox. While the MicroVM provides isolation, defense-in-depth suggests adding a non-root user:

RUN groupadd -r codebuddy && useradd -r -g codebuddy -d /workspace -s /bin/bash codebuddy \
    && chown codebuddy:codebuddy /workspace
USER codebuddy

Document that this is a sandbox hardening recommendation (the entrypoint may have root requirements — verify compatibility).

@fslongjin

Copy link
Copy Markdown
Member

Refs #644

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants