Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 16 additions & 38 deletions crates/cloud-sdk/src/sandbox_images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ const ROOTFS_BUILDER_PATH: &str = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
const ROOTFS_BUILDER_COMMAND: &str = "tl-rootfs-build";
const ROOTFS_BUILDER_PROCESS_USER: &str = "root";

/// Dockerfile instructions rejected during sandbox-image build. The rootfs
/// builder hands the Dockerfile to `docker build` verbatim, so without this
/// allowlist these instructions would silently take effect inside the
/// resulting rootfs. Mirrors the historical Python/TS SDK behavior.
const UNSUPPORTED_DOCKERFILE_INSTRUCTIONS: &[&str] = &["ARG", "ONBUILD", "SHELL", "USER"];

/// Dockerfile instructions that affect image config (CMD, ENTRYPOINT, etc.)
/// rather than the filesystem. `docker build --output type=tar` discards the
/// image config, so these are effectively no-ops on the rootfs path — we warn
Expand Down Expand Up @@ -1181,12 +1175,6 @@ fn load_dockerfile_text_plan(
base_image = Some(parse_from_value(&value, line_number)?);
continue;
}
if UNSUPPORTED_DOCKERFILE_INSTRUCTIONS.contains(&keyword.as_str()) {
return Err(SandboxImageBuildError::other(format!(
"line {}: Dockerfile instruction '{}' is not supported for sandbox image creation",
line_number, keyword
)));
}
if IGNORED_DOCKERFILE_INSTRUCTIONS.contains(&keyword.as_str()) {
ignored_instructions.push((line_number, keyword));
}
Expand Down Expand Up @@ -1507,32 +1495,22 @@ mod tests {
}

#[test]
fn load_dockerfile_plan_rejects_unsupported_instructions() {
for instruction in [
"ARG FOO=1",
"ONBUILD RUN echo",
"SHELL [\"/bin/bash\"]",
"USER app",
] {
let temp_dir = tempfile::tempdir().unwrap();
let dockerfile_path = temp_dir.path().join("Dockerfile");
std::fs::write(
&dockerfile_path,
format!("FROM python:3.12-slim\n{}\n", instruction),
)
.unwrap();

let error = load_dockerfile_plan(&dockerfile_path, None).unwrap_err();
let keyword = instruction.split_whitespace().next().unwrap();
let expected = format!(
"line 2: Dockerfile instruction '{}' is not supported for sandbox image creation",
keyword
);
assert!(
error.to_string().contains(&expected),
"instruction {instruction}: expected {expected:?} in {error}",
);
}
fn load_dockerfile_plan_preserves_builder_interpreted_instructions() {
let temp_dir = tempfile::tempdir().unwrap();
let dockerfile_path = temp_dir.path().join("Dockerfile");
let dockerfile_text = "FROM python:3.12-slim\n\
ARG FOO=1\n\
SHELL [\"/bin/bash\", \"-lc\"]\n\
RUN echo \"$FOO\"\n\
USER app\n\
ONBUILD RUN echo child\n";
std::fs::write(&dockerfile_path, dockerfile_text).unwrap();

let plan = load_dockerfile_plan(&dockerfile_path, None).unwrap();

assert_eq!(plan.base_image, "python:3.12-slim");
assert_eq!(plan.dockerfile_text, dockerfile_text);
assert!(plan.ignored_instructions.is_empty());
}

#[test]
Expand Down
7 changes: 4 additions & 3 deletions src/tensorlake/image/sandbox_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

This is the programmatic backend for :meth:`Image.build` and the
``tl sbx image create`` CLI command. The Rust core owns parsing, the
Dockerfile-instruction allowlist (``ARG``/``ONBUILD``/``SHELL``/``USER`` are
rejected) and ignored-set warnings (``CMD``/``ENTRYPOINT``/``EXPOSE``/
``HEALTHCHECK``/``LABEL``/``STOPSIGNAL``/``VOLUME``).
single-stage ``FROM`` validation, and ignored-set warnings
(``CMD``/``ENTRYPOINT``/``EXPOSE``/``HEALTHCHECK``/``LABEL``/``STOPSIGNAL``/
``VOLUME``). All other Dockerfile instructions are preserved for the rootfs
builder to interpret.
"""

from __future__ import annotations
Expand Down
5 changes: 3 additions & 2 deletions typescript/src/sandbox-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import { Image, dockerfileContent } from "./image.js";
* Renders the source to a Dockerfile (for `Image` inputs) and hands the
* Dockerfile path/text + context to the Rust core via `@tensorlake/native`,
* which parses, validates, materializes, and registers the image. The Rust
* core owns parsing, the Dockerfile-instruction allowlist
* (`ARG`/`ONBUILD`/`SHELL`/`USER` are rejected) and ignored-set warnings
* core owns single-stage `FROM` validation and ignored-set warnings
* (`CMD`/`ENTRYPOINT`/`EXPOSE`/`HEALTHCHECK`/`LABEL`/`STOPSIGNAL`/`VOLUME`).
* All other Dockerfile instructions are preserved for the rootfs builder to
* interpret.
*/

export interface CreateSandboxImageOptions {
Expand Down
Loading