From a8670e8a7da337e230ecd31bc81a040af208f9d0 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 30 Sep 2025 03:16:35 +0300 Subject: [PATCH] libexpr-tests: Add unit tests for broken readDir /. for pure eval A very unfortunate interaction of current filtering with pure eval is that the following actually leads to `lib.a = {}`. This just adds a unit test for this broken behavior. This is really good to be done as a unit test via the in-memory store. { outputs = { ... }: { lib.a = builtins.readDir /.; }; } --- .../include/nix/expr/tests/libexpr.hh | 13 ++++++- src/libexpr-tests/eval.cc | 38 +++++++++++++++++++ .../include/nix/store/tests/libstore.hh | 13 +++---- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/libexpr-test-support/include/nix/expr/tests/libexpr.hh b/src/libexpr-test-support/include/nix/expr/tests/libexpr.hh index 4cf985e15..a1320e14a 100644 --- a/src/libexpr-test-support/include/nix/expr/tests/libexpr.hh +++ b/src/libexpr-test-support/include/nix/expr/tests/libexpr.hh @@ -26,11 +26,20 @@ public: } protected: - LibExprTest() + LibExprTest(ref store, auto && makeEvalSettings) : LibStoreTest() + , evalSettings(makeEvalSettings(readOnlyMode)) , state({}, store, fetchSettings, evalSettings, nullptr) { - evalSettings.nixPath = {}; + } + + LibExprTest() + : LibExprTest(openStore("dummy://"), [](bool & readOnlyMode) { + EvalSettings settings{readOnlyMode}; + settings.nixPath = {}; + return settings; + }) + { } Value eval(std::string input, bool forceValue = true) diff --git a/src/libexpr-tests/eval.cc b/src/libexpr-tests/eval.cc index ad70ea5b8..7562a9da2 100644 --- a/src/libexpr-tests/eval.cc +++ b/src/libexpr-tests/eval.cc @@ -3,6 +3,7 @@ #include "nix/expr/eval.hh" #include "nix/expr/tests/libexpr.hh" +#include "nix/util/memory-source-accessor.hh" namespace nix { @@ -174,4 +175,41 @@ TEST_F(EvalStateTest, getBuiltin_fail) ASSERT_THROW(state.getBuiltin("nonexistent"), EvalError); } +class PureEvalTest : public LibExprTest +{ +public: + PureEvalTest() + : LibExprTest(openStore("dummy://", {{"read-only", "false"}}), [](bool & readOnlyMode) { + EvalSettings settings{readOnlyMode}; + settings.pureEval = true; + settings.restrictEval = true; + return settings; + }) + { + } +}; + +TEST_F(PureEvalTest, pathExists) +{ + ASSERT_THAT(eval("builtins.pathExists /."), IsFalse()); + ASSERT_THAT(eval("builtins.pathExists /nix"), IsFalse()); + ASSERT_THAT(eval("builtins.pathExists /nix/store"), IsFalse()); + + { + std::string contents = "Lorem ipsum"; + + StringSource s{contents}; + auto path = state.store->addToStoreFromDump( + s, "source", FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256); + auto printed = store->printStorePath(path); + + ASSERT_THROW(eval(fmt("builtins.readFile %s", printed)), RestrictedPathError); + ASSERT_THAT(eval(fmt("builtins.pathExists %s", printed)), IsFalse()); + + ASSERT_THROW(eval("builtins.readDir /."), RestrictedPathError); + state.allowPath(path); // FIXME: This shouldn't behave this way. + ASSERT_THAT(eval("builtins.readDir /."), IsAttrsOfSize(0)); + } +} + } // namespace nix diff --git a/src/libstore-test-support/include/nix/store/tests/libstore.hh b/src/libstore-test-support/include/nix/store/tests/libstore.hh index 28b29fa31..d79b55312 100644 --- a/src/libstore-test-support/include/nix/store/tests/libstore.hh +++ b/src/libstore-test-support/include/nix/store/tests/libstore.hh @@ -19,14 +19,13 @@ public: } protected: + LibStoreTest(ref store) + : store(std::move(store)) + { + } + LibStoreTest() - : store(openStore({ - .variant = - StoreReference::Specified{ - .scheme = "dummy", - }, - .params = {}, - })) + : LibStoreTest(openStore("dummy://")) { }