mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2025-07-13 01:20:24 +02:00
update schema and add normalized fields
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
"""jc - JSON CLI output utility `ufw app info [application]` command output parser
|
"""jc - JSON CLI output utility `ufw app info [application]` command output parser
|
||||||
|
|
||||||
|
Because `ufw` application definitions allow overlapping ports and port ranges, this parser preserves that behavior, but also provides `normalized` lists and ranges that remove duplicate ports and merge overlapping ranges.
|
||||||
|
|
||||||
Usage (cli):
|
Usage (cli):
|
||||||
|
|
||||||
@ -17,29 +18,45 @@ Usage (module):
|
|||||||
Schema:
|
Schema:
|
||||||
|
|
||||||
{
|
{
|
||||||
"profile": string,
|
"profile": string,
|
||||||
"title": string,
|
"title": string,
|
||||||
"description": string,
|
"description": string,
|
||||||
"ports": {
|
"tcp_list": [
|
||||||
"tcp_list": [
|
integer
|
||||||
integer
|
],
|
||||||
],
|
"tcp_ranges": [
|
||||||
"tcp_ranges": [
|
{
|
||||||
{
|
"start": integer, # 'any' is converted to start/end: 0/65535
|
||||||
"start": integer, # 'any' is converted to start/end: 0/65535
|
"end": integer
|
||||||
"end": integer
|
}
|
||||||
}
|
],
|
||||||
],
|
"udp_list": [
|
||||||
"udp_list": [
|
integer
|
||||||
integer
|
],
|
||||||
],
|
"udp_ranges": [
|
||||||
"upd_ranges": [
|
{
|
||||||
{
|
"start": integer, # 'any' is converted to start/end: 0/65535
|
||||||
"start": integer, # 'any' is converted to start/end: 0/65535
|
"end": integer
|
||||||
"end": integer
|
}
|
||||||
}
|
],
|
||||||
]
|
"normalized_tcp_list": [
|
||||||
}
|
integers # duplicates and overlapping are removed
|
||||||
|
],
|
||||||
|
"normalized_tcp_ranges": [
|
||||||
|
{
|
||||||
|
"start": integer, # 'any' is converted to start/end: 0/65535
|
||||||
|
"end": integers # overlapping are merged
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"normalized_udp_list": [
|
||||||
|
integer
|
||||||
|
],
|
||||||
|
"normalized_udp_ranges": [
|
||||||
|
{
|
||||||
|
"start": integer, # 'any' is converted to start/end: 0/65535
|
||||||
|
"end": integers # overlapping are merged
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
@ -78,9 +95,50 @@ def _process(proc_data):
|
|||||||
|
|
||||||
Dictionary. Structured to conform to the schema.
|
Dictionary. Structured to conform to the schema.
|
||||||
"""
|
"""
|
||||||
|
# convert to ints
|
||||||
|
int_list = ['start', 'end']
|
||||||
|
|
||||||
# rebuild output for added semantic information
|
if 'tcp_list' in proc_data:
|
||||||
# use helper functions in jc.utils for int, float, bool conversions and timestamps
|
proc_data['tcp_list'] = [int(p) for p in proc_data['tcp_list']]
|
||||||
|
|
||||||
|
if 'udp_list' in proc_data:
|
||||||
|
proc_data['udp_list'] = [int(p) for p in proc_data['udp_list']]
|
||||||
|
|
||||||
|
for protocol in ['tcp', 'udp']:
|
||||||
|
if protocol + '_ranges' in proc_data:
|
||||||
|
for i, item in enumerate(proc_data[protocol + '_ranges']):
|
||||||
|
for key in item:
|
||||||
|
if key in int_list:
|
||||||
|
proc_data[protocol + '_ranges'][i][key] = int(proc_data[protocol + '_ranges'][i][key])
|
||||||
|
|
||||||
|
# create normalized port lists and port ranges (remove duplicates and merge ranges)
|
||||||
|
# dump ranges into a set of 0 - 65535
|
||||||
|
# if items in the port list are in the set, then remove them
|
||||||
|
# iterate through the set to find gaps and create new ranges based on them
|
||||||
|
for protocol in ['tcp', 'udp']:
|
||||||
|
port_set = set()
|
||||||
|
if protocol + '_ranges' in proc_data:
|
||||||
|
for item in proc_data[protocol + '_ranges']:
|
||||||
|
port_set.update(range(item['start'], item['end'] + 1))
|
||||||
|
|
||||||
|
proc_data['normalized_' + protocol + '_list'] = sorted(set([p for p in proc_data[protocol + '_list'] if p not in port_set]))
|
||||||
|
|
||||||
|
new_port_ranges = []
|
||||||
|
state = 'findstart' # 'findstart' or 'findend'
|
||||||
|
for port in range(0, 65535 + 2):
|
||||||
|
if state == 'findstart':
|
||||||
|
port_range_obj = {}
|
||||||
|
if port in port_set:
|
||||||
|
port_range_obj['start'] = port
|
||||||
|
state = 'findend'
|
||||||
|
continue
|
||||||
|
if state == 'findend':
|
||||||
|
if port not in port_set:
|
||||||
|
port_range_obj['end'] = port - 1
|
||||||
|
new_port_ranges.append(port_range_obj)
|
||||||
|
state = 'findstart'
|
||||||
|
|
||||||
|
proc_data['normalized_' + protocol + '_ranges'] = new_port_ranges
|
||||||
|
|
||||||
return proc_data
|
return proc_data
|
||||||
|
|
||||||
@ -94,10 +152,10 @@ def _parse_port_list(data, port_list=None):
|
|||||||
port_list = []
|
port_list = []
|
||||||
|
|
||||||
data = data.split(',')
|
data = data.split(',')
|
||||||
data_list = [int(p) for p in data if ':' not in p and 'any' not in p]
|
data_list = [p.strip() for p in data if ':' not in p and 'any' not in p]
|
||||||
port_list.extend(data_list)
|
port_list.extend(data_list)
|
||||||
|
|
||||||
return sorted(list(set(port_list)))
|
return port_list
|
||||||
|
|
||||||
|
|
||||||
def _parse_port_range(data, range_list=None):
|
def _parse_port_range(data, range_list=None):
|
||||||
@ -122,8 +180,8 @@ def _parse_port_range(data, range_list=None):
|
|||||||
|
|
||||||
for range_ in ranges:
|
for range_ in ranges:
|
||||||
range_obj = {
|
range_obj = {
|
||||||
'start': int(range_.split(':')[0]),
|
'start': range_.split(':')[0],
|
||||||
'end': int(range_.split(':')[1])
|
'end': range_.split(':')[1]
|
||||||
}
|
}
|
||||||
range_list.append(range_obj)
|
range_list.append(range_obj)
|
||||||
|
|
||||||
@ -169,18 +227,17 @@ def parse(data, raw=False, quiet=False):
|
|||||||
|
|
||||||
if line.startswith('Ports:'):
|
if line.startswith('Ports:'):
|
||||||
ports = True
|
ports = True
|
||||||
ports_obj = {'ports': {}}
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if ports:
|
if ports:
|
||||||
line_list = line.rsplit('/', maxsplit=1)
|
line_list = line.rsplit('/', maxsplit=1)
|
||||||
if len(line_list) == 2:
|
if len(line_list) == 2:
|
||||||
if line_list[1] == 'tcp':
|
if line_list[1] == 'tcp':
|
||||||
ports_obj['ports']['tcp_list'] = _parse_port_list(line_list[0])
|
raw_output['tcp_list'] = _parse_port_list(line_list[0])
|
||||||
ports_obj['ports']['tcp_ranges'] = _parse_port_range(line_list[0])
|
raw_output['tcp_ranges'] = _parse_port_range(line_list[0])
|
||||||
elif line_list[1] == 'udp':
|
elif line_list[1] == 'udp':
|
||||||
ports_obj['ports']['udp_list'] = _parse_port_list(line_list[0])
|
raw_output['udp_list'] = _parse_port_list(line_list[0])
|
||||||
ports_obj['ports']['udp_ranges'] = _parse_port_range(line_list[0])
|
raw_output['udp_ranges'] = _parse_port_range(line_list[0])
|
||||||
|
|
||||||
# 'any' case
|
# 'any' case
|
||||||
else:
|
else:
|
||||||
@ -189,24 +246,24 @@ def parse(data, raw=False, quiet=False):
|
|||||||
u_list = []
|
u_list = []
|
||||||
u_range = []
|
u_range = []
|
||||||
|
|
||||||
if 'tcp_list' in ports_obj['ports']:
|
if 'tcp_list' in raw_output:
|
||||||
t_list = ports_obj['ports']['tcp_list']
|
t_list = raw_output['tcp_list']
|
||||||
|
|
||||||
if 'tcp_ranges' in ports_obj['ports']:
|
if 'tcp_ranges' in raw_output:
|
||||||
t_range = ports_obj['ports']['tcp_ranges']
|
t_range = raw_output['tcp_ranges']
|
||||||
|
|
||||||
if 'udp_list' in ports_obj['ports']:
|
if 'udp_list' in raw_output:
|
||||||
u_list = ports_obj['ports']['udp_list']
|
u_list = raw_output['udp_list']
|
||||||
|
|
||||||
if 'udp_ranges' in ports_obj['ports']:
|
if 'udp_ranges' in raw_output:
|
||||||
u_range = ports_obj['ports']['udp_ranges']
|
u_range = raw_output['udp_ranges']
|
||||||
|
|
||||||
ports_obj['ports']['tcp_list'] = _parse_port_list(line, t_list)
|
raw_output['tcp_list'] = _parse_port_list(line, t_list)
|
||||||
ports_obj['ports']['tcp_ranges'] = _parse_port_range(line, t_range)
|
raw_output['tcp_ranges'] = _parse_port_range(line, t_range)
|
||||||
ports_obj['ports']['udp_list'] = _parse_port_list(line, u_list)
|
raw_output['udp_list'] = _parse_port_list(line, u_list)
|
||||||
ports_obj['ports']['udp_ranges'] = _parse_port_range(line, u_range)
|
raw_output['udp_ranges'] = _parse_port_range(line, u_range)
|
||||||
|
|
||||||
raw_output.update(ports_obj)
|
raw_output.update(raw_output)
|
||||||
|
|
||||||
if raw:
|
if raw:
|
||||||
return raw_output
|
return raw_output
|
||||||
|
Reference in New Issue
Block a user