Disallow accessing files whose names are in the default ignore list

This commit is contained in:
Jyrki Vesterinen 2018-03-17 14:01:10 +02:00
parent eed3927d12
commit c5e972b8b4
7 changed files with 61 additions and 21 deletions

View file

@ -89,6 +89,8 @@ public:
directory_patterns_.push_back(pattern);
}
void remove_blacklisted_files_and_dirs(std::vector<std::string>& files, std::vector<std::string>& directories) const;
private:
std::vector<std::string> file_patterns_;
std::vector<std::string> directory_patterns_;
@ -97,7 +99,7 @@ private:
static const blacklist_pattern_list default_blacklist{
{
/* Blacklist dot-files/dirs, which are hidden files in UNIX platforms */
".*",
".+",
"#*#",
"*~",
"*-bak",
@ -122,7 +124,7 @@ static const blacklist_pattern_list default_blacklist{
"*.project",
},
{
".*",
".+",
/* macOS metadata-like cruft (http://floatingsun.net/2007/02/07/whats-with-__macosx-in-zip-files/) */
"__MACOSX",
}

View file

@ -26,6 +26,7 @@
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#include <set>
#ifdef _WIN32
@ -1135,27 +1136,34 @@ void clear_binary_paths_cache()
binary_paths_cache.clear();
}
static bool is_legal_file(const std::string &filename)
static bool is_legal_file(const std::string &filename_str)
{
DBG_FS << "Looking for '" << filename << "'.\n";
DBG_FS << "Looking for '" << filename_str << "'.\n";
if (filename.empty()) {
if(filename_str.empty()) {
LOG_FS << " invalid filename\n";
return false;
}
if (filename.find("..") != std::string::npos) {
ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed).\n";
if(filename_str.find("..") != std::string::npos) {
ERR_FS << "Illegal path '" << filename_str << "' (\"..\" not allowed).\n";
return false;
}
if (filename.find('\\') != std::string::npos) {
ERR_FS << "Illegal path '" << filename << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl;
if(filename_str.find('\\') != std::string::npos) {
ERR_FS << "Illegal path '" << filename_str << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl;
return false;
}
if (looks_like_pbl(filename)) {
ERR_FS << "Illegal path '" << filename << "' (.pbl files are not allowed)." << std::endl;
path filepath(filename_str);
if(default_blacklist.match_file(filepath.filename().string())) {
ERR_FS << "Illegal path '" << filename_str << "' (blacklisted filename)." << std::endl;
return false;
}
if(std::any_of(filepath.begin(), filepath.end(), [](const path& dirname) { return default_blacklist.match_dir(dirname.string()); })) {
ERR_FS << "Illegal path '" << filename_str << "' (blacklisted directory name)." << std::endl;
return false;
}

View file

@ -29,6 +29,16 @@ static lg::log_domain log_filesystem("filesystem");
namespace filesystem
{
void blacklist_pattern_list::remove_blacklisted_files_and_dirs(std::vector<std::string>& files, std::vector<std::string>& directories) const
{
files.erase(
std::remove_if(files.begin(), files.end(), [this](const std::string& name) { return match_file(name); }),
files.end());
directories.erase(
std::remove_if(directories.begin(), directories.end(), [this](const std::string& name) { return match_dir(name); }),
directories.end());
}
std::string get_prefs_file()
{
return get_user_config_dir() + "/preferences";

View file

@ -21,6 +21,7 @@
#include "scripting/lua_common.hpp" // for chat_message, luaW_pcall
#include "scripting/push_check.hpp"
#include <algorithm>
#include <exception>
#include <string>
@ -149,6 +150,7 @@ int intf_read_file(lua_State *L)
if(filesystem::is_directory(p)) {
std::vector<std::string> files, dirs;
filesystem::get_files_in_dir(p, &files, &dirs);
filesystem::default_blacklist.remove_blacklisted_files_and_dirs(files, dirs);
size_t ndirs = dirs.size();
std::copy(files.begin(), files.end(), std::back_inserter(dirs));
lua_push(L, dirs);

View file

@ -701,17 +701,23 @@ bool word_match(const std::string& message, const std::string& word) {
}
bool wildcard_string_match(const std::string& str, const std::string& match) {
const bool wild_matching = (!match.empty() && match[0] == '*');
const std::string::size_type solid_begin = match.find_first_not_of('*');
const bool wild_matching = (!match.empty() && (match[0] == '*' || match[0] == '+'));
const std::string::size_type solid_begin = match.find_first_not_of("*+");
const bool have_solids = (solid_begin != std::string::npos);
// Check the simple case first
if(str.empty() || !have_solids) {
return wild_matching || str == match;
// Check the simple cases first
if(!have_solids) {
const std::string::size_type plus_count = std::count(match.begin(), match.end(), '+');
return match.empty() ? str.empty() : str.length() >= plus_count;
} else if(str.empty()) {
return false;
}
const std::string::size_type solid_end = match.find_first_of('*', solid_begin);
const std::string::size_type solid_end = match.find_first_of("*+", solid_begin);
const std::string::size_type solid_len = (solid_end == std::string::npos)
? match.length() - solid_begin : solid_end - solid_begin;
std::string::size_type current = 0;
// Since + always consumes at least one character, increment current if the match
// begins with one
std::string::size_type current = match[0] == '+' ? 1 : 0;
bool matches;
do {
matches = true;

View file

@ -321,8 +321,8 @@ bool word_completion(std::string& text, std::vector<std::string>& wordlist);
bool word_match(const std::string& message, const std::string& word);
/**
* Match using '*' as any number of characters (including none), and '?' as any
* one character.
* Match using '*' as any number of characters (including none),
* '+' as one or more characters, and '?' as any one character.
*/
bool wildcard_string_match(const std::string& str, const std::string& match);

View file

@ -106,12 +106,16 @@ BOOST_AUTO_TEST_CASE( test_wildcard_string_match )
const std::string str = "foo bar baz";
BOOST_CHECK(utils::wildcard_string_match(str, "*bar*"));
BOOST_CHECK(utils::wildcard_string_match(str, "+bar+"));
BOOST_CHECK(!utils::wildcard_string_match(str, "*BAR*"));
BOOST_CHECK(!utils::wildcard_string_match(str, "+BAR+"));
BOOST_CHECK(!utils::wildcard_string_match(str, "bar"));
BOOST_CHECK(utils::wildcard_string_match(str, "*ba? b*"));
BOOST_CHECK(utils::wildcard_string_match(str, "+ba? b+"));
BOOST_CHECK(utils::wildcard_string_match(str, "*?a?*"));
BOOST_CHECK(utils::wildcard_string_match(str, "+?a?+"));
BOOST_CHECK(!utils::wildcard_string_match(str, "foo? "));
BOOST_CHECK(!utils::wildcard_string_match(str, "?foo"));
@ -126,11 +130,19 @@ BOOST_AUTO_TEST_CASE( test_wildcard_string_match )
BOOST_CHECK(utils::wildcard_string_match(str, superfluous_mask));
BOOST_CHECK(utils::wildcard_string_match(str, superfluous_mask + '*'));
superfluous_mask = std::string(str.length(), '+');
BOOST_CHECK(utils::wildcard_string_match(str, superfluous_mask));
BOOST_CHECK(!utils::wildcard_string_match(str, superfluous_mask + '+'));
BOOST_CHECK(utils::wildcard_string_match("", ""));
BOOST_CHECK(!utils::wildcard_string_match(str, ""));
BOOST_CHECK(utils::wildcard_string_match("", "*"));
BOOST_CHECK(utils::wildcard_string_match("", "***?**"));
BOOST_CHECK(utils::wildcard_string_match("", "***"));
BOOST_CHECK(!utils::wildcard_string_match("", "+"));
BOOST_CHECK(!utils::wildcard_string_match("", "*bar"));
BOOST_CHECK(!utils::wildcard_string_match("", "***?**"));
BOOST_CHECK(!utils::wildcard_string_match("", "+++?++"));
BOOST_CHECK(!utils::wildcard_string_match("", "?"));
BOOST_CHECK(!utils::wildcard_string_match("", "???"));
}