Add new hook HookLoadFileExtended that allows plugins to supply Zeek script code to parse.

The new hooks works similar to the existing `HookLoadFile` but,
additionally, allows the plugin to return a string that contains the
code to be used for the file being loaded. If the plugin does so, the
content of any actual file on disk will be ignored (in fact, there
doesn't even need to be a file on disk in that case). This works for
both Zeek scripts and signatures.

There's a new test that covers the new functionality, testing loading
both scripts and signatures from memory. I also manually tested that the
debugger integration works, but I don't see much of a way to add a
regression test for that part.

We keep the existing hook as well for backwards compatibility. We could
decide to deprecate it, but not sure that buys us much, so left that
out.

Closes #1757.
This commit is contained in:
Robin Sommer 2021-09-24 12:50:27 +02:00
parent 1efaf8d7a4
commit 34eaf42b92
21 changed files with 1525 additions and 92 deletions

View file

@ -24,10 +24,14 @@
#include "zeek/ZeekString.h"
#include "zeek/analyzer/Analyzer.h"
#include "zeek/module_util.h"
#include "zeek/plugin/Manager.h"
using namespace std;
// Functions exposed by rule-scan.l
extern void rules_set_input_from_buffer(const char* data, size_t size);
extern void rules_set_input_from_file(FILE* f);
extern void rules_parse_input();
namespace zeek::detail
{
@ -262,11 +266,18 @@ bool RuleMatcher::ReadFiles(const std::vector<SignatureFile>& files)
if ( ! f.full_path )
f.full_path = util::find_file(f.file, util::zeek_path(), ".sig");
int rc = PLUGIN_HOOK_WITH_RESULT(
std::pair<int, std::optional<std::string>> rc = {-1, std::nullopt};
rc.first = PLUGIN_HOOK_WITH_RESULT(
HOOK_LOAD_FILE, HookLoadFile(zeek::plugin::Plugin::SIGNATURES, f.file, *f.full_path),
-1);
switch ( rc )
if ( rc.first < 0 )
rc = PLUGIN_HOOK_WITH_RESULT(
HOOK_LOAD_FILE_EXT,
HookLoadFileExtended(zeek::plugin::Plugin::SIGNATURES, f.file, *f.full_path),
std::make_pair(-1, std::nullopt));
switch ( rc.first )
{
case -1:
// No plugin in charge of this file.
@ -287,26 +298,45 @@ bool RuleMatcher::ReadFiles(const std::vector<SignatureFile>& files)
break;
case 1:
// A plugin took care of it, just skip.
continue;
if ( ! rc.second )
// A plugin took care of it, just skip.
continue;
break;
default:
assert(false);
break;
}
rules_in = util::open_file(*f.full_path);
FILE* rules_in = nullptr;
if ( ! rules_in )
if ( rc.first == 1 )
{
reporter->Error("Can't open signature file %s", f.file.c_str());
return false;
// Parse code provided by plugin.
assert(rc.second);
rules_set_input_from_buffer(rc.second->data(), rc.second->size());
}
else
{
// Parse from file.
rules_in = util::open_file(*f.full_path);
if ( ! rules_in )
{
reporter->Error("Can't open signature file %s", f.file.c_str());
return false;
}
rules_set_input_from_file(rules_in);
}
rules_line_number = 0;
current_rule_file = f.full_path->c_str();
rules_parse();
fclose(rules_in);
rules_parse_input();
if ( rules_in )
fclose(rules_in);
}
if ( parse_error )