#include #include #include #include #include "fold/fuzz/dict_miner.hpp" #include "fold/fuzz/witness_recorder.hpp" using fold::fuzz::mineDictionary; using fold::fuzz::ParamWitness; using fold::fuzz::PathWitness; using fold::fuzz::PointerShape; using fold::fuzz::WitnessKind; namespace { ParamWitness intParam(std::initializer_list bytes) { ParamWitness p; p.kind = WitnessKind::Integer; p.bit_width = static_cast(bytes.size() * 8); for (auto b : bytes) p.bytes.push_back(static_cast(b)); return p; } PathWitness witnessWith(std::vector params) { PathWitness w; w.function = "f"; w.finding_id = "id:1"; w.params = std::move(params); return w; } } // namespace TEST_CASE("mineDictionary emits one entry per non-trivial witness value", "[fuzz][dict]") { auto w = witnessWith({intParam({0xAB, 0x00, 0x00, 0x00})}); const auto dict = mineDictionary({w}); REQUIRE(!dict.empty()); REQUIRE(dict.find("\"\\xab\\x00\\x00\\x00\"") != std::string::npos); } TEST_CASE("mineDictionary dedups identical byte sequences across witnesses", "[fuzz][dict]") { // 0xAB is non-printable so the encoded form uses `\xab` escapes, // which keeps the needle unambiguous. auto w1 = witnessWith({intParam({0xAB, 0x12, 0x00, 0x00})}); auto w2 = witnessWith({intParam({0xAB, 0x12, 0x00, 0x00})}); const auto dict = mineDictionary({w1, w2}); // Count occurrences of the encoded form. Two witnesses with the // same value should still produce only one line. std::size_t count = 0; std::string needle = "\"\\xab\\x12\\x00\\x00\""; for (std::size_t pos = 0; (pos = dict.find(needle, pos)) != std::string::npos; pos += needle.size()) ++count; REQUIRE(count == 1); } TEST_CASE("mineDictionary filters trivial all-zero values", "[fuzz][dict]") { auto w = witnessWith({intParam({0, 0, 0, 0})}); REQUIRE(mineDictionary({w}).empty()); } TEST_CASE("mineDictionary filters trivial all-FF values", "[fuzz][dict]") { auto w = witnessWith({intParam({0xFF, 0xFF, 0xFF, 0xFF})}); REQUIRE(mineDictionary({w}).empty()); } TEST_CASE("mineDictionary filters single-byte 0x01", "[fuzz][dict]") { auto w = witnessWith({intParam({0x01})}); REQUIRE(mineDictionary({w}).empty()); } TEST_CASE("mineDictionary emits ASCII bytes literally", "[fuzz][dict]") { // "GET " — 4 ASCII printable bytes. auto w = witnessWith({intParam({'G', 'E', 'T', ' '})}); const auto dict = mineDictionary({w}); REQUIRE(dict.find("\"GET \"") != std::string::npos); } TEST_CASE("mineDictionary escapes \" and \\ properly", "[fuzz][dict]") { // A "magic value" whose middle bytes are a quote and a backslash. auto w = witnessWith({intParam({'a', '"', '\\', 'z'})}); const auto dict = mineDictionary({w}); REQUIRE(dict.find("\"a\\\"\\\\z\"") != std::string::npos); } TEST_CASE("mineDictionary skips pointer params", "[fuzz][dict]") { ParamWitness p; p.kind = WitnessKind::Pointer; p.pointer.shape = PointerShape::NullableBuffer; p.pointer.buffer_bytes = 64; auto w = witnessWith({p}); REQUIRE(mineDictionary({w}).empty()); } TEST_CASE("mineDictionary header reports unique count and witness count", "[fuzz][dict]") { auto w1 = witnessWith({intParam({0xAA, 0xBB, 0xCC, 0xDD})}); auto w2 = witnessWith({intParam({0x11, 0x22, 0x33, 0x44})}); auto w3 = witnessWith({intParam({0xAA, 0xBB, 0xCC, 0xDD})}); // dup of w1 const auto dict = mineDictionary({w1, w2, w3}); REQUIRE(dict.find("2 unique value(s)") != std::string::npos); REQUIRE(dict.find("from 3 witness(es)") != std::string::npos); } TEST_CASE("mineDictionary returns empty when nothing remains after filtering", "[fuzz][dict]") { // All trivial values. auto w = witnessWith({intParam({0}), intParam({1}), intParam({0xFF, 0xFF})}); REQUIRE(mineDictionary({w}).empty()); }