1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-07 14:21:48 +02:00

Disallow store path names that are . or .. (plus opt. -)

As discussed in the maintainer meeting on 2024-01-29.

Mainly this is to avoid a situation where the name is parsed and
treated as a file name, mostly to protect users.
.-* and ..-* are also considered invalid because they might strip
on that separator to remove versions. Doesn't really work, but that's
what we decided, and I won't argue with it, because .-* probably
doesn't seem to have a real world application anyway.
We do still permit a 1-character name that's just "-", which still
poses a similar risk in such a situation. We can't start disallowing
trailing -, because a non-zero number of users will need it and we've
seen how annoying and painful such a change is.

What matters most is preventing a situation where . or .. can be
injected, and to just get this done.
This commit is contained in:
Robert Hensing 2024-01-30 18:37:23 +01:00
parent 8406da2877
commit f1b4663805
4 changed files with 88 additions and 2 deletions

View file

@ -39,6 +39,12 @@ TEST_DONT_PARSE(double_star, "**")
TEST_DONT_PARSE(star_first, "*,foo")
TEST_DONT_PARSE(star_second, "foo,*")
TEST_DONT_PARSE(bang, "foo!o")
TEST_DONT_PARSE(dot, ".")
TEST_DONT_PARSE(dot_dot, "..")
TEST_DONT_PARSE(dot_dot_dash, "..-1")
TEST_DONT_PARSE(dot_dash, ".-1")
TEST_DONT_PARSE(dot_dot_dash_a, "..-a")
TEST_DONT_PARSE(dot_dash_a, ".-a")
#undef TEST_DONT_PARSE
@ -63,6 +69,10 @@ TEST_DO_PARSE(period, "foo.txt")
TEST_DO_PARSE(question_mark, "foo?why")
TEST_DO_PARSE(equals_sign, "foo=foo")
TEST_DO_PARSE(dotfile, ".gitignore")
TEST_DO_PARSE(triple_dot_a, "...a")
TEST_DO_PARSE(triple_dot_1, "...1")
TEST_DO_PARSE(triple_dot_dash, "...-")
TEST_DO_PARSE(triple_dot, "...")
#undef TEST_DO_PARSE
@ -84,6 +94,64 @@ RC_GTEST_FIXTURE_PROP(
RC_ASSERT(p == store->parseStorePath(store->printStorePath(p)));
}
RC_GTEST_FIXTURE_PROP(
StorePathTest,
prop_check_regex_eq_parse,
())
{
static auto nameFuzzer =
rc::gen::container<std::string>(
rc::gen::oneOf(
// alphanum, repeated to weigh heavier
rc::gen::oneOf(
rc::gen::inRange('0', '9'),
rc::gen::inRange('a', 'z'),
rc::gen::inRange('A', 'Z')
),
// valid symbols
rc::gen::oneOf(
rc::gen::just('+'),
rc::gen::just('-'),
rc::gen::just('.'),
rc::gen::just('_'),
rc::gen::just('?'),
rc::gen::just('=')
),
// symbols for scary .- and ..- cases, repeated for weight
rc::gen::just('.'), rc::gen::just('.'),
rc::gen::just('.'), rc::gen::just('.'),
rc::gen::just('-'), rc::gen::just('-'),
// ascii symbol ranges
rc::gen::oneOf(
rc::gen::inRange(' ', '/'),
rc::gen::inRange(':', '@'),
rc::gen::inRange('[', '`'),
rc::gen::inRange('{', '~')
),
// typical whitespace
rc::gen::oneOf(
rc::gen::just(' '),
rc::gen::just('\t'),
rc::gen::just('\n'),
rc::gen::just('\r')
),
// some chance of control codes, non-ascii or other garbage we missed
rc::gen::inRange('\0', '\xff')
));
auto name = *nameFuzzer;
std::string path = store->storeDir + "/575s52sh487i0ylmbs9pvi606ljdszr0-" + name;
bool parsed = false;
try {
store->parseStorePath(path);
parsed = true;
} catch (const BadStorePath &) {
}
RC_ASSERT(parsed == std::regex_match(std::string { name }, nameRegex));
}
#endif
}