From 8385c3ac9b4498bd66c26c4679def3fc7c0d3892 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Mon, 24 Nov 2025 18:37:10 +0000 Subject: [PATCH] (zilch nixpkgs): Attempt to limit Nix expression leakage Change-Id: Ifab2c0e7784145b9f57ca2c3cb713a476a6a6964 --- cli/zilch-ninja.scm | 16 +++++++++++ core/src/nixpkgs.sld | 40 ++++++++++++++++++++++---- docs/modules/ROOT/pages/ninja/man.adoc | 7 +++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/cli/zilch-ninja.scm b/cli/zilch-ninja.scm index 71acbe8..f6d8acd 100644 --- a/cli/zilch-ninja.scm +++ b/cli/zilch-ninja.scm @@ -22,6 +22,7 @@ (verbose #f #\v) (source #t #\s) (project #t #\p) + (trust-all #f #\T) (print-build-logs #f #\L)) (list->vector (cdr (command-line))) print-help)) @@ -62,10 +63,25 @@ (zilch zexpr) (srfi 128) (srfi 146) (srfi 152)) +(when (assoc 'trust-all options) (nixpkgs-eval-allow-all)) + (define source (and (assoc 'source options) (cdr (assoc 'source options)))) +(when source (nixpkgs-eval-allow-path source)) (define config-path (if (assoc 'config-file options) (cdr (assoc 'config-file options)) "zilch.scm")) +; This is fine, canonicalisation is done without influence of the FS. +(nixpkgs-eval-allow-path (string-append config-path "/..")) + +(define (allow-source-paths config) + (when (ninja-build-config-override-source-path config) + (nixpkgs-eval-allow-path (ninja-build-config-override-source-path))) + (for-each + (lambda (rewrite) + (allow-source-paths (cdr rewrite))) + (ninja-build-config-rewrites config))) + (define config (parse-ninja-config config-path (string=? (car args) "build")`(override-source: ,(and source (vfs-from-directory source)) ,@(call-with-input-file config-path read)))) +(allow-source-paths config) (when (and (ninja-build-config-depfile-path config) (file-exists? (ninja-build-config-depfile-path config))) (set-ninja-build-config-depfile! config (alist->mapping (make-default-comparator) (call-with-input-file (ninja-build-config-depfile-path config) read)))) diff --git a/core/src/nixpkgs.sld b/core/src/nixpkgs.sld index 4088575..531286e 100644 --- a/core/src/nixpkgs.sld +++ b/core/src/nixpkgs.sld @@ -5,12 +5,42 @@ (zilch magic) (zilch nix drv) (zilch nix hash) (zilch nix path) (zilch zexpr) (srfi 18) (srfi 128) (srfi 146) (srfi 152) - (chicken format) (chicken process) + (chicken format) (chicken process) (chicken pathname) + (chicken process-context) json) (export nix-prefetch-url nixpkgs nixpkgs-eval - nix-eval environment-for-derivation) + nix-eval environment-for-derivation + nixpkgs-eval-allow-all nixpkgs-eval-allow-path) (begin + (define trusted-paths '()) + + (define (normalize-path path) + (if (string-prefix? "/" path) + path + (normalize-pathname (string-append (current-directory) "/" path) 'unix))) + + ;; Add a path to the list of trusted paths for Nix evaluations. + ;; Paths will be normalized to be absolute, relative to the CWD. + ;; This does not check symlinks, or the existence of any path component. + (define (nixpkgs-eval-allow-path path) + (when (and path trusted-paths) + (set! trusted-paths (cons (normalize-path path) trusted-paths)))) + + ;; Don't restrict Nix evaluations. + (define (nixpkgs-eval-allow-all) + (set! trusted-paths #f)) + + ; Append ">={path}" to the NIX_PATH for each argument. + ; This is a hack as this is invalid channel syntax, e.g. `<<>` is invalid. + (define (nixpkgs-eval-trust-arguments) + (if trusted-paths + (let loop ((out '("--option" "restrict-eval" "true")) (args trusted-paths)) + (if (null? args) + out + (loop (cons "-I" (cons (string-append ">=" (car args)) out)) (cdr args)))) + '())) + (define (run-stderr-thread prefix port) (define line (read-line port)) (if (eof-object? line) @@ -20,7 +50,7 @@ (run-stderr-thread prefix port)))) (define (read-from-nixpkgs path) - (define-values (stdout stdin pid stderr) (process* "nix-instantiate" `("--argstr" "path" ,path "-E" "{path}: let nixpkgs = import {}; in nixpkgs.${path}.out"))) + (define-values (stdout stdin pid stderr) (process* "nix-instantiate" `(,@(nixpkgs-eval-trust-arguments) "--argstr" "path" ,path "-E" "{path}: let nixpkgs = import {}; in nixpkgs.${path}.out"))) (define thread (thread-start! (make-thread (lambda () (run-stderr-thread (string-append "nixpkgs." path) stderr)) "read-from-nixpkgs stderr passthrough"))) (close-port stdin) (define drvpath (read-line stdout)) @@ -29,7 +59,7 @@ drvpath) (define (read-from-nixpkgs-raw path) - (define-values (stdout stdin pid stderr) (process* "nix-instantiate" `("-E" ,(string-append "with import {}; (" path ")")))) + (define-values (stdout stdin pid stderr) (process* "nix-instantiate" `(,@(nixpkgs-eval-trust-arguments) "-E" ,(string-append "with import {}; (" path ")")))) (define thread (thread-start! (make-thread (lambda () (run-stderr-thread (string-append path) stderr)) "read-from-nixpkgs-raw stderr passthrough"))) (close-port stdin) (define drvpath (read-line stdout)) @@ -42,7 +72,7 @@ drvpath) (define (read-from-eval code) - (define-values (stdout stdin pid stderr) (process* "nix-instantiate" `("--json" "--eval" "--strict" "--read-write-mode" "-E" ,code))) + (define-values (stdout stdin pid stderr) (process* "nix-instantiate" `(,@(nixpkgs-eval-trust-arguments) "--json" "--eval" "--strict" "--read-write-mode" "-E" ,code))) (define thread (thread-start! (make-thread (lambda () (run-stderr-thread (string-append "eval `" code "`") stderr)) "read-from-eval stderr passthrough"))) (close-port stdin) (define output (json-read stdout)) diff --git a/docs/modules/ROOT/pages/ninja/man.adoc b/docs/modules/ROOT/pages/ninja/man.adoc index 77c49dd..52200cd 100644 --- a/docs/modules/ROOT/pages/ninja/man.adoc +++ b/docs/modules/ROOT/pages/ninja/man.adoc @@ -65,6 +65,13 @@ written. See the documentation at https://puck.moe/zilch/docs/. *--verbose*:: Increase the verbosity configured in the Nix daemon. +*-T*:: +*--trust-all*:: + By default, Nix expressions can only access the paths in NIX_PATH, + plus the source directories and the directory of the configuration. + Setting this flag drops this restriction, at the cost of Nix + expressions being able to escape the Nix sandbox easier. + *-L*:: *--print-build-logs*:: Print derivation logs as they come in.