allowed-uris: Match whole schemes also when scheme is not followed by slashes

(cherry picked from commit a05bc9eb92)
This commit is contained in:
Robert Hensing 2023-12-06 15:27:29 +01:00
parent 2116ee2454
commit ffb6246650
4 changed files with 63 additions and 1 deletions

View File

@ -0,0 +1,7 @@
---
synopsis: Option `allowed-uris` can now match whole schemes in URIs without slashes
prs: 9547
---
If a scheme, such as `github:` is specified in the `allowed-uris` option, all URIs starting with `github:` are allowed.
Previously this only worked for schemes whose URIs used the `://` syntax.

View File

@ -68,6 +68,11 @@ struct EvalSettings : Config
evaluation mode. For example, when set to evaluation mode. For example, when set to
`https://github.com/NixOS`, builtin functions such as `fetchGit` are `https://github.com/NixOS`, builtin functions such as `fetchGit` are
allowed to access `https://github.com/NixOS/patchelf.git`. allowed to access `https://github.com/NixOS/patchelf.git`.
Access is granted when
- the URI is equal to the prefix,
- or the URI is a subpath of the prefix,
- or the prefix is a URI scheme ended by a colon `:` and the URI has the same scheme.
)"}; )"};
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls", Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",

View File

@ -16,6 +16,7 @@
#include "fs-input-accessor.hh" #include "fs-input-accessor.hh"
#include "memory-input-accessor.hh" #include "memory-input-accessor.hh"
#include "signals.hh" #include "signals.hh"
#include "url.hh"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
@ -602,6 +603,14 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
mkStorePathString(storePath, v); mkStorePathString(storePath, v);
} }
inline static bool isJustSchemePrefix(std::string_view prefix)
{
return
!prefix.empty()
&& prefix[prefix.size() - 1] == ':'
&& isValidSchemeName(prefix.substr(0, prefix.size() - 1));
}
SourcePath EvalState::checkSourcePath(const SourcePath & path_) SourcePath EvalState::checkSourcePath(const SourcePath & path_)
{ {
@ -663,8 +672,14 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris)
&& prefix.size() > 0 && prefix.size() > 0
&& hasPrefix(uri, prefix) && hasPrefix(uri, prefix)
&& ( && (
// Allow access to subdirectories of the prefix.
prefix[prefix.size() - 1] == '/' prefix[prefix.size() - 1] == '/'
|| uri[prefix.size()] == '/'))) || uri[prefix.size()] == '/'
// Allow access to whole schemes
|| isJustSchemePrefix(prefix)
)
))
return true; return true;
} }

View File

@ -103,4 +103,39 @@ TEST(nix_isAllowedURI, file_url) {
ASSERT_FALSE(isAllowedURI("file://", allowed)); ASSERT_FALSE(isAllowedURI("file://", allowed));
} }
TEST(nix_isAllowedURI, github_all) {
Strings allowed;
allowed.push_back("github:");
ASSERT_TRUE(isAllowedURI("github:", allowed));
ASSERT_TRUE(isAllowedURI("github:foo/bar", allowed));
ASSERT_TRUE(isAllowedURI("github:foo/bar/feat-multi-bar", allowed));
ASSERT_TRUE(isAllowedURI("github:foo/bar?ref=refs/heads/feat-multi-bar", allowed));
ASSERT_TRUE(isAllowedURI("github://foo/bar", allowed));
ASSERT_FALSE(isAllowedURI("https://github:443/foo/bar/archive/master.tar.gz", allowed));
ASSERT_FALSE(isAllowedURI("file://github:foo/bar/archive/master.tar.gz", allowed));
ASSERT_FALSE(isAllowedURI("file:///github:foo/bar/archive/master.tar.gz", allowed));
ASSERT_FALSE(isAllowedURI("github", allowed));
}
TEST(nix_isAllowedURI, github_org) {
Strings allowed;
allowed.push_back("github:foo");
ASSERT_FALSE(isAllowedURI("github:", allowed));
ASSERT_TRUE(isAllowedURI("github:foo/bar", allowed));
ASSERT_TRUE(isAllowedURI("github:foo/bar/feat-multi-bar", allowed));
ASSERT_TRUE(isAllowedURI("github:foo/bar?ref=refs/heads/feat-multi-bar", allowed));
ASSERT_FALSE(isAllowedURI("github://foo/bar", allowed));
ASSERT_FALSE(isAllowedURI("https://github:443/foo/bar/archive/master.tar.gz", allowed));
ASSERT_FALSE(isAllowedURI("file://github:foo/bar/archive/master.tar.gz", allowed));
ASSERT_FALSE(isAllowedURI("file:///github:foo/bar/archive/master.tar.gz", allowed));
}
TEST(nix_isAllowedURI, non_scheme_colon) {
Strings allowed;
allowed.push_back("https://foo/bar:");
ASSERT_TRUE(isAllowedURI("https://foo/bar:", allowed));
ASSERT_TRUE(isAllowedURI("https://foo/bar:/baz", allowed));
ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed));
}
} // namespace nix } // namespace nix