forked from maaaaz/fgpoliciestocsv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fgaddressestocsv.py
179 lines (138 loc) · 6.43 KB
/
fgaddressestocsv.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This file is part of fgpoliciestocsv.
#
# Copyright (C) 2014, 2022, Thomas Debize <tdebize at mail.com>
# All rights reserved.
#
# fgpoliciestocsv is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# fgpoliciestocsv is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with fgpoliciestocsv. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from os import path
import io
import sys
import re
import csv
import os
# OptionParser imports
from optparse import OptionParser
from optparse import OptionGroup
# Options definition
parser = OptionParser(usage="%prog [options]")
main_grp = OptionGroup(parser, 'Main parameters')
main_grp.add_option('-i', '--input-file', help='Partial or full Fortigate configuration file. Ex: fgfw.cfg')
main_grp.add_option('-o', '--output-file', help='Output csv file (default ./addresses-out.csv)', default=path.abspath(path.join(os.getcwd(), './addresses-out.csv')))
main_grp.add_option('-s', '--skip-header', help='Do not print the csv header', action='store_true', default=False)
main_grp.add_option('-n', '--newline', help='Insert a newline between each group for better readability', action='store_true', default=False)
main_grp.add_option('-d', '--delimiter', help='CSV delimiter (default ";")', default=';')
main_grp.add_option('-e', '--input-encoding', help='Input file encoding (default "utf-8")', default='utf-8')
main_grp.add_option('-f', '--output-encoding', help='Output file encoding (default "utf-8-sig" to make it easily viewable with MS Excel)', default='utf-8-sig')
parser.option_groups.extend([main_grp])
# Python 2 and 3 compatibility
if (sys.version_info < (3, 0)):
fd_read_options = 'r'
fd_write_options = 'wb'
else:
fd_read_options = 'r'
fd_write_options = 'w'
# Handful patterns
# -- Entering address definition block
p_entering_address_block = re.compile(r'^\s*config firewall address$', re.IGNORECASE)
# -- Exiting address definition block
p_exiting_address_block = re.compile(r'^end$', re.IGNORECASE)
# -- Commiting the current address definition and going to the next one
p_address_next = re.compile(r'^next$', re.IGNORECASE)
# -- Policy number
p_address_name = re.compile(r'^\s*edit\s+"(?P<address_name>.*)"$', re.IGNORECASE)
# -- Policy setting
p_address_set = re.compile(r'^\s*set\s+(?P<address_key>\S+)\s+(?P<address_value>.*)$', re.IGNORECASE)
# Functions
def parse(options):
"""
Parse the data according to several regexes
@param options: options
@rtype: return a list of addresses ( [ {'id' : '1', 'srcintf' : 'internal', ...}, {'id' : '2', 'srcintf' : 'external', ...}, ... ] )
and the list of unique seen keys ['id', 'srcintf', 'dstintf', ...]
"""
global p_entering_address_block, p_exiting_address_block, p_address_next, p_address_name, p_address_set
in_address_block = False
address_list = []
address_elem = {}
order_keys = []
with io.open(options.input_file, mode=fd_read_options, encoding=options.input_encoding) as fd_input:
for line in fd_input:
line = line.strip()
# We match a address block
if p_entering_address_block.search(line):
in_address_block = True
# We are in a address block
if in_address_block:
if p_address_name.search(line):
address_name = p_address_name.search(line).group('address_name')
address_elem['name'] = address_name
if not('name' in order_keys):
order_keys.append('name')
# We match a setting
if p_address_set.search(line):
address_key = p_address_set.search(line).group('address_key')
if not(address_key in order_keys):
order_keys.append(address_key)
address_value = p_address_set.search(line).group('address_value').strip()
address_value = re.sub('["]', '', address_value)
address_elem[address_key] = address_value
# We are done with the current address id
if p_address_next.search(line):
address_list.append(address_elem)
address_elem = {}
# We are exiting the address block
if p_exiting_address_block.search(line):
in_address_block = False
return (address_list, order_keys)
def generate_csv(results, keys, options):
"""
Generate a plain csv file
"""
if results and keys:
with io.open(options.output_file, mode=fd_write_options, encoding=options.output_encoding) as fd_output:
spamwriter = csv.writer(fd_output, delimiter=options.delimiter, quoting=csv.QUOTE_ALL, lineterminator='\n')
if not(options.skip_header):
spamwriter.writerow(keys)
for address in results:
output_line = []
for key in keys:
if key in address.keys():
output_line.append(address[key])
else:
output_line.append('')
spamwriter.writerow(output_line)
if options.newline:
spamwriter.writerow('')
fd_output.close()
return None
def main():
"""
Dat main
"""
global parser
options, arguments = parser.parse_args()
if (options.input_file == None):
parser.error('Please specify a valid input file')
if (sys.version_info < (3, 0)):
options.output_encoding = None
results, keys = parse(options)
generate_csv(results, keys, options)
return None
if __name__ == "__main__" :
main()