aboutsummaryrefslogtreecommitdiff
path: root/projects/ujson/hypothesis_structured_fuzzer.py
diff options
context:
space:
mode:
authorZac Hatfield-Dodds <zac.hatfield.dodds@gmail.com>2021-01-16 10:33:29 +1100
committerGitHub <noreply@github.com>2021-01-15 15:33:29 -0800
commit613d735dc704c8d292ae2b420b10346666a4c834 (patch)
treee0f30eaa7a1349145179e572ac75ec7d52bf75da /projects/ujson/hypothesis_structured_fuzzer.py
parent69f39bc373c3869b17eec99148890b67b9badca6 (diff)
downloadoss-fuzz-613d735dc704c8d292ae2b420b10346666a4c834.tar.gz
Show how to use Hypothesis to fuzz Python code (#4975)
* Show how to use PBT Hypothesis makes fuzzing complex Python code fun, easy, and a lot more rewarding than constructing all your objects by hand. * Review updates
Diffstat (limited to 'projects/ujson/hypothesis_structured_fuzzer.py')
-rw-r--r--projects/ujson/hypothesis_structured_fuzzer.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/projects/ujson/hypothesis_structured_fuzzer.py b/projects/ujson/hypothesis_structured_fuzzer.py
new file mode 100644
index 000000000..c07a2cf5f
--- /dev/null
+++ b/projects/ujson/hypothesis_structured_fuzzer.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python3
+
+# Copyright 2021 Zac Hatfield-Dodds
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""This fuzzer is an example harness using Hypothesis for structured inputs.
+
+It would be possible, though more difficult, to write this test in terms
+of Atheris' `FuzzedDataProvider` instead of Hypothesis strategies.
+
+As well as defining structured inputs however, the call to
+`test_ujson_roundtrip()` will replay, deduplicate, and minimize any known
+failing examples from previous runs - which is great when debugging.
+Hypothesis uses a separate cache to Atheris/LibFuzzer seeds, so this is
+strictly complementary to your traditional fuzzing workflow.
+
+For more details on Hypothesis, see:
+https://hypothesis.readthedocs.io/en/latest/data.html
+https://hypothesis.readthedocs.io/en/latest/details.html#use-with-external-fuzzers
+"""
+
+import sys
+import atheris
+import ujson
+from hypothesis import given, strategies as st
+
+# We could define all these inline within the call to @given(),
+# but it's a bit easier to read if we name them here instead.
+JSON_ATOMS = st.one_of(
+ st.none(),
+ st.booleans(),
+ st.integers(min_value=-(2 ** 63), max_value=2 ** 63 - 1),
+ st.floats(allow_nan=False, allow_infinity=False),
+ st.text(),
+)
+JSON_OBJECTS = st.recursive(
+ base=JSON_ATOMS,
+ extend=lambda inner: st.lists(inner) | st.dictionaries(st.text(), inner),
+)
+UJSON_ENCODE_KWARGS = {
+ "ensure_ascii": st.booleans(),
+ "encode_html_chars": st.booleans(),
+ "escape_forward_slashes": st.booleans(),
+ "sort_keys": st.booleans(),
+ "indent": st.integers(0, 20),
+}
+
+
+@given(obj=JSON_OBJECTS, kwargs=st.fixed_dictionaries(UJSON_ENCODE_KWARGS))
+def test_ujson_roundtrip(obj, kwargs):
+ """Check that all JSON objects round-trip regardless of other options."""
+ assert obj == ujson.decode(ujson.encode(obj, **kwargs))
+
+
+if __name__ == "__main__":
+ # Running `pytest hypothesis_structured_fuzzer.py` will replay, deduplicate,
+ # and minimize any failures discovered by earlier runs or by OSS-Fuzz, or
+ # briefly search for new failures if none are known.
+ # Or, when running via OSS-Fuzz, we'll execute it via the fuzzing hook:
+ atheris.Setup(sys.argv, test_ujson_roundtrip.hypothesis.fuzz_one_input)
+ atheris.Fuzz()