Coverage Report

Created: 2025-06-13 07:32

/src/osquery/plugins/config/parsers/views.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * Copyright (c) 2014-present, The osquery authors
3
 *
4
 * This source code is licensed as defined by the LICENSE file found in the
5
 * root directory of this source tree.
6
 *
7
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
8
 */
9
10
#include <set>
11
12
#include <osquery/config/config.h>
13
#include <osquery/database/database.h>
14
#include <osquery/logger/logger.h>
15
#include <osquery/registry/registry_factory.h>
16
#include <osquery/sql/sql.h>
17
18
namespace osquery {
19
20
/**
21
 * @brief A simple ConfigParserPlugin for a "views" dictionary key.
22
 */
23
class ViewsConfigParserPlugin : public ConfigParserPlugin {
24
 public:
25
0
  std::vector<std::string> keys() const override {
26
0
    return {"views"};
27
0
  }
28
29
  Status update(const std::string& source, const ParserConfig& config) override;
30
31
 private:
32
  const std::string kConfigViews = "config_views.";
33
  std::atomic<bool> first_time_{true};
34
};
35
36
Status ViewsConfigParserPlugin::update(const std::string& source,
37
0
                                       const ParserConfig& config) {
38
0
  auto cv = config.find("views");
39
0
  if (cv == config.end()) {
40
0
    return Status(1);
41
0
  }
42
43
0
  {
44
0
    auto doc = JSON::newObject();
45
0
    auto obj = doc.getObject();
46
0
    doc.copyFrom(cv->second.doc(), obj);
47
0
    doc.add("views", obj);
48
0
    data_ = std::move(doc);
49
0
  }
50
51
0
  const auto& views = data_.doc()["views"];
52
53
  // We use a restricted scope below to change the data structure from
54
  // an array to a set. This lets us do deletes much more efficiently
55
0
  std::vector<std::string> created_views;
56
0
  std::set<std::string> erase_views;
57
0
  {
58
0
    std::vector<std::string> old_views_vec;
59
0
    scanDatabaseKeys(kQueries, old_views_vec, kConfigViews);
60
0
    for (const auto& view : old_views_vec) {
61
0
      erase_views.insert(view.substr(kConfigViews.size()));
62
0
    }
63
0
  }
64
65
0
  QueryData r;
66
0
  if (views.IsObject()) {
67
0
    for (const auto& view : views.GetObject()) {
68
0
      std::string name = view.name.GetString();
69
0
      if (!view.value.IsString()) {
70
0
        continue;
71
0
      }
72
0
      std::string query = view.value.GetString();
73
0
      if (query.empty()) {
74
0
        continue;
75
0
      }
76
77
0
      std::string old_query;
78
0
      getDatabaseValue(kQueries, kConfigViews + name, old_query);
79
0
      erase_views.erase(name);
80
81
      // If query exists in the store, view would already have been
82
      // created and we don't need to create it. Except, at startup,
83
      // the view always needs to be created.
84
0
      if (!first_time_ && old_query == query) {
85
0
        continue;
86
0
      }
87
88
0
#ifdef OSQUERY_IS_FUZZING
89
0
      auto s = Status::success();
90
#else
91
      // View has been updated
92
      osquery::query("DROP VIEW " + name, r);
93
      auto s = osquery::query("CREATE VIEW " + name + " AS " + query, r);
94
#endif
95
0
      if (s.ok()) {
96
0
        setDatabaseValue(kQueries, kConfigViews + name, query);
97
0
      } else {
98
0
        LOG(INFO) << "Error creating view (" << name << "): " << s.getMessage();
99
0
      }
100
0
    }
101
0
  }
102
103
  // Any views left are views that don't exist in the new configuration file
104
  // so we tear them down and remove them from the database.
105
0
  for (const auto& old_view : erase_views) {
106
#ifndef OSQUERY_IS_FUZZING
107
    osquery::query("DROP VIEW " + old_view, r);
108
#endif
109
0
    deleteDatabaseValue(kQueries, kConfigViews + old_view);
110
0
  }
111
112
0
  first_time_ = false;
113
0
  return Status(0, "OK");
114
0
}
115
116
REGISTER_INTERNAL(ViewsConfigParserPlugin, "config_parser", "views");
117
} // namespace osquery