Feb 15th 2023 update

This commit is contained in:
Ned Wright
2023-02-15 19:09:38 +00:00
parent f7934cd09d
commit 6a9b33ff93
159 changed files with 16474 additions and 2096 deletions

View File

@@ -191,7 +191,7 @@ protected:
dbgWarning(D_WAAP) << "Failed to send object. Attempt: " << i;
mainloop->yield(true);
}
dbgError(D_WAAP) << "Failed to send object, reached maximum attempts: " <<
dbgError(D_WAAP) << "Failed to send object to " << uri << ", reached maximum attempts: " <<
max_send_obj_retries;
return false;
}
@@ -245,7 +245,7 @@ protected:
dbgWarning(D_WAAP) << "Failed to send object. Attempt: " << i;
mainloop->yield(true);
}
dbgError(D_WAAP) << "Failed to send object, reached maximum attempts: " <<
dbgError(D_WAAP) << "Failed to send object to " << uri << ", reached maximum attempts: " <<
max_send_obj_retries;
return false;
}

View File

@@ -129,17 +129,29 @@ SourceReputationFeaturesAgg::addHeaders(const ReputationFeaturesEntry &entry)
}
const auto &referer_header_itr = headers.find("referer");
if (referer_header_itr == headers.cend()) {
if (referer_header_itr == headers.cend() || referer_header_itr->second.empty()) {
m_referer_count.na++;
} else {
const string &uri = referer_header_itr->second;
size_t scheme_end_pos = uri.find("://") + 3;
size_t authority_end_pos = uri.find("/", scheme_end_pos + 1);
string authority = uri.substr(scheme_end_pos + 1, authority_end_pos);
if (authority.find(entry.getHost()) != string::npos) {
m_referer_count.external_host++;
size_t scheme_end_pos = uri.find("://");
if (scheme_end_pos != string::npos) {
string authority;
scheme_end_pos = scheme_end_pos + 3;
size_t authority_end_pos = uri.find("/", scheme_end_pos);
if (authority_end_pos == string::npos) {
authority = uri.substr(scheme_end_pos);
} else {
authority = uri.substr(scheme_end_pos, authority_end_pos - scheme_end_pos);
}
if (authority.find(entry.getHost()) != string::npos) {
m_referer_count.internal_host++;
} else {
m_referer_count.external_host++;
}
} else {
m_referer_count.internal_host++;
m_referer_count.external_host++;
dbgTrace(D_WAAP_REPUTATION) << "No scheme found in referer header: " << uri;
}
}

View File

@@ -82,6 +82,7 @@ add_library(waap_clib
SyncLearningNotification.cc
LogGenWrapper.cc
WaapSampleValue.cc
ParserGql.cc
)
add_definitions("-Wno-unused-function")

View File

@@ -17,6 +17,7 @@
#include "ParserUrlEncode.h"
#include "PHPSerializedDataParser.h"
#include "ParserJson.h"
#include "ParserGql.h"
#include "ParserConfluence.h"
#include "ParserXML.h"
#include "ParserHTML.h"
@@ -231,26 +232,30 @@ int DeepParser::onKv(const char* k, size_t k_len, const char* v, size_t v_len, i
bool base64ParamFound = false;
dbgTrace(D_WAAP_DEEP_PARSER) << " ===Processing potential base64===";
std::string base64_decoded_val, base64_key;
std::string decoded_val, key;
base64_variants base64_status = Waap::Util::b64Test (cur_val,
base64_key,
base64_decoded_val);
key,
decoded_val);
dbgTrace(D_WAAP_DEEP_PARSER) << " status = " << base64_status
<< " key = " << base64_key
<< " value = " << base64_decoded_val;
dbgTrace(D_WAAP_DEEP_PARSER)
<< " status = "
<< base64_status
<< " key = "
<< key
<< " value = "
<< decoded_val;
switch (base64_status) {
case SINGLE_B64_CHUNK_CONVERT:
cur_val = base64_decoded_val;
cur_val = decoded_val;
base64ParamFound = true;
break;
case KEY_VALUE_B64_PAIR:
// going deep with new pair in case value is not empty
if (base64_decoded_val.size() > 0) {
cur_val = base64_decoded_val;
if (decoded_val.size() > 0) {
cur_val = decoded_val;
base64ParamFound = true;
rc = onKv(base64_key.c_str(), base64_key.size(), cur_val.data(), cur_val.size(), flags);
rc = onKv(key.c_str(), key.size(), cur_val.data(), cur_val.size(), flags);
dbgTrace(D_WAAP_DEEP_PARSER) << " rc = " << rc;
if (rc != CONTINUE_PARSING) {
return rc;
@@ -323,6 +328,7 @@ int DeepParser::onKv(const char* k, size_t k_len, const char* v, size_t v_len, i
}
}
// Parse buffer
// Note: API report does not include output of "PIPE" and similar extracted stuff.
@@ -365,6 +371,21 @@ int DeepParser::onKv(const char* k, size_t k_len, const char* v, size_t v_len, i
return rc;
}
if (Waap::Util::detectJSONasParameter(cur_val, key, decoded_val)) {
dbgTrace(D_WAAP_DEEP_PARSER)
<< " detectJSONasParameter was true: key = "
<< key
<< " value = "
<< decoded_val;
rc = onKv(key.c_str(), key.size(), decoded_val.data(), decoded_val.size(), flags);
dbgTrace(D_WAAP_DEEP_PARSER) << " After processing potential JSON rc = " << rc;
if (rc != CONTINUE_PARSING) {
return rc;
}
}
m_depth--;
// Send key/value pair to the Signature scanner
@@ -878,6 +899,11 @@ void DeepParser::createInternalParser(const char *k, size_t k_len, std::string&
dbgTrace(D_WAAP_DEEP_PARSER) << "Starting to parse phpSerializedData";
m_parsersDeque.push_front(std::make_shared<BufferedParser<PHPSerializedDataParser>>(*this));
}
else if (isPotentialGqlQuery && cur_val.size() > 0 && !validateJson(cur_val.data(), cur_val.size())) {
// Graphql value detected
dbgTrace(D_WAAP_DEEP_PARSER) << "Starting to parse graphql";
m_parsersDeque.push_front(std::make_shared<BufferedParser<ParserGql>>(*this));
}
else if (cur_val.length() > 0 && (cur_val[0] == '[' || cur_val[0] == '{'))
{
boost::smatch confulence_match;

View File

@@ -25,7 +25,8 @@ IndicatorsFiltersManager::IndicatorsFiltersManager(const std::string& remotePath
:
SerializeToFileBase(pWaapAssetState->getWaapDataDir() + "/6.data"),
m_ignoreSources(pWaapAssetState->getWaapDataDir(), remotePath, assetId),
m_tuning(remotePath)
m_tuning(remotePath),
m_matchedOverrideKeywords()
{
restore();
m_keywordsFreqFilter = std::make_unique<KeywordIndicatorFilter>(
@@ -88,6 +89,12 @@ bool IndicatorsFiltersManager::shouldFilterKeyword(const std::string &key, const
shouldFilter |= m_keywordsFreqFilter->shouldFilterKeyword(type, keyword);
}
}
if (m_matchedOverrideKeywords.size() > 0 &&
m_matchedOverrideKeywords.find(keyword) != m_matchedOverrideKeywords.end())
{
dbgTrace(D_WAAP_OVERRIDE) << "Filtering keyword '" << keyword << "' due to override";
shouldFilter = true;
}
return shouldFilter;
}
@@ -315,3 +322,8 @@ void IndicatorsFiltersManager::pushSample(
}
m_typeFilter->registerKeywords(key, sample, pTransaction);
}
std::set<std::string> & IndicatorsFiltersManager::getMatchedOverrideKeywords(void)
{
return m_matchedOverrideKeywords;
}

View File

@@ -42,6 +42,7 @@ public:
virtual bool shouldFilterKeyword(const std::string &key, const std::string &keyword) const;
virtual void filterKeywords(const std::string &key, Waap::Keywords::KeywordsSet& keywords,
std::vector<std::string>& filteredKeywords);
std::set<std::string> &getMatchedOverrideKeywords(void);
void pushSample(const std::string& key, const std::string& sample, IWaf2Transaction* pTransaction);
@@ -67,4 +68,5 @@ private:
std::shared_ptr<Waap::TrustedSources::TrustedSourcesParameter> m_trustedSrcParams;
ScannerDetector m_ignoreSources;
TuningDecision m_tuning;
std::set<std::string> m_matchedOverrideKeywords;
};

View File

@@ -43,6 +43,7 @@ struct IParserReceiver2 {
virtual void onEndMap() = 0;
virtual void onStartArray() = 0;
virtual void onEndArray() = 0;
virtual void onEndOfData() = 0;
};
// Interface for receiver classes that can accept not only full key/value pairs, but also partial content

View File

@@ -0,0 +1,134 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ParserGql.h"
#include "graphqlparser/AstNode.h"
#include "graphqlparser/AstVisitor.h"
#include "graphqlparser/GraphQLParser.h"
#include "debug.h"
USE_DEBUG_FLAG(D_WAAP_PARSER_GQL);
const std::string ParserGql::m_parserName = "gqlParser";
ParserGql::ParserGql(IParserReceiver& receiver) :
m_receiver(receiver),
m_error(false),
m_curNameValues(0)
{
dbgFlow(D_WAAP_PARSER_GQL);
}
ParserGql::~ParserGql() {
dbgFlow(D_WAAP_PARSER_GQL);
}
size_t ParserGql::push(const char* buf, size_t len) {
dbgTrace(D_WAAP_PARSER_GQL) << "buf='" << std::string(buf, len) << "'";
if (len > 0) {
dbgTrace(D_WAAP_PARSER_GQL) << "appending " << len << " bytes ...";
m_buffer.append(buf, len);
return len;
}
const char *errorstr = nullptr;
dbgTrace(D_WAAP_PARSER_GQL) << "parsing ...";
std::unique_ptr<facebook::graphql::ast::Node> ast = facebook::graphql::parseString(m_buffer.c_str(), &errorstr);
if (!ast) {
dbgTrace(D_WAAP_PARSER_GQL) << "GraphQL parser failed: " << errorstr;
m_error = true;
return 0;
}
// Walk over AST and call the visitXXX callbacks
ast->accept(this);
// Handle corner case of last name visited without value: don't forget to output that name too
if (m_curNameValues == 0 && !m_curNodeName.empty()) {
dbgTrace(D_WAAP_PARSER_GQL) << "handle last name: '" << m_curNodeName << "'";
if (m_receiver.onKv(m_curNodeName.data(), m_curNodeName.size(), "", 0, BUFFERED_RECEIVER_F_BOTH) != 0) {
m_error = true;
}
}
return len;
}
void ParserGql::finish() {
push(NULL, 0);
}
const std::string &
ParserGql::name() const {
return m_parserName;
}
bool ParserGql::error() const {
return m_error;
}
bool ParserGql::visitValue(const char *value)
{
dbgTrace(D_WAAP_PARSER_GQL) << "'" << value << "'";
m_curNameValues++;
return m_receiver.onKv(m_curNodeName.data(), m_curNodeName.size(), value, strlen(value), BUFFERED_RECEIVER_F_BOTH);
}
bool ParserGql::visitName(const facebook::graphql::ast::Name &node)
{
dbgTrace(D_WAAP_PARSER_GQL) << node.getValue() << "'";
bool ret = true;
if (m_curNameValues == 0 && !m_curNodeName.empty()) {
ret = m_receiver.onKv(m_curNodeName.data(), m_curNodeName.size(), "", 0, BUFFERED_RECEIVER_F_BOTH);
}
// wait for next name
m_curNodeName = std::string(node.getValue());
m_curNameValues = 0;
return ret;
}
bool ParserGql::visitIntValue(const facebook::graphql::ast::IntValue &node)
{
dbgFlow(D_WAAP_PARSER_GQL);
return visitValue(node.getValue());
}
bool ParserGql::visitFloatValue(const facebook::graphql::ast::FloatValue &node)
{
dbgFlow(D_WAAP_PARSER_GQL);
return visitValue(node.getValue());
}
bool ParserGql::visitStringValue(const facebook::graphql::ast::StringValue &node)
{
dbgFlow(D_WAAP_PARSER_GQL);
return visitValue(node.getValue());
}
bool ParserGql::visitBooleanValue(const facebook::graphql::ast::BooleanValue &node)
{
dbgFlow(D_WAAP_PARSER_GQL);
return visitValue(node.getValue() ? "true" : "false");
}
bool ParserGql::visitNullValue(const facebook::graphql::ast::NullValue &node)
{
dbgFlow(D_WAAP_PARSER_GQL);
return visitValue("null");
}
bool ParserGql::visitEnumValue(const facebook::graphql::ast::EnumValue &node)
{
dbgFlow(D_WAAP_PARSER_GQL);
return visitValue(node.getValue());
}

View File

@@ -0,0 +1,56 @@
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __PARSER_GQL_H
#define __PARSER_GQL_H
#include <string.h>
#include <vector>
#include "ParserBase.h"
#include "graphqlparser/Ast.h"
#include "graphqlparser/AstNode.h"
#include "graphqlparser/AstVisitor.h"
#include "KeyStack.h"
class ParserGql : public ParserBase, public facebook::graphql::ast::visitor::AstVisitor {
public:
ParserGql(IParserReceiver &receiver);
virtual ~ParserGql();
size_t push(const char *data, size_t data_len);
void finish();
virtual const std::string &name() const;
bool error() const;
virtual size_t depth() { return 0; }
private:
IParserReceiver &m_receiver;
bool m_error;
std::string m_buffer;
std::string m_curNodeName;
int m_curNameValues;
bool visitValue(const char *value);
// Callbacks from the parser
bool visitName(const facebook::graphql::ast::Name &node) override;
bool visitIntValue(const facebook::graphql::ast::IntValue &node) override;
bool visitFloatValue(const facebook::graphql::ast::FloatValue &node) override;
bool visitStringValue(const facebook::graphql::ast::StringValue &node) override;
bool visitBooleanValue(const facebook::graphql::ast::BooleanValue &node) override;
bool visitNullValue(const facebook::graphql::ast::NullValue &node) override;
bool visitEnumValue(const facebook::graphql::ast::EnumValue &node) override;
public:
static const std::string m_parserName;
};
#endif // __PARSER_JQL_H

View File

@@ -270,6 +270,10 @@ size_t ParserJson::push(const char* buf, size_t len) {
m_state = s_error;
}
if (m_receiver2) {
m_receiver2->onEndOfData();
}
return 0;
}

View File

@@ -44,18 +44,19 @@ static const string defaultSharedStorageHost = "appsec-shared-storage-svc";
#define LEARNING_HOST_ENV_NAME "LEARNING_HOST"
static bool
isGZipped(const std::string &stream)
isGZipped(const string &stream)
{
if (stream.size() < 2) return false;
auto unsinged_stream = reinterpret_cast<const u_char *>(stream.data());
return unsinged_stream[0] == 0x1f && unsinged_stream[1] == 0x8b;
}
bool RestGetFile::loadJson(const std::string& json)
bool RestGetFile::loadJson(const string& json)
{
string json_str;
std::string json_str = json;
if (isGZipped(json_str) == 0)
json_str = json;
if (!isGZipped(json_str))
{
return ClientRest::loadJson(json_str);
}
@@ -66,7 +67,7 @@ bool RestGetFile::loadJson(const std::string& json)
reinterpret_cast<const unsigned char *>(json_str.c_str()));
if (res.ok){
json_str = std::string((const char *)res.output, res.num_output_bytes);
json_str = string((const char *)res.output, res.num_output_bytes);
if (res.output) free(res.output);
res.output = nullptr;
res.num_output_bytes = 0;
@@ -76,12 +77,12 @@ bool RestGetFile::loadJson(const std::string& json)
return ClientRest::loadJson(json_str);
}
Maybe<std::string> RestGetFile::genJson() const
Maybe<string> RestGetFile::genJson() const
{
Maybe<std::string> json = ClientRest::genJson();
Maybe<string> json = ClientRest::genJson();
if (json.ok())
{
std::string data = json.unpack();
string data = json.unpack();
auto compression_stream = initCompressionStream();
CompressionResult res = compressData(
compression_stream,
@@ -94,7 +95,7 @@ Maybe<std::string> RestGetFile::genJson() const
dbgWarning(D_WAAP_CONFIDENCE_CALCULATOR) << "Failed to gzip data";
return genError("Failed to compress data");
}
data = std::string((const char *)res.output, res.num_output_bytes);
data = string((const char *)res.output, res.num_output_bytes);
json = data;
@@ -104,7 +105,7 @@ Maybe<std::string> RestGetFile::genJson() const
}
return json;
}
SerializeToFilePeriodically::SerializeToFilePeriodically(std::chrono::seconds pollingIntervals, std::string filePath) :
SerializeToFilePeriodically::SerializeToFilePeriodically(ch::seconds pollingIntervals, string filePath) :
SerializeToFileBase(filePath),
m_lastSerialization(0),
m_interval(pollingIntervals)
@@ -140,7 +141,7 @@ void SerializeToFilePeriodically::backupWorker()
}
}
void SerializeToFilePeriodically::setInterval(std::chrono::seconds newInterval)
void SerializeToFilePeriodically::setInterval(ch::seconds newInterval)
{
if (m_interval != newInterval)
{
@@ -150,7 +151,7 @@ void SerializeToFilePeriodically::setInterval(std::chrono::seconds newInterval)
}
}
SerializeToFileBase::SerializeToFileBase(std::string fileName) : m_filePath(fileName)
SerializeToFileBase::SerializeToFileBase(string fileName) : m_filePath(fileName)
{
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "SerializeToFileBase::SerializeToFileBase() fname='" << m_filePath
<< "'";
@@ -163,12 +164,12 @@ SerializeToFileBase::~SerializeToFileBase()
void SerializeToFileBase::saveData()
{
std::fstream filestream;
fstream filestream;
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "saving to file: " << m_filePath;
filestream.open(m_filePath, std::fstream::out);
filestream.open(m_filePath, fstream::out);
std::stringstream ss;
stringstream ss;
if (filestream.is_open() == false) {
dbgWarning(D_WAAP_CONFIDENCE_CALCULATOR) << "failed to open file: " << m_filePath << " Error: "
@@ -182,12 +183,12 @@ void SerializeToFileBase::saveData()
filestream.close();
}
void SerializeToFileBase::loadFromFile(std::string filePath)
void SerializeToFileBase::loadFromFile(string filePath)
{
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "loadFromFile() file: " << filePath;
std::fstream filestream;
fstream filestream;
filestream.open(filePath, std::fstream::in);
filestream.open(filePath, fstream::in);
if (filestream.is_open() == false) {
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "failed to open file: " << filePath << " Error: " <<
@@ -200,14 +201,14 @@ void SerializeToFileBase::loadFromFile(std::string filePath)
// try to strip the unique ID from the path and load the file from the parent directory
// that might exist in previous run where instance awareness didn't exits.
I_InstanceAwareness* instanceAwareness = Singleton::Consume<I_InstanceAwareness>::by<WaapComponent>();
Maybe<std::string> id = instanceAwareness->getUniqueID();
Maybe<string> id = instanceAwareness->getUniqueID();
if (!id.ok())
{
return;
}
std::string idStr = "/" + id.unpack() + "/";
string idStr = "/" + id.unpack() + "/";
size_t idPosition = filePath.find(idStr);
if (idPosition != std::string::npos)
if (idPosition != string::npos)
{
filePath.erase(idPosition, idStr.length() - 1);
dbgDebug(D_WAAP_CONFIDENCE_CALCULATOR) << "retry to load file from : " << filePath;
@@ -219,12 +220,12 @@ void SerializeToFileBase::loadFromFile(std::string filePath)
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "loading from file: " << filePath;
int length;
filestream.seekg(0, std::ios::end); // go to the end
filestream.seekg(0, ios::end); // go to the end
length = filestream.tellg(); // report location (this is the length)
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "file length: " << length;
assert(length >= 0); // length -1 really happens if filePath is a directory (!)
char* buffer = new char[length]; // allocate memory for a buffer of appropriate dimension
filestream.seekg(0, std::ios::beg); // go back to the beginning
filestream.seekg(0, ios::beg); // go back to the beginning
if (!filestream.read(buffer, length)) // read the whole file into the buffer
{
filestream.close();
@@ -234,18 +235,18 @@ void SerializeToFileBase::loadFromFile(std::string filePath)
}
filestream.close();
std::string dataObfuscated(buffer, length);
string dataObfuscated(buffer, length);
delete[] buffer;
std::stringstream ss(dataObfuscated);
stringstream ss(dataObfuscated);
try
{
deserialize(ss);
}
catch (std::runtime_error & e) {
catch (runtime_error & e) {
dbgWarning(D_WAAP_CONFIDENCE_CALCULATOR) << "failed to deserialize file: " << m_filePath << ", error: " <<
e.what();
}
@@ -263,7 +264,7 @@ RemoteFilesList::RemoteFilesList() : files(), filesPathsList()
// parses xml instead of json
// extracts a file list in <Contents><Key>
bool RemoteFilesList::loadJson(const std::string& xml)
bool RemoteFilesList::loadJson(const string& xml)
{
xmlDocPtr doc; // the resulting document tree
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "XML input: " << xml;
@@ -293,22 +294,22 @@ bool RemoteFilesList::loadJson(const std::string& xml)
{
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "Found the Contents element";
xmlNodePtr contents_node = node->children;
std::string file;
std::string lastModified;
string file;
string lastModified;
while (contents_node != NULL)
{
if (xmlStrEqual(key_name, contents_node->name) == 1)
{
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "Found the Key element";
xmlChar* xml_file = xmlNodeGetContent(contents_node);
file = std::string(reinterpret_cast<const char*>(xml_file));
file = string(reinterpret_cast<const char*>(xml_file));
xmlFree(xml_file);
}
if (xmlStrEqual(last_modified_name, contents_node->name) == 1)
{
dbgTrace(D_WAAP_CONFIDENCE_CALCULATOR) << "Found the LastModified element";
xmlChar* xml_file = xmlNodeGetContent(contents_node);
lastModified = std::string(reinterpret_cast<const char*>(xml_file));
lastModified = string(reinterpret_cast<const char*>(xml_file));
xmlFree(xml_file);
}
if (!file.empty() && !lastModified.empty())
@@ -333,24 +334,24 @@ bool RemoteFilesList::loadJson(const std::string& xml)
return true;
}
const std::vector<std::string>& RemoteFilesList::getFilesList() const
const vector<string>& RemoteFilesList::getFilesList() const
{
return filesPathsList;
}
const std::vector<FileMetaData>& RemoteFilesList::getFilesMetadataList() const
const vector<FileMetaData>& RemoteFilesList::getFilesMetadataList() const
{
return files.get();
}
SerializeToLocalAndRemoteSyncBase::SerializeToLocalAndRemoteSyncBase(
std::chrono::minutes interval,
std::chrono::seconds waitForSync,
const std::string& filePath,
const std::string& remotePath,
const std::string& assetId,
const std::string& owner)
ch::minutes interval,
ch::seconds waitForSync,
const string& filePath,
const string& remotePath,
const string& assetId,
const string& owner)
:
SerializeToFileBase(filePath),
m_remotePath(remotePath),
@@ -400,7 +401,7 @@ SerializeToLocalAndRemoteSyncBase::SerializeToLocalAndRemoteSyncBase(
if (parts[0].empty()) {
offset = 1;
}
std::string type = "";
string type = "";
for (size_t i = offset + 2; i < parts.size(); i++)
{
type += type.empty() ? parts[i] : "/" + parts[i];
@@ -417,7 +418,7 @@ bool SerializeToLocalAndRemoteSyncBase::isBase()
return m_remotePath == "";
}
std::string SerializeToLocalAndRemoteSyncBase::getUri()
string SerializeToLocalAndRemoteSyncBase::getUri()
{
static const string hybridModeUri = "/api";
static const string onlineModeUri = "/storage/waap";
@@ -437,24 +438,24 @@ SerializeToLocalAndRemoteSyncBase::~SerializeToLocalAndRemoteSyncBase()
}
std::string SerializeToLocalAndRemoteSyncBase::getWindowId()
string SerializeToLocalAndRemoteSyncBase::getWindowId()
{
return "window_" + std::to_string(m_daysCount) + "_" + std::to_string(m_windowsCount);
return "window_" + to_string(m_daysCount) + "_" + to_string(m_windowsCount);
}
std::string SerializeToLocalAndRemoteSyncBase::getPostDataUrl()
string SerializeToLocalAndRemoteSyncBase::getPostDataUrl()
{
std::string agentId = Singleton::Consume<I_AgentDetails>::by<WaapComponent>()->getAgentId();
string agentId = Singleton::Consume<I_AgentDetails>::by<WaapComponent>()->getAgentId();
if (Singleton::exists<I_InstanceAwareness>())
{
I_InstanceAwareness* instance = Singleton::Consume<I_InstanceAwareness>::by<WaapComponent>();
Maybe<std::string> uniqueId = instance->getUniqueID();
Maybe<string> uniqueId = instance->getUniqueID();
if (uniqueId.ok())
{
agentId += "/" + uniqueId.unpack();
}
}
std::string windowId = getWindowId();
string windowId = getWindowId();
return getUri() + "/" + m_remotePath + "/" + windowId + "/" + agentId + "/data.data";
}
void SerializeToLocalAndRemoteSyncBase::setRemoteSyncEnabled(bool enabled)
@@ -462,7 +463,7 @@ void SerializeToLocalAndRemoteSyncBase::setRemoteSyncEnabled(bool enabled)
m_remoteSyncEnabled = enabled;
}
void SerializeToLocalAndRemoteSyncBase::setInterval(std::chrono::seconds newInterval)
void SerializeToLocalAndRemoteSyncBase::setInterval(ch::seconds newInterval)
{
dbgDebug(D_WAAP_CONFIDENCE_CALCULATOR) << "setInterval: from " << m_interval.count() << " to " <<
newInterval.count() << " seconds. assetId='" << m_assetId << "', owner='" << m_owner << "'";
@@ -495,7 +496,7 @@ void SerializeToLocalAndRemoteSyncBase::setInterval(std::chrono::seconds newInte
size_t slicesCount = m_interval / assetSyncTimeSliceLength;
size_t sliceIndex = 0;
if (slicesCount != 0 && m_assetId != "") {
sliceIndex = std::hash<std::string>{}(m_assetId) % slicesCount;
sliceIndex = hash<string>{}(m_assetId) % slicesCount;
}
ch::seconds sliceOffset = assetSyncTimeSliceLength * sliceIndex;
@@ -572,7 +573,7 @@ bool SerializeToLocalAndRemoteSyncBase::localSyncAndProcess()
return true;
}
std::chrono::seconds SerializeToLocalAndRemoteSyncBase::getIntervalDuration() const
ch::seconds SerializeToLocalAndRemoteSyncBase::getIntervalDuration() const
{
return m_interval;
}
@@ -581,14 +582,14 @@ void SerializeToLocalAndRemoteSyncBase::updateStateFromRemoteService()
{
for (int i = 0; i < remoteSyncMaxPollingAttempts; i++)
{
m_pMainLoop->yield(std::chrono::seconds(60));
m_pMainLoop->yield(ch::seconds(60));
RemoteFilesList remoteFiles = getRemoteProcessedFilesList();
if (remoteFiles.getFilesMetadataList().empty())
{
dbgWarning(D_WAAP_CONFIDENCE_CALCULATOR) << "no files generated by the remote service were found";
continue;
}
std::string lastModified = remoteFiles.getFilesMetadataList().begin()->modified;
string lastModified = remoteFiles.getFilesMetadataList().begin()->modified;
if (lastModified != m_lastProcessedModified)
{
m_lastProcessedModified = lastModified;
@@ -618,7 +619,7 @@ void SerializeToLocalAndRemoteSyncBase::syncWorker()
<< "Did not synchronize the data. Remote URL: "
<< m_remotePath
<< " is enabled: "
<< std::to_string(m_remoteSyncEnabled);
<< to_string(m_remoteSyncEnabled);
processData();
saveData();
return;
@@ -659,7 +660,7 @@ void SerializeToLocalAndRemoteSyncBase::syncWorker()
Flags<MessageConnConfig> conn_flags;
conn_flags.setFlag(MessageConnConfig::EXTERNAL);
std::string tenant_header = "X-Tenant-Id: " + agentDetails->getTenantId();
string tenant_header = "X-Tenant-Id: " + agentDetails->getTenantId();
bool ok = messaging->sendNoReplyObject(syncObj,
I_Messaging::Method::POST,
getLearningHost(),
@@ -735,7 +736,7 @@ RemoteFilesList SerializeToLocalAndRemoteSyncBase::getProcessedFilesList()
if (!processedFilesList.getFilesList().empty())
{
const std::vector<FileMetaData>& filesMD = processedFilesList.getFilesMetadataList();
const vector<FileMetaData>& filesMD = processedFilesList.getFilesMetadataList();
if (filesMD.size() > 1) {
dbgWarning(D_WAAP_CONFIDENCE_CALCULATOR) << "got more than 1 expected processed file";
}
@@ -764,7 +765,7 @@ RemoteFilesList SerializeToLocalAndRemoteSyncBase::getProcessedFilesList()
return processedFilesList;
}
// backward compatibility - try to get backup file with the buggy prefix tenantID/assetID/instanceID/
std::string bcRemotePath = m_remotePath;
string bcRemotePath = m_remotePath;
size_t pos = bcRemotePath.find('/');
pos = bcRemotePath.find('/', pos + 1);
if (!Singleton::exists<I_InstanceAwareness>())
@@ -774,14 +775,14 @@ RemoteFilesList SerializeToLocalAndRemoteSyncBase::getProcessedFilesList()
return processedFilesList;
}
I_InstanceAwareness* instanceAwareness = Singleton::Consume<I_InstanceAwareness>::by<WaapComponent>();
Maybe<std::string> id = instanceAwareness->getUniqueID();
Maybe<string> id = instanceAwareness->getUniqueID();
if (!id.ok())
{
dbgDebug(D_WAAP_CONFIDENCE_CALCULATOR) << "failed to get instance id err: " << id.getErr() <<
". can't check backward compatibility";
return processedFilesList;
}
std::string idStr = id.unpack();
string idStr = id.unpack();
bcRemotePath.insert(pos + 1, idStr + "/");
dbgDebug(D_WAAP_CONFIDENCE_CALCULATOR) << "List of files is empty - trying to get the file from " <<
bcRemotePath;

View File

@@ -65,7 +65,8 @@ bool TypeIndicatorFilter::shouldFilterKeyword(const std::string &key, const std:
key.compare(key.size() - htmlParam.size(), htmlParam.size(), htmlParam) == 0);
for (auto keyType : keyTypes)
{
if (keyType == "free_text" && !isHtmlInput)
static const std::string free_text = "free_text";
if (!keyType.compare(0, free_text.size(), free_text) && !isHtmlInput)
{
return true;
}

View File

@@ -1993,7 +1993,7 @@ void WaapAssetState::filterKeywordsByParameters(
}
else
{
dbgTrace(D_WAAP_ASSET_STATE) << "No keywords need to be filter for this parameter";
dbgTrace(D_WAAP_ASSET_STATE) << "No keywords need to be filtered for this parameter";
}
}

View File

@@ -23,6 +23,15 @@ set<string> WaapConfigApplication::assets_ids{};
set<string> WaapConfigApplication::assets_ids_aggregation{};
bool WaapConfigApplication::getWaapSiteConfig(WaapConfigApplication& ngenSiteConfig) {
auto maybe_tenant_id = Singleton::Consume<I_Environment>::by<WaapConfigApplication>()->get<string>(
"ActiveTenantId"
);
auto maybe_profile_id = Singleton::Consume<I_Environment>::by<WaapConfigApplication>()->get<string>(
"ActiveProfileId"
);
string tenant_id = (maybe_tenant_id.ok() ? *maybe_tenant_id : "not found");
string profile_id = (maybe_profile_id.ok() ? *maybe_profile_id : "not found");
dbgTrace(D_WAAP) << "Tenant ID: " << tenant_id << ", Profile ID: " << profile_id;
auto &maybe_ngen_config = getConfiguration<WaapConfigApplication>(
"WAAP",
"WebApplicationSecurity"

View File

@@ -19,9 +19,13 @@
#include "WaapConfigBase.h"
#include "log_generator.h"
#include "i_environment.h"
#include "debug.h"
class WaapConfigApplication : public WaapConfigBase
class WaapConfigApplication
:
public WaapConfigBase,
Singleton::Consume<I_Environment>
{
public:
WaapConfigApplication();

View File

@@ -31,6 +31,7 @@ double Waap::Scanner::getScoreData(Waf2ScanResult& res, const std::string &poolN
std::string param_name = IndicatorsFiltersManager::generateKey(res.location, res.param_name, m_transaction);
dbgTrace(D_WAAP_SCANNER) << "filter processing for parameter: " << param_name;
m_transaction->getAssetState()->logIndicatorsInFilters(param_name, keywordsSet, m_transaction);
m_transaction->getAssetState()->filterKeywords(param_name, keywordsSet, res.filtered_keywords);
if (m_transaction->getSiteConfig() != nullptr)
{
@@ -41,6 +42,7 @@ double Waap::Scanner::getScoreData(Waf2ScanResult& res, const std::string &poolN
}
m_transaction->getAssetState()->filterKeywordsByParameters(res.param_name, keywordsSet);
// The keywords are only removed in production, they are still used while building scores
if (!m_transaction->get_ignoreScore()) {
m_transaction->getAssetState()->removeKeywords(keywordsSet);
@@ -66,6 +68,7 @@ double Waap::Scanner::getScoreData(Waf2ScanResult& res, const std::string &poolN
std::sort(res.keyword_matches.begin(), res.keyword_matches.end());
std::string keywords_string;
std::vector<std::string> strippedKeywords;
for (auto pKeyword = keywordsSet.begin(); pKeyword != keywordsSet.end(); ++pKeyword) {
// Add spaces between the items, but not before the first one
if (pKeyword != keywordsSet.begin()) {
@@ -75,15 +78,21 @@ double Waap::Scanner::getScoreData(Waf2ScanResult& res, const std::string &poolN
std::string k = *pKeyword;
stripSpaces(k);
keywords_string += k;
strippedKeywords.push_back(k);
}
std::vector<std::string> newKeywords;
for (auto pKeyword = keywordsSet.begin(); pKeyword != keywordsSet.end(); ++pKeyword) {
std::string k = *pKeyword;
stripSpaces(k);
// if keyword_string.count(key) < 2: new_keywords.append(key)
if (countSubstrings(keywords_string, k) < 2) {
newKeywords.push_back(k);
} else {
if ((std::count(strippedKeywords.begin(), strippedKeywords.end(), k) > 1) ) {
if ((std::count(newKeywords.begin(), newKeywords.end(), k) < 1)) {
newKeywords.push_back(k);
}
}
}
}
@@ -127,6 +136,10 @@ bool Waap::Scanner::suspiciousHit(Waf2ScanResult& res, DeepParser &dp,
res.location = location;
res.param_name = param_name; // remember the param name (analyzer needs it for reporting)
// call shouldIgnoreOverride prior to score calculation, so that matched override keywords will be filtered
// when an ignore override action is detected
bool ignoreOverride = m_transaction->shouldIgnoreOverride(res);
// Select scores pool by location
std::string poolName = Waap::Scores::getScorePoolNameByLocation(location);
@@ -145,7 +158,7 @@ bool Waap::Scanner::suspiciousHit(Waf2ScanResult& res, DeepParser &dp,
);
}
if (isKeyCspReport(key, res, dp) || m_transaction->shouldIgnoreOverride(res)) {
if (isKeyCspReport(key, res, dp) || ignoreOverride) {
dbgTrace(D_WAAP_SCANNER) << "Ignoring parameter key/value " << res.param_name <<
" due to ignore action in override";
m_bIgnoreOverride = true;

View File

@@ -67,8 +67,6 @@ using namespace ReportIS;
// Score threshold below which the match won't be considered
#define SCORE_THRESHOLD (1.4f)
static const ParameterBehavior action_ignore(BehaviorKey::ACTION, BehaviorValue::IGNORE);
void Waf2Transaction::learnScore(ScoreBuilderData& data, const std::string &poolName)
{
m_pWaapAssetState->scoreBuilder.analyzeFalseTruePositive(data, poolName, !m_ignoreScore);
@@ -1372,7 +1370,7 @@ Waf2Transaction::checkShouldInject()
std::string uri = m_uriPath;
std::string low_method = m_methodStr;
std::transform(low_method.begin(), low_method.end(), low_method.begin(), ::tolower);
auto csrfPolicy = m_siteConfig ? m_siteConfig->get_CsrfPolicy() : NULL;
bool csrf = false;
dbgTrace(D_WAAP) << "Waf2Transaction::checkShouldInject(): received the relevant Application configuration "
@@ -1384,7 +1382,7 @@ Waf2Transaction::checkShouldInject()
{
dbgTrace(D_WAAP) << "Waf2Transaction::checkShouldInject(): Should not inject CSRF scripts.";
}
if(csrf) {
dbgTrace(D_WAAP) << "Waf2Transaction::checkShouldInject(): Should inject CSRF script";
m_responseInjectReasons.setCsrf(true);
@@ -1554,6 +1552,8 @@ void Waf2Transaction::appendCommonLogFields(LogGen& waapLog,
const std::string& incidentType) const
{
auto env = Singleton::Consume<I_Environment>::by<WaapComponent>();
auto active_id = env->get<std::string>("ActiveTenantId");
if (active_id.ok()) waapLog.addToOrigin(LogField("tenantId", *active_id));
auto proxy_ip = env->get<std::string>(HttpTransactionData::proxy_ip_ctx);
if (proxy_ip.ok() && m_remote_addr != proxy_ip.unpack())
{
@@ -2155,7 +2155,7 @@ Waf2Transaction::shouldIgnoreOverride(const Waf2ScanResult &res) {
auto exceptions = getConfiguration<ParameterException>("rulebase", "exception");
if (!exceptions.ok()) return false;
dbgTrace(D_WAAP) << "matching exceptions";
dbgTrace(D_WAAP_OVERRIDE) << "matching exceptions";
std::unordered_map<std::string, std::set<std::string>> exceptions_dict;
@@ -2184,11 +2184,16 @@ Waf2Transaction::shouldIgnoreOverride(const Waf2ScanResult &res) {
exceptions_dict["url"].insert(getUriStr());
exceptions_dict["hostName"].insert(m_hostStr);
for (auto &keyword : res.keyword_matches) {
exceptions_dict["indicator"].insert(keyword);
}
// calling behavior and check if there is a behavior that match to this specific param name.
auto behaviors = exceptions.unpack().getBehavior(exceptions_dict);
auto behaviors = exceptions.unpack().getBehavior(exceptions_dict,
getAssetState()->m_filtersMngr->getMatchedOverrideKeywords());
for (auto const &behavior : behaviors) {
if (behavior == action_ignore) {
dbgTrace(D_WAAP) << "matched exceptions for " << res.param_name << " should ignore.";
dbgTrace(D_WAAP_OVERRIDE) << "matched exceptions for " << res.param_name << " should ignore.";
std::string overrideId = behavior.getId();
if (!overrideId.empty()) {
m_matchedOverrideIds.insert(overrideId);

View File

@@ -41,6 +41,7 @@ using namespace std;
USE_DEBUG_FLAG(D_WAAP);
USE_DEBUG_FLAG(D_WAAP_EVASIONS);
USE_DEBUG_FLAG(D_WAAP_BASE64);
USE_DEBUG_FLAG(D_WAAP_JSON);
#define MIN_HEX_LENGTH 6
#define charToDigit(c) (c - '0')
@@ -1144,6 +1145,8 @@ namespace Util {
#define B64_TRAILERCHAR '='
static bool err = false;
// based on malicious JSON "{1:\x00}"
static const int minimal_legal_json_size = 8;
static const SingleRegex invalid_hex_evasion_re(
"%([g-zG-Z][0-9a-zA-Z]|[0-9a-zA-Z][g-zG-Z])",
@@ -1161,11 +1164,15 @@ static const SingleRegex csp_report_policy_re(
"csp_report_policy"
);
static const SingleRegex base64_key_value_detector_re(
"^[^<>;&\\?|=\\s]+={1}\\s*.+",
"^[^<>{};,&\\?|=\\s]+={1}\\s*.+",
err,
"base64_key_value");
static const SingleRegex json_key_value_detector_re(
"^[^<>{};,&\\?|=\\s]+={.+:.+}\\z",
err,
"json_key_value");
static const SingleRegex base64_key_detector_re(
"^[^<>;&\\?|=\\s]+={1}",
"^[^<>{};,&\\?|=\\s]+={1}",
err,
"base64_key");
static const SingleRegex base64_prefix_detector_re(
@@ -1173,6 +1180,44 @@ static const SingleRegex base64_prefix_detector_re(
err,
"base64_prefix");
// looks for combination <param>={<some text>*:<some text>*}
//used to allow parsing param=JSON to reduce false positives
bool detectJSONasParameter(const string &string_buffer,
string &key,
string &value)
{
key.clear();
value.clear();
bool is_json_candidate_detected = json_key_value_detector_re.hasMatch(string_buffer);
if (is_json_candidate_detected) {
dbgTrace(D_WAAP_JSON) << "===JSONdetect===: json_key_value_detector_re test passed - looking for key";
string::const_iterator it = string_buffer.begin();
for (; it != string_buffer.end(); ++it) {
if (*it != '{') {
continue;
}
// candidate should have size 8 or more - minimum for JSON with attack
if ((string_buffer.end() - it) < minimal_legal_json_size) {
dbgTrace(D_WAAP_JSON)
<< "===JSONdetect===: candidate is shorter then the length"
"of the shortest known json attack which is: " << minimal_legal_json_size;
return false;
}
key = std::string(string_buffer.begin(), it-1);
value = std::string(it, string_buffer.end());
break;
}
}
dbgTrace(D_WAAP_JSON)
<< "===JSONdetect===: key = '"
<< key
<< "', value = '"
<< value <<"'";
return is_json_candidate_detected;
}
static void b64TestChunk(const string &s,
string::const_iterator chunkStart,
string::const_iterator chunkEnd,
@@ -2115,6 +2160,8 @@ string convertParamTypeToStr(ParamType type)
return "urls";
case FREE_TEXT_PARAM_TYPE:
return "free_text";
case FREE_TEXT_FRENCH_PARAM_TYPE:
return "free_text_french";
case PIPE_PARAM_TYPE:
return "pipes";
case LONG_RANDOM_TEXT_PARAM_TYPE:
@@ -2148,6 +2195,7 @@ ParamType convertTypeStrToEnum(const string& typeStr)
{"administration_config", ParamType::ADMINISTRATOR_CONFIG_PARAM_TYPE},
{"base64", ParamType::BASE64_PARAM_TYPE },
{"free_text", ParamType::FREE_TEXT_PARAM_TYPE},
{"free_text_french", ParamType::FREE_TEXT_FRENCH_PARAM_TYPE},
{"html_input", ParamType::HTML_PARAM_TYPE},
{"long_random_text", ParamType::LONG_RANDOM_TEXT_PARAM_TYPE},
{"pipes", ParamType::PIPE_PARAM_TYPE},

View File

@@ -858,6 +858,10 @@ namespace Util {
std::string::const_iterator e,
std::string &repl);
bool detectJSONasParameter(const std::string &s,
std::string &key,
std::string &value);
void b64Decode(
const std::string &s,
RegexSubCallback_f cb,