aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.cpp
blob: 7c16527c23ff94b16c0c02eba8ddbf96d6cf13dd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include "fuzz_json.h"
#include "util/generic/fwd.h"

#include <library/cpp/scheme/scheme.h>
#include <util/stream/null.h>

namespace {
    static constexpr size_t MAX_DEPTH = 4;
    static constexpr size_t MAX_PATH_LEN = 256;
    static constexpr size_t MAX_ITERATIONS = 4;

    void SplitOnDepth(const TStringBuf src, const size_t depth, const size_t maxPathLen,
        TStringBuf& left, TStringBuf& right)
    {
        size_t pos = 0;
        size_t prevPos = 0;
        for(size_t i = 0; i < depth; ++i) {
            if (pos > maxPathLen) {
                break;
            }
            prevPos = pos;
            pos = src.find_first_of(TStringBuf("/]"), pos + 1);
            if (pos == TStringBuf::npos) {
                break;
            }
        }
        if (pos == TStringBuf::npos && prevPos > 0) {
            pos = prevPos;
        }
        if (src.length() > maxPathLen)  {
            if (pos == TStringBuf::npos || pos > maxPathLen) {
                pos = maxPathLen;
            }
        }
        if (pos == TStringBuf::npos || pos == 0) {
            left = src;
            right = TStringBuf();
        } else {
            src.SplitAt(pos + 1, left, right);
        }
    }

    TString tmp;
    //Limit max array size in the path to 256
    TStringBuf ProcessPath(TStringBuf path) {
        size_t pos = 0;
        while(pos != TStringBuf::npos) {
            pos = path.find(']', pos + 1);
            if (pos == TStringBuf::npos) {
                continue;
            }
            size_t open = path.rfind('[', pos);
            if (open == TStringBuf::npos) {
                continue;
            }
            bool allDigit = true;
            for(size_t i = open + 1; i < pos; ++i) {
                if (path[i] < '0' || path[i] > '9') {
                    allDigit = false;
                    break;
                }
            }
            if (!allDigit) {
                continue;
            }
            if (pos - open > 4)  {
                TString str = TString::Join(path.Head(open + 1), "256", path.Tail(pos));
                tmp = std::move(str);
                path = tmp;
                pos = (open + 1) + 3;
                continue;
            }
        }
        return path;
    }
}

namespace NSc::NUt {


    void FuzzJson(TStringBuf wire) {
        if (wire.size() < 2) {
            return;
        }


        ProcessPath("[123][1234][12][2134][12312312][1][12]");
        ui8 len1 = wire[0];
        ui8 len2 = wire[1];
        wire.Skip(2);
        auto json1 = wire.NextTokAt(len1);
        auto json2 = wire.NextTokAt(len2);
        NSc::TValue val1 = NSc::TValue::FromJson(json1);
        NSc::TValue val2 = NSc::TValue::FromJson(json2);
        NSc::TValue val3;
        val3.MergeUpdate(val1);

        size_t i = 0;
        while (!wire.empty()) {
            TStringBuf path;
            SplitOnDepth(wire, MAX_DEPTH, MAX_PATH_LEN, path, wire);
            path = ProcessPath(path);
            if (auto* target = val3.TrySelectOrAdd(path)) {
                target->MergeUpdate(val2);
            }
            ++i;
            // Release memory since there are up to MAX_DICT_SIZE * MAX_DEPTH elements
            if (i > MAX_ITERATIONS) {
                Cnull << val3.ToJson();
                val3 = NSc::TValue();
            }
        }
        Cnull << val3.ToJson();
    }
}