1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-10-08 23:22:21 +02:00

net-user parser fixup

This commit is contained in:
Kelly Brazil
2025-09-08 14:25:44 -07:00
parent ffaf455d43
commit 883f0c9cf7
2 changed files with 115 additions and 59 deletions

View File

@@ -74,7 +74,7 @@ Examples:
]
}
$ net users /domain | jc --net-user -p | jq
$ net users /domain | jc --net-user -p
{
"account_origin": "\\\\DESKTOP-WIN10-PRO.somecompany.corp",
"domain": "somecompany.corp",
@@ -121,7 +121,7 @@ Examples:
]
}
$ net users Administrator | jc --net-user -p | jq
$ net users Administrator | jc --net-user -p
{
"domain": "",
"user_accounts": [
@@ -179,12 +179,9 @@ Examples:
]
}
"""
from datetime import datetime
import re
import jc.utils
from jc.exceptions import ParseError
class info():
@@ -193,7 +190,7 @@ class info():
description = '`net user` command parser'
author = 'joehacksalot'
author_email = 'joehacksalot@gmail.com'
compatible = ['windows']
compatible = ['win32']
magic_commands = ['net user']
tags = ['command']
@@ -220,14 +217,13 @@ def parse(data, raw=False, quiet=False):
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:
raise ParseError('Could not parse data due to unexpected format.')
return {}
raw_output = _parse(data)
if not raw_output:
raw_output = {}
return raw_output if raw else _process(raw_output)
def _set_if_not_none(output_dict, key, value):
if value is not None:
output_dict[key] = value
@@ -267,7 +263,11 @@ def _process(proc_data):
_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
if not proc_data:
proc_data = {}
return proc_data
class _PushbackIterator:
@@ -339,7 +339,7 @@ def _parse(data):
"domain": "",
"user_accounts": []
}
lines = data.splitlines()
lines = [line.rstrip() for line in lines if line.strip() != ""]
@@ -358,11 +358,11 @@ def _parse(data):
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
@@ -371,7 +371,7 @@ def _parse(data):
line = line.strip()
if not line:
continue # Skip empty lines
match = kv_pattern.match(line)
if "The command completed" in line:
break
@@ -437,5 +437,5 @@ def _parse(data):
break
else:
raise ValueError(f"Unexpected line: {line}")
return result

View File

@@ -1,56 +1,112 @@
import json
import os
import unittest
import jc.parsers.ipconfig
import jc.parsers.net_localgroup
import jc.parsers.net_user
import json
from typing import Dict
from jc.parsers.net_user import parse
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"
]
f_in: Dict = {}
f_json: Dict = {}
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")
@classmethod
def setUpClass(cls):
fixtures = {
'windows_xp_net_user': (
'fixtures/windows/windows-xp/net_user.out',
'fixtures/windows/windows-xp/net_user.json'),
'windows_7_net_user': (
'fixtures/windows/windows-7/net_user.out',
'fixtures/windows/windows-7/net_user.json'),
'windows_2008_net_user': (
'fixtures/windows/windows-2008/net_user.out',
'fixtures/windows/windows-2008/net_user.json'),
'windows_2016_net_user': (
'fixtures/windows/windows-2016/net_user.out',
'fixtures/windows/windows-2016/net_user.json'),
'windows_10_net_user': (
'fixtures/windows/windows-10/net_user.out',
'fixtures/windows/windows-10/net_user.json'),
'windows_11_net_user': (
'fixtures/windows/windows-11/net_user.out',
'fixtures/windows/windows-11/net_user.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()))
for file, filepaths in fixtures.items():
with open(os.path.join(THIS_DIR, filepaths[0]), 'r', encoding='utf-8') as a, \
open(os.path.join(THIS_DIR, filepaths[1]), 'r', encoding='utf-8') as b:
cls.f_in[file] = a.read()
cls.f_json[file] = json.loads(b.read())
def varName(self, path):
return (
path.replace("tests/fixtures/windows", "")
.replace("-", "_")
.replace("/", "_")
def test_net_user_nodata(self):
"""
Test 'net_user' with no data
"""
self.assertEqual(parse('', quiet=True), {})
def test_net_user_windows_xp(self):
"""
Test 'net_user' on Windows XP
"""
self.assertEqual(
parse(self.f_in['windows_xp_net_user'], quiet=True),
self.f_json['windows_xp_net_user']
)
def test_windows_net_localgroup(self):
def test_net_user_windows_7(self):
"""
Test a sample Windows "net localgroup" command output
Test 'net_user' on Windows 7
"""
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)
self.assertEqual(
parse(self.f_in['windows_7_net_user'], quiet=True),
self.f_json['windows_7_net_user']
)
if __name__ == "__main__":
def test_net_user_windows_2008(self):
"""
Test 'net_user' on Windows 2008
"""
self.assertEqual(
parse(self.f_in['windows_2008_net_user'], quiet=True),
self.f_json['windows_2008_net_user']
)
def test_net_user_windows_2016(self):
"""
Test 'net_user' on Windows 2016
"""
self.assertEqual(
parse(self.f_in['windows_2016_net_user'], quiet=True),
self.f_json['windows_2016_net_user']
)
def test_net_user_windows_10(self):
"""
Test 'net_user' on Windows 10
"""
self.assertEqual(
parse(self.f_in['windows_10_net_user'], quiet=True),
self.f_json['windows_10_net_user']
)
def test_net_user_windows_11(self):
"""
Test 'net_user' on Windows 11
"""
self.assertEqual(
parse(self.f_in['windows_11_net_user'], quiet=True),
self.f_json['windows_11_net_user']
)
if __name__ == '__main__':
unittest.main()