diff --git a/README.md b/README.md index 7cf7e04..f004930 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ For convenience, a web front-end on top of the command-line tool is available at - Updated Ubuntu Server & Client policies for 20.04 and 22.04 to account for key exchange list changes due to Terrapin vulnerability patches. - Re-organized option host key types for OpenSSH 9.2 server policy to correspond with updated Debian 12 hardening guide. - Added built-in policies for OpenSSH 9.5 and 9.6. + - Added an "additional_notes" field to the JSON output. ### v3.0.0 (2023-09-07) - Results from concurrent scans against multiple hosts are no longer improperly combined; bug discovered by [Adam Russell](https://github.com/thecliguy). diff --git a/src/ssh_audit/ssh_audit.py b/src/ssh_audit/ssh_audit.py index 95e6f85..67db02a 100755 --- a/src/ssh_audit/ssh_audit.py +++ b/src/ssh_audit/ssh_audit.py @@ -697,7 +697,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 + ":" + str(aconf.port), banner, cves, kex=kex, client_host=client_host, software=software, algorithms=algs, algorithm_recommendation_suppress_list=algorithm_recommendation_suppress_list), indent=4 if aconf.json_print_indent else None, sort_keys=True)) + out.info(json.dumps(build_struct(aconf.host + ":" + str(aconf.port), banner, cves, kex=kex, client_host=client_host, software=software, algorithms=algs, algorithm_recommendation_suppress_list=algorithm_recommendation_suppress_list, additional_notes=additional_notes), 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)) @@ -1033,7 +1033,7 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[. return aconf -def build_struct(target_host: str, banner: Optional['Banner'], cves: List[Dict[str, Union[str, float]]], kex: Optional['SSH2_Kex'] = None, pkm: Optional['SSH1_PublicKeyMessage'] = None, client_host: Optional[str] = None, software: Optional[Software] = None, algorithms: Optional[Algorithms] = None, algorithm_recommendation_suppress_list: Optional[List[str]] = None) -> Any: # pylint: disable=too-many-arguments +def build_struct(target_host: str, banner: Optional['Banner'], cves: List[Dict[str, Union[str, float]]], kex: Optional['SSH2_Kex'] = None, pkm: Optional['SSH1_PublicKeyMessage'] = None, client_host: Optional[str] = None, software: Optional[Software] = None, algorithms: Optional[Algorithms] = None, algorithm_recommendation_suppress_list: Optional[List[str]] = None, additional_notes: str = "") -> Any: # pylint: disable=too-many-arguments def fetch_notes(algorithm: str, alg_type: str) -> Dict[str, List[Optional[str]]]: '''Returns a dictionary containing the messages in the "fail", "warn", and "info" levels for this algorithm.''' @@ -1201,6 +1201,9 @@ def build_struct(target_host: str, banner: Optional['Banner'], cves: List[Dict[s # Add in the recommendations. res['recommendations'] = get_algorithm_recommendations(algorithms, algorithm_recommendation_suppress_list, software, for_server=True) + # Add in the additional notes. Currently just one string, but in the future this may grow to multiple strings. Hence, an array is needed to prevent future schema breakage. + res['additional_notes'] = [additional_notes] + return res diff --git a/test/docker/expected_results/dropbear_2019.78_test1.json b/test/docker/expected_results/dropbear_2019.78_test1.json index 171ab79..55dd8b6 100644 --- a/test/docker/expected_results/dropbear_2019.78_test1.json +++ b/test/docker/expected_results/dropbear_2019.78_test1.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_4.0p1_test1.json b/test/docker/expected_results/openssh_4.0p1_test1.json index 603f44e..f5735a9 100644 --- a/test/docker/expected_results/openssh_4.0p1_test1.json +++ b/test/docker/expected_results/openssh_4.0p1_test1.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "1.99", diff --git a/test/docker/expected_results/openssh_5.6p1_test1.json b/test/docker/expected_results/openssh_5.6p1_test1.json index 38035fb..53216e5 100644 --- a/test/docker/expected_results/openssh_5.6p1_test1.json +++ b/test/docker/expected_results/openssh_5.6p1_test1.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_5.6p1_test2.json b/test/docker/expected_results/openssh_5.6p1_test2.json index 962f3ec..a1dd987 100644 --- a/test/docker/expected_results/openssh_5.6p1_test2.json +++ b/test/docker/expected_results/openssh_5.6p1_test2.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_5.6p1_test3.json b/test/docker/expected_results/openssh_5.6p1_test3.json index 586e06b..2cbd316 100644 --- a/test/docker/expected_results/openssh_5.6p1_test3.json +++ b/test/docker/expected_results/openssh_5.6p1_test3.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_5.6p1_test4.json b/test/docker/expected_results/openssh_5.6p1_test4.json index c52386d..90f5fc6 100644 --- a/test/docker/expected_results/openssh_5.6p1_test4.json +++ b/test/docker/expected_results/openssh_5.6p1_test4.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_5.6p1_test5.json b/test/docker/expected_results/openssh_5.6p1_test5.json index 593323d..0749cd1 100644 --- a/test/docker/expected_results/openssh_5.6p1_test5.json +++ b/test/docker/expected_results/openssh_5.6p1_test5.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_8.0p1_test1.json b/test/docker/expected_results/openssh_8.0p1_test1.json index 8555655..350af5e 100644 --- a/test/docker/expected_results/openssh_8.0p1_test1.json +++ b/test/docker/expected_results/openssh_8.0p1_test1.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_8.0p1_test2.json b/test/docker/expected_results/openssh_8.0p1_test2.json index 02a5469..a05ae96 100644 --- a/test/docker/expected_results/openssh_8.0p1_test2.json +++ b/test/docker/expected_results/openssh_8.0p1_test2.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/openssh_8.0p1_test3.json b/test/docker/expected_results/openssh_8.0p1_test3.json index bd165a7..13a8130 100644 --- a/test/docker/expected_results/openssh_8.0p1_test3.json +++ b/test/docker/expected_results/openssh_8.0p1_test3.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": null, "protocol": "2.0", diff --git a/test/docker/expected_results/tinyssh_20190101_test1.json b/test/docker/expected_results/tinyssh_20190101_test1.json index f95391d..7cc7629 100644 --- a/test/docker/expected_results/tinyssh_20190101_test1.json +++ b/test/docker/expected_results/tinyssh_20190101_test1.json @@ -1,4 +1,7 @@ { + "additional_notes": [ + "" + ], "banner": { "comments": "", "protocol": "2.0",