From eabf69d8b97b83bce32334913919021d0269d76f Mon Sep 17 00:00:00 2001 From: Joydeep Tripathy <113792434+crazytrain328@users.noreply.github.com> Date: Fri, 9 Feb 2024 03:41:12 +0530 Subject: [PATCH] feat: Fuzz Testing SwiftParser (#3728) Signed-off-by: Joydeep Tripathy --- fuzz/fuzz_package_resolved.py | 78 +++++++++++++++++++++++++ fuzz/generated/package_resolved_pb2.py | 29 +++++++++ fuzz/proto_files/package_resolved.proto | 31 ++++++++++ 3 files changed, 138 insertions(+) create mode 100644 fuzz/fuzz_package_resolved.py create mode 100644 fuzz/generated/package_resolved_pb2.py create mode 100644 fuzz/proto_files/package_resolved.proto diff --git a/fuzz/fuzz_package_resolved.py b/fuzz/fuzz_package_resolved.py new file mode 100644 index 0000000000..eb2676777f --- /dev/null +++ b/fuzz/fuzz_package_resolved.py @@ -0,0 +1,78 @@ +# Copyright (C) 2023 Intel Corporation +# SPDX-License-Identifier: GPL-3.0-or-later + +import json +import sys +import tempfile +from pathlib import Path + +import atheris +import atheris_libprotobuf_mutator +from google.protobuf.json_format import MessageToDict + +import fuzz.generated.package_resolved_pb2 as package_resolved_pb2 +from cve_bin_tool.cvedb import CVEDB +from cve_bin_tool.log import LOGGER + +with atheris.instrument_imports(): + from cve_bin_tool.parsers.swift import SwiftParser + +cve_db = CVEDB() +logger = LOGGER.getChild("Fuzz") + + +def PackageResolvedBuilder(data): + """Convert the Protobuf message to a dictionary""" + json_data = MessageToDict( + data, preserving_proto_field_name=True, including_default_value_fields=True + ) + + with open(file_path, "w") as f: + f.write("{\n") + f.write(' "object": {\n') + f.write(' "pins": [\n') + + # Iterating through package pins + for i, pin in enumerate(json_data.get("object", {}).get("pins", [])): + f.write(" {\n") + f.write(f' "package": "{pin.get("package", "")}",\n') + f.write(f' "repositoryURL": "{pin.get("repositoryURL", "")}",\n') + f.write(' "state": {\n') + state = pin.get("state", {}) + f.write(f' "branch": {json.dumps(state.get("branch"))},\n') + f.write(f' "revision": "{state.get("revision", "")}",\n') + f.write(f' "version": "{state.get("version", "")}"\n') + f.write(" }\n") + f.write( + " }" + + ( + "," + if i < len(json_data.get("object", {}).get("pins", [])) - 1 + else "" + ) + + "\n" + ) + + f.write(" ]\n") + f.write(" },\n") + f.write(f' "version": {json_data.get("version", 1)}\n') + f.write("}\n") + + +def TestParseData(data): + try: + PackageResolvedBuilder(data) + + swift_parser = SwiftParser(cve_db, logger) + swift_parser.run_checker(file_path) + + except SystemExit: + return + + +file_path = str(Path(tempfile.mkdtemp(prefix="cve-bin-tool-")) / "Package.resolved") + +atheris_libprotobuf_mutator.Setup( + sys.argv, TestParseData, proto=package_resolved_pb2.PackageResolved +) +atheris.Fuzz() diff --git a/fuzz/generated/package_resolved_pb2.py b/fuzz/generated/package_resolved_pb2.py new file mode 100644 index 0000000000..2edc764706 --- /dev/null +++ b/fuzz/generated/package_resolved_pb2.py @@ -0,0 +1,29 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16package_resolved.proto\x12\x08resolved"A\n\x0cPackageState\x12\x0e\n\x06\x62ranch\x18\x01 \x01(\t\x12\x10\n\x08revision\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t"[\n\nPackagePin\x12\x0f\n\x07package\x18\x01 \x01(\t\x12\x15\n\rrepositoryURL\x18\x02 \x01(\t\x12%\n\x05state\x18\x03 \x01(\x0b\x32\x16.resolved.PackageState"\x82\x01\n\x0fPackageResolved\x12\x30\n\x06object\x18\x01 \x01(\x0b\x32 .resolved.PackageResolved.Object\x12\x0f\n\x07version\x18\x02 \x01(\x05\x1a,\n\x06Object\x12"\n\x04pins\x18\x01 \x03(\x0b\x32\x14.resolved.PackagePinb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "package_resolved_pb2", _globals) +if _descriptor._USE_C_DESCRIPTORS is False: + DESCRIPTOR._options = None + _globals["_PACKAGESTATE"]._serialized_start = 36 + _globals["_PACKAGESTATE"]._serialized_end = 101 + _globals["_PACKAGEPIN"]._serialized_start = 103 + _globals["_PACKAGEPIN"]._serialized_end = 194 + _globals["_PACKAGERESOLVED"]._serialized_start = 197 + _globals["_PACKAGERESOLVED"]._serialized_end = 327 + _globals["_PACKAGERESOLVED_OBJECT"]._serialized_start = 283 + _globals["_PACKAGERESOLVED_OBJECT"]._serialized_end = 327 +# @@protoc_insertion_point(module_scope) \ No newline at end of file diff --git a/fuzz/proto_files/package_resolved.proto b/fuzz/proto_files/package_resolved.proto new file mode 100644 index 0000000000..a242a839de --- /dev/null +++ b/fuzz/proto_files/package_resolved.proto @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: GPL-3.0-or-later + +syntax = "proto3"; + +package resolved; + +// Represents the state of a package, with optional branch, revision, and version. +message PackageState { + string branch = 1; // Branch name (optional, can be empty) + string revision = 2; // Revision or commit hash + string version = 3; // Version of the package +} + +// Represents a single package pin. +message PackagePin { + string package = 1; // Name of the package + string repositoryURL = 2; // URL of the package's repository + PackageState state = 3; // State of the package +} + +// Represents the top-level structure of the package.resolved file. +message PackageResolved { + message Object { + repeated PackagePin pins = 1; // List of package pins + } + + Object object = 1; // Object containing the list of pins + int32 version = 2; // Version of the package.resolved file +} +