diff --git a/e2e_tests/testdata/BUILD b/e2e_tests/testdata/BUILD index f0c862c3..599e4660 100644 --- a/e2e_tests/testdata/BUILD +++ b/e2e_tests/testdata/BUILD @@ -47,6 +47,7 @@ cc_binary( "@com_google_fuzztest//fuzztest", "@com_google_fuzztest//fuzztest:fuzztest_gtest_main", "@com_google_fuzztest//fuzztest:test_protobuf_cc_proto", + "@com_googlesource_code_re2//:re2", ], ) diff --git a/e2e_tests/testdata/fuzz_tests_with_proto_inputs.cc b/e2e_tests/testdata/fuzz_tests_with_proto_inputs.cc index ce055354..6e75b215 100644 --- a/e2e_tests/testdata/fuzz_tests_with_proto_inputs.cc +++ b/e2e_tests/testdata/fuzz_tests_with_proto_inputs.cc @@ -15,10 +15,12 @@ #include #include #include +#include #include #include "./fuzztest/fuzztest.h" #include "./fuzztest/internal/test_protobuf.pb.h" +#include "re2/re2.h" namespace { @@ -27,6 +29,7 @@ using fuzztest::internal::FoodMachineProcedure; using fuzztest::internal::RoboCourier560Plan; using fuzztest::internal::SingleBytesField; using fuzztest::internal::TestProtobuf; +using fuzztest::internal::WebSearchResult; void BytesSummingToMagicValue(const SingleBytesField& input) { char sum = 0; @@ -434,4 +437,50 @@ void IntegerConditionsOnTestProtobufLevel02(const TestProtobuf& input) { } FUZZ_TEST(ProtoPuzzles, IntegerConditionsOnTestProtobufLevel02); +static bool IsValidUrl(const std::string& url) { + const std::string url_pattern = + R"(^(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]$)"; + return RE2::FullMatch(url, url_pattern); +} + +void ValidateUrls(const WebSearchResult& search_result) { + // Search result must contain at least 1 URL. + if (search_result.count() <= 0) { + return; + } + + // Search result must contain at most 10 URLs. + if (search_result.count() > 10) { + return; + } + + // Query should be at least 2 letters in size. + if (search_result.query().empty() || search_result.query().size() < 2) { + return; + } + + // Search result must contain exactly `count` URLs. + if (search_result.urls_size() != search_result.count()) { + return; + } + + int valid_urls = 0; + std::unordered_set unique_urls; + + for (const auto& url : search_result.urls()) { + // Check if url is a valid url. + if (IsValidUrl(url)) { + valid_urls++; + unique_urls.insert(url); + } + } + + // Ensure that all valid URLs are unique. + if (unique_urls.size() != search_result.urls_size()) { + return; + } + std::abort(); +} +FUZZ_TEST(ProtoPuzzles, ValidateUrls); + } // namespace diff --git a/fuzztest/internal/test_protobuf.proto b/fuzztest/internal/test_protobuf.proto index d36871bd..38698cab 100644 --- a/fuzztest/internal/test_protobuf.proto +++ b/fuzztest/internal/test_protobuf.proto @@ -205,3 +205,11 @@ message RoboCourier560Plan { // Any non-mail related actions to take at a address. map extra_actions = 2; } + +message WebSearchResult { + // Number of URLs in the search result. + optional int64 count = 1; + optional string query = 2; + // A list of size `count` containing valid URLs. + repeated string urls = 3; +}