commit a86f2c305eb355113c46e8873b858c3ffc80e768 Author: Raphaël Vinot Date: Mon Jul 25 18:48:08 2016 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..697c693 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +dist +pytaxonomies.egg-info +*.swp +*.pyc diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bdaffec --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: python + +cache: pip + +python: + - "3.3" + - "3.4" + - "3.5" + - "nightly" + +install: + - pip install coveralls + - pip install codecov + - pip install . + +script: + - nosetests --with-coverage --cover-package=pytaxonomies + +after_success: + - codecov + - coveralls diff --git a/README.md b/README.md new file mode 100644 index 0000000..3956468 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# PyTaxonomies + +Pythonic way to work with the taxonomies defined there: https://github.com/MISP/misp-taxonomies + diff --git a/bin/pytaxonomies b/bin/pytaxonomies new file mode 100644 index 0000000..33244f0 --- /dev/null +++ b/bin/pytaxonomies @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +from pytaxonomies import Taxonomies + +if __name__ == '__main__': + argParser = argparse.ArgumentParser(description='Use MISP taxonomies') + argParser.add_argument('-p', action='store_true', help='Print all taxonomies') + args = argParser.parse_args() + if args.p: + t = Taxonomies() + print(t) diff --git a/pytaxonomies/__init__.py b/pytaxonomies/__init__.py new file mode 100644 index 0000000..9ee35ce --- /dev/null +++ b/pytaxonomies/__init__.py @@ -0,0 +1 @@ +from .api import Taxonomies diff --git a/pytaxonomies/api.py b/pytaxonomies/api.py new file mode 100644 index 0000000..8f61d50 --- /dev/null +++ b/pytaxonomies/api.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import json +import requests +import os +import collections + + +class Entry(): + + def __init__(self, value, expanded, description): + self.value = value + self.expanded = expanded + self.description = description + + +class Predicate(collections.Mapping): + + def __init__(self, predicate, description, entries): + self.predicate = predicate + self.description = description + self.entries = {} + if entries: + self.__init_entries(entries) + + def __init_entries(self, entries): + for e in entries: + self.entries[e['value']] = Entry(e['value'], e['expanded'], + e.get('description')) + + def __getitem__(self, entry): + return self.entries[entry] + + def __iter__(self): + return iter(self.entries) + + def __len__(self): + return len(self.entries) + + +class Taxonomy(collections.Mapping): + + def __init__(self, taxonomy): + self.taxonomy = taxonomy + self.name = self.taxonomy['namespace'] + self.description = self.taxonomy['description'] + self.version = self.taxonomy['version'] + self.__init_predicates() + + def __init_predicates(self): + self.predicates = {} + entries = {} + if self.taxonomy.get('values'): + for v in self.taxonomy['values']: + if not entries.get(v['predicate']): + entries[v['predicate']] = [] + entries[v['predicate']] += v['entry'] + for p in self.taxonomy['predicates']: + self.predicates[p['value']] = Predicate(p['value'], p.get('expanded'), + entries.get(p['value'])) + + def __str__(self): + to_return = '' + for p, content in self.predicates.items(): + if content: + for k in content.keys(): + to_return += '{}:{}="{}"\n'.format(self.name, p, k) + else: + to_return += '{}:{}\n'.format(self.name, p) + return to_return + + def __getitem__(self, predicate): + return self.predicates[predicate] + + def __iter__(self): + return iter(self.predicates) + + def __len__(self): + return len(self.predicates) + + def amount_entries(self): + return sum([len(p) for p in self.predicates]) + + def print_expanded_entries(self): + to_return = '' + for p, content in self.predicates.items(): + if content: + for k, entry in content.items(): + to_return += '{}:{}="{}"\n'.format(self.name, p, entry.expanded) + else: + to_return += '{}:{}\n'.format(self.name, p) + return to_return + + +class Taxonomies(collections.Mapping): + + def __init__(self, manifest_url='https://raw.githubusercontent.com/MISP/misp-taxonomies/master/MANIFEST.json', + manifest_path=None): + if manifest_path: + self.loader = self.__load_path + self.manifest = self.loader(manifest_path) + else: + self.loader = self.__load_url + self.manifest = self.loader(manifest_url) + + if manifest_path: + self.url = os.path.dirname(os.path.realpath(manifest_path)) + self.version = self.manifest['version'] + self.license = self.manifest['license'] + self.description = self.manifest['description'] + self.taxonomies_names = [t['name'] for t in self.manifest['taxonomies']] + self.__init_taxonomies() + + def __load_path(self, path): + with open(path, 'r') as f: + return json.load(f) + + def __load_url(self, url): + return requests.get(url).json() + + def __make_uri(self, taxonomy_name): + return '{}{}/{}'.format(self.manifest['url'], taxonomy_name, self.manifest['path']) + + def __init_taxonomies(self): + self.taxonomies = {} + for t in self.manifest['taxonomies']: + uri = self.__make_uri(t['name']) + tax = self.loader(uri) + self.taxonomies[t['name']] = Taxonomy(tax) + + def __getitem__(self, name): + return self.taxonomies[name] + + def __iter__(self): + return iter(self.taxonomies) + + def __len__(self): + return len(self.taxonomies) + + def __str__(self): + to_return = '' + for k, taxonomy in self.taxonomies.items(): + to_return += '{}\n'.format(taxonomy.__str__()) + return to_return diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bad80a8 --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from setuptools import setup + + +setup( + name='pytaxonomies', + version='0.5', + author='Raphaël Vinot', + author_email='raphael.vinot@circl.lu', + maintainer='Raphaël Vinot', + url='https://github.com/MISP/PyTaxonomies', + description='Python API for the taxonomies.', + packages=['pytaxonomies'], + scripts=['bin/pytaxonomies'], + classifiers=[ + 'License :: OSI Approved :: BSD License', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Science/Research', + 'Intended Audience :: Telecommunications Industry', + 'Programming Language :: Python', + 'Topic :: Security', + 'Topic :: Internet', + ], + tests_requires=['nose'], + test_suite='nose.collector', + install_requires=['requests'], +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 0000000..282d039 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import unittest +from pytaxonomies import Taxonomies + + +class TestPyTaxonomies(unittest.TestCase): + + def setUp(self): + self.taxonomies = Taxonomies() + + def test_print(self): + print(self.taxonomies) + +if __name__ == "__main__": + unittest.main()