* the LICENSE file found in the root directory of this source tree.
*/
-#include <osquery/utils/json/json.h>
-
-#include <vist/logger.hpp>
#include <osquery/registry_factory.h>
#include <osquery/tables.h>
#include <osquery/utils/conversions/tryto.h>
+#include <vist/logger.hpp>
+
namespace osquery {
CREATE_LAZY_REGISTRY(TablePlugin, "table");
}
void TablePlugin::setRequestFromContext(const QueryContext& context,
- PluginRequest& request) {
- auto doc = JSON::newObject();
- auto constraints = doc.getArray();
-
- // The QueryContext contains a constraint map from column to type information
- // and the list of operand/expression constraints applied to that column from
- // the query given.
- for (const auto& constraint : context.constraints) {
- auto child = doc.getObject();
- doc.addRef("name", constraint.first, child);
- constraint.second.serialize(doc, child);
- doc.push(child, constraints);
- }
-
- doc.add("constraints", constraints);
-
- if (context.colsUsed) {
- auto colsUsed = doc.getArray();
- for (const auto& columnName : *context.colsUsed) {
- doc.pushCopy(columnName, colsUsed);
- }
- doc.add("colsUsed", colsUsed);
- }
-
- if (context.colsUsedBitset) {
- doc.add("colsUsedBitset", context.colsUsedBitset->to_ullong());
- }
-
- doc.toString(request["context"]);
-}
-
-QueryContext TablePlugin::getContextFromRequest(
- const PluginRequest& request) const {
- QueryContext context;
- if (request.count("context") == 0) {
- return context;
- }
-
- auto doc = JSON::newObject();
- doc.fromString(request.at("context"));
- if (doc.doc().HasMember("colsUsed")) {
- UsedColumns colsUsed;
- for (const auto& columnName : doc.doc()["colsUsed"].GetArray()) {
- colsUsed.insert(columnName.GetString());
- }
- context.colsUsed = colsUsed;
- }
- if (doc.doc().HasMember("colsUsedBitset")) {
- context.colsUsedBitset = doc.doc()["colsUsedBitset"].GetUint64();
- } else if (context.colsUsed) {
- context.colsUsedBitset = usedColumnsToBitset(*context.colsUsed);
- }
-
- if (!doc.doc().HasMember("constraints") ||
- !doc.doc()["constraints"].IsArray()) {
- return context;
- }
-
- // Set the context limit and deserialize each column constraint list.
- for (const auto& constraint : doc.doc()["constraints"].GetArray()) {
- auto column_name = constraint["name"].GetString();
- context.constraints[column_name].deserialize(constraint);
- }
-
- return context;
+ PluginRequest& request) {
+ vist::json::Json document;
+ vist::json::Array constraints;
+
+ // The QueryContext contains a constraint map from column to type information
+ // and the list of operand/expression constraints applied to that column from
+ // the query given.
+ //
+ // {"constraints":[{"name":"test_int","list":[{"op":2,"expr":"2"}],"affinity":"TEXT"}]}
+ for (const auto& constraint : context.constraints) {
+ auto child = constraint.second.serialize();
+ child["name"] = constraint.first;
+
+ constraints.push(child);
+ }
+
+ document.push("constraints", constraints);
+
+ if (context.colsUsed) {
+ vist::json::Array colsUsed;
+ for (const auto& columnName : *context.colsUsed)
+ colsUsed.push(columnName);
+
+ document.push("colsUsed", colsUsed);
+ }
+
+ request["context"] = document.serialize();
+ DEBUG(OSQUERY) << "request context->" << request["context"];
+}
+
+QueryContext TablePlugin::getContextFromRequest(const PluginRequest& request) const {
+ QueryContext context;
+ if (request.count("context") == 0)
+ return context;
+
+ using namespace vist::json;
+ std::string serialized = request.at("context");
+ Json document = Json::Parse(serialized);
+ DEBUG(OSQUERY) << "request context->" << document.serialize();
+
+ if (document.exist("colsUsed")) {
+ UsedColumns colsUsed;
+ Array array = document.get<Array>("colsUsed");
+ for (auto i = 0; i < array.size(); i++) {
+ std::string name = array.at(i);
+ colsUsed.insert(name);
+ }
+ context.colsUsed = colsUsed;
+ }
+
+ Array constraints = document.get<Array>("constraints");
+ for (auto i = 0; i < constraints.size(); i++) {
+ auto constraint = Object::Create(constraints.at(i));
+ std::string name = constraint["name"];
+ context.constraints[name].deserialize(constraint);
+ }
+
+ return context;
}
UsedColumnsBitset TablePlugin::usedColumnsToBitset(
template std::set<unsigned long long>
ConstraintList::getAll<unsigned long long>(ConstraintOperator) const;
-void ConstraintList::serialize(JSON& doc, rapidjson::Value& obj) const {
- auto expressions = doc.getArray();
- for (const auto& constraint : constraints_) {
- auto child = doc.getObject();
- doc.add("op", static_cast<size_t>(constraint.op), child);
- doc.addRef("expr", constraint.expr, child);
- doc.push(child, expressions);
- }
- doc.add("list", expressions, obj);
- doc.addCopy("affinity", columnTypeName(affinity), obj);
-}
-
-void ConstraintList::deserialize(const rapidjson::Value& obj) {
- // Iterate through the list of operand/expressions, then set the constraint
- // type affinity.
- if (!obj.IsObject() || !obj.HasMember("list") || !obj["list"].IsArray()) {
- return;
- }
-
- for (const auto& list : obj["list"].GetArray()) {
- auto op = static_cast<unsigned char>(JSON::valueToSize(list["op"]));
- Constraint constraint(op);
- constraint.expr = list["expr"].GetString();
- constraints_.push_back(constraint);
- }
-
- auto affinity_name = (obj.HasMember("affinity") && obj["affinity"].IsString())
- ? obj["affinity"].GetString()
- : "UNKNOWN";
- affinity = columnTypeName(affinity_name);
+vist::json::Object ConstraintList::serialize() const {
+ // format: { "affinity": "TEXT", "list": [ { "expr": "1", "op": 2 } ] }
+ vist::json::Array list;
+ for (const auto& constraint : constraints_) {
+ vist::json::Object element;
+ element["op"] = static_cast<int>(constraint.op);
+ element["expr"] = constraint.expr;
+ list.push(element);
+ }
+
+ vist::json::Object object;
+ object.push("list", list);
+ object["affinity"] = columnTypeName(affinity);
+
+ return object;
+}
+
+void ConstraintList::deserialize(vist::json::Object& obj) {
+ using namespace vist::json;
+ Array list = obj.get<Array>("list");
+ for (auto i = 0; i < list.size(); i++) {
+ auto element = vist::json::Object::Create(list.at(i));
+ int op = element["op"];
+ Constraint constraint(static_cast<unsigned char>(op));
+ constraint.expr = static_cast<std::string>(element["expr"]);
+ this->constraints_.emplace_back(std::move(constraint));
+ }
+
+ std::string name = obj.exist("affinity") ? static_cast<std::string>(obj["affinity"]) : "UNKNOWN";
+ this->affinity = columnTypeName(name);
}
bool QueryContext::isColumnUsed(const std::string& colName) const {
#include <osquery/core/sql/column.h>
#include <osquery/plugins/plugin.h>
#include <osquery/query.h>
-#include <osquery/utils/json/json.h>
+
+#include <vist/json.hpp>
#include <gtest/gtest_prod.h>
* ]
* }
*/
- void serialize(JSON& doc, rapidjson::Value& obj) const;
+ vist::json::Object serialize() const;
+
+ void deserialize(vist::json::Object& obj);
- /// See ConstraintList::unserialize.
- void deserialize(const rapidjson::Value& obj);
private:
/// List of constraint operator/expressions.
#include <osquery/core/sql/query_data.h>
#include <osquery/core/sql/table_row.h>
+#include <osquery/utils/json/json.h>
namespace osquery {
#include <osquery/registry_interface.h>
#include <osquery/sql.h>
#include <osquery/sql/sqlite_util.h>
+#include <osquery/utils/json/json.h>
namespace osquery {
#pragma once
+#include <vist/json/util.hpp>
#include <vist/json/value.hpp>
#include <vist/string.hpp>
void push(const Type& data)
{
auto value = std::make_shared<Value>();
- *value = data;
+ if constexpr (std::is_same_v<Type, Array> || std::is_same_v<Type, Object>) {
+ auto real = std::make_shared<Type>();
+ *real = data;
+ value->leaf = real;
+ } else {
+ *value = data;
+ }
+
this->buffer.emplace_back(std::move(value));
}
if (dumped.empty())
throw std::invalid_argument("Dumped value cannot empty.");
- auto stripped = strip(dumped, '[', ']');
- auto tokens = split(trim(stripped), ",");
+ auto stripped = strip(trim(dumped), '[', ']');
+ if (trim(stripped).empty())
+ return;
+
+ auto divided = split(stripped, ",");
+ auto tokens = canonicalize(divided);
for (const auto& token : tokens) {
auto value = std::make_shared<Value>();
value->deserialize(trim(token));
template <typename CompositeType>
CompositeType& get(const std::string& key)
{
- if (!this->root.exist(key))
- throw std::runtime_error("Not exist key.");
-
- if constexpr (std::is_same_v<CompositeType, Array> ||
- std::is_same_v<CompositeType, Object>) {
- if (auto downcast = std::dynamic_pointer_cast<CompositeType>(this->root.pairs[key]->leaf);
- downcast == nullptr)
- throw std::runtime_error(key + "Mismatched type.");
- else
- return *downcast;
- } else {
- static_assert(dependent_false<CompositeType>::value, "Only Composite type supported.");
- }
+ return this->root.get<CompositeType>(key);
+ }
+
+ bool exist(const std::string& key)
+ {
+ return this->root.exist(key);
}
std::size_t size() const noexcept
#pragma once
+#include <vist/json/util.hpp>
#include <vist/json/value.hpp>
#include <vist/string.hpp>
namespace json {
struct Object : public Value {
+ static Object Create(Value& value)
+ {
+ if (auto downcast = std::dynamic_pointer_cast<Object>(value.leaf); downcast == nullptr)
+ throw std::runtime_error("Mismatched type.");
+ else
+ return *downcast;
+ }
+
template <typename Type>
void push(const std::string& key, const Type& data)
{
+ auto real = std::make_shared<Type>();
+ *real = data;
+
auto value = std::make_shared<Value>();
- *value = data;
+ value->leaf = real;
+
this->pairs[key] = std::move(value);
}
return *(this->pairs[key]);
}
+ template <typename CompositeType>
+ CompositeType& get(const std::string& key)
+ {
+ if (!this->exist(key))
+ throw std::runtime_error("Not exist key.");
+
+ if constexpr (std::is_same_v<CompositeType, Array> ||
+ std::is_same_v<CompositeType, Object>) {
+ if (auto downcast = std::dynamic_pointer_cast<CompositeType>(this->pairs[key]->leaf);
+ downcast == nullptr)
+ throw std::runtime_error(key + "Mismatched type.");
+ else
+ return *downcast;
+ } else {
+ static_assert(dependent_false<CompositeType>::value, "Only Composite type supported.");
+ }
+ }
+
std::string serialize() const override
{
std::stringstream ss;
if (dumped.empty())
throw std::invalid_argument("Dumped value cannot empty.");
- auto stripped = strip(dumped, '{', '}');
- auto divided = split(trim(stripped), ",");
- auto tokens = this->canonicalize(divided);
+ auto stripped = strip(trim(dumped), '{', '}');
+ auto divided = split(stripped, ",");
+ auto tokens = canonicalize(divided);
for (const auto& token : tokens) {
if (auto pos = token.find(":"); pos != std::string::npos) {
auto lhs = token.substr(0, pos);
}
std::unordered_map<std::string, std::shared_ptr<Value>> pairs;
-
- std::vector<std::string> canonicalize(std::vector<std::string>& tokens)
- {
- std::vector<std::string> result;
- auto rearrange = [&](std::vector<std::string>::iterator& iter, char first, char last) {
- if ((*iter).find(first) == std::string::npos)
- return false;
-
- std::string origin = *iter;
- iter++;
-
- std::size_t lcount = 1, rcount = 0;
- while (iter != tokens.end()) {
- if ((*iter).find(first) != std::string::npos)
- lcount++;
- else if ((*iter).find(last) != std::string::npos)
- rcount++;
-
- origin += ", " + *iter;
-
- if (lcount == rcount)
- break;
- else
- iter++;
- }
- result.emplace_back(std::move(origin));
-
- return true;
- };
-
- for (auto iter = tokens.begin() ; iter != tokens.end(); iter++) {
- if (rearrange(iter, '{', '}') || rearrange(iter, '[', ']'))
- continue;
-
- result.emplace_back(std::move(*iter));
- }
-
- return result;
- }
};
} // namespace json
EXPECT_EQ(static_cast<std::string>(result["string"]), "initial value");
}
+TEST(JsonTests, empty_array)
+{
+ Json json = Json::Parse("{ \"empty\": [ ] }");
+ EXPECT_EQ(json.size(), 1);
+}
+
+TEST(JsonTests, osquery_format)
+{
+ // {"constraints":[{"name":"test_int","list":[{"op":2,"expr":"2"}],"affinity":"TEXT"}]}
+ Json document;
+
+ {
+ // "constraints"
+ Array constraints;
+
+ // "name"
+ Object child;
+ child["name"] = "test_int";
+
+ // "list"
+ Array list;
+ Object element;
+ element["op"] = 2;
+ element["expr"] = "2";
+ list.push(element);
+ child.push("list", list);
+
+ // "affinity"
+ child["affinity"] = "TEXT";
+
+ constraints.push(child);
+ document.push("constraints", constraints);
+
+ EXPECT_EQ(document.serialize(), "{ \"constraints\": [ { \"affinity\": \"TEXT\", "
+ "\"name\": \"test_int\", "
+ "\"list\": [ { \"expr\": \"2\", \"op\": 2 } ] } ] }");
+ }
+
+ {
+ Json restore = Json::Parse(document.serialize());
+ EXPECT_TRUE(restore.exist("constraints"));
+
+ Array constraints = restore.get<Array>("constraints");
+ Object child = Object::Create(constraints.at(0));
+ EXPECT_EQ(static_cast<std::string>(child["name"]), "test_int");
+
+ Array list = child.get<Array>("list");
+ Object element = Object::Create(list.at(0));
+ EXPECT_EQ(static_cast<int>(element["op"]), 2);
+ EXPECT_EQ(static_cast<std::string>(element["expr"]), "2");
+ }
+}
+
TEST(JsonTests, serialize)
{
Json json;
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <vist/string.hpp>
+
+#include <string>
+#include <vector>
+
+namespace vist {
+namespace json {
+
+inline std::vector<std::string> canonicalize(std::vector<std::string>& tokens)
+{
+ std::vector<std::string> result;
+ auto rearrange = [&](std::vector<std::string>::iterator& iter, char first, char last) {
+ if ((*iter).find(first) == std::string::npos)
+ return false;
+
+ std::string origin = *iter;
+ iter++;
+
+ std::size_t lcount = 1, rcount = 0;
+ while (iter != tokens.end()) {
+ lcount += count(*iter, first);
+ rcount += count(*iter, last);
+
+ origin += ", " + *iter;
+
+ if (lcount == rcount)
+ break;
+ else
+ iter++;
+ }
+ result.emplace_back(std::move(origin));
+
+ return true;
+ };
+
+ for (auto iter = tokens.begin() ; iter != tokens.end(); iter++) {
+ if (rearrange(iter, '{', '}') || rearrange(iter, '[', ']')) {
+ if (iter == tokens.end())
+ break;
+ else
+ continue;
+ }
+
+ result.emplace_back(std::move(*iter));
+ }
+
+ return result;
+}
+
+} // namespace json
+} // namespace vist
}
std::shared_ptr<Value> leaf;
-
-private:
};
struct Int : public Value {
return copied.substr(1, copied.size() -2);
}
+inline std::size_t count(const std::string& str, char ch)
+{
+ return std::count(str.begin(), str.end(), ch);
+}
+
} // namespace vist