/src/osquery/plugins/config/parsers/file_paths.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 <osquery/config/config.h> |
11 | | #include <osquery/filesystem/filesystem.h> |
12 | | #include <osquery/logger/logger.h> |
13 | | #include <osquery/registry/registry_factory.h> |
14 | | #include <osquery/sql/sql.h> |
15 | | |
16 | | namespace osquery { |
17 | | |
18 | | /** |
19 | | * @brief A simple ConfigParserPlugin for an "file_paths" dictionary key. |
20 | | */ |
21 | | class FilePathsConfigParserPlugin : public ConfigParserPlugin { |
22 | | public: |
23 | 2 | virtual ~FilePathsConfigParserPlugin() = default; |
24 | | |
25 | 64.6k | std::vector<std::string> keys() const override { |
26 | 64.6k | return {"file_paths", "file_paths_query", "file_accesses", "exclude_paths"}; |
27 | 64.6k | } |
28 | | |
29 | | Status setUp() override; |
30 | | |
31 | | Status update(const std::string& source, const ParserConfig& config) override; |
32 | | |
33 | | private: |
34 | | // Parse and update file_accesses top-level key. |
35 | | void updateFileAccesses(const JSON& file_accesses, const std::string& source); |
36 | | |
37 | | // Parse and update file_paths top-level key. |
38 | | void updateFilePaths(const JSON& file_paths, const std::string& source); |
39 | | |
40 | | // Parse and update file_paths_query top-level key. |
41 | | void updateFilePathsQuery(const JSON& file_paths_query, |
42 | | const std::string& source); |
43 | | |
44 | | // Parse and update exclude_paths top-level key. |
45 | | void updateExcludePaths(const JSON& exclude_paths); |
46 | | |
47 | | private: |
48 | | /// The access map binds source to category. |
49 | | std::map<std::string, std::vector<std::string>> access_map_; |
50 | | }; |
51 | | |
52 | 0 | Status FilePathsConfigParserPlugin::setUp() { |
53 | 0 | auto accesses_arr = data_.getArray(); |
54 | 0 | data_.add("file_accesses", accesses_arr); |
55 | 0 | auto exclude_obj = data_.getObject(); |
56 | 0 | data_.add("exclude_paths", exclude_obj); |
57 | |
|
58 | 0 | access_map_.clear(); |
59 | 0 | return Status::success(); |
60 | 0 | } |
61 | | |
62 | | void FilePathsConfigParserPlugin::updateFileAccesses( |
63 | 664 | const JSON& file_accesses, const std::string& source) { |
64 | 664 | if (!file_accesses.doc().IsArray()) { |
65 | 44 | return; |
66 | 44 | } |
67 | | |
68 | 1.23k | for (const auto& category : file_accesses.doc().GetArray()) { |
69 | 1.23k | if (!category.IsString()) { |
70 | 609 | continue; |
71 | 609 | } |
72 | 627 | std::string path = category.GetString(); |
73 | 627 | access_map_[source].push_back(path); |
74 | 627 | } |
75 | | |
76 | 620 | auto arr = data_.getArray(); |
77 | 620 | std::set<std::string> valid_categories; |
78 | 17.5k | for (const auto& access_source : access_map_) { |
79 | 18.3k | for (const auto& category : access_source.second) { |
80 | 18.3k | valid_categories.insert(category); |
81 | 18.3k | } |
82 | 17.5k | } |
83 | | |
84 | 4.41k | for (const auto& category : valid_categories) { |
85 | 4.41k | data_.pushCopy(category, arr); |
86 | 4.41k | } |
87 | 620 | data_.add("file_accesses", arr); |
88 | 620 | } |
89 | | |
90 | | void FilePathsConfigParserPlugin::updateFilePaths(const JSON& file_paths, |
91 | 4.45k | const std::string& source) { |
92 | 4.45k | if (!file_paths.doc().IsObject()) { |
93 | 10 | return; |
94 | 10 | } |
95 | | |
96 | 4.64k | for (const auto& category : file_paths.doc().GetObject()) { |
97 | 4.64k | if (!category.value.IsArray() || !category.name.IsString()) { |
98 | 231 | continue; |
99 | 231 | } |
100 | | |
101 | 4.41k | std::string name = category.name.GetString(); |
102 | 274k | for (const auto& path : category.value.GetArray()) { |
103 | 274k | if (!path.IsString()) { |
104 | 261k | continue; |
105 | 261k | } |
106 | | |
107 | 13.1k | std::string pattern = path.GetString(); |
108 | 13.1k | if (pattern.empty()) { |
109 | 145 | continue; |
110 | 145 | } |
111 | | |
112 | 12.9k | replaceGlobWildcards(pattern); |
113 | 12.9k | Config::get().addFile(source, name, pattern); |
114 | 12.9k | } |
115 | 4.41k | } |
116 | 4.44k | } |
117 | | |
118 | | void FilePathsConfigParserPlugin::updateFilePathsQuery( |
119 | 132 | const JSON& file_paths_query, const std::string& source) { |
120 | 132 | #ifdef OSQUERY_IS_FUZZING |
121 | 132 | return; |
122 | | #else |
123 | | |
124 | | if (!file_paths_query.doc().IsObject()) { |
125 | | return; |
126 | | } |
127 | | |
128 | | for (const auto& category : file_paths_query.doc().GetObject()) { |
129 | | if (!category.value.IsArray() || !category.name.IsString()) { |
130 | | continue; |
131 | | } |
132 | | |
133 | | std::string name = category.name.GetString(); |
134 | | for (const auto& query : category.value.GetArray()) { |
135 | | if (!query.IsString()) { |
136 | | continue; |
137 | | } |
138 | | |
139 | | auto sql = SQL(query.GetString()); |
140 | | if (!sql.ok()) { |
141 | | LOG(ERROR) << "Could not add file_paths using file_paths_query '" |
142 | | << query.GetString() << "': " << sql.getMessageString(); |
143 | | } else { |
144 | | for (const auto& row : sql.rows()) { |
145 | | auto pathIt = row.find("path"); |
146 | | if (pathIt == row.end()) { |
147 | | LOG(ERROR) << "Cold not find non-empty 'path' column in the " |
148 | | "results of file_paths_query '" |
149 | | << query.GetString() << "'"; |
150 | | } else { |
151 | | std::string path = pathIt->second; |
152 | | replaceGlobWildcards(path); |
153 | | Config::get().addFile(source, name, path); |
154 | | } |
155 | | } |
156 | | } |
157 | | } |
158 | | } |
159 | | #endif |
160 | 132 | } |
161 | | |
162 | | void FilePathsConfigParserPlugin::updateExcludePaths( |
163 | 388 | const JSON& exclude_paths) { |
164 | 388 | if (!exclude_paths.doc().IsObject()) { |
165 | 84 | return; |
166 | 84 | } |
167 | | |
168 | 304 | auto obj = data_.getObject(); |
169 | 1.74k | for (const auto& category : exclude_paths.doc().GetObject()) { |
170 | 1.74k | if (!category.value.IsArray() || !category.name.IsString()) { |
171 | 414 | continue; |
172 | 414 | } |
173 | | |
174 | 1.33k | auto arr = data_.getArray(); |
175 | 1.33k | std::string category_string = category.name.GetString(); |
176 | 154k | for (const auto& path : category.value.GetArray()) { |
177 | 154k | if (!path.IsString()) { |
178 | 831 | continue; |
179 | 831 | } |
180 | | |
181 | 153k | std::string path_string = path.GetString(); |
182 | 153k | data_.pushCopy(path_string, arr); |
183 | 153k | } |
184 | 1.33k | data_.add(category_string, arr, obj); |
185 | 1.33k | } |
186 | | |
187 | | // Will attempt a merge so be careful that the key is initialized. |
188 | 304 | if (!data_.doc().HasMember("exclude_paths")) { |
189 | 1 | auto exclude_obj = data_.getObject(); |
190 | 1 | data_.add("exclude_paths", exclude_obj); |
191 | 1 | } |
192 | 304 | data_.mergeObject(data_.doc()["exclude_paths"], obj); |
193 | 304 | } |
194 | | |
195 | | Status FilePathsConfigParserPlugin::update(const std::string& source, |
196 | 64.6k | const ParserConfig& config) { |
197 | 64.6k | Config::get().removeFiles(source); |
198 | 64.6k | access_map_.erase(source); |
199 | | |
200 | 64.6k | auto file_paths = config.find("file_paths"); |
201 | 64.6k | auto file_paths_query = config.find("file_paths_query"); |
202 | 64.6k | if (file_paths == config.end() && file_paths_query == config.end()) { |
203 | 60.1k | return Status::success(); |
204 | 60.1k | } |
205 | | |
206 | 4.48k | if (file_paths != config.end()) { |
207 | 4.45k | updateFilePaths(file_paths->second, source); |
208 | 4.45k | } |
209 | | |
210 | 4.48k | if (file_paths_query != config.end()) { |
211 | 132 | updateFilePathsQuery(file_paths_query->second, source); |
212 | 132 | } |
213 | | |
214 | 4.48k | auto file_accesses = config.find("file_accesses"); |
215 | 4.48k | if (file_accesses != config.end()) { |
216 | 664 | updateFileAccesses(file_accesses->second, source); |
217 | 664 | } |
218 | | |
219 | 4.48k | auto exclude_paths = config.find("exclude_paths"); |
220 | 4.48k | if (exclude_paths != config.end()) { |
221 | 388 | updateExcludePaths(exclude_paths->second); |
222 | 388 | } |
223 | | |
224 | 4.48k | auto doc = JSON::newObject(); |
225 | 4.48k | doc.copyFrom(data_.doc()); |
226 | 4.48k | data_ = std::move(doc); |
227 | | |
228 | 4.48k | return Status::success(); |
229 | 64.6k | } |
230 | | |
231 | | REGISTER_INTERNAL(FilePathsConfigParserPlugin, "config_parser", "file_paths"); |
232 | | } // namespace osquery |