Skip to content

Commit

Permalink
Listing built-in policies will now hide older versions, unless -v is …
Browse files Browse the repository at this point in the history
…used.
  • Loading branch information
jtesta committed Oct 11, 2024
1 parent 3220043 commit c0133a8
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 9 deletions.
44 changes: 36 additions & 8 deletions src/ssh_audit/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,18 +513,46 @@ def list_builtin_policies(verbose: bool) -> Tuple[List[str], List[str]]:
server_policy_descriptions = []
client_policy_descriptions = []

latest_server_policies: Dict[str, Dict[str, Union[int, str]]] = {}
latest_client_policies: Dict[str, Dict[str, Union[int, str]]] = {}
for policy_name, policy in BUILTIN_POLICIES.items():
policy_description = ""
if verbose:
policy_description = "\"{:s}\": {:s}".format(policy_name, policy['changelog'])
else:

# If not in verbose mode, only store the latest version of each policy.
if not verbose:
policy_description = "\"{:s}\"".format(policy_name)

if policy['server_policy']:
server_policy_descriptions.append(policy_description)
else:
client_policy_descriptions.append(policy_description)
# Truncate the version off the policy name and obtain the version as an integer. (i.e.: "Platform X (version 3)" -> "Platform X", 3
policy_name_no_version = ""
version = 0
version_pos = policy_name.find(" (version ")
if version_pos != -1:
policy_name_no_version = policy_name[0:version_pos]
version = int(cast(str, policy['version'])) # Unit tests guarantee this to be parseable as an int.

d = latest_server_policies if policy['server_policy'] else latest_client_policies
if policy_name_no_version not in d:
d[policy_name_no_version] = {}
d[policy_name_no_version]['latest_version'] = version
d[policy_name_no_version]['description'] = policy_description
elif version > cast(int, d[policy_name_no_version]['latest_version']): # If an updated version of the policy was found, replace the old one.
d[policy_name_no_version]['latest_version'] = version
d[policy_name_no_version]['description'] = policy_description
else: # In verbose mode, return all policy versions.
policy_description = "\"{:s}\": {:s}".format(policy_name, policy['changelog'])
if policy['server_policy']:
server_policy_descriptions.append(policy_description)
else:
client_policy_descriptions.append(policy_description)

# Now that we have references to the latest policies only, add their full descriptions to the lists for returning.
if not verbose:
for _, dd in latest_server_policies.items():
server_policy_descriptions.append(cast(str, dd['description']))

for _, dd in latest_client_policies.items():
client_policy_descriptions.append(cast(str, dd['description']))

# Sort the lists for better readability.
server_policy_descriptions.sort()
client_policy_descriptions.sort()
return server_policy_descriptions, client_policy_descriptions
Expand Down
2 changes: 1 addition & 1 deletion src/ssh_audit/ssh_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def list_policies(out: OutputBuffer, verbose: bool) -> None:
out.fail("Error: no built-in policies found!")
else:
out.info("\nHint: Use -P and provide the full name of a policy to run a policy scan with.\n")
out.info("Hint: Use -L -v to also see the change log for each policy.\n")
out.info("Hint: Use -L -v to see the change log for each policy, as well as previous versions.\n")
out.info("Note: the general OpenSSH policies apply to the official releases only. OS distributions may back-port changes that cause failures (for example, Debian 11 back-ported the strict KEX mode into their package of OpenSSH v8.4, whereas it was only officially added to OpenSSH v9.6 and later). In these cases, consider creating a custom policy (-M option).\n")
out.info("Note: instructions for hardening targets, which correspond to the above policies, can be found at: <https://ssh-audit.com/hardening_guides.html>\n")
out.write()
Expand Down
8 changes: 8 additions & 0 deletions test/test_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ def test_builtin_policy_consistency(self):
version_str = " (version %s)" % BUILTIN_POLICIES[policy_name]['version']
assert policy_name.endswith(version_str)

# Ensure version field is a string, but can be parsed as an integer.
version_field = BUILTIN_POLICIES[policy_name]['version']
assert type(version_field) is str
try:
int(version_field)
except ValueError:
assert False, "version field of %s policy is not parseable as an integer." % policy_name

# Ensure no extra fields are present.
assert len(required_fields) == len(BUILTIN_POLICIES[policy_name])

Expand Down

0 comments on commit c0133a8

Please sign in to comment.