Fix to use vist-json
authorSangwan Kwon <sangwan.kwon@samsung.com>
Fri, 15 May 2020 07:48:46 +0000 (16:48 +0900)
committer권상완/Security 2Lab(SR)/Engineer/삼성전자 <sangwan.kwon@samsung.com>
Wed, 20 May 2020 01:51:37 +0000 (10:51 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
src/osquery/core/tables.cpp
src/osquery/include/osquery/tables.h
src/osquery/sql/dynamic_table_row.h
src/osquery/sql/tests/sql_test_utils.h
src/vist/json/array.hpp
src/vist/json/json.hpp
src/vist/json/object.hpp
src/vist/json/tests/json.cpp
src/vist/json/util.hpp [new file with mode: 0644]
src/vist/json/value.hpp
src/vist/string.hpp

index 8f3885a..8d1c400 100644 (file)
@@ -6,13 +6,12 @@
  *  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");
@@ -40,71 +39,64 @@ void TablePlugin::removeExternal(const std::string& name) {
 }
 
 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(
@@ -451,36 +443,36 @@ template std::set<long long> ConstraintList::getAll<long long>(
 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 {
index a59cc70..232d8d6 100644 (file)
@@ -24,7 +24,8 @@
 #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>
 
@@ -345,10 +346,10 @@ struct ConstraintList : private boost::noncopyable {
    *   ]
    * }
    */
-  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.
index 7da1493..a0d48b5 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <osquery/core/sql/query_data.h>
 #include <osquery/core/sql/table_row.h>
+#include <osquery/utils/json/json.h>
 
 namespace osquery {
 
index 6660bed..9a93f0a 100644 (file)
@@ -3,6 +3,7 @@
 #include <osquery/registry_interface.h>
 #include <osquery/sql.h>
 #include <osquery/sql/sqlite_util.h>
+#include <osquery/utils/json/json.h>
 
 namespace osquery {
 
index 692e554..1a0de55 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <vist/json/util.hpp>
 #include <vist/json/value.hpp>
 #include <vist/string.hpp>
 
@@ -30,7 +31,14 @@ struct Array : public Value {
        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));
        }
 
@@ -74,8 +82,12 @@ struct Array : public 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));
index bf445fc..78483ef 100644 (file)
@@ -84,19 +84,12 @@ struct Json {
        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
index df25a51..54d90ef 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <vist/json/util.hpp>
 #include <vist/json/value.hpp>
 #include <vist/string.hpp>
 
@@ -26,11 +27,23 @@ namespace vist {
 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);
        }
 
@@ -42,6 +55,24 @@ struct Object : public 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;
@@ -67,9 +98,9 @@ struct Object : public Value {
                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);
@@ -97,45 +128,6 @@ struct Object : public Value {
        }
 
        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
index 7c461f6..abaf752 100644 (file)
@@ -208,6 +208,59 @@ TEST(JsonTests, object)
        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;
diff --git a/src/vist/json/util.hpp b/src/vist/json/util.hpp
new file mode 100644 (file)
index 0000000..97df9a9
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  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
index 04bae16..64ff36f 100644 (file)
@@ -116,8 +116,6 @@ struct Value {
        }
 
        std::shared_ptr<Value> leaf;
-
-private:
 };
 
 struct Int : public Value {
index c71a533..bfffd34 100644 (file)
@@ -62,4 +62,9 @@ inline std::string strip(const std::string& origin, char begin, char end)
        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