Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
directionless committed Mar 3, 2021
1 parent d6f66ff commit 4fd8412
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 46 deletions.
83 changes: 37 additions & 46 deletions osquery/tables/system/posix/augeas.cpp
Expand Up @@ -153,18 +153,40 @@ class AugeasHandle {

static AugeasHandle kAugeasHandle;

// Augeas presents data as a slash seperated tree. It uses `/*` as a
// single level wildcard, and `//*` as a recursive wildcard. However,
// sqlite uses % as a wildcard. To allow for LIKE expressions, we need
// to convert.
void convertWildcards(std::string& str) {
size_t pos;
while ((pos = str.find("%%")) != std::string::npos) {
str.replace(pos, 2, "/*");
std::string patternFromOsquery(const std::string& input,
bool isLike,
bool isPath) {
// If this is a path, then we must prepend /files. Otherwise we
// assume the caller knows what it's doing.
std::string pattern = isPath ? "/files" + input : input;

// Augeas presents data as a slash seperated tree. It uses `/*` as a
// single level wildcard, and `//*` as a recursive wildcard. However,
// sqlite uses % as a wildcard. To allow for LIKE expressions, we need
// to convert.
if (isLike) {
size_t pos;
while ((pos = pattern.find("%%")) != std::string::npos) {
pattern.replace(pos, 2, "/*");
}
while ((pos = pattern.find("%")) != std::string::npos) {
pattern.replace(pos, 1, "*");
}
}
while ((pos = str.find("%")) != std::string::npos) {
str.replace(pos, 1, "*");

// augues blurs filename and contents into the node. So when
// dealing with files, osquery must append the recuse wildcard. To
// allow a LIKE query some flexibility, and to prevent augeas
// syntax errors on extra wildcards, we only do this if there is
// not already a wildcard there. (This handles both the LIKE and
// EQUALS case)
if (isPath) {
if (strncmp(&pattern.back(), "*", 1) != 0) {
pattern.append("//*");
}
}

return pattern;
}

QueryData genAugeas(QueryContext& context) {
Expand Down Expand Up @@ -218,36 +240,22 @@ QueryData genAugeas(QueryContext& context) {

if (context.hasConstraint("node", LIKE)) {
auto nodes = context.constraints["node"].getAll(LIKE);
for (std::string node : nodes) {
for (const auto& node : nodes) {
if (node.empty()) {
continue;
}
convertWildcards(node);
patterns.insert(node);
patterns.insert(patternFromOsquery(node, true, false));
}
}

if (context.hasConstraint("path", EQUALS)) {
// Allow requests via filesystem path.
auto paths = context.constraints["path"].getAll(EQUALS);
std::ostringstream pattern;

for (const auto& path : paths) {
if (path.empty()) {
continue;
}

pattern << "/files" << path;
patterns.insert(pattern.str());

pattern.clear();
pattern.str(std::string());

pattern << "/files" << path << "//*";
patterns.insert(pattern.str());

pattern.clear();
pattern.str(std::string());
patterns.insert(patternFromOsquery(path, false, true));
}
}

Expand All @@ -256,28 +264,11 @@ QueryData genAugeas(QueryContext& context) {
// will break.
if (context.hasConstraint("path", LIKE)) {
auto paths = context.constraints["path"].getAll(LIKE);
std::ostringstream pattern;

for (std::string path : paths) {
for (const auto& path : paths) {
if (path.empty()) {
continue;
}

convertWildcards(path);

pattern << "/files" << path;
patterns.insert(pattern.str());

pattern.clear();
pattern.str(std::string());

if (!strncmp(&path.back(), "*", 1)) {
pattern << "/files" << path << "//*";
patterns.insert(pattern.str());
}

pattern.clear();
pattern.str(std::string());
patterns.insert(patternFromOsquery(path, true, true));
}
}

Expand Down
51 changes: 51 additions & 0 deletions osquery/tables/system/tests/posix/augeas_tests.cpp
Expand Up @@ -87,5 +87,56 @@ TEST_F(AugeasTests, select_hosts_by_node) {
<< "Value is not empty. Got " << results.rows()[0].at("value")
<< "instead";
}

TEST_F(AugeasTests, select_augeas_load) {
auto results = SQL("select * from augeas where node = '/augeas/load'");
ASSERT_EQ(results.rows().size(), 1U);
ASSERT_EQ(results.rows()[0].at("node"), "/augeas/load");
ASSERT_EQ(results.rows()[0].at("label"), "load");
ASSERT_TRUE(results.rows()[0].at("path").empty());
ASSERT_TRUE(results.rows()[0].at("value").empty());
}

TEST_F(AugeasTests, select_augeas_load_wildcards) {
// Exact matches, should be 1 result
ASSERT_EQ(
SQL("select * from augeas where node LIKE '/augeas/load'").rows().size(),
1U);
ASSERT_EQ(SQL("select * from augeas where node LIKE '/%/load'").rows().size(),
1U);

// Single recurse, about 200 results
ASSERT_GT(SQL("select * from augeas where node LIKE '/augeas/load/%'")
.rows()
.size(),
100U);

// full recuse, about 1500 results
ASSERT_GT(SQL("select * from augeas where node LIKE '/augeas/load/%%'")
.rows()
.size(),
1000U);
}

TEST_F(AugeasTests, select_file_wildcards) {
// These are a bit funny. Augeas doesn't do partial matches,
// and because file is a real file, you have to be carefuly
// with trailing slashes.
ASSERT_EQ(
SQL("select * from augeas where path LIKE '/etc/hosts/%'").rows().size(),
0U);
ASSERT_EQ(
SQL("select * from augeas where path LIKE '/etc/hosts%'").rows().size(),
0U);
ASSERT_GE(
SQL("select * from augeas where path LIKE '/etc/hosts'").rows().size(),
1U);
ASSERT_GE(
SQL("select * from augeas where path LIKE '/etc/hosts%%'").rows().size(),
1U);
ASSERT_GE(
SQL("select * from augeas where path LIKE '/%/hosts'").rows().size(), 1U);
}

} // namespace tables
} // namespace osquery

0 comments on commit 4fd8412

Please sign in to comment.