blob: b903cc741033808fde005123a36d1166994c3b61 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/forensics/feedback_data/config.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/errors.h>
#include "src/lib/files/file.h"
#include "third_party/rapidjson/include/rapidjson/document.h"
#include "third_party/rapidjson/include/rapidjson/error/en.h"
#include "third_party/rapidjson/include/rapidjson/schema.h"
#include "third_party/rapidjson/include/rapidjson/stringbuffer.h"
namespace forensics {
namespace feedback_data {
namespace {
const char kSchema[] = R"({
"type": "object",
"properties": {
"annotation_allowlist": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
},
"attachment_allowlist": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
},
"required": [
"annotation_allowlist",
"attachment_allowlist"
],
"additionalProperties": false
})";
const char kAnnotationWhitelistKey[] = "annotation_allowlist";
const char kAttachmentWhitelistKey[] = "attachment_allowlist";
bool CheckAgainstSchema(rapidjson::Document& doc) {
// Check that the schema is actually valid.
rapidjson::Document sd;
rapidjson::ParseResult ok = sd.Parse(kSchema);
if (!ok) {
FX_LOGS(ERROR) << "invalid JSON schema for config at offset " << ok.Offset() << " "
<< rapidjson::GetParseError_En(ok.Code());
return false;
}
// Check the document against the schema.
rapidjson::SchemaDocument schema(sd);
rapidjson::SchemaValidator validator(schema);
if (!doc.Accept(validator)) {
rapidjson::StringBuffer sb;
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
FX_LOGS(ERROR) << "config does not match schema, violating '"
<< validator.GetInvalidSchemaKeyword() << "' rule";
return false;
}
return true;
}
} // namespace
zx_status_t ParseConfig(const std::string& filepath, Config* config) {
std::string json;
if (!files::ReadFileToString(filepath, &json)) {
FX_LOGS(ERROR) << "error reading config file at " << filepath;
return ZX_ERR_IO;
}
rapidjson::Document doc;
rapidjson::ParseResult ok = doc.Parse(json.c_str());
if (!ok) {
FX_LOGS(ERROR) << "error parsing config as JSON at offset " << ok.Offset() << " "
<< rapidjson::GetParseError_En(ok.Code());
return ZX_ERR_INTERNAL;
}
if (!CheckAgainstSchema(doc)) {
return ZX_ERR_INTERNAL;
}
// We use a local config to only set the out argument after all the checks.
Config local_config = {};
// It is safe to directly access the field as the keys are marked as required and we have checked
// the config against the schema.
for (const auto& annotation_key : doc[kAnnotationWhitelistKey].GetArray()) {
// No need to warn on duplicates as the schema enforces "uniqueItems".
local_config.annotation_allowlist.insert(annotation_key.GetString());
}
for (const auto& attachment_key : doc[kAttachmentWhitelistKey].GetArray()) {
// No need to warn on duplicates as the schema enforces "uniqueItems".
local_config.attachment_allowlist.insert(attachment_key.GetString());
}
*config = local_config;
return ZX_OK;
}
} // namespace feedback_data
} // namespace forensics