suricata/scripts/schema-sort.py
Philippe Antoine cf88ed518c
Some checks failed
builds / Prepare dependencies (push) Has been cancelled
builds / Prepare cbindgen (push) Has been cancelled
CodeQL (Rust/C) / Analyze (push) Has been cancelled
CodeQL (Python) / Analyze (push) Has been cancelled
docs / Prepare dependencies (push) Has been cancelled
docs / Prepare cbindgen (push) Has been cancelled
Nix Env Build / tests (push) Has been cancelled
Check Rust / Check Rust (push) Has been cancelled
Scan-build / Scan-build (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
builds / AlmaLinux 10 (schema, plugins) (push) Has been cancelled
builds / AlmaLinux 9 (schema) (push) Has been cancelled
builds / AlmaLinux 9 Test Templates (push) Has been cancelled
builds / Build RPMs (push) Has been cancelled
builds / AlmaLinux 8 (push) Has been cancelled
builds / CentOS Stream 9 (push) Has been cancelled
builds / Fedora 42 (Suricata Verify codecov) (push) Has been cancelled
builds / Fedora 42 (clang, debug, asan, wshadow, rust-strict, systemd) (push) Has been cancelled
builds / Fedora 42 (gcc, debug, flto, asan, wshadow, rust-strict) (push) Has been cancelled
builds / Fedora (non-root, debug, clang, asan, wshadow, rust-strict, no-ja) (push) Has been cancelled
builds / AlmaLinux 9 (no jansson) (push) Has been cancelled
builds / AlmaLinux 9 (Minimal/Recommended Build) (push) Has been cancelled
builds / Ubuntu 24.04 (cocci) (push) Has been cancelled
builds / Ubuntu 24.04 (RUSTC+CARGO vars) (push) Has been cancelled
builds / Ubuntu 24.04 (unittests coverage) (push) Has been cancelled
builds / Ubuntu 22.04 (unix socket mode coverage) (push) Has been cancelled
builds / Ubuntu 22.04 (afpacket and dpdk coverage) (push) Has been cancelled
builds / Ubuntu 24.04 (pcap unix socket ASAN) (push) Has been cancelled
builds / Ubuntu 24.04 (afpacket and dpdk live tests with ASAN) (push) Has been cancelled
builds / Ubuntu 22.04 (fuzz corpus coverage) (push) Has been cancelled
builds / Ubuntu 20.04 (-DNDEBUG) (push) Has been cancelled
builds / Ubuntu 20.04 (unsupported rust) (push) Has been cancelled
builds / Ubuntu 22.04 (Debug Validation) (push) Has been cancelled
builds / Ubuntu 22.04 (Fuzz) (push) Has been cancelled
builds / Ubuntu 22.04 (Netmap build) (push) Has been cancelled
builds / Ubuntu 22.04 (Minimal/Recommended Build) (push) Has been cancelled
builds / Ubuntu 22.04 (DPDK Build) (push) Has been cancelled
builds / Debian 12 (xdp) (push) Has been cancelled
builds / Debian 13 (xdp) (push) Has been cancelled
builds / Ubuntu 22.04 Dist Builder (push) Has been cancelled
builds / Debian 12 MSRV (push) Has been cancelled
builds / Debian 11 (push) Has been cancelled
builds / MacOS Latest (push) Has been cancelled
builds / Windows MSYS2 MINGW64 (NPcap) (push) Has been cancelled
builds / Windows MSYS2 MINGW64 (libpcap) (push) Has been cancelled
builds / Windows MSYS2 UCRT64 (libpcap) (push) Has been cancelled
builds / Windows MSYS2 MINGW64 (WinDivert) (push) Has been cancelled
builds / PF_RING (push) Has been cancelled
docs / Ubuntu 22.04 Dist Builder (push) Has been cancelled
jsonschema: check for duplicate keys
Ticket: 6691

And fix the one duplicate found
2025-12-10 06:38:12 +00:00

131 lines
4.1 KiB
Python
Executable file

#!/usr/bin/env python3
#
# Script to sort or just check that properties are in alphabetic order
import json
import sys
import argparse
from collections import OrderedDict
def sort_properties(obj, path=""):
"""Recursively sort 'properties' keys in a JSON schema object."""
if isinstance(obj, dict):
new_obj = OrderedDict()
for key, value in obj.items():
current_path = f"{path}.{key}" if path else key
if key == "properties" and isinstance(value, dict):
sorted_properties = OrderedDict(sorted(value.items()))
new_obj[key] = sorted_properties
for prop_key, prop_value in sorted_properties.items():
new_obj[key][prop_key] = sort_properties(
prop_value, f"{current_path}.{prop_key}"
)
else:
new_obj[key] = sort_properties(value, current_path)
return new_obj
elif isinstance(obj, list):
return [sort_properties(item, f"{path}[{i}]") for i, item in enumerate(obj)]
else:
return obj
def check_duplicate(pairs):
dct = OrderedDict()
for key, value in pairs:
if key in dct:
raise Exception("{} is duplicate in pairs", key, pairs)
dct[key] = value
return dct
def check_properties_sorted(obj, path=""):
"""Check if all 'properties' keys have their contents sorted alphabetically."""
errors = []
if isinstance(obj, dict):
for key, value in obj.items():
current_path = f"{path}.{key}" if path else key
if key == "properties" and isinstance(value, dict):
keys_list = list(value.keys())
sorted_keys = sorted(keys_list)
if keys_list != sorted_keys:
errors.append(f"Properties not sorted at path: {current_path}")
errors.append(f" Current order: {keys_list}")
errors.append(f" Should be: {sorted_keys}")
for prop_key, prop_value in value.items():
errors.extend(
check_properties_sorted(
prop_value, f"{current_path}.{prop_key}"
)
)
else:
errors.extend(check_properties_sorted(value, current_path))
elif isinstance(obj, list):
for i, item in enumerate(obj):
errors.extend(check_properties_sorted(item, f"{path}[{i}]"))
return errors
def main():
parser = argparse.ArgumentParser(
description="Sort JSON schema properties alphabetically"
)
parser.add_argument("schema_file", help="Path to the JSON schema file")
parser.add_argument(
"--check",
action="store_true",
help="Check if properties are sorted (exit 1 if not)",
)
parser.add_argument(
"--in-place",
action="store_true",
help="Sort the file in place (only if not in check mode)",
)
args = parser.parse_args()
try:
with open(args.schema_file, "r") as f:
schema = json.load(f, object_pairs_hook=check_duplicate)
except Exception as e:
print(f"Error reading schema file: {e}", file=sys.stderr)
sys.exit(1)
if args.check:
errors = check_properties_sorted(schema)
if errors:
print("Schema properties are not sorted!", file=sys.stderr)
for error in errors:
print(error, file=sys.stderr)
sys.exit(1)
else:
print("Schema properties are properly sorted.")
sys.exit(0)
else:
sorted_schema = sort_properties(schema)
if args.in_place:
try:
with open(args.schema_file, "w") as f:
json.dump(sorted_schema, f, indent=4)
f.write("\n")
print(f"Sorted schema written to {args.schema_file}")
except Exception as e:
print(f"Error writing schema file: {e}", file=sys.stderr)
sys.exit(1)
else:
print(json.dumps(sorted_schema, indent=4))
if __name__ == "__main__":
main()