on 05-18-2023 10:21 AM
80 # Assemble the search URL.
81 page_size_query = f"per_page={SEARCH_PAGE_SIZE}"
82 custom_field_query = f"custom_fields:{custom_field}[]=none"
83 search_url = f"{base_url}vulnerabilities/search?{page_size_query}&{custom_field_query}"
38 page_size_query = f"per_page={page_size}"
39 custom_field_query = f"custom_fields:{search_custom_field}[]={search_value}"
40 search_url = f"{base_url}vulnerabilities/search?{page_size_query}&{custom_field_query}"
16 def update_vuln(base_url, headers, vuln_id, custom_field_id, custom_field_value):
17 update_url = f"{base_url}vulnerabilities/{vuln_id}"
18 update_custom_field_id = f"{custom_field_id}"
19 update_data = {
20 "vulnerability": {
21 "custom_fields": {
22 update_custom_field_id: custom_field_value
23 }
24 }
25 }
26
27 print(f"Update URL: {update_url}")
28 print_json(update_data)
29
30 # Invoke the update vulnerability endpoint.
31 response = requests.put(update_url, headers=headers, data=json.dumps(update_data))
32 if response.status_code != 204:
33 print("Vulnerability Update API ", response, update_url)
34 sys.exit(1)
152 def update_cisa_vulns(base_url, headers, vuln_ids, custom_field_id):
153 if len(vuln_ids) == 0:
154 return
155
156 bulk_update_url = f"{base_url}vulnerabilities/bulk"
157 update_custom_field_id = f"{custom_field_id}"
158 update_data = {
159 "vulnerability_ids": vuln_ids,
160 "vulnerability": {
161 "custom_fields": {
162 update_custom_field_id: "true"
163 }
164 }
165 }
166
167 response = requests.put(bulk_update_url, headers=headers, data=json.dumps(update_data))
29 # Append a custom field value to the list of values.
30 def append_new_value(self, custom_field_value):
31 if custom_field_value is None:
32 return
33
34 self.custom_field_count += 1
35
36 # If the value is not in the list of values, add it.
37 if not custom_field_value in self.custom_field_values:
38 self.custom_field_values.append(custom_field_value)
331 # A dictionary of custom fields keyed by custom field name with the value
332 # of a custom field object.
333 unique_custom_fields = {}
filter_params = {
'status' : ['open'],
'export_settings': {
'format': 'jsonl',
'model': 'vulnerability'
}
}
241 # Process vulnerabilities in the JSONL format.
242 def process_vuln_export(jsonl_vuln_file_name, unique_custom_fields):
243 print_info(f"Opening {jsonl_vuln_file_name} for processing.")
244 logging_interval = 1000
245
246 # Open the JSONL file and read it line by line, checking each vulnerability line for custom fields.
247 with open(jsonl_vuln_file_name, 'r') as jsonl_f:
248 for line_num, vuln_line in enumerate(jsonl_f):
249 vuln = convert_to_json(vuln_line)
250 if "custom_fields" in vuln:
251 logging.info(f"Found custom_field in Vuln {line_num}")
252 process_custom_fields(unique_custom_fields, vuln["custom_fields"])
253
254 if (line_num + 1) % logging_interval == 0:
255 print(".", end='', flush='True')
256
257 print("")
258 return (line_num + 1)
224 # Process an array of custom fields in a vulnerability.
225 def process_custom_fields(unique_custom_fields, custom_fields):
226
227 # Process one custom field at a time.
228 for custom_field in custom_fields:
229 cf_name = custom_field["name"]
230
231 logging.debug(f"Processing custom field: {cf_name}")
232 if cf_name in unique_custom_fields:
233 unique_custom_field = unique_custom_fields[cf_name]
234 if unique_custom_field.custom_field_id != custom_field["custom_field_definition_id"]:
235 print_error(f"IDs for custom field {cf_name} do not match. " +
236 f"{unique_custom_field.custom_field_id}, {custom_field['custom_field_definition_id']}")
237 unique_custom_field.append_new_value(custom_field["value"])
238 else:
239 append_custom_field(unique_custom_fields, custom_field)
240
218 # Append a custom field to the dictionary of unique custom fields.
219 def append_custom_field(unique_custom_fields, custom_field):
220 cf_name = custom_field["name"]
221 a_custom_field = Custom_Field(custom_field)
222 unique_custom_fields[cf_name] = a_custom_field
Finally, we get to the function that writes the unique custom field information to the CSV file, uniq_custom_fields.csv.
260 # Write the information out to a CSV file.
261 def write_csv_file(custom_fields):
262 # Open the CSV file and write the header row.
263 csv_file_name = "uniq_custom_fields.csv"
264 uniq_custom_fields_fp = open(csv_file_name, 'w', newline='')
265 uniq_custom_field_writer = csv.writer(uniq_custom_fields_fp)
266 uniq_custom_field_writer.writerow(["Custom Field", "Custom Field ID", "Field Count", "Value Count", "Custom Field Values"])
267
268 # Process each custom field and write it to the CSV file.
269 for custom_field_key in custom_fields:
270 custom_field = custom_fields[custom_field_key]
271
272 if custom_field.custom_field_count == 0:
273 uniq_custom_field_writer.writerow([custom_field.custom_field_name, custom_field.custom_field_id, custom_field.custom_field_count])
274 else:
275 uniq_custom_field_writer.writerow([custom_field.custom_field_name, custom_field.custom_field_id,
276 custom_field.custom_field_count, str(custom_field.get_num_values()), custom_field.get_values()])
277
278 uniq_custom_fields_fp.close()
279 print_info(f"{csv_file_name} is now available.")
There are two types of rows, one for null custom field values and one for non-null custom field values.
If the count is zero, that implies that no one is currently using the custom field. It could be time to remove the unused custom field.
Here is an example of the CSV output:
Above is an Excel representation of the unique_custom_fields.csv. As you can see the spreadsheet has "Custom Field," "Custom Field ID," "Field Count," "Value Count," and "Custom Field Values." If the "Field Count" is zero, the custom field is currently not being used. "Custom Field Values is a comma separate string; for example, "Bad, good, extract, excellent" are contained in one cell.
After reading this blog, my hope is as a developer, you will know how to work with VM custom fields and the code examples will inspire you to write your own scripts. The new code mentioned in this blog is located in Kenna Security's Github blog_samples repository under the python/custom_fields directory.
Until next time,
API Evangelist
This blog was originally written for Kenna Security, which has been acquired by Cisco Systems.
Learn more about Cisco Vulnerability Management.
Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: