diff options
Diffstat (limited to 'scripts/cve-json-to-text.py')
-rwxr-xr-x | scripts/cve-json-to-text.py | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/scripts/cve-json-to-text.py b/scripts/cve-json-to-text.py new file mode 100755 index 0000000000..5531ee5eb6 --- /dev/null +++ b/scripts/cve-json-to-text.py | |||
@@ -0,0 +1,145 @@ | |||
1 | #!/bin/env python3 | ||
2 | # SPDX-FileCopyrightText: OpenEmbedded Contributors | ||
3 | # | ||
4 | # SPDX-License-Identifier: MIT | ||
5 | |||
6 | # CVE results conversion script: JSON format to text | ||
7 | # Derived from cve-report.py from Oniro (MIT, by Huawei Inc) | ||
8 | |||
9 | import sys | ||
10 | import getopt | ||
11 | |||
12 | infile = "in.json" | ||
13 | outfile = "out.txt" | ||
14 | |||
15 | |||
16 | def show_syntax_and_exit(code): | ||
17 | """ | ||
18 | Show the program syntax and exit with an errror | ||
19 | Arguments: | ||
20 | code: the error code to return | ||
21 | """ | ||
22 | print("Syntax: %s [-h] [-i inputJSONfile][-o outputfile]" % sys.argv[0]) | ||
23 | sys.exit(code) | ||
24 | |||
25 | |||
26 | def exit_error(code, message): | ||
27 | """ | ||
28 | Show the error message and exit with an errror | ||
29 | Arguments: | ||
30 | code: the error code to return | ||
31 | message: the message to show | ||
32 | """ | ||
33 | print("Error: %s" % message) | ||
34 | sys.exit(code) | ||
35 | |||
36 | |||
37 | def parse_args(argv): | ||
38 | """ | ||
39 | Parse the program arguments, put options in global variables | ||
40 | Arguments: | ||
41 | argv: program arguments | ||
42 | """ | ||
43 | global infile, outfile | ||
44 | try: | ||
45 | opts, args = getopt.getopt( | ||
46 | argv, "hi:o:", ["help", "input", "output"] | ||
47 | ) | ||
48 | except getopt.GetoptError: | ||
49 | show_syntax_and_exit(1) | ||
50 | for opt, arg in opts: | ||
51 | if opt in ("-h", "--help"): | ||
52 | show_syntax_and_exit(0) | ||
53 | elif opt in ("-a", "--all"): | ||
54 | show_all = True | ||
55 | show_unknown = True | ||
56 | elif opt in ("-i", "--input"): | ||
57 | infile = arg | ||
58 | |||
59 | def load_json(filename): | ||
60 | """ | ||
61 | Load the JSON file, return the resulting dictionary | ||
62 | Arguments: | ||
63 | filename: the file to open | ||
64 | Returns: | ||
65 | Parsed file as a dictionary | ||
66 | """ | ||
67 | import json | ||
68 | |||
69 | out = {} | ||
70 | try: | ||
71 | with open(filename, "r") as f: | ||
72 | out = json.load(f) | ||
73 | except FileNotFoundError: | ||
74 | exit_error(1, "Input file (%s) not found" % (filename)) | ||
75 | except json.decoder.JSONDecodeError as error: | ||
76 | exit_error(1, "Malformed JSON file: %s" % str(error)) | ||
77 | return out | ||
78 | |||
79 | |||
80 | def process_data(filename, data): | ||
81 | """ | ||
82 | Write the resulting CSV with one line for each package | ||
83 | Arguments: | ||
84 | filename: the file to write to | ||
85 | data: dictionary from parsing the JSON file | ||
86 | Returns: | ||
87 | None | ||
88 | """ | ||
89 | if not "version" in data or data["version"] != "1": | ||
90 | exit_error(1, "Unrecognized format version number") | ||
91 | if not "package" in data: | ||
92 | exit_error(1, "Mandatory 'package' key not found") | ||
93 | |||
94 | lines = "" | ||
95 | total_issue_count = 0 | ||
96 | for package in data["package"]: | ||
97 | package_info = "" | ||
98 | keys_in_package = {"name", "layer", "version", "issue"} | ||
99 | if keys_in_package - package.keys(): | ||
100 | exit_error( | ||
101 | 1, | ||
102 | "Missing a mandatory key in package: %s" | ||
103 | % (keys_in_package - package.keys()), | ||
104 | ) | ||
105 | |||
106 | package_info += "LAYER: %s\n" % package["layer"] | ||
107 | package_info += "PACKAGE NAME: %s\n" % package["name"] | ||
108 | package_info += "PACKAGE VERSION: %s\n" % package["version"] | ||
109 | |||
110 | for issue in package["issue"]: | ||
111 | keys_in_issue = {"id", "status", "detail"} | ||
112 | if keys_in_issue - issue.keys(): | ||
113 | print("Warning: Missing keys %s in 'issue' for the package '%s'" | ||
114 | % (keys_in_issue - issue.keys(), package["name"])) | ||
115 | |||
116 | lines += package_info | ||
117 | lines += "CVE: %s\n" % issue["id"] | ||
118 | lines += "CVE STATUS: %s\n" % issue["status"] | ||
119 | lines += "CVE DETAIL: %s\n" % issue["detail"] | ||
120 | if "description" in issue: | ||
121 | lines += "CVE DESCRIPTION: %s\n" % issue["description"] | ||
122 | if "summary" in issue: | ||
123 | lines += "CVE SUMMARY: %s\n" % issue["summary"] | ||
124 | if "scorev2" in issue: | ||
125 | lines += "CVSS v2 BASE SCORE: %s\n" % issue["scorev2"] | ||
126 | if "scorev3" in issue: | ||
127 | lines += "CVSS v3 BASE SCORE: %s\n" % issue["scorev3"] | ||
128 | if "vector" in issue: | ||
129 | lines += "VECTOR: %s\n" % issue["vector"] | ||
130 | if "vectorString" in issue: | ||
131 | lines += "VECTORSTRING: %s\n" % issue["vectorString"] | ||
132 | lines += "MORE INFORMATION: https://nvd.nist.gov/vuln/detail/%s\n" % issue["id"] | ||
133 | lines += "\n" | ||
134 | |||
135 | with open(filename, "w") as f: | ||
136 | f.write(lines) | ||
137 | |||
138 | def main(argv): | ||
139 | parse_args(argv) | ||
140 | data = load_json(infile) | ||
141 | process_data(outfile, data) | ||
142 | |||
143 | |||
144 | if __name__ == "__main__": | ||
145 | main(sys.argv[1:]) | ||