#include #include #include #include "http_api.h" #include "sync_service.h" typedef struct { uint32_t value; } test_random_t; static uint32_t next_random(void *context) { test_random_t *random = context; random->value = random->value * 1664525U + 1013904223U; return random->value; } static application_t new_application(test_random_t *random) { application_t application; application_init(&application, (random_source_t){.next_u32 = next_random, .context = random}); return application; } static void test_http_malformed_inputs_do_not_mutate(void) { test_random_t random = {.value = 7U}; application_t application = new_application(&random); http_api_t api; http_api_init(&api, &application); const char *const corpus[] = {"", "{", "[]", "{\"token\":", "{\"token\":null}", "{\"name\":\"A\"}", "{\"token\":\"00000000000000000000000000000000\",\"gameId\":-1}", "{\"type\":\"shot\"}", "{\"name\":\"A\",\"requestedRole\":\"player1\",\"extra\":1}"}; const http_api_route_t routes[] = {HTTP_API_ROUTE_JOIN, HTTP_API_ROUTE_RESUME, HTTP_API_ROUTE_CONFIG, HTTP_API_ROUTE_START, HTTP_API_ROUTE_SHOT, HTTP_API_ROUTE_REMATCH, HTTP_API_ROUTE_ABORT}; for (size_t index = 0; index < 400U; ++index) { const char *body = corpus[index % (sizeof(corpus) / sizeof(corpus[0]))]; const game_lifecycle_t before = application.lifecycle; http_api_response_t response; const http_api_request_t request = {.method = HTTP_API_POST, .route = routes[index % (sizeof(routes) / sizeof(routes[0]))], .content_type_json = true, .body = body, .body_length = strlen(body)}; assert(http_api_handle(&api, &request, &response)); assert(response.status >= 400U && response.status < 600U && response.body_length < sizeof(response.body)); assert(memcmp(&before, &application.lifecycle, sizeof(before)) == 0); } char oversized[kRequestBodyCapacity + 2U]; memset(oversized, 'x', sizeof(oversized) - 1U); oversized[sizeof(oversized) - 1U] = '\0'; http_api_response_t response; const http_api_request_t request = {.method = HTTP_API_POST, .route = HTTP_API_ROUTE_JOIN, .content_type_json = true, .body = oversized, .body_length = strlen(oversized)}; assert(http_api_handle(&api, &request, &response)); assert(response.status == 413U); } static void test_websocket_fuzz_and_connection_churn(void) { test_random_t random = {.value = 19U}; application_t application = new_application(&random); sync_service_t service; sync_service_init(&service, &application); for (uint16_t attempt = 0; attempt < 600U; ++attempt) { char frame[kWebSocketFrameCapacity + 1U]; const size_t length = (size_t)(next_random(&random) % (kWebSocketFrameCapacity + 1U)); for (size_t index = 0; index < length; ++index) frame[index] = (char)(next_random(&random) & 0x7fU); frame[length] = '\0'; const game_lifecycle_t before = application.lifecycle; char output[kStateMessageCapacity] = {0}; size_t output_length = 0U; bool changed = true; bool close = false; assert(sync_service_open(&service, 1, attempt)); assert(sync_service_receive(&service, 1, frame, length, attempt, output, &output_length, &changed, &close)); assert(!changed && output_length < sizeof(output)); assert(memcmp(&before, &application.lifecycle, sizeof(before)) == 0); sync_service_close(&service, 1); } char too_large[kWebSocketFrameCapacity + 1U] = {0}; char output[kStateMessageCapacity] = {0}; size_t output_length = 0U; bool changed = false; bool close = false; assert(sync_service_open(&service, 1, 0U)); assert(sync_service_receive(&service, 1, too_large, sizeof(too_large), 0U, output, &output_length, &changed, &close)); assert(!changed && strstr(output, "MALFORMED_JSON") != NULL); sync_service_close(&service, 1); } int main(void) { test_http_malformed_inputs_do_not_mutate(); test_websocket_fuzz_and_connection_churn(); puts("robustness tests passed"); return 0; }