scripts/verify_checklist.py (163 lines of code) (raw):
#################################################################################
#
# This script verifies a specific checklist for correctness.
#
# Last updated: April 2024
#
#################################################################################
import json
import argparse
import sys
import glob
import os
# Get input arguments
parser = argparse.ArgumentParser(description='Verify a JSON checklist for correctness')
parser.add_argument('--input-file', dest='input_file', action='store',
help='You need to supply the name of the JSON file with the checklist to be filtered')
parser.add_argument('--compare-file', dest='compare_file', action='store',
help='You can optionally supply the name of the JSON file with a second checklist to be compared against the first one')
parser.add_argument('--input-folder', dest='input_folder', action='store',
help='If no input file has been specified, input folder where the checklists to verify are stored')
parser.add_argument('--verbose', dest='verbose', action='store_true',
default=False,
help='run in verbose mode (default: False)')
args = parser.parse_args()
# Global variables
guids = []
# Function that verifies the correctness of a single checklist
def verify_file(input_file):
# Banner
if args.verbose:
print("DEBUG: ======================================================================")
print("DEBUG: Verifying file", input_file)
# Look for non-unicode characters in the file
if args.verbose:
print("DEBUG: Verifying all characters are Unicode-8...")
f1 = open (input_file, "r")
text = f1.read()
for line in text:
for character in line:
if ord(character) > 127:
print("ERROR: Non-unicode character found in file", input_file, ":", character)
sys.exit(1)
# if args.verbose:
# print("DEBUG: All characters are Unicode-8")
# Reading into JSON
if args.verbose:
print("DEBUG: Verifying JSON can be loaded up...")
try:
with open(input_file) as f:
checklist = json.load(f)
if 'items' in checklist:
if args.verbose:
print("DEBUG: {0} items found in JSON file {1}".format(len(checklist['items']), input_file))
except Exception as e:
print("ERROR: Error when processing JSON file, nothing changed", input_file, ":", str(e))
sys.exit(1)
# if args.verbose:
# print("DEBUG: JSON can be loaded up correctly")
# Verify the required keys are present
if args.verbose:
print("DEBUG: Verifying the required keys are present...")
required_keys = ['items', 'metadata', 'categories', 'status', 'severities', 'yesno']
for key in required_keys:
if key not in checklist:
print("ERROR: Required key missing from JSON file", input_file, ":", key)
# Verify the metadata keys are present
if 'metadata' in checklist:
if args.verbose:
print("DEBUG: Verifying the metadata keys are present...")
required_keys = ['name', 'timestamp', 'state', 'waf']
for key in required_keys:
if key not in checklist['metadata']:
print("ERROR: Required key missing from metadata in JSON file", input_file, ":", key)
else:
if args.verbose:
print("WARNING: skipping metadata verification, no metadata in JSON file", input_file)
# Verify the metadata waf key has a valid value
if 'metadata' in checklist:
if 'waf' in checklist['metadata']:
if checklist['metadata']['waf'].lower() not in ['none', 'all', 'reliability', 'security', 'performance', 'cost', 'operations']:
print("ERROR: Invalid WAF value in metadata in JSON file", input_file, ":", checklist['metadata']['waf'])
# Verify the items have all required keys
if args.verbose:
print("DEBUG: Verifying the items have all required keys...")
# Counter dictionary for inconsistencies
item_count = 0
inconsistencies = {
'missing_graph': 0,
'missing_description': 0,
'wrong_cat': 0,
'missing_cat': 0,
'missing_subcat': 0,
'missing_waf': 0,
'wrong_waf': 0,
'missing_svc': 0,
'missing_link': 0,
'missing_sev': 0,
'missing_guid': 0,
'localized_link': 0
}
# Load categories to verify whether the items have the correct category
if 'categories' in checklist:
categories = [x['name'] for x in checklist['categories']]
if args.verbose:
print("DEBUG: Categories found in JSON file", input_file, ":", str(categories))
else:
categories = []
if 'items' in checklist:
for item in checklist['items']:
item_count += 1
if 'category' not in item:
inconsistencies['missing_cat'] += 1
elif item['category'] not in categories:
inconsistencies['wrong_cat'] += 1
if 'subcategory' not in item:
inconsistencies['missing_subcat'] += 1
if 'waf' not in item:
inconsistencies['missing_waf'] += 1
elif item['waf'].lower() not in ['reliability', 'security', 'performance', 'cost', 'operations']:
inconsistencies['wrong_waf'] += 1
if 'service' not in item:
inconsistencies['missing_svc'] += 1
if 'guid' not in item:
inconsistencies['missing_guid'] += 1
elif item['guid'] in guids:
print("ERROR: Duplicated GUID in JSON file", input_file, ":", item['guid'])
else:
guids.append(item['guid'])
if 'link' not in item:
inconsistencies['missing_link'] += 1
elif 'en-us' in item['link']:
inconsistencies['localized_link'] += 1
if 'severity' not in item:
inconsistencies['missing_sev'] += 1
if 'graph' not in item:
inconsistencies['missing_graph'] += 1
if 'description' not in item:
inconsistencies['missing_description'] += 1
if inconsistencies['missing_cat'] > 0:
print("ERROR: Items with missing category in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['missing_cat'], round(inconsistencies['missing_cat'] / item_count * 100, 2)))
if inconsistencies['wrong_cat'] > 0:
print("WARNING: Items with wrong category in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['wrong_cat'], round(inconsistencies['wrong_cat'] / item_count * 100, 2)))
if inconsistencies['missing_subcat'] > 0:
print("ERROR: Items with missing subcategory in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['missing_subcat'], round(inconsistencies['missing_subcat'] / item_count * 100, 2)))
if inconsistencies['missing_waf'] > 0:
print("WARNING: Items with missing WAF in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['missing_waf'], round(inconsistencies['missing_waf'] / item_count * 100, 2)))
if inconsistencies['wrong_waf'] > 0:
print("ERROR: Items with wrong WAF in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['wrong_waf'], round(inconsistencies['wrong_waf'] / item_count * 100, 2)))
if inconsistencies['missing_svc'] > 0:
print("WARNING: Items with missing service in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['missing_svc'], round(inconsistencies['missing_svc'] / item_count * 100, 2)))
if inconsistencies['missing_link'] > 0:
print("WARNING: Items with missing link in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['missing_link'], round(inconsistencies['missing_link'] / item_count * 100, 2)))
if inconsistencies['missing_sev'] > 0:
print("ERROR: Items with missing severity in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['missing_sev'], round(inconsistencies['missing_sev'] / item_count * 100, 2)))
if inconsistencies['localized_link'] > 0:
print("WARNING: Items with localized link in JSON file {0}: {1} ({2}%)".format(input_file, inconsistencies['localized_link'], round(inconsistencies['localized_link'] / item_count * 100, 2)))
return {
'item_count': item_count,
'inconsistencies': inconsistencies
}
# We need an input file
if args.input_file:
file_stats = verify_file(args.input_file)
if args.compare_file:
compare_stats = verify_file(args.compare_file)
# Print the differences between the two checklists stats in a table format
print("INFO: Comparing the two checklists...")
print("INFO: {0: <40} {1: <40} {2: <40}".format("Item", os.path.basename(args.input_file), os.path.basename(args.compare_file)))
print("INFO: {0: <40} {1: <40} {2: <40}".format("----", "-" * len(os.path.basename(args.input_file)), "-" * len(os.path.basename(args.compare_file))))
print("INFO: {0: <40} {1: <40} {2: <40}".format("Total items", file_stats['item_count'], compare_stats['item_count']))
for key in file_stats['inconsistencies']:
print("INFO: {0: <40} {1: <40} {2: <40}".format(key, file_stats['inconsistencies'][key], compare_stats['inconsistencies'][key]))
else:
if args.input_folder:
language = "en" # This could be changed to a parameter
if args.verbose:
print("DEBUG: looking for JSON files in folder", args.input_folder, "with pattern *.", language + ".json...")
checklist_files = glob.glob(args.input_folder + "/*." + language + ".json")
if len(checklist_files) > 0:
if args.verbose:
print("DEBUG: found", len(checklist_files), "JSON files, analyzing correctness...")
for file in checklist_files:
if file:
file_stats = verify_file(file)
else:
print("ERROR: no input file found, not doing anything")
else:
print("ERROR: you need to use the parameters `--input-file` or `--input-folder` to specify the file or folder to verify")