Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new query language plugin #2074

Merged
merged 12 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/vast.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,9 @@ jobs:
path: plugins/broker
dependencies:
- libbroker-dev
- name: Sigma
mavam marked this conversation as resolved.
Show resolved Hide resolved
target: sigma
path: plugins/sigma
env:
INSTALL_DIR: '${{ github.workspace }}/_install'
BUILD_DIR: '${{ github.workspace }}/_build'
Expand Down
28 changes: 14 additions & 14 deletions libvast/src/system/spawn_arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
#include "vast/concept/parseable/vast/expression.hpp"
#include "vast/concept/parseable/vast/schema.hpp"
#include "vast/detail/load_contents.hpp"
#include "vast/detail/sigma.hpp"
#include "vast/detail/string.hpp"
#include "vast/error.hpp"
#include "vast/expression.hpp"
#include "vast/logger.hpp"
#include "vast/plugin.hpp"
#include "vast/schema.hpp"

#include <caf/config_value.hpp>
Expand All @@ -35,19 +35,19 @@ normalized_and_validated(std::vector<std::string>::const_iterator begin,
std::vector<std::string>::const_iterator end) {
if (begin == end)
return caf::make_error(ec::syntax_error, "no query expression given");
auto str = detail::join(begin, end, " ");
// TODO: improve error handling. Right now, we silently try to parse the
// query expression as Sigma rule. If it fails, we only print a debug log
// entry and move on to parsing the input as VAST expression.
if (auto yaml = from_yaml(str)) {
if (auto e = detail::sigma::parse_rule(*yaml))
return normalize_and_validate(std::move(*e));
else
VAST_DEBUG("failed to parse query as Sigma rule: {}", e.error());
} else {
VAST_DEBUG("failed to parse input as YAML: {}", yaml.error());
}
if (auto e = to<expression>(str)) {
auto query = detail::join(begin, end, " ");
// We are trying all query language plugins in a non-deterministic order.
mavam marked this conversation as resolved.
Show resolved Hide resolved
for (const auto& plugin : plugins::get())
if (const auto* query_lang = plugin.as<query_language_plugin>()) {
if (auto expr = query_lang->parse(query))
return normalize_and_validate(std::move(*expr));
else
// TODO: Demote to DEBUG when polishing the PR.
VAST_ERROR("failed to parse query as {} languae: {}", plugin->name(),
expr.error());
mavam marked this conversation as resolved.
Show resolved Hide resolved
}
// TODO: rewrite the VAST expression as a query language plugin.
if (auto e = to<expression>(query)) {
mavam marked this conversation as resolved.
Show resolved Hide resolved
return normalize_and_validate(std::move(*e));
} else {
VAST_DEBUG("failed to parse query as VAST expression: {}", e.error());
Expand Down
14 changes: 14 additions & 0 deletions libvast/vast/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,20 @@ class store_plugin : public virtual plugin {
std::span<const std::byte> header) const = 0;
};

// -- query language plugin ---------------------------------------------------

/// A query language parser to pass query in a custom language to VAST.
/// @relates plugin
class query_language_plugin : public virtual plugin {
public:
/// Parses a query expression string into a VAST expression.
/// @param The string representing the custom query.
/// In the future, we may want to let this plugin return a substrait query
/// plan instead of a VAST expression.
[[nodiscard]] virtual caf::expected<expression>
parse(std::string_view query) const = 0;
};

// -- plugin_ptr ---------------------------------------------------------------

/// An owned plugin and dynamically loaded plugin.
Expand Down
7 changes: 7 additions & 0 deletions plugins/sigma/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

This changelog documents all notable changes to the Sigma plugin for VAST.

## v0.1.0
mavam marked this conversation as resolved.
Show resolved Hide resolved

This is the first official release.
24 changes: 24 additions & 0 deletions plugins/sigma/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.18...3.22 FATAL_ERROR)

project(
sigma
VERSION 1.0.0
DESCRIPTION "Sigma query language plugin for VAST"
LANGUAGES CXX)

include(CTest)

file(GLOB_RECURSE sigma_sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")

file(GLOB_RECURSE sigma_tests CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp")

find_package(VAST REQUIRED)
VASTRegisterPlugin(
TARGET sigma
ENTRYPOINT src/plugin.cpp
SOURCES ${sigma_sources}
TEST_SOURCES ${sigma_tests}
INCLUDE_DIRECTORIES include)
3 changes: 3 additions & 0 deletions plugins/sigma/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sigma Plugin for VAST

to be written
mavam marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include <caf/expected.hpp>

/// Utilities to work with [Sigma](https://github.com/Neo23x0/sigma).
namespace vast::detail::sigma {
namespace vast::plugins::sigma {

/// Parses a *rule* as VAST expression.
/// @param yaml The rule contents.
Expand All @@ -27,4 +27,4 @@ caf::expected<expression> parse_rule(const data& yaml);
/// @returns The VAST expression corresponding to *yaml*.
caf::expected<expression> parse_search_id(const data& yaml);

} // namespace vast::detail::sigma
} // namespace vast::plugins::sigma
1 change: 1 addition & 0 deletions plugins/sigma/integration/data/zeek
26 changes: 26 additions & 0 deletions plugins/sigma/integration/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
config-file: vast.yaml

fixtures:
ServerTester:
enter: | # python
node = Server(self.cmd,
['-e', f'127.0.0.1:{VAST_PORT}', '-i', 'node', 'start'],
work_dir, name='node', port=VAST_PORT,
config_file=self.config_file)
cmd += ['-e', f'127.0.0.1:{VAST_PORT}']

exit: | # python
node.stop()

tests:
# This test only checks whether it is possible to take a Sigma rule as valid
# input and perform a simple query. It does not check for subtleties in the
# expression language itself. See the corresponding unit tests for that.
Sigma:
tags: [server, import-export, sigma, zeek]
fixture: ServerTester
steps:
- command: import -b zeek
input: data/zeek/conn.log.gz
- command: export json
input: data/sigma/zeek-conn.yaml
3 changes: 3 additions & 0 deletions plugins/sigma/integration/vast.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vast:
plugins:
- sigma
20 changes: 10 additions & 10 deletions libvast/src/detail/sigma.cpp → plugins/sigma/src/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@
// SPDX-FileCopyrightText: (c) 2021 The VAST Contributors
// SPDX-License-Identifier: BSD-3-Clause

#include "vast/detail/sigma.hpp"
#include "sigma/parse.hpp"

#include "vast/concept/parseable/core.hpp"
#include "vast/concept/parseable/string.hpp"
#include "vast/detail/base64.hpp"
#include "vast/detail/string.hpp"
#include "vast/error.hpp"
#include "vast/expression_visitors.hpp"
#include <vast/concept/parseable/core.hpp>
#include <vast/concept/parseable/string.hpp>
#include <vast/detail/base64.hpp>
#include <vast/detail/string.hpp>
#include <vast/error.hpp>
#include <vast/expression_visitors.hpp>

#include <map>
#include <regex>
#include <string>
#include <tuple>
#include <vector>

namespace vast::detail::sigma {
namespace vast::plugins::sigma {

// TODO: A lot of code in here is directly copied from
// src/concept/parseable/expression.cpp. We should factor the implementation in
Expand Down Expand Up @@ -239,7 +239,7 @@ caf::expected<expression> parse_search_id(const data& yaml) {
if (auto xs = caf::get_if<record>(&yaml)) {
conjunction result;
for (auto& [key, rhs] : *xs) {
auto keys = split(key, "|");
auto keys = detail::split(key, "|");
auto extractor = field_extractor{std::string{keys[0]}};
auto op = relational_operator::equal;
auto all = false;
Expand Down Expand Up @@ -370,4 +370,4 @@ caf::expected<expression> parse_rule(const data& yaml) {
return result;
}

} // namespace vast::detail::sigma
} // namespace vast::plugins::sigma
42 changes: 42 additions & 0 deletions plugins/sigma/src/plugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// _ _____ __________
// | | / / _ | / __/_ __/ Visibility
// | |/ / __ |_\ \ / / Across
// |___/_/ |_/___/ /_/ Space and Time
//
// SPDX-FileCopyrightText: (c) 2022 The VAST Contributors
// SPDX-License-Identifier: BSD-3-Clause

#include "sigma/parse.hpp"

#include <vast/data.hpp>
#include <vast/error.hpp>
#include <vast/plugin.hpp>

#include <caf/error.hpp>
#include <caf/expected.hpp>
#include <fmt/format.h>

namespace vast::plugins::sigma {

class plugin final : public virtual query_language_plugin {
caf::error initialize(data) override {
return caf::none;
}

[[nodiscard]] const char* name() const override {
return "sigma";
}

[[nodiscard]] caf::expected<expression>
parse(std::string_view query) const override {
if (auto yaml = from_yaml(query))
return parse_rule(*yaml);
else
return caf::make_error(ec::invalid_query,
fmt::format("not a Sigma rule: {}", yaml.error()));
}
};

} // namespace vast::plugins::sigma

VAST_REGISTER_PLUGIN(vast::plugins::sigma::plugin)
20 changes: 10 additions & 10 deletions libvast/test/detail/sigma.cpp → plugins/sigma/tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
// | |/ / __ |_\ \ / / Across
// |___/_/ |_/___/ /_/ Space and Time
//
// SPDX-FileCopyrightText: (c) 2021 The VAST Contributors
// SPDX-FileCopyrightText: (c) 2022 The VAST Contributors
// SPDX-License-Identifier: BSD-3-Clause

#define SUITE sigma

#include "vast/detail/sigma.hpp"
#include "sigma/parse.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"
#include "vast/concept/printable/stream.hpp"
#include "vast/concept/printable/vast/expression.hpp"
#include "vast/expression.hpp"
#include "vast/test/test.hpp"
#include <vast/concept/parseable/to.hpp>
#include <vast/concept/parseable/vast/expression.hpp>
#include <vast/concept/printable/stream.hpp>
#include <vast/concept/printable/vast/expression.hpp>
#include <vast/expression.hpp>
#include <vast/test/test.hpp>

#include <caf/test/dsl.hpp>

Expand All @@ -27,11 +27,11 @@ using namespace vast::detail;
namespace {

expression to_search_id(std::string_view yaml) {
return unbox(sigma::parse_search_id(unbox(from_yaml(yaml))));
return unbox(plugins::sigma::parse_search_id(unbox(from_yaml(yaml))));
}

expression to_rule(std::string_view yaml) {
return unbox(sigma::parse_rule(unbox(from_yaml(yaml))));
return unbox(plugins::sigma::parse_rule(unbox(from_yaml(yaml))));
}

expression to_expr(std::string_view expr) {
Expand Down
11 changes: 0 additions & 11 deletions vast/integration/vast_integration_suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -534,17 +534,6 @@ tests:
input: data/suricata/eve.json
transformation: sleep 4
- command: count '#type == /zeek.*/'
# This test only checks whether it is possible to take a Sigma rule as valid
# input and perform a simple query. It does not check for subtleties in the
# expression language itself. See the corresponding unit tests for that.
Sigma:
tags: [server, import-export, sigma, zeek]
fixture: ServerTester
steps:
- command: import -b zeek
input: data/zeek/conn.log.gz
- command: export json
input: data/sigma/zeek-conn.yaml
Transforms:
tags: [server, client, import-export, transforms, suricata]
fixture: TransformsTester
Expand Down