diff --git a/README.md b/README.md index 586b2e1..f39b32a 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ usage: ssh-audit.py [options] software config (use -p to change port; use -t to change timeout) -d, --debug Enable debug output. - -j, --json JSON output + -j, --json JSON output (use -jj to enable indents) -l, --level= minimum output level (info|warn|fail) -L, --list-policies list all the official, built-in policies --lookup= looks up an algorithm(s) without @@ -177,6 +177,7 @@ For convenience, a web front-end on top of the command-line tool is available at ## ChangeLog ### v2.5.0-dev (???) - Fixed crash when running host key tests. + - Now prints JSON with indents when `-jj` is used (useful for debugging). - Added MD5 fingerprints to verbose output. - Added `-d`/`--debug` option for getting debugging output; credit [Adam Russell](https://github.com/thecliguy). - Updated JSON output to include MD5 fingerprints. Note that this results in a breaking change in the 'fingerprints' dictionary format. diff --git a/src/ssh_audit/auditconf.py b/src/ssh_audit/auditconf.py index de0153a..1d51240 100644 --- a/src/ssh_audit/auditconf.py +++ b/src/ssh_audit/auditconf.py @@ -1,7 +1,7 @@ """ The MIT License (MIT) - Copyright (C) 2017-2020 Joe Testa (jtesta@positronsecurity.com) + Copyright (C) 2017-2021 Joe Testa (jtesta@positronsecurity.com) Copyright (C) 2017 Andris Raugulis (moo@arthepsy.eu) Permission is hereby granted, free of charge, to any person obtaining a copy @@ -41,6 +41,7 @@ class AuditConf: self.client_audit = False self.colors = True self.json = False + self.json_print_indent = False self.verbose = False self.level = 'info' self.ip_version_preference: List[int] = [] # Holds only 5 possible values: [] (no preference), [4] (use IPv4 only), [6] (use IPv6 only), [46] (use both IPv4 and IPv6, but prioritize v4), and [64] (use both IPv4 and IPv6, but prioritize v6). @@ -61,7 +62,7 @@ class AuditConf: def __setattr__(self, name: str, value: Union[str, int, float, bool, Sequence[int]]) -> None: valid = False - if name in ['batch', 'client_audit', 'colors', 'json', 'list_policies', 'manual', 'make_policy', 'ssh1', 'ssh2', 'timeout_set', 'verbose', 'debug']: + if name in ['batch', 'client_audit', 'colors', 'json', 'json_print_indent', 'list_policies', 'manual', 'make_policy', 'ssh1', 'ssh2', 'timeout_set', 'verbose', 'debug']: valid, value = True, bool(value) elif name in ['ipv4', 'ipv6']: valid, value = True, bool(value) diff --git a/src/ssh_audit/ssh_audit.py b/src/ssh_audit/ssh_audit.py index cbfe8ad..d083ee5 100755 --- a/src/ssh_audit/ssh_audit.py +++ b/src/ssh_audit/ssh_audit.py @@ -85,7 +85,7 @@ def usage(err: Optional[str] = None) -> None: uout.info(' -b, --batch batch output') uout.info(' -c, --client-audit starts a server on port 2222 to audit client\n software config (use -p to change port;\n use -t to change timeout)') uout.info(' -d, --debug debug output') - uout.info(' -j, --json JSON output') + uout.info(' -j, --json JSON output (use -jj to enable indents)') uout.info(' -l, --level= minimum output level (info|warn|fail)') uout.info(' -L, --list-policies list all the official, built-in policies') uout.info(' --lookup= looks up an algorithm(s) without\n connecting to a server') @@ -477,7 +477,7 @@ def output(out: OutputBuffer, aconf: AuditConf, banner: Optional[Banner], header if aconf.json: out.reset() # Build & write the JSON struct. - out.info(json.dumps(build_struct(aconf.host, banner, kex=kex, client_host=client_host), sort_keys=True)) + out.info(json.dumps(build_struct(aconf.host, banner, kex=kex, client_host=client_host), indent=4 if aconf.json_print_indent else None, sort_keys=True)) elif len(unknown_algorithms) > 0: # If we encountered any unknown algorithms, ask the user to report them. out.warn("\n\n!!! WARNING: unknown algorithm(s) found!: %s. Please email the full output above to the maintainer (jtesta@positronsecurity.com), or create a Github issue at .\n" % ','.join(unknown_algorithms)) @@ -492,7 +492,7 @@ def evaluate_policy(out: OutputBuffer, aconf: AuditConf, banner: Optional['Banne passed, error_struct, error_str = aconf.policy.evaluate(banner, kex) if aconf.json: json_struct = {'host': aconf.host, 'policy': aconf.policy.get_name_and_version(), 'passed': passed, 'errors': error_struct} - out.info(json.dumps(json_struct, sort_keys=True)) + out.info(json.dumps(json_struct, indent=4 if aconf.json_print_indent else None, sort_keys=True)) else: spacing = '' if aconf.client_audit: @@ -609,7 +609,10 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[. aconf.colors = False out.use_colors = False elif o in ('-j', '--json'): - aconf.json = True + if aconf.json: # If specified twice, enable indent printing. + aconf.json_print_indent = True + else: + aconf.json = True elif o in ('-v', '--verbose'): aconf.verbose = True out.verbose = True diff --git a/ssh-audit.1 b/ssh-audit.1 index 482f164..6939627 100644 --- a/ssh-audit.1 +++ b/ssh-audit.1 @@ -54,7 +54,7 @@ Enable debug output. .TP .B -j, \-\-json .br -Output results in JSON format. +Output results in JSON format. Specify twice (-jj) to enable indent printing (useful for debugging). .TP .B -l, \-\-level=