Add sanity-checking for WML attribute/tag/variable names to prevent invalid save files

- It's now an error to set an invalid variable
- If an invalid key or tag is somehow set, it won't be written to the file (with an error message in the log)
- There's an API function config::valid_id() which can be used to catch issues earlier.
This commit is contained in:
Celtic Minstrel 2015-09-17 21:43:25 -04:00
parent e9be2324b5
commit af2e16f842
4 changed files with 30 additions and 6 deletions

View file

@ -480,6 +480,19 @@ config &config::operator=(config &&cfg)
}
#endif
bool config::valid_id(const std::string id)
{
if (id.empty()) {
return false;
}
BOOST_FOREACH(char c, id) {
if (!isalnum(c) && c != '_') {
return false;
}
}
return true;
}
bool config::has_attribute(const std::string &key) const
{
check_valid();

View file

@ -103,7 +103,9 @@ public:
explicit config(const std::string &child);
~config();
// Verifies that the string can be used as an attribute or tag name
static bool valid_id(std::string);
#ifdef HAVE_CXX11
explicit operator bool() const

View file

@ -581,11 +581,19 @@ static void write_internal(config const &cfg, std::ostream &out, std::string& te
throw config::error("Too many recursion levels in config write");
BOOST_FOREACH(const config::attribute &i, cfg.attribute_range()) {
if (!config::valid_id(i.first)) {
ERR_CF << "Config contains invalid attribute name '" << i.first << "', skipping...\n";
continue;
}
write_key_val(out, i.first, i.second, tab, textdomain);
}
BOOST_FOREACH(const config::any_child &item, cfg.all_children_range())
{
if (!config::valid_id(item.key)) {
ERR_CF << "Config contains invalid tag name '" << item.key << "', skipping...\n";
continue;
}
write_open_child(out, item.key, tab);
write_internal(item.cfg, out, textdomain, tab + 1);
write_close_child(out, item.key, tab);

View file

@ -170,10 +170,7 @@ namespace
char* endptr;
int res = strtol(index_str, &endptr, 10);
if (*endptr != ']') {
res = 0;//default
}
if(res > int(game_config::max_loop))
if (*endptr != ']' || res > int(game_config::max_loop))
{
throw invalid_variablename_exception();
}
@ -186,7 +183,11 @@ namespace
: public variable_info_visitor<vit, void>
{
public:
get_variable_key_visitor(const std::string& key) : key_(key) {}
get_variable_key_visitor(const std::string& key) : key_(key) {
if (!config::valid_id(key_)) {
throw invalid_variablename_exception();
}
}
void from_named(typename get_variable_key_visitor::param_type state) const
{
if(key_ == "length")