Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[fuzz] Create ext authz http fuzzer with dynamic metadata #15520

Merged
merged 10 commits into from
Dec 7, 2021
Next Next commit
Create ext authz http fuzzer with dynamic metadata
Signed-off-by: Asra Ali <asraa@google.com>
  • Loading branch information
asraa committed Mar 16, 2021
commit 11e9d6396e0ab14b7b9a20f3a338ba989b4cf01b
2 changes: 1 addition & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ build:plain-fuzzer --copt=-fsanitize=fuzzer-no-link
build:plain-fuzzer --linkopt=-fsanitize=fuzzer-no-link

build:asan-fuzzer --config=plain-fuzzer
build:asan-fuzzer --config=asan
build:asan-fuzzer --config=clang-asan
asraa marked this conversation as resolved.
Show resolved Hide resolved
build:asan-fuzzer --copt=-fno-omit-frame-pointer
# Remove UBSAN halt_on_error to avoid crashing on protobuf errors.
build:asan-fuzzer --test_env=UBSAN_OPTIONS=print_stacktrace=1
Expand Down
30 changes: 30 additions & 0 deletions test/extensions/filters/http/ext_authz/BUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_fuzz_test",
"envoy_package",
"envoy_proto_library",
)
load(
"//test/extensions:extensions_build_system.bzl",
Expand Down Expand Up @@ -67,3 +69,31 @@ envoy_extension_cc_test(
"@envoy_api//envoy/service/auth/v3:pkg_cc_proto",
],
)

envoy_proto_library(
name = "ext_authz_fuzz_proto",
srcs = ["ext_authz_fuzz.proto"],
deps = [
"//test/fuzz:common_proto",
"@envoy_api//envoy/config/core/v3:pkg",
"@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg",
],
)

envoy_cc_fuzz_test(
name = "ext_authz_fuzz_test",
srcs = ["ext_authz_fuzz_test.cc"],
corpus = "ext_authz_corpus",
deps = [
":ext_authz_fuzz_proto_cc_proto",
"//source/common/http:context_lib",
"//source/common/network:address_lib",
"//source/extensions/filters/http/ext_authz",
"//test/extensions/filters/common/ext_authz:ext_authz_mocks",
"//test/extensions/filters/http/common/fuzz:http_filter_fuzzer_lib",
"//test/mocks/http:http_mocks",
"//test/mocks/network:network_mocks",
"//test/mocks/runtime:runtime_mocks",
"@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto",
],
)
11 changes: 11 additions & 0 deletions test/extensions/filters/http/ext_authz/ext_authz_corpus/example

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions test/extensions/filters/http/ext_authz/ext_authz_fuzz.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";
package envoy.extensions.filters.http.ext_authz;

import "envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto";
import "test/fuzz/common.proto";
import "envoy/config/core/v3/base.proto";
import "google/protobuf/empty.proto";
import "validate/validate.proto";

message Result {
asraa marked this conversation as resolved.
Show resolved Hide resolved
oneof result_selector {
option (validate.required) = true;
// Authorization check status
asraa marked this conversation as resolved.
Show resolved Hide resolved
google.protobuf.Empty error = 1;
google.protobuf.Empty denied = 2;
google.protobuf.Empty ok = 3;
}
}

// We only fuzz a single request per iteration.
message ExtAuthzTestCase {
envoy.extensions.filters.http.ext_authz.v3.ExtAuthz config = 1
[(validate.rules).message = {required: true}];
// Request data.
test.fuzz.HttpData data = 2 [(validate.rules).message = {required: true}];
asraa marked this conversation as resolved.
Show resolved Hide resolved
// Set default auth check result.
Result result = 3 [(validate.rules).message = {required: true}];
// Filter metadata.
envoy.config.core.v3.Metadata filter_metadata = 4;
// TODO: add response data and check against check result headers to add.
asraa marked this conversation as resolved.
Show resolved Hide resolved
}
asraa marked this conversation as resolved.
Show resolved Hide resolved
123 changes: 123 additions & 0 deletions test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "envoy/extensions/filters/http/ext_authz/v3/ext_authz.pb.validate.h"

#include "common/http/context_impl.h"
#include "common/network/address_impl.h"

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

#include "test/extensions/filters/common/ext_authz/mocks.h"
#include "test/extensions/filters/http/common/fuzz/http_filter_fuzzer.h"
#include "test/extensions/filters/http/ext_authz/ext_authz_fuzz.pb.validate.h"
#include "test/fuzz/fuzz_runner.h"
#include "test/mocks/http/mocks.h"
#include "test/mocks/network/mocks.h"
#include "test/mocks/runtime/mocks.h"

#include "gmock/gmock.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace ExtAuthz {
namespace {

Filters::Common::ExtAuthz::ResponsePtr
makeAuthzResponse(Filters::Common::ExtAuthz::CheckStatus status) {
asraa marked this conversation as resolved.
Show resolved Hide resolved
Filters::Common::ExtAuthz::ResponsePtr response =
std::make_unique<Filters::Common::ExtAuthz::Response>();
response->status = status;
// TODO: add headers to remove, append, set, body, status_code.
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this TODO mean?

return response;
}

Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus(
envoy::extensions::filters::http::ext_authz::Result::ResultSelectorCase result_case) {
Filters::Common::ExtAuthz::CheckStatus check_status;
switch (result_case) {
case envoy::extensions::filters::http::ext_authz::Result::kOk: {
check_status = Filters::Common::ExtAuthz::CheckStatus::OK;
break;
}
case envoy::extensions::filters::http::ext_authz::Result::kError: {
check_status = Filters::Common::ExtAuthz::CheckStatus::Error;
break;
}
case envoy::extensions::filters::http::ext_authz::Result::kDenied: {
check_status = Filters::Common::ExtAuthz::CheckStatus::Denied;
break;
}
default: {
// Unhandled status.
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
}
}
return check_status;
}

class FuzzerMocks {
public:
FuzzerMocks() : addr_(std::make_shared<Network::Address::PipeInstance>("/test/test.sock")) {

ON_CALL(decoder_callbacks_, connection()).WillByDefault(Return(&connection_));
connection_.stream_info_.downstream_address_provider_->setRemoteAddress(addr_);
connection_.stream_info_.downstream_address_provider_->setLocalAddress(addr_);
}

NiceMock<Runtime::MockLoader> runtime_;
NiceMock<Http::MockStreamDecoderFilterCallbacks> decoder_callbacks_;
NiceMock<Http::MockStreamEncoderFilterCallbacks> encoder_callbacks_;
Network::Address::InstanceConstSharedPtr addr_;
NiceMock<Envoy::Network::MockConnection> connection_;
};

DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase& input) {
try {
TestUtility::validate(input);
} catch (const EnvoyException& e) {
ENVOY_LOG_MISC(debug, "EnvoyException during validation: {}", e.what());
return;
}

static FuzzerMocks mocks;
asraa marked this conversation as resolved.
Show resolved Hide resolved
NiceMock<Stats::MockIsolatedStatsStore> stats_store;
Http::ContextImpl http_context(stats_store.symbolTable());
Filters::Common::ExtAuthz::MockClient* client = new Filters::Common::ExtAuthz::MockClient();
asraa marked this conversation as resolved.
Show resolved Hide resolved

// Prepare filter.
envoy::extensions::filters::http::ext_authz::v3::ExtAuthz proto_config = input.config();
asraa marked this conversation as resolved.
Show resolved Hide resolved
FilterConfigSharedPtr config = std::make_shared<FilterConfig>(
asraa marked this conversation as resolved.
Show resolved Hide resolved
proto_config, stats_store, mocks.runtime_, http_context, "ext_authz_prefix");
std::unique_ptr<Filter> filter =
std::make_unique<Filter>(config, Filters::Common::ExtAuthz::ClientPtr{client});
filter->setDecoderFilterCallbacks(mocks.decoder_callbacks_);
filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_);

// Set metadata context.
envoy::config::core::v3::Metadata metadata = input.filter_metadata();

ON_CALL(mocks.decoder_callbacks_.stream_info_, dynamicMetadata())
.WillByDefault(testing::ReturnRef(metadata));
// Set check result default action.
envoy::service::auth::v3::CheckRequest check_request;
ON_CALL(*client, check(_, _, _, _))
.WillByDefault(Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks& callbacks,
const envoy::service::auth::v3::CheckRequest& check_param,
Tracing::Span&, const StreamInfo::StreamInfo&) -> void {
check_request = check_param;
callbacks.onComplete(
makeAuthzResponse(resultCaseToCheckStatus(input.result().result_selector_case())));
}));

// TODO: Add response headers.
Envoy::Extensions::HttpFilters::HttpFilterFuzzer fuzzer;
fuzzer.runData(static_cast<Envoy::Http::StreamDecoderFilter*>(filter.get()), input.data());
// TODO: Query the request header map in HttpFilterFuzzer to test headers_to_(add/remove/append).
// TODO: Test check request attributes against config and filter metadata.
ENVOY_LOG_MISC(trace, "Check Request attributes {}", check_request.attributes().DebugString());
}

} // namespace
} // namespace ExtAuthz
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
3 changes: 3 additions & 0 deletions test/extensions/filters/http/ext_authz/ext_authz_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ template <class T> class HttpFilterTestBase : public T {
if (!yaml.empty()) {
TestUtility::loadFromYaml(yaml, proto_config);
}
ENVOY_LOG_MISC(info, "CONFIG \n {}", proto_config.DebugString());
config_.reset(
new FilterConfig(proto_config, stats_store_, runtime_, http_context_, "ext_authz_prefix"));
client_ = new Filters::Common::ExtAuthz::MockClient();
Expand Down Expand Up @@ -1126,6 +1127,8 @@ TEST_F(HttpFilterTest, MetadataContext) {
Invoke([&](Filters::Common::ExtAuthz::RequestCallbacks&,
const envoy::service::auth::v3::CheckRequest& check_param, Tracing::Span&,
const StreamInfo::StreamInfo&) -> void { check_request = check_param; }));
ENVOY_LOG_MISC(info, "METADATA \n{}", metadata.DebugString());
ENVOY_LOG_MISC(info, "HEADERS \n{}", request_headers_);

filter_->decodeHeaders(request_headers_, false);
Http::MetadataMap metadata_map{{"metadata", "metadata"}};
Expand Down