diff --git a/CMakeLists.txt b/CMakeLists.txt index 847ce38..5f9dbbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project (Simple-Web-Server) option(USE_STANDALONE_ASIO "set ON to use standalone Asio instead of Boost.Asio" OFF) option(BUILD_TESTING "set ON to build library tests" OFF) +option(BUILD_FUZZING "set ON to build library fuzzers" OFF) option(USE_OPENSSL "set OFF to build without OpenSSL" ON) add_library(simple-web-server INTERFACE) @@ -79,7 +80,9 @@ if(CMAKE_SOURCE_DIR STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") install(FILES asio_compatibility.hpp server_http.hpp client_http.hpp server_https.hpp client_https.hpp crypto.hpp utility.hpp status_code.hpp mutex.hpp DESTINATION include/simple-web-server) endif() -if(BUILD_TESTING) - enable_testing() +if(BUILD_TESTING OR BUILD_FUZZING) + if(BUILD_TESTING) + enable_testing() + endif() add_subdirectory(tests) endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4491826..e4c0659 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,21 +4,57 @@ if(NOT MSVC) add_compile_options(-Wno-thread-safety) endif() - add_executable(io_test io_test.cpp) - target_link_libraries(io_test simple-web-server) - add_test(NAME io_test COMMAND io_test) - - add_executable(parse_test parse_test.cpp) - target_link_libraries(parse_test simple-web-server) - add_test(NAME parse_test COMMAND parse_test) + if(BUILD_TESTING) + add_executable(io_test io_test.cpp) + target_link_libraries(io_test simple-web-server) + add_test(NAME io_test COMMAND io_test) + + add_executable(parse_test parse_test.cpp) + target_link_libraries(parse_test simple-web-server) + add_test(NAME parse_test COMMAND parse_test) + endif() endif() -if(OPENSSL_FOUND) +if(OPENSSL_FOUND AND BUILD_TESTING) add_executable(crypto_test crypto_test.cpp) target_link_libraries(crypto_test simple-web-server) add_test(NAME crypto_test COMMAND crypto_test) endif() -add_executable(status_code_test status_code_test.cpp) -target_link_libraries(status_code_test simple-web-server) -add_test(NAME status_code_test COMMAND status_code_test) +if(BUILD_TESTING) + add_executable(status_code_test status_code_test.cpp) + target_link_libraries(status_code_test simple-web-server) + add_test(NAME status_code_test COMMAND status_code_test) +endif() + +if(BUILD_FUZZING) + add_executable(percent_decode fuzzers/percent_decode.cpp) + target_compile_options(percent_decode PRIVATE -fsanitize=address,fuzzer) + target_link_options(percent_decode PRIVATE -fsanitize=address,fuzzer) + target_link_libraries(percent_decode simple-web-server) + + add_executable(query_string_parse fuzzers/query_string_parse.cpp) + target_compile_options(query_string_parse PRIVATE -fsanitize=address,fuzzer) + target_link_options(query_string_parse PRIVATE -fsanitize=address,fuzzer) + target_link_libraries(query_string_parse simple-web-server) + + add_executable(http_header_parse fuzzers/http_header_parse.cpp) + target_compile_options(http_header_parse PRIVATE -fsanitize=address,fuzzer) + target_link_options(http_header_parse PRIVATE -fsanitize=address,fuzzer) + target_link_libraries(http_header_parse simple-web-server) + + add_executable(http_header_field_value_semicolon_separated_attributes_parse fuzzers/http_header_field_value_semicolon_separated_attributes_parse.cpp) + target_compile_options(http_header_field_value_semicolon_separated_attributes_parse PRIVATE -fsanitize=address,fuzzer) + target_link_options(http_header_field_value_semicolon_separated_attributes_parse PRIVATE -fsanitize=address,fuzzer) + target_link_libraries(http_header_field_value_semicolon_separated_attributes_parse simple-web-server) + + add_executable(request_message_parse fuzzers/request_message_parse.cpp) + target_compile_options(request_message_parse PRIVATE -fsanitize=address,fuzzer) + target_link_options(request_message_parse PRIVATE -fsanitize=address,fuzzer) + target_link_libraries(request_message_parse simple-web-server) + + add_executable(response_message_parse fuzzers/response_message_parse.cpp) + target_compile_options(response_message_parse PRIVATE -fsanitize=address,fuzzer) + target_link_options(response_message_parse PRIVATE -fsanitize=address,fuzzer) + target_link_libraries(response_message_parse simple-web-server) +endif() \ No newline at end of file diff --git a/tests/fuzzers/README.md b/tests/fuzzers/README.md new file mode 100644 index 0000000..0534d5a --- /dev/null +++ b/tests/fuzzers/README.md @@ -0,0 +1,6 @@ +Prior to running the fuzzers, build and prepare for instance as follows: +```sh +CXX=clang++ cmake -DBUILD_FUZZING=1 .. +make +export LSAN_OPTIONS=detect_leaks=0 +``` diff --git a/tests/fuzzers/http_header_field_value_semicolon_separated_attributes_parse.cpp b/tests/fuzzers/http_header_field_value_semicolon_separated_attributes_parse.cpp new file mode 100644 index 0000000..e45a2db --- /dev/null +++ b/tests/fuzzers/http_header_field_value_semicolon_separated_attributes_parse.cpp @@ -0,0 +1,6 @@ +#include "utility.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + SimpleWeb::HttpHeader::FieldValue::SemicolonSeparatedAttributes::parse(std::string(reinterpret_cast(data), size)); + return 0; +} diff --git a/tests/fuzzers/http_header_parse.cpp b/tests/fuzzers/http_header_parse.cpp new file mode 100644 index 0000000..e90c28f --- /dev/null +++ b/tests/fuzzers/http_header_parse.cpp @@ -0,0 +1,9 @@ +#include "utility.hpp" +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + std::stringstream ss; + ss << std::string(reinterpret_cast(data), size); + SimpleWeb::HttpHeader::parse(ss); + return 0; +} diff --git a/tests/fuzzers/percent_decode.cpp b/tests/fuzzers/percent_decode.cpp new file mode 100644 index 0000000..c108464 --- /dev/null +++ b/tests/fuzzers/percent_decode.cpp @@ -0,0 +1,6 @@ +#include "utility.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + SimpleWeb::Percent::decode(std::string(reinterpret_cast(data), size)); + return 0; +} diff --git a/tests/fuzzers/query_string_parse.cpp b/tests/fuzzers/query_string_parse.cpp new file mode 100644 index 0000000..76967ec --- /dev/null +++ b/tests/fuzzers/query_string_parse.cpp @@ -0,0 +1,6 @@ +#include "utility.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + SimpleWeb::QueryString::parse(std::string(reinterpret_cast(data), size)); + return 0; +} diff --git a/tests/fuzzers/request_message_parse.cpp b/tests/fuzzers/request_message_parse.cpp new file mode 100644 index 0000000..51dfb29 --- /dev/null +++ b/tests/fuzzers/request_message_parse.cpp @@ -0,0 +1,11 @@ +#include "utility.hpp" +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + std::stringstream ss; + ss << std::string(reinterpret_cast(data), size); + std::string method, path, query_string, version; + SimpleWeb::CaseInsensitiveMultimap header; + SimpleWeb::RequestMessage::parse(ss, method, path, query_string, version, header); + return 0; +} diff --git a/tests/fuzzers/response_message_parse.cpp b/tests/fuzzers/response_message_parse.cpp new file mode 100644 index 0000000..247b66d --- /dev/null +++ b/tests/fuzzers/response_message_parse.cpp @@ -0,0 +1,11 @@ +#include "utility.hpp" +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + std::stringstream ss; + ss << std::string(reinterpret_cast(data), size); + std::string version, status_code; + SimpleWeb::CaseInsensitiveMultimap header; + SimpleWeb::ResponseMessage::parse(ss, version, status_code, header); + return 0; +}