Cassandra for years has relied on snakeyaml to parse configuration files, and it does a fine job, really not a lot of complaints. Until one day a colleague of mine discovered an issue where a permission issue incorrectly reported as an invalid yaml file (complete with the supposedly offending key). Digging into the source code and trying to match up the message. I found that we were handling YAMLException
Yaml yaml = new Yaml(constructor);
Config result = loadConfig(yaml, configBytes);
propertiesChecker.check();
return result;
}
catch (YAMLException e)
{
throw new ConfigurationException("Invalid yaml: " + url + SystemUtils.LINE_SEPARATOR
+ " Error: " + e.getMessage(), false);
}
Which is effectively calling loadAs from snakeyaml
private Config loadConfig(Yaml yaml, byte[] configBytes)
{
Config config = yaml.loadAs(new ByteArrayInputStream(configBytes), Config.class);
// If the configuration file is empty yaml will return null. In this case we should use the default
// configuration to avoid hitting a NPE at a later stage.
return config == null ? new Config() : config;
}
So for this particular version I hunted down the release of snakeyaml as 1.12 and digging into the source I find load as uses StreamReader to wrap the stream passed into loadAs
@SuppressWarnings("unchecked")
public <T> T loadAs(Reader io, Class<T> type) {
return (T) loadFromReader(new StreamReader(io), type);
}
A new StreamReader is called, which in the source calls this.update
public StreamReader(Reader reader) {
this.name = "'reader'";
this.buffer = "";
this.stream = reader;
this.eof = false;
this.data = new char[1024];
this.update();
}
Unfortunately, “this.update” handles IOException by throwing a new YAMLException. So this is how we turn a permission error into an invalid YAML
} catch (IOException ioe) {
throw new YAMLException(ioe);
}
Just watch out for invalid yaml errors when using Cassandra (or anything consuming snakeyaml) it may not actually be what you think it is.