diff --git a/test/extensions/filters/http/ext_authz/BUILD b/test/extensions/filters/http/ext_authz/BUILD index 3ba58efb6997..dc83e80e281d 100644 --- a/test/extensions/filters/http/ext_authz/BUILD +++ b/test/extensions/filters/http/ext_authz/BUILD @@ -108,6 +108,7 @@ envoy_cc_fuzz_test( "//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/grpc:grpc_mocks", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", diff --git a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc index 7745a37cb4d5..9b121b4e6626 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_fuzz_test.cc @@ -8,6 +8,7 @@ #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/grpc/mocks.h" #include "test/mocks/http/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/runtime/mocks.h" @@ -23,30 +24,29 @@ namespace HttpFilters { namespace ExtAuthz { namespace { -Filters::Common::ExtAuthz::ResponsePtr -makeAuthzResponse(const Filters::Common::ExtAuthz::CheckStatus status) { - Filters::Common::ExtAuthz::ResponsePtr response = - std::make_unique(); - response->status = status; +std::unique_ptr +makeGrpcCheckResponse(const Grpc::Status::WellKnownGrpcStatus status) { + auto response = std::make_unique(); + response->mutable_status()->set_code(status); // TODO: We only add the response status. // Add fuzzed inputs for headers_to_(set/append/add), body, status_code to the Response. return response; } -Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus( +Grpc::Status::WellKnownGrpcStatus resultCaseToGrpcStatus( const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::AuthResult result) { - Filters::Common::ExtAuthz::CheckStatus check_status; + Grpc::Status::WellKnownGrpcStatus check_status; switch (result) { case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::OK: { - check_status = Filters::Common::ExtAuthz::CheckStatus::OK; + check_status = Grpc::Status::WellKnownGrpcStatus::Ok; break; } case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::ERROR: { - check_status = Filters::Common::ExtAuthz::CheckStatus::Error; + check_status = Grpc::Status::WellKnownGrpcStatus::Internal; break; } case envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase::DENIED: { - check_status = Filters::Common::ExtAuthz::CheckStatus::Denied; + check_status = Grpc::Status::WellKnownGrpcStatus::PermissionDenied; break; } default: { @@ -57,32 +57,22 @@ Filters::Common::ExtAuthz::CheckStatus resultCaseToCheckStatus( return check_status; } -class FuzzerMocks { +class StatelessFuzzerMocks { public: - FuzzerMocks() : addr_(std::make_shared("/test/test.sock")) { - - ON_CALL(decoder_callbacks_, connection()) - .WillByDefault(Return(OptRef{connection_})); + StatelessFuzzerMocks() + : addr_(std::make_shared("/test/test.sock")) { connection_.stream_info_.downstream_connection_info_provider_->setRemoteAddress(addr_); connection_.stream_info_.downstream_connection_info_provider_->setLocalAddress(addr_); - - ON_CALL(decoder_callbacks_.stream_info_, dynamicMetadata()) - .WillByDefault( - Invoke([&]() -> envoy::config::core::v3::Metadata& { return filter_metadata_; })); - } - - void setFilterMetadata(const envoy::config::core::v3::Metadata& metadata) { - filter_metadata_.CopyFrom(metadata); } + // Only add mocks here that are stateless. I.e. if you need to call ON_CALL on a mock each fuzzer + // run, do not add the mock here, because it will leak memory. NiceMock factory_context_; - NiceMock decoder_callbacks_; NiceMock encoder_callbacks_; Network::Address::InstanceConstSharedPtr addr_; NiceMock connection_; - - // Returned by mock decoder_callbacks_.stream_info_.dynamicMetadata() - envoy::config::core::v3::Metadata filter_metadata_; + NiceMock async_request_; + Envoy::Tracing::MockSpan mock_span_; }; DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzTestCase& input) { @@ -93,7 +83,7 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzT return; } - static FuzzerMocks mocks; + static StatelessFuzzerMocks mocks; NiceMock stats_store; static ScopedInjectableLoader engine(std::make_unique()); @@ -109,32 +99,52 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::ext_authz::ExtAuthzT return; } - Filters::Common::ExtAuthz::MockClient* client = new Filters::Common::ExtAuthz::MockClient(); - std::unique_ptr filter = - std::make_unique(config, Filters::Common::ExtAuthz::ClientPtr{client}); - filter->setDecoderFilterCallbacks(mocks.decoder_callbacks_); - filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_); + auto internal_mock_client = std::make_shared>(); + auto grpc_client = new Filters::Common::ExtAuthz::GrpcClientImpl(internal_mock_client, + std::chrono::milliseconds(1000)); + auto filter = std::make_unique(config, Filters::Common::ExtAuthz::ClientPtr{grpc_client}); // Set metadata context. - mocks.setFilterMetadata(input.filter_metadata()); + NiceMock decoder_callbacks; + ON_CALL(decoder_callbacks, connection()) + .WillByDefault(Return(OptRef{mocks.connection_})); + envoy::config::core::v3::Metadata metadata = input.filter_metadata(); + ON_CALL(decoder_callbacks.stream_info_, dynamicMetadata()) + .WillByDefault(testing::ReturnRef(metadata)); + + filter->setDecoderFilterCallbacks(decoder_callbacks); + filter->setEncoderFilterCallbacks(mocks.encoder_callbacks_); // 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()))); - })); + ON_CALL(*internal_mock_client, sendRaw(_, _, _, _, _, _)) + .WillByDefault( + Invoke([&](absl::string_view, absl::string_view, Buffer::InstancePtr&& serialized_req, + Grpc::RawAsyncRequestCallbacks&, Tracing::Span&, + const Http::AsyncClient::RequestOptions&) -> Grpc::AsyncRequest* { + envoy::service::auth::v3::CheckRequest check_request; + EXPECT_TRUE(check_request.ParseFromString(serialized_req->toString())) + << "Could not parse serialized check request"; + + // 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()); + + const Grpc::Status::WellKnownGrpcStatus status = resultCaseToGrpcStatus(input.result()); + if (status == Grpc::Status::WellKnownGrpcStatus::Ok) { + grpc_client->onSuccess(makeGrpcCheckResponse(status), mocks.mock_span_); + } else { + grpc_client->onFailure(status, "Fuzz input status was not ok!", mocks.mock_span_); + } + return &mocks.async_request_; + })); // TODO: Add response headers. Envoy::Extensions::HttpFilters::HttpFilterFuzzer fuzzer; fuzzer.runData(static_cast(filter.get()), input.request_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