Apply makeNotAllowedError to empty repos

(cherry picked from commit 67e957b636)

# Conflicts:
#	src/libexpr/eval.cc
#	tests/functional/flakes/meson.build
This commit is contained in:
Eelco Dolstra 2025-04-01 15:19:46 +02:00 committed by Mergify
parent 06add7e551
commit 59fb0a72c6
6 changed files with 87 additions and 10 deletions

View File

@ -246,6 +246,7 @@ EvalState::EvalState(
, repair(NoRepair)
, emptyBindings(0)
, rootFS(
<<<<<<< HEAD
settings.restrictEval || settings.pureEval
? ref<SourceAccessor>(AllowListSourceAccessor::create(getFSSourceAccessor(), {},
[&settings](const CanonPath & path) -> RestrictedPathError {
@ -255,6 +256,44 @@ EvalState::EvalState(
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
}))
: getFSSourceAccessor())
=======
({
/* In pure eval mode, we provide a filesystem that only
contains the Nix store.
If we have a chroot store and pure eval is not enabled,
use a union accessor to make the chroot store available
at its logical location while still having the
underlying directory available. This is necessary for
instance if we're evaluating a file from the physical
/nix/store while using a chroot store. */
auto accessor = getFSSourceAccessor();
auto realStoreDir = dirOf(store->toRealPath(StorePath::dummy));
if (settings.pureEval || store->storeDir != realStoreDir) {
auto storeFS = makeMountedSourceAccessor(
{
{CanonPath::root, makeEmptySourceAccessor()},
{CanonPath(store->storeDir), makeFSSourceAccessor(realStoreDir)}
});
accessor = settings.pureEval
? storeFS
: makeUnionSourceAccessor({accessor, storeFS});
}
/* Apply access control if needed. */
if (settings.restrictEval || settings.pureEval)
accessor = AllowListSourceAccessor::create(accessor, {}, {},
[&settings](const CanonPath & path) -> RestrictedPathError {
auto modeInformation = settings.pureEval
? "in pure evaluation mode (use '--impure' to override)"
: "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
});
accessor;
}))
>>>>>>> 67e957b63 (Apply makeNotAllowedError to empty repos)
, corepkgsFS(make_ref<MemorySourceAccessor>())
, internalFS(make_ref<MemorySourceAccessor>())
, derivationInternal{corepkgsFS->addFile(

View File

@ -58,18 +58,23 @@ void FilteringSourceAccessor::checkAccess(const CanonPath & path)
struct AllowListSourceAccessorImpl : AllowListSourceAccessor
{
std::set<CanonPath> allowedPrefixes;
std::unordered_set<CanonPath> allowedPaths;
AllowListSourceAccessorImpl(
ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError)
: AllowListSourceAccessor(SourcePath(next), std::move(makeNotAllowedError))
, allowedPrefixes(std::move(allowedPrefixes))
, allowedPaths(std::move(allowedPaths))
{ }
bool isAllowed(const CanonPath & path) override
{
return path.isAllowed(allowedPrefixes);
return
allowedPaths.contains(path)
|| path.isAllowed(allowedPrefixes);
}
void allowPrefix(CanonPath prefix) override
@ -81,9 +86,14 @@ struct AllowListSourceAccessorImpl : AllowListSourceAccessor
ref<AllowListSourceAccessor> AllowListSourceAccessor::create(
ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
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)

View File

@ -2,6 +2,8 @@
#include "source-path.hh"
#include <unordered_set>
namespace nix {
/**
@ -70,6 +72,7 @@ struct AllowListSourceAccessor : public FilteringSourceAccessor
static ref<AllowListSourceAccessor> create(
ref<SourceAccessor> next,
std::set<CanonPath> && allowedPrefixes,
std::unordered_set<CanonPath> && allowedPaths,
MakeNotAllowedError && makeNotAllowedError);
using FilteringSourceAccessor::FilteringSourceAccessor;

View File

@ -1214,16 +1214,12 @@ ref<SourceAccessor> GitRepoImpl::getAccessor(
ref<SourceAccessor> GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError)
{
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
error for the root (and we can't add the root to the allow-list
since that would allow access to all its children). */
ref<SourceAccessor> fileAccessor =
wd.files.empty()
? makeEmptySourceAccessor()
: AllowListSourceAccessor::create(
AllowListSourceAccessor::create(
makeFSSourceAccessor(path),
std::set<CanonPath> { wd.files },
std::set<CanonPath>{ wd.files },
// Always allow access to the root, but not its children.
std::unordered_set<CanonPath>{CanonPath::root},
std::move(makeNotAllowedError)).cast<SourceAccessor>();
if (exportIgnore)
return make_ref<GitExportIgnoreSourceAccessor>(self, fileAccessor, std::nullopt);

View File

@ -28,6 +28,12 @@ suites += {
'commit-lock-file-summary.sh',
'non-flake-inputs.sh',
'relative-paths.sh',
<<<<<<< HEAD
=======
'symlink-paths.sh',
'debugger.sh',
'source-paths.sh',
>>>>>>> 67e957b63 (Apply makeNotAllowedError to empty repos)
],
'workdir': meson.current_source_dir(),
}

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
source ./common.sh
requireGit
repo=$TEST_ROOT/repo
createGitRepo "$repo"
cat > "$repo/flake.nix" <<EOF
{
outputs = { ... }: {
x = 1;
};
}
EOF
expectStderr 1 nix eval "$repo#x" | grepQuiet "error: Path 'flake.nix' in the repository \"$repo\" is not tracked by Git."
git -C "$repo" add flake.nix
[[ $(nix eval "$repo#x") = 1 ]]