mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
Feb 15th 2023 update
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -82,6 +82,7 @@ add_library(waap_clib
|
||||
SyncLearningNotification.cc
|
||||
LogGenWrapper.cc
|
||||
WaapSampleValue.cc
|
||||
ParserGql.cc
|
||||
)
|
||||
|
||||
add_definitions("-Wno-unused-function")
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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
|
||||
|
134
components/security_apps/waap/waap_clib/ParserGql.cc
Normal file
134
components/security_apps/waap/waap_clib/ParserGql.cc
Normal 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());
|
||||
}
|
56
components/security_apps/waap/waap_clib/ParserGql.h
Normal file
56
components/security_apps/waap/waap_clib/ParserGql.h
Normal 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
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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},
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user