1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2024-10-18 00:16:11 -04:00

Compare commits

...

19 commits

Author SHA1 Message Date
Eelco Dolstra c326bfdefa
Merge fc3f70cd5e into f51974d698 2024-10-17 00:09:53 +02:00
Robert Hensing f51974d698
Merge pull request #11665 from roberth/fix-Interrupted-falling-out-of-thread
Fix `Interrupted` falling out of thread crash
2024-10-16 20:09:29 +02:00
Robert Hensing ed184f0b61
Typo
Co-authored-by: Cole Helbling <cole.e.helbling@outlook.com>
2024-10-16 19:40:45 +02:00
Robert Hensing fd8a4a86d9 ThreadPool: don't silently ignore non-std exceptions
Introduced in 8f6b347abd without explanation.

Throwing anything that's not that is a programming mistake that we don't want
to ignore silently. A crash would be ok, because that means we/they can fix
the offending throw.
2024-10-16 17:56:08 +02:00
Robert Hensing 16320f6d24 Handle ThreadPoolShutdown with normal catch 2024-10-16 17:56:08 +02:00
Robert Hensing 3f9ff10786 ThreadPool: catch Interrupted 2024-10-16 17:56:08 +02:00
Robert Hensing de41e46175 Document recursive-nix startDaemon/stopDaemon 2024-10-16 17:56:08 +02:00
Robert Hensing 0e5a5303ad fix: Ignore Interrupted in recursive-nix daemon worker
Otherwise, if checkInterrupt() in any of the supported store operations
would catch onto a user interrupt, the exception would bubble to the thread
start and be handled by std::terminate(): a crash.
2024-10-16 17:56:08 +02:00
Eelco Dolstra fc3f70cd5e Return instead of throw RestrictedPathError 2024-09-25 14:32:39 +02:00
Eelco Dolstra be7dac494f Merge remote-tracking branch 'origin/master' into pathExists-regression 2024-09-25 14:21:43 +02:00
Eelco Dolstra 7a11e60389 Reword 2024-04-18 16:57:02 +02:00
Eelco Dolstra 873be03a42 Fix test 2024-04-16 18:50:06 +02:00
Eelco Dolstra 75a070c52e tests/functional/restricted.sh: Check error message 2024-04-16 18:45:02 +02:00
Eelco Dolstra b3a6b794b0 Fix "access to path is forbidden" 2024-04-16 18:45:02 +02:00
Eelco Dolstra 978b3648df Typo 2024-04-16 18:45:02 +02:00
Eelco Dolstra 9668546178 Remove unnecessary arg highlighting 2024-04-16 18:45:02 +02:00
Eelco Dolstra 702ca51fd2 Restore old behaviour wrt inaccessible parents of accessible paths
E.g. `pathExists` on such parents will return `false`, and `readDir`
will fail.
2024-04-16 18:45:02 +02:00
Eelco Dolstra e2443092a4 tests/functional/restricted.sh: Remove broken test
'--restrict-eval true' was probably not what was intended
(i.e. parsing a file named 'true').
2024-04-16 18:45:02 +02:00
Eelco Dolstra 4065f16888 pathExists: Return false on "/nix/store" in pure mode
AllowListInputAccessor has the invariant that if a path is accessible,
its parent directories are also considered accessible (though reading
them only yields the allowed subdirectories). As a result
`builtins.pathExists "/nix/store"` returns true.

However this wasn't the behaviour of previous path access control,
where `builtins.pathExists "/nix/store"` returns false even if a
subdirectory of the store is accessible.

Fixes #9672.
2024-04-16 13:14:52 +02:00
14 changed files with 114 additions and 71 deletions

View file

@ -247,12 +247,14 @@ EvalState::EvalState(
, emptyBindings(0) , emptyBindings(0)
, rootFS( , rootFS(
settings.restrictEval || settings.pureEval settings.restrictEval || settings.pureEval
? ref<SourceAccessor>(AllowListSourceAccessor::create(getFSSourceAccessor(), {}, ? ref<SourceAccessor>(AllowListSourceAccessor::create(getFSSourceAccessor(), {}, {},
[&settings](const CanonPath & path) -> RestrictedPathError { [&settings](const CanonPath & path) -> RestrictedPathError {
auto modeInformation = settings.pureEval return RestrictedPathError(
? "in pure evaluation mode (use '--impure' to override)" std::string("access to absolute path '%1%' is forbidden ") +
: "in restricted mode"; (settings.pureEval
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); ? "in pure evaluation mode (use '--impure' to override)"
: "in restricted mode"),
path);
})) }))
: getFSSourceAccessor()) : getFSSourceAccessor())
, corepkgsFS(make_ref<MemorySourceAccessor>()) , corepkgsFS(make_ref<MemorySourceAccessor>())

View file

@ -1,5 +1,7 @@
#include "filtering-source-accessor.hh" #include "filtering-source-accessor.hh"
#include <unordered_set>
namespace nix { namespace nix {
std::optional<std::filesystem::path> FilteringSourceAccessor::getPhysicalPath(const CanonPath & path) std::optional<std::filesystem::path> FilteringSourceAccessor::getPhysicalPath(const CanonPath & path)
@ -19,10 +21,15 @@ bool FilteringSourceAccessor::pathExists(const CanonPath & path)
return isAllowed(path) && next->pathExists(prefix / path); return isAllowed(path) && next->pathExists(prefix / path);
} }
std::optional<SourceAccessor::Stat> FilteringSourceAccessor::maybeLstat(const CanonPath & path) SourceAccessor::Stat FilteringSourceAccessor::lstat(const CanonPath & path)
{ {
checkAccess(path); checkAccess(path);
return next->maybeLstat(prefix / path); return next->lstat(prefix / path);
}
std::optional<SourceAccessor::Stat> FilteringSourceAccessor::maybeLstat(const CanonPath & path)
{
return isAllowed(path) ? next->maybeLstat(prefix / path) : std::nullopt;
} }
SourceAccessor::DirEntries FilteringSourceAccessor::readDirectory(const CanonPath & path) SourceAccessor::DirEntries FilteringSourceAccessor::readDirectory(const CanonPath & path)
@ -57,33 +64,46 @@ void FilteringSourceAccessor::checkAccess(const CanonPath & path)
struct AllowListSourceAccessorImpl : AllowListSourceAccessor struct AllowListSourceAccessorImpl : AllowListSourceAccessor
{ {
std::set<CanonPath> allowedPrefixes; std::unordered_set<CanonPath> allowedPrefixes;
std::unordered_set<CanonPath> allowedPaths;
AllowListSourceAccessorImpl( AllowListSourceAccessorImpl(
ref<SourceAccessor> next, ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes, std::unordered_set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError) MakeNotAllowedError && makeNotAllowedError)
: AllowListSourceAccessor(SourcePath(next), std::move(makeNotAllowedError)) : AllowListSourceAccessor(SourcePath(next), std::move(makeNotAllowedError))
, allowedPrefixes(std::move(allowedPrefixes)) , allowedPrefixes(std::move(allowedPrefixes))
, allowedPaths(std::move(allowedPaths))
{ } { }
bool isAllowed(const CanonPath & path) override bool isAllowed(const CanonPath & path) override
{ {
return path.isAllowed(allowedPrefixes); return allowedPaths.count(path) || path.isAllowed(allowedPrefixes);
} }
void allowPrefix(CanonPath prefix) override void allowPrefix(CanonPath prefix) override
{ {
allowedPrefixes.insert(std::move(prefix)); allowedPrefixes.insert(std::move(prefix));
} }
void allowPath(CanonPath path) override
{
allowedPaths.insert(std::move(path));
}
}; };
ref<AllowListSourceAccessor> AllowListSourceAccessor::create( ref<AllowListSourceAccessor> AllowListSourceAccessor::create(
ref<SourceAccessor> next, ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes, std::unordered_set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError) MakeNotAllowedError && makeNotAllowedError)
{ {
return make_ref<AllowListSourceAccessorImpl>(next, std::move(allowedPrefixes), std::move(makeNotAllowedError)); return make_ref<AllowListSourceAccessorImpl>(
next,
std::move(allowedPrefixes),
std::move(allowedPaths),
std::move(makeNotAllowedError));
} }
bool CachingFilteringSourceAccessor::isAllowed(const CanonPath & path) bool CachingFilteringSourceAccessor::isAllowed(const CanonPath & path)

View file

@ -36,6 +36,8 @@ struct FilteringSourceAccessor : SourceAccessor
bool pathExists(const CanonPath & path) override; bool pathExists(const CanonPath & path) override;
Stat lstat(const CanonPath & path) override;
std::optional<Stat> maybeLstat(const CanonPath & path) override; std::optional<Stat> maybeLstat(const CanonPath & path) override;
DirEntries readDirectory(const CanonPath & path) override; DirEntries readDirectory(const CanonPath & path) override;
@ -63,13 +65,20 @@ struct FilteringSourceAccessor : SourceAccessor
struct AllowListSourceAccessor : public FilteringSourceAccessor struct AllowListSourceAccessor : public FilteringSourceAccessor
{ {
/** /**
* Grant access to the specified prefix. * Grant access to the specified prefix, i.e. the path *and* its
* children.
*/ */
virtual void allowPrefix(CanonPath prefix) = 0; virtual void allowPrefix(CanonPath prefix) = 0;
/**
* Grant access to a path but not its children.
*/
virtual void allowPath(CanonPath path) = 0;
static ref<AllowListSourceAccessor> create( static ref<AllowListSourceAccessor> create(
ref<SourceAccessor> next, ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes, std::unordered_set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError); MakeNotAllowedError && makeNotAllowedError);
using FilteringSourceAccessor::FilteringSourceAccessor; using FilteringSourceAccessor::FilteringSourceAccessor;

View file

@ -1201,17 +1201,23 @@ ref<SourceAccessor> GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore
ref<SourceAccessor> GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) ref<SourceAccessor> GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
{ {
auto self = ref<GitRepoImpl>(shared_from_this()); auto self = ref<GitRepoImpl>(shared_from_this());
/* In case of an empty workdir, return an empty in-memory tree. We
cannot use AllowListSourceAccessor because it would return an /* Grant access to the parent directories of every accessible
error for the root (and we can't add the root to the allow-list file. The root is always accessible. */
since that would allow access to all its children). */ std::unordered_set<CanonPath> files{CanonPath::root};
ref<SourceAccessor> fileAccessor = for (auto path : wd.files) {
wd.files.empty() while (!path.isRoot()) {
? makeEmptySourceAccessor() if (!files.insert(path).second) break;
: AllowListSourceAccessor::create( path.pop();
makeFSSourceAccessor(path), }
std::set<CanonPath> { wd.files }, }
std::move(makeNotAllowedError)).cast<SourceAccessor>();
auto fileAccessor = AllowListSourceAccessor::create(
makeFSSourceAccessor(path),
{},
std::move(files),
std::move(makeNotAllowedError));
if (exportIgnore) if (exportIgnore)
return make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt); return make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt);
else else

View file

@ -65,6 +65,7 @@
#include <iostream> #include <iostream>
#include "strings.hh" #include "strings.hh"
#include "signals.hh"
namespace nix { namespace nix {
@ -1579,6 +1580,8 @@ void LocalDerivationGoal::startDaemon()
FdSink(remote.get()), FdSink(remote.get()),
NotTrusted, daemon::Recursive); NotTrusted, daemon::Recursive);
debug("terminated daemon connection"); debug("terminated daemon connection");
} catch (const Interrupted &) {
debug("interrupted daemon connection");
} catch (SystemError &) { } catch (SystemError &) {
ignoreExceptionExceptInterrupt(); ignoreExceptionExceptInterrupt();
} }

View file

@ -225,8 +225,15 @@ struct LocalDerivationGoal : public DerivationGoal
*/ */
void writeStructuredAttrs(); void writeStructuredAttrs();
/**
* Start an in-process nix daemon thread for recursive-nix.
*/
void startDaemon(); void startDaemon();
/**
* Stop the in-process nix daemon thread.
* @see startDaemon
*/
void stopDaemon(); void stopDaemon();
/** /**

View file

@ -90,25 +90,16 @@ CanonPath CanonPath::operator / (std::string_view c) const
return res; return res;
} }
bool CanonPath::isAllowed(const std::set<CanonPath> & allowed) const bool CanonPath::isAllowed(const std::unordered_set<CanonPath> & allowed) const
{ {
/* Check if `this` is an exact match or the parent of an
allowed path. */
auto lb = allowed.lower_bound(*this);
if (lb != allowed.end()) {
if (lb->isWithin(*this))
return true;
}
/* Check if a parent of `this` is allowed. */
auto path = *this; auto path = *this;
while (!path.isRoot()) { while (true) {
path.pop();
if (allowed.count(path)) if (allowed.count(path))
return true; return true;
if (path.isRoot())
return false;
path.pop();
} }
return false;
} }
std::ostream & operator << (std::ostream & stream, const CanonPath & path) std::ostream & operator << (std::ostream & stream, const CanonPath & path)

View file

@ -5,7 +5,7 @@
#include <optional> #include <optional>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <set> #include <unordered_set>
#include <vector> #include <vector>
namespace nix { namespace nix {
@ -209,12 +209,10 @@ public:
CanonPath operator / (std::string_view c) const; CanonPath operator / (std::string_view c) const;
/** /**
* Check whether access to this path is allowed, which is the case * Check whether access to this path is allowed, i.e. `this` is
* if 1) `this` is within any of the `allowed` paths; or 2) any of * within any of the `allowed` paths.
* the `allowed` paths are within `this`. (The latter condition
* ensures access to the parents of allowed paths.)
*/ */
bool isAllowed(const std::set<CanonPath> & allowed) const; bool isAllowed(const std::unordered_set<CanonPath> & allowed) const;
/** /**
* Return a representation `x` of `path` relative to `this`, i.e. * Return a representation `x` of `path` relative to `this`, i.e.

View file

@ -114,7 +114,7 @@ struct SourceAccessor : std::enable_shared_from_this<SourceAccessor>
std::optional<uint64_t> narOffset; std::optional<uint64_t> narOffset;
}; };
Stat lstat(const CanonPath & path); virtual Stat lstat(const CanonPath & path);
virtual std::optional<Stat> maybeLstat(const CanonPath & path) = 0; virtual std::optional<Stat> maybeLstat(const CanonPath & path) = 0;

View file

@ -110,10 +110,15 @@ void ThreadPool::doWork(bool mainThread)
propagate it. */ propagate it. */
try { try {
std::rethrow_exception(exc); std::rethrow_exception(exc);
} catch (const Interrupted &) {
// The interrupted state may be picked up by multiple
// workers, which is expected, so we should ignore
// it silently and let the first one bubble up,
// rethrown via the original state->exception.
} catch (const ThreadPoolShutDown &) {
// Similarly expected.
} catch (std::exception & e) { } catch (std::exception & e) {
if (!dynamic_cast<ThreadPoolShutDown*>(&e)) ignoreExceptionExceptInterrupt();
ignoreExceptionExceptInterrupt();
} catch (...) {
} }
} }
} }

View file

@ -42,6 +42,7 @@ cat > "$flake2Dir/flake.nix" <<EOF
outputs = { self, flake1 }: rec { outputs = { self, flake1 }: rec {
packages.$system.bar = flake1.packages.$system.foo; packages.$system.bar = flake1.packages.$system.foo;
foo = builtins.pathExists (self + "/..");
}; };
} }
EOF EOF
@ -274,6 +275,9 @@ nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" --commit-lock-file
[[ -e "$flake2Dir/flake.lock" ]] [[ -e "$flake2Dir/flake.lock" ]]
[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]] [[ -z $(git -C "$flake2Dir" diff main || echo failed) ]]
# Test that pathExist on the parent of a flake returns false.
[[ $(nix eval "$flake2Dir#foo") = false ]]
# Rerunning the build should not change the lockfile. # Rerunning the build should not change the lockfile.
nix build -o "$TEST_ROOT/result" "$flake2Dir#bar" nix build -o "$TEST_ROOT/result" "$flake2Dir#bar"
[[ -z $(git -C "$flake2Dir" diff main || echo failed) ]] [[ -z $(git -C "$flake2Dir" diff main || echo failed) ]]

View file

@ -153,7 +153,7 @@ foo + baz
' "3" \ ' "3" \
./flake ./flake\#bar --experimental-features 'flakes' ./flake ./flake\#bar --experimental-features 'flakes'
# Test the `:reload` mechansim with flakes: # Test the `:reload` mechanism with flakes:
# - Eval `./flake#changingThing` # - Eval `./flake#changingThing`
# - Modify the flake # - Modify the flake
# - Re-eval it # - Re-eval it

View file

@ -5,15 +5,15 @@ source common.sh
clearStoreIfPossible clearStoreIfPossible
nix-instantiate --restrict-eval --eval -E '1 + 2' nix-instantiate --restrict-eval --eval -E '1 + 2'
(! nix-instantiate --eval --restrict-eval ./restricted.nix) expectStderr 1 nix-instantiate --eval --restrict-eval ./restricted.nix | grepQuiet "forbidden in restricted mode"
(! nix-instantiate --eval --restrict-eval <(echo '1 + 2')) expectStderr 1 nix-instantiate --eval --restrict-eval <(echo '1 + 2') | grepQuiet "forbidden in restricted mode"
nix-instantiate --restrict-eval ./simple.nix -I src=. nix-instantiate --restrict-eval ./simple.nix -I src=.
nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
# no default NIX_PATH # no default NIX_PATH
(unset NIX_PATH; ! nix-instantiate --restrict-eval --find-file .) (unset NIX_PATH; expectStderr 1 nix-instantiate --restrict-eval --find-file . | grepQuiet "file '.' was not found in the Nix search path")
(! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix') expectStderr 1 nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' | grepQuiet "forbidden in restricted mode"
nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../.. nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../..
expectStderr 1 nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in builtins.readFile <foo/simple.nix>' | grepQuiet "forbidden in restricted mode" expectStderr 1 nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in builtins.readFile <foo/simple.nix>' | grepQuiet "forbidden in restricted mode"
@ -22,21 +22,21 @@ nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; p
p=$(nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)") p=$(nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)")
cmp $p restricted.sh cmp $p restricted.sh
(! nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval) expectStderr 1 nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval | grepQuiet "forbidden in restricted mode"
(! nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/") expectStderr 1 nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/" | grepQuiet "forbidden in restricted mode"
nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh" nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh"
(! nix eval --raw --expr "builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz" --impure --restrict-eval) expectStderr 1 nix eval --raw --expr "builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz" --impure --restrict-eval | grepQuiet "forbidden in restricted mode"
(! nix eval --raw --expr "builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz" --impure --restrict-eval) expectStderr 1 nix eval --raw --expr "builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz" --impure --restrict-eval | grepQuiet "forbidden in restricted mode"
(! nix eval --raw --expr "fetchGit git://github.com/NixOS/patchelf.git" --impure --restrict-eval) expectStderr 1 nix eval --raw --expr "fetchGit git://github.com/NixOS/patchelf.git" --impure --restrict-eval | grepQuiet "forbidden in restricted mode"
ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix
[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]] [[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]]
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix) expectStderr 1 nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix | grepQuiet "forbidden in restricted mode"
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT) expectStderr 1 nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT | grepQuiet "forbidden in restricted mode"
(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .) expectStderr 1 nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I . | grepQuiet "forbidden in restricted mode"
nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I . nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I .
[[ $(nix eval --raw --impure --restrict-eval -I . --expr 'builtins.readFile "${import ./simple.nix}/hello"') == 'Hello World!' ]] [[ $(nix eval --raw --impure --restrict-eval -I . --expr 'builtins.readFile "${import ./simple.nix}/hello"') == 'Hello World!' ]]
@ -50,8 +50,8 @@ expectStderr 1 nix-instantiate --restrict-eval --eval -E "let __nixPath = [ { pr
expectStderr 1 nix-instantiate --restrict-eval --eval -E "let __nixPath = [ { prefix = \"foo\"; path = $TEST_ROOT/tunnel.d; } ]; in builtins.readDir <foo/tunnel/foo2>" -I $TEST_ROOT/tunnel.d | grepQuiet "forbidden in restricted mode" expectStderr 1 nix-instantiate --restrict-eval --eval -E "let __nixPath = [ { prefix = \"foo\"; path = $TEST_ROOT/tunnel.d; } ]; in builtins.readDir <foo/tunnel/foo2>" -I $TEST_ROOT/tunnel.d | grepQuiet "forbidden in restricted mode"
# Reading the parents of allowed paths should show only the ancestors of the allowed paths. # Reading the parents of allowed paths is forbidden.
[[ $(nix-instantiate --restrict-eval --eval -E "let __nixPath = [ { prefix = \"foo\"; path = $TEST_ROOT/tunnel.d; } ]; in builtins.readDir <foo/tunnel>" -I $TEST_ROOT/tunnel.d) == '{ "tunnel.d" = "directory"; }' ]] expectStderr 1 nix-instantiate --restrict-eval --eval -E "let __nixPath = [ { prefix = \"foo\"; path = $TEST_ROOT/tunnel.d; } ]; in builtins.readDir <foo/tunnel>" -I $TEST_ROOT/tunnel.d | grepQuiet "forbidden in restricted mode"
# Check whether we can leak symlink information through directory traversal. # Check whether we can leak symlink information through directory traversal.
traverseDir="$(pwd)/restricted-traverse-me" traverseDir="$(pwd)/restricted-traverse-me"
@ -63,5 +63,3 @@ output="$(nix eval --raw --restrict-eval -I "$traverseDir" \
2>&1 || :)" 2>&1 || :)"
echo "$output" | grep "is forbidden" echo "$output" | grep "is forbidden"
echo "$output" | grepInverse -F restricted-secret echo "$output" | grepInverse -F restricted-secret
expectStderr 1 nix-instantiate --restrict-eval true ./dependencies.nix | grepQuiet "forbidden in restricted mode"

View file

@ -143,7 +143,7 @@ namespace nix {
} }
TEST(CanonPath, allowed) { TEST(CanonPath, allowed) {
std::set<CanonPath> allowed { std::unordered_set<CanonPath> allowed {
CanonPath("foo/bar"), CanonPath("foo/bar"),
CanonPath("foo!"), CanonPath("foo!"),
CanonPath("xyzzy"), CanonPath("xyzzy"),
@ -152,11 +152,11 @@ namespace nix {
ASSERT_TRUE (CanonPath("foo/bar").isAllowed(allowed)); ASSERT_TRUE (CanonPath("foo/bar").isAllowed(allowed));
ASSERT_TRUE (CanonPath("foo/bar/bla").isAllowed(allowed)); ASSERT_TRUE (CanonPath("foo/bar/bla").isAllowed(allowed));
ASSERT_TRUE (CanonPath("foo").isAllowed(allowed)); ASSERT_FALSE(CanonPath("foo").isAllowed(allowed));
ASSERT_FALSE(CanonPath("bar").isAllowed(allowed)); ASSERT_FALSE(CanonPath("bar").isAllowed(allowed));
ASSERT_FALSE(CanonPath("bar/a").isAllowed(allowed)); ASSERT_FALSE(CanonPath("bar/a").isAllowed(allowed));
ASSERT_TRUE (CanonPath("a").isAllowed(allowed)); ASSERT_FALSE(CanonPath("a").isAllowed(allowed));
ASSERT_TRUE (CanonPath("a/b").isAllowed(allowed)); ASSERT_FALSE(CanonPath("a/b").isAllowed(allowed));
ASSERT_TRUE (CanonPath("a/b/c").isAllowed(allowed)); ASSERT_TRUE (CanonPath("a/b/c").isAllowed(allowed));
ASSERT_TRUE (CanonPath("a/b/c/d").isAllowed(allowed)); ASSERT_TRUE (CanonPath("a/b/c/d").isAllowed(allowed));
ASSERT_TRUE (CanonPath("a/b/c/d/e").isAllowed(allowed)); ASSERT_TRUE (CanonPath("a/b/c/d/e").isAllowed(allowed));
@ -164,7 +164,7 @@ namespace nix {
ASSERT_FALSE(CanonPath("a/b/d").isAllowed(allowed)); ASSERT_FALSE(CanonPath("a/b/d").isAllowed(allowed));
ASSERT_FALSE(CanonPath("aaa").isAllowed(allowed)); ASSERT_FALSE(CanonPath("aaa").isAllowed(allowed));
ASSERT_FALSE(CanonPath("zzz").isAllowed(allowed)); ASSERT_FALSE(CanonPath("zzz").isAllowed(allowed));
ASSERT_TRUE (CanonPath("/").isAllowed(allowed)); ASSERT_FALSE(CanonPath("/").isAllowed(allowed));
} }
TEST(CanonPath, makeRelative) { TEST(CanonPath, makeRelative) {