diff --git a/packages/llama-cpp/default.nix b/packages/llama-cpp/default.nix index dfc6a30..7a1f2f5 100644 --- a/packages/llama-cpp/default.nix +++ b/packages/llama-cpp/default.nix @@ -3,13 +3,13 @@ let # Version MUST be an integer string. # For tagged releases use the tag number (e.g. "9222"). # For HEAD builds use YYYYMMDD (e.g. "20260519"). - version = "9496"; + version = "9802"; src = pkgs.fetchFromGitHub { owner = "ggml-org"; repo = "llama.cpp"; - rev = "94a220cd6745e6e3f8de62870b66fd5b9bc92700"; - hash = "sha256-1jAowfGVzrrHDwWWzKESY7aV82whnuIg1N37fmtcgyw="; + rev = "beac5309f1bc67534f509bf29420abf58fff063c"; + hash = "sha256-muVLC9PBzu6fRD5ddz5I4b1INRGsfgjkuqnMj4MUoqk="; leaveDotGit = true; postFetch = '' git -C "$out" rev-parse --short HEAD > $out/COMMIT @@ -30,7 +30,7 @@ in # WebUI npm deps hash for our pinned src. Upstream nixpkgs builds the WebUI # from tools/ui via `npm run build` in preConfigure (offline, using these # deps), so no custom webui derivation / HF-bucket workaround is needed. - npmDepsHash = "sha256-1iM0LGeI9e+gZEHk46lkBe51DxIhiimfAm9o3Z3m9Ik="; + npmDepsHash = "sha256-X1DZgmhS/zHTqDT5zq0kywwntthcJ9vRXeqyO3zz6UU="; # Add SPIR-V Headers for Vulkan Backend # Newer llama.cpp requires spirv/unified1/spirv.hpp which isn't diff --git a/packages/llama-swap/default.nix b/packages/llama-swap/default.nix index bf45441..efbf501 100644 --- a/packages/llama-swap/default.nix +++ b/packages/llama-swap/default.nix @@ -13,13 +13,13 @@ let in buildGo126Module (finalAttrs: { pname = "llama-swap"; - version = "216"; + version = "230"; src = fetchFromGitHub { owner = "mostlygeek"; repo = "llama-swap"; tag = "v${finalAttrs.version}"; - hash = "sha256-PHSY4z2h406xL+EcIYyrzr4s28txO7SCsWm8hrXf+2U="; + hash = "sha256-IoA7YMxOtrAeyVBSRVjUx64lPxBLNEzu5J5HAl2vr98="; # populate values that require us to use git. By doing this in postFetch we # can delete .git afterwards and maintain better reproducibility of the src. leaveDotGit = true; @@ -32,7 +32,7 @@ buildGo126Module (finalAttrs: { ''; }; - vendorHash = "sha256-QysQ7YdwJcLTziwL25j73n3tQVvzVQIFxN4GkTU8JZg="; + vendorHash = "sha256-is8pm5g27in/LraLVJUzsa7EPqs+C3qzY8OQ/DXe98A="; passthru.ui = callPackage ./ui.nix { llama-swap = finalAttrs.finalPackage; }; passthru.npmDepsHash = "sha256-NJqEJ+XTdpPFtJJxP4CGu+JDUW7lKDcFgsixQJ3SXtQ="; @@ -55,8 +55,8 @@ buildGo126Module (finalAttrs: { ldflags+=" -X main.commit=$(cat COMMIT)" ldflags+=" -X main.date=$(cat SOURCE_DATE_EPOCH)" - # copy for go:embed in proxy/ui_embed.go - cp -r ${finalAttrs.passthru.ui}/ui_dist proxy/ + # copy for go:embed in internal/server/ui.go + cp -r ${finalAttrs.passthru.ui}/ui_dist internal/server/ ''; excludedPackages = [ @@ -73,7 +73,14 @@ buildGo126Module (finalAttrs: { checkFlags = let - skippedTests = lib.optionals (stdenv.isDarwin && stdenv.isx86_64) [ + # These tests write fixtures with a hardcoded `#!/bin/bash` shebang and exec + # them; the sandbox has no /bin/bash, so fork/exec fails with ENOENT. + forkingTests = [ + "TestProcessCommand_StopForkingWrapper" + "TestProcessCommand_StopHonorsGracefulTimeout" + "TestProcessCommand_StopReapsForkedGrandchild" + ]; + skippedTests = forkingTests ++ lib.optionals (stdenv.isDarwin && stdenv.isx86_64) [ # Fail only on x86_64-darwin intermittently # https://github.com/mostlygeek/llama-swap/issues/320 "TestProcess_AutomaticallyStartsUpstream" diff --git a/packages/llama-swap/ui.nix b/packages/llama-swap/ui.nix index 788dd8b..099307a 100644 --- a/packages/llama-swap/ui.nix +++ b/packages/llama-swap/ui.nix @@ -9,7 +9,7 @@ buildNpmPackage (finalAttrs: { postPatch = '' substituteInPlace vite.config.ts \ - --replace-fail "../proxy/ui_dist" "${placeholder "out"}/ui_dist" + --replace-fail "../internal/server/ui_dist" "${placeholder "out"}/ui_dist" ''; sourceRoot = "${finalAttrs.src.name}/ui-svelte"; diff --git a/packages/stable-diffusion-cpp/default.nix b/packages/stable-diffusion-cpp/default.nix index b1b056a..f487d7d 100644 --- a/packages/stable-diffusion-cpp/default.nix +++ b/packages/stable-diffusion-cpp/default.nix @@ -34,13 +34,13 @@ let in effectiveStdenv.mkDerivation (finalAttrs: { pname = "stable-diffusion-cpp"; - version = "master-462-c5602a6"; + version = "master-721-8caa3f9"; src = fetchFromGitHub { owner = "leejet"; repo = "stable-diffusion.cpp"; - rev = "master-462-c5602a6"; - hash = "sha256-6uW9k30QqvozJACw+Hv4nRj9PyTzQqY/M0/CWjqrV28="; + rev = "master-721-8caa3f9"; + hash = "sha256-voybvJQrG6/Puogf9vBr/3jzHBcl1MnIAsRQtswUw2U="; fetchSubmodules = true; }; @@ -102,12 +102,6 @@ effectiveStdenv.mkDerivation (finalAttrs: { (cmakeFeature "CMAKE_HIP_ARCHITECTURES" (builtins.concatStringsSep ";" rocmGpuTargets)) ]; - patchFlags = [ "-p1" ]; - patches = [ - ./lora_enable.patch # https://github.com/leejet/stable-diffusion.cpp/pull/1156 - ./server_mask.patch # https://github.com/leejet/stable-diffusion.cpp/pull/1178 - ]; - meta = with lib; { description = "Stable Diffusion inference in pure C/C++"; homepage = "https://github.com/leejet/stable-diffusion.cpp"; diff --git a/packages/stable-diffusion-cpp/lora_enable.patch b/packages/stable-diffusion-cpp/lora_enable.patch deleted file mode 100644 index 0318f44..0000000 --- a/packages/stable-diffusion-cpp/lora_enable.patch +++ /dev/null @@ -1,221 +0,0 @@ -From 4aaca67479469faab232dc276afe12acdcd7f801 Mon Sep 17 00:00:00 2001 -From: mateusgpe -Date: Wed, 31 Dec 2025 18:42:23 -0300 -Subject: [PATCH 1/2] fix(server): sanitize LoRA paths and enable dynamic - loading - -- Implement `sanitize_lora_path` in `SDGenerationParams` to prevent directory traversal attacks via LoRA tags in prompts. -- Restrict LoRA paths to be relative and strictly within the configured LoRA directory (no subdirectories allowed, optional? drawback: users cannot organize their LoRAs into subfolders.). -- Update server example to pass `lora_model_dir` to `process_and_check`, enabling LoRA extraction from prompts. -- Force `LORA_APPLY_AT_RUNTIME` in the server to allow applying LoRAs dynamically per request without reloading the model. ---- - examples/common/common.hpp | 67 +++++++++++++++++++++++++++++++++++--- - examples/server/main.cpp | 5 +-- - 2 files changed, 65 insertions(+), 7 deletions(-) - -diff --git a/examples/common/common.hpp b/examples/common/common.hpp -index 7ea95ed14..7f869868c 100644 ---- a/examples/common/common.hpp -+++ b/examples/common/common.hpp -@@ -1601,6 +1601,63 @@ struct SDGenerationParams { - return true; - } - -+ static bool sanitize_lora_path(const std::string& lora_model_dir, -+ const std::string& raw_path_str, -+ fs::path& full_path) { -+ if (lora_model_dir.empty()) { -+ return false; -+ } -+ -+ fs::path raw_path(raw_path_str); -+ -+ // Disallow absolute paths. -+ if (raw_path.is_absolute()) { -+ LOG_WARN("lora path must be relative: %s", raw_path_str.c_str()); -+ return false; -+ } -+ -+ // Disallow '..' in the raw path to prevent basic traversal attempts. -+ for (const auto& part : raw_path) { -+ if (part == "..") { -+ LOG_WARN("lora path cannot contain '..': %s", raw_path_str.c_str()); -+ return false; -+ } -+ } -+ -+ fs::path lora_dir(lora_model_dir); -+ full_path = lora_dir / raw_path; -+ -+ // --- Security Checks on Canonical Path --- -+ // Canonicalize paths to resolve symlinks and normalize separators for robust checks. -+ // weakly_canonical is used because the target file might not exist yet. -+ auto canonical_lora_dir = fs::weakly_canonical(lora_dir); -+ auto canonical_full_path = fs::weakly_canonical(full_path); -+ -+ // 1. The resolved path must not be a directory. -+ if (fs::is_directory(canonical_full_path)) { -+ LOG_WARN("lora path resolved to a directory, not a file: %s", raw_path_str.c_str()); -+ return false; -+ } -+ -+ // 2. The file must be inside the designated lora directory. -+ // We check this by ensuring the relative path does not climb up with '..'. -+ fs::path relative_path = canonical_full_path.lexically_relative(canonical_lora_dir); -+ for (const auto& part : relative_path) { -+ if (part == "..") { -+ LOG_WARN("lora path is outside of the lora model directory: %s", raw_path_str.c_str()); -+ return false; -+ } -+ } -+ -+ // 3. The file must be directly in the lora directory, not in a subdirectory. -+ if (relative_path.has_parent_path() && !relative_path.parent_path().empty()) { -+ LOG_WARN("lora path in subdirectories is not allowed: %s", raw_path_str.c_str()); -+ return false; -+ } -+ -+ return true; -+ } -+ - void extract_and_remove_lora(const std::string& lora_model_dir) { - if (lora_model_dir.empty()) { - return; -@@ -1632,10 +1689,10 @@ struct SDGenerationParams { - } - - fs::path final_path; -- if (is_absolute_path(raw_path)) { -- final_path = raw_path; -- } else { -- final_path = fs::path(lora_model_dir) / raw_path; -+ if (!sanitize_lora_path(lora_model_dir, raw_path, final_path)) { -+ tmp = m.suffix().str(); -+ prompt = std::regex_replace(prompt, re, "", std::regex_constants::format_first_only); -+ continue; - } - if (!fs::exists(final_path)) { - bool found = false; -@@ -1643,7 +1700,7 @@ struct SDGenerationParams { - fs::path try_path = final_path; - try_path += ext; - if (fs::exists(try_path)) { -- final_path = try_path; -+ final_path = try_path.lexically_normal(); - found = true; - break; - } -diff --git a/examples/server/main.cpp b/examples/server/main.cpp -index c540958f8..69c75d322 100644 ---- a/examples/server/main.cpp -+++ b/examples/server/main.cpp -@@ -293,6 +293,7 @@ int main(int argc, const char** argv) { - LOG_DEBUG("%s", default_gen_params.to_string().c_str()); - - sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(false, false, false); -+ ctx_params.lora_apply_mode = LORA_APPLY_AT_RUNTIME; - sd_ctx_t* sd_ctx = new_sd_ctx(&sd_ctx_params); - - if (sd_ctx == nullptr) { -@@ -414,7 +415,7 @@ int main(int argc, const char** argv) { - return; - } - -- if (!gen_params.process_and_check(IMG_GEN, "")) { -+ if (!gen_params.process_and_check(IMG_GEN, ctx_params.lora_model_dir)) { - res.status = 400; - res.set_content(R"({"error":"invalid params"})", "application/json"); - return; -@@ -592,7 +593,7 @@ int main(int argc, const char** argv) { - return; - } - -- if (!gen_params.process_and_check(IMG_GEN, "")) { -+ if (!gen_params.process_and_check(IMG_GEN, ctx_params.lora_model_dir)) { - res.status = 400; - res.set_content(R"({"error":"invalid params"})", "application/json"); - return; - -From 4b80b61003aa06f41c6bdec47ff926e37007b87d Mon Sep 17 00:00:00 2001 -From: mateusgpe -Date: Thu, 1 Jan 2026 15:24:01 -0300 -Subject: [PATCH 2/2] fix: sanitize LoRA paths and enable dynamic loading - -- Remove the restriction that LoRA models must be in the root of the LoRA directory, allowing them to be organized in subfolders. -- Refactor the directory containment check to use `std::mismatch` instead of `lexically_relative` to verify the path is inside the allowed root. -- Remove redundant `lexically_normal()` call when resolving file extensions. ---- - examples/common/common.hpp | 29 ++++++++++------------------- - 1 file changed, 10 insertions(+), 19 deletions(-) - -diff --git a/examples/common/common.hpp b/examples/common/common.hpp -index 7f869868c..a2e919409 100644 ---- a/examples/common/common.hpp -+++ b/examples/common/common.hpp -@@ -1610,13 +1610,12 @@ struct SDGenerationParams { - - fs::path raw_path(raw_path_str); - -- // Disallow absolute paths. -+ // Disallow absolute paths and '..' components - if (raw_path.is_absolute()) { - LOG_WARN("lora path must be relative: %s", raw_path_str.c_str()); - return false; - } - -- // Disallow '..' in the raw path to prevent basic traversal attempts. - for (const auto& part : raw_path) { - if (part == "..") { - LOG_WARN("lora path cannot contain '..': %s", raw_path_str.c_str()); -@@ -1624,34 +1623,26 @@ struct SDGenerationParams { - } - } - -+ // Construct and canonicalize paths - fs::path lora_dir(lora_model_dir); - full_path = lora_dir / raw_path; - -- // --- Security Checks on Canonical Path --- -- // Canonicalize paths to resolve symlinks and normalize separators for robust checks. -- // weakly_canonical is used because the target file might not exist yet. - auto canonical_lora_dir = fs::weakly_canonical(lora_dir); - auto canonical_full_path = fs::weakly_canonical(full_path); - -- // 1. The resolved path must not be a directory. -+ // Check if path is a directory - if (fs::is_directory(canonical_full_path)) { - LOG_WARN("lora path resolved to a directory, not a file: %s", raw_path_str.c_str()); - return false; - } - -- // 2. The file must be inside the designated lora directory. -- // We check this by ensuring the relative path does not climb up with '..'. -- fs::path relative_path = canonical_full_path.lexically_relative(canonical_lora_dir); -- for (const auto& part : relative_path) { -- if (part == "..") { -- LOG_WARN("lora path is outside of the lora model directory: %s", raw_path_str.c_str()); -- return false; -- } -- } -+ // Verify path stays within lora directory -+ auto [root_end, nothing] = std::mismatch( -+ canonical_lora_dir.begin(), canonical_lora_dir.end(), -+ canonical_full_path.begin(), canonical_full_path.end()); - -- // 3. The file must be directly in the lora directory, not in a subdirectory. -- if (relative_path.has_parent_path() && !relative_path.parent_path().empty()) { -- LOG_WARN("lora path in subdirectories is not allowed: %s", raw_path_str.c_str()); -+ if (root_end != canonical_lora_dir.end()) { -+ LOG_WARN("lora path is outside of the lora model directory: %s", raw_path_str.c_str()); - return false; - } - -@@ -1700,7 +1691,7 @@ struct SDGenerationParams { - fs::path try_path = final_path; - try_path += ext; - if (fs::exists(try_path)) { -- final_path = try_path.lexically_normal(); -+ final_path = try_path; - found = true; - break; - } diff --git a/packages/stable-diffusion-cpp/server_mask.patch b/packages/stable-diffusion-cpp/server_mask.patch deleted file mode 100644 index e9702a0..0000000 --- a/packages/stable-diffusion-cpp/server_mask.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git i/examples/server/main.cpp w/examples/server/main.cpp -index 9fa8804..b15daca 100644 ---- i/examples/server/main.cpp -+++ w/examples/server/main.cpp -@@ -537,7 +537,7 @@ int main(int argc, const char** argv) { - } - - std::vector mask_bytes; -- if (req.form.has_field("mask")) { -+ if (req.form.has_file("mask")) { - auto file = req.form.get_file("mask"); - mask_bytes.assign(file.content.begin(), file.content.end()); - }