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
|
# Copyright 2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Contains the seting auditor used to clean up security.cfg."""
__metaclass__ = type
__all__ = [
"SettingsAuditor",
]
from collections import defaultdict
import re
class SettingsAuditor:
"""Reads the security.cfg file and collects errors.
We can't just use ConfigParser for this case, as we're doing our own
specialized parsing--not interpreting the settings, but verifying."""
header_regex = re.compile(r'.*?(?=\[)', re.MULTILINE|re.DOTALL)
section_regex = re.compile(
r'\[.*?\].*?(?=(\[)|($\Z))', re.MULTILINE|re.DOTALL)
section_label_regex = re.compile(r'\[.*\]')
def __init__(self, data):
self.data = data
self.errors = {}
self.current_section = ''
self.observed_settings = defaultdict(lambda: 0)
def _getHeader(self):
"""Removes the header comments from the security file.
The comments at the start of the file aren't something we
want to kill.
"""
header = self.header_regex.match(self.data)
if header is not None:
header = header.group()
self.data = self.data.replace(header, '')
return header
def _strip(self, data):
data = data.split('\n')
data = [d.strip() for d in data]
return '\n'.join(d for d in data if not (d.startswith('#') or d == ''))
def _getSectionName(self, line):
if line.strip().startswith('['):
return self.section_regex.match(line).group()
else:
return None
def _separateConfigBlocks(self):
# We keep the copy of config_labels so we can keep them in order.
self.config_blocks = {}
self.config_labels = []
self.data = self._strip(self.data)
while self.data != '':
section = self.section_regex.match(self.data)
section = section.group()
self.data = self.data.replace(section, '')
label = self.section_label_regex.match(section).group()
self.config_labels.append(label)
self.config_blocks[label] = section
def _processBlocks(self):
for block in self.config_labels:
data = set(self.config_blocks[block].split('\n')[1:])
data.discard('')
data = [line for line in sorted(data)
if line.strip() != '' and
not line.strip().startswith('#')]
self._checkForDupes(data, block)
data = '\n'.join([block] + data)
self.config_blocks[block] = data
def _checkForDupes(self, data, label):
settings = defaultdict(lambda: 0)
for line in data:
settings[self._getSetting(line)] += 1
dupe_settings = [setting for setting in settings.keys()
if settings[setting] > 1]
if dupe_settings != []:
self.errors[label] = dupe_settings
def _getSetting(self, line):
return line.split()[0]
def audit(self):
header = self._getHeader()
self._separateConfigBlocks()
self._processBlocks()
data = []
for label in self.config_labels:
data.append(self.config_blocks[label])
return '%s%s' % (header, '\n\n'.join(data))
@property
def error_data(self):
error_data = []
error_data.append("The following errors were found in security.cfg")
error_data.append("-----------------------------------------------")
for section in self.errors.keys():
error_data.append("In section: %s" % section)
for setting in self.errors[section]:
error_data.append('\tDuplicate setting found: %s' % setting)
return '\n'.join(error_data)
|