feat: add JSON output format for both syntactic and semantic validation#361
Closed
ChristopherJHart wants to merge 3 commits intonetascode:mainfrom
Closed
feat: add JSON output format for both syntactic and semantic validation#361ChristopherJHart wants to merge 3 commits intonetascode:mainfrom
ChristopherJHart wants to merge 3 commits intonetascode:mainfrom
Conversation
Changes error output from inline Python list format:
ERROR - Semantic error, rule 101: ... (["error1", "error2"])
To a cleaner bulleted list format:
ERROR - Semantic error, rule 101: ...:
- error1
- error2
This improves readability when multiple validation errors are reported.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds --format/-f CLI option with 'text' (default) and 'json' values.
JSON output includes structured syntax and semantic validation errors:
{
"syntax_errors": [{"file": "...", "line": N, "column": N, "message": "..."}],
"semantic_errors": [{"rule_id": "...", "description": "...", "errors": [...]}]
}
Key changes:
- Add SyntaxErrorResult and SemanticErrorResult dataclasses
- Populate structured results in validator for both error types
- Add OutputFormat enum and --format CLI option
- Suppress logging at default verbosity when using JSON format
- Add integration tests for JSON output
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Schema validation errors don't have line/column info (only YAML parsing errors do), so omit these fields when null for cleaner output. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
I've opened a draft PR (#363) that addresses the output format improvements for semantic validation and cherry picked your JSON format stuff. |
Author
|
Closing this as #363 will supersede it |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a
--format jsonCLI option for machine-parseable validation results.Usage
Output Structure
Syntax Error Example (YAML parsing errors)
{ "syntax_errors": [ { "file": "aac/data/tenant_chart2.nac.yaml", "line": 6, "column": 15, "message": "could not find expected ':'" } ], "semantic_errors": [] }Syntax Error Example (Schema validation errors)
{ "syntax_errors": [ { "file": "aac/data/tenant_chart2.nac.yaml", "message": "apic.tenants.[name=chart2].filters.[name=chart2_test_ssh].entries.[naem=chart2_test_ssh_entry].naem: Unexpected element" }, { "file": "aac/data/tenant_chart2.nac.yaml", "message": "apic.tenants.[name=chart2].filters.[name=chart2_test_ssh].entries.[naem=chart2_test_ssh_entry].name: Required field missing" }, { "file": "aac/data/tenant_chart2.nac.yaml", "message": "apic.tenants.[name=chart2].filters.[name=chart2_test_http].entries.[name=chart2_test_http_entry].source_form_port: Unexpected element" } ], "semantic_errors": [] }Semantic Error Example
{ "syntax_errors": [], "semantic_errors": [ { "rule_id": "311", "description": "Verify TCP or UDP protocol is specified for non-well-known ports in filters", "errors": [ "apic.tenants[EXAMPLE].filters[BACKUP_PORTS].entries[TCP_8400] - Port fields contain non-well-known port(s) (destination_from_port=8400) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[BACKUP_PORTS].entries[TCP_8401] - Port fields contain non-well-known port(s) (destination_from_port=8401) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[BACKUP_PORTS].entries[TCP_8408] - Port fields contain non-well-known port(s) (destination_from_port=8408) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[BACKUP_PORTS].entries[TCP_2987] - Port fields contain non-well-known port(s) (destination_from_port=2987) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[BACKUP_PORTS].entries[TCP_8403] - Port fields contain non-well-known port(s) (destination_from_port=8403) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[BACKUP_PORTS].entries[TCP_8405] - Port fields contain non-well-known port(s) (destination_from_port=8405) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[SMB].entries[TCP_445] - Port fields contain non-well-known port(s) (destination_from_port=445) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[SMB-S].entries[TCP_445] - Port fields contain non-well-known port(s) (source_from_port=445) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[ESX_PORTS].entries[TCP_2049] - Port fields contain non-well-known port(s) (destination_from_port=2049) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[ESX_PORTS].entries[TCP_902] - Port fields contain non-well-known port(s) (destination_from_port=902) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[RDP-S].entries[TCP_3389] - Port fields contain non-well-known port(s) (source_from_port=3389) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[NetBIOS].entries[TCP_139] - Port fields contain non-well-known port(s) (destination_from_port=139) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[NetBIOS-S].entries[TCP_139] - Port fields contain non-well-known port(s) (source_from_port=445) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[EXAMPLE].filters[RDP].entries[RDP-TCP] - Port fields contain non-well-known port(s) (destination_from_port=3389) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[FILTER_TEST_FAIL].filters[CUSTOM_PORT_NO_PROTOCOL].entries[PORT_8080_NO_PROTO] - Port fields contain non-well-known port(s) (destination_from_port=8080) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[FILTER_TEST_FAIL].filters[SRC_CUSTOM_NO_PROTOCOL].entries[SRC_9000_NO_PROTO] - Port fields contain non-well-known port(s) (source_from_port=9000) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[FILTER_TEST_FAIL].filters[CUSTOM_PORT_ICMP].entries[ICMP_WITH_PORT] - Port fields contain non-well-known port(s) (destination_from_port=8080) but protocol is 'icmp' which is not valid for port-based filtering. Protocol must be 'tcp' or 'udp' when port fields are used.", "apic.tenants[FILTER_TEST_FAIL].filters[PORT_RANGE_NO_PROTOCOL].entries[RANGE_NO_PROTO] - Port fields contain non-well-known port(s) (destination_from_port=5000, destination_to_port=6000) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[FILTER_TEST_FAIL].filters[MIXED_PORTS_NO_PROTOCOL].entries[MIXED] - Port fields contain non-well-known port(s) (destination_from_port=9999) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers.", "apic.tenants[FILTER_TEST_FAIL].filters[BOTH_RULES_FAIL].entries[BAD_FILTER] - Port fields contain non-well-known port(s) (destination_to_port=5000) but protocol is 'igmp' which is not valid for port-based filtering. Protocol must be 'tcp' or 'udp' when port fields are used.", "apic.tenants[TEST].filters[PERMIT_TCP_81].entries[TCP-81] - Port fields contain non-well-known port(s) (destination_from_port=81) but protocol is not specified. Best practice dictates that protocol (tcp/udp) be explicitly defined when using non-well-known port numbers." ] }, { "rule_id": "310", "description": "Verify filter port ranges are valid (to_port requires from_port, from <= to)", "errors": [ "apic.tenants[FILTER_TEST_FAIL].filters[MISSING_DST_FROM_PORT].entries[TCP_TO_443] - destination_to_port is specified (443) but destination_from_port is missing. destination_from_port must be specified to define a valid port range.", "apic.tenants[FILTER_TEST_FAIL].filters[MISSING_SRC_FROM_PORT].entries[TCP_TO_8080] - source_to_port is specified (8080) but source_from_port is missing. source_from_port must be specified to define a valid port range.", "apic.tenants[FILTER_TEST_FAIL].filters[INVALID_DST_PORT_RANGE].entries[TCP_INVALID_RANGE] - destination_from_port (9000) is greater than destination_to_port (8000). From port must be less than or equal to to port.", "apic.tenants[FILTER_TEST_FAIL].filters[INVALID_SRC_PORT_RANGE].entries[TCP_INVALID_SRC_RANGE] - source_from_port (65535) is greater than source_to_port (1024). From port must be less than or equal to to port.", "apic.tenants[FILTER_TEST_FAIL].filters[BOTH_RULES_FAIL].entries[BAD_FILTER] - destination_to_port is specified (5000) but destination_from_port is missing. destination_from_port must be specified to define a valid port range.", "apic.tenants[chart2].filters[chart2_test_https].entries[chart2_test_https_entry] - destination_to_port is specified (https) but destination_from_port is missing. destination_from_port must be specified to define a valid port range.", "apic.tenants[chart2].filters[chart2_test_dns].entries[chart2_test_dns_entry] - source_to_port is specified (53) but source_from_port is missing. source_from_port must be specified to define a valid port range." ] } ] }Notes