From c0c2e33d24290a16d9df76191cf8fcfe9850ad0d Mon Sep 17 00:00:00 2001 From: bcapuano Date: Sun, 9 Jun 2024 19:56:50 -0400 Subject: [PATCH] Initial OSS skeleton Changed titlecase to lowercase for project name in build.sh Don't build examples or test for Crow fuzzing Signficiantly simplified corpus Added cifuzz.yml Clean up More fixes --- .github/workflows/cifuzz.yml | 39 +++++++++++ CMakeLists.txt | 6 ++ tests/fuzz/CMakeLists.txt | 29 +++++++++ tests/fuzz/b64_fuzzer.cpp | 27 ++++++++ tests/fuzz/build.sh | 8 +++ tests/fuzz/html_corpus/get.seed | 4 ++ tests/fuzz/request_fuzzer.cpp | 82 ++++++++++++++++++++++++ tests/fuzz/template_corpus/template.seed | 12 ++++ tests/fuzz/template_fuzzer.cpp | 33 ++++++++++ 9 files changed, 240 insertions(+) create mode 100644 .github/workflows/cifuzz.yml create mode 100644 tests/fuzz/CMakeLists.txt create mode 100644 tests/fuzz/b64_fuzzer.cpp create mode 100644 tests/fuzz/build.sh create mode 100644 tests/fuzz/html_corpus/get.seed create mode 100644 tests/fuzz/request_fuzzer.cpp create mode 100644 tests/fuzz/template_corpus/template.seed create mode 100644 tests/fuzz/template_fuzzer.cpp diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 000000000..eb7a58f6f --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,39 @@ +name: CIFuzz +on: + push: + branches: + - master + pull_request: +permissions: {} +jobs: + Fuzzing: + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'crow' + language: c++ + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'crow' + language: c++ + fuzz-seconds: 800 + output-sarif: true + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts + - name: Upload Sarif + if: always() && steps.build.outcome == 'success' + uses: github/codeql-action/upload-sarif@v2 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: cifuzz-sarif/results.sarif + checkout_path: cifuzz-sarif diff --git a/CMakeLists.txt b/CMakeLists.txt index f02d9c489..53e5ad232 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ find_package(Python3) ##################################### option(CROW_BUILD_EXAMPLES "Build the examples in the project" ${CROW_IS_MAIN_PROJECT}) option(CROW_BUILD_TESTS "Build the tests in the project" ${CROW_IS_MAIN_PROJECT}) +option(CROW_BUILD_FUZZER "Instrument and build Crow fuzzer" OFF) option(CROW_AMALGAMATE "Combine all headers into one" OFF) option(CROW_INSTALL "Add install step for Crow" ON ) option(CROW_USE_BOOST "Use Boost.Asio for Crow" OFF) @@ -134,6 +135,11 @@ if(CROW_BUILD_TESTS) endif() endif() +# Fuzzers +if (CROW_BUILD_FUZZER) + add_subdirectory(tests/fuzz) +endif() + ##################################### # Install Files ##################################### diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt new file mode 100644 index 000000000..53f2b690e --- /dev/null +++ b/tests/fuzz/CMakeLists.txt @@ -0,0 +1,29 @@ +# Utilized by OSSFuzz to build the harness(es) for continuous fuzz-testing +# OSSFuzz defines the following environment variables, that this target relies upon: +# CXX, CFLAGS, LIB_FUZZING_ENGINE, OUT +cmake_minimum_required(VERSION 3.14) + +function(define_fuzzer executable_name) + add_executable(${executable_name} ${executable_name}.cpp) + target_link_libraries(${executable_name} PRIVATE Crow $ENV{LIB_FUZZING_ENGINE}) + target_compile_features(${executable_name} PRIVATE cxx_std_17) + + if (DEFINED ENV{OUT}) + install(TARGETS ${executable_name} DESTINATION $ENV{OUT}) + else () + message(WARNING "Cannot install if $OUT is not defined!") + endif () +endfunction() + +add_definitions(-DNDEBUG) # Do not want assertions + +if (DEFINED ENV{CFLAGS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{CFLAGS}") +endif () +if (DEFINED ENV{CXXFLAGS}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{CXXFLAGS}") +endif () + +define_fuzzer(template_fuzzer) +define_fuzzer(request_fuzzer) +define_fuzzer(b64_fuzzer) \ No newline at end of file diff --git a/tests/fuzz/b64_fuzzer.cpp b/tests/fuzz/b64_fuzzer.cpp new file mode 100644 index 000000000..f3ed62861 --- /dev/null +++ b/tests/fuzz/b64_fuzzer.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include "crow.h" + +class FuzzException : public std::exception +{ + virtual const char* what() const throw() + { + return "Base64 decoding error!"; + } +}; + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_t size) +{ + FuzzedDataProvider fdp{data, size}; + + std::string plaintext = fdp.ConsumeRandomLengthString(); + std::string encoded = crow::utility::base64encode(plaintext, plaintext.size()); + std::string decoded = crow::utility::base64decode(encoded, encoded.size()); + + if (plaintext != decoded) + { + throw FuzzException(); + } + return 0; +} \ No newline at end of file diff --git a/tests/fuzz/build.sh b/tests/fuzz/build.sh new file mode 100644 index 000000000..f9b047c6f --- /dev/null +++ b/tests/fuzz/build.sh @@ -0,0 +1,8 @@ +cd $SRC/crow +mkdir -p build +cmake -S . -B build -DCROW_BUILD_FUZZER=ON -DCROW_BUILD_EXAMPLES=OFF -DCROW_BUILD_TESTS=OFF && cmake --build build --target install + +# Build the corpora +cd tests/fuzz +zip -q $OUT/template_fuzzer_seed_corpus.zip template_corpus/* +zip -q $OUT/request_fuzzer_seed_corpus.zip html_corpus/* diff --git a/tests/fuzz/html_corpus/get.seed b/tests/fuzz/html_corpus/get.seed new file mode 100644 index 000000000..8d16c1ad4 --- /dev/null +++ b/tests/fuzz/html_corpus/get.seed @@ -0,0 +1,4 @@ +GET /test/?string_param=exampleString&integer_param=12345 HTTP/1.1 +Host: example.com +User-Agent: Fuzzer/1.0 +Accept: */* diff --git a/tests/fuzz/request_fuzzer.cpp b/tests/fuzz/request_fuzzer.cpp new file mode 100644 index 000000000..de47a5cb0 --- /dev/null +++ b/tests/fuzz/request_fuzzer.cpp @@ -0,0 +1,82 @@ +#include +#include + +#include + +#include "crow.h" + +constexpr const int SERVER_PORT = 18080; + +/** + * To be run in a separate thread, + * + * Starts up the web-server, configures a dummy route, and serves incoming requests + */ +static void start_web_server() +{ + crow::SimpleApp app{}; + + CROW_ROUTE(app, "/test//") + ([](const crow::request& req, std::string a, int b) + { + std::string resp{}; + for (const auto & param : req.get_body_params().keys()) + { + resp += param; + } + return resp; + }); + + crow::logger::setLogLevel(crow::LogLevel::CRITICAL); + app.bindaddr("127.0.0.1") + .port(SERVER_PORT) + .multithreaded() + .run(); +} + +/** + * Called once at fuzzer start-up, initializes the web-server + * @return True, + */ +static bool initialize_web_server() +{ + static std::thread ws_th{start_web_server}; + return true; +} + +static int send_request_to_web_server(FuzzedDataProvider &fdp) +{ + int rc = -1; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + auto http_msg = fdp.ConsumeRemainingBytesAsString(); + sockaddr_in ws_addr{.sin_family=AF_INET, .sin_port= htons(SERVER_PORT)}; + ws_addr.sin_addr.s_addr = INADDR_ANY; + + if (-1 == sock) + { + goto done; + } + + if (-1 == connect(sock, (struct sockaddr*) &ws_addr, sizeof(ws_addr))) + { + close(sock); + goto done; + } + http_msg.insert(0, "GET / HTTP/1.1\r\n"); + + send(sock, http_msg.c_str(), http_msg.length(), 0); + close(sock); + rc = 0; +done: + return rc; +} + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_t size) +{ + static bool initialized = initialize_web_server(); + FuzzedDataProvider fdp{data, size}; + + send_request_to_web_server(fdp); + return 0; +} diff --git a/tests/fuzz/template_corpus/template.seed b/tests/fuzz/template_corpus/template.seed new file mode 100644 index 000000000..8dbb2a3c6 --- /dev/null +++ b/tests/fuzz/template_corpus/template.seed @@ -0,0 +1,12 @@ + + + + + User Profile + + +

{{name}}'s Profile

+

{{bio}}

+

Favorite Programming Language: {{favoriteLanguage}}

+ + diff --git a/tests/fuzz/template_fuzzer.cpp b/tests/fuzz/template_fuzzer.cpp new file mode 100644 index 000000000..62368c001 --- /dev/null +++ b/tests/fuzz/template_fuzzer.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include "crow.h" + +static crow::mustache::context build_context_object(FuzzedDataProvider &fdp) +{ + crow::mustache::context ctx{}; + + for (auto i = 0; i < fdp.ConsumeIntegralInRange(0, 10); ++i) + { + ctx[fdp.ConsumeRandomLengthString()] = fdp.ConsumeRandomLengthString(); + } + + return ctx; +} + +extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t* data, const std::size_t size) +{ + FuzzedDataProvider fdp{data, size}; + try + { + auto page = crow::mustache::compile(fdp.ConsumeRandomLengthString()); + auto ctx = build_context_object(fdp); + page.render_string(ctx); + } + catch (const crow::mustache::invalid_template_exception& e) + { + return -1; + } + + return 0; +}