Repository: microsoft/PowerToys
File: src/modules/powerrename/PowerRename.FuzzingTest/PowerRename.FuzzingTest.cpp (C++)

Code before:
    size_t offset = 0;

    auto read_length = [&](size_t max_len) -> size_t {
        if (offset + 1 > size)
            return 0;
        uint8_t len = data[offset++];
        return (std::min)(static_cast<size_t>(len), max_len);
    };

    auto read_wstring = [&](size_t max_len) -> std::wstring {
        size_t len = read_length(max_len);
        if (offset + len > size)
            len = size - offset;
        std::string utf8(reinterpret_cast<const char*>(data + offset), len);
        offset += len;

        int wide_len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
        if (wide_len == 0)
            return L"";

        std::wstring wide(wide_len, L'\0');
        MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, &wide[0], wide_len);
        return wide;
    };

    std::wstring input = read_wstring(100);
    std::wstring find = read_wstring(50);
    std::wstring replace = read_wstring(100);

    CComPtr<IPowerRenameRegEx> renamer;
    CPowerRenameRegEx::s_CreateInstance(&renamer);

    renamer->PutFlags(UseRegularExpressions | CaseSensitive);

    renamer->PutSearchTerm(find.c_str());
    renamer->PutReplaceTerm(replace.c_str());

    BSTR result{};
    unsigned long index = 0;

    renamer->Replace(input.c_str(), &result, index);

    if (result)
        SysFreeString(result);

    return 0;
}


Review comment (suggestion):
Do you think we can always use the full "*data" for testing for each round?
e.g.
Iteration, input, find, replace are global variable.

switch (iteration%3)
{
    case 0: input = StringOfData
    case 1: find = StringOfData
    case 2: replace = StringOfData
}

I know no solution is perfect on spot the fuzzing testing with 3 parameters, but at least we use the whole test data each time.

Code after:

    size_t offset = 0;

    size_t input_len = size / 3;
    size_t find_len = size / 3;
    size_t replace_len = size - input_len - find_len;

    auto read_wstring = [&](size_t len) -> std::wstring {
        std::wstring result;
        if (offset + len > size)
            len = size - offset;

        result.assign(reinterpret_cast<const wchar_t*>(data + offset), len / sizeof(wchar_t));
        offset += len;
        return result;
    };

    std::wstring input = read_wstring(input_len);
    std::wstring find = read_wstring(find_len);
    std::wstring replace = read_wstring(replace_len);

    if (find.empty() || replace.empty())
        return 0;

    CComPtr<IPowerRenameRegEx> renamer;
    CPowerRenameRegEx::s_CreateInstance(&renamer);

    renamer->PutFlags(UseRegularExpressions | CaseSensitive);

    renamer->PutSearchTerm(find.c_str());
    renamer->PutReplaceTerm(replace.c_str());

    PWSTR result = nullptr;
    unsigned long index = 0;
    HRESULT hr = renamer->Replace(input.c_str(), &result, index);
    if (SUCCEEDED(hr) && result != nullptr)
    {
        CoTaskMemFree(result);
    }

    return 0;
}

#ifndef DISABLE_FOR_FUZZING

int main(int argc, char** argv)
{
    const char8_t raw[] = u8"test_string";

    std::vector<uint8_t> data(reinterpret_cast<const uint8_t*>(raw), reinterpret_cast<const uint8_t*>(raw) + sizeof(raw) - 1);