forked from osquery/osquery
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new table vscode_extensions (osquery#8150)
Co-authored-by: Zach Wasserman <zach@fleetdm.com> Co-authored-by: Stefano Bonicatti <stefano.bonicatti@gmail.com>
- Loading branch information
1 parent
52974c7
commit c396d07
Showing
6 changed files
with
213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** | ||
* Copyright (c) 2014-present, The osquery authors | ||
* | ||
* This source code is licensed as defined by the LICENSE file found in the | ||
* root directory of this source tree. | ||
* | ||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only) | ||
*/ | ||
|
||
#include <set> | ||
#include <string> | ||
|
||
#include <boost/filesystem.hpp> | ||
|
||
#include <osquery/core/tables.h> | ||
#include <osquery/filesystem/filesystem.h> | ||
#include <osquery/logger/logger.h> | ||
#include <osquery/tables/system/system_utils.h> | ||
#include <osquery/utils/json/json.h> | ||
|
||
namespace fs = boost::filesystem; | ||
|
||
namespace osquery { | ||
namespace tables { | ||
|
||
void genReadJSONAndAddExtensionRows(const std::string& uid, | ||
const std::string& path, | ||
QueryData& results) { | ||
std::string json; | ||
if (!readFile(path, json).ok()) { | ||
LOG(INFO) << "Could not read vscode extensions.json from " << path; | ||
return; | ||
} | ||
|
||
auto doc = JSON::newArray(); | ||
if (!doc.fromString(json) || !doc.doc().IsArray()) { | ||
LOG(WARNING) << "Could not parse vscode extensions.json from " << path; | ||
return; | ||
} | ||
|
||
for (const rapidjson::Value& extension : doc.doc().GetArray()) { | ||
if (!(extension.IsObject() && extension.HasMember("identifier") && | ||
extension.HasMember("metadata") && extension.HasMember("location"))) { | ||
LOG(WARNING) << "Extension entry missing expected subkeys in " << path; | ||
continue; | ||
} | ||
const rapidjson::Value& identifier = extension["identifier"]; | ||
const rapidjson::Value& metadata = extension["metadata"]; | ||
const rapidjson::Value& location = extension["location"]; | ||
if (!(identifier.IsObject() && metadata.IsObject() && | ||
location.IsObject())) { | ||
LOG(WARNING) << "Extension subkeys are not objects in " << path; | ||
continue; | ||
} | ||
|
||
Row r; | ||
r["uid"] = uid; | ||
|
||
rapidjson::Value::ConstMemberIterator it = identifier.FindMember("id"); | ||
if (it != identifier.MemberEnd() && it->value.IsString()) { | ||
r["name"] = it->value.GetString(); | ||
} | ||
|
||
it = identifier.FindMember("uuid"); | ||
if (it != identifier.MemberEnd() && it->value.IsString()) { | ||
r["uuid"] = it->value.GetString(); | ||
} | ||
|
||
it = extension.FindMember("version"); | ||
if (it != extension.MemberEnd() && it->value.IsString()) { | ||
r["version"] = it->value.GetString(); | ||
} | ||
|
||
it = location.FindMember("path"); | ||
if (it != location.MemberEnd() && it->value.IsString()) { | ||
r["path"] = it->value.GetString(); | ||
} | ||
|
||
it = metadata.FindMember("publisherDisplayName"); | ||
if (it != metadata.MemberEnd() && it->value.IsString()) { | ||
r["publisher"] = it->value.GetString(); | ||
} | ||
|
||
it = metadata.FindMember("publisherId"); | ||
if (it != metadata.MemberEnd() && it->value.IsString()) { | ||
r["publisher_id"] = it->value.GetString(); | ||
} | ||
|
||
it = metadata.FindMember("installedTimestamp"); | ||
if (it != metadata.MemberEnd() && it->value.IsInt64()) { | ||
r["installed_at"] = INTEGER(it->value.GetInt64()); | ||
} | ||
|
||
it = metadata.FindMember("isPreReleaseVersion"); | ||
if (it != metadata.MemberEnd() && it->value.IsBool()) { | ||
r["prerelease"] = INTEGER(it->value.GetBool() ? "1" : "0"); | ||
} | ||
|
||
results.push_back(r); | ||
} | ||
} | ||
|
||
QueryData genVSCodeExtensions(QueryContext& context) { | ||
QueryData results; | ||
|
||
// find vscode config directories | ||
std::set<std::pair<std::string, fs::path>> confDirs; | ||
auto users = usersFromContext(context); | ||
for (const auto& row : users) { | ||
auto uid = row.find("uid"); | ||
auto directory = row.find("directory"); | ||
if (directory == row.end() || uid == row.end()) { | ||
continue; | ||
} | ||
confDirs.insert( | ||
{uid->second, fs::path(directory->second) / ".vscode-server"}); | ||
confDirs.insert({uid->second, fs::path(directory->second) / ".vscode"}); | ||
} | ||
|
||
for (const auto& confDir : confDirs) { | ||
auto path = confDir.second / "extensions" / "extensions.json"; | ||
genReadJSONAndAddExtensionRows(confDir.first, path.string(), results); | ||
} | ||
|
||
return results; | ||
} | ||
} // namespace tables | ||
} // namespace osquery |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
table_name("vscode_extensions") | ||
description("Lists all vscode extensions.") | ||
schema([ | ||
Column("name", TEXT, "Extension Name"), | ||
Column("uuid", TEXT, "Extension UUID"), | ||
Column("version", TEXT, "Extension version"), | ||
Column("path", TEXT, "Extension path"), | ||
Column("publisher", TEXT, "Publisher Name"), | ||
Column("publisher_id", TEXT, "Publisher ID"), | ||
Column("installed_at", BIGINT, "Installed Timestamp"), | ||
Column("prerelease", INTEGER, "Pre release version"), | ||
Column("uid", BIGINT, "The local user that owns the plugin", | ||
additional=True), | ||
]) | ||
attributes(user_data=True) | ||
implementation("applications/vscode_extensions@genVSCodeExtensions") | ||
examples([ | ||
"select * from vscode_extensions", | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/** | ||
* Copyright (c) 2014-present, The osquery authors | ||
* | ||
* This source code is licensed as defined by the LICENSE file found in the | ||
* root directory of this source tree. | ||
* | ||
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only) | ||
*/ | ||
|
||
// Sanity check integration test for vscode_extensions | ||
// Spec file: specs/vscode_extensions.table | ||
|
||
#include <osquery/dispatcher/dispatcher.h> | ||
#include <osquery/logger/logger.h> | ||
#include <osquery/tests/integration/tables/helper.h> | ||
#include <osquery/tests/test_util.h> | ||
|
||
namespace osquery { | ||
namespace table_tests { | ||
|
||
class vscodeExtensions : public testing::Test { | ||
protected: | ||
void SetUp() override { | ||
setUpEnvironment(); | ||
} | ||
|
||
#ifdef OSQUERY_WINDOWS | ||
static void SetUpTestSuite() { | ||
initUsersAndGroupsServices(true, false); | ||
} | ||
|
||
static void TearDownTestSuite() { | ||
Dispatcher::stopServices(); | ||
Dispatcher::joinServices(); | ||
deinitUsersAndGroupsServices(true, false); | ||
Dispatcher::instance().resetStopping(); | ||
} | ||
#endif | ||
}; | ||
|
||
TEST_F(vscodeExtensions, test_sanity) { | ||
auto const data = execute_query("select * from vscode_extensions"); | ||
if (data.empty()) { | ||
LOG(WARNING) | ||
<< "Empty results of query from 'vscode_extensions', assume there " | ||
"is no vscode on the system"; | ||
return; | ||
} | ||
|
||
ValidationMap row_map = { | ||
{"id", NormalType}, | ||
{"version", NormalType}, | ||
{"path", NormalType}, | ||
{"publisher", NormalType}, | ||
{"installed_at", NonNegativeInt}, | ||
{"prerelease", Bool}, | ||
{"uid", NonNegativeInt}, | ||
}; | ||
validate_rows(data, row_map); | ||
} | ||
|
||
} // namespace table_tests | ||
} // namespace osquery |