diff --git a/fuzz/fuzz_cpanfile.py b/fuzz/fuzz_cpanfile.py new file mode 100644 index 0000000000..de7f3243cb --- /dev/null +++ b/fuzz/fuzz_cpanfile.py @@ -0,0 +1,75 @@ +# Copyright (C) 2023 Intel Corporation +# SPDX-License-Identifier: GPL-3.0-or-later + +import sys +import tempfile +from pathlib import Path + +import atheris +import atheris_libprotobuf_mutator +from google.protobuf.json_format import MessageToDict + +import fuzz.generated.cpanfile_pb2 as cpanfile_pb2 +from cve_bin_tool.cvedb import CVEDB +from cve_bin_tool.log import LOGGER + +with atheris.instrument_imports(): + from cve_bin_tool.parsers.perl import PerlParser + +cve_db = CVEDB() +logger = LOGGER.getChild("Fuzz") + + +def cpanfileBuilder(data, file_path): + # 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: + # Writing general requirements + for module in json_data.get("general_requirements", []): + f.write( + f'requires "{module.get("name", "")}" => "{module.get("version", "0")}";\n' + ) + + # Handling 'test' environment + test_deps = json_data.get("test_dependencies", {}) + if test_deps: + f.write("\non 'test' => sub {\n") + for module in test_deps.get("test_requirements", []): + f.write( + f' requires "{module.get("name", "")}" => "{module.get("version", "0")}";\n' + ) + for module in test_deps.get("test_recommends", []): + f.write( + f' recommends "{module.get("name", "")}" => "{module.get("version", "0")}";\n' + ) + f.write("};\n") + + # Handling 'develop' environment + develop_deps = json_data.get("develop_dependencies", {}) + if develop_deps: + f.write("\non 'develop' => sub {\n") + for module in develop_deps.get("develop_requirements", []): + f.write( + f' requires "{module.get("name", "")}" => "{module.get("version", "0")}";\n' + ) + f.write("};\n") + + +def TestParseData(data): + try: + cpanfileBuilder(data) + + perl_parser = PerlParser(cve_db, logger) + perl_parser.run_checker(file_path) + + except SystemExit: + return + + +file_path = str(Path(tempfile.mkdtemp(prefix="cve-bin-tool-")) / "cpanfile") + +atheris_libprotobuf_mutator.Setup(sys.argv, TestParseData, proto=cpanfile_pb2.CPANFile) +atheris.Fuzz() diff --git a/fuzz/generated/cpanfile_pb2.py b/fuzz/generated/cpanfile_pb2.py new file mode 100644 index 0000000000..2bb0671c53 --- /dev/null +++ b/fuzz/generated/cpanfile_pb2.py @@ -0,0 +1,32 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: cpanfile.proto +# Protobuf Python Version: 4.25.0 +"""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\x0e\x63panfile.proto"2\n\x11ModuleRequirement\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"\xe9\x02\n\x08\x43PANFile\x12\x30\n\x14general_requirements\x18\x01 \x03(\x0b\x32\x12.ModuleRequirement\x12\x35\n\x11test_dependencies\x18\x02 \x01(\x0b\x32\x1a.CPANFile.TestDependencies\x12;\n\x14\x64\x65velop_dependencies\x18\x03 \x01(\x0b\x32\x1d.CPANFile.DevelopDependencies\x1an\n\x10TestDependencies\x12-\n\x11test_requirements\x18\x01 \x03(\x0b\x32\x12.ModuleRequirement\x12+\n\x0ftest_recommends\x18\x02 \x03(\x0b\x32\x12.ModuleRequirement\x1aG\n\x13\x44\x65velopDependencies\x12\x30\n\x14\x64\x65velop_requirements\x18\x01 \x03(\x0b\x32\x12.ModuleRequirementb\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "cpanfile_pb2", _globals) +if _descriptor._USE_C_DESCRIPTORS is False: + DESCRIPTOR._options = None + _globals["_MODULEREQUIREMENT"]._serialized_start = 18 + _globals["_MODULEREQUIREMENT"]._serialized_end = 68 + _globals["_CPANFILE"]._serialized_start = 71 + _globals["_CPANFILE"]._serialized_end = 432 + _globals["_CPANFILE_TESTDEPENDENCIES"]._serialized_start = 249 + _globals["_CPANFILE_TESTDEPENDENCIES"]._serialized_end = 359 + _globals["_CPANFILE_DEVELOPDEPENDENCIES"]._serialized_start = 361 + _globals["_CPANFILE_DEVELOPDEPENDENCIES"]._serialized_end = 432 +# @@protoc_insertion_point(module_scope) diff --git a/fuzz/proto_files/cpanfile.proto b/fuzz/proto_files/cpanfile.proto new file mode 100644 index 0000000000..b5c440f4a7 --- /dev/null +++ b/fuzz/proto_files/cpanfile.proto @@ -0,0 +1,28 @@ +// Copyright (C) 2022 Intel Corporation +// SPDX-License-Identifier: GPL-3.0-or-later + +syntax = "proto3"; + +// Represents a single Perl module requirement. +message ModuleRequirement { + string name = 1; // The name of the module + string version = 2; // The required version of the module +} + +// Represents the entire CPAN file. +message CPANFile { + repeated ModuleRequirement general_requirements = 1; // General requirements + + // Dependencies specific to the 'test' environment. + message TestDependencies { + repeated ModuleRequirement test_requirements = 1; // Test requirements + repeated ModuleRequirement test_recommends = 2; // Test recommendations + } + TestDependencies test_dependencies = 2; + + // Dependencies specific to the 'develop' environment. + message DevelopDependencies { + repeated ModuleRequirement develop_requirements = 1; // Develop requirements + } + DevelopDependencies develop_dependencies = 3; +}