From 939055897fff9729a71c8e0883fe90e3c5fbb7cf Mon Sep 17 00:00:00 2001 From: Dominic Anaeto Date: Mon, 26 Oct 2026 22:59:25 -0400 Subject: [PATCH 1/4] LC-003: reject non-UTF-8 CLI arguments cleanly --- apps/lightcraft-cli/src/main.rs | 11 ++++++++++- apps/lightcraft-cli/tests/it/cli.rs | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/apps/lightcraft-cli/src/main.rs b/apps/lightcraft-cli/src/main.rs index c1f9d258c..a650ef748 100644 --- a/apps/lightcraft-cli/src/main.rs +++ b/apps/lightcraft-cli/src/main.rs @@ -155,7 +155,16 @@ fn main() -> ExitCode { // Warnings (a GPU render redone on the CPU, an unknown backend name) on stderr; LIGHTCRAFT_LOG // or RUST_LOG picks another level (#168). lightcraft_engine::logging::install("lightcraft-cli"); - let args: Vec = std::env::args().skip(1).collect(); + let mut args: Vec = Vec::new(); + for (index, arg) in std::env::args_os().skip(1).enumerate() { + match arg.into_string() { + Ok(arg) => args.push(arg), + Err(_) => { + eprintln!("lightcraft-cli: argument {} is not valid UTF-8; use a UTF-8 encoded argument", index + 1); + return ExitCode::FAILURE; + } + } + } let r = match args.first().map(String::as_str) { Some("run") => run(&args[1..]), Some("mcp") => mcp(&args[1..]), diff --git a/apps/lightcraft-cli/tests/it/cli.rs b/apps/lightcraft-cli/tests/it/cli.rs index 8a11f5c9a..cb898005c 100644 --- a/apps/lightcraft-cli/tests/it/cli.rs +++ b/apps/lightcraft-cli/tests/it/cli.rs @@ -162,6 +162,24 @@ fn mcp_over_stdio_sets_exposure_and_renders() { assert!(b > a + 10.0, "{a} → {b}"); } +#[cfg(unix)] +#[test] +fn non_utf8_argument_fails_without_a_panic() { + use std::ffi::OsString; + use std::os::unix::ffi::OsStringExt; + + let out = tmp("non-utf8-output.png"); + let _ = std::fs::remove_file(&out); + let output = Command::new(binary()).arg("render").arg(OsString::from_vec(b"bad-\xff.png".to_vec())).arg("-o").arg(&out).output().unwrap(); + let stderr = String::from_utf8_lossy(&output.stderr); + assert_eq!(output.status.code(), Some(1), "{stderr}"); + assert!(stderr.contains("argument 2"), "{stderr}"); + assert!(stderr.contains("UTF-8"), "{stderr}"); + assert!(!stderr.contains("panicked"), "{stderr}"); + assert!(!out.exists()); + assert!(Command::new(binary()).arg("--version").status().unwrap().success()); +} + #[test] fn render_subcommand() { let input = tmp("r-in.png"); From 3d357617c4a6fa8c42ab6372e02c59a476d2bf33 Mon Sep 17 00:00:00 2001 From: Dominic Anaeto Date: Tue, 27 Oct 2026 18:05:24 -0400 Subject: [PATCH 2/4] CLI: cover invalid Windows arguments --- apps/lightcraft-cli/src/main.rs | 2 +- apps/lightcraft-cli/tests/it/cli.rs | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/lightcraft-cli/src/main.rs b/apps/lightcraft-cli/src/main.rs index a650ef748..70a9771b0 100644 --- a/apps/lightcraft-cli/src/main.rs +++ b/apps/lightcraft-cli/src/main.rs @@ -160,7 +160,7 @@ fn main() -> ExitCode { match arg.into_string() { Ok(arg) => args.push(arg), Err(_) => { - eprintln!("lightcraft-cli: argument {} is not valid UTF-8; use a UTF-8 encoded argument", index + 1); + eprintln!("lightcraft-cli: argument {} is not valid Unicode", index + 1); return ExitCode::FAILURE; } } diff --git a/apps/lightcraft-cli/tests/it/cli.rs b/apps/lightcraft-cli/tests/it/cli.rs index cb898005c..fef1e7331 100644 --- a/apps/lightcraft-cli/tests/it/cli.rs +++ b/apps/lightcraft-cli/tests/it/cli.rs @@ -174,7 +174,26 @@ fn non_utf8_argument_fails_without_a_panic() { let stderr = String::from_utf8_lossy(&output.stderr); assert_eq!(output.status.code(), Some(1), "{stderr}"); assert!(stderr.contains("argument 2"), "{stderr}"); - assert!(stderr.contains("UTF-8"), "{stderr}"); + assert!(stderr.contains("is not valid Unicode"), "{stderr}"); + assert!(!stderr.contains("panicked"), "{stderr}"); + assert!(!out.exists()); + assert!(Command::new(binary()).arg("--version").status().unwrap().success()); +} + +#[cfg(windows)] +#[test] +fn non_unicode_argument_fails_without_a_panic_on_windows() { + use std::ffi::OsString; + use std::os::windows::ffi::OsStringExt; + + let out = tmp("non-unicode-output.png"); + let _ = std::fs::remove_file(&out); + let name: Vec = "bad-".encode_utf16().chain([0xD800]).chain(".png".encode_utf16()).collect(); + let output = Command::new(binary()).arg("render").arg(OsString::from_wide(&name)).arg("-o").arg(&out).output().unwrap(); + let stderr = String::from_utf8_lossy(&output.stderr); + assert_eq!(output.status.code(), Some(1), "{stderr}"); + assert!(stderr.contains("argument 2"), "{stderr}"); + assert!(stderr.contains("is not valid Unicode"), "{stderr}"); assert!(!stderr.contains("panicked"), "{stderr}"); assert!(!out.exists()); assert!(Command::new(binary()).arg("--version").status().unwrap().success()); From a412dc1dbddc1c604c4dae7bb5d05182e2bf5acf Mon Sep 17 00:00:00 2001 From: Dominic Anaeto Date: Tue, 27 Oct 2026 18:25:33 -0400 Subject: [PATCH 3/4] LC-003: run CLI tests on Windows --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a309a9fb3..53b96e5aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,3 +30,14 @@ jobs: pkg-config libxkbcommon-dev libwayland-dev libx11-dev libxcb1-dev libxcursor-dev libxrandr-dev libxi-dev libfontconfig1-dev libfreetype-dev mesa-vulkan-drivers libgl1-mesa-dev libdbus-1-dev + + windows-cli: + uses: storytold/craft-common-actions/.github/workflows/gate.yml@main + with: + os: '["windows-latest"]' + command: cargo test --locked -p lightcraft-cli + craft-fonts-ref: b076417b5880bd4154bd22f00695b1d28c6bea83 + timeout-minutes: 90 + tools: "" + targets: "" + packages: "" From 647da76d20ca3e4fa494f5feea88b359bdc3d255 Mon Sep 17 00:00:00 2001 From: Dominic Anaeto Date: Wed, 28 Oct 2026 01:32:16 -0400 Subject: [PATCH 4/4] LC-003: keep Windows CI changes out of the fix --- .github/workflows/ci.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53b96e5aa..a309a9fb3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,14 +30,3 @@ jobs: pkg-config libxkbcommon-dev libwayland-dev libx11-dev libxcb1-dev libxcursor-dev libxrandr-dev libxi-dev libfontconfig1-dev libfreetype-dev mesa-vulkan-drivers libgl1-mesa-dev libdbus-1-dev - - windows-cli: - uses: storytold/craft-common-actions/.github/workflows/gate.yml@main - with: - os: '["windows-latest"]' - command: cargo test --locked -p lightcraft-cli - craft-fonts-ref: b076417b5880bd4154bd22f00695b1d28c6bea83 - timeout-minutes: 90 - tools: "" - targets: "" - packages: ""