- PVSM.RU - https://www.pvsm.ru -

Реализация инструментов для создания контента OVAL® на Python

Приветствую, коллеги! image
В процессе исследования языка 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