Skip to content

Commit 40e5180

Browse files
authored
feat: fuzz testing PerlParser (#3725)
Closes #3335
1 parent 470e206 commit 40e5180

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

fuzz/fuzz_cpanfile.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (C) 2023 Intel Corporation
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
import sys
5+
import tempfile
6+
from pathlib import Path
7+
8+
import atheris
9+
import atheris_libprotobuf_mutator
10+
from google.protobuf.json_format import MessageToDict
11+
12+
import fuzz.generated.cpanfile_pb2 as cpanfile_pb2
13+
from cve_bin_tool.cvedb import CVEDB
14+
from cve_bin_tool.log import LOGGER
15+
16+
with atheris.instrument_imports():
17+
from cve_bin_tool.parsers.perl import PerlParser
18+
19+
cve_db = CVEDB()
20+
logger = LOGGER.getChild("Fuzz")
21+
22+
23+
def cpanfileBuilder(data, file_path):
24+
# Convert the Protobuf message to a dictionary
25+
json_data = MessageToDict(
26+
data, preserving_proto_field_name=True, including_default_value_fields=True
27+
)
28+
29+
with open(file_path, "w") as f:
30+
# Writing general requirements
31+
for module in json_data.get("general_requirements", []):
32+
f.write(
33+
f'requires "{module.get("name", "")}" => "{module.get("version", "0")}";\n'
34+
)
35+
36+
# Handling 'test' environment
37+
test_deps = json_data.get("test_dependencies", {})
38+
if test_deps:
39+
f.write("\non 'test' => sub {\n")
40+
for module in test_deps.get("test_requirements", []):
41+
f.write(
42+
f' requires "{module.get("name", "")}" => "{module.get("version", "0")}";\n'
43+
)
44+
for module in test_deps.get("test_recommends", []):
45+
f.write(
46+
f' recommends "{module.get("name", "")}" => "{module.get("version", "0")}";\n'
47+
)
48+
f.write("};\n")
49+
50+
# Handling 'develop' environment
51+
develop_deps = json_data.get("develop_dependencies", {})
52+
if develop_deps:
53+
f.write("\non 'develop' => sub {\n")
54+
for module in develop_deps.get("develop_requirements", []):
55+
f.write(
56+
f' requires "{module.get("name", "")}" => "{module.get("version", "0")}";\n'
57+
)
58+
f.write("};\n")
59+
60+
61+
def TestParseData(data):
62+
try:
63+
cpanfileBuilder(data)
64+
65+
perl_parser = PerlParser(cve_db, logger)
66+
perl_parser.run_checker(file_path)
67+
68+
except SystemExit:
69+
return
70+
71+
72+
file_path = str(Path(tempfile.mkdtemp(prefix="cve-bin-tool-")) / "cpanfile")
73+
74+
atheris_libprotobuf_mutator.Setup(sys.argv, TestParseData, proto=cpanfile_pb2.CPANFile)
75+
atheris.Fuzz()

fuzz/generated/cpanfile_pb2.py

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fuzz/proto_files/cpanfile.proto

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (C) 2022 Intel Corporation
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
syntax = "proto3";
5+
6+
// Represents a single Perl module requirement.
7+
message ModuleRequirement {
8+
string name = 1; // The name of the module
9+
string version = 2; // The required version of the module
10+
}
11+
12+
// Represents the entire CPAN file.
13+
message CPANFile {
14+
repeated ModuleRequirement general_requirements = 1; // General requirements
15+
16+
// Dependencies specific to the 'test' environment.
17+
message TestDependencies {
18+
repeated ModuleRequirement test_requirements = 1; // Test requirements
19+
repeated ModuleRequirement test_recommends = 2; // Test recommendations
20+
}
21+
TestDependencies test_dependencies = 2;
22+
23+
// Dependencies specific to the 'develop' environment.
24+
message DevelopDependencies {
25+
repeated ModuleRequirement develop_requirements = 1; // Develop requirements
26+
}
27+
DevelopDependencies develop_dependencies = 3;
28+
}

0 commit comments

Comments
 (0)