- PVSM.RU - https://www.pvsm.ru -
Приветствую, коллеги!
В процессе исследования языка OVAL [1] и концепции SCAP [2]-сканера я столкнулся с довольно серьезной проблемой, а именно с отсутствием удобных инструментов для создания контента на языке OVAL. [3] Нет, я не утверждаю, что нет совсем ничего. Есть небольшой набор утилит [4], представленных на официальном сайте. Большая часть из них платная, остальные же представляют собой не очень удобные решения, больше всего похожие на XML-Notepad [5]. В итоге я решил создать небольшой необходимый мне для работы инструмент самостоятельно, используя в качестве языка Python.
Такой выбор обусловлен хорошей репутацией языка Python как «rapid development language» [6] и наличием богатой палитры сторонних библиотек. Вооружившись документацией по языку, я попытался воссоздать задумки MITRE [7] в реальности. Конечной целью для себя я поставил реализацию объектов языка OVAL и системы их хранения и индексации.
Первоочередным вопросом был выбор способа хранения собранной информации. Первым (и неудачным) решением стал SQLite 3 [8]. Я не буду особо задерживаться на этом печальном опыте, но как показала практика, — хранение непредсказуемого дерева в реляционной базе данных слишком сложная задача для меня. Поэтому я обратил внимание на базы данных NoSQL [9]. Так как я планировал ограничиться монопольным использованием базы, мой выбор пал на ZoDB [10]. Конечно же, это решение тоже было поспешным. Но переделывать на MongoDB [11] было уже поздно. Так что для дальнейших выкладок кода хотелось бы отметить, что PersistentMapping() — это, по сути, обычный dict(). Приняв во внимание неопределенность восприятия хэшмапов базой данных, в качестве хэшмапов я использовал ключи того же класса. PersistentList() — это эквивалент list(). Данная замена необходима для хранения подобных структур в ZoDB. Это обусловлено внутренней логикой работы базы данных. Для сохранения класса в базе данных целиком необходимо, что-бы класс являлся наследником класса persistent.Persistent.
Кроме того, прошу заранее меня простить за не-шаблонное именование переменных, методов и классов и возможную «косорукость»: я не волшебник, я только учусь. И с удовольствием приму любые правки и замечания от более осведомленных коллег.
Во избежание цитирования излишне больших кусков кода сразу привожу ссылку на исходник на code.google.com [12].
В целом при анализе структур языка OVAL я отметил, что его главной особенностью является расширяемость. Если необходимо ввести собственную структуру, достаточно описать ее в XSD, определяющим ваш namespace. Поскольку структуры в большинстве своем представляют типовые наборы данных, то я решил создать выделенный класс для каждого объекта языка. Основная цель такой реализации — создание контейнера для информации, который будет удобно закладывать в БД. Таким образом, в качестве базового объекта выступил элемент OBJECT:
# UNIVERSAL OVAL OBJECTS
class oval_object(persistent.Persistent):
def __init__(self):
self.id = str()
self.tag = "object"
self.namespace = str()
self.oval = str()
self.version = 0
self.vHash = 0
self.deprecated = False
self.variables = PersistentList()
self.notes = PersistentList()
self.comment = str()
self.signature = str()
def assign_id(self, UniqId):
self.id = "oval:"+self.oval+":obj:"+str(UniqId)
def hash(self):
summ_hash = 0
summ_hash += hash(self.tag)
summ_hash += hash(self.namespace)
summ_hash += hash(self.comment)
for var in self.variables:
if isinstance(var, variable):
summ_hash += var.hashme()
else:
raise Exception("Error input parameters in object variables")
return summ_hash
def hashme(self):
self.vHash = self.hash()
return self.vHash
def clearme(self):
hashmap = PersistentMapping()
for var in self.variables:
var.clearme()
var_hash = var.hashme()
if var_hash not in hashmap.keys():
hashmap[var_hash] = None
else:
self.variables.remove(var)
Данный класс является базовым для всех основных элементов OVAL.Definition [13], Test [14], State [15] и Oval_variable [16] будут являться наследниками этого класса. Для того что-бы реализовать возможность сравнения элементов, были созданы методы hash() (универсальный для всех) и hashme()(переопределяемый у потомков).
Как видно из названий переменных, все основные структуры заданы статически. Динамическими же являются возможные «потомки», структуры которых мы не знаем. Для реализации таких «потомков» я сделал класс Variable, во многом повторяющий по идеологии Element из ElementTree [17]:
class variable(persistent.Persistent):
def __init__(self, tag=str(), body = str(), attributes = None, variables = None):
if not attributes:
self.attributes = PersistentMapping()
if not variables:
self.variables = PersistentList()
if attributes and not isinstance(attributes, PersistentMapping):
attributes = PersistentMapping(attributes)
if variables and not isinstance(variables, PersistentList):
variables = PersistentList(variables)
self.tag = tag
self.body = body
if attributes:
self.attributes = attributes
if variables:
self.variables = variables
self.vHash = 0
def clearme(self):
hashmap = PersistentMapping()
for var in self.variables:
var.clearme()
var_hash = var.hashme()
if var_hash not in hashmap.keys():
hashmap[var_hash] = None
else:
self.variables.remove(var)
def hashme(self):
summ_hash = 0
summ_hash += hash(self.tag)
summ_hash += hash(self.body)
if self.attributes:
for attribute in self.attributes.keys():
summ_hash += hash(attribute) + hash(self.attributes[attribute])
if self.variables:
for var in self.variables:
if isinstance(var, variable):
if var != self:
summ_hash += var.hashme()
else:
raise Exception("Error input parameters in variables. Self append ?")
self.vHash = summ_hash
return self.vHash
Как и в случае с OBJECT, потребовалась функция расчета хэша. Структура класса максимально подогнана к ElementTree.Element, что бы упростить в будущем выгрузку базы данных в XML-формат.
В целом получился набор объектов, удобных для заполнения. Но мало их заполнить: при создании собственных объектов необходимо выполнять требования регулятора (в нашем случае MITRE [18]) к заполнению и храненю новыхDefinition [19]. Соответственно, мне потребовалась система индексации и контроля изменения версии Definition. Для этого был создан класс oval_suite() [20]. В нем реализован базовый набор методов (put [21], get [22], delete [23], search [24], import_xml [25], export_xml [26]), который облегчает работу с контентом и позволяет не заморачиваться на вышеупомянутых проблемах.
Одна из специфичных проблем, с которыми я столкнулся в процессе создания oval_suite(),- нехватка памяти при попытке экспортировать в XML-формат большой объем данных (порядка 100Мб) за один заход. Поэтому для реализации этой функции пришлось создавать конечный файл поэтапно и использовать функцию удаления элементов ElementTree.Element, позаимствованную из StackOverflow [27]:
def _garbager(self, root):
for element in list(root):
self._garbager(element)
root.clear()
del root
В целом и общем, получились наброски инструментария для работы с OVAL на Python, который может пригодиться при сборке и создании секьюрити-контента. Данный модуль позволяет выполнять сборку секьюрити-контента и «упаковку» его в базу данных с контролем версий и уникальности идентификаторов.
Спасибо за внимание! Надеюсь, кто-нибудь заинтересуется тематикой OVAL и данные наработки окажутся ему полезны.
Автор: isox
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/3623
Ссылки в тексте:
[1] OVAL: http://oval.mitre.org/
[2] SCAP: http://habrahabr.ru/company/pt/blog/139296/
[3] OVAL.: http://habrahabr.ru/post/136046/
[4] утилит: http://oval.mitre.org/adoption/capabilitylist.html#at
[5] XML-Notepad: http://www.google.ru/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cts=1331724886539&sqi=2&ved=0CDMQFjAA&url=http%3A%2F%2Fwww.microsoft.com%2Fdownloads%2Fdetails.aspx%3Ffamilyid%3D72d6aa49-787d-4118-ba5f-4f30fe913628&ei=UoJgT5n3NKzN4QTA7421Dg&usg=AFQjCNHWESIp-76Tf5NTZokHiE0ZbNU3ng
[6] «rapid development language»: http://www.python.org/about/success/strakt/
[7] MITRE: http://oval.mitre.org/language/index.html
[8] SQLite 3: http://docs.python.org/library/sqlite3.html
[9] NoSQL: http://ru.wikipedia.org/wiki/NoSQL
[10] ZoDB: http://www.zodb.org/
[11] MongoDB: http://www.mongodb.org/
[12] исходник на code.google.com: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py
[13] Definition: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#167
[14] Test: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#150
[15] State: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#124
[16] Oval_variable: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#137
[17] ElementTree: http://docs.python.org/library/xml.etree.elementtree.html
[18] MITRE: http://mitre.org/
[19] Definition: http://oval.mitre.org/adoption/requirements.html#repository
[20] oval_suite(): https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#206
[21] put: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#299
[22] get: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#480
[23] delete: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#486
[24] search: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#520
[25] import_xml: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#803
[26] export_xml: https://code.google.com/p/oval-core/source/browse/oval_oodbs.py#542
[27] StackOverflow: http://stackoverflow.com
Нажмите здесь для печати.