Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
22 changes: 16 additions & 6 deletions test/extensions/filters/http/common/fuzz/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ licenses(["notice"]) # Apache 2
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_fuzz_test",
"envoy_cc_test_library",
"envoy_package",
"envoy_proto_library",
)
Expand All @@ -22,22 +23,31 @@ envoy_proto_library(
],
)

envoy_cc_test_library(
name = "uber_filter_lib",
hdrs = ["uber_filter.h"],
deps = [
":filter_fuzz_proto_cc_proto",
"//source/common/config:utility_lib",
"//source/common/protobuf:utility_lib",
"//test/fuzz:utility_lib",
"//test/mocks/buffer:buffer_mocks",
"//test/mocks/http:http_mocks",
"//test/mocks/server:server_mocks",
],
)

envoy_cc_fuzz_test(
name = "filter_fuzz_test",
srcs = ["filter_fuzz_test.cc"],
corpus = "filter_corpus",
# All Envoy extensions must be linked to the test in order for the fuzzer to pick
# these up via the NamedHttpFilterConfigFactory.
deps = [
":filter_fuzz_proto_cc_proto",
":uber_filter_lib",
"//source/common/config:utility_lib",
"//source/common/config:version_converter_lib",
"//source/common/protobuf:utility_lib",
"//test/config:utility_lib",
"//test/fuzz:utility_lib",
"//test/mocks/buffer:buffer_mocks",
"//test/mocks/http:http_mocks",
"//test/mocks/server:server_mocks",
"@envoy_api//envoy/service/auth/v3:pkg_cc_proto",
"@envoy_api//envoy/service/auth/v2alpha:pkg_cc_proto",
] + envoy_all_extensions(),
Expand Down
135 changes: 1 addition & 134 deletions test/extensions/filters/http/common/fuzz/filter_fuzz_test.cc
Original file line number Diff line number Diff line change
@@ -1,153 +1,20 @@
#include <chrono>
#include <memory>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think you can get rid of these two as well.


#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h"

#include "common/config/utility.h"
#include "common/config/version_converter.h"
#include "common/protobuf/utility.h"

#include "extensions/filters/http/well_known_names.h"

#include "test/config/utility.h"
#include "test/extensions/filters/http/common/fuzz/filter_fuzz.pb.validate.h"
#include "test/extensions/filters/http/common/fuzz/uber_filter.h"
#include "test/fuzz/fuzz_runner.h"
#include "test/fuzz/utility.h"
#include "test/mocks/buffer/mocks.h"
#include "test/mocks/http/mocks.h"
#include "test/mocks/server/mocks.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {

class UberFilterFuzzer {
public:
UberFilterFuzzer() {
// Need to set for both a decoder filter and an encoder/decoder filter.
ON_CALL(filter_callback_, addStreamDecoderFilter(_))
.WillByDefault(
Invoke([&](std::shared_ptr<Envoy::Http::StreamDecoderFilter> filter) -> void {
filter_ = filter;
filter_->setDecoderFilterCallbacks(callbacks_);
}));
ON_CALL(filter_callback_, addStreamFilter(_))
.WillByDefault(
Invoke([&](std::shared_ptr<Envoy::Http::StreamDecoderFilter> filter) -> void {
filter_ = filter;
filter_->setDecoderFilterCallbacks(callbacks_);
}));
// Ext-authz setup
prepareExtAuthz();
prepareCache();
prepareTap();
}

void prepareExtAuthz() {
// Preparing the expectations for the ext_authz filter.
addr_ = std::make_shared<Network::Address::Ipv4Instance>("1.2.3.4", 1111);
ON_CALL(connection_, remoteAddress()).WillByDefault(testing::ReturnRef(addr_));
ON_CALL(connection_, localAddress()).WillByDefault(testing::ReturnRef(addr_));
ON_CALL(callbacks_, connection()).WillByDefault(testing::Return(&connection_));
ON_CALL(callbacks_, activeSpan())
.WillByDefault(testing::ReturnRef(Tracing::NullSpan::instance()));
callbacks_.stream_info_.protocol_ = Envoy::Http::Protocol::Http2;
}

void prepareCache() {
// Prepare expectations for dynamic forward proxy.
ON_CALL(factory_context_.dispatcher_, createDnsResolver(_, _))
.WillByDefault(testing::Return(resolver_));
}

void prepareTap() {
ON_CALL(factory_context_.admin_, addHandler(_, _, _, _, _))
.WillByDefault(testing::Return(true));
ON_CALL(factory_context_.admin_, removeHandler(_)).WillByDefault(testing::Return(true));
}

// This executes the decode methods to be fuzzed.
void decode(Http::StreamDecoderFilter* filter, const test::fuzz::HttpData& data) {
bool end_stream = false;

auto headers = Fuzz::fromHeaders<Http::TestRequestHeaderMapImpl>(data.headers());
if (headers.Path() == nullptr) {
headers.setPath("/foo");
}
if (headers.Method() == nullptr) {
headers.setMethod("GET");
}
if (headers.Host() == nullptr) {
headers.setHost("foo.com");
}

if (data.data().size() == 0 && !data.has_trailers()) {
end_stream = true;
}
ENVOY_LOG_MISC(debug, "Decoding headers: {} ", data.headers().DebugString());
const auto& headersStatus = filter->decodeHeaders(headers, end_stream);
if (headersStatus != Http::FilterHeadersStatus::Continue ||
headersStatus != Http::FilterHeadersStatus::StopIteration) {
return;
}

for (int i = 0; i < data.data().size(); i++) {
if (i == data.data().size() - 1 && !data.has_trailers()) {
end_stream = true;
}
Buffer::OwnedImpl buffer(data.data().Get(i));
ENVOY_LOG_MISC(debug, "Decoding data: {} ", buffer.toString());
if (filter->decodeData(buffer, end_stream) != Http::FilterDataStatus::Continue) {
return;
}
}

if (data.has_trailers()) {
ENVOY_LOG_MISC(debug, "Decoding trailers: {} ", data.trailers().DebugString());
auto trailers = Fuzz::fromHeaders<Http::TestRequestTrailerMapImpl>(data.trailers());
filter->decodeTrailers(trailers);
}
}

// This creates the filter config and runs decode.
void fuzz(const envoy::extensions::filters::network::http_connection_manager::v3::HttpFilter&
proto_config,
const test::fuzz::HttpData& data) {
try {
// Try to create the filter. Exit early if the config is invalid or violates PGV constraints.
ENVOY_LOG_MISC(info, "filter name {}", proto_config.name());
auto& factory = Config::Utility::getAndCheckFactoryByName<
Server::Configuration::NamedHttpFilterConfigFactory>(proto_config.name());
ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
proto_config, factory_context_.messageValidationVisitor(), factory);
cb_ = factory.createFilterFactoryFromProto(*message, "stats", factory_context_);
cb_(filter_callback_);
} catch (const EnvoyException& e) {
ENVOY_LOG_MISC(debug, "Controlled exception {}", e.what());
return;
}

decode(filter_.get(), data);
reset();
}

void reset() {
if (filter_.get() != nullptr) {
filter_.get()->onDestroy();
}
filter_.reset();
}

NiceMock<Server::Configuration::MockFactoryContext> factory_context_;
NiceMock<Http::MockStreamDecoderFilterCallbacks> callbacks_;
NiceMock<Http::MockFilterChainFactoryCallbacks> filter_callback_;
std::shared_ptr<Network::MockDnsResolver> resolver_{std::make_shared<Network::MockDnsResolver>()};
std::shared_ptr<Http::StreamDecoderFilter> filter_;
Http::FilterFactoryCb cb_;
NiceMock<Envoy::Network::MockConnection> connection_;
Network::Address::InstanceConstSharedPtr addr_;
};

DEFINE_PROTO_FUZZER(const test::extensions::filters::http::FilterFuzzTestCase& input) {
static PostProcessorRegistration reg = {[](test::extensions::filters::http::FilterFuzzTestCase*
input,
Expand Down
150 changes: 150 additions & 0 deletions test/extensions/filters/http/common/fuzz/uber_filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include <chrono>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't think this is being used either

#include <memory>

#include "common/config/utility.h"
#include "common/config/version_converter.h"
#include "common/protobuf/utility.h"

#include "test/fuzz/utility.h"
#include "test/mocks/buffer/mocks.h"
#include "test/mocks/http/mocks.h"
#include "test/mocks/server/mocks.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {

class UberFilterFuzzer {
public:
UberFilterFuzzer() {
// Need to set for both a decoder filter and an encoder/decoder filter.
ON_CALL(filter_callback_, addStreamDecoderFilter(_))
.WillByDefault(
Invoke([&](std::shared_ptr<Envoy::Http::StreamDecoderFilter> filter) -> void {
filter_ = filter;
filter_->setDecoderFilterCallbacks(callbacks_);
}));
ON_CALL(filter_callback_, addStreamFilter(_))
.WillByDefault(
Invoke([&](std::shared_ptr<Envoy::Http::StreamDecoderFilter> filter) -> void {
filter_ = filter;
filter_->setDecoderFilterCallbacks(callbacks_);
}));
setExpectations();
}

void setExpectations() {
// Ext-authz setup
prepareExtAuthz();
prepareCache();
prepareTap();
}

void prepareExtAuthz() {
// Preparing the expectations for the ext_authz filter.
addr_ = std::make_shared<Network::Address::Ipv4Instance>("1.2.3.4", 1111);
ON_CALL(connection_, remoteAddress()).WillByDefault(testing::ReturnRef(addr_));
ON_CALL(connection_, localAddress()).WillByDefault(testing::ReturnRef(addr_));
ON_CALL(callbacks_, connection()).WillByDefault(testing::Return(&connection_));
ON_CALL(callbacks_, activeSpan())
.WillByDefault(testing::ReturnRef(Tracing::NullSpan::instance()));
callbacks_.stream_info_.protocol_ = Envoy::Http::Protocol::Http2;
}

void prepareCache() {
// Prepare expectations for dynamic forward proxy.
ON_CALL(factory_context_.dispatcher_, createDnsResolver(_, _))
.WillByDefault(testing::Return(resolver_));
}

void prepareTap() {
ON_CALL(factory_context_.admin_, addHandler(_, _, _, _, _))
.WillByDefault(testing::Return(true));
ON_CALL(factory_context_.admin_, removeHandler(_)).WillByDefault(testing::Return(true));
}

// This executes the decode methods to be fuzzed.
void decode(Http::StreamDecoderFilter* filter, const test::fuzz::HttpData& data) {
bool end_stream = false;

auto headers = Fuzz::fromHeaders<Http::TestRequestHeaderMapImpl>(data.headers());
if (headers.Path() == nullptr) {
headers.setPath("/foo");
}
if (headers.Method() == nullptr) {
headers.setMethod("GET");
}
if (headers.Host() == nullptr) {
headers.setHost("foo.com");
}

if (data.data().size() == 0 && !data.has_trailers()) {
end_stream = true;
}
ENVOY_LOG_MISC(debug, "Decoding headers: {} ", data.headers().DebugString());
const auto& headersStatus = filter->decodeHeaders(headers, end_stream);
if (headersStatus != Http::FilterHeadersStatus::Continue ||
headersStatus != Http::FilterHeadersStatus::StopIteration) {
return;
}

for (int i = 0; i < data.data().size(); i++) {
if (i == data.data().size() - 1 && !data.has_trailers()) {
end_stream = true;
}
Buffer::OwnedImpl buffer(data.data().Get(i));
ENVOY_LOG_MISC(debug, "Decoding data: {} ", buffer.toString());
if (filter->decodeData(buffer, end_stream) != Http::FilterDataStatus::Continue) {
return;
}
}

if (data.has_trailers()) {
ENVOY_LOG_MISC(debug, "Decoding trailers: {} ", data.trailers().DebugString());
auto trailers = Fuzz::fromHeaders<Http::TestRequestTrailerMapImpl>(data.trailers());
filter->decodeTrailers(trailers);
}
}

// This creates the filter config and runs decode.
void fuzz(const envoy::extensions::filters::network::http_connection_manager::v3::HttpFilter&
proto_config,
const test::fuzz::HttpData& data) {
try {
// Try to create the filter. Exit early if the config is invalid or violates PGV constraints.
ENVOY_LOG_MISC(info, "filter name {}", proto_config.name());
auto& factory = Config::Utility::getAndCheckFactoryByName<
Server::Configuration::NamedHttpFilterConfigFactory>(proto_config.name());
ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
proto_config, factory_context_.messageValidationVisitor(), factory);
cb_ = factory.createFilterFactoryFromProto(*message, "stats", factory_context_);
cb_(filter_callback_);
} catch (const EnvoyException& e) {
ENVOY_LOG_MISC(debug, "Controlled exception {}", e.what());
return;
}

decode(filter_.get(), data);
reset();
}

void reset() {
if (filter_.get() != nullptr) {
filter_.get()->onDestroy();
}
filter_.reset();
}

NiceMock<Server::Configuration::MockFactoryContext> factory_context_;
NiceMock<Http::MockStreamDecoderFilterCallbacks> callbacks_;
NiceMock<Http::MockFilterChainFactoryCallbacks> filter_callback_;
std::shared_ptr<Network::MockDnsResolver> resolver_{std::make_shared<Network::MockDnsResolver>()};
std::shared_ptr<Http::StreamDecoderFilter> filter_;
Http::FilterFactoryCb cb_;
NiceMock<Envoy::Network::MockConnection> connection_;
Network::Address::InstanceConstSharedPtr addr_;
};

} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy