From 68d5118a9d2f46040117c63d4d1ba5e6d8e048c8 Mon Sep 17 00:00:00 2001
From: "Dylan M. Taylor"
Date: Wed, 21 Oct 2026 17:13:05 -0400
Subject: [PATCH 1/6] security: validate CLI paths/args in dev tools
(SonarCloud S2083/S8707/S8705/S7044)
Resolve and validate command-line path arguments before use, enforce HTTPS at
download sinks, and confirm path containment. Hardens local codegen/fetch tools
against path-traversal and command-argument-injection findings.
---
docs/showcase/run.py | 12 +++++++++++-
tools/spectral_camera_table.py | 19 ++++++++++++++++++-
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/docs/showcase/run.py b/docs/showcase/run.py
index 8ea6c9fc9b..6b680e9f2f 100644
--- a/docs/showcase/run.py
+++ b/docs/showcase/run.py
@@ -1,6 +1,16 @@
#!/usr/bin/env python3
"""lcb.py FILE.jsonl — run control requests from a JSON-lines file ({"method":..., "params":...} or {"sleep": s})."""
import json, socket, sys, time
+from pathlib import Path
+
+
+def _jsonl_path(arg):
+ """Resolve the CLI file argument; require an existing regular file."""
+ path = Path(arg).resolve()
+ if not path.is_file():
+ sys.exit(f"not a readable file: {arg}")
+ return path
+
def call(method, params):
s = socket.create_connection(("127.0.0.1", 7980), timeout=180)
s.sendall((json.dumps({"id": 1, "method": method, "params": params}) + "\n").encode())
@@ -10,7 +20,7 @@ def call(method, params):
if not c: break
buf += c
return json.loads(buf)
-for line in open(sys.argv[1]):
+for line in open(_jsonl_path(sys.argv[1])):
line = line.strip()
if not line or line.startswith("#"): continue
r = json.loads(line)
diff --git a/tools/spectral_camera_table.py b/tools/spectral_camera_table.py
index b05ac28028..0e6fef7df2 100644
--- a/tools/spectral_camera_table.py
+++ b/tools/spectral_camera_table.py
@@ -19,6 +19,23 @@ import sys
import colour
import numpy as np
from scipy.optimize import minimize
+from pathlib import Path
+
+
+def _existing_dir(arg):
+ """Resolve a directory CLI argument; require it to exist."""
+ path = Path(arg).resolve()
+ if not path.is_dir():
+ sys.exit(f"not a directory: {arg}")
+ return str(path)
+
+
+def _out_path(arg):
+ """Resolve an output path CLI argument; require its parent to exist."""
+ path = Path(arg).resolve()
+ if not path.parent.is_dir():
+ sys.exit(f"output directory does not exist: {arg}")
+ return str(path)
WL = np.arange(380, 781, 5)
D50 = colour.xy_to_XYZ(colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D50"])
@@ -119,4 +136,4 @@ def main(root, out):
if __name__ == "__main__":
if len(sys.argv) != 3:
sys.exit("usage: spectral_camera_table.py
+
+
+
+
LightCraft
Your photos. Your pixels. Your machine.
From 479a05a0be4756be460859c4d42b4fbb7a60e99d Mon Sep 17 00:00:00 2001
From: Brandon Thomas
Date: Thu, 22 Oct 2026 15:39:28 -0400
Subject: [PATCH 4/6] Release 0.12.0
Co-Authored-By: Claude Opus 5.5
---
Cargo.lock | 66 +++++++++++++++++++++++++++---------------------------
Cargo.toml | 2 +-
2 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 216fdeca4e..1ec8fad294 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3087,7 +3087,7 @@ dependencies = [
[[package]]
name = "lightcraft"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"chrono",
"eframe",
@@ -3113,7 +3113,7 @@ dependencies = [
[[package]]
name = "lightcraft-catalog"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"chrono",
"crc32fast",
@@ -3131,7 +3131,7 @@ dependencies = [
[[package]]
name = "lightcraft-cli"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"dhat",
"lightcraft-codecs",
@@ -3147,7 +3147,7 @@ dependencies = [
[[package]]
name = "lightcraft-codecs"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"avif-serialize",
"bytemuck",
@@ -3178,7 +3178,7 @@ dependencies = [
[[package]]
name = "lightcraft-color"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-geom",
"proptest",
@@ -3187,7 +3187,7 @@ dependencies = [
[[package]]
name = "lightcraft-denoise"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"faer-core",
"lightcraft-denoise-core",
@@ -3204,7 +3204,7 @@ dependencies = [
[[package]]
name = "lightcraft-denoise-core"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-raster",
"miniz_oxide 0.8.9",
@@ -3219,7 +3219,7 @@ dependencies = [
[[package]]
name = "lightcraft-develop"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-color",
"lightcraft-geom",
@@ -3231,7 +3231,7 @@ dependencies = [
[[package]]
name = "lightcraft-engine"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"ab_glyph",
"chrono",
@@ -3275,7 +3275,7 @@ dependencies = [
[[package]]
name = "lightcraft-faces"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-testkit",
"matrixmultiply",
@@ -3290,7 +3290,7 @@ dependencies = [
[[package]]
name = "lightcraft-fetch"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"log",
"rustls",
@@ -3301,7 +3301,7 @@ dependencies = [
[[package]]
name = "lightcraft-geocode"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"miniz_oxide 0.8.9",
"serde",
@@ -3309,7 +3309,7 @@ dependencies = [
[[package]]
name = "lightcraft-geom"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"proptest",
"serde",
@@ -3317,7 +3317,7 @@ dependencies = [
[[package]]
name = "lightcraft-gpu"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"bytemuck",
"lightcraft-color",
@@ -3337,7 +3337,7 @@ dependencies = [
[[package]]
name = "lightcraft-heif"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"heic-rs",
"rayon",
@@ -3345,14 +3345,14 @@ dependencies = [
[[package]]
name = "lightcraft-inpaint"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"rayon",
]
[[package]]
name = "lightcraft-lensdb"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-develop",
"lightcraft-geom",
@@ -3361,7 +3361,7 @@ dependencies = [
[[package]]
name = "lightcraft-mcp"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-codecs",
"lightcraft-engine",
@@ -3372,7 +3372,7 @@ dependencies = [
[[package]]
name = "lightcraft-merge"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-codecs",
"lightcraft-color",
@@ -3388,7 +3388,7 @@ dependencies = [
[[package]]
name = "lightcraft-meta"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"chrono",
"lightcraft-geom",
@@ -3401,7 +3401,7 @@ dependencies = [
[[package]]
name = "lightcraft-pipeline"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"image",
"lightcraft-color",
@@ -3419,7 +3419,7 @@ dependencies = [
[[package]]
name = "lightcraft-preview"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"jpeg-encoder",
"lightcraft-raster",
@@ -3431,7 +3431,7 @@ dependencies = [
[[package]]
name = "lightcraft-raster"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"libc",
"lightcraft-color",
@@ -3443,7 +3443,7 @@ dependencies = [
[[package]]
name = "lightcraft-raw"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"dhat",
"jpeg-encoder",
@@ -3467,7 +3467,7 @@ dependencies = [
[[package]]
name = "lightcraft-scenes"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"image",
"lightcraft-color",
@@ -3478,7 +3478,7 @@ dependencies = [
[[package]]
name = "lightcraft-segment"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"candle-core",
"candle-nn",
@@ -3492,7 +3492,7 @@ dependencies = [
[[package]]
name = "lightcraft-slideshow"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"lightcraft-raster",
"serde",
@@ -3501,15 +3501,15 @@ dependencies = [
[[package]]
name = "lightcraft-sysmem"
-version = "0.11.0"
+version = "0.12.0"
[[package]]
name = "lightcraft-testkit"
-version = "0.11.0"
+version = "0.12.0"
[[package]]
name = "lightcraft-tiff"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"proptest",
"serde",
@@ -3518,7 +3518,7 @@ dependencies = [
[[package]]
name = "lightcraft-ui-egui"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"craft-ui",
"egui",
@@ -3547,7 +3547,7 @@ dependencies = [
[[package]]
name = "lightcraft-web"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"crc32fast",
"eframe",
@@ -7017,7 +7017,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
-version = "0.11.0"
+version = "0.12.0"
dependencies = [
"brotli",
"flate2",
diff --git a/Cargo.toml b/Cargo.toml
index 0a0d78e5f6..1ca7383152 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*", "apps/*", "xtask"]
[workspace.package]
-version = "0.11.0"
+version = "0.12.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.90"
From ac2d7623e3dc5b0188a45571e6ac45ada45650cf Mon Sep 17 00:00:00 2001
From: "Dylan M. Taylor"
Date: Thu, 22 Oct 2026 19:18:41 -0400
Subject: [PATCH 5/6] security: tighten readable/regular file checks in CLI
tools
---
docs/showcase/run.py | 7 ++++++-
tools/spectral_camera_table.py | 2 ++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/docs/showcase/run.py b/docs/showcase/run.py
index 6b680e9f2f..bb80e0c1cc 100644
--- a/docs/showcase/run.py
+++ b/docs/showcase/run.py
@@ -5,10 +5,15 @@ from pathlib import Path
def _jsonl_path(arg):
- """Resolve the CLI file argument; require an existing regular file."""
+ """Resolve the CLI file argument; require an existing readable regular file."""
path = Path(arg).resolve()
if not path.is_file():
sys.exit(f"not a readable file: {arg}")
+ try:
+ with path.open("rb"):
+ pass
+ except OSError:
+ sys.exit(f"not a readable file: {arg}")
return path
def call(method, params):
diff --git a/tools/spectral_camera_table.py b/tools/spectral_camera_table.py
index 0e6fef7df2..188f6ba4c5 100644
--- a/tools/spectral_camera_table.py
+++ b/tools/spectral_camera_table.py
@@ -35,6 +35,8 @@ def _out_path(arg):
path = Path(arg).resolve()
if not path.parent.is_dir():
sys.exit(f"output directory does not exist: {arg}")
+ if path.exists() and not path.is_file():
+ sys.exit(f"output path is not a regular file: {arg}")
return str(path)
WL = np.arange(380, 781, 5)
From 23e181283cb448205d865e8cd288b56afe987986 Mon Sep 17 00:00:00 2001
From: Brandon Thomas
Date: Thu, 22 Oct 2026 20:06:09 -0400
Subject: [PATCH 6/6] Release 0.13.0
Co-Authored-By: Claude Opus 5.5
---
Cargo.lock | 66 +++++++++++++++++++++++++++---------------------------
Cargo.toml | 2 +-
2 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 1ec8fad294..8b3985fc09 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3087,7 +3087,7 @@ dependencies = [
[[package]]
name = "lightcraft"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"chrono",
"eframe",
@@ -3113,7 +3113,7 @@ dependencies = [
[[package]]
name = "lightcraft-catalog"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"chrono",
"crc32fast",
@@ -3131,7 +3131,7 @@ dependencies = [
[[package]]
name = "lightcraft-cli"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"dhat",
"lightcraft-codecs",
@@ -3147,7 +3147,7 @@ dependencies = [
[[package]]
name = "lightcraft-codecs"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"avif-serialize",
"bytemuck",
@@ -3178,7 +3178,7 @@ dependencies = [
[[package]]
name = "lightcraft-color"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-geom",
"proptest",
@@ -3187,7 +3187,7 @@ dependencies = [
[[package]]
name = "lightcraft-denoise"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"faer-core",
"lightcraft-denoise-core",
@@ -3204,7 +3204,7 @@ dependencies = [
[[package]]
name = "lightcraft-denoise-core"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-raster",
"miniz_oxide 0.8.9",
@@ -3219,7 +3219,7 @@ dependencies = [
[[package]]
name = "lightcraft-develop"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-color",
"lightcraft-geom",
@@ -3231,7 +3231,7 @@ dependencies = [
[[package]]
name = "lightcraft-engine"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"ab_glyph",
"chrono",
@@ -3275,7 +3275,7 @@ dependencies = [
[[package]]
name = "lightcraft-faces"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-testkit",
"matrixmultiply",
@@ -3290,7 +3290,7 @@ dependencies = [
[[package]]
name = "lightcraft-fetch"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"log",
"rustls",
@@ -3301,7 +3301,7 @@ dependencies = [
[[package]]
name = "lightcraft-geocode"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"miniz_oxide 0.8.9",
"serde",
@@ -3309,7 +3309,7 @@ dependencies = [
[[package]]
name = "lightcraft-geom"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"proptest",
"serde",
@@ -3317,7 +3317,7 @@ dependencies = [
[[package]]
name = "lightcraft-gpu"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"bytemuck",
"lightcraft-color",
@@ -3337,7 +3337,7 @@ dependencies = [
[[package]]
name = "lightcraft-heif"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"heic-rs",
"rayon",
@@ -3345,14 +3345,14 @@ dependencies = [
[[package]]
name = "lightcraft-inpaint"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"rayon",
]
[[package]]
name = "lightcraft-lensdb"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-develop",
"lightcraft-geom",
@@ -3361,7 +3361,7 @@ dependencies = [
[[package]]
name = "lightcraft-mcp"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-codecs",
"lightcraft-engine",
@@ -3372,7 +3372,7 @@ dependencies = [
[[package]]
name = "lightcraft-merge"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-codecs",
"lightcraft-color",
@@ -3388,7 +3388,7 @@ dependencies = [
[[package]]
name = "lightcraft-meta"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"chrono",
"lightcraft-geom",
@@ -3401,7 +3401,7 @@ dependencies = [
[[package]]
name = "lightcraft-pipeline"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"image",
"lightcraft-color",
@@ -3419,7 +3419,7 @@ dependencies = [
[[package]]
name = "lightcraft-preview"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"jpeg-encoder",
"lightcraft-raster",
@@ -3431,7 +3431,7 @@ dependencies = [
[[package]]
name = "lightcraft-raster"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"libc",
"lightcraft-color",
@@ -3443,7 +3443,7 @@ dependencies = [
[[package]]
name = "lightcraft-raw"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"dhat",
"jpeg-encoder",
@@ -3467,7 +3467,7 @@ dependencies = [
[[package]]
name = "lightcraft-scenes"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"image",
"lightcraft-color",
@@ -3478,7 +3478,7 @@ dependencies = [
[[package]]
name = "lightcraft-segment"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"candle-core",
"candle-nn",
@@ -3492,7 +3492,7 @@ dependencies = [
[[package]]
name = "lightcraft-slideshow"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"lightcraft-raster",
"serde",
@@ -3501,15 +3501,15 @@ dependencies = [
[[package]]
name = "lightcraft-sysmem"
-version = "0.12.0"
+version = "0.13.0"
[[package]]
name = "lightcraft-testkit"
-version = "0.12.0"
+version = "0.13.0"
[[package]]
name = "lightcraft-tiff"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"proptest",
"serde",
@@ -3518,7 +3518,7 @@ dependencies = [
[[package]]
name = "lightcraft-ui-egui"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"craft-ui",
"egui",
@@ -3547,7 +3547,7 @@ dependencies = [
[[package]]
name = "lightcraft-web"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"crc32fast",
"eframe",
@@ -7017,7 +7017,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
-version = "0.12.0"
+version = "0.13.0"
dependencies = [
"brotli",
"flate2",
diff --git a/Cargo.toml b/Cargo.toml
index 1ca7383152..ef204030ff 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*", "apps/*", "xtask"]
[workspace.package]
-version = "0.12.0"
+version = "0.13.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.90"