From 0f576e8c5bd53bd2e4f07dafc764abb929776704 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 21 Oct 2024 15:25:19 -0400 Subject: [PATCH 1/3] feat(patch): introduce net localgroup, net user, and route print parsers --- jc/lib.py | 3 + jc/parsers/net_localgroup.py | 208 +++++++ jc/parsers/net_user.py | 439 ++++++++++++++ jc/parsers/route_print.py | 565 ++++++++++++++++++ .../net_localgroup.administrators.json | 14 + .../net_localgroup.administrators.out | 9 + .../windows/windows-10/net_localgroup.json | 87 +++ .../windows/windows-10/net_localgroup.out | 25 + .../windows-10/net_user.administrator.json | 24 + .../windows-10/net_user.administrator.out | 25 + .../fixtures/windows/windows-10/net_user.json | 24 + .../fixtures/windows/windows-10/net_user.out | 7 + .../windows/windows-10/route_print.json | 241 ++++++++ .../windows/windows-10/route_print.out | 59 ++ .../net_localgroup.administrators.json | 14 + .../net_localgroup.administrators.out | 9 + .../windows/windows-11/net_localgroup.json | 63 ++ .../windows/windows-11/net_localgroup.out | 19 + .../windows-11/net_user.administrator.json | 24 + .../windows-11/net_user.administrator.out | 25 + .../fixtures/windows/windows-11/net_user.json | 21 + .../fixtures/windows/windows-11/net_user.out | 6 + .../windows/windows-11/route_print.json | 312 ++++++++++ .../windows/windows-11/route_print.out | 73 +++ .../net_localgroup.administrators.json | 15 + .../net_localgroup.administrators.out | 10 + .../windows/windows-2008/net_localgroup.json | 71 +++ .../windows/windows-2008/net_localgroup.out | 21 + .../windows-2008/net_user.administrator.json | 23 + .../windows-2008/net_user.administrator.out | 26 + .../windows/windows-2008/net_user.json | 18 + .../windows/windows-2008/net_user.out | 7 + .../windows/windows-2008/route_print.json | 143 +++++ .../windows/windows-2008/route_print.out | 41 ++ .../net_localgroup.administrators.json | 18 + .../net_localgroup.administrators.out | 13 + .../windows/windows-2016/net_localgroup.json | 103 ++++ .../windows/windows-2016/net_localgroup.out | 28 + .../windows-2016/net_user.administrator.json | 0 .../windows-2016/net_user.administrators.json | 24 + .../windows-2016/net_user.administrators.out | 25 + .../windows/windows-2016/net_user.json | 18 + .../windows/windows-2016/net_user.out | 7 + .../windows/windows-2016/route_print.json | 143 +++++ .../windows/windows-2016/route_print.out | 41 ++ .../net_localgroup.administrators.json | 18 + .../net_localgroup.administrators.out | 13 + .../windows/windows-7/net_localgroup.json | 67 +++ .../windows/windows-7/net_localgroup.out | 20 + .../windows-7/net_user.administrator.json | 23 + .../windows-7/net_user.administrator.out | 25 + .../fixtures/windows/windows-7/net_user.json | 15 + tests/fixtures/windows/windows-7/net_user.out | 6 + .../windows/windows-7/route_print.json | 136 +++++ .../windows/windows-7/route_print.out | 39 ++ .../net_localgroup.administrators.json | 14 + .../net_localgroup.administrators.out | 9 + .../windows/windows-xp/net_localgroup.json | 55 ++ .../windows/windows-xp/net_localgroup.out | 17 + .../windows-xp/net_user.administrator.json | 23 + .../windows-xp/net_user.administrator.out | 25 + .../fixtures/windows/windows-xp/net_user.json | 15 + .../fixtures/windows/windows-xp/net_user.out | 6 + .../windows/windows-xp/route_print.json | 86 +++ .../windows/windows-xp/route_print.out | 22 + tests/test_net_localgroup.py | 55 ++ tests/test_net_user.py | 56 ++ tests/test_route_print.py | 50 ++ 68 files changed, 3886 insertions(+) create mode 100644 jc/parsers/net_localgroup.py create mode 100644 jc/parsers/net_user.py create mode 100644 jc/parsers/route_print.py create mode 100644 tests/fixtures/windows/windows-10/net_localgroup.administrators.json create mode 100644 tests/fixtures/windows/windows-10/net_localgroup.administrators.out create mode 100644 tests/fixtures/windows/windows-10/net_localgroup.json create mode 100644 tests/fixtures/windows/windows-10/net_localgroup.out create mode 100644 tests/fixtures/windows/windows-10/net_user.administrator.json create mode 100644 tests/fixtures/windows/windows-10/net_user.administrator.out create mode 100644 tests/fixtures/windows/windows-10/net_user.json create mode 100644 tests/fixtures/windows/windows-10/net_user.out create mode 100644 tests/fixtures/windows/windows-10/route_print.json create mode 100644 tests/fixtures/windows/windows-10/route_print.out create mode 100644 tests/fixtures/windows/windows-11/net_localgroup.administrators.json create mode 100644 tests/fixtures/windows/windows-11/net_localgroup.administrators.out create mode 100644 tests/fixtures/windows/windows-11/net_localgroup.json create mode 100644 tests/fixtures/windows/windows-11/net_localgroup.out create mode 100644 tests/fixtures/windows/windows-11/net_user.administrator.json create mode 100644 tests/fixtures/windows/windows-11/net_user.administrator.out create mode 100644 tests/fixtures/windows/windows-11/net_user.json create mode 100644 tests/fixtures/windows/windows-11/net_user.out create mode 100644 tests/fixtures/windows/windows-11/route_print.json create mode 100644 tests/fixtures/windows/windows-11/route_print.out create mode 100644 tests/fixtures/windows/windows-2008/net_localgroup.administrators.json create mode 100644 tests/fixtures/windows/windows-2008/net_localgroup.administrators.out create mode 100644 tests/fixtures/windows/windows-2008/net_localgroup.json create mode 100644 tests/fixtures/windows/windows-2008/net_localgroup.out create mode 100644 tests/fixtures/windows/windows-2008/net_user.administrator.json create mode 100644 tests/fixtures/windows/windows-2008/net_user.administrator.out create mode 100644 tests/fixtures/windows/windows-2008/net_user.json create mode 100644 tests/fixtures/windows/windows-2008/net_user.out create mode 100644 tests/fixtures/windows/windows-2008/route_print.json create mode 100644 tests/fixtures/windows/windows-2008/route_print.out create mode 100644 tests/fixtures/windows/windows-2016/net_localgroup.administrators.json create mode 100644 tests/fixtures/windows/windows-2016/net_localgroup.administrators.out create mode 100644 tests/fixtures/windows/windows-2016/net_localgroup.json create mode 100644 tests/fixtures/windows/windows-2016/net_localgroup.out create mode 100644 tests/fixtures/windows/windows-2016/net_user.administrator.json create mode 100644 tests/fixtures/windows/windows-2016/net_user.administrators.json create mode 100644 tests/fixtures/windows/windows-2016/net_user.administrators.out create mode 100644 tests/fixtures/windows/windows-2016/net_user.json create mode 100644 tests/fixtures/windows/windows-2016/net_user.out create mode 100644 tests/fixtures/windows/windows-2016/route_print.json create mode 100644 tests/fixtures/windows/windows-2016/route_print.out create mode 100644 tests/fixtures/windows/windows-7/net_localgroup.administrators.json create mode 100644 tests/fixtures/windows/windows-7/net_localgroup.administrators.out create mode 100644 tests/fixtures/windows/windows-7/net_localgroup.json create mode 100644 tests/fixtures/windows/windows-7/net_localgroup.out create mode 100644 tests/fixtures/windows/windows-7/net_user.administrator.json create mode 100644 tests/fixtures/windows/windows-7/net_user.administrator.out create mode 100644 tests/fixtures/windows/windows-7/net_user.json create mode 100644 tests/fixtures/windows/windows-7/net_user.out create mode 100644 tests/fixtures/windows/windows-7/route_print.json create mode 100644 tests/fixtures/windows/windows-7/route_print.out create mode 100644 tests/fixtures/windows/windows-xp/net_localgroup.administrators.json create mode 100644 tests/fixtures/windows/windows-xp/net_localgroup.administrators.out create mode 100644 tests/fixtures/windows/windows-xp/net_localgroup.json create mode 100644 tests/fixtures/windows/windows-xp/net_localgroup.out create mode 100644 tests/fixtures/windows/windows-xp/net_user.administrator.json create mode 100644 tests/fixtures/windows/windows-xp/net_user.administrator.out create mode 100644 tests/fixtures/windows/windows-xp/net_user.json create mode 100644 tests/fixtures/windows/windows-xp/net_user.out create mode 100644 tests/fixtures/windows/windows-xp/route_print.json create mode 100644 tests/fixtures/windows/windows-xp/route_print.out create mode 100644 tests/test_net_localgroup.py create mode 100644 tests/test_net_user.py create mode 100644 tests/test_route_print.py diff --git a/jc/lib.py b/jc/lib.py index 0b4341ba4..d7b2a9894 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -102,6 +102,8 @@ 'mpstat-s', 'needrestart', 'netstat', + 'net-localgroup', + 'net-user', 'nmcli', 'nsd-control', 'ntpq', @@ -178,6 +180,7 @@ 'ps', 'resolve-conf', 'route', + 'route-print', 'rpm-qi', 'rsync', 'rsync-s', diff --git a/jc/parsers/net_localgroup.py b/jc/parsers/net_localgroup.py new file mode 100644 index 000000000..6bd204b45 --- /dev/null +++ b/jc/parsers/net_localgroup.py @@ -0,0 +1,208 @@ +r"""jc - JSON Convert `net localgroup` command output parser + + +Usage (cli): + + $ net localgroup | jc --net-localgroup + $ net localgroup /domain | jc --net-localgroup + $ net localgroup Administrators | jc --net-localgroup + $ net localgroup Administrators /domain | jc --net-localgroup + +Usage (module): + + import jc + result = jc.parse('net-localgroup', net_localgroup_command_output) + +Schema: + + { + "account_origin": string, + "domain": string, + "comment": string, + "groups": [ + { + "name": string + "members": [ + string + ] + } + ], + } + + + Notes: + [0] - 'lease_expires' and 'lease_obtained' are parsed to ISO8601 format date strings. if the value was unable + to be parsed by datetime, the fields will be in their raw form + [1] - 'autoconfigured' under 'ipv4_address' is only providing indication if the ipv4 address was labeled as + "Autoconfiguration IPv4 Address" vs "IPv4 Address". It does not infer any information from other fields + [2] - Windows XP uses 'IP Address' instead of 'IPv4 Address'. Both values are parsed to the 'ipv4_address' + object for consistency + +Examples: + + $ net localgroup | jc --net-localgroup -p | jq + { + "account_origin": null, + "comment": null, + "domain": null, + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "Operator", + "ansible", + "user1" + ] + } + ] + } + $ net localgroup Administrators | jc --net-localgroup -p | jq + $ net localgroup /domain | jc --net-localgroup -p | jq + +""" + +from datetime import datetime +import re +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`net localgroup` command parser' + author = 'joehacksalot' + author_email = 'joehacksalot@gmail.com' + compatible = ['windows'] + magic_commands = ['net-localgroup'] + tags = ['command'] + + +__version__ = info.version + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Parsed dictionary. The raw and processed data structures are the same. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output = {} + if jc.utils.has_data(data): + # Initialize the parsed output dictionary with all fields set to None or empty lists + raw_output = _parse(data) + + return raw_output if raw else _process(raw_output) + +def _process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (Dictionary) raw structured data to process + + Returns: + + Processed Dictionary. Structured data to conform to the schema. + """ + return proc_data # No further processing is needed + + +class _PushbackIterator: + def __init__(self, iterator): + self.iterator = iterator + self.pushback_stack = [] + def __iter__(self): + return self + def __next__(self): + if self.pushback_stack: + return self.pushback_stack.pop() + else: + return next(self.iterator) + def pushback(self, value): + self.pushback_stack.append(value) + +def _parse_net_localgroup_list(line_iter, expect_asterisk): + name_list = [] + while True: + try: + line = next(line_iter) + if not line.strip(): + continue # Skip empty lines + + # Check if the line starts with an asterisk + if line == 'The command completed successfully.': + break + elif expect_asterisk and line.startswith('*'): + name_list.append(line[1:].strip()) + else: + name_list.append(line) + except StopIteration: + break + return name_list + + +def _parse(data): + lines = data.splitlines() + parse_type = None # Can be 'groups_list' or 'members' + + result = { + "account_origin": None, + "domain": None, + "comment": None, + "groups": [] + } + + group_name = "" + lines = data.splitlines() + lines = [line.rstrip() for line in lines if line.strip() != ""] + + line_iter = _PushbackIterator(iter(lines)) + for line in line_iter: + line = line.rstrip() + + # Skip empty lines + if not line.strip(): + continue + + match_domain_processed = re.match(r"^The request will be processed at a domain controller for domain (.+)", line, re.IGNORECASE) + match_localgroup_list = re.match(r"^Aliases for[\s]*([^:]+)", line, re.IGNORECASE) # "Aliases for \\DESKTOP-WIN11:" + match_localgroup_members = re.match(r"^Alias name[\s]*([^:]+)", line, re.IGNORECASE) # "Alias name administrators:" + if match_domain_processed: + # Extract the domain name + result["domain"] = match_domain_processed.group(1).strip() + elif match_localgroup_list: + # Extract the account origin + result["account_origin"] = match_localgroup_list.group(1).strip() + parse_type = 'groups_list' # Prepare to read groups + elif match_localgroup_members: + # We are querying a specific group + group_name = match_localgroup_members.group(1).strip() + parse_type = 'members_list' # Prepare to read members + elif line.startswith('Comment'): + comment_line = line.split('Comment', 1)[1].strip() + result["comment"] = comment_line if comment_line else None + elif line.startswith('---'): + # Start of a section (groups or members) + if parse_type == 'groups_list': + names_list = _parse_net_localgroup_list(line_iter, expect_asterisk=True) + result["groups"] = [{"name": group_name, "members": []} for group_name in names_list] + elif parse_type == 'members_list': + names_list = _parse_net_localgroup_list(line_iter, expect_asterisk=False) + result["groups"] = [{ + "name": group_name, + "members": names_list + }] + return result \ No newline at end of file diff --git a/jc/parsers/net_user.py b/jc/parsers/net_user.py new file mode 100644 index 000000000..21a97655e --- /dev/null +++ b/jc/parsers/net_user.py @@ -0,0 +1,439 @@ +r"""jc - JSON Convert `net user` command output parser + + +Usage (cli): + + $ net users | jc --net-user + $ net users /domain | jc --net-user + $ net users User1 | jc --net-user + $ net users User1 /domain | jc --net-user + +Usage (module): + + import jc + result = jc.parse('net-user', net_user_command_output) + +Schema: + + { + "domain": string, + "account_origin": string, + "user_accounts": [ + { + "user_name": string, + "full_name": string, + "comment": string, + "user_comment": string, + "country_region_code": string, + "account_active": boolean, + "account_expires": string, + "password_last_set": string, + "password_expires": string, + "password_changeable": string, + "password_required": boolean, + "user_may_change_password": boolean, + "workstations_allowed": string, + "logon_script": string, + "user_profile": string, + "home_directory": string, + "last_logon": string, + "logon_hours_allowed": string, + "local_group_memberships": [ + string, + ], + "global_group_memberships": [ + string, + ] + } + ] + } + + +Examples: + + $ net users | jc --net-user -p | jq + { + "account_origin": "\\\\WIN-SERVER16", + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "DefaultAccount" + }, + { + "user_name": "Guest" + }, + { + "user_name": "pentera_BnlLQVnd7p" + }, + { + "user_name": "user1" + } + ] + } + + $ net users /domain | jc --net-user -p | jq + { + "account_origin": "\\\\DESKTOP-WIN10-PRO.somecompany.corp", + "domain": "somecompany.corp", + "user_accounts": [ + { + "user_name": "aaron" + }, + { + "user_name": "addison" + }, + { + "user_name": "Administrator" + }, + { + "user_name": "ansible" + }, + { + "user_name": "da" + }, + { + "user_name": "DefaultAccount" + }, + { + "user_name": "Guest" + }, + { + "user_name": "harrison" + }, + { + "user_name": "james" + }, + { + "user_name": "krbtgt" + }, + { + "user_name": "liam" + }, + { + "user_name": "localadmin" + }, + { + "user_name": "tiffany" + } + ] + } + + $ net users Administrator | jc --net-user -p | jq + { + "domain": "", + "user_accounts": [ + { + "account_active": true, + "account_expires": "Never", + "comment": "Built-in account for administering the computer/domain", + "country_region_code": "000 (System Default)", + "global_group_memberships": [], + "last_logon": "2024-08-23T13:47:11", + "local_group_memberships": [ + "Administrators" + ], + "logon_hours_allowed": "All", + "password_changeable": "2021-12-17T11:07:14", + "password_expires": "2022-01-27T11:07:14", + "password_last_set": "2021-12-16T11:07:14", + "password_required": true, + "user_may_change_password": true, + "user_name": "Administrators", + "workstations_allowed": "All" + } + ] + } + + $ net users Administrator /domain | jc --net-user -p | jq + { + "domain": "somecompany.corp", + "user_accounts": [ + { + "account_active": true, + "account_expires": "Never", + "comment": "Built-in account for administering the computer/domain", + "country_region_code": "000 (System Default)", + "global_group_memberships": [ + "Domain Admins", + "Domain Users", + "Group Policy Creator", + "Enterprise Admins", + "Schema Admins" + ], + "last_logon": "2024-07-17T13:46:12", + "local_group_memberships": [ + "Administrators" + ], + "logon_hours_allowed": "All", + "password_changeable": "2023-09-30T11:44:26", + "password_expires": "Never", + "password_last_set": "2023-09-29T11:44:26", + "password_required": true, + "user_may_change_password": true, + "user_name": "Administrators", + "workstations_allowed": "All" + } + ] + } +""" + + +from datetime import datetime +import re +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`net user` command parser' + author = 'joehacksalot' + author_email = 'joehacksalot@gmail.com' + compatible = ['windows'] + magic_commands = ['net-user'] + tags = ['command'] + + +__version__ = info.version + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Parsed dictionary. The raw and processed data structures are the same. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output = {} + if jc.utils.has_data(data): + try: + raw_output = _parse(data) + return raw_output if raw else _process(raw_output) + except Exception as e: + if not quiet: + jc.utils.warning_message(['Could not parse data due to unexpected format.']) + return {} + +def _set_if_not_none(output_dict, key, value): + if value is not None: + output_dict[key] = value + +def _process_string_is_yes(text): + if text: + return text.lower() == "yes" + else: + return None + +def _process_date(s): + if s is not None: + for fmt in ('%m/%d/%Y %I:%M:%S %p', '%m/%d/%Y %H:%M:%S %p'): + try: + return datetime.strptime(s, fmt).isoformat() + except ValueError: + continue + return s # Return the original string if parsing fails + +def _process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (Dictionary) raw structured data to process + + Returns: + + Processed Dictionary. Structured data to conform to the schema. + """ + for user_account in proc_data.get("user_accounts", []): + _set_if_not_none(user_account, "account_active", _process_string_is_yes(user_account.get("account_active", None))) + _set_if_not_none(user_account, "password_last_set", _process_date(user_account.get("password_last_set", None))) + _set_if_not_none(user_account, "password_expires", _process_date(user_account.get("password_expires", None))) + _set_if_not_none(user_account, "password_changeable", _process_date(user_account.get("password_changeable", None))) + _set_if_not_none(user_account, "password_required", _process_string_is_yes(user_account.get("password_required", None))) + _set_if_not_none(user_account, "user_may_change_password", _process_string_is_yes(user_account.get("user_may_change_password", None))) + _set_if_not_none(user_account, "last_logon", _process_date(user_account.get("last_logon", None))) + return proc_data # No further processing is needed + + +class _PushbackIterator: + def __init__(self, iterator): + self.iterator = iterator + self.pushback_stack = [] + def __iter__(self): + return self + def __next__(self): + if self.pushback_stack: + return self.pushback_stack.pop() + else: + return next(self.iterator) + def pushback(self, value): + self.pushback_stack.append(value) + +def _parse_user_account_keypairs(keypair_dict): + user_account_parsed = {} + _set_if_not_none(user_account_parsed, "user_name", keypair_dict.get("user_name", None)) + _set_if_not_none(user_account_parsed, "full_name", keypair_dict.get("full_name", None)) + _set_if_not_none(user_account_parsed, "comment", keypair_dict.get("comment", None)) + _set_if_not_none(user_account_parsed, "users_comment", keypair_dict.get("users_comment", None)) + _set_if_not_none(user_account_parsed, "country_region_code", keypair_dict.get("country_region_code", None)) + _set_if_not_none(user_account_parsed, "account_active", keypair_dict.get("account_active", None)) + _set_if_not_none(user_account_parsed, "account_expires", keypair_dict.get("account_expires", None)) + _set_if_not_none(user_account_parsed, "password_last_set", keypair_dict.get("password_last_set", None)) + _set_if_not_none(user_account_parsed, "password_expires", keypair_dict.get("password_expires", None)) + _set_if_not_none(user_account_parsed, "password_changeable", keypair_dict.get("password_changeable", None)) + _set_if_not_none(user_account_parsed, "password_required", keypair_dict.get("password_required", None)) + _set_if_not_none(user_account_parsed, "user_may_change_password", keypair_dict.get("user_may_change_password", None)) + _set_if_not_none(user_account_parsed, "workstations_allowed", keypair_dict.get("workstations_allowed", None)) + _set_if_not_none(user_account_parsed, "logon_script", keypair_dict.get("logon_script", None)) + _set_if_not_none(user_account_parsed, "user_profile", keypair_dict.get("user_profile", None)) + _set_if_not_none(user_account_parsed, "home_directory", keypair_dict.get("home_directory", None)) + _set_if_not_none(user_account_parsed, "last_logon", keypair_dict.get("last_logon", None)) + _set_if_not_none(user_account_parsed, "logon_hours_allowed", keypair_dict.get("logon_hours_allowed", None)) + _set_if_not_none(user_account_parsed, "local_group_memberships", keypair_dict.get("local_group_memberships", None)) + _set_if_not_none(user_account_parsed, "global_group_memberships", keypair_dict.get("global_group_memberships", None)) + return user_account_parsed + + +def _parse_groups(line_iter): + group_list = [] + # Process additional lines that belong to the current entry (e.g., additional DNS servers, DNS Suffix Search List) + while True: + try: + next_line = next(line_iter).strip() + if not next_line: + continue # Skip empty lines + + # Check if the line is indented (starts with whitespace) + if next_line.startswith('*'): + groups = next_line.split("*") + groups = [group.strip() for group in groups if group.strip() != ""] + if "None" in groups: + groups.remove("None") + # It's an indented line; append the stripped line to entry_list + group_list.extend(groups) + else: + # Not an indented line; push it back and exit + line_iter.pushback(next_line) + break + except StopIteration: + break + return group_list + +def _parse(data): + result = { + "domain": "", + "user_accounts": [] + } + + lines = data.splitlines() + lines = [line.rstrip() for line in lines if line.strip() != ""] + + line_iter = _PushbackIterator(iter(lines)) + for line in line_iter: + line = line.rstrip() + + # Skip empty lines + if not line.strip(): + continue + + match_domain_processed = re.match(r"^The request will be processed at a domain controller for domain (.+)\.$", line, re.IGNORECASE) + if match_domain_processed: + result["domain"] = match_domain_processed.group(1).strip() + # Check if the text is of the first type (detailed user info) + elif "User name" in line: + line_iter.pushback(line) + user_account_keypairs = {} + + # Regular expression to match key-value pairs + kv_pattern = re.compile(r'^([\w\s\/\'\-]{1,29})\s*(.+)?$') + key = None + + while True: + # Process each line + # Break when done + try: + line = next(line_iter) + line = line.strip() + if not line: + continue # Skip empty lines + + match = kv_pattern.match(line) + if "The command completed" in line: + break + elif match: + key_raw = match.group(1).strip() + key = key_raw.lower().replace(" ", "_").replace("'", "").replace("/", "_").replace("-", "_") + if len(match.groups()) == 2 and match.group(2) is not None: + value = match.group(2).strip() + if key in ["local_group_memberships", "global_group_memberships"]: + line_iter.pushback(value) + user_account_keypairs[key] = _parse_groups(line_iter) + else: + user_account_keypairs[key] = value + else: + # Line without value, it's a key with empty value + user_account_keypairs[key] = None + else: + raise ValueError(f"Unexpected line: {line}") + except StopIteration: + break + + # Convert specific fields + result["user_accounts"].append(_parse_user_account_keypairs(user_account_keypairs)) + elif "User accounts for" in line: + line_iter.pushback(line) + collecting_users = False + + while True: + # Process each line + # Break when done + try: + line = next(line_iter) + line = line.strip() + if not line: + continue # Skip empty lines + + # Check for domain line + domain_pattern = re.compile(r'^User accounts for (.+)$') + account_origin_match = domain_pattern.match(line) + if account_origin_match: + result["account_origin"] = account_origin_match.group(1) + continue + + # Check for the line of dashes indicating start of user list + if line.startswith('---'): + collecting_users = True + continue + + # Check for the completion message + if line.startswith('The command completed'): + break + + if collecting_users: + # Split the line into usernames + user_matches = re.match(r'(.{1,20})\s+(.{1,20})\s+(.{1,20})', line) + if user_matches: + for username in user_matches.groups(): + username = username.strip() + user_account = {"user_name": username} + result["user_accounts"].append(user_account) + except StopIteration: + break + else: + raise ValueError(f"Unexpected line: {line}") + + return result \ No newline at end of file diff --git a/jc/parsers/route_print.py b/jc/parsers/route_print.py new file mode 100644 index 000000000..da7845383 --- /dev/null +++ b/jc/parsers/route_print.py @@ -0,0 +1,565 @@ +r"""jc - JSON Convert `route-print` command output parser + + +Usage (cli): + + $ route print | jc --route-print + +Usage (module): + + import jc + result = jc.parse('route-print', route_print_command_output) + +Schema: + + { + "interface_list": [ + { + "interface_index": integer, + "mac_address": string, + "description": string + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": string, + "netmask": string, + "gateway": string, + "interface": string, + "metric": string + } + ], + "persistent_routes": [ + { + "network_address": string, + "netmask": string, + "gateway_address": string, + "metric": string + } + ] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": integer, + "metric": string, + "network_destination": string, + "gateway": string + } + ], + "persistent_routes": [ + { + "interface": integer, + "metric": string, + "network_destination": string, + "gateway": string + } + ] + } + } + + Notes: + - The `metric` field is typically an integer but can sometimes be set to "Default" + + +Examples: + + $ route print | jc --route-print -p | jq + + { + "interface_list": [ + { + "interface_index": 28, + "mac_address": null, + "description": "Tailscale Tunnel" + }, + { + "interface_index": 12, + "mac_address": "00:1c:42:da:01:6a", + "description": "Parallels VirtIO Ethernet Adapter" + }, + { + "interface_index": 1, + "mac_address": null, + "description": "Software Loopback Interface 1" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "10.211.55.1", + "interface": "10.211.55.3", + "metric": "15" + }, + { + "network_destination": "10.0.0.0", + "netmask": "255.0.0.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.0.1.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.0.3.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.0.4.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.211.55.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "10.211.55.3", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "10.211.55.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.0.0.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "169.254.0.0", + "netmask": "255.255.0.0", + "gateway": "On-link", + "interface": "169.254.83.107", + "metric": "261" + }, + { + "network_destination": "169.254.83.107", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "169.254.83.107", + "metric": "261" + }, + { + "network_destination": "169.254.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "169.254.83.107", + "metric": "261" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + } + ], + "persistent_routes": [ + { + "network_address": "10.0.1.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + }, + { + "network_address": "10.0.3.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + }, + { + "network_address": "10.0.4.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + }, + { + "network_address": "10.0.0.0", + "netmask": "255.0.0.0", + "gateway_address": "192.168.22.1", + "metric": "1" + } + ] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": 1, + "metric": "331", + "network_destination": "::1/128", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "2001:db8::/64", + "gateway": "fe80::1" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fdb2:2c26:f4e4::/64", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fdb2:2c26:f4e4:0:670f:fec7:75b7:eb83/128", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fdb2:2c26:f4e4:0:b4d5:8083:6ce6:37d/128", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fe80::4abd:fa70:3d36:83fd/128", + "gateway": "On-link" + }, + { + "interface": 1, + "metric": "331", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "ff00::/8", + "gateway": "On-link" + } + ], + "persistent_routes": [ + { + "interface": 0, + "metric": "4294967295", + "network_destination": "2001:db8::/64", + "gateway": "fe80::1" + } + ] + } + } +""" + +import re +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`route-print` command parser' + author = 'joehacksalot' + author_email = 'joehacksalot@gmail.com' + compatible = ['windows'] + magic_commands = ['route-print'] + tags = ['command'] + + +__version__ = info.version + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Parsed dictionary. The raw and processed data structures are the same. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output = {} + if jc.utils.has_data(data): + try: + raw_output = _parse(data) + return raw_output if raw else _process(raw_output) + except Exception as e: + if not quiet: + jc.utils.warning_message(['Could not parse data due to unexpected format.']) + return {} + +def _process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (Dictionary) raw structured data to process + + Returns: + + Processed Dictionary. Structured data to conform to the schema. + """ + for interface in proc_data['interface_list']: + if interface["mac_address"] == '' or interface["mac_address"] == '00 00 00 00 00 00 00 e0': # Placeholder MAC address for virtual adapters + mac_address = None + else: + mac_address = interface["mac_address"].replace(" ", ":") + interface["mac_address"] = mac_address + interface["interface_index"] = int(interface["interface_index"],0) # allow failure to convert to int exception to flow upwards + for ipv6_active_route in proc_data['ipv6_route_table']['active_routes']: + ipv6_active_route["interface"] = int(ipv6_active_route["interface"],0) + for ipv6_persistent_route in proc_data['ipv6_route_table']['persistent_routes']: + ipv6_persistent_route["interface"] = int(ipv6_persistent_route["interface"],0) + return proc_data # No further processing is needed + + +class _PushbackIterator: + """Iterator that allows pushing back values onto the iterator. Supports handing off + parsing to localized parsers while maintaining line synchonization.""" + + def __init__(self, iterator): + self.iterator = iterator + self.pushback_stack = [] + + def __iter__(self): + return self + + def __next__(self): + if self.pushback_stack: + return self.pushback_stack.pop() + else: + return next(self.iterator) + + def pushback(self, value): + self.pushback_stack.append(value) + + def contains(self, pattern): + iter_lines = list(self.iterator) + list_lines = self.pushback_stack.copy() + list_lines.extend(iter_lines) + self.iterator = iter(list_lines) + self.pushback_stack = [] + + # Check the pushback stack first + for line in list_lines: + if re.match(pattern, line): + return True + return False + + def skip_until(self, pattern): + for line in self: + if re.match(pattern, line): + return line + return None + + +def _parse_interface_list(data, lines_iter): + start_of_interface_list_pattern = r'^Interface List' + if lines_iter.contains(start_of_interface_list_pattern): + line = lines_iter.skip_until(start_of_interface_list_pattern) + for line in lines_iter: + if re.match(r'^=+$', line): + break # End of interface list + interface_index = line[:5].replace(".", "").strip() + mac_address = line[5:30].replace(".","").strip() + description = line[30:].strip() + data['interface_list'].append({ + "interface_index": interface_index, + "mac_address": mac_address, + "description": description + }) + +def _parse_ipv4_route_table(data, lines_iter): + def _parse_ipv4_active_routes(data, lines_iter): + line = lines_iter.skip_until(r'^Active Routes') + line = next(lines_iter, '') # Skip the header line + if line.strip() == 'None': + return + for line in lines_iter: + if re.match(r'^=+$', line): + break # End of interface list + if 'Default Gateway' in line: + continue + lines_split = line.split() + network_destination = lines_split[0] + netmask = lines_split[1] + gateway = lines_split[2] + interface = lines_split[3] + metric = lines_split[4] + data['ipv4_route_table']["active_routes"].append({ + "network_destination": network_destination, + "netmask": netmask, + "gateway": gateway, + "interface": interface, + "metric": metric + }) + + def _parse_ipv4_persistent_routes(data, lines_iter): + line = lines_iter.skip_until(r'^Persistent Routes') + line = next(lines_iter, '') # line is either "None" and we abort parsing this section or we skip header line + if line.strip() == 'None': + return + for line in lines_iter: + if re.match(r'^=+$', line): + break + lines_split = line.split() + network_address = lines_split[0] + netmask = lines_split[1] + gateway_address = lines_split[2] + metric = lines_split[3] + data['ipv4_route_table']["persistent_routes"].append({ + "network_address": network_address, + "netmask": netmask, + "gateway_address": gateway_address, + "metric": metric + }) + + start_of_ipv4_route_table_pattern = r'^IPv4 Route Table' + if lines_iter.contains(start_of_ipv4_route_table_pattern): + line = lines_iter.skip_until(start_of_ipv4_route_table_pattern) + line = next(lines_iter, '') # Skip the separator line + _parse_ipv4_active_routes(data, lines_iter) + _parse_ipv4_persistent_routes(data, lines_iter) + +def _parse_ipv6_route_table(data, lines_iter): + def _parse_ipv6_active_routes(data, lines_iter): + line = lines_iter.skip_until(r'^Active Routes') + line = next(lines_iter, '') # line is either "None" and we abort parsing this section or we skip header line + if line.strip() == 'None': + return + for line in lines_iter: + if re.match(r'^=+$', line): + break + split_line = line.split() + interface = split_line[0] + metric = split_line[1] + network_destination = split_line[2] + if len(split_line) > 3: + gateway = split_line[3] + else: + gateway = next(lines_iter, '').strip() + data['ipv6_route_table']["active_routes"].append({ + "interface": interface, + "metric": metric, + "network_destination": network_destination, + "gateway": gateway + }) + + def _parse_ipv6_persistent_routes(data, lines_iter): + line = lines_iter.skip_until(r'^Persistent Routes') + line = next(lines_iter, '') # line is either "None" and we abort parsing this section or we skip header line + if line.strip() == 'None': + return + for line in lines_iter: + if re.match(r'^=+$', line): + break + split_line = line.split() + interface = split_line[0] + metric = split_line[1] + network_destination = split_line[2] + if len(split_line) > 3: + gateway = split_line[3] + else: + gateway = next(lines_iter, '').strip() + data['ipv6_route_table']["persistent_routes"].append({ + "interface": interface, + "metric": metric, + "network_destination": network_destination, + "gateway": gateway + }) + + start_of_ipv6_route_table_pattern = r'^IPv6 Route Table' + if lines_iter.contains(start_of_ipv6_route_table_pattern): + line = lines_iter.skip_until(start_of_ipv6_route_table_pattern) + line = next(lines_iter, '') # Skip the separator line + _parse_ipv6_active_routes(data, lines_iter) + _parse_ipv6_persistent_routes(data, lines_iter) + + +def _parse(output): + data = { + "interface_list": [], + "ipv4_route_table": { + "active_routes": [], + "persistent_routes": [] + }, + "ipv6_route_table": { + "active_routes": [], + "persistent_routes": [] + } + } + + lines = output.splitlines() + _parse_interface_list(data, _PushbackIterator(iter(lines))) + _parse_ipv4_route_table(data, _PushbackIterator(iter(lines))) + _parse_ipv6_route_table(data, _PushbackIterator(iter(lines))) + + return data diff --git a/tests/fixtures/windows/windows-10/net_localgroup.administrators.json b/tests/fixtures/windows/windows-10/net_localgroup.administrators.json new file mode 100644 index 000000000..575bdcc91 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_localgroup.administrators.json @@ -0,0 +1,14 @@ +{ + "account_origin": null, + "domain": null, + "comment": "Administrators have complete and unrestricted access to the computer/domain", + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "user1" + ] + } + ] +} diff --git a/tests/fixtures/windows/windows-10/net_localgroup.administrators.out b/tests/fixtures/windows/windows-10/net_localgroup.administrators.out new file mode 100644 index 000000000..c8fec1202 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_localgroup.administrators.out @@ -0,0 +1,9 @@ +Alias name Administrators +Comment Administrators have complete and unrestricted access to the computer/domain + +Members + +------------------------------------------------------------------------------- +Administrator +user1 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-10/net_localgroup.json b/tests/fixtures/windows/windows-10/net_localgroup.json new file mode 100644 index 000000000..bedcfe7e9 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_localgroup.json @@ -0,0 +1,87 @@ +{ + "account_origin": "\\\\DESKTOP-WIN10-PRO", + "domain": null, + "comment": null, + "groups": [ + { + "name": "Access Control Assistance Operators", + "members": [] + }, + { + "name": "Administrators", + "members": [] + }, + { + "name": "Backup Operators", + "members": [] + }, + { + "name": "Cryptographic Operators", + "members": [] + }, + { + "name": "Device Owners", + "members": [] + }, + { + "name": "Distributed COM Users", + "members": [] + }, + { + "name": "docker-users", + "members": [] + }, + { + "name": "Event Log Readers", + "members": [] + }, + { + "name": "Guests", + "members": [] + }, + { + "name": "Hyper-V Administrators", + "members": [] + }, + { + "name": "IIS_IUSRS", + "members": [] + }, + { + "name": "Network Configuration Operators", + "members": [] + }, + { + "name": "Performance Log Users", + "members": [] + }, + { + "name": "Performance Monitor Users", + "members": [] + }, + { + "name": "Power Users", + "members": [] + }, + { + "name": "Remote Desktop Users", + "members": [] + }, + { + "name": "Remote Management Users", + "members": [] + }, + { + "name": "Replicator", + "members": [] + }, + { + "name": "System Managed Accounts Group", + "members": [] + }, + { + "name": "Users", + "members": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-10/net_localgroup.out b/tests/fixtures/windows/windows-10/net_localgroup.out new file mode 100644 index 000000000..3ea4397b4 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_localgroup.out @@ -0,0 +1,25 @@ + +Aliases for \\DESKTOP-WIN10-PRO + +------------------------------------------------------------------------------- +*Access Control Assistance Operators +*Administrators +*Backup Operators +*Cryptographic Operators +*Device Owners +*Distributed COM Users +*docker-users +*Event Log Readers +*Guests +*Hyper-V Administrators +*IIS_IUSRS +*Network Configuration Operators +*Performance Log Users +*Performance Monitor Users +*Power Users +*Remote Desktop Users +*Remote Management Users +*Replicator +*System Managed Accounts Group +*Users +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-10/net_user.administrator.json b/tests/fixtures/windows/windows-10/net_user.administrator.json new file mode 100644 index 000000000..417790969 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_user.administrator.json @@ -0,0 +1,24 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator", + "comment": "Built-in account for administering the computer/domain", + "country_region_code": "000 (System Default)", + "account_active": false, + "account_expires": "Never", + "password_last_set": "2022-04-04T20:06:30", + "password_expires": "Never", + "password_changeable": "2022-04-04T20:06:30", + "password_required": false, + "user_may_change_password": true, + "workstations_allowed": "All", + "last_logon": "Never", + "logon_hours_allowed": "All", + "local_group_memberships": [ + "Administrators" + ], + "global_group_memberships": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-10/net_user.administrator.out b/tests/fixtures/windows/windows-10/net_user.administrator.out new file mode 100644 index 000000000..f62934f06 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_user.administrator.out @@ -0,0 +1,25 @@ +User name Administrator +Full Name +Comment Built-in account for administering the computer/domain +User's comment +Country/region code 000 (System Default) +Account active No +Account expires Never + +Password last set 4/4/2022 8:06:30 PM +Password expires Never +Password changeable 4/4/2022 8:06:30 PM +Password required No +User may change password Yes + +Workstations allowed All +Logon script +User profile +Home directory +Last logon Never + +Logon hours allowed All + +Local Group Memberships *Administrators +Global Group memberships *None +The command completed successfully. diff --git a/tests/fixtures/windows/windows-10/net_user.json b/tests/fixtures/windows/windows-10/net_user.json new file mode 100644 index 000000000..8ea644f7a --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_user.json @@ -0,0 +1,24 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "DefaultAccount" + }, + { + "user_name": "Guest" + }, + { + "user_name": "user1" + }, + { + "user_name": "sshd" + }, + { + "user_name": "WDAGUtilityAccount" + } + ], + "account_origin": "\\\\DESKTOP-WIN10-PRO" +} diff --git a/tests/fixtures/windows/windows-10/net_user.out b/tests/fixtures/windows/windows-10/net_user.out new file mode 100644 index 000000000..190c9ae03 --- /dev/null +++ b/tests/fixtures/windows/windows-10/net_user.out @@ -0,0 +1,7 @@ + +User accounts for \\DESKTOP-WIN10-PRO + +------------------------------------------------------------------------------- +Administrator DefaultAccount Guest +user1 sshd WDAGUtilityAccount +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-10/route_print.json b/tests/fixtures/windows/windows-10/route_print.json new file mode 100644 index 000000000..0e1b08009 --- /dev/null +++ b/tests/fixtures/windows/windows-10/route_print.json @@ -0,0 +1,241 @@ +{ + "interface_list": [ + { + "interface_index": 28, + "mac_address": null, + "description": "Tailscale Tunnel" + }, + { + "interface_index": 12, + "mac_address": "00:1c:42:da:01:6a", + "description": "Parallels VirtIO Ethernet Adapter" + }, + { + "interface_index": 1, + "mac_address": null, + "description": "Software Loopback Interface 1" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "10.211.55.1", + "interface": "10.211.55.3", + "metric": "15" + }, + { + "network_destination": "10.0.0.0", + "netmask": "255.0.0.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.0.1.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.0.3.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.0.4.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "10.211.55.3", + "metric": "16" + }, + { + "network_destination": "10.211.55.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "10.211.55.3", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "10.211.55.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.0.0.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "169.254.0.0", + "netmask": "255.255.0.0", + "gateway": "On-link", + "interface": "169.254.83.107", + "metric": "261" + }, + { + "network_destination": "169.254.83.107", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "169.254.83.107", + "metric": "261" + }, + { + "network_destination": "169.254.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "169.254.83.107", + "metric": "261" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "10.211.55.3", + "metric": "271" + } + ], + "persistent_routes": [ + { + "network_address": "10.0.1.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + }, + { + "network_address": "10.0.3.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + }, + { + "network_address": "10.0.4.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + }, + { + "network_address": "10.0.0.0", + "netmask": "255.0.0.0", + "gateway_address": "192.168.22.1", + "metric": "1" + } + ] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": 1, + "metric": "331", + "network_destination": "::1/128", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "2001:db8::/64", + "gateway": "fe80::1" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fdb2:2c26:f4e4::/64", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fdb2:2c26:f4e4:0:670f:fec7:75b7:eb83/128", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fdb2:2c26:f4e4:0:b4d5:8083:6ce6:37d/128", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "fe80::4abd:fa70:3d36:83fd/128", + "gateway": "On-link" + }, + { + "interface": 1, + "metric": "331", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 12, + "metric": "271", + "network_destination": "ff00::/8", + "gateway": "On-link" + } + ], + "persistent_routes": [ + { + "interface": 0, + "metric": "4294967295", + "network_destination": "2001:db8::/64", + "gateway": "fe80::1" + } + ] + } +} diff --git a/tests/fixtures/windows/windows-10/route_print.out b/tests/fixtures/windows/windows-10/route_print.out new file mode 100644 index 000000000..b3ffe2ea0 --- /dev/null +++ b/tests/fixtures/windows/windows-10/route_print.out @@ -0,0 +1,59 @@ +=========================================================================== +Interface List + 28...........................Tailscale Tunnel + 12...00 1c 42 da 01 6a ......Parallels VirtIO Ethernet Adapter + 1...........................Software Loopback Interface 1 +=========================================================================== + +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 10.211.55.1 10.211.55.3 15 + 10.0.0.0 255.0.0.0 192.168.22.1 10.211.55.3 16 + 10.0.1.0 255.255.255.0 192.168.22.1 10.211.55.3 16 + 10.0.3.0 255.255.255.0 192.168.22.1 10.211.55.3 16 + 10.0.4.0 255.255.255.0 192.168.22.1 10.211.55.3 16 + 10.211.55.0 255.255.255.0 On-link 10.211.55.3 271 + 10.211.55.3 255.255.255.255 On-link 10.211.55.3 271 + 10.211.55.255 255.255.255.255 On-link 10.211.55.3 271 + 127.0.0.0 255.0.0.0 On-link 127.0.0.1 331 + 127.0.0.1 255.255.255.255 On-link 127.0.0.1 331 + 127.255.255.255 255.255.255.255 On-link 127.0.0.1 331 + 169.254.0.0 255.255.0.0 On-link 169.254.83.107 261 + 169.254.83.107 255.255.255.255 On-link 169.254.83.107 261 + 169.254.255.255 255.255.255.255 On-link 169.254.83.107 261 + 224.0.0.0 240.0.0.0 On-link 127.0.0.1 331 + 224.0.0.0 240.0.0.0 On-link 10.211.55.3 271 + 255.255.255.255 255.255.255.255 On-link 127.0.0.1 331 + 255.255.255.255 255.255.255.255 On-link 10.211.55.3 271 +=========================================================================== +Persistent Routes: + Network Address Netmask Gateway Address Metric + 10.0.1.0 255.255.255.0 192.168.22.1 1 + 10.0.3.0 255.255.255.0 192.168.22.1 1 + 10.0.4.0 255.255.255.0 192.168.22.1 1 + 10.0.0.0 255.0.0.0 192.168.22.1 1 +=========================================================================== + +IPv6 Route Table +=========================================================================== +Active Routes: + If Metric Network Destination Gateway + 1 331 ::1/128 On-link + 12 271 2001:db8::/64 fe80::1 + 12 271 fdb2:2c26:f4e4::/64 On-link + 12 271 fdb2:2c26:f4e4:0:670f:fec7:75b7:eb83/128 + On-link + 12 271 fdb2:2c26:f4e4:0:b4d5:8083:6ce6:37d/128 + On-link + 12 271 fe80::/64 On-link + 12 271 fe80::4abd:fa70:3d36:83fd/128 + On-link + 1 331 ff00::/8 On-link + 12 271 ff00::/8 On-link +=========================================================================== +Persistent Routes: + If Metric Network Destination Gateway + 0 4294967295 2001:db8::/64 fe80::1 +=========================================================================== diff --git a/tests/fixtures/windows/windows-11/net_localgroup.administrators.json b/tests/fixtures/windows/windows-11/net_localgroup.administrators.json new file mode 100644 index 000000000..575bdcc91 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_localgroup.administrators.json @@ -0,0 +1,14 @@ +{ + "account_origin": null, + "domain": null, + "comment": "Administrators have complete and unrestricted access to the computer/domain", + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "user1" + ] + } + ] +} diff --git a/tests/fixtures/windows/windows-11/net_localgroup.administrators.out b/tests/fixtures/windows/windows-11/net_localgroup.administrators.out new file mode 100644 index 000000000..c8fec1202 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_localgroup.administrators.out @@ -0,0 +1,9 @@ +Alias name Administrators +Comment Administrators have complete and unrestricted access to the computer/domain + +Members + +------------------------------------------------------------------------------- +Administrator +user1 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-11/net_localgroup.json b/tests/fixtures/windows/windows-11/net_localgroup.json new file mode 100644 index 000000000..a2ba58b02 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_localgroup.json @@ -0,0 +1,63 @@ +{ + "account_origin": "\\\\DESKTOP-WIN11-HOME", + "domain": null, + "comment": null, + "groups": [ + { + "name": "__vmware__", + "members": [] + }, + { + "name": "Administrators", + "members": [] + }, + { + "name": "Device Owners", + "members": [] + }, + { + "name": "Distributed COM Users", + "members": [] + }, + { + "name": "docker-users", + "members": [] + }, + { + "name": "Event Log Readers", + "members": [] + }, + { + "name": "Guests", + "members": [] + }, + { + "name": "Hyper-V Administrators", + "members": [] + }, + { + "name": "IIS_IUSRS", + "members": [] + }, + { + "name": "Performance Log Users", + "members": [] + }, + { + "name": "Performance Monitor Users", + "members": [] + }, + { + "name": "Remote Management Users", + "members": [] + }, + { + "name": "System Managed Accounts Group", + "members": [] + }, + { + "name": "Users", + "members": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-11/net_localgroup.out b/tests/fixtures/windows/windows-11/net_localgroup.out new file mode 100644 index 000000000..a1320a1f1 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_localgroup.out @@ -0,0 +1,19 @@ + +Aliases for \\DESKTOP-WIN11-HOME + +------------------------------------------------------------------------------- +*__vmware__ +*Administrators +*Device Owners +*Distributed COM Users +*docker-users +*Event Log Readers +*Guests +*Hyper-V Administrators +*IIS_IUSRS +*Performance Log Users +*Performance Monitor Users +*Remote Management Users +*System Managed Accounts Group +*Users +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-11/net_user.administrator.json b/tests/fixtures/windows/windows-11/net_user.administrator.json new file mode 100644 index 000000000..416e363ba --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_user.administrator.json @@ -0,0 +1,24 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator", + "comment": "Built-in account for administering the computer/domain", + "country_region_code": "000 (System Default)", + "account_active": false, + "account_expires": "Never", + "password_last_set": "2024-09-27T13:08:58", + "password_expires": "Never", + "password_changeable": "2024-09-27T13:08:58", + "password_required": true, + "user_may_change_password": true, + "workstations_allowed": "All", + "last_logon": "Never", + "logon_hours_allowed": "All", + "local_group_memberships": [ + "Administrators" + ], + "global_group_memberships": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-11/net_user.administrator.out b/tests/fixtures/windows/windows-11/net_user.administrator.out new file mode 100644 index 000000000..172297a64 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_user.administrator.out @@ -0,0 +1,25 @@ +User name Administrator +Full Name +Comment Built-in account for administering the computer/domain +User's comment +Country/region code 000 (System Default) +Account active No +Account expires Never + +Password last set 9/27/2024 1:08:58 PM +Password expires Never +Password changeable 9/27/2024 1:08:58 PM +Password required Yes +User may change password Yes + +Workstations allowed All +Logon script +User profile +Home directory +Last logon Never + +Logon hours allowed All + +Local Group Memberships *Administrators +Global Group memberships *None +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-11/net_user.json b/tests/fixtures/windows/windows-11/net_user.json new file mode 100644 index 000000000..070256867 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_user.json @@ -0,0 +1,21 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "user1" + }, + { + "user_name": "DefaultAccount" + }, + { + "user_name": "Guest" + }, + { + "user_name": "WDAGUtilityAccount" + } + ], + "account_origin": "\\\\DESKTOP-WIN11-HOME" +} diff --git a/tests/fixtures/windows/windows-11/net_user.out b/tests/fixtures/windows/windows-11/net_user.out new file mode 100644 index 000000000..605ded667 --- /dev/null +++ b/tests/fixtures/windows/windows-11/net_user.out @@ -0,0 +1,6 @@ +User accounts for \\DESKTOP-WIN11-HOME + +------------------------------------------------------------------------------- +Administrator user1 DefaultAccount +Guest WDAGUtilityAccount +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-11/route_print.json b/tests/fixtures/windows/windows-11/route_print.json new file mode 100644 index 000000000..41ea46ba5 --- /dev/null +++ b/tests/fixtures/windows/windows-11/route_print.json @@ -0,0 +1,312 @@ +{ + "interface_list": [ + { + "interface_index": 17, + "mac_address": "24:4b:fe:5b:9b:e6", + "description": "Intel(R) I211 Gigabit Network Connection" + }, + { + "interface_index": 20, + "mac_address": "24:4b:fe:5b:9b:e7", + "description": "Realtek PCIe 2.5GbE Family Controller" + }, + { + "interface_index": 3, + "mac_address": null, + "description": "OpenVPN Data Channel Offload" + }, + { + "interface_index": 7, + "mac_address": "00:ff:4c:ff:b8:68", + "description": "TAP-NordVPN Windows Adapter V9" + }, + { + "interface_index": 13, + "mac_address": "a8:7e:ea:36:46:61", + "description": "Microsoft Wi-Fi Direct Virtual Adapter" + }, + { + "interface_index": 22, + "mac_address": "aa:7e:ea:36:46:60", + "description": "Microsoft Wi-Fi Direct Virtual Adapter #2" + }, + { + "interface_index": 8, + "mac_address": "00:50:56:c0:00:01", + "description": "VMware Virtual Ethernet Adapter for VMnet1" + }, + { + "interface_index": 10, + "mac_address": "00:50:56:c0:00:08", + "description": "VMware Virtual Ethernet Adapter for VMnet8" + }, + { + "interface_index": 11, + "mac_address": "a8:7e:ea:36:46:60", + "description": "Intel(R) Wi-Fi 6 AX200 160MHz" + }, + { + "interface_index": 4, + "mac_address": "a8:7e:ea:36:46:64", + "description": "Bluetooth Device (Personal Area Network)" + }, + { + "interface_index": 1, + "mac_address": null, + "description": "Software Loopback Interface 1" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "192.168.1.1", + "interface": "192.168.1.169", + "metric": "35" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.0.0.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "192.168.1.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "192.168.1.169", + "metric": "291" + }, + { + "network_destination": "192.168.1.169", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.1.169", + "metric": "291" + }, + { + "network_destination": "192.168.1.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.1.169", + "metric": "291" + }, + { + "network_destination": "192.168.181.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "192.168.181.1", + "metric": "291" + }, + { + "network_destination": "192.168.181.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.181.1", + "metric": "291" + }, + { + "network_destination": "192.168.181.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.181.1", + "metric": "291" + }, + { + "network_destination": "192.168.213.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "192.168.213.1", + "metric": "291" + }, + { + "network_destination": "192.168.213.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.213.1", + "metric": "291" + }, + { + "network_destination": "192.168.213.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.213.1", + "metric": "291" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "192.168.181.1", + "metric": "291" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "192.168.213.1", + "metric": "291" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "192.168.1.169", + "metric": "291" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.181.1", + "metric": "291" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.213.1", + "metric": "291" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.1.169", + "metric": "291" + } + ], + "persistent_routes": [] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": 1, + "metric": "331", + "network_destination": "::1/128", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fd18:4310:ad04:1::/64", + "gateway": "fe80::4600:49ff:fee1:c6ee" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fd63:cc9c:65eb:3f95::/64", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fd63:cc9c:65eb:3f95:85e:ed3a:ebbf:d3ea/128", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fd63:cc9c:65eb:3f95:3d2f:46d0:6f06:f255/128", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fd63:cc9c:65eb:3f95:57c2:aa:10d8:db08/128", + "gateway": "On-link" + }, + { + "interface": 8, + "metric": "291", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 10, + "metric": "291", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 10, + "metric": "291", + "network_destination": "fe80::4551:bf0d:59dd:a4f0/128", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "fe80::4fae:1380:5a1b:8b6b/128", + "gateway": "On-link" + }, + { + "interface": 8, + "metric": "291", + "network_destination": "fe80::f47d:9c7f:69dc:591e/128", + "gateway": "On-link" + }, + { + "interface": 1, + "metric": "331", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 8, + "metric": "291", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 10, + "metric": "291", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "291", + "network_destination": "ff00::/8", + "gateway": "On-link" + } + ], + "persistent_routes": [] + } +} diff --git a/tests/fixtures/windows/windows-11/route_print.out b/tests/fixtures/windows/windows-11/route_print.out new file mode 100644 index 000000000..d8fa18bb9 --- /dev/null +++ b/tests/fixtures/windows/windows-11/route_print.out @@ -0,0 +1,73 @@ +=========================================================================== +Interface List + 17...24 4b fe 5b 9b e6 ......Intel(R) I211 Gigabit Network Connection + 20...24 4b fe 5b 9b e7 ......Realtek PCIe 2.5GbE Family Controller + 3...........................OpenVPN Data Channel Offload + 7...00 ff 4c ff b8 68 ......TAP-NordVPN Windows Adapter V9 + 13...a8 7e ea 36 46 61 ......Microsoft Wi-Fi Direct Virtual Adapter + 22...aa 7e ea 36 46 60 ......Microsoft Wi-Fi Direct Virtual Adapter #2 + 8...00 50 56 c0 00 01 ......VMware Virtual Ethernet Adapter for VMnet1 + 10...00 50 56 c0 00 08 ......VMware Virtual Ethernet Adapter for VMnet8 + 11...a8 7e ea 36 46 60 ......Intel(R) Wi-Fi 6 AX200 160MHz + 4...a8 7e ea 36 46 64 ......Bluetooth Device (Personal Area Network) + 1...........................Software Loopback Interface 1 +=========================================================================== + +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 192.168.1.1 192.168.1.169 35 + 127.0.0.0 255.0.0.0 On-link 127.0.0.1 331 + 127.0.0.1 255.255.255.255 On-link 127.0.0.1 331 + 127.255.255.255 255.255.255.255 On-link 127.0.0.1 331 + 192.168.1.0 255.255.255.0 On-link 192.168.1.169 291 + 192.168.1.169 255.255.255.255 On-link 192.168.1.169 291 + 192.168.1.255 255.255.255.255 On-link 192.168.1.169 291 + 192.168.181.0 255.255.255.0 On-link 192.168.181.1 291 + 192.168.181.1 255.255.255.255 On-link 192.168.181.1 291 + 192.168.181.255 255.255.255.255 On-link 192.168.181.1 291 + 192.168.213.0 255.255.255.0 On-link 192.168.213.1 291 + 192.168.213.1 255.255.255.255 On-link 192.168.213.1 291 + 192.168.213.255 255.255.255.255 On-link 192.168.213.1 291 + 224.0.0.0 240.0.0.0 On-link 127.0.0.1 331 + 224.0.0.0 240.0.0.0 On-link 192.168.181.1 291 + 224.0.0.0 240.0.0.0 On-link 192.168.213.1 291 + 224.0.0.0 240.0.0.0 On-link 192.168.1.169 291 + 255.255.255.255 255.255.255.255 On-link 127.0.0.1 331 + 255.255.255.255 255.255.255.255 On-link 192.168.181.1 291 + 255.255.255.255 255.255.255.255 On-link 192.168.213.1 291 + 255.255.255.255 255.255.255.255 On-link 192.168.1.169 291 +=========================================================================== +Persistent Routes: + None + +IPv6 Route Table +=========================================================================== +Active Routes: + If Metric Network Destination Gateway + 1 331 ::1/128 On-link + 11 291 fd18:4310:ad04:1::/64 fe80::4600:49ff:fee1:c6ee + 11 291 fd63:cc9c:65eb:3f95::/64 On-link + 11 291 fd63:cc9c:65eb:3f95:85e:ed3a:ebbf:d3ea/128 + On-link + 11 291 fd63:cc9c:65eb:3f95:3d2f:46d0:6f06:f255/128 + On-link + 11 291 fd63:cc9c:65eb:3f95:57c2:aa:10d8:db08/128 + On-link + 8 291 fe80::/64 On-link + 10 291 fe80::/64 On-link + 11 291 fe80::/64 On-link + 10 291 fe80::4551:bf0d:59dd:a4f0/128 + On-link + 11 291 fe80::4fae:1380:5a1b:8b6b/128 + On-link + 8 291 fe80::f47d:9c7f:69dc:591e/128 + On-link + 1 331 ff00::/8 On-link + 8 291 ff00::/8 On-link + 10 291 ff00::/8 On-link + 11 291 ff00::/8 On-link +=========================================================================== +Persistent Routes: + None \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2008/net_localgroup.administrators.json b/tests/fixtures/windows/windows-2008/net_localgroup.administrators.json new file mode 100644 index 000000000..2d70dbb1d --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_localgroup.administrators.json @@ -0,0 +1,15 @@ +{ + "account_origin": null, + "domain": null, + "comment": "Administrators have complete and unrestricted access to the computer/domain", + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "user1", + "vagrant" + ] + } + ] +} diff --git a/tests/fixtures/windows/windows-2008/net_localgroup.administrators.out b/tests/fixtures/windows/windows-2008/net_localgroup.administrators.out new file mode 100644 index 000000000..c8ea5adca --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_localgroup.administrators.out @@ -0,0 +1,10 @@ +Alias name Administrators +Comment Administrators have complete and unrestricted access to the computer/domain + +Members + +------------------------------------------------------------------------------- +Administrator +user1 +vagrant +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2008/net_localgroup.json b/tests/fixtures/windows/windows-2008/net_localgroup.json new file mode 100644 index 000000000..dc185cca6 --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_localgroup.json @@ -0,0 +1,71 @@ +{ + "account_origin": "\\\\WIN-SERVER8", + "domain": null, + "comment": null, + "groups": [ + { + "name": "Administrators", + "members": [] + }, + { + "name": "Backup Operators", + "members": [] + }, + { + "name": "Certificate Service DCOM Access", + "members": [] + }, + { + "name": "Cryptographic Operators", + "members": [] + }, + { + "name": "Distributed COM Users", + "members": [] + }, + { + "name": "Event Log Readers", + "members": [] + }, + { + "name": "Guests", + "members": [] + }, + { + "name": "IIS_IUSRS", + "members": [] + }, + { + "name": "Network Configuration Operators", + "members": [] + }, + { + "name": "Performance Log Users", + "members": [] + }, + { + "name": "Performance Monitor Users", + "members": [] + }, + { + "name": "Power Users", + "members": [] + }, + { + "name": "Print Operators", + "members": [] + }, + { + "name": "Remote Desktop Users", + "members": [] + }, + { + "name": "Replicator", + "members": [] + }, + { + "name": "Users", + "members": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-2008/net_localgroup.out b/tests/fixtures/windows/windows-2008/net_localgroup.out new file mode 100644 index 000000000..a37ad3e3c --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_localgroup.out @@ -0,0 +1,21 @@ + +Aliases for \\WIN-SERVER8 + +------------------------------------------------------------------------------- +*Administrators +*Backup Operators +*Certificate Service DCOM Access +*Cryptographic Operators +*Distributed COM Users +*Event Log Readers +*Guests +*IIS_IUSRS +*Network Configuration Operators +*Performance Log Users +*Performance Monitor Users +*Power Users +*Print Operators +*Remote Desktop Users +*Replicator +*Users +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2008/net_user.administrator.json b/tests/fixtures/windows/windows-2008/net_user.administrator.json new file mode 100644 index 000000000..5be018b0c --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_user.administrator.json @@ -0,0 +1,23 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator", + "comment": "Built-in account for administering the computer/dom", + "account_active": true, + "account_expires": "Never", + "password_last_set": "2024-01-11T21:34:38", + "password_expires": "2024-02-22T21:34:38", + "password_changeable": "2024-01-11T21:34:38", + "password_required": true, + "user_may_change_password": true, + "workstations_allowed": "All", + "last_logon": "2010-11-20T20:48:04", + "logon_hours_allowed": "All", + "local_group_memberships": [ + "Administrators" + ], + "global_group_memberships": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-2008/net_user.administrator.out b/tests/fixtures/windows/windows-2008/net_user.administrator.out new file mode 100644 index 000000000..b330052cc --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_user.administrator.out @@ -0,0 +1,26 @@ +User name Administrator +Full Name +Comment Built-in account for administering the computer/dom +ain +User's comment +Country code 000 (System Default) +Account active Yes +Account expires Never + +Password last set 1/11/2024 9:34:38 PM +Password expires 2/22/2024 9:34:38 PM +Password changeable 1/11/2024 9:34:38 PM +Password required Yes +User may change password Yes + +Workstations allowed All +Logon script +User profile +Home directory +Last logon 11/20/2010 8:48:04 PM + +Logon hours allowed All + +Local Group Memberships *Administrators +Global Group memberships *None +The command completed successfully. diff --git a/tests/fixtures/windows/windows-2008/net_user.json b/tests/fixtures/windows/windows-2008/net_user.json new file mode 100644 index 000000000..434d38d4b --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_user.json @@ -0,0 +1,18 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "Guest" + }, + { + "user_name": "user1" + }, + { + "user_name": "vagrant" + } + ], + "account_origin": "\\\\WIN-SERVER8" +} diff --git a/tests/fixtures/windows/windows-2008/net_user.out b/tests/fixtures/windows/windows-2008/net_user.out new file mode 100644 index 000000000..3e23d1bc9 --- /dev/null +++ b/tests/fixtures/windows/windows-2008/net_user.out @@ -0,0 +1,7 @@ + +User accounts for \\WIN-SERVER8 + +------------------------------------------------------------------------------- +Administrator Guest user1 +vagrant +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2008/route_print.json b/tests/fixtures/windows/windows-2008/route_print.json new file mode 100644 index 000000000..106c9436b --- /dev/null +++ b/tests/fixtures/windows/windows-2008/route_print.json @@ -0,0 +1,143 @@ +{ + "interface_list": [ + { + "interface_index": 11, + "mac_address": "00:0c:29:76:c8:d0", + "description": "Intel(R) PRO/1000 MT Network Connection" + }, + { + "interface_index": 1, + "mac_address": null, + "description": "Software Loopback Interface 1" + }, + { + "interface_index": 12, + "mac_address": null, + "description": "Microsoft ISATAP Adapter" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "192.168.23.1", + "interface": "192.168.23.154", + "metric": "266" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "127.0.0.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "127.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "192.168.23.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "192.168.23.154", + "metric": "266" + }, + { + "network_destination": "192.168.23.154", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.23.154", + "metric": "266" + }, + { + "network_destination": "192.168.23.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.23.154", + "metric": "266" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "192.168.23.154", + "metric": "266" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.23.154", + "metric": "266" + } + ], + "persistent_routes": [ + { + "network_address": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway_address": "192.168.23.1", + "metric": "Default" + } + ] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": 1, + "metric": "306", + "network_destination": "::1/128", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "266", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "266", + "network_destination": "fe80::9595:e8d9:b190:ecac/128", + "gateway": "On-link" + }, + { + "interface": 1, + "metric": "306", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 11, + "metric": "266", + "network_destination": "ff00::/8", + "gateway": "On-link" + } + ], + "persistent_routes": [] + } +} diff --git a/tests/fixtures/windows/windows-2008/route_print.out b/tests/fixtures/windows/windows-2008/route_print.out new file mode 100644 index 000000000..ec6b56cdc --- /dev/null +++ b/tests/fixtures/windows/windows-2008/route_print.out @@ -0,0 +1,41 @@ +=========================================================================== +Interface List + 11...00 0c 29 76 c8 d0 ......Intel(R) PRO/1000 MT Network Connection + 1...........................Software Loopback Interface 1 + 12...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter +=========================================================================== + +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 192.168.23.1 192.168.23.154 266 + 127.0.0.0 255.0.0.0 On-link 127.0.0.1 306 + 127.0.0.1 255.255.255.255 On-link 127.0.0.1 306 + 127.255.255.255 255.255.255.255 On-link 127.0.0.1 306 + 192.168.23.0 255.255.255.0 On-link 192.168.23.154 266 + 192.168.23.154 255.255.255.255 On-link 192.168.23.154 266 + 192.168.23.255 255.255.255.255 On-link 192.168.23.154 266 + 224.0.0.0 240.0.0.0 On-link 127.0.0.1 306 + 224.0.0.0 240.0.0.0 On-link 192.168.23.154 266 + 255.255.255.255 255.255.255.255 On-link 127.0.0.1 306 + 255.255.255.255 255.255.255.255 On-link 192.168.23.154 266 +=========================================================================== +Persistent Routes: + Network Address Netmask Gateway Address Metric + 0.0.0.0 0.0.0.0 192.168.23.1 Default +=========================================================================== + +IPv6 Route Table +=========================================================================== +Active Routes: + If Metric Network Destination Gateway + 1 306 ::1/128 On-link + 11 266 fe80::/64 On-link + 11 266 fe80::9595:e8d9:b190:ecac/128 + On-link + 1 306 ff00::/8 On-link + 11 266 ff00::/8 On-link +=========================================================================== +Persistent Routes: + None \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2016/net_localgroup.administrators.json b/tests/fixtures/windows/windows-2016/net_localgroup.administrators.json new file mode 100644 index 000000000..3da729c00 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_localgroup.administrators.json @@ -0,0 +1,18 @@ +{ + "account_origin": null, + "domain": null, + "comment": null, + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "SOMECOMPANY\\ansible", + "SOMECOMPANY\\da", + "SOMECOMPANY\\Domain Admins", + "SOMECOMPANY\\localadmin", + "user1" + ] + } + ] +} diff --git a/tests/fixtures/windows/windows-2016/net_localgroup.administrators.out b/tests/fixtures/windows/windows-2016/net_localgroup.administrators.out new file mode 100644 index 000000000..823bd11d7 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_localgroup.administrators.out @@ -0,0 +1,13 @@ +Alias name Administrators +Comment + +Members + +------------------------------------------------------------------------------- +Administrator +SOMECOMPANY\ansible +SOMECOMPANY\da +SOMECOMPANY\Domain Admins +SOMECOMPANY\localadmin +user1 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2016/net_localgroup.json b/tests/fixtures/windows/windows-2016/net_localgroup.json new file mode 100644 index 000000000..710c80a9d --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_localgroup.json @@ -0,0 +1,103 @@ +{ + "account_origin": "\\\\WIN-SERVER16", + "domain": null, + "comment": null, + "groups": [ + { + "name": "Access Control Assistance Operators", + "members": [] + }, + { + "name": "Administrators", + "members": [] + }, + { + "name": "Backup Operators", + "members": [] + }, + { + "name": "Certificate Service DCOM Access", + "members": [] + }, + { + "name": "Cryptographic Operators", + "members": [] + }, + { + "name": "Distributed COM Users", + "members": [] + }, + { + "name": "Event Log Readers", + "members": [] + }, + { + "name": "Guests", + "members": [] + }, + { + "name": "Hyper-V Administrators", + "members": [] + }, + { + "name": "IIS_IUSRS", + "members": [] + }, + { + "name": "Network Configuration Operators", + "members": [] + }, + { + "name": "Performance Log Users", + "members": [] + }, + { + "name": "Performance Monitor Users", + "members": [] + }, + { + "name": "Power Users", + "members": [] + }, + { + "name": "Print Operators", + "members": [] + }, + { + "name": "RDS Endpoint Servers", + "members": [] + }, + { + "name": "RDS Management Servers", + "members": [] + }, + { + "name": "RDS Remote Access Servers", + "members": [] + }, + { + "name": "Remote Desktop Users", + "members": [] + }, + { + "name": "Remote Management Users", + "members": [] + }, + { + "name": "Replicator", + "members": [] + }, + { + "name": "Storage Replica Administrators", + "members": [] + }, + { + "name": "System Managed Accounts Group", + "members": [] + }, + { + "name": "Users", + "members": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-2016/net_localgroup.out b/tests/fixtures/windows/windows-2016/net_localgroup.out new file mode 100644 index 000000000..91d779e69 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_localgroup.out @@ -0,0 +1,28 @@ +Aliases for \\WIN-SERVER16 + +------------------------------------------------------------------------------- +*Access Control Assistance Operators +*Administrators +*Backup Operators +*Certificate Service DCOM Access +*Cryptographic Operators +*Distributed COM Users +*Event Log Readers +*Guests +*Hyper-V Administrators +*IIS_IUSRS +*Network Configuration Operators +*Performance Log Users +*Performance Monitor Users +*Power Users +*Print Operators +*RDS Endpoint Servers +*RDS Management Servers +*RDS Remote Access Servers +*Remote Desktop Users +*Remote Management Users +*Replicator +*Storage Replica Administrators +*System Managed Accounts Group +*Users +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2016/net_user.administrator.json b/tests/fixtures/windows/windows-2016/net_user.administrator.json new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/windows/windows-2016/net_user.administrators.json b/tests/fixtures/windows/windows-2016/net_user.administrators.json new file mode 100644 index 000000000..06dda6206 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_user.administrators.json @@ -0,0 +1,24 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator", + "comment": "Built-in account for administering the computer/domain", + "country_region_code": "000 (System Default)", + "account_active": true, + "account_expires": "Never", + "password_last_set": "2021-12-16T11:07:14", + "password_expires": "2022-01-27T11:07:14", + "password_changeable": "2021-12-17T11:07:14", + "password_required": true, + "user_may_change_password": true, + "workstations_allowed": "All", + "last_logon": "2024-08-23T13:47:11", + "logon_hours_allowed": "All", + "local_group_memberships": [ + "Administrators" + ], + "global_group_memberships": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-2016/net_user.administrators.out b/tests/fixtures/windows/windows-2016/net_user.administrators.out new file mode 100644 index 000000000..115f56916 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_user.administrators.out @@ -0,0 +1,25 @@ +User name Administrator +Full Name +Comment Built-in account for administering the computer/domain +User's comment +Country/region code 000 (System Default) +Account active Yes +Account expires Never + +Password last set 12/16/2021 11:07:14 AM +Password expires 1/27/2022 11:07:14 AM +Password changeable 12/17/2021 11:07:14 AM +Password required Yes +User may change password Yes + +Workstations allowed All +Logon script +User profile +Home directory +Last logon 8/23/2024 1:47:11 PM + +Logon hours allowed All + +Local Group Memberships *Administrators +Global Group memberships *None +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2016/net_user.json b/tests/fixtures/windows/windows-2016/net_user.json new file mode 100644 index 000000000..3276a25a7 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_user.json @@ -0,0 +1,18 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "DefaultAccount" + }, + { + "user_name": "Guest" + }, + { + "user_name": "user1" + } + ], + "account_origin": "\\\\WIN-SERVER16" +} diff --git a/tests/fixtures/windows/windows-2016/net_user.out b/tests/fixtures/windows/windows-2016/net_user.out new file mode 100644 index 000000000..8a743fe19 --- /dev/null +++ b/tests/fixtures/windows/windows-2016/net_user.out @@ -0,0 +1,7 @@ + +User accounts for \\WIN-SERVER16 + +------------------------------------------------------------------------------- +Administrator DefaultAccount Guest +user1 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-2016/route_print.json b/tests/fixtures/windows/windows-2016/route_print.json new file mode 100644 index 000000000..a0d91a44d --- /dev/null +++ b/tests/fixtures/windows/windows-2016/route_print.json @@ -0,0 +1,143 @@ +{ + "interface_list": [ + { + "interface_index": 2, + "mac_address": "00:0c:29:a1:38:2c", + "description": "Intel(R) 82574L Gigabit Network Connection" + }, + { + "interface_index": 1, + "mac_address": null, + "description": "Software Loopback Interface 1" + }, + { + "interface_index": 6, + "mac_address": null, + "description": "Microsoft ISATAP Adapter #2" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "192.168.22.1", + "interface": "192.168.22.153", + "metric": "281" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.0.0.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "127.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "192.168.22.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "192.168.22.153", + "metric": "281" + }, + { + "network_destination": "192.168.22.153", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.22.153", + "metric": "281" + }, + { + "network_destination": "192.168.22.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.22.153", + "metric": "281" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "192.168.22.153", + "metric": "281" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "331" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.22.153", + "metric": "281" + } + ], + "persistent_routes": [ + { + "network_address": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway_address": "192.168.22.1", + "metric": "Default" + } + ] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": 1, + "metric": "331", + "network_destination": "::1/128", + "gateway": "On-link" + }, + { + "interface": 2, + "metric": "281", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 2, + "metric": "281", + "network_destination": "fe80::40c:2f5f:6658:9dc3/128", + "gateway": "On-link" + }, + { + "interface": 1, + "metric": "331", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 2, + "metric": "281", + "network_destination": "ff00::/8", + "gateway": "On-link" + } + ], + "persistent_routes": [] + } +} diff --git a/tests/fixtures/windows/windows-2016/route_print.out b/tests/fixtures/windows/windows-2016/route_print.out new file mode 100644 index 000000000..f488f147f --- /dev/null +++ b/tests/fixtures/windows/windows-2016/route_print.out @@ -0,0 +1,41 @@ +=========================================================================== +Interface List + 2...00 0c 29 a1 38 2c ......Intel(R) 82574L Gigabit Network Connection + 1...........................Software Loopback Interface 1 + 6...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter #2 +=========================================================================== + +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 192.168.22.1 192.168.22.153 281 + 127.0.0.0 255.0.0.0 On-link 127.0.0.1 331 + 127.0.0.1 255.255.255.255 On-link 127.0.0.1 331 + 127.255.255.255 255.255.255.255 On-link 127.0.0.1 331 + 192.168.22.0 255.255.255.0 On-link 192.168.22.153 281 + 192.168.22.153 255.255.255.255 On-link 192.168.22.153 281 + 192.168.22.255 255.255.255.255 On-link 192.168.22.153 281 + 224.0.0.0 240.0.0.0 On-link 127.0.0.1 331 + 224.0.0.0 240.0.0.0 On-link 192.168.22.153 281 + 255.255.255.255 255.255.255.255 On-link 127.0.0.1 331 + 255.255.255.255 255.255.255.255 On-link 192.168.22.153 281 +=========================================================================== +Persistent Routes: + Network Address Netmask Gateway Address Metric + 0.0.0.0 0.0.0.0 192.168.22.1 Default +=========================================================================== + +IPv6 Route Table +=========================================================================== +Active Routes: + If Metric Network Destination Gateway + 1 331 ::1/128 On-link + 2 281 fe80::/64 On-link + 2 281 fe80::40c:2f5f:6658:9dc3/128 + On-link + 1 331 ff00::/8 On-link + 2 281 ff00::/8 On-link +=========================================================================== +Persistent Routes: + None \ No newline at end of file diff --git a/tests/fixtures/windows/windows-7/net_localgroup.administrators.json b/tests/fixtures/windows/windows-7/net_localgroup.administrators.json new file mode 100644 index 000000000..3da729c00 --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_localgroup.administrators.json @@ -0,0 +1,18 @@ +{ + "account_origin": null, + "domain": null, + "comment": null, + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "SOMECOMPANY\\ansible", + "SOMECOMPANY\\da", + "SOMECOMPANY\\Domain Admins", + "SOMECOMPANY\\localadmin", + "user1" + ] + } + ] +} diff --git a/tests/fixtures/windows/windows-7/net_localgroup.administrators.out b/tests/fixtures/windows/windows-7/net_localgroup.administrators.out new file mode 100644 index 000000000..823bd11d7 --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_localgroup.administrators.out @@ -0,0 +1,13 @@ +Alias name Administrators +Comment + +Members + +------------------------------------------------------------------------------- +Administrator +SOMECOMPANY\ansible +SOMECOMPANY\da +SOMECOMPANY\Domain Admins +SOMECOMPANY\localadmin +user1 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-7/net_localgroup.json b/tests/fixtures/windows/windows-7/net_localgroup.json new file mode 100644 index 000000000..b57016e9d --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_localgroup.json @@ -0,0 +1,67 @@ +{ + "account_origin": "\\\\DESKTOP-WIN7", + "domain": null, + "comment": null, + "groups": [ + { + "name": "Administrators", + "members": [] + }, + { + "name": "Backup Operators", + "members": [] + }, + { + "name": "Cryptographic Operators", + "members": [] + }, + { + "name": "Distributed COM Users", + "members": [] + }, + { + "name": "Event Log Readers", + "members": [] + }, + { + "name": "Guests", + "members": [] + }, + { + "name": "IIS_IUSRS", + "members": [] + }, + { + "name": "Network Configuration Operators", + "members": [] + }, + { + "name": "Performance Log Users", + "members": [] + }, + { + "name": "Performance Monitor Users", + "members": [] + }, + { + "name": "Power Users", + "members": [] + }, + { + "name": "Remote Desktop Users", + "members": [] + }, + { + "name": "Replicator", + "members": [] + }, + { + "name": "Users", + "members": [] + }, + { + "name": "WinRMRemoteWMIUsers__", + "members": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-7/net_localgroup.out b/tests/fixtures/windows/windows-7/net_localgroup.out new file mode 100644 index 000000000..cb1a1d063 --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_localgroup.out @@ -0,0 +1,20 @@ + +Aliases for \\DESKTOP-WIN7 + +------------------------------------------------------------------------------- +*Administrators +*Backup Operators +*Cryptographic Operators +*Distributed COM Users +*Event Log Readers +*Guests +*IIS_IUSRS +*Network Configuration Operators +*Performance Log Users +*Performance Monitor Users +*Power Users +*Remote Desktop Users +*Replicator +*Users +*WinRMRemoteWMIUsers__ +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-7/net_user.administrator.json b/tests/fixtures/windows/windows-7/net_user.administrator.json new file mode 100644 index 000000000..9ee1093bd --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_user.administrator.json @@ -0,0 +1,23 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator", + "comment": "Built-in account for administering the computer/domain", + "account_active": false, + "account_expires": "Never", + "password_last_set": "2009-07-14T00:13:36", + "password_expires": "Never", + "password_changeable": "2009-07-15T00:13:36", + "password_required": true, + "user_may_change_password": true, + "workstations_allowed": "All", + "last_logon": "2009-07-14T00:08:59", + "logon_hours_allowed": "All", + "local_group_memberships": [ + "Administrators" + ], + "global_group_memberships": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-7/net_user.administrator.out b/tests/fixtures/windows/windows-7/net_user.administrator.out new file mode 100644 index 000000000..27ff58362 --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_user.administrator.out @@ -0,0 +1,25 @@ +User name Administrator +Full Name +Comment Built-in account for administering the computer/domain +User's comment +Country code 000 (System Default) +Account active No +Account expires Never + +Password last set 7/14/2009 12:13:36 AM +Password expires Never +Password changeable 7/15/2009 12:13:36 AM +Password required Yes +User may change password Yes + +Workstations allowed All +Logon script +User profile +Home directory +Last logon 7/14/2009 12:08:59 AM + +Logon hours allowed All + +Local Group Memberships *Administrators +Global Group memberships *None +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-7/net_user.json b/tests/fixtures/windows/windows-7/net_user.json new file mode 100644 index 000000000..2f7f1bed5 --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_user.json @@ -0,0 +1,15 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "Guest" + }, + { + "user_name": "user1" + } + ], + "account_origin": "\\\\DESKTOP-WIN7" +} diff --git a/tests/fixtures/windows/windows-7/net_user.out b/tests/fixtures/windows/windows-7/net_user.out new file mode 100644 index 000000000..389547a66 --- /dev/null +++ b/tests/fixtures/windows/windows-7/net_user.out @@ -0,0 +1,6 @@ + +User accounts for \\DESKTOP-WIN7 + +------------------------------------------------------------------------------- +Administrator Guest user1 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-7/route_print.json b/tests/fixtures/windows/windows-7/route_print.json new file mode 100644 index 000000000..ced4834a7 --- /dev/null +++ b/tests/fixtures/windows/windows-7/route_print.json @@ -0,0 +1,136 @@ +{ + "interface_list": [ + { + "interface_index": 10, + "mac_address": "00:0c:29:86:1e:1f", + "description": "Intel(R) PRO/1000 MT Network Connection" + }, + { + "interface_index": 1, + "mac_address": null, + "description": "Software Loopback Interface 1" + }, + { + "interface_index": 11, + "mac_address": null, + "description": "Microsoft ISATAP Adapter" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "192.168.22.1", + "interface": "192.168.22.33", + "metric": "10" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "127.0.0.1", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "127.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "192.168.22.0", + "netmask": "255.255.255.0", + "gateway": "On-link", + "interface": "192.168.22.33", + "metric": "266" + }, + { + "network_destination": "192.168.22.33", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.22.33", + "metric": "266" + }, + { + "network_destination": "192.168.22.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.22.33", + "metric": "266" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "On-link", + "interface": "192.168.22.33", + "metric": "266" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "127.0.0.1", + "metric": "306" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "On-link", + "interface": "192.168.22.33", + "metric": "266" + } + ], + "persistent_routes": [] + }, + "ipv6_route_table": { + "active_routes": [ + { + "interface": 1, + "metric": "306", + "network_destination": "::1/128", + "gateway": "On-link" + }, + { + "interface": 10, + "metric": "266", + "network_destination": "fe80::/64", + "gateway": "On-link" + }, + { + "interface": 10, + "metric": "266", + "network_destination": "fe80::c447:d1ef:c29f:c48c/128", + "gateway": "On-link" + }, + { + "interface": 1, + "metric": "306", + "network_destination": "ff00::/8", + "gateway": "On-link" + }, + { + "interface": 10, + "metric": "266", + "network_destination": "ff00::/8", + "gateway": "On-link" + } + ], + "persistent_routes": [] + } +} diff --git a/tests/fixtures/windows/windows-7/route_print.out b/tests/fixtures/windows/windows-7/route_print.out new file mode 100644 index 000000000..3f581fa20 --- /dev/null +++ b/tests/fixtures/windows/windows-7/route_print.out @@ -0,0 +1,39 @@ +=========================================================================== +Interface List + 10...00 0c 29 86 1e 1f ......Intel(R) PRO/1000 MT Network Connection + 1...........................Software Loopback Interface 1 + 11...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter +=========================================================================== + +IPv4 Route Table +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 192.168.22.1 192.168.22.33 10 + 127.0.0.0 255.0.0.0 On-link 127.0.0.1 306 + 127.0.0.1 255.255.255.255 On-link 127.0.0.1 306 + 127.255.255.255 255.255.255.255 On-link 127.0.0.1 306 + 192.168.22.0 255.255.255.0 On-link 192.168.22.33 266 + 192.168.22.33 255.255.255.255 On-link 192.168.22.33 266 + 192.168.22.255 255.255.255.255 On-link 192.168.22.33 266 + 224.0.0.0 240.0.0.0 On-link 127.0.0.1 306 + 224.0.0.0 240.0.0.0 On-link 192.168.22.33 266 + 255.255.255.255 255.255.255.255 On-link 127.0.0.1 306 + 255.255.255.255 255.255.255.255 On-link 192.168.22.33 266 +=========================================================================== +Persistent Routes: + None + +IPv6 Route Table +=========================================================================== +Active Routes: + If Metric Network Destination Gateway + 1 306 ::1/128 On-link + 10 266 fe80::/64 On-link + 10 266 fe80::c447:d1ef:c29f:c48c/128 + On-link + 1 306 ff00::/8 On-link + 10 266 ff00::/8 On-link +=========================================================================== +Persistent Routes: + None \ No newline at end of file diff --git a/tests/fixtures/windows/windows-xp/net_localgroup.administrators.json b/tests/fixtures/windows/windows-xp/net_localgroup.administrators.json new file mode 100644 index 000000000..bb454fac2 --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_localgroup.administrators.json @@ -0,0 +1,14 @@ +{ + "account_origin": null, + "domain": null, + "comment": "Administrators have complete and unrestricted access to the computer/domain", + "groups": [ + { + "name": "Administrators", + "members": [ + "Administrator", + "SOMECOMPANY\\Domain Admins" + ] + } + ] +} diff --git a/tests/fixtures/windows/windows-xp/net_localgroup.administrators.out b/tests/fixtures/windows/windows-xp/net_localgroup.administrators.out new file mode 100644 index 000000000..c9d48a0ce --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_localgroup.administrators.out @@ -0,0 +1,9 @@ +Alias name Administrators +Comment Administrators have complete and unrestricted access to the computer/domain + +Members + +------------------------------------------------------------------------------- +Administrator +SOMECOMPANY\Domain Admins +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-xp/net_localgroup.json b/tests/fixtures/windows/windows-xp/net_localgroup.json new file mode 100644 index 000000000..d90628640 --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_localgroup.json @@ -0,0 +1,55 @@ +{ + "account_origin": "\\\\DESKTOP-WINXP", + "domain": null, + "comment": null, + "groups": [ + { + "name": "Administrators", + "members": [] + }, + { + "name": "Backup Operators", + "members": [] + }, + { + "name": "Distributed COM Users", + "members": [] + }, + { + "name": "Guests", + "members": [] + }, + { + "name": "HelpServicesGroup", + "members": [] + }, + { + "name": "Network Configuration Operators", + "members": [] + }, + { + "name": "Performance Log Users", + "members": [] + }, + { + "name": "Performance Monitor Users", + "members": [] + }, + { + "name": "Power Users", + "members": [] + }, + { + "name": "Remote Desktop Users", + "members": [] + }, + { + "name": "Replicator", + "members": [] + }, + { + "name": "Users", + "members": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-xp/net_localgroup.out b/tests/fixtures/windows/windows-xp/net_localgroup.out new file mode 100644 index 000000000..666e7566a --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_localgroup.out @@ -0,0 +1,17 @@ + +Aliases for \\DESKTOP-WINXP + +------------------------------------------------------------------------------- +*Administrators +*Backup Operators +*Distributed COM Users +*Guests +*HelpServicesGroup +*Network Configuration Operators +*Performance Log Users +*Performance Monitor Users +*Power Users +*Remote Desktop Users +*Replicator +*Users +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-xp/net_user.administrator.json b/tests/fixtures/windows/windows-xp/net_user.administrator.json new file mode 100644 index 000000000..6c295391b --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_user.administrator.json @@ -0,0 +1,23 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator", + "comment": "Built-in account for administering the computer/domain", + "account_active": true, + "account_expires": "Never", + "password_last_set": "10/4/2023 8:53 AM", + "password_expires": "Never", + "password_changeable": "10/5/2023 8:53 AM", + "password_required": true, + "user_may_change_password": true, + "workstations_allowed": "All", + "last_logon": "7/17/2024 1:37 PM", + "logon_hours_allowed": "All", + "local_group_memberships": [ + "Administrators" + ], + "global_group_memberships": [] + } + ] +} diff --git a/tests/fixtures/windows/windows-xp/net_user.administrator.out b/tests/fixtures/windows/windows-xp/net_user.administrator.out new file mode 100644 index 000000000..b9d767c38 --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_user.administrator.out @@ -0,0 +1,25 @@ +User name Administrator +Full Name +Comment Built-in account for administering the computer/domain +User's comment +Country code 000 (System Default) +Account active Yes +Account expires Never + +Password last set 10/4/2023 8:53 AM +Password expires Never +Password changeable 10/5/2023 8:53 AM +Password required Yes +User may change password Yes + +Workstations allowed All +Logon script +User profile +Home directory +Last logon 7/17/2024 1:37 PM + +Logon hours allowed All + +Local Group Memberships *Administrators +Global Group memberships *None +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-xp/net_user.json b/tests/fixtures/windows/windows-xp/net_user.json new file mode 100644 index 000000000..afcc7cc4d --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_user.json @@ -0,0 +1,15 @@ +{ + "domain": "", + "user_accounts": [ + { + "user_name": "Administrator" + }, + { + "user_name": "Guest" + }, + { + "user_name": "SUPPORT_3556336b0" + } + ], + "account_origin": "\\\\DESKTOP-WINXP" +} diff --git a/tests/fixtures/windows/windows-xp/net_user.out b/tests/fixtures/windows/windows-xp/net_user.out new file mode 100644 index 000000000..256c5bf6a --- /dev/null +++ b/tests/fixtures/windows/windows-xp/net_user.out @@ -0,0 +1,6 @@ + +User accounts for \\DESKTOP-WINXP + +------------------------------------------------------------------------------- +Administrator Guest SUPPORT_3556336b0 +The command completed successfully. \ No newline at end of file diff --git a/tests/fixtures/windows/windows-xp/route_print.json b/tests/fixtures/windows/windows-xp/route_print.json new file mode 100644 index 000000000..6015b5b24 --- /dev/null +++ b/tests/fixtures/windows/windows-xp/route_print.json @@ -0,0 +1,86 @@ +{ + "interface_list": [ + { + "interface_index": 1, + "mac_address": null, + "description": ". MS TCP Loopback interface" + }, + { + "interface_index": 2, + "mac_address": "00:0c:29:21:f9:3e", + "description": ". Intel(R) PRO/1000 MT Network Connection - Packet Scheduler Miniport" + } + ], + "ipv4_route_table": { + "active_routes": [ + { + "network_destination": "0.0.0.0", + "netmask": "0.0.0.0", + "gateway": "192.168.22.1", + "interface": "192.168.22.135", + "metric": "10" + }, + { + "network_destination": "10.0.4.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.1", + "interface": "192.168.22.135", + "metric": "1" + }, + { + "network_destination": "127.0.0.0", + "netmask": "255.0.0.0", + "gateway": "127.0.0.1", + "interface": "127.0.0.1", + "metric": "1" + }, + { + "network_destination": "192.168.22.0", + "netmask": "255.255.255.0", + "gateway": "192.168.22.135", + "interface": "192.168.22.135", + "metric": "10" + }, + { + "network_destination": "192.168.22.135", + "netmask": "255.255.255.255", + "gateway": "127.0.0.1", + "interface": "127.0.0.1", + "metric": "10" + }, + { + "network_destination": "192.168.22.255", + "netmask": "255.255.255.255", + "gateway": "192.168.22.135", + "interface": "192.168.22.135", + "metric": "10" + }, + { + "network_destination": "224.0.0.0", + "netmask": "240.0.0.0", + "gateway": "192.168.22.135", + "interface": "192.168.22.135", + "metric": "10" + }, + { + "network_destination": "255.255.255.255", + "netmask": "255.255.255.255", + "gateway": "192.168.22.135", + "interface": "192.168.22.135", + "metric": "1" + } + ], + "persistent_routes": [ + { + "network_address": "10.0.4.0", + "netmask": "255.255.255.0", + "gateway_address": "192.168.22.1", + "metric": "1" + } + ] + }, + "ipv6_route_table": { + "active_routes": [], + "persistent_routes": [] + } +} diff --git a/tests/fixtures/windows/windows-xp/route_print.out b/tests/fixtures/windows/windows-xp/route_print.out new file mode 100644 index 000000000..43b61bcc3 --- /dev/null +++ b/tests/fixtures/windows/windows-xp/route_print.out @@ -0,0 +1,22 @@ +IPv4 Route Table +=========================================================================== +Interface List +0x1 ........................... MS TCP Loopback interface +0x2 ...00 0c 29 21 f9 3e ...... Intel(R) PRO/1000 MT Network Connection - Packet Scheduler Miniport +=========================================================================== +=========================================================================== +Active Routes: +Network Destination Netmask Gateway Interface Metric + 0.0.0.0 0.0.0.0 192.168.22.1 192.168.22.135 10 + 10.0.4.0 255.255.255.0 192.168.22.1 192.168.22.135 1 + 127.0.0.0 255.0.0.0 127.0.0.1 127.0.0.1 1 + 192.168.22.0 255.255.255.0 192.168.22.135 192.168.22.135 10 + 192.168.22.135 255.255.255.255 127.0.0.1 127.0.0.1 10 + 192.168.22.255 255.255.255.255 192.168.22.135 192.168.22.135 10 + 224.0.0.0 240.0.0.0 192.168.22.135 192.168.22.135 10 + 255.255.255.255 255.255.255.255 192.168.22.135 192.168.22.135 1 +Default Gateway: 192.168.22.1 +=========================================================================== +Persistent Routes: + Network Address Netmask Gateway Address Metric + 10.0.4.0 255.255.255.0 192.168.22.1 1 \ No newline at end of file diff --git a/tests/test_net_localgroup.py b/tests/test_net_localgroup.py new file mode 100644 index 000000000..32c4dfcee --- /dev/null +++ b/tests/test_net_localgroup.py @@ -0,0 +1,55 @@ +import json +import os +import unittest +import jc.parsers.ipconfig +import jc.parsers.net_localgroup + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + test_files = [ + "tests/fixtures/windows/windows-xp/net_localgroup", + "tests/fixtures/windows/windows-xp/net_localgroup.administrators", + "tests/fixtures/windows/windows-7/net_localgroup", + "tests/fixtures/windows/windows-7/net_localgroup.administrators", + "tests/fixtures/windows/windows-2008/net_localgroup", + "tests/fixtures/windows/windows-2008/net_localgroup.administrators", + "tests/fixtures/windows/windows-2016/net_localgroup", + "tests/fixtures/windows/windows-2016/net_localgroup.administrators", + "tests/fixtures/windows/windows-10/net_localgroup", + "tests/fixtures/windows/windows-10/net_localgroup.administrators", + "tests/fixtures/windows/windows-11/net_localgroup", + "tests/fixtures/windows/windows-11/net_localgroup.administrators" + ] + + def setUp(self): + for tf in MyTests.test_files: + in_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.out") + out_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.json") + + with open(in_file, "r", encoding="utf-8") as f: + setattr(self, self.varName(tf), f.read()) + with open(out_file, "r", encoding="utf-8") as f: + setattr(self, self.varName(tf) + "_json", json.loads(f.read())) + + def varName(self, path): + return ( + path.replace("tests/fixtures/windows", "") + .replace("-", "_") + .replace("/", "_") + ) + + def test_windows_net_localgroup(self): + """ + Test a sample Windows "net localgroup" command output + """ + for tf in MyTests.test_files: + in_var = getattr(self, self.varName(tf)) + out_var = getattr(self, self.varName(tf) + "_json") + + self.assertEqual(jc.parsers.net_localgroup.parse(in_var, quiet=True), out_var) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_net_user.py b/tests/test_net_user.py new file mode 100644 index 000000000..a709186ab --- /dev/null +++ b/tests/test_net_user.py @@ -0,0 +1,56 @@ +import json +import os +import unittest +import jc.parsers.ipconfig +import jc.parsers.net_localgroup +import jc.parsers.net_user + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + test_files = [ + "tests/fixtures/windows/windows-xp/net_user", + "tests/fixtures/windows/windows-xp/net_user.administrator", + "tests/fixtures/windows/windows-7/net_user", + "tests/fixtures/windows/windows-7/net_user.administrator", + "tests/fixtures/windows/windows-2008/net_user", + "tests/fixtures/windows/windows-2008/net_user.administrator", + "tests/fixtures/windows/windows-2016/net_user.administrators", + "tests/fixtures/windows/windows-2016/net_user", + "tests/fixtures/windows/windows-10/net_user", + "tests/fixtures/windows/windows-10/net_user.administrator", + "tests/fixtures/windows/windows-11/net_user", + "tests/fixtures/windows/windows-11/net_user.administrator" + ] + + def setUp(self): + for tf in MyTests.test_files: + in_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.out") + out_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.json") + + with open(in_file, "r", encoding="utf-8") as f: + setattr(self, self.varName(tf), f.read()) + with open(out_file, "r", encoding="utf-8") as f: + setattr(self, self.varName(tf) + "_json", json.loads(f.read())) + + def varName(self, path): + return ( + path.replace("tests/fixtures/windows", "") + .replace("-", "_") + .replace("/", "_") + ) + + def test_windows_net_localgroup(self): + """ + Test a sample Windows "net localgroup" command output + """ + for tf in MyTests.test_files: + in_var = getattr(self, self.varName(tf)) + out_var = getattr(self, self.varName(tf) + "_json") + + self.assertEqual(jc.parsers.net_user.parse(in_var, quiet=True), out_var) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_route_print.py b/tests/test_route_print.py new file mode 100644 index 000000000..c266a3e29 --- /dev/null +++ b/tests/test_route_print.py @@ -0,0 +1,50 @@ +import json +import os +import unittest +import jc.parsers.ipconfig +import jc.parsers.net_localgroup +import jc.parsers.route_print + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + test_files = [ + "tests/fixtures/windows/windows-xp/route_print", + "tests/fixtures/windows/windows-7/route_print", + "tests/fixtures/windows/windows-2008/route_print", + "tests/fixtures/windows/windows-2016/route_print", + "tests/fixtures/windows/windows-10/route_print", + "tests/fixtures/windows/windows-11/route_print" + ] + + def setUp(self): + for tf in MyTests.test_files: + in_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.out") + out_file = os.path.join(THIS_DIR, os.pardir, f"{tf}.json") + + with open(in_file, "r", encoding="utf-8") as f: + setattr(self, self.varName(tf), f.read()) + with open(out_file, "r", encoding="utf-8") as f: + setattr(self, self.varName(tf) + "_json", json.loads(f.read())) + + def varName(self, path): + return ( + path.replace("tests/fixtures/windows", "") + .replace("-", "_") + .replace("/", "_") + ) + + def test_windows_route_print(self): + """ + Test a sample Windows "route print" command output + """ + for tf in MyTests.test_files: + in_var = getattr(self, self.varName(tf)) + out_var = getattr(self, self.varName(tf) + "_json") + + self.assertEqual(jc.parsers.route_print.parse(in_var, quiet=True), out_var) + + +if __name__ == "__main__": + unittest.main() From 3c0d75b0ec41ec20ca4943310a76f75c21135e23 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 21 Oct 2024 16:08:23 -0400 Subject: [PATCH 2/3] fix: fix net user parsing error --- jc/parsers/net_user.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jc/parsers/net_user.py b/jc/parsers/net_user.py index 21a97655e..76de99610 100644 --- a/jc/parsers/net_user.py +++ b/jc/parsers/net_user.py @@ -425,12 +425,14 @@ def _parse(data): if collecting_users: # Split the line into usernames - user_matches = re.match(r'(.{1,20})\s+(.{1,20})\s+(.{1,20})', line) + user_matches = re.match(r'(.{1,20})(\s+.{1,20})?(\s+.{1,20})?', line) if user_matches: for username in user_matches.groups(): - username = username.strip() - user_account = {"user_name": username} - result["user_accounts"].append(user_account) + if username: + username = username.strip() + print(username) + user_account = {"user_name": username} + result["user_accounts"].append(user_account) except StopIteration: break else: From c98957ebc4c6141722aa77d7e122a26de055f8a2 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 25 Nov 2024 13:45:05 -0500 Subject: [PATCH 3/3] fix: address PR findings --- jc/parsers/net_localgroup.py | 18 +++++++++--------- jc/parsers/net_user.py | 10 +++++----- jc/parsers/route_print.py | 9 +++++---- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/jc/parsers/net_localgroup.py b/jc/parsers/net_localgroup.py index 6bd204b45..55ccfb8ee 100644 --- a/jc/parsers/net_localgroup.py +++ b/jc/parsers/net_localgroup.py @@ -3,15 +3,15 @@ Usage (cli): - $ net localgroup | jc --net-localgroup - $ net localgroup /domain | jc --net-localgroup - $ net localgroup Administrators | jc --net-localgroup - $ net localgroup Administrators /domain | jc --net-localgroup + $ net localgroup | jc --net-localgroup -p + $ net localgroup /domain | jc --net-localgroup -p + $ net localgroup Administrators | jc --net-localgroup -p + $ net localgroup Administrators /domain | jc --net-localgroup -p Usage (module): import jc - result = jc.parse('net-localgroup', net_localgroup_command_output) + result = jc.parse('net_localgroup', net_localgroup_command_output) Schema: @@ -40,7 +40,7 @@ Examples: - $ net localgroup | jc --net-localgroup -p | jq + $ net localgroup | jc --net-localgroup -p { "account_origin": null, "comment": null, @@ -57,8 +57,8 @@ } ] } - $ net localgroup Administrators | jc --net-localgroup -p | jq - $ net localgroup /domain | jc --net-localgroup -p | jq + $ net localgroup Administrators | jc --net-localgroup -p + $ net localgroup /domain | jc --net-localgroup -p """ @@ -74,7 +74,7 @@ class info(): author = 'joehacksalot' author_email = 'joehacksalot@gmail.com' compatible = ['windows'] - magic_commands = ['net-localgroup'] + magic_commands = ['net localgroup'] tags = ['command'] diff --git a/jc/parsers/net_user.py b/jc/parsers/net_user.py index 76de99610..87d614c93 100644 --- a/jc/parsers/net_user.py +++ b/jc/parsers/net_user.py @@ -11,7 +11,7 @@ Usage (module): import jc - result = jc.parse('net-user', net_user_command_output) + result = jc.parse('net_user', net_user_command_output) Schema: @@ -51,7 +51,7 @@ Examples: - $ net users | jc --net-user -p | jq + $ net users | jc --net-user -p { "account_origin": "\\\\WIN-SERVER16", "domain": "", @@ -184,6 +184,7 @@ from datetime import datetime import re import jc.utils +from jc.exceptions import ParseError class info(): @@ -193,7 +194,7 @@ class info(): author = 'joehacksalot' author_email = 'joehacksalot@gmail.com' compatible = ['windows'] - magic_commands = ['net-user'] + magic_commands = ['net user'] tags = ['command'] @@ -224,7 +225,7 @@ def parse(data, raw=False, quiet=False): return raw_output if raw else _process(raw_output) except Exception as e: if not quiet: - jc.utils.warning_message(['Could not parse data due to unexpected format.']) + raise ParseError('Could not parse data due to unexpected format.') return {} def _set_if_not_none(output_dict, key, value): @@ -430,7 +431,6 @@ def _parse(data): for username in user_matches.groups(): if username: username = username.strip() - print(username) user_account = {"user_name": username} result["user_accounts"].append(user_account) except StopIteration: diff --git a/jc/parsers/route_print.py b/jc/parsers/route_print.py index da7845383..d7cbd8563 100644 --- a/jc/parsers/route_print.py +++ b/jc/parsers/route_print.py @@ -1,4 +1,4 @@ -r"""jc - JSON Convert `route-print` command output parser +r"""jc - JSON Convert `route print` command output parser Usage (cli): @@ -8,7 +8,7 @@ Usage (module): import jc - result = jc.parse('route-print', route_print_command_output) + result = jc.parse('route_print', route_print_command_output) Schema: @@ -312,6 +312,7 @@ import re import jc.utils +from jc.exceptions import ParseError class info(): @@ -321,7 +322,7 @@ class info(): author = 'joehacksalot' author_email = 'joehacksalot@gmail.com' compatible = ['windows'] - magic_commands = ['route-print'] + magic_commands = ['route print'] tags = ['command'] @@ -352,7 +353,7 @@ def parse(data, raw=False, quiet=False): return raw_output if raw else _process(raw_output) except Exception as e: if not quiet: - jc.utils.warning_message(['Could not parse data due to unexpected format.']) + raise ParseError('Could not parse data due to unexpected format.') return {} def _process(proc_data):