libexpr/flake-config: collect untrusted settings into one prompt
Instead of showing untrusted configuration items one by one, collect all of them into one batched ask with 4 options: - apply the configuration wholesale **for now** (yes) - apply the configuration wholesale and trust it permanently (allow permanently) - selectively choose subset (no) - rejecting the configuration wholesale (no to all) Note that we do not offer options to reject permanently a configuration, that's what `--no-accept-flake-config` is for. Individual prompts gain a new option to distinguish "yes for now" and "yes forever". Fixes #682. Change-Id: I2492717ac277af33ea4ac9b1a11bd7d6d1bfe0db
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
---
|
||||
synopsis: "Collect Flakes untrusted settings into one prompt"
|
||||
cls: [2921]
|
||||
issues: [fj#682]
|
||||
category: "Improvements"
|
||||
credits: [isabelroses, raito, horrors]
|
||||
---
|
||||
|
||||
When working with Flakes containing untrusted settings, a prompt is shown for each setting, asking whether to vet or approve it. This looks like:
|
||||
|
||||
```
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all)
|
||||
```
|
||||
|
||||
In Flakes with a large number of settings to approve or reject, this process can become tedious as each option must be handled individually.
|
||||
|
||||
To address this, all untrusted settings are now consolidated into a single prompt: allowing for bulk acceptance permanently or not, rejection, or detailed review. For example:
|
||||
|
||||
### Scrutiny scenario
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) n
|
||||
warning: you can set 'accept-flake-config' to 'false' to automatically reject configuration options supplied by flakes
|
||||
Do you want to allow setting 'allow-dirty = false'? (yes for now/Allow always/no for now) y
|
||||
Do you want to allow setting 'sandbox = false'? (yes for now/Allow always/no for now) n
|
||||
```
|
||||
|
||||
### Reject everything scenario
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) N
|
||||
Rejecting all untrusted nix.conf entries
|
||||
warning: you can set 'accept-flake-config' to 'false' to automatically reject configuration options supplied by flakes
|
||||
```
|
||||
|
||||
### Accept everything scenario
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) y
|
||||
```
|
||||
|
||||
### Accept everything PERMANENTLY scenario
|
||||
|
||||
Note that accepting everything permanently will authorize these options for any
|
||||
further operations.
|
||||
|
||||
The file containing this trust information is usually located in
|
||||
`~/.local/share/nix/trusted-settings.json` and can be edited manually to revoke
|
||||
this permission until Lix provides a first-class command for this manipulation.
|
||||
|
||||
```console
|
||||
❯ nix flake lock
|
||||
warning: ignoring untrusted flake configuration setting 'allow-dirty', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
warning: ignoring untrusted flake configuration setting 'sandbox', pass '--accept-flake-config' to trust it (may allow the flake to gain root, see the nix.conf manual page)
|
||||
The following settings require your decision:
|
||||
- allow-dirty = false
|
||||
- sandbox = false
|
||||
Do you want to allow configuration settings to be applied?
|
||||
This may allow the flake to gain root, see the nix.conf manual page (yes for now/Allow always/no/No to all) A
|
||||
```
|
||||
+76
-29
@@ -1,4 +1,5 @@
|
||||
#include "lix/libexpr/flake/flake.hh"
|
||||
#include "lix/libutil/fmt.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
@@ -29,19 +30,28 @@ static void writeTrustedList(const TrustedList & trustedList)
|
||||
writeFile(path, JSON(trustedList).dump());
|
||||
}
|
||||
|
||||
static bool askForSetting(
|
||||
static bool batchAskForSetting(
|
||||
bool & negativeTrustOverride,
|
||||
TrustedList & trustedList,
|
||||
const std::string & name,
|
||||
const std::string & valueS)
|
||||
std::map<std::string, std::string> & untrustedSettings)
|
||||
{
|
||||
bool trusted = false;
|
||||
printWarning("The following settings require your decision:");
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
// FIXME: filter ANSI escapes, newlines, \r, etc.
|
||||
logger->cout("- %s = %s", name, valueS);
|
||||
}
|
||||
|
||||
// FIXME: filter ANSI escapes, newlines, \r, etc.
|
||||
auto reply = logger->ask(fmt("Do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "'?\nThis may allow the flake to gain root, see the nix.conf manual page (" ANSI_BOLD "y" ANSI_NORMAL "es/" ANSI_BOLD "n" ANSI_NORMAL "o/" ANSI_BOLD "N" ANSI_NORMAL "o to all) ", name, valueS)).value_or('n');
|
||||
auto reply = logger
|
||||
->ask(
|
||||
fmt("Do you want to allow configuration settings to be applied?\nThis may allow the "
|
||||
"flake to gain root, see the nix.conf manual page (" ANSI_BOLD "y" ANSI_NORMAL
|
||||
"es for now/" ANSI_BOLD "A" ANSI_NORMAL "llow always/" ANSI_BOLD "n" ANSI_NORMAL
|
||||
"o/" ANSI_BOLD "N" ANSI_NORMAL "o to all) ")
|
||||
)
|
||||
.value_or('n');
|
||||
|
||||
if (reply == 'N') {
|
||||
printTaggedWarning("Rejecting all untrusted nix.conf entries");
|
||||
printWarning("Rejecting all untrusted nix.conf entries");
|
||||
printTaggedWarning(
|
||||
"you can set '%s' to '%b' to automatically reject configuration options supplied by "
|
||||
"flakes",
|
||||
@@ -49,25 +59,59 @@ static bool askForSetting(
|
||||
false
|
||||
);
|
||||
negativeTrustOverride = true;
|
||||
} else {
|
||||
if (std::tolower(reply) == 'y') {
|
||||
trusted = true;
|
||||
} else {
|
||||
printTaggedWarning(
|
||||
"you can set '%s' to '%b' to automatically reject configuration options supplied "
|
||||
"by flakes",
|
||||
"accept-flake-config",
|
||||
false
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply == 'y' || reply == 'A') {
|
||||
auto alwaysAllow = reply == 'A';
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
if (alwaysAllow) {
|
||||
trustedList[name][valueS] = true;
|
||||
}
|
||||
globalConfig.set(name, valueS);
|
||||
}
|
||||
|
||||
if (std::tolower(logger->ask(fmt("do you want to permanently (in %s) mark this value as %s? (y/N) ", trustedListPath(), trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') {
|
||||
trustedList[name][valueS] = trusted;
|
||||
if (alwaysAllow) {
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
printTaggedWarning(
|
||||
"you can set '%s' to '%b' to automatically reject configuration options supplied "
|
||||
"by flakes",
|
||||
"accept-flake-config",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
auto didTrustedListChange = false;
|
||||
for (const auto & [name, valueS] : untrustedSettings) {
|
||||
auto individualReply = logger
|
||||
->ask(
|
||||
fmt("Do you want to allow setting '%s = %s'? (" ANSI_BOLD
|
||||
"y" ANSI_NORMAL "es for now/" ANSI_BOLD "A" ANSI_NORMAL
|
||||
"llow always/" ANSI_BOLD "n" ANSI_NORMAL "o for now) ",
|
||||
name,
|
||||
valueS)
|
||||
)
|
||||
.value_or('n');
|
||||
|
||||
if (individualReply == 'y' || individualReply == 'A') {
|
||||
if (individualReply == 'A') {
|
||||
trustedList[name][valueS] = true;
|
||||
didTrustedListChange = true;
|
||||
}
|
||||
|
||||
globalConfig.set(name, valueS);
|
||||
}
|
||||
}
|
||||
|
||||
return trusted;
|
||||
if (didTrustedListChange) {
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigFile::apply()
|
||||
@@ -77,8 +121,11 @@ void ConfigFile::apply()
|
||||
// Allows to ignore all subsequent settings from this file.
|
||||
bool negativeTrustOverride = false;
|
||||
|
||||
for (auto & [name, value] : settings) {
|
||||
std::map<std::string, std::string> untrustedSettings;
|
||||
|
||||
TrustedList trustedList = readTrustedList();
|
||||
|
||||
for (auto & [name, value] : settings) {
|
||||
auto baseName = name.starts_with("extra-") ? std::string(name, 6) : name;
|
||||
|
||||
// FIXME: Move into libutil/config.cc.
|
||||
@@ -90,11 +137,12 @@ void ConfigFile::apply()
|
||||
else if (auto* b = std::get_if<Explicit<bool>>(&value))
|
||||
valueS = b->t ? "true" : "false";
|
||||
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
|
||||
valueS = concatStringsSep(" ", *ss); // FIXME: evil
|
||||
valueS = concatStringsSep(" ", *ss); // FIXME: evil
|
||||
else
|
||||
assert(false);
|
||||
|
||||
bool trusted = whitelist.count(baseName);
|
||||
|
||||
if (!trusted) {
|
||||
switch (nix::fetchSettings.acceptFlakeConfig.get()) {
|
||||
case AcceptFlakeConfig::True: {
|
||||
@@ -102,21 +150,16 @@ void ConfigFile::apply()
|
||||
break;
|
||||
}
|
||||
case AcceptFlakeConfig::Ask: {
|
||||
auto trustedList = readTrustedList();
|
||||
auto tlname = get(trustedList, name);
|
||||
if (auto saved = tlname ? get(*tlname, valueS) : nullptr) {
|
||||
trusted = *saved;
|
||||
printInfo("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name, valueS);
|
||||
} else {
|
||||
if (negativeTrustOverride) {
|
||||
trusted = false;
|
||||
} else {
|
||||
trusted = askForSetting(negativeTrustOverride, trustedList, name, valueS);
|
||||
}
|
||||
untrustedSettings[name] = valueS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nix::AcceptFlakeConfig::False: {
|
||||
case AcceptFlakeConfig::False: {
|
||||
trusted = false;
|
||||
break;
|
||||
};
|
||||
@@ -135,6 +178,10 @@ void ConfigFile::apply()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!untrustedSettings.empty()) {
|
||||
batchAskForSetting(negativeTrustOverride, trustedList, untrustedSettings);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user