mirror of https://github.com/MISP/PyMISP
Merge branch 'main' of github.com:misp/pymisp
commit
afb1c8f2d0
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
|
@ -56,7 +56,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
|
@ -69,6 +69,6 @@ jobs:
|
|||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [3.8, 3.9, '3.10', '3.11']
|
||||
python-version: [3.8, 3.9, '3.10', '3.11', '3.12']
|
||||
|
||||
steps:
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
submodules: recursive
|
||||
|
||||
- name: Set up Python ${{matrix.python-version}}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{matrix.python-version}}
|
||||
|
||||
|
|
@ -34,7 +34,12 @@ jobs:
|
|||
- name: Test with nosetests
|
||||
run: |
|
||||
poetry run pytest --cov=pymisp tests/test_*.py
|
||||
poetry run mypy tests/testlive_comprehensive.py tests/test_mispevent.py tests/testlive_sync.py pymisp
|
||||
poetry run mypy .
|
||||
|
||||
- name: Test with nosetests with orjson
|
||||
run: |
|
||||
pip3 install orjson
|
||||
poetry run pytest --cov=pymisp tests/test_*.py
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
|
|
|
|||
247
CHANGELOG.txt
247
CHANGELOG.txt
|
|
@ -2,6 +2,250 @@ Changelog
|
|||
=========
|
||||
|
||||
|
||||
v2.4.185 (2024-02-16)
|
||||
---------------------
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump deps, version. [Raphaël Vinot]
|
||||
|
||||
|
||||
v2.4.184.3 (2024-02-12)
|
||||
-----------------------
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Bump version. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
|
||||
Fix
|
||||
~~~
|
||||
- Properly get body from message, without headers. [Raphaël Vinot]
|
||||
- Remove from __all__ entries that shouldn't be there. [Raphaël Vinot]
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
- Build(deps-dev): bump jupyter-lsp from 2.2.1 to 2.2.2.
|
||||
[dependabot[bot]]
|
||||
|
||||
Bumps [jupyter-lsp](https://github.com/jupyter-lsp/jupyterlab-lsp) from 2.2.1 to 2.2.2.
|
||||
- [Release notes](https://github.com/jupyter-lsp/jupyterlab-lsp/releases)
|
||||
- [Changelog](https://github.com/jupyter-lsp/jupyterlab-lsp/blob/main/CHANGELOG.md)
|
||||
- [Commits](https://github.com/jupyter-lsp/jupyterlab-lsp/commits)
|
||||
|
||||
---
|
||||
updated-dependencies:
|
||||
- dependency-name: jupyter-lsp
|
||||
dependency-type: indirect
|
||||
...
|
||||
|
||||
|
||||
v2.4.184.2 (2024-02-06)
|
||||
-----------------------
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Add changelog. [Raphaël Vinot]
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Re-add ExpandedPyMISP, with a warning. [Raphaël Vinot]
|
||||
|
||||
Fix
|
||||
~~~
|
||||
- Do not throw a warning every time one import pymisp... [Raphaël Vinot]
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
- Build(deps): bump codecov/codecov-action from 3 to 4.
|
||||
[dependabot[bot]]
|
||||
|
||||
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
|
||||
- [Release notes](https://github.com/codecov/codecov-action/releases)
|
||||
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
|
||||
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)
|
||||
|
||||
---
|
||||
updated-dependencies:
|
||||
- dependency-name: codecov/codecov-action
|
||||
dependency-type: direct:production
|
||||
update-type: version-update:semver-major
|
||||
...
|
||||
|
||||
|
||||
v2.4.184.1 (2024-02-06)
|
||||
-----------------------
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Re-add ExpandedPyMISP, with a warning. [Raphaël Vinot]
|
||||
|
||||
|
||||
v2.4.184 (2024-02-02)
|
||||
---------------------
|
||||
|
||||
New
|
||||
~~~
|
||||
- Enable support for python 3.12. [Raphaël Vinot]
|
||||
- Relationship_type in tag. [Raphaël Vinot]
|
||||
|
||||
Fix https://github.com/MISP/MISP/issues/9483
|
||||
- [internal] Add support for orjson. [Jakub Onderka]
|
||||
|
||||
orjson is much faster library for decoding and encoding JSON formats
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Bump deps, version, objects. [Raphaël Vinot]
|
||||
- Remove IntEnum. [Raphaël Vinot]
|
||||
- Add even more debug for gha. [Raphaël Vinot]
|
||||
- Add some debug for gha. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- Add more strict typing, not done yet. [Raphaël Vinot]
|
||||
- Add a bunch more typing. [Raphaël Vinot]
|
||||
- Use typing info of lief. [Raphaël Vinot]
|
||||
- First batch of changes for strict typing. [Raphaël Vinot]
|
||||
- Update typing to please lief. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- [internal] Simplify code. [Jakub Onderka]
|
||||
- [internal] User faster method to convert bytes to str. [Jakub Onderka]
|
||||
- New annotations in tests. [Raphaël Vinot]
|
||||
- Initial changes to use new annotations. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- Bump deps, try to install with python 3.12. [Raphaël Vinot]
|
||||
- Make the publish_timestamp a string, as per specs. [Raphaël Vinot]
|
||||
- [internal] Update poetry.lock. [Jakub Onderka]
|
||||
|
||||
Fix
|
||||
~~~
|
||||
- Revert typing changes. [Raphaël Vinot]
|
||||
- More responses athat are lists. [Raphaël Vinot]
|
||||
- Another call that cn be a list or a dict. [Raphaël Vinot]
|
||||
- Do not cast enum. [Raphaël Vinot]
|
||||
- More fixes to support responses from MISP. [Raphaël Vinot]
|
||||
- Handle list responses properly. [Raphaël Vinot]
|
||||
- Import FileObject as needed. [Raphaël Vinot]
|
||||
- Also skip docs from mypy. [Raphaël Vinot]
|
||||
- Run mypy on what I want. [Raphaël Vinot]
|
||||
- Compatibility with python 3.8. [Raphaël Vinot]
|
||||
- Python < 3.10 support on typing, for good. [Raphaël Vinot]
|
||||
- Python < 3.10 support on typing. [Raphaël Vinot]
|
||||
- Rollback tests on python 3.12 as lief is not supported yet. [Raphaël
|
||||
Vinot]
|
||||
- Add missing wheel. [Raphaël Vinot]
|
||||
- Make publish_timestamp a string in tests. [Raphaël Vinot]
|
||||
- [internal] README typos. [Jakub Onderka]
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
- Revert "fix: More responses athat are lists" [Raphaël Vinot]
|
||||
|
||||
This reverts commit 709a10c64c0513b515f25c3ecfb9eb577b55084b.
|
||||
- Build(deps): bump jinja2 from 3.1.2 to 3.1.3. [dependabot[bot]]
|
||||
|
||||
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
|
||||
- [Release notes](https://github.com/pallets/jinja/releases)
|
||||
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
|
||||
- [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3)
|
||||
|
||||
---
|
||||
updated-dependencies:
|
||||
- dependency-name: jinja2
|
||||
dependency-type: indirect
|
||||
...
|
||||
|
||||
|
||||
v2.4.183 (2024-01-04)
|
||||
---------------------
|
||||
|
||||
New
|
||||
~~~
|
||||
- Documentation to install PyMISP on offline machine. [Raphaël Vinot]
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Bump objects. [Raphaël Vinot]
|
||||
- Bump version. [Raphaël Vinot]
|
||||
- Remove jsonschema from dependencies. [Raphaël Vinot]
|
||||
- Encrypt malicious js. [Raphaël Vinot]
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
- Fix api ssl verify typing. [Steven]
|
||||
- Add HTTPS Adapter. [Steven]
|
||||
|
||||
Add the ability to provide a custom HTTPS adapter to the PyMISP class. With M2Crypto and m2requests, this can enable mutual TLS with hardware tokens.
|
||||
|
||||
|
||||
v2.4.182 (2023-12-14)
|
||||
---------------------
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Bump version. [Raphaël Vinot]
|
||||
- Bump objects. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
|
||||
Fix
|
||||
~~~
|
||||
- Avoid exception when the malware file name contains a "|" [Raphaël
|
||||
Vinot]
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
- Build(deps): bump github/codeql-action from 2 to 3. [dependabot[bot]]
|
||||
|
||||
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
|
||||
- [Release notes](https://github.com/github/codeql-action/releases)
|
||||
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
|
||||
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)
|
||||
|
||||
---
|
||||
updated-dependencies:
|
||||
- dependency-name: github/codeql-action
|
||||
dependency-type: direct:production
|
||||
update-type: version-update:semver-major
|
||||
...
|
||||
- Build(deps): bump actions/setup-python from 4 to 5. [dependabot[bot]]
|
||||
|
||||
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
|
||||
- [Release notes](https://github.com/actions/setup-python/releases)
|
||||
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)
|
||||
|
||||
---
|
||||
updated-dependencies:
|
||||
- dependency-name: actions/setup-python
|
||||
dependency-type: direct:production
|
||||
update-type: version-update:semver-major
|
||||
...
|
||||
|
||||
|
||||
v2.4.179 (2023-11-23)
|
||||
---------------------
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump version, changelog. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
- [misp-objects] Bumped latest version. [Christian Studer]
|
||||
|
||||
Fix
|
||||
~~~
|
||||
- Eml and msg are in sync again. [Raphaël Vinot]
|
||||
- Update calls to getStringStream to the public method. [Raphaël Vinot]
|
||||
- Avoid confusing error when an auth key is limited to an IP. [Raphaël
|
||||
Vinot]
|
||||
|
||||
Fix #1099
|
||||
|
||||
|
||||
v2.4.178 (2023-10-24)
|
||||
---------------------
|
||||
|
||||
|
|
@ -11,6 +255,7 @@ New
|
|||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Bump version, make __version__ dynamic. [Raphaël Vinot]
|
||||
- Bump deps, allow older jsonschema for compatibility. [Raphaël Vinot]
|
||||
- Bump deps. [Raphaël Vinot]
|
||||
|
|
@ -5080,5 +5325,3 @@ v1.1.2 (2015-08-05)
|
|||
- Json export is not supported everywhere. [Raphaël Vinot]
|
||||
- Some testing. [Raphaël Vinot]
|
||||
- Initial commit. [Raphaël Vinot]
|
||||
|
||||
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -125,7 +125,6 @@ logging.basicConfig(level=logging.DEBUG, filename="debug.log", filemode='w', for
|
|||
# From poetry
|
||||
|
||||
pytest --cov=pymisp tests/test_*.py tests/testlive_comprehensive.py:TestComprehensive.[test_name]
|
||||
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
|
@ -159,6 +158,35 @@ Your new MISPObject generator must generate attributes and add them as class pro
|
|||
|
||||
When the object is sent to MISP, all the class properties will be exported to the JSON export.
|
||||
|
||||
## Installing PyMISP on a machine with no internet access
|
||||
|
||||
This is done using poetry and you need to have this repository cloned on your machine.
|
||||
The commands below have to be run from inside the cloned directory.
|
||||
|
||||
|
||||
1. From a machine with access to the internet, get the dependencies:
|
||||
|
||||
```bash
|
||||
mkdir offline
|
||||
poetry export --all-extras > offline/requirements.txt
|
||||
poetry run pip download -r offline/requirements.txt -d offline/packages/
|
||||
```
|
||||
|
||||
2. Prepare the PyMISP Package
|
||||
|
||||
```bash
|
||||
poetry build
|
||||
mv dist/*.whl offline/packages/
|
||||
```
|
||||
|
||||
3. Copy the content of `offline/packages/` to the machine with no internet access.
|
||||
|
||||
4. Install the packages:
|
||||
|
||||
```bash
|
||||
python -m pip install --no-index --no-deps packages/*.whl
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
PyMISP is distributed under an [open source license](./LICENSE). A simplified 2-BSD license.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pymisp import ExpandedPyMISP
|
||||
from pymisp import PyMISP
|
||||
from pymisp.tools import EMailObject
|
||||
import traceback
|
||||
from keys import misp_url, misp_key, misp_verifycert # type: ignore
|
||||
|
|
@ -15,7 +14,7 @@ if __name__ == '__main__':
|
|||
parser.add_argument("-p", "--path", required=True, help="Path to process (expanded using glob).")
|
||||
args = parser.parse_args()
|
||||
|
||||
pymisp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert, debug=True)
|
||||
pymisp = PyMISP(misp_url, misp_key, misp_verifycert, debug=True)
|
||||
|
||||
for f in glob.glob(args.path):
|
||||
try:
|
||||
|
|
|
|||
15
mypy.ini
15
mypy.ini
|
|
@ -1,6 +1,15 @@
|
|||
[mypy]
|
||||
ignore_errors = False
|
||||
|
||||
strict = True
|
||||
warn_return_any = False
|
||||
show_error_context = True
|
||||
pretty = True
|
||||
exclude = pymisp/data|example|docs
|
||||
exclude = tests/testlive_comprehensive.py|tests/testlive_sync.py|feed-generator|examples|pymisp/data|docs|pymisp/tools/openioc.py|pymisp/tools/reportlab_generator.py|tests/test_reportlab.py
|
||||
|
||||
# Stuff to remove gradually
|
||||
disallow_untyped_defs = False
|
||||
disallow_untyped_calls = False
|
||||
disable_error_code = arg-type,return-value,assignment,call-overload,union-attr
|
||||
|
||||
|
||||
[mypy-docs.source.*]
|
||||
ignore_errors = True
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import warnings
|
||||
|
|
@ -9,7 +11,7 @@ logger = logging.getLogger(__name__)
|
|||
__version__ = importlib.metadata.version("pymisp")
|
||||
|
||||
|
||||
def warning_2024():
|
||||
def warning_2024() -> None:
|
||||
if sys.version_info < (3, 10):
|
||||
warnings.warn("""
|
||||
As our baseline system is the latest Ubuntu LTS, and Ubuntu LTS 22.04 has Python 3.10 available,
|
||||
|
|
@ -38,17 +40,14 @@ try:
|
|||
MISPEventDelegation, MISPUserSetting, MISPInbox, MISPEventBlocklist, MISPOrganisationBlocklist,
|
||||
MISPEventReport, MISPCorrelationExclusion, MISPDecayingModel, MISPGalaxy, MISPGalaxyCluster,
|
||||
MISPGalaxyClusterElement, MISPGalaxyClusterRelation)
|
||||
from .api import PyMISP, register_user # noqa
|
||||
# NOTE: the direct imports to .tools are kept for backward compatibility but should be removed in the future
|
||||
from .tools import AbstractMISPObjectGenerator # noqa
|
||||
from .tools import Neo4j # noqa
|
||||
from .tools import stix # noqa
|
||||
from .tools import openioc # noqa
|
||||
from .tools import ext_lookups # noqa
|
||||
from .tools import update_objects # noqa
|
||||
|
||||
from .api import PyMISP, register_user # noqa
|
||||
from .api import PyMISP as ExpandedPyMISP # noqa
|
||||
from .tools import load_warninglists # noqa
|
||||
# Let's not bother with old python
|
||||
|
||||
try:
|
||||
from .tools import reportlab_generator # noqa
|
||||
except ImportError:
|
||||
|
|
@ -59,4 +58,25 @@ try:
|
|||
pass
|
||||
logger.debug('pymisp loaded properly')
|
||||
except ImportError as e:
|
||||
logger.warning('Unable to load pymisp properly: {}'.format(e))
|
||||
logger.warning(f'Unable to load pymisp properly: {e}')
|
||||
|
||||
|
||||
class ExpandedPyMISP(PyMISP):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn('This class is deprecated, use PyMISP instead', FutureWarning)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
__all__ = ['PyMISP', 'register_user', 'AbstractMISP', 'MISPTag',
|
||||
'MISPEvent', 'MISPAttribute', 'MISPObjectReference', 'MISPObjectAttribute',
|
||||
'MISPObject', 'MISPUser', 'MISPOrganisation', 'MISPSighting', 'MISPLog',
|
||||
'MISPShadowAttribute', 'MISPWarninglist', 'MISPTaxonomy', 'MISPNoticelist',
|
||||
'MISPObjectTemplate', 'MISPSharingGroup', 'MISPRole', 'MISPServer', 'MISPFeed',
|
||||
'MISPEventDelegation', 'MISPUserSetting', 'MISPInbox', 'MISPEventBlocklist',
|
||||
'MISPOrganisationBlocklist', 'MISPEventReport', 'MISPCorrelationExclusion',
|
||||
'MISPDecayingModel', 'MISPGalaxy', 'MISPGalaxyCluster', 'MISPGalaxyClusterElement',
|
||||
'MISPGalaxyClusterRelation', 'PyMISPError', 'NewEventError', 'NewAttributeError',
|
||||
'NoURL', 'NoKey', 'InvalidMISPObject', 'UnknownMISPObjectTemplate', 'PyMISPInvalidFormat',
|
||||
'Distribution', 'ThreatLevel', 'Analysis', 'ExpandedPyMISP'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,53 +1,48 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import date, datetime
|
||||
|
||||
from deprecated import deprecated # type: ignore
|
||||
from json import JSONEncoder
|
||||
from uuid import UUID
|
||||
from abc import ABCMeta
|
||||
|
||||
try:
|
||||
from rapidjson import load # type: ignore
|
||||
from rapidjson import loads # type: ignore
|
||||
from rapidjson import dumps # type: ignore
|
||||
HAS_RAPIDJSON = True
|
||||
except ImportError:
|
||||
from json import load
|
||||
from json import loads
|
||||
from json import dumps
|
||||
HAS_RAPIDJSON = False
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import Union, Optional, Any, Dict, List, Set, Mapping
|
||||
|
||||
from .exceptions import PyMISPInvalidFormat, PyMISPError
|
||||
|
||||
|
||||
from enum import Enum, IntEnum
|
||||
from typing import Any, Mapping
|
||||
from collections.abc import MutableMapping
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import orjson # type: ignore
|
||||
from orjson import loads, dumps
|
||||
HAS_ORJSON = True
|
||||
except ImportError:
|
||||
from json import loads, dumps
|
||||
HAS_ORJSON = False
|
||||
|
||||
from .exceptions import PyMISPInvalidFormat, PyMISPError
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
resources_path = Path(__file__).parent / 'data'
|
||||
misp_objects_path = resources_path / 'misp-objects' / 'objects'
|
||||
with (resources_path / 'describeTypes.json').open('r') as f:
|
||||
describe_types = load(f)['result']
|
||||
with (resources_path / 'describeTypes.json').open('rb') as f:
|
||||
describe_types: dict[str, Any] = loads(f.read())['result']
|
||||
|
||||
|
||||
class MISPFileCache(object):
|
||||
class MISPFileCache:
|
||||
# cache up to 150 JSON structures in class attribute
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=150)
|
||||
def _load_json(path: Path) -> Union[dict, None]:
|
||||
def _load_json(path: Path) -> dict[str, Any] | None:
|
||||
if not path.exists():
|
||||
return None
|
||||
with path.open('r', encoding='utf-8') as f:
|
||||
data = load(f)
|
||||
with path.open('rb') as f:
|
||||
data = loads(f.read())
|
||||
return data
|
||||
|
||||
|
||||
|
|
@ -73,7 +68,7 @@ class Analysis(Enum):
|
|||
completed = 2
|
||||
|
||||
|
||||
def _int_to_str(d: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def _int_to_str(d: dict[str, Any]) -> dict[str, Any]:
|
||||
# transform all integer back to string
|
||||
for k, v in d.items():
|
||||
if isinstance(v, dict):
|
||||
|
|
@ -85,7 +80,7 @@ def _int_to_str(d: Dict[str, Any]) -> Dict[str, Any]:
|
|||
|
||||
@deprecated(reason=" Use method default=pymisp_json_default instead of cls=MISPEncode", version='2.4.117', action='default')
|
||||
class MISPEncode(JSONEncoder):
|
||||
def default(self, obj):
|
||||
def default(self, obj: Any) -> dict[str, Any] | str:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
|
|
@ -97,12 +92,12 @@ class MISPEncode(JSONEncoder):
|
|||
return JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||
class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta): # type: ignore[type-arg]
|
||||
__resources_path = resources_path
|
||||
__misp_objects_path = misp_objects_path
|
||||
__describe_types = describe_types
|
||||
|
||||
def __init__(self, **kwargs: Dict):
|
||||
def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Abstract class for all the MISP objects.
|
||||
NOTE: Every method in every classes inheriting this one are doing
|
||||
changes in memory and do not modify data on a remote MISP instance.
|
||||
|
|
@ -111,9 +106,9 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
"""
|
||||
super().__init__()
|
||||
self.__edited: bool = True # As we create a new object, we assume it is edited
|
||||
self.__not_jsonable: List[str] = []
|
||||
self._fields_for_feed: Set
|
||||
self.__self_defined_describe_types: Optional[Dict] = None
|
||||
self.__not_jsonable: list[str] = []
|
||||
self._fields_for_feed: set[str]
|
||||
self.__self_defined_describe_types: dict[str, Any] | None = None
|
||||
self.uuid: str
|
||||
|
||||
if kwargs.get('force_timestamps') is not None:
|
||||
|
|
@ -123,13 +118,13 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
self.__force_timestamps = False
|
||||
|
||||
@property
|
||||
def describe_types(self) -> Dict:
|
||||
def describe_types(self) -> dict[str, Any]:
|
||||
if self.__self_defined_describe_types:
|
||||
return self.__self_defined_describe_types
|
||||
return self.__describe_types
|
||||
|
||||
@describe_types.setter
|
||||
def describe_types(self, describe_types: Dict):
|
||||
def describe_types(self, describe_types: dict[str, Any]) -> None:
|
||||
self.__self_defined_describe_types = describe_types
|
||||
|
||||
@property
|
||||
|
|
@ -141,12 +136,12 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return self.__misp_objects_path
|
||||
|
||||
@misp_objects_path.setter
|
||||
def misp_objects_path(self, misp_objects_path: Union[str, Path]):
|
||||
def misp_objects_path(self, misp_objects_path: str | Path) -> None:
|
||||
if isinstance(misp_objects_path, str):
|
||||
misp_objects_path = Path(misp_objects_path)
|
||||
self.__misp_objects_path = misp_objects_path
|
||||
|
||||
def from_dict(self, **kwargs) -> None:
|
||||
def from_dict(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Loading all the parameters as class properties, if they aren't `None`.
|
||||
This method aims to be called when all the properties requiring a special
|
||||
treatment are processed.
|
||||
|
|
@ -159,15 +154,15 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
# We load an existing dictionary, marking it an not-edited
|
||||
self.__edited = False
|
||||
|
||||
def update_not_jsonable(self, *args) -> None:
|
||||
def update_not_jsonable(self, *args) -> None: # type: ignore[no-untyped-def]
|
||||
"""Add entries to the __not_jsonable list"""
|
||||
self.__not_jsonable += args
|
||||
|
||||
def set_not_jsonable(self, args: List[str]) -> None:
|
||||
def set_not_jsonable(self, args: list[str]) -> None:
|
||||
"""Set __not_jsonable to a new list"""
|
||||
self.__not_jsonable = args
|
||||
|
||||
def _remove_from_not_jsonable(self, *args) -> None:
|
||||
def _remove_from_not_jsonable(self, *args) -> None: # type: ignore[no-untyped-def]
|
||||
"""Remove the entries that are in the __not_jsonable list"""
|
||||
for entry in args:
|
||||
try:
|
||||
|
|
@ -179,7 +174,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
"""Load a JSON string"""
|
||||
self.from_dict(**loads(json_string))
|
||||
|
||||
def to_dict(self, json_format: bool = False) -> Dict:
|
||||
def to_dict(self, json_format: bool = False) -> dict[str, Any]:
|
||||
"""Dump the class to a dictionary.
|
||||
This method automatically removes the timestamp recursively in every object
|
||||
that has been edited is order to let MISP update the event accordingly."""
|
||||
|
|
@ -221,15 +216,15 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
to_return = _int_to_str(to_return)
|
||||
return to_return
|
||||
|
||||
def jsonable(self) -> Dict:
|
||||
def jsonable(self) -> dict[str, Any]:
|
||||
"""This method is used by the JSON encoder"""
|
||||
return self.to_dict()
|
||||
|
||||
def _to_feed(self) -> Dict:
|
||||
def _to_feed(self) -> dict[str, Any]:
|
||||
if not hasattr(self, '_fields_for_feed') or not self._fields_for_feed:
|
||||
raise PyMISPError('Unable to export in the feed format, _fields_for_feed is missing.')
|
||||
if hasattr(self, '_set_default') and callable(self._set_default): # type: ignore
|
||||
self._set_default() # type: ignore
|
||||
if hasattr(self, '_set_default') and callable(self._set_default):
|
||||
self._set_default()
|
||||
to_return = {}
|
||||
for field in sorted(self._fields_for_feed):
|
||||
if getattr(self, field, None) is not None:
|
||||
|
|
@ -243,15 +238,25 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
if field in ['data', 'first_seen', 'last_seen', 'deleted']:
|
||||
# special fields
|
||||
continue
|
||||
raise PyMISPError('The field {} is required in {} when generating a feed.'.format(field, self.__class__.__name__))
|
||||
raise PyMISPError(f'The field {field} is required in {self.__class__.__name__} when generating a feed.')
|
||||
to_return = _int_to_str(to_return)
|
||||
return to_return
|
||||
|
||||
def to_json(self, sort_keys: bool = False, indent: Optional[int] = None) -> str:
|
||||
def to_json(self, sort_keys: bool = False, indent: int | None = None) -> str:
|
||||
"""Dump recursively any class of type MISPAbstract to a json string"""
|
||||
if HAS_ORJSON:
|
||||
option = 0
|
||||
if sort_keys:
|
||||
option |= orjson.OPT_SORT_KEYS
|
||||
if indent:
|
||||
option |= orjson.OPT_INDENT_2
|
||||
# orjson dumps method returns bytes instead of bytes, to keep compatibility with json
|
||||
# we have to convert output to str
|
||||
return str(dumps(self, default=pymisp_json_default, option=option))
|
||||
|
||||
return dumps(self, default=pymisp_json_default, sort_keys=sort_keys, indent=indent)
|
||||
|
||||
def __getitem__(self, key):
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
try:
|
||||
if key[0] != '_' and key not in self.__not_jsonable:
|
||||
return self.__dict__[key]
|
||||
|
|
@ -260,13 +265,13 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
# Expected by pop and other dict-related methods
|
||||
raise KeyError
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
setattr(self, key, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
def __delitem__(self, key: str) -> None:
|
||||
delattr(self, key)
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Any:
|
||||
'''When we call **self, skip keys:
|
||||
* starting with _
|
||||
* in __not_jsonable
|
||||
|
|
@ -285,7 +290,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return self.__force_timestamps
|
||||
|
||||
@force_timestamp.setter
|
||||
def force_timestamp(self, force: bool):
|
||||
def force_timestamp(self, force: bool) -> None:
|
||||
self.__force_timestamps = force
|
||||
|
||||
@property
|
||||
|
|
@ -305,28 +310,28 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return self.__edited
|
||||
|
||||
@edited.setter
|
||||
def edited(self, val: bool):
|
||||
def edited(self, val: bool) -> None:
|
||||
"""Set the edit flag"""
|
||||
if isinstance(val, bool):
|
||||
self.__edited = val
|
||||
else:
|
||||
raise PyMISPError('edited can only be True or False')
|
||||
|
||||
def __setattr__(self, name: str, value: Any):
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if name[0] != '_' and not self.__edited and name in self:
|
||||
# The private members don't matter
|
||||
# If we already have a key with that name, we're modifying it.
|
||||
self.__edited = True
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def _datetime_to_timestamp(self, d: Union[int, float, str, datetime]) -> int:
|
||||
def _datetime_to_timestamp(self, d: int | float | str | datetime) -> int:
|
||||
"""Convert a datetime object to a timestamp (int)"""
|
||||
if isinstance(d, (int, float, str)):
|
||||
# Assume we already have a timestamp
|
||||
return int(d)
|
||||
return int(d.timestamp())
|
||||
|
||||
def _add_tag(self, tag: Optional[Union[str, 'MISPTag', Mapping]] = None, **kwargs):
|
||||
def _add_tag(self, tag: str | MISPTag | Mapping[str, Any] | None = None, **kwargs): # type: ignore[no-untyped-def]
|
||||
"""Add a tag to the attribute (by name or a MISPTag object)"""
|
||||
if isinstance(tag, str):
|
||||
misp_tag = MISPTag()
|
||||
|
|
@ -346,14 +351,14 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
self.edited = True
|
||||
return misp_tag
|
||||
|
||||
def _set_tags(self, tags: List['MISPTag']):
|
||||
def _set_tags(self, tags: list[MISPTag]) -> None:
|
||||
"""Set a list of prepared MISPTag."""
|
||||
if all(isinstance(x, MISPTag) for x in tags):
|
||||
self.Tag = tags
|
||||
else:
|
||||
raise PyMISPInvalidFormat('All the attributes have to be of type MISPTag.')
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, AbstractMISP):
|
||||
return self.to_dict() == other.to_dict()
|
||||
elif isinstance(other, dict):
|
||||
|
|
@ -362,26 +367,26 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return '<{self.__class__.__name__} - please define me>'.format(self=self)
|
||||
return f'<{self.__class__.__name__} - please define me>'
|
||||
|
||||
|
||||
class MISPTag(AbstractMISP):
|
||||
|
||||
_fields_for_feed: set = {'name', 'colour', 'relationship_type', 'local'}
|
||||
_fields_for_feed: set[str] = {'name', 'colour', 'relationship_type', 'local'}
|
||||
|
||||
def __init__(self, **kwargs: Dict):
|
||||
def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__(**kwargs)
|
||||
self.name: str
|
||||
self.exportable: bool
|
||||
self.local: bool
|
||||
self.relationship_type: Optional[str]
|
||||
self.relationship_type: str | None
|
||||
|
||||
def from_dict(self, **kwargs):
|
||||
def from_dict(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
if kwargs.get('Tag'):
|
||||
kwargs = kwargs.get('Tag')
|
||||
kwargs = kwargs.get('Tag') # type: ignore[assignment]
|
||||
super().from_dict(**kwargs)
|
||||
|
||||
def _set_default(self):
|
||||
def _set_default(self) -> None:
|
||||
if not hasattr(self, 'relationship_type'):
|
||||
self.relationship_type = ''
|
||||
if not hasattr(self, 'colour'):
|
||||
|
|
@ -389,40 +394,30 @@ class MISPTag(AbstractMISP):
|
|||
if not hasattr(self, 'local'):
|
||||
self.local = False
|
||||
|
||||
def _to_feed(self, with_local: bool = True) -> Dict:
|
||||
def _to_feed(self, with_local: bool = True) -> dict[str, Any]:
|
||||
if hasattr(self, 'exportable') and not self.exportable:
|
||||
return {}
|
||||
if with_local is False and hasattr(self, 'local') and self.local:
|
||||
return {}
|
||||
return super()._to_feed()
|
||||
|
||||
def delete(self):
|
||||
def delete(self) -> None:
|
||||
self.deleted = True
|
||||
self.edited = True
|
||||
|
||||
def __repr__(self) -> str:
|
||||
if hasattr(self, 'name'):
|
||||
return '<{self.__class__.__name__}(name={self.name})>'.format(self=self)
|
||||
return '<{self.__class__.__name__}(NotInitialized)>'.format(self=self)
|
||||
return f'<{self.__class__.__name__}(NotInitialized)>'
|
||||
|
||||
|
||||
if HAS_RAPIDJSON:
|
||||
def pymisp_json_default(obj: Union[AbstractMISP, datetime, date, Enum, UUID]) -> Union[Dict, str]:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, Enum):
|
||||
return obj.value
|
||||
elif isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
else:
|
||||
def pymisp_json_default(obj: Union[AbstractMISP, datetime, date, Enum, UUID]) -> Union[Dict, str]:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, Enum):
|
||||
return obj.value
|
||||
elif isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
# UUID, datetime, date and Enum is serialized by ORJSON by default
|
||||
def pymisp_json_default(obj: AbstractMISP | datetime | date | Enum | UUID) -> dict[str, Any] | str:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, Enum):
|
||||
return obj.value
|
||||
elif isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
|
|
|
|||
936
pymisp/api.py
936
pymisp/api.py
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1 @@
|
|||
Subproject commit ca371d456712d0484a7585fbe1bcad4128272512
|
||||
Subproject commit 3ac509965fdbca06d8a027db22c0064588babd3c
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
class PyMISPError(Exception):
|
||||
def __init__(self, message):
|
||||
super(PyMISPError, self).__init__(message)
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .vtreportobject import VTReportObject # noqa
|
||||
from .neo4j import Neo4j # noqa
|
||||
from .fileobject import FileObject # noqa
|
||||
|
|
@ -41,3 +43,13 @@ try:
|
|||
except ImportError:
|
||||
# Requires lief, optional [fileobjects]
|
||||
pass
|
||||
|
||||
__all__ = ['VTReportObject', 'Neo4j', 'FileObject', 'make_binary_objects',
|
||||
'AbstractMISPObjectGenerator', 'GenericObjectGenerator',
|
||||
'load_openioc', 'load_openioc_file', 'SBSignatureObject',
|
||||
'Fail2BanObject', 'DomainIPObject', 'ASNObject', 'GeolocationObject',
|
||||
'GitVulnFinderObject', 'VehicleObject', 'CSVLoader',
|
||||
'SSHAuthorizedKeysObject', 'feed_meta_generator', 'update_objects',
|
||||
'EMailObject', 'URLObject', 'PEObject', 'PESectionObject', 'ELFObject',
|
||||
'ELFSectionObject', 'MachOObject', 'MachOSectionObject'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,36 +1,37 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
import socket
|
||||
import idna
|
||||
from publicsuffixlist import PublicSuffixList # type: ignore
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
from urllib.parse import urlparse, urlunparse, ParseResult
|
||||
|
||||
|
||||
class UrlNotDecoded(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PSLFaup(object):
|
||||
class PSLFaup:
|
||||
"""
|
||||
Fake Faup Python Library using PSL for Windows support
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.decoded = False
|
||||
self.psl = PublicSuffixList()
|
||||
self._url = None
|
||||
self._retval = {}
|
||||
self.ip_as_host = False
|
||||
self._url: ParseResult | None = None
|
||||
self._retval: dict[str, str | int | None | bytes] = {}
|
||||
self.ip_as_host = ''
|
||||
|
||||
def _clear(self):
|
||||
def _clear(self) -> None:
|
||||
self.decoded = False
|
||||
self._url = None
|
||||
self._retval = {}
|
||||
self.ip_as_host = False
|
||||
self.ip_as_host = ''
|
||||
|
||||
def decode(self, url) -> None:
|
||||
def decode(self, url: str) -> None:
|
||||
"""
|
||||
This function creates a dict of all the url fields.
|
||||
:param url: The URL to normalize
|
||||
|
|
@ -42,10 +43,15 @@ class PSLFaup(object):
|
|||
url = '//' + url
|
||||
self._url = urlparse(url)
|
||||
|
||||
self.ip_as_host = False
|
||||
if self._url is None:
|
||||
raise UrlNotDecoded("Unable to parse URL")
|
||||
|
||||
self.ip_as_host = ''
|
||||
if self._url.hostname is None:
|
||||
raise UrlNotDecoded("Unable to parse URL")
|
||||
hostname = _ensure_str(self._url.hostname)
|
||||
try:
|
||||
ipv4_bytes = socket.inet_aton(_ensure_str(hostname))
|
||||
ipv4_bytes = socket.inet_aton(hostname)
|
||||
ipv4 = ipaddress.IPv4Address(ipv4_bytes)
|
||||
self.ip_as_host = ipv4.compressed
|
||||
except (OSError, ValueError):
|
||||
|
|
@ -60,61 +66,70 @@ class PSLFaup(object):
|
|||
self._retval = {}
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
if not self.decoded:
|
||||
def url(self) -> bytes | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
netloc = self.get_host() + ('' if self.get_port() is None else ':{}'.format(self.get_port()))
|
||||
return _ensure_bytes(
|
||||
urlunparse(
|
||||
(self.get_scheme(), netloc, self.get_resource_path(),
|
||||
'', self.get_query_string(), self.get_fragment(),)
|
||||
if host := self.get_host():
|
||||
netloc = host + ('' if self.get_port() is None else f':{self.get_port()}')
|
||||
return _ensure_bytes(
|
||||
urlunparse(
|
||||
(self.get_scheme(), netloc, self.get_resource_path(),
|
||||
'', self.get_query_string(), self.get_fragment(),)
|
||||
)
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
def get_scheme(self):
|
||||
def get_scheme(self) -> str:
|
||||
"""
|
||||
Get the scheme of the url given in the decode function
|
||||
:returns: The URL scheme
|
||||
"""
|
||||
if not self.decoded:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.scheme)
|
||||
return _ensure_str(self._url.scheme if self._url.scheme else '')
|
||||
|
||||
def get_credential(self):
|
||||
if not self.decoded:
|
||||
def get_credential(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self._url.password:
|
||||
if self._url.username and self._url.password:
|
||||
return _ensure_str(self._url.username) + ':' + _ensure_str(self._url.password)
|
||||
if self._url.username:
|
||||
return _ensure_str(self._url.username)
|
||||
return None
|
||||
|
||||
def get_subdomain(self):
|
||||
if not self.decoded:
|
||||
def get_subdomain(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_host() is not None and not self.ip_as_host:
|
||||
if self.get_domain() in self.get_host():
|
||||
return self.get_host().rsplit(self.get_domain(), 1)[0].rstrip('.') or None
|
||||
domain = self.get_domain()
|
||||
host = self.get_host()
|
||||
if domain and host and domain in host:
|
||||
return host.rsplit(domain, 1)[0].rstrip('.') or None
|
||||
return None
|
||||
|
||||
def get_domain(self):
|
||||
if not self.decoded:
|
||||
def get_domain(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_host() is not None and not self.ip_as_host:
|
||||
return self.psl.privatesuffix(self.get_host())
|
||||
return None
|
||||
|
||||
def get_domain_without_tld(self):
|
||||
if not self.decoded:
|
||||
def get_domain_without_tld(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_tld() is not None and not self.ip_as_host:
|
||||
return self.get_domain().rsplit(self.get_tld(), 1)[0].rstrip('.')
|
||||
if domain := self.get_domain():
|
||||
return domain.rsplit(self.get_tld(), 1)[0].rstrip('.')
|
||||
return None
|
||||
|
||||
def get_host(self):
|
||||
if not self.decoded:
|
||||
def get_host(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self._url.hostname is None:
|
||||
|
|
@ -124,45 +139,48 @@ class PSLFaup(object):
|
|||
else:
|
||||
return _ensure_str(idna.encode(self._url.hostname, uts46=True))
|
||||
|
||||
def get_unicode_host(self):
|
||||
if not self.decoded:
|
||||
def get_unicode_host(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if not self.ip_as_host:
|
||||
return idna.decode(self.get_host(), uts46=True)
|
||||
if host := self.get_host():
|
||||
return idna.decode(host, uts46=True)
|
||||
return None
|
||||
|
||||
def get_tld(self):
|
||||
if not self.decoded:
|
||||
def get_tld(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_host() is not None and not self.ip_as_host:
|
||||
return self.psl.publicsuffix(self.get_host())
|
||||
return None
|
||||
|
||||
def get_port(self):
|
||||
if not self.decoded:
|
||||
def get_port(self) -> int | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return self._url.port
|
||||
|
||||
def get_resource_path(self):
|
||||
if not self.decoded:
|
||||
def get_resource_path(self) -> str:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.path)
|
||||
|
||||
def get_query_string(self):
|
||||
if not self.decoded:
|
||||
def get_query_string(self) -> str:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.query)
|
||||
|
||||
def get_fragment(self):
|
||||
if not self.decoded:
|
||||
def get_fragment(self) -> str:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.fragment)
|
||||
|
||||
def get(self):
|
||||
def get(self) -> dict[str, str | int | None | bytes]:
|
||||
self._retval["scheme"] = self.get_scheme()
|
||||
self._retval["tld"] = self.get_tld()
|
||||
self._retval["domain"] = self.get_domain()
|
||||
|
|
@ -177,14 +195,14 @@ class PSLFaup(object):
|
|||
return self._retval
|
||||
|
||||
|
||||
def _ensure_bytes(binary) -> bytes:
|
||||
def _ensure_bytes(binary: str | bytes) -> bytes:
|
||||
if isinstance(binary, bytes):
|
||||
return binary
|
||||
else:
|
||||
return binary.encode('utf-8')
|
||||
|
||||
|
||||
def _ensure_str(string) -> str:
|
||||
def _ensure_str(string: str | bytes) -> str:
|
||||
if isinstance(string, str):
|
||||
return string
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, date
|
||||
from dateutil.parser import parse
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .. import MISPObject
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from datetime import datetime, date
|
||||
from dateutil.parser import parse
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
class AbstractMISPObjectGenerator(MISPObject):
|
||||
|
||||
def _detect_epoch(self, timestamp: Union[str, int, float]) -> bool:
|
||||
def _detect_epoch(self, timestamp: str | int | float) -> bool:
|
||||
try:
|
||||
tmp = float(timestamp)
|
||||
if tmp < 30000000:
|
||||
|
|
@ -21,7 +24,7 @@ class AbstractMISPObjectGenerator(MISPObject):
|
|||
except ValueError:
|
||||
return False
|
||||
|
||||
def _sanitize_timestamp(self, timestamp: Optional[Union[datetime, date, dict, str, int, float]] = None) -> datetime:
|
||||
def _sanitize_timestamp(self, timestamp: datetime | date | dict[str, Any] | str | int | float | None = None) -> datetime:
|
||||
if not timestamp:
|
||||
return datetime.now()
|
||||
|
||||
|
|
@ -42,9 +45,9 @@ class AbstractMISPObjectGenerator(MISPObject):
|
|||
else:
|
||||
raise Exception(f'Unable to convert {timestamp} to a datetime.')
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
"""Contains the logic where all the values of the object are gathered"""
|
||||
if hasattr(self, '_parameters'):
|
||||
if hasattr(self, '_parameters') and self._definition is not None:
|
||||
for object_relation in self._definition['attributes']:
|
||||
value = self._parameters.pop(object_relation, None)
|
||||
if not value:
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class ASNObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('asn', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||
self._parameters['first-seen'] = first
|
||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from io import BytesIO
|
||||
from typing import Any, TYPE_CHECKING
|
||||
|
||||
from . import FileObject
|
||||
from ..exceptions import MISPObjectException
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
try:
|
||||
import lief
|
||||
import lief.logging
|
||||
lief.logging.disable()
|
||||
HAS_LIEF = True
|
||||
|
||||
from .peobject import make_pe_objects
|
||||
from .elfobject import make_elf_objects
|
||||
from .machoobject import make_macho_objects
|
||||
from . import FileObject
|
||||
|
||||
except AttributeError:
|
||||
HAS_LIEF = False
|
||||
|
|
@ -26,19 +28,29 @@ except AttributeError:
|
|||
except ImportError:
|
||||
HAS_LIEF = False
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import PEObject, ELFObject, MachOObject, PESectionObject, ELFSectionObject, MachOSectionObject
|
||||
|
||||
|
||||
class FileTypeNotImplemented(MISPObjectException):
|
||||
pass
|
||||
|
||||
|
||||
def make_binary_objects(filepath: Optional[str] = None, pseudofile: Optional[BytesIO] = None, filename: Optional[str] = None, standalone: bool = True, default_attributes_parameters: dict = {}):
|
||||
def make_binary_objects(filepath: str | None = None,
|
||||
pseudofile: BytesIO | bytes | None = None,
|
||||
filename: str | None = None,
|
||||
standalone: bool = True,
|
||||
default_attributes_parameters: dict[str, Any] = {}) -> tuple[FileObject, PEObject | ELFObject | MachOObject | None, list[PESectionObject] | list[ELFSectionObject] | list[MachOSectionObject]]:
|
||||
misp_file = FileObject(filepath=filepath, pseudofile=pseudofile, filename=filename,
|
||||
standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||
if HAS_LIEF and (filepath or (pseudofile and filename)):
|
||||
if HAS_LIEF and (filepath or pseudofile):
|
||||
if filepath:
|
||||
lief_parsed = lief.parse(filepath=filepath)
|
||||
elif pseudofile and filename:
|
||||
lief_parsed = lief.parse(raw=pseudofile.getvalue(), name=filename)
|
||||
elif pseudofile:
|
||||
if isinstance(pseudofile, bytes):
|
||||
lief_parsed = lief.parse(raw=pseudofile)
|
||||
else: # BytesIO
|
||||
lief_parsed = lief.parse(obj=pseudofile)
|
||||
else:
|
||||
logger.critical('You need either a filepath, or a pseudofile and a filename.')
|
||||
lief_parsed = None
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
import csv
|
||||
from pymisp import MISPObject
|
||||
|
|
@ -10,8 +10,9 @@ from pymisp import MISPObject
|
|||
|
||||
class CSVLoader():
|
||||
|
||||
def __init__(self, template_name: str, csv_path: Path, fieldnames: Optional[List[str]] = None, has_fieldnames=False,
|
||||
delimiter: str = ',', quotechar: str = '"'):
|
||||
def __init__(self, template_name: str, csv_path: Path,
|
||||
fieldnames: list[str] | None = None, has_fieldnames: bool=False,
|
||||
delimiter: str = ',', quotechar: str = '"') -> None:
|
||||
self.template_name = template_name
|
||||
self.delimiter = delimiter
|
||||
self.quotechar = quotechar
|
||||
|
|
@ -25,7 +26,7 @@ class CSVLoader():
|
|||
else:
|
||||
self.has_fieldnames = has_fieldnames
|
||||
|
||||
def load(self):
|
||||
def load(self) -> list[MISPObject]:
|
||||
|
||||
objects = []
|
||||
|
||||
|
|
@ -43,7 +44,7 @@ class CSVLoader():
|
|||
# Check if the CSV file has a header, and if it matches with the object template
|
||||
tmp_object = MISPObject(self.template_name)
|
||||
|
||||
if not tmp_object._definition['attributes']:
|
||||
if not tmp_object._definition or not tmp_object._definition['attributes']:
|
||||
raise Exception(f'Unable to find the object template ({self.template_name}), impossible to create objects.')
|
||||
allowed_fieldnames = list(tmp_object._definition['attributes'].keys())
|
||||
for fieldname in self.fieldnames:
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class DomainIPObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('domain-ip', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||
self._parameters['first-seen'] = first
|
||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from . import FileObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from io import BytesIO
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
import logging
|
||||
from typing import Union, Optional
|
||||
from pathlib import Path
|
||||
from . import FileObject
|
||||
|
||||
import lief
|
||||
|
||||
|
|
@ -21,7 +24,10 @@ except ImportError:
|
|||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
def make_elf_objects(lief_parsed: lief.ELF.Binary, misp_file: FileObject, standalone: bool = True, default_attributes_parameters: dict = {}):
|
||||
def make_elf_objects(lief_parsed: lief.ELF.Binary,
|
||||
misp_file: FileObject,
|
||||
standalone: bool = True,
|
||||
default_attributes_parameters: dict[str, Any] = {}) -> tuple[FileObject, ELFObject, list[ELFSectionObject]]:
|
||||
elf_object = ELFObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||
misp_file.add_reference(elf_object.uuid, 'includes', 'ELF indicators')
|
||||
elf_sections = []
|
||||
|
|
@ -32,29 +38,39 @@ def make_elf_objects(lief_parsed: lief.ELF.Binary, misp_file: FileObject, standa
|
|||
|
||||
class ELFObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parsed: Optional[lief.ELF.Binary] = None, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, **kwargs):
|
||||
__elf: lief.ELF.Binary
|
||||
|
||||
def __init__(self, parsed: lief.ELF.Binary | None = None, # type: ignore[no-untyped-def]
|
||||
filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | bytes | list[int] | None = None, **kwargs) -> None:
|
||||
"""Creates an ELF object, with lief"""
|
||||
super().__init__('elf', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||
if pseudofile:
|
||||
if isinstance(pseudofile, BytesIO):
|
||||
self.__elf = lief.ELF.parse(io=pseudofile)
|
||||
e = lief.ELF.parse(obj=pseudofile)
|
||||
elif isinstance(pseudofile, bytes):
|
||||
self.__elf = lief.ELF.parse(raw=pseudofile)
|
||||
e = lief.ELF.parse(raw=list(pseudofile))
|
||||
elif isinstance(pseudofile, list):
|
||||
e = lief.ELF.parse(raw=pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('Pseudo file can be BytesIO or bytes got {}'.format(type(pseudofile)))
|
||||
raise InvalidMISPObject(f'Pseudo file can be BytesIO or bytes got {type(pseudofile)}')
|
||||
if not e:
|
||||
raise InvalidMISPObject('Unable to parse pseudofile')
|
||||
self.__elf = e
|
||||
elif filepath:
|
||||
self.__elf = lief.ELF.parse(filepath)
|
||||
if e := lief.ELF.parse(filepath):
|
||||
self.__elf = e
|
||||
elif parsed:
|
||||
# Got an already parsed blob
|
||||
if isinstance(parsed, lief.ELF.Binary):
|
||||
self.__elf = parsed
|
||||
else:
|
||||
raise InvalidMISPObject('Not a lief.ELF.Binary: {}'.format(type(parsed)))
|
||||
raise InvalidMISPObject(f'Not a lief.ELF.Binary: {type(parsed)}')
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
# General information
|
||||
self.add_attribute('type', value=str(self.__elf.header.file_type).split('.')[1])
|
||||
self.add_attribute('entrypoint-address', value=self.__elf.entrypoint)
|
||||
|
|
@ -68,7 +84,7 @@ class ELFObject(AbstractMISPObjectGenerator):
|
|||
if not section.name:
|
||||
continue
|
||||
s = ELFSectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
||||
self.add_reference(s.uuid, 'includes', 'Section {} of ELF'.format(pos))
|
||||
self.add_reference(s.uuid, 'includes', f'Section {pos} of ELF')
|
||||
pos += 1
|
||||
self.sections.append(s)
|
||||
self.add_attribute('number-sections', value=len(self.sections))
|
||||
|
|
@ -76,22 +92,22 @@ class ELFObject(AbstractMISPObjectGenerator):
|
|||
|
||||
class ELFSectionObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, section: lief.ELF.Section, **kwargs):
|
||||
def __init__(self, section: lief.ELF.Section, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Creates an ELF Section object. Object generated by ELFObject."""
|
||||
# Python3 way
|
||||
# super().__init__('pe-section')
|
||||
super(ELFSectionObject, self).__init__('elf-section', **kwargs)
|
||||
super().__init__('elf-section', **kwargs)
|
||||
self.__section = section
|
||||
self.__data = bytes(self.__section.content)
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('name', value=self.__section.name)
|
||||
self.add_attribute('type', value=str(self.__section.type).split('.')[1])
|
||||
for flag in self.__section.flags_list:
|
||||
self.add_attribute('flag', value=str(flag).split('.')[1])
|
||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(self.__section.size) > 0:
|
||||
self.add_attribute('entropy', value=self.__section.entropy)
|
||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
|
@ -9,16 +10,17 @@ from email import policy, message_from_bytes
|
|||
from email.message import EmailMessage
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Union, List, Tuple, Dict, cast, Any, Optional
|
||||
from typing import cast, Any
|
||||
|
||||
from extract_msg import openMsg
|
||||
from extract_msg.msg_classes import MessageBase
|
||||
from extract_msg.attachments import AttachmentBase, SignedAttachment
|
||||
from extract_msg.properties import FixedLengthProp
|
||||
from RTFDE.exceptions import MalformedEncapsulatedRtf, NotEncapsulatedRtf # type: ignore
|
||||
from RTFDE.deencapsulate import DeEncapsulator # type: ignore
|
||||
from oletools.common.codepages import codepage2codec # type: ignore
|
||||
|
||||
from ..exceptions import InvalidMISPObject, PyMISPNotImplementedYet, MISPObjectException, NewAttributeError
|
||||
from ..exceptions import InvalidMISPObject, MISPObjectException, NewAttributeError
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
|
@ -29,15 +31,14 @@ class MISPMsgConverstionError(MISPObjectException):
|
|||
|
||||
|
||||
class EMailObject(AbstractMISPObjectGenerator):
|
||||
def __init__(self, filepath: Optional[Union[Path, str]]=None, pseudofile: Optional[BytesIO]=None,
|
||||
attach_original_email: bool = True, **kwargs):
|
||||
def __init__(self, filepath: Path | str | None=None, pseudofile: BytesIO | bytes | None=None, # type: ignore[no-untyped-def]
|
||||
attach_original_email: bool = True, **kwargs) -> None:
|
||||
super().__init__('email', **kwargs)
|
||||
|
||||
self.attach_original_email = attach_original_email
|
||||
self.encapsulated_body: Union[str, None] = None
|
||||
self.eml_from_msg: Union[bool, None] = None
|
||||
self.raw_emails: Dict[str, Union[BytesIO, None]] = {'msg': None,
|
||||
'eml': None}
|
||||
self.encapsulated_body: str | None = None
|
||||
self.eml_from_msg: bool | None = None
|
||||
self.raw_emails: dict[str, BytesIO | None] = {'msg': None, 'eml': None}
|
||||
|
||||
self.__pseudofile = self.create_pseudofile(filepath, pseudofile)
|
||||
self.email = self.parse_email()
|
||||
|
|
@ -66,7 +67,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return message
|
||||
except ValueError as _e: # Exception
|
||||
logger.debug("Email not in .msg format or is a corrupted .msg. Attempting to decode email from other formats.")
|
||||
logger.debug("Error: {} ".format(_e))
|
||||
logger.debug(f"Error: {_e} ")
|
||||
try:
|
||||
if content_in_bytes[:3] == b'\xef\xbb\xbf': # utf-8-sig byte-order mark (BOM)
|
||||
eml_bytes = content_in_bytes.decode("utf_8_sig").encode("utf-8")
|
||||
|
|
@ -78,11 +79,11 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return eml
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
raise PyMISPNotImplementedYet("EmailObject does not know how to decode data passed to it. Object may not be an email. If this is an email please submit it as an issue to PyMISP so we can add support.")
|
||||
raise InvalidMISPObject("EmailObject does not know how to decode data passed to it. Object may not be an email. If this is an email please submit it as an issue to PyMISP so we can add support.")
|
||||
|
||||
@staticmethod
|
||||
def create_pseudofile(filepath: Optional[Union[Path, str]] = None,
|
||||
pseudofile: Optional[BytesIO] = None) -> BytesIO:
|
||||
def create_pseudofile(filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | bytes | None = None) -> BytesIO:
|
||||
"""Creates a pseudofile using directly passed data or data loaded from file path.
|
||||
"""
|
||||
if filepath:
|
||||
|
|
@ -90,6 +91,8 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return BytesIO(f.read())
|
||||
elif pseudofile and isinstance(pseudofile, BytesIO):
|
||||
return pseudofile
|
||||
elif pseudofile and isinstance(pseudofile, bytes):
|
||||
return BytesIO(pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('File buffer (BytesIO) or a path is required.')
|
||||
|
||||
|
|
@ -102,7 +105,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
eml = self._build_eml(message, body, attachments)
|
||||
return eml
|
||||
|
||||
def _extract_msg_objects(self, msg_obj: MessageBase) -> Tuple[EmailMessage, Dict, List[Any]]:
|
||||
def _extract_msg_objects(self, msg_obj: MessageBase) -> tuple[EmailMessage, dict[str, Any], list[AttachmentBase] | list[SignedAttachment]]:
|
||||
"""Extracts email objects needed to construct an eml from a msg."""
|
||||
message: EmailMessage = email.message_from_string(msg_obj.header.as_string(), policy=policy.default) # type: ignore
|
||||
body = {}
|
||||
|
|
@ -150,17 +153,16 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
attachments = msg_obj.attachments
|
||||
return message, body, attachments
|
||||
|
||||
def _build_eml(self, message: EmailMessage, body: dict, attachments: list) -> EmailMessage:
|
||||
def _build_eml(self, message: EmailMessage, body: dict[str, Any], attachments: list[Any]) -> EmailMessage:
|
||||
"""Constructs an eml file from objects extracted from a msg."""
|
||||
# Order the body objects by increasing complexity and toss any missing objects
|
||||
body_objects: List[dict] = [body.get('text', {}),
|
||||
body.get('html', {}),
|
||||
body.get('rtf', {})]
|
||||
body_objects = [i for i in body_objects if i != {}]
|
||||
body_objects: list[dict[str, Any]] = [i for i in [body.get('text'),
|
||||
body.get('html'),
|
||||
body.get('rtf')] if i is not None]
|
||||
# If this a non-multipart email then we only need to attach the payload
|
||||
if message.get_content_maintype() != 'multipart':
|
||||
for _body in body_objects:
|
||||
if "text/{0}".format(_body['subtype']) == message.get_content_type():
|
||||
if "text/{}".format(_body['subtype']) == message.get_content_type():
|
||||
message.set_content(**_body)
|
||||
return message
|
||||
raise MISPMsgConverstionError("Unable to find appropriate eml payload in message body.")
|
||||
|
|
@ -172,8 +174,8 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
if isinstance(body.get('html', None), dict):
|
||||
_html = body.get('html', {}).get('obj')
|
||||
for attch in attachments:
|
||||
if _html.find("cid:{0}".format(attch.cid)) != -1:
|
||||
_content_type = attch._getStringStream('__substg1.0_370E')
|
||||
if _html.find(f"cid:{attch.cid}") != -1:
|
||||
_content_type = attch.getStringStream('__substg1.0_370E')
|
||||
maintype, subtype = _content_type.split("/", 1)
|
||||
related_content[attch.cid] = (attch,
|
||||
{'obj': attch.data,
|
||||
|
|
@ -210,7 +212,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
message.add_alternative(**mime_dict)
|
||||
for attch in attachments: # Add attachments at the end.
|
||||
if attch.cid not in related_content.keys():
|
||||
_content_type = attch._getStringStream('__substg1.0_370E')
|
||||
_content_type = attch.getStringStream('__substg1.0_370E')
|
||||
maintype, subtype = _content_type.split("/", 1)
|
||||
message.add_attachment(attch.data,
|
||||
maintype=maintype,
|
||||
|
|
@ -224,7 +226,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return message
|
||||
|
||||
@staticmethod
|
||||
def _update_content_disp_properties(msg_attch, eml_attch):
|
||||
def _update_content_disp_properties(msg_attch: AttachmentBase, eml_attch: EmailMessage) -> None:
|
||||
"""Set Content-Disposition params on binary eml objects
|
||||
|
||||
You currently have to set non-filename content-disp params by hand in python.
|
||||
|
|
@ -234,14 +236,14 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
for num, name in attch_cont_disp_props.items():
|
||||
try:
|
||||
eml_attch.set_param(name,
|
||||
email.utils.format_datetime(msg_attch.props[num].value),
|
||||
email.utils.format_datetime(msg_attch.props.getValue(num)),
|
||||
header='Content-Disposition')
|
||||
except KeyError:
|
||||
# It's fine if they don't have those values
|
||||
pass
|
||||
|
||||
@property
|
||||
def attachments(self) -> List[Tuple[Optional[str], BytesIO]]:
|
||||
def attachments(self) -> list[tuple[str | None, BytesIO]]:
|
||||
to_return = []
|
||||
try:
|
||||
for attachment in self.email.iter_attachments():
|
||||
|
|
@ -255,7 +257,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
pass
|
||||
return to_return
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
|
||||
# Attach original & Converted
|
||||
if self.attach_original_email is not None:
|
||||
|
|
@ -267,21 +269,30 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
data=self.raw_emails.get('msg'))
|
||||
|
||||
message = self.email
|
||||
body: EmailMessage
|
||||
|
||||
for _pref, body in message._find_body(message, preferencelist=['plain', 'html']):
|
||||
comment = "{0} body".format(body.get_content_type())
|
||||
if body := message.get_body(preferencelist=['plain']):
|
||||
comment = f"{body.get_content_type()} body"
|
||||
if self.encapsulated_body == body.get_content_type():
|
||||
comment += " De-Encapsulated from RTF in original msg."
|
||||
self.add_attribute("email-body",
|
||||
body.get_content(),
|
||||
comment=comment)
|
||||
|
||||
headers = ["{}: {}".format(k, v) for k, v in message.items()]
|
||||
if body := message.get_body(preferencelist=['html']):
|
||||
comment = f"{body.get_content_type()} body"
|
||||
if self.encapsulated_body == body.get_content_type():
|
||||
comment += " De-Encapsulated from RTF in original msg."
|
||||
self.add_attribute("email-body",
|
||||
body.get_content(),
|
||||
comment=comment)
|
||||
|
||||
headers = [f"{k}: {v}" for k, v in message.items()]
|
||||
if headers:
|
||||
self.add_attribute("header", "\n".join(headers))
|
||||
|
||||
if "Date" in message and message.get('date').datetime is not None:
|
||||
self.add_attribute("send-date", message.get('date').datetime)
|
||||
if "Date" in message and message['date'].datetime is not None:
|
||||
self.add_attribute("send-date", message['date'].datetime)
|
||||
|
||||
if "To" in message:
|
||||
self.__add_emails("to", message["To"])
|
||||
|
|
@ -325,31 +336,32 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
|
||||
self.__generate_received()
|
||||
|
||||
def __add_emails(self, typ: str, data: str, insert_display_names: bool = True):
|
||||
addresses = []
|
||||
display_names = []
|
||||
def __add_emails(self, typ: str, data: str, insert_display_names: bool = True) -> None:
|
||||
addresses: list[dict[str, str]] = []
|
||||
display_names: list[dict[str, str]] = []
|
||||
|
||||
for realname, address in email.utils.getaddresses([data]):
|
||||
if address and realname:
|
||||
addresses.append({"value": address, "comment": "{} <{}>".format(realname, address)})
|
||||
addresses.append({"value": address, "comment": f"{realname} <{address}>"})
|
||||
elif address:
|
||||
addresses.append({"value": address})
|
||||
else: # parsing failed, skip
|
||||
continue
|
||||
|
||||
if realname:
|
||||
display_names.append({"value": realname, "comment": "{} <{}>".format(realname, address)})
|
||||
display_names.append({"value": realname, "comment": f"{realname} <{address}>"})
|
||||
|
||||
if addresses:
|
||||
self.add_attributes(typ, *addresses)
|
||||
for a in addresses:
|
||||
self.add_attribute(typ, **a)
|
||||
if insert_display_names and display_names:
|
||||
try:
|
||||
self.add_attributes("{}-display-name".format(typ), *display_names)
|
||||
for d in display_names:
|
||||
self.add_attribute(f"{typ}-display-name", **d)
|
||||
except NewAttributeError:
|
||||
# email object doesn't support display name for all email addrs
|
||||
pass
|
||||
|
||||
def __generate_received(self):
|
||||
def __generate_received(self) -> None:
|
||||
"""
|
||||
Extract IP addresses from received headers that are not private. Also extract hostnames or domains.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from pymispgalaxies import Clusters # type: ignore
|
||||
|
|
@ -14,7 +15,7 @@ except ImportError:
|
|||
has_pymispgalaxies = False
|
||||
|
||||
|
||||
def revert_tag_from_galaxies(tag):
|
||||
def revert_tag_from_galaxies(tag: str) -> list[str]:
|
||||
clusters = Clusters()
|
||||
try:
|
||||
return clusters.revert_machinetag(tag)
|
||||
|
|
@ -22,7 +23,7 @@ def revert_tag_from_galaxies(tag):
|
|||
return []
|
||||
|
||||
|
||||
def revert_tag_from_taxonomies(tag):
|
||||
def revert_tag_from_taxonomies(tag: str) -> list[str]:
|
||||
taxonomies = Taxonomies()
|
||||
try:
|
||||
return taxonomies.revert_machinetag(tag)
|
||||
|
|
@ -30,7 +31,7 @@ def revert_tag_from_taxonomies(tag):
|
|||
return []
|
||||
|
||||
|
||||
def search_taxonomies(query):
|
||||
def search_taxonomies(query: str) -> list[str]:
|
||||
taxonomies = Taxonomies()
|
||||
found = taxonomies.search(query)
|
||||
if not found:
|
||||
|
|
@ -38,6 +39,6 @@ def search_taxonomies(query):
|
|||
return found
|
||||
|
||||
|
||||
def search_galaxies(query):
|
||||
def search_galaxies(query: str) -> list[str]:
|
||||
clusters = Clusters()
|
||||
return clusters.search(query)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class Fail2BanObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs): # type: ignore[no-untyped-def]
|
||||
super().__init__('fail2ban', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
timestamp = self._sanitize_timestamp(self._parameters.pop('processing-timestamp', None))
|
||||
self._parameters['processing-timestamp'] = timestamp
|
||||
super().generate_attributes()
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from pymisp import MISPEvent
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
|
||||
def feed_meta_generator(path: Path):
|
||||
def feed_meta_generator(path: Path) -> None:
|
||||
manifests = {}
|
||||
hashes: List[str] = []
|
||||
hashes: list[str] = []
|
||||
|
||||
for f_name in path.glob('*.json'):
|
||||
if str(f_name.name) == 'manifest.json':
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
|
@ -10,7 +11,6 @@ import math
|
|||
from collections import Counter
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ except ImportError:
|
|||
HAS_PYDEEP = False
|
||||
|
||||
try:
|
||||
import magic # type: ignore
|
||||
import magic
|
||||
HAS_MAGIC = True
|
||||
except ImportError:
|
||||
HAS_MAGIC = False
|
||||
|
|
@ -30,7 +30,9 @@ except ImportError:
|
|||
|
||||
class FileObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, filename: Optional[str] = None, **kwargs) -> None:
|
||||
def __init__(self, filepath: Path | str | None = None, # type: ignore[no-untyped-def]
|
||||
pseudofile: BytesIO | bytes | None = None,
|
||||
filename: str | None = None, **kwargs) -> None:
|
||||
super().__init__('file', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||
|
|
@ -55,10 +57,10 @@ class FileObject(AbstractMISPObjectGenerator):
|
|||
self.__data = self.__pseudofile.getvalue()
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('filename', value=self.__filename)
|
||||
size = self.add_attribute('size-in-bytes', value=len(self.__data))
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=len(self.__data))
|
||||
if len(self.__data) > 0:
|
||||
self.add_attribute('entropy', value=self.__entropy_H(self.__data))
|
||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from typing import List
|
||||
|
||||
|
||||
class GenericObjectGenerator(AbstractMISPObjectGenerator):
|
||||
|
||||
# FIXME: this method is different from the master one, and that's probably not a good idea.
|
||||
def generate_attributes(self, attributes: List[dict]): # type: ignore
|
||||
def generate_attributes(self, attributes: list[dict[str, Any]]) -> None: # type: ignore[override]
|
||||
"""Generates MISPObjectAttributes from a list of dictionaries.
|
||||
Each entry if the list must be in one of the two following formats:
|
||||
* {<object_relation>: <value>}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class GeolocationObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('geolocation', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||
self._parameters['first-seen'] = first
|
||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class GitVulnFinderObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('git-vuln-finder', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
authored_date = self._sanitize_timestamp(self._parameters.pop('authored_date', None))
|
||||
self._parameters['authored_date'] = authored_date
|
||||
committed_date = self._sanitize_timestamp(self._parameters.pop('committed_date', None))
|
||||
|
|
|
|||
|
|
@ -1,26 +1,30 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ..api import PyMISP
|
||||
|
||||
try:
|
||||
from pymispwarninglists import WarningLists # type: ignore
|
||||
from pymispwarninglists import WarningLists, WarningList # type: ignore
|
||||
has_pymispwarninglists = True
|
||||
except ImportError:
|
||||
has_pymispwarninglists = False
|
||||
|
||||
|
||||
def from_instance(pymisp_instance, slow_search=False):
|
||||
def from_instance(pymisp_instance: PyMISP, slow_search: bool=False) -> WarningLists:
|
||||
"""Load the warnindlist from an existing MISP instance
|
||||
:pymisp_instance: Already instantialized PyMISP instance."""
|
||||
|
||||
warninglists_index = pymisp_instance.get_warninglists()['Warninglists']
|
||||
warninglists_index = pymisp_instance.warninglists(pythonify=True)
|
||||
all_warningslists = []
|
||||
for warninglist in warninglists_index:
|
||||
wl = pymisp_instance.get_warninglist(warninglist['Warninglist']['id'])['Warninglist']
|
||||
wl['list'] = wl.pop('WarninglistEntry')
|
||||
all_warningslists.append(wl)
|
||||
if isinstance(warninglist, WarningList):
|
||||
wl = pymisp_instance.get_warninglist(warninglist['Warninglist']['id'])['Warninglist']
|
||||
wl['list'] = wl.pop('WarninglistEntry')
|
||||
all_warningslists.append(wl)
|
||||
|
||||
return WarningLists(slow_search, all_warningslists)
|
||||
|
||||
|
||||
def from_package(slow_search=False):
|
||||
def from_package(slow_search: bool=False) -> WarningLists:
|
||||
return WarningLists(slow_search)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from io import BytesIO
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
from pathlib import Path
|
||||
|
||||
from . import FileObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
import lief
|
||||
|
||||
|
|
@ -21,7 +25,10 @@ except ImportError:
|
|||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
def make_macho_objects(lief_parsed: lief.MachO.Binary, misp_file: FileObject, standalone: bool = True, default_attributes_parameters: dict = {}):
|
||||
def make_macho_objects(lief_parsed: lief.MachO.Binary,
|
||||
misp_file: FileObject,
|
||||
standalone: bool = True,
|
||||
default_attributes_parameters: dict[str, Any] = {}) -> tuple[FileObject, MachOObject, list[MachOSectionObject]]:
|
||||
macho_object = MachOObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||
misp_file.add_reference(macho_object.uuid, 'includes', 'MachO indicators')
|
||||
macho_sections = []
|
||||
|
|
@ -32,31 +39,43 @@ def make_macho_objects(lief_parsed: lief.MachO.Binary, misp_file: FileObject, st
|
|||
|
||||
class MachOObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parsed: Optional[lief.MachO.Binary] = None, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, **kwargs):
|
||||
__macho: lief.MachO.Binary
|
||||
|
||||
def __init__(self, parsed: lief.MachO.Binary | lief.MachO.FatBinary | None = None, # type: ignore[no-untyped-def]
|
||||
filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | list[int] | None = None,
|
||||
**kwargs) -> None:
|
||||
"""Creates an MachO object, with lief"""
|
||||
super().__init__('macho', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||
if pseudofile:
|
||||
if isinstance(pseudofile, BytesIO):
|
||||
self.__macho = lief.MachO.parse(io=pseudofile)
|
||||
m = lief.MachO.parse(obj=pseudofile)
|
||||
elif isinstance(pseudofile, bytes):
|
||||
self.__macho = lief.MachO.parse(raw=pseudofile)
|
||||
m = lief.MachO.parse(raw=list(pseudofile))
|
||||
elif isinstance(pseudofile, list):
|
||||
m = lief.MachO.parse(raw=pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('Pseudo file can be BytesIO or bytes got {}'.format(type(pseudofile)))
|
||||
raise InvalidMISPObject(f'Pseudo file can be BytesIO or bytes got {type(pseudofile)}')
|
||||
if not m:
|
||||
raise InvalidMISPObject('Unable to parse pseudofile')
|
||||
self.__macho = m.at(0)
|
||||
elif filepath:
|
||||
self.__macho = lief.MachO.parse(filepath)
|
||||
if m := lief.MachO.parse(filepath):
|
||||
self.__macho = m.at(0)
|
||||
elif parsed:
|
||||
# Got an already parsed blob
|
||||
if isinstance(parsed, lief.MachO.Binary):
|
||||
if isinstance(parsed, lief.MachO.FatBinary):
|
||||
self.__macho = parsed.at(0)
|
||||
elif isinstance(parsed, lief.MachO.Binary):
|
||||
self.__macho = parsed
|
||||
else:
|
||||
raise InvalidMISPObject('Not a lief.MachO.Binary: {}'.format(type(parsed)))
|
||||
raise InvalidMISPObject(f'Not a lief.MachO.Binary: {type(parsed)}')
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('type', value=str(self.__macho.header.file_type).split('.')[1])
|
||||
self.add_attribute('name', value=self.__macho.name)
|
||||
# General information
|
||||
if self.__macho.has_entrypoint:
|
||||
self.add_attribute('entrypoint-address', value=self.__macho.entrypoint)
|
||||
|
|
@ -66,7 +85,7 @@ class MachOObject(AbstractMISPObjectGenerator):
|
|||
pos = 0
|
||||
for section in self.__macho.sections:
|
||||
s = MachOSectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
||||
self.add_reference(s.uuid, 'includes', 'Section {} of MachO'.format(pos))
|
||||
self.add_reference(s.uuid, 'includes', f'Section {pos} of MachO')
|
||||
pos += 1
|
||||
self.sections.append(s)
|
||||
self.add_attribute('number-sections', value=len(self.sections))
|
||||
|
|
@ -74,19 +93,19 @@ class MachOObject(AbstractMISPObjectGenerator):
|
|||
|
||||
class MachOSectionObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, section: lief.MachO.Section, **kwargs):
|
||||
def __init__(self, section: lief.MachO.Section, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Creates an MachO Section object. Object generated by MachOObject."""
|
||||
# Python3 way
|
||||
# super().__init__('pe-section')
|
||||
super(MachOSectionObject, self).__init__('macho-section', **kwargs)
|
||||
super().__init__('macho-section', **kwargs)
|
||||
self.__section = section
|
||||
self.__data = bytes(self.__section.content)
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('name', value=self.__section.name)
|
||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(self.__section.size) > 0:
|
||||
self.add_attribute('entropy', value=self.__section.entropy)
|
||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
# NOTE: Reference on how this module is used: https://vvx7.io/posts/2020/05/misp-slack-bot/
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class MicroblogObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs): # type: ignore[no-untyped-def]
|
||||
super().__init__('microblog', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
# Raw post.
|
||||
if 'post' in self._parameters:
|
||||
self.add_attribute('post', value=self._parameters['post'])
|
||||
|
|
@ -32,7 +34,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# Original URL location of the microblog post (potentially malicious.
|
||||
if 'url' in self._parameters:
|
||||
if isinstance(self._parameters.get('url'), list):
|
||||
for i in self._parameters.get('url'):
|
||||
for i in self._parameters['url']:
|
||||
self.add_attribute('url', value=i)
|
||||
else:
|
||||
self.add_attribute('url', value=self._parameters['url'])
|
||||
|
|
@ -40,7 +42,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# Archive of the original document (Internet Archive, Archive.is, etc).
|
||||
if 'archive' in self._parameters:
|
||||
if isinstance(self._parameters.get('archive'), list):
|
||||
for i in self._parameters.get('archive'):
|
||||
for i in self._parameters['archive']:
|
||||
self.add_attribute('archive', value=i)
|
||||
else:
|
||||
self.add_attribute('archive', value=self._parameters['archive'])
|
||||
|
|
@ -74,7 +76,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
"Instagram", "Forum", "Other"]
|
||||
if 'type' in self._parameters:
|
||||
if isinstance(self._parameters.get('type'), list):
|
||||
for i in self._parameters.get('type'):
|
||||
for i in self._parameters['type']:
|
||||
if i in type_allowed_values:
|
||||
self.add_attribute('type', value=i)
|
||||
else:
|
||||
|
|
@ -85,7 +87,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
type_allowed_values = ["Informative", "Malicious", "Misinformation", "Disinformation", "Unknown"]
|
||||
if 'state' in self._parameters:
|
||||
if isinstance(self._parameters.get('state'), list):
|
||||
for i in self._parameters.get('state'):
|
||||
for i in self._parameters['state']:
|
||||
if i in type_allowed_values:
|
||||
self.add_attribute('state', value=i)
|
||||
else:
|
||||
|
|
@ -100,7 +102,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
type_allowed_values = ["Verified", "Unverified", "Unknown"]
|
||||
if 'verified-username' in self._parameters:
|
||||
if isinstance(self._parameters.get('verified-username'), list):
|
||||
for i in self._parameters.get('verified-username'):
|
||||
for i in self._parameters['verified-username']:
|
||||
if i in type_allowed_values:
|
||||
self.add_attribute('verified-username', value=i)
|
||||
else:
|
||||
|
|
@ -110,7 +112,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# embedded-link.
|
||||
if 'embedded-link' in self._parameters:
|
||||
if isinstance(self._parameters.get('embedded-link'), list):
|
||||
for i in self._parameters.get('embedded-link'):
|
||||
for i in self._parameters['embedded-link']:
|
||||
self.add_attribute('embedded-link', value=i)
|
||||
else:
|
||||
self.add_attribute('embedded-link', value=self._parameters['embedded-link'])
|
||||
|
|
@ -118,7 +120,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# embedded-safe-link
|
||||
if 'embedded-safe-link' in self._parameters:
|
||||
if isinstance(self._parameters.get('embedded-safe-link'), list):
|
||||
for i in self._parameters.get('embedded-safe-link'):
|
||||
for i in self._parameters['embedded-safe-link']:
|
||||
self.add_attribute('embedded-safe-link', value=i)
|
||||
else:
|
||||
self.add_attribute('embedded-safe-link', value=self._parameters['embedded-safe-link'])
|
||||
|
|
@ -126,7 +128,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# Hashtag into the microblog post.
|
||||
if 'hashtag' in self._parameters:
|
||||
if isinstance(self._parameters.get('hashtag'), list):
|
||||
for i in self._parameters.get('hashtag'):
|
||||
for i in self._parameters['hashtag']:
|
||||
self.add_attribute('hashtag', value=i)
|
||||
else:
|
||||
self.add_attribute('hashtag', value=self._parameters['hashtag'])
|
||||
|
|
@ -134,7 +136,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# username quoted
|
||||
if 'username-quoted' in self._parameters:
|
||||
if isinstance(self._parameters.get('username-quoted'), list):
|
||||
for i in self._parameters.get('username-quoted'):
|
||||
for i in self._parameters['username-quoted']:
|
||||
self.add_attribute('username-quoted', value=i)
|
||||
else:
|
||||
self.add_attribute('username-quoted', value=self._parameters['username-quoted'])
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
from .. import MISPEvent
|
||||
|
||||
try:
|
||||
|
|
@ -13,23 +14,23 @@ except ImportError:
|
|||
|
||||
class Neo4j():
|
||||
|
||||
def __init__(self, host='localhost:7474', username='neo4j', password='neo4j'):
|
||||
def __init__(self, host: str='localhost:7474', username: str='neo4j', password: str='neo4j') -> None:
|
||||
if not has_py2neo:
|
||||
raise Exception('py2neo is required, please install: pip install py2neo')
|
||||
authenticate(host, username, password)
|
||||
self.graph = Graph("http://{}/db/data/".format(host))
|
||||
self.graph = Graph(f"http://{host}/db/data/")
|
||||
|
||||
def load_events_directory(self, directory):
|
||||
self.events = []
|
||||
def load_events_directory(self, directory: str) -> None:
|
||||
self.events: list[MISPEvent] = []
|
||||
for path in glob.glob(os.path.join(directory, '*.json')):
|
||||
e = MISPEvent()
|
||||
e.load(path)
|
||||
self.import_event(e)
|
||||
|
||||
def del_all(self):
|
||||
def del_all(self) -> None:
|
||||
self.graph.delete_all()
|
||||
|
||||
def import_event(self, event):
|
||||
def import_event(self, event: MISPEvent) -> None:
|
||||
tx = self.graph.begin()
|
||||
event_node = Node('Event', uuid=event.uuid, name=event.info)
|
||||
# event_node['distribution'] = event.distribution
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
|
|
@ -156,7 +155,7 @@ def extract_field(report, field_name):
|
|||
def load_openioc_file(openioc_path):
|
||||
if not os.path.exists(openioc_path):
|
||||
raise Exception("Path doesn't exists.")
|
||||
with open(openioc_path, 'r') as f:
|
||||
with open(openioc_path) as f:
|
||||
return load_openioc(f)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from io import BytesIO
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from datetime import datetime
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
from pathlib import Path
|
||||
|
||||
from base64 import b64encode
|
||||
from datetime import datetime
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from . import FileObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from ..exceptions import InvalidMISPObject
|
||||
|
||||
import lief
|
||||
import lief.PE
|
||||
|
||||
try:
|
||||
import pydeep # type: ignore
|
||||
|
|
@ -24,7 +27,10 @@ except ImportError:
|
|||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
def make_pe_objects(lief_parsed: lief.PE.Binary, misp_file: FileObject, standalone: bool = True, default_attributes_parameters: dict = {}):
|
||||
def make_pe_objects(lief_parsed: lief.PE.Binary,
|
||||
misp_file: FileObject,
|
||||
standalone: bool = True,
|
||||
default_attributes_parameters: dict[str, Any] = {}) -> tuple[FileObject, PEObject, list[PESectionObject]]:
|
||||
pe_object = PEObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||
misp_file.add_reference(pe_object.uuid, 'includes', 'PE indicators')
|
||||
pe_sections = []
|
||||
|
|
@ -35,44 +41,57 @@ def make_pe_objects(lief_parsed: lief.PE.Binary, misp_file: FileObject, standalo
|
|||
|
||||
class PEObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parsed: Optional[lief.PE.Binary] = None, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, **kwargs):
|
||||
__pe: lief.PE.Binary
|
||||
|
||||
def __init__(self, parsed: lief.PE.Binary | None = None, # type: ignore[no-untyped-def]
|
||||
filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | list[int] | None = None,
|
||||
**kwargs) -> None:
|
||||
"""Creates an PE object, with lief"""
|
||||
super().__init__('pe', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||
if pseudofile:
|
||||
if isinstance(pseudofile, BytesIO):
|
||||
self.__pe = lief.PE.parse(io=pseudofile)
|
||||
p = lief.PE.parse(obj=pseudofile)
|
||||
elif isinstance(pseudofile, bytes):
|
||||
self.__pe = lief.PE.parse(raw=pseudofile)
|
||||
p = lief.PE.parse(raw=list(pseudofile))
|
||||
elif isinstance(pseudofile, list):
|
||||
p = lief.PE.parse(raw=pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('Pseudo file can be BytesIO or bytes got {}'.format(type(pseudofile)))
|
||||
raise InvalidMISPObject(f'Pseudo file can be BytesIO or bytes got {type(pseudofile)}')
|
||||
if not p:
|
||||
raise InvalidMISPObject('Unable to parse pseudofile')
|
||||
self.__pe = p
|
||||
elif filepath:
|
||||
self.__pe = lief.PE.parse(filepath)
|
||||
if p := lief.PE.parse(filepath):
|
||||
self.__pe = p
|
||||
else:
|
||||
raise InvalidMISPObject(f'Unable to parse {filepath}')
|
||||
elif parsed:
|
||||
# Got an already parsed blob
|
||||
if isinstance(parsed, lief.PE.Binary):
|
||||
self.__pe = parsed
|
||||
else:
|
||||
raise InvalidMISPObject('Not a lief.PE.Binary: {}'.format(type(parsed)))
|
||||
raise InvalidMISPObject(f'Not a lief.PE.Binary: {type(parsed)}')
|
||||
self.generate_attributes()
|
||||
|
||||
def _is_exe(self):
|
||||
def _is_exe(self) -> bool:
|
||||
if not self._is_dll() and not self._is_driver():
|
||||
return self.__pe.header.has_characteristic(lief.PE.HEADER_CHARACTERISTICS.EXECUTABLE_IMAGE)
|
||||
return self.__pe.header.has_characteristic(lief.PE.Header.CHARACTERISTICS.EXECUTABLE_IMAGE)
|
||||
return False
|
||||
|
||||
def _is_dll(self):
|
||||
return self.__pe.header.has_characteristic(lief.PE.HEADER_CHARACTERISTICS.DLL)
|
||||
def _is_dll(self) -> bool:
|
||||
return self.__pe.header.has_characteristic(lief.PE.Header.CHARACTERISTICS.DLL)
|
||||
|
||||
def _is_driver(self):
|
||||
def _is_driver(self) -> bool:
|
||||
# List from pefile
|
||||
system_DLLs = set(('ntoskrnl.exe', 'hal.dll', 'ndis.sys', 'bootvid.dll', 'kdcom.dll'))
|
||||
system_DLLs = {'ntoskrnl.exe', 'hal.dll', 'ndis.sys', 'bootvid.dll', 'kdcom.dll'}
|
||||
if system_DLLs.intersection([imp.lower() for imp in self.__pe.libraries]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_pe_type(self):
|
||||
def _get_pe_type(self) -> str:
|
||||
if self._is_dll():
|
||||
return 'dll'
|
||||
elif self._is_driver():
|
||||
|
|
@ -82,31 +101,27 @@ class PEObject(AbstractMISPObjectGenerator):
|
|||
else:
|
||||
return 'unknown'
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('type', value=self._get_pe_type())
|
||||
# General information
|
||||
self.add_attribute('entrypoint-address', value=self.__pe.entrypoint)
|
||||
self.add_attribute('compilation-timestamp', value=datetime.utcfromtimestamp(self.__pe.header.time_date_stamps).isoformat())
|
||||
self.add_attribute('imphash', value=lief.PE.get_imphash(self.__pe, lief.PE.IMPHASH_MODE.PEFILE))
|
||||
self.add_attribute('authentihash', value=self.__pe.authentihash_sha256.hex())
|
||||
try:
|
||||
if (self.__pe.has_resources
|
||||
and self.__pe.resources_manager.has_version
|
||||
and self.__pe.resources_manager.version.has_string_file_info
|
||||
and self.__pe.resources_manager.version.string_file_info.langcode_items):
|
||||
fileinfo = dict(self.__pe.resources_manager.version.string_file_info.langcode_items[0].items.items())
|
||||
r_manager = self.__pe.resources_manager
|
||||
if isinstance(r_manager, lief.PE.ResourcesManager):
|
||||
version = r_manager.version
|
||||
if isinstance(version, lief.PE.ResourceVersion) and version.string_file_info is not None:
|
||||
fileinfo = dict(version.string_file_info.langcode_items[0].items.items())
|
||||
self.add_attribute('original-filename', value=fileinfo.get('OriginalFilename'))
|
||||
self.add_attribute('internal-filename', value=fileinfo.get('InternalName'))
|
||||
self.add_attribute('file-description', value=fileinfo.get('FileDescription'))
|
||||
self.add_attribute('file-version', value=fileinfo.get('FileVersion'))
|
||||
self.add_attribute('lang-id', value=self.__pe.resources_manager.version.string_file_info.langcode_items[0].key)
|
||||
self.add_attribute('product-name', value=fileinfo.get('ProductName'))
|
||||
self.add_attribute('product-version', value=fileinfo.get('ProductVersion'))
|
||||
self.add_attribute('company-name', value=fileinfo.get('CompanyName'))
|
||||
self.add_attribute('legal-copyright', value=fileinfo.get('LegalCopyright'))
|
||||
except lief.read_out_of_bound:
|
||||
# The file is corrupted
|
||||
pass
|
||||
self.add_attribute('lang-id', value=version.string_file_info.langcode_items[0].key)
|
||||
# Sections
|
||||
self.sections = []
|
||||
if self.__pe.sections:
|
||||
|
|
@ -116,10 +131,14 @@ class PEObject(AbstractMISPObjectGenerator):
|
|||
# Skip section if name is none AND size is 0.
|
||||
continue
|
||||
s = PESectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
||||
self.add_reference(s.uuid, 'includes', 'Section {} of PE'.format(pos))
|
||||
self.add_reference(s.uuid, 'includes', f'Section {pos} of PE')
|
||||
if ((self.__pe.entrypoint >= section.virtual_address)
|
||||
and (self.__pe.entrypoint < (section.virtual_address + section.virtual_size))):
|
||||
self.add_attribute('entrypoint-section-at-position', value='{}|{}'.format(section.name, pos))
|
||||
if isinstance(section.name, bytes):
|
||||
section_name = section.name.decode()
|
||||
else:
|
||||
section_name = section.name
|
||||
self.add_attribute('entrypoint-section-at-position', value=f'{section_name}|{pos}')
|
||||
pos += 1
|
||||
self.sections.append(s)
|
||||
self.add_attribute('number-sections', value=len(self.sections))
|
||||
|
|
@ -139,16 +158,30 @@ class PEObject(AbstractMISPObjectGenerator):
|
|||
|
||||
class PECertificate(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, certificate: lief.PE.x509, **kwargs):
|
||||
def __init__(self, certificate: lief.PE.x509, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('x509')
|
||||
self.__certificate = certificate
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('issuer', value=self.__certificate.issuer)
|
||||
self.add_attribute('serial-number', value=self.__certificate.serial_number)
|
||||
self.add_attribute('validity-not-before', value=datetime(*self.__certificate.valid_from))
|
||||
self.add_attribute('validity-not-after', value=datetime(*self.__certificate.valid_to))
|
||||
if len(self.__certificate.valid_from) == 6:
|
||||
self.add_attribute('validity-not-before',
|
||||
value=datetime(year=self.__certificate.valid_from[0],
|
||||
month=self.__certificate.valid_from[1],
|
||||
day=self.__certificate.valid_from[2],
|
||||
hour=self.__certificate.valid_from[3],
|
||||
minute=self.__certificate.valid_from[4],
|
||||
second=self.__certificate.valid_from[5]))
|
||||
if len(self.__certificate.valid_to) == 6:
|
||||
self.add_attribute('validity-not-after',
|
||||
value=datetime(year=self.__certificate.valid_to[0],
|
||||
month=self.__certificate.valid_to[1],
|
||||
day=self.__certificate.valid_to[2],
|
||||
hour=self.__certificate.valid_to[3],
|
||||
minute=self.__certificate.valid_to[4],
|
||||
second=self.__certificate.valid_to[5]))
|
||||
self.add_attribute('version', value=self.__certificate.version)
|
||||
self.add_attribute('subject', value=self.__certificate.subject)
|
||||
self.add_attribute('signature_algorithm', value=self.__certificate.signature_algorithm)
|
||||
|
|
@ -157,19 +190,19 @@ class PECertificate(AbstractMISPObjectGenerator):
|
|||
|
||||
class PESigners(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, signer: lief.PE.SignerInfo, **kwargs):
|
||||
def __init__(self, signer: lief.PE.SignerInfo, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('authenticode-signerinfo')
|
||||
self.__signer = signer
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('issuer', value=self.__signer.issuer)
|
||||
self.add_attribute('serial-number', value=self.__signer.serial_number)
|
||||
self.add_attribute('version', value=self.__signer.version)
|
||||
self.add_attribute('digest_algorithm', value=self.__signer.digest_algorithm.name)
|
||||
self.add_attribute('encryption_algorithm', value=self.__signer.encryption_algorithm.name)
|
||||
self.add_attribute('digest_algorithm', value=str(self.__signer.digest_algorithm))
|
||||
self.add_attribute('encryption_algorithm', value=str(self.__signer.encryption_algorithm))
|
||||
self.add_attribute('digest-base64', value=b64encode(self.__signer.encrypted_digest))
|
||||
info = self.__signer.get_attribute(lief.PE.SIG_ATTRIBUTE_TYPES.SPC_SP_OPUS_INFO)
|
||||
info: lief.PE.SpcSpOpusInfo = self.__signer.get_attribute(lief.PE.Attribute.TYPE.SPC_SP_OPUS_INFO) # type: ignore[assignment]
|
||||
if info:
|
||||
self.add_attribute('program-name', value=info.program_name)
|
||||
self.add_attribute('url', value=info.more_info)
|
||||
|
|
@ -177,17 +210,17 @@ class PESigners(AbstractMISPObjectGenerator):
|
|||
|
||||
class PESectionObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, section: lief.PE.Section, **kwargs):
|
||||
def __init__(self, section: lief.PE.Section, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Creates an PE Section object. Object generated by PEObject."""
|
||||
super().__init__('pe-section')
|
||||
self.__section = section
|
||||
self.__data = bytes(self.__section.content)
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('name', value=self.__section.name)
|
||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(self.__section.size) > 0:
|
||||
# zero-filled sections can create too many correlations
|
||||
to_ids = float(self.__section.entropy) > 0
|
||||
disable_correlation = not to_ids
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# Standard imports
|
||||
import base64
|
||||
|
|
@ -49,7 +50,7 @@ def create_flowable_tag(misp_tag):
|
|||
return [Flowable_Tag(text=misp_tag.name, color=misp_tag.colour, custom_style=col1_style)]
|
||||
|
||||
|
||||
class Flowable_Tag(Flowable):
|
||||
class Flowable_Tag(Flowable): # type: ignore[misc]
|
||||
"""
|
||||
Custom flowable to handle tags. Draw one Tag with the webview formatting
|
||||
Modified from : http://two.pairlist.net/pipermail/reportlab-users/2005-February/003695.html
|
||||
|
|
@ -108,7 +109,7 @@ class Flowable_Tag(Flowable):
|
|||
LEFT_INTERNAL_PADDING = 2
|
||||
ELONGATION = LEFT_INTERNAL_PADDING * 2
|
||||
|
||||
p = Paragraph("<font color='{}'>{}</font>".format(self.choose_good_text_color(), self.text), style=self.custom_style)
|
||||
p = Paragraph(f"<font color='{self.choose_good_text_color()}'>{self.text}</font>", style=self.custom_style)
|
||||
string_width = stringWidth(self.text, self.custom_style.fontName, self.custom_style.fontSize)
|
||||
|
||||
self.width = string_width + ELONGATION
|
||||
|
|
@ -615,7 +616,7 @@ class Value_Formatter():
|
|||
curr_uuid = str(is_safe_value(uuid))
|
||||
curr_baseurl = self.config[moduleconfig[0]]
|
||||
curr_url = uuid_to_url(curr_baseurl, curr_uuid)
|
||||
html_url = "<a href={}>{}</a>".format(curr_url, safe_string(text))
|
||||
html_url = f"<a href={curr_url}>{safe_string(text)}</a>"
|
||||
|
||||
if color:
|
||||
# They want fancy colors
|
||||
|
|
@ -744,7 +745,7 @@ class Value_Formatter():
|
|||
answer = YES_ANSWER
|
||||
if is_safe_value(published_timestamp):
|
||||
# Published and have published date
|
||||
answer += '({})'.format(published_timestamp.strftime(EXPORT_DATE_FORMAT))
|
||||
answer += f'({published_timestamp.strftime(EXPORT_DATE_FORMAT)})'
|
||||
else:
|
||||
# Published without published date
|
||||
answer += "(no date)"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
|
|
@ -8,13 +9,13 @@ class SBSignatureObject(AbstractMISPObjectGenerator):
|
|||
'''
|
||||
Sandbox Analyzer
|
||||
'''
|
||||
def __init__(self, software: str, report: list, **kwargs):
|
||||
def __init__(self, software: str, report: list[tuple[str, str]], **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('sb-signature', **kwargs)
|
||||
self._software = software
|
||||
self._report = report
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
''' Parse the report for relevant attributes '''
|
||||
self.add_attribute("software", value=self._software)
|
||||
for (signature_name, description) in self._report:
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from io import StringIO
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, authorized_keys_path: Optional[Union[Path, str]] = None, authorized_keys_pseudofile: Optional[StringIO] = None, **kwargs):
|
||||
# PY3 way:
|
||||
def __init__(self, authorized_keys_path: Path | str | None = None, # type: ignore[no-untyped-def]
|
||||
authorized_keys_pseudofile: StringIO | None = None, **kwargs):
|
||||
super().__init__('ssh-authorized-keys', **kwargs)
|
||||
if authorized_keys_path:
|
||||
with open(authorized_keys_path, 'r') as f:
|
||||
with open(authorized_keys_path) as f:
|
||||
self.__pseudofile = StringIO(f.read())
|
||||
elif authorized_keys_pseudofile and isinstance(authorized_keys_pseudofile, StringIO):
|
||||
self.__pseudofile = authorized_keys_pseudofile
|
||||
|
|
@ -26,7 +28,7 @@ class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
|||
self.__data = self.__pseudofile.getvalue()
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
for line in self.__pseudofile:
|
||||
if line.startswith('ssh') or line.startswith('ecdsa'):
|
||||
key = line.split(' ')[1]
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
try:
|
||||
from misp_stix_converter.converters.buildMISPAttribute import buildEvent # type: ignore
|
||||
from misp_stix_converter.converters import convert # type: ignore
|
||||
from misp_stix_converter.converters.convert import MISPtoSTIX # type: ignore
|
||||
has_misp_stix_converter = True
|
||||
except ImportError:
|
||||
has_misp_stix_converter = False
|
||||
|
||||
|
||||
def load_stix(stix, distribution: int = 3, threat_level_id: int = 2, analysis: int = 0):
|
||||
'''Returns a MISPEvent object from a STIX package'''
|
||||
if not has_misp_stix_converter:
|
||||
raise Exception('You need to install misp_stix_converter: pip install git+https://github.com/MISP/MISP-STIX-Converter.git')
|
||||
stix = convert.load_stix(stix)
|
||||
return buildEvent(stix, distribution=distribution,
|
||||
threat_level_id=threat_level_id, analysis=analysis)
|
||||
|
||||
|
||||
def make_stix_package(misp_event, to_json: bool = False, to_xml: bool = False):
|
||||
'''Returns a STIXPackage from a MISPEvent.
|
||||
|
||||
Optionally can return the package in json or xml.
|
||||
|
||||
'''
|
||||
if not has_misp_stix_converter:
|
||||
raise Exception('You need to install misp_stix_converter: pip install git+https://github.com/MISP/MISP-STIX-Converter.git')
|
||||
package = MISPtoSTIX(misp_event)
|
||||
if to_json:
|
||||
return package.to_json()
|
||||
elif to_xml:
|
||||
return package.to_xml()
|
||||
else:
|
||||
return package
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
|
|
@ -12,7 +13,7 @@ from ..abstract import resources_path
|
|||
static_repo = "https://github.com/MISP/misp-objects/archive/main.zip"
|
||||
|
||||
|
||||
def update_objects():
|
||||
def update_objects() -> None:
|
||||
r = requests.get(static_repo)
|
||||
|
||||
zipped_repo = BytesIO(r.content)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
try:
|
||||
from pyfaup.faup import Faup # type: ignore
|
||||
|
|
@ -17,13 +20,13 @@ faup = Faup()
|
|||
|
||||
class URLObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, url: str, generate_all=False, **kwargs):
|
||||
def __init__(self, url: str, generate_all=False, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('url', **kwargs)
|
||||
self._generate_all = True if generate_all is True else False
|
||||
faup.decode(unquote_plus(url))
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('url', value=faup.url.decode())
|
||||
if faup.get_host():
|
||||
self.add_attribute('host', value=faup.get_host())
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
# Original sourcecode: https://github.com/hayk57/MISP_registration_check
|
||||
|
|
@ -11,14 +15,16 @@ from .abstractgenerator import AbstractMISPObjectGenerator
|
|||
class VehicleObject(AbstractMISPObjectGenerator):
|
||||
'''Vehicle object generator out of regcheck.org.uk'''
|
||||
|
||||
country_urls = {
|
||||
country_urls: dict[str, str] = {
|
||||
'fr': "http://www.regcheck.org.uk/api/reg.asmx/CheckFrance",
|
||||
'es': "http://www.regcheck.org.uk/api/reg.asmx/CheckSpain",
|
||||
'uk': "http://www.regcheck.org.uk/api/reg.asmx/Check"
|
||||
}
|
||||
|
||||
def __init__(self, country: str, registration: str, username: str, **kwargs):
|
||||
def __init__(self, country: str, registration: str, username: str, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('vehicle', **kwargs)
|
||||
if country not in self.country_urls:
|
||||
raise ValueError(f"Country {country} not supportet, must be one of {self.country_urls.keys()}")
|
||||
self._country = country
|
||||
self._registration = registration
|
||||
self._username = username
|
||||
|
|
@ -26,10 +32,10 @@ class VehicleObject(AbstractMISPObjectGenerator):
|
|||
self.generate_attributes()
|
||||
|
||||
@property
|
||||
def report(self):
|
||||
def report(self) -> dict[str, Any]:
|
||||
return self._report
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
carDescription = self._report["Description"]
|
||||
carMake = self._report["CarMake"]["CurrentTextValue"]
|
||||
carModel = self._report["CarModel"]["CurrentTextValue"]
|
||||
|
|
@ -65,14 +71,14 @@ class VehicleObject(AbstractMISPObjectGenerator):
|
|||
self.add_attribute('date-first-registration', type='text', value=firstRegistration)
|
||||
self.add_attribute('image-url', type='text', value=ImageUrl)
|
||||
|
||||
def _query(self):
|
||||
payload = "RegistrationNumber={}&username={}".format(self._registration, self._username)
|
||||
def _query(self) -> dict[str, Any]:
|
||||
payload = f"RegistrationNumber={self._registration}&username={self._username}"
|
||||
headers = {
|
||||
'Content-Type': "application/x-www-form-urlencoded",
|
||||
'cache-control': "no-cache",
|
||||
}
|
||||
|
||||
response = requests.request("POST", self.country_urls.get(self._country), data=payload, headers=headers)
|
||||
response = requests.request("POST", self.country_urls[self._country], data=payload, headers=headers)
|
||||
# FIXME: Clean that up.
|
||||
for item in response.text.split("</vehicleJson>"):
|
||||
if "<vehicleJson>" in item:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Optional
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
try:
|
||||
|
|
@ -24,7 +25,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
|
||||
:indicator: IOC to search VirusTotal for
|
||||
'''
|
||||
def __init__(self, apikey: str, indicator: str, vt_proxies: Optional[dict] = None, **kwargs):
|
||||
def __init__(self, apikey: str, indicator: str, vt_proxies: dict[str, str] | None = None, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('virustotal-report', **kwargs)
|
||||
indicator = indicator.strip()
|
||||
self._resource_type = self.__validate_resource(indicator)
|
||||
|
|
@ -33,20 +34,20 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
self._report = self.__query_virustotal(apikey, indicator)
|
||||
self.generate_attributes()
|
||||
else:
|
||||
error_msg = "A valid indicator is required. (One of type url, md5, sha1, sha256). Received '{}' instead".format(indicator)
|
||||
error_msg = f"A valid indicator is required. (One of type url, md5, sha1, sha256). Received '{indicator}' instead"
|
||||
raise InvalidMISPObject(error_msg)
|
||||
|
||||
def get_report(self):
|
||||
def get_report(self) -> dict[str, Any]:
|
||||
return self._report
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
''' Parse the VirusTotal report for relevant attributes '''
|
||||
self.add_attribute("last-submission", value=self._report["scan_date"])
|
||||
self.add_attribute("permalink", value=self._report["permalink"])
|
||||
ratio = "{}/{}".format(self._report["positives"], self._report["total"])
|
||||
self.add_attribute("detection-ratio", value=ratio)
|
||||
|
||||
def __validate_resource(self, ioc: str):
|
||||
def __validate_resource(self, ioc: str) -> str | bool:
|
||||
'''
|
||||
Validate the data type of an indicator.
|
||||
Domains and IP addresses aren't supported because
|
||||
|
|
@ -62,7 +63,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
return "file"
|
||||
return False
|
||||
|
||||
def __query_virustotal(self, apikey: str, resource: str):
|
||||
def __query_virustotal(self, apikey: str, resource: str) -> dict[str, Any]:
|
||||
'''
|
||||
Query VirusTotal for information about an indicator
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
|
||||
:resource: Indicator to search in VirusTotal
|
||||
'''
|
||||
url = "https://www.virustotal.com/vtapi/v2/{}/report".format(self._resource_type)
|
||||
url = f"https://www.virustotal.com/vtapi/v2/{self._resource_type}/report"
|
||||
params = {"apikey": apikey, "resource": resource}
|
||||
# for now assume we're using a public API key - we'll figure out private keys later
|
||||
if self._proxies:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "pymisp"
|
||||
version = "2.4.178"
|
||||
version = "2.4.185"
|
||||
description = "Python API for MISP."
|
||||
authors = ["Raphaël Vinot <raphael.vinot@circl.lu>"]
|
||||
license = "BSD-2-Clause"
|
||||
|
|
@ -21,6 +21,7 @@ classifiers=[
|
|||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Security',
|
||||
'Topic :: Internet'
|
||||
]
|
||||
|
|
@ -43,21 +44,20 @@ include = [
|
|||
python = "^3.8"
|
||||
requests = "^2.31.0"
|
||||
python-dateutil = "^2.8.2"
|
||||
jsonschema = ">=4.17.3"
|
||||
deprecated = "^1.2.14"
|
||||
extract_msg = {version = "^0.45.0", optional = true}
|
||||
RTFDE = {version = "^0.1.0", optional = true}
|
||||
extract_msg = {version = "^0.47.0", optional = true}
|
||||
RTFDE = {version = "^0.1.1", optional = true}
|
||||
oletools = {version = "^0.60.1", optional = true}
|
||||
python-magic = {version = "^0.4.27", optional = true}
|
||||
pydeep2 = {version = "^0.5.1", optional = true}
|
||||
lief = {version = "^0.13.2", optional = true}
|
||||
beautifulsoup4 = {version = "^4.12.2", optional = true}
|
||||
lief = {version = "^0.14.1", optional = true}
|
||||
beautifulsoup4 = {version = "^4.12.3", optional = true}
|
||||
validators = {version = "^0.22.0", optional = true}
|
||||
sphinx-autodoc-typehints = {version = "^1.24.0", optional = true}
|
||||
sphinx-autodoc-typehints = {version = "^2.0.0", optional = true}
|
||||
recommonmark = {version = "^0.7.1", optional = true}
|
||||
reportlab = {version = "^4.0.6", optional = true}
|
||||
reportlab = {version = "^4.1.0", optional = true}
|
||||
pyfaup = {version = "^1.2", optional = true}
|
||||
publicsuffixlist = {version = "^0.10.0.20231022", optional = true}
|
||||
publicsuffixlist = {version = "^0.10.0.20231214", optional = true}
|
||||
urllib3 = {extras = ["brotli"], version = "*", optional = true}
|
||||
Sphinx = [
|
||||
{version = "<7.2", python = "<3.9", optional = true},
|
||||
|
|
@ -76,15 +76,16 @@ brotli = ['urllib3']
|
|||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
requests-mock = "^1.11.0"
|
||||
mypy = "^1.6.1"
|
||||
mypy = "^1.8.0"
|
||||
ipython = [
|
||||
{version = "<8.13.0", python = "<3.9"},
|
||||
{version = "^8.13.0", python = ">=3.9"}
|
||||
{version = "^8.18.0", python = ">=3.9"},
|
||||
{version = "^8.19.0", python = ">=3.10"}
|
||||
]
|
||||
jupyterlab = "^4.0.7"
|
||||
types-requests = "^2.31.0.10"
|
||||
types-python-dateutil = "^2.8.19.14"
|
||||
types-redis = "^4.6.0.7"
|
||||
jupyterlab = "^4.1.2"
|
||||
types-requests = "^2.31.0.20240218"
|
||||
types-python-dateutil = "^2.8.19.20240106"
|
||||
types-redis = "^4.6.0.20240218"
|
||||
types-Flask = "^1.1.6"
|
||||
pytest-cov = "^4.1.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,858 +0,0 @@
|
|||
Return-Path: <suvorov.s@nalg.ru>
|
||||
Delivered-To: kinney@noth.com
|
||||
Received: (qmail 11769 invoked from network); 22 Aug 2016 14:23:01 -0000
|
||||
Received: from smtprelay0207.b.hostedemail.com (HELO smtprelay.b.hostedemail.com) (64.98.42.207)
|
||||
by smtp.server.net with SMTP; 22 Aug 2016 14:23:01 -0000
|
||||
Received: from filter.hostedemail.com (10.5.19.248.rfc1918.com [10.5.19.248])
|
||||
by smtprelay06.b.hostedemail.com (Postfix) with ESMTP id 2CC378D014
|
||||
for <kinney@noth.com>; Mon, 22 Aug 2016 14:22:58 +0000 (UTC)
|
||||
Received: from DM6PR06MB4475.namprd06.prod.outlook.com (2603:10b6:207:3d::31)
|
||||
by BL0PR06MB4465.namprd06.prod.outlook.com with HTTPS id 12345 via
|
||||
BL0PR02CA0054.NAMPRD02.PROD.OUTLOOK.COM; Mon, 1 Oct 2018 09:49:22 +0000
|
||||
Received: from DM3NAM03FT035.eop-NAM03.prod.protection.outlook.com
|
||||
(2a01:111:f400:7e49::205) by CY4PR0601CA0051.outlook.office365.com
|
||||
(2603:10b6:910:89::28) with Microsoft SMTP Server (version=TLS1_2,
|
||||
cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.1185.23 via Frontend
|
||||
Transport; Mon, 1 Oct 2018 09:49:21 +0000
|
||||
X-Session-Marker: 6A64617A657940616C6578616E646572736D6974682E636F6D
|
||||
X-Spam-Summary: 69,4.5,0,,d41d8cd98f00b204,suvorov.s@nalg.ru,:,RULES_HIT:46:150:152:379:553:871:967:989:1000:1254:1260:1263:1313:1381:1516:1517:1520:1575:1594:1605:1676:1699:1730:1747:1764:1777:1792:1823:2044:2197:2199:2393:2525:2560:2563:2682:2685:2827:2859:2911:2933:2937:2939:2942:2945:2947:2951:2954:3022:3867:3872:3890:3934:3936:3938:3941:3944:3947:3950:3953:3956:3959:4425:5007:6001:6261:6506:6678:6747:6748:7281:7398:7688:8599:8824:8957:9009:9025:9388:10004:10848:11604:11638:11639:11783:11914:12043:12185:12445:12517:12519:12740:13026:14149:14381:14658:14659:14687:21080:21221:30054:30055:30065:30066,0,RBL:none,CacheIP:none,Bayesian:0.5,0.5,0.5,Netcheck:none,DomainCache:0,MSF:not bulk,SPF:fn,MSBL:0,DNSBL:none,Custom_rules:0:0:0,LFtime:5,LUA_SUMMARY:none
|
||||
X-HE-Tag: print38_7083d7fd63e24
|
||||
X-Filterd-Recvd-Size: 64695
|
||||
Received: from computer_3436 (unknown [43.230.105.145])
|
||||
(Authenticated sender: jdazey@alexandersmith.com)
|
||||
by omf06.b.hostedemail.com (Postfix) with ESMTPA
|
||||
for <kinney@noth.com>; Mon, 22 Aug 2016 14:22:52 +0000 (UTC)
|
||||
From: =?UTF-8?B?0YHQu9GD0LbQsdCwINCk0J3QoSDQlNCw0L3QuNC40Lsg0KHRg9Cy0L7RgNC+0LI=?= <suvorov.s@nalg.ru>
|
||||
To: kinney@noth.com
|
||||
Subject: =?UTF-8?B?0L/QuNGB0YzQvNC+INGD0LLQtdC00L7QvC3QtQ==?=
|
||||
Content-Type: multipart/mixed; boundary="2NqJR3m2cLnhEraiqXA4Q9hqnmihx7b7"
|
||||
|
||||
--2NqJR3m2cLnhEraiqXA4Q9hqnmihx7b7
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
0J3QsNC70L7Qs9C+0L/Qu9Cw0YLQtdC70YzRidC40LohPGJyPg0K0JjQvdGE0L7RgNC80LjRgNGD
|
||||
0LXQvCDQktCw0YEg0L7QsSDQuNC80LXRjtGJ0LXQudGB0Y8g0LfQsNC00L7Qu9C20LXQvdC90L7R
|
||||
gdGC0LguPHA+DQrQn9GA0L7RgdGM0LHQsCDQvtC30L3QsNC60L7QvNC40YLRjNGB0Y8g0LIg0L/R
|
||||
gNC40LvQvtC20LXQvdC40LguPHA+DQo8YnI+DQo8YnI+DQrQoSDRg9Cy0LDQttC10L3QuNC10Lws
|
||||
PHA+DQrQuNC90YHQv9C10LrRgtC+0YAg0KTQndChINCg0KQg0JXQs9C+0YAg0KHRg9Cy0L7RgNC+
|
||||
0LIuPGJyPg0KPHA+DQo8YnI+DQo8cD4NCjxwPg0K0JjQvdGE0L7RgNC80LDRhtC40L7QvdC90YvQ
|
||||
uSDRgNCw0LfQtNC10Ls8YnI+DQo8YnI+DQrQn9GA0LXQt9C40LTQtdC90YIg0L/QvtGA0YPRh9C4
|
||||
0Lsg0L/RgNCw0LLQuNGC0LXQu9GM0YHRgtCy0YMg0LfQsNC60YDQtdC/0LjRgtGMINCyINC30LDQ
|
||||
utC+0L3QtSDRgNC40YHQui3QvtGA0LjQtdC90YLQuNGA0L7QstCw0L3QvdGL0Lkg0L/QvtC00YXQ
|
||||
vtC0INC6INC/0YDQvtCy0LXRgNC60LDQvDxwPg0KPHA+DQoxNSDQsNCy0LPRg9GB0YLQsCAyMDE2
|
||||
PGJyPg0K0JLQsNC70LXRgNC40Y8g0JfQtdC90L7QstC40L3QsDxicj4NCjxwPg0K0J/RgNC10LfQ
|
||||
uNC00LXQvdGCINC/0L7RgNGD0YfQuNC7INC/0YDQsNCy0LjRgtC10LvRjNGB0YLQstGDINC30LDQ
|
||||
utGA0LXQv9C40YLRjCDQsiDQt9Cw0LrQvtC90LUg0YDQuNGB0Lot0L7RgNC40LXQvdGC0LjRgNC+
|
||||
0LLQsNC90L3Ri9C5INC/0L7QtNGF0L7QtCDQuiDQv9GA0L7QstC10YDQutCw0LzQktCy0LXRgdGC
|
||||
0Lgg0YLQsNC60L7QuSDQv9C+0LTRhdC+0LQg0L/RgNC4INC+0YDQs9Cw0L3QuNC30LDRhtC40Lgg
|
||||
0LrQvtC90YLRgNC+0LvRjNC90L4t0L3QsNC00LfQvtGA0L3Ri9GFINC80LXRgNC+0L/RgNC40Y/R
|
||||
gtC40Lkg0Lgg0LIg0YbQtdC70L7QvCDQvtC/0YLQuNC80LjQt9C40YDQvtCy0LDRgtGMINC/0L7Q
|
||||
tNC+0LHQvdGL0LUg0LzQtdGA0L7Qv9GA0LjRj9GC0LjRjyDQvdCwINGA0LXQs9C40L7QvdCw0LvR
|
||||
jNC90L7QvCDQuCDQvNGD0L3QuNGG0LjQv9Cw0LvRjNC90L7QvCDRg9GA0L7QstC90Y/RhSDQn9GA
|
||||
0LXQt9C40LTQtdC90YIg0KDQpCDQktC70LDQtNC40LzQuNGAINCf0YPRgtC40L0g0L/QvtGA0YPR
|
||||
h9C40Lsg0LrQsNCx0LzQuNC90YMg0LTQviAzMSDQtNC10LrQsNCx0YDRjyDRgtC10LrRg9GJ0LXQ
|
||||
s9C+INCz0L7QtNCwLiDQmNC30LzQtdC90LXQvdC40Y8g0LzQvtCz0YPRgiDQutC+0YHQvdGD0YLR
|
||||
jNGB0Y8g0Lgg0LLQvdC10L/Qu9Cw0L3QvtCy0YvRhSDQv9GA0L7QstC10YDQvtC6LiDQkiDRgdC+
|
||||
0L7RgtCy0LXRgtGB0YLQstC40Lgg0YEg0YPQutCw0LfQsNC90LjRj9C80Lgg0L/RgNC10LfQuNC0
|
||||
0LXQvdGC0LAsINCT0LXQvdC10YDQsNC70YzQvdCw0Y8g0L/RgNC+0LrRg9GA0LDRgtGD0YDQsCDQ
|
||||
oNCkINC00L7Qu9C20L3QsCDQstC90LXRgdGC0Lgg0LIg0LfQsNC60L7QvdC+0LTQsNGC0LXQu9GM
|
||||
0YHRgtCy0L4g0L/QvtC/0YDQsNCy0LrQuCwg0L/RgNC10LTRg9GB0LzQsNGC0YDQuNCy0LDRjtGJ
|
||||
0LjQtSDRgdC+0LPQu9Cw0YHQvtCy0LDQvdC40LUg0YLQsNC60LjRhSDQv9GA0L7QstC10YDQvtC6
|
||||
INGBINC+0YDQs9Cw0L3QsNC80Lgg0L/RgNC+0LrRg9GA0LDRgtGD0YDRiy4g0K3RgtC+INC30LDR
|
||||
gtGA0L7QvdC10YIg0L/RgNC+0LLQtdGA0LrQuCwg0LjQvdC40YbQuNC40YDRg9C10LzRi9C1INCy
|
||||
INGB0LLRj9C30Lgg0YEg0L3QsNGA0YPRiNC10L3QuNC10Lwg0L/RgNCw0LIg0L/QvtGC0YDQtdCx
|
||||
0LjRgtC10LvQtdC5LCDQuCDQvdC10LrQvtGC0L7RgNGL0LUg0LTRgNGD0LPQuNC1LjxwPg0KPGJy
|
||||
Pg0K0J3QsNGA0Y/QtNGDINGBINGN0YLQuNC8LCDRgNGP0LQg0LfQsNC60L7QvdC+0LTQsNGC0LXQ
|
||||
u9GM0L3Ri9GFINC40LfQvNC10L3QtdC90LjQuSDQvNC+0LbQtdGCINC60L7RgdC90YPRgtGM0YHR
|
||||
jyDQtdC00LjQvdC+0LPQviDRgNC10LXRgdGC0YDQsCDQv9GA0L7QstC10YDQvtC6IChwcm92ZXJr
|
||||
aS5nb3YucnUpLjxwPg0KPGJyPg0K0KLQsNC6LCDQvtGA0LPQsNC90YssINGA0LXQsNC70LjQt9GD
|
||||
0Y7RidC40LUg0LrQvtC90YLRgNC+0LvRjNC90L4t0L3QsNC00LfQvtGA0L3Ri9C1INC/0L7Qu9C9
|
||||
0L7QvNC+0YfQuNGPLCDQtNC+0LvQttC90Ysg0LHRg9C00YPRgiDQv9C+INGB0L7Qs9C70LDRgdC+
|
||||
0LLQsNC90LjRjiDRgSDQk9C10L3QtdGA0LDQu9GM0L3QvtC5INC/0YDQvtC60YPRgNCw0YLRg9GA
|
||||
0L7QuSDQoNCkINGA0LDQt9GA0LDQsdC+0YLQsNGC0Ywg0Lgg0LjQt9C00LDRgtGMINCw0LrRgtGL
|
||||
LCDRgNC10LPQu9Cw0LzQtdC90YLQuNGA0YPRjtGJ0LjQtSDQv9C+0YDRj9C00L7QuiDQstC90LXR
|
||||
gdC10L3QuNGPINC40L3RhNC+0YDQvNCw0YbQuNC4INC+INC/0YDQvtCy0LXRgNC60LDRhSDQsiDQ
|
||||
tdC00LjQvdGL0Lkg0YDQtdC10YHRgtGAINC/0YDQvtCy0LXRgNC+0LouINCQINC30LAg0L3QtdCy
|
||||
0L3QtdGB0LXQvdC40LUg0YLQsNC60LjRhSDRgdCy0LXQtNC10L3QuNC5INC4INC30LAg0L3QsNGA
|
||||
0YPRiNC10L3QuNC1INC/0L7RgNGP0LTQutCwINC4INGB0YDQvtC60L7QsiDQuNGFINCy0L3QtdGB
|
||||
0LXQvdC40Y8g0L/RgNC10LTQu9Cw0LPQsNC10YLRgdGPINGD0YHRgtCw0L3QvtCy0LjRgtGMINCw
|
||||
0LTQvNC40L3QuNGB0YLRgNCw0YLQuNCy0L3Rg9GOINC+0YLQstC10YLRgdGC0LLQtdC90L3QvtGB
|
||||
0YLRjC4g0JrQsNC60LjQtSDQuNC80LXQvdC90L4g0L3QsNC60LDQt9Cw0L3QuNGPINC80L7Qs9GD
|
||||
0YIg0LHRi9GC0Ywg0LLQstC10LTQtdC90Ysg0LIg0JrQvtCQ0J8g0KDQpCDigJMg0L3QtSDRg9GC
|
||||
0L7Rh9C90Y/QtdGC0YHRjy4g0J7QttC40LTQsNC10YLRgdGPLCDRh9GC0L4g0YPQutCw0LfQsNC9
|
||||
0L3Ri9C1INC/0L7RgNGD0YfQtdC90LjRjyDQsdGD0LTRg9GCINC40YHQv9C+0LvQvdC10L3RiyDQ
|
||||
uiAxINC00LXQutCw0LHRgNGPLjxicj4NCjxwPg0K0JrRgNC+0LzQtSDRgtC+0LPQviwg0LTQviAx
|
||||
NSDQtNC10LrQsNCx0YDRjyDQk9C10L3QtdGA0LDQu9GM0L3QvtC5INC/0YDQvtC60YPRgNCw0YLR
|
||||
g9GA0LUg0KDQpCDQvdC10L7QsdGF0L7QtNC40LzQviDQsdGD0LTQtdGCINC00L7RgNCw0LHQvtGC
|
||||
0LDRgtGMINC10LTQuNC90YvQuSDRgNC10LXRgdGC0YAg0L/RgNC+0LLQtdGA0L7QuiDRgtCw0Los
|
||||
INGH0YLQvtCx0Ysg0L/RgNC4INCy0L3QtdGB0LXQvdC40Lgg0LIg0L3QtdCz0L4g0LjQvdGE0L7R
|
||||
gNC80LDRhtC40Lgg0YHRgtCw0LvQviDQstC+0LfQvNC+0LbQvdGL0Lwg0LjRgdC/0L7Qu9GM0LfQ
|
||||
vtCy0LDQvdC40LUg0YHQstC10LTQtdC90LjQuSDQuNC3INC00YDRg9Cz0LjRhSDQs9C+0YHRg9C0
|
||||
0LDRgNGB0YLQstC10L3QvdGL0YUg0LjQvdGE0L7RgNC80LDRhtC40L7QvdC90YvRhSDRgdC40YHR
|
||||
gtC10LwsINGB0L/RgNCw0LLQvtGH0L3QuNC60L7QsiDQuCDQutC70LDRgdGB0LjRhNC40LrQsNGC
|
||||
0L7RgNC+0LIuINCi0LDQutC20LUg0LPQu9Cw0LLQsCDQs9C+0YHRg9C00LDRgNGB0YLQstCwINGD
|
||||
0LrQsNC30LDQuywg0YfRgtC+INC90LXQvtCx0YXQvtC00LjQvNCwINGE0L7RgNC80LDQu9C40LfQ
|
||||
sNGG0LjRjyDQuCDQutC+0L3QutGA0LXRgtC40LfQsNGG0LjRjyDQstC90L7RgdC40LzQvtC5INCy
|
||||
INGA0LXQtdGB0YLRgCDQuNC90YTQvtGA0LzQsNGG0LjQuCwg0LXRgdC70Lgg0L7QvdCwINC60LDR
|
||||
gdCw0LXRgtGB0Y8g0L/RgNCw0LLQvtCy0YvRhSDQvtGB0L3QvtCy0LDQvdC40Lkg0LTQu9GPINC+
|
||||
0YDQs9Cw0L3QuNC30LDRhtC40Lgg0L/RgNC+0LLQtdGA0L7Qui4g0KDQtdGH0Ywg0LjQtNC10YIg
|
||||
0Lgg0L4g0YTQvtGA0LzQsNC70LjQt9Cw0YbQuNC4INGC0YDQtdCx0L7QstCw0L3QuNC5LCDRgdC+
|
||||
0LHQu9GO0LTQtdC90LjQtSDQutC+0YLQvtGA0YvRhSDQvtGG0LXQvdC40LLQsNC10YLRgdGPINC/
|
||||
0YDQuCDQv9GA0L7QstC10LTQtdC90LjQuCDRgdC+0L7RgtCy0LXRgtGB0YLQstGD0Y7RidC40YUg
|
||||
0LzQtdGA0L7Qv9GA0LjRj9GC0LjQuSwg0LAg0YLQsNC60LbQtSDRgNC10LfRg9C70YzRgtCw0YLQ
|
||||
vtCyINC/0YDQvtCy0LXRgNC+0Log0Lgg0L/RgNC40L3Rj9GC0YvRhSDQvNC10YAuPHA+DQo8YnI+
|
||||
DQrQn9C+0LzQuNC80L4g0Y3RgtC+0LPQviDQv9GA0LXQt9C40LTQtdC90YIg0L/QvtGA0YPRh9C4
|
||||
0Lsg0L/RgNCw0LLQuNGC0LXQu9GM0YHRgtCy0YMg0YDQsNGB0YHQvNC+0YLRgNC10YLRjCDQstC+
|
||||
0L/RgNC+0YEg0L4g0YHRgtC40LzRg9C70LjRgNGD0Y7RidC40YUg0LLRi9C/0LvQsNGC0LDRhSDQ
|
||||
tNC70Y8g0YHQvtGC0YDRg9C00L3QuNC60L7QsiDRhNC10LTQtdGA0LDQu9GM0L3Ri9GFINC40YHQ
|
||||
v9C+0LvQvdC40YLQtdC70YzQvdGL0YUg0L7RgNCz0LDQvdC+0LIsINC60L7RgtC+0YDRi9C1INC+
|
||||
0YHRg9GJ0LXRgdGC0LLQu9GP0Y7RgiDQv9GA0L7QstC10YDQutC4LjxwPg0KPGJyPg0K0J/QviDR
|
||||
gNC10LfRg9C70YzRgtCw0YLQsNC8INGN0LvQtdC60YLRgNC+0L3QvdC+0LPQviDQsNGD0LrRhtC4
|
||||
0L7QvdCwINC30LDQutC70Y7Rh9Cw0YLRjCDQutC+0L3RgtGA0LDQutGCINC90LAg0LHRg9C80LDQ
|
||||
s9C1INC90LUg0L3Rg9C20L3Qvjxicj4NCjxwPg0KMTIg0LDQstCz0YPRgdGC0LAgMjAxNjxicj4N
|
||||
CtCS0LDQu9C10YDQuNGPINCX0LXQvdC+0LLQuNC90LA8cD4NCjxicj4NCtCf0L4g0YDQtdC30YPQ
|
||||
u9GM0YLQsNGC0LDQvCDRjdC70LXQutGC0YDQvtC90L3QvtCz0L4g0LDRg9C60YbQuNC+0L3QsCDQ
|
||||
t9Cw0LrQu9GO0YfQsNGC0Ywg0LrQvtC90YLRgNCw0LrRgiDQvdCwINCx0YPQvNCw0LPQtSDQvdC1
|
||||
INC90YPQttC90L7QnNC40L3RjdC60L7QvdC+0LzRgNCw0LfQstC40YLQuNGPINGA0LDQt9GK0Y/R
|
||||
gdC90LjQu9C+LCDRh9GC0L4g0L/QviDRgNC10LfRg9C70YzRgtCw0YLQsNC8INGN0LvQtdC60YLR
|
||||
gNC+0L3QvdC+0LPQviDQsNGD0LrRhtC40L7QvdCwINC/0YDQuCDQvdCw0LvQuNGH0LjQuCDQt9Cw
|
||||
0LrQu9GO0YfQtdC90L3QvtCz0L4g0LrQvtC90YLRgNCw0LrRgtCwINCyINGN0LvQtdC60YLRgNC+
|
||||
0L3QvdC+0Lkg0YTQvtGA0LzQtSDQt9Cw0LrQu9GO0YfQsNGC0Ywg0LrQvtC90YLRgNCw0LrRgiDQ
|
||||
tdGJ0LUg0Lgg0LIg0L/QuNGB0YzQvNC10L3QvdC+0Lkg0YTQvtGA0LzQtSDQvdCwINCx0YPQvNCw
|
||||
0LbQvdC+0Lwg0L3QvtGB0LjRgtC10LvQtSDQvdC1INC90YPQttC90L4uINCS0LXQtNC+0LzRgdGC
|
||||
0LLQviDQv9C+0LTRh9C10YDQutC90YPQu9C+LCDRh9GC0L4g0YLQsNC60LDRjyDQvdC10L7QsdGF
|
||||
0L7QtNC40LzQvtGB0YLRjCDQvdC1INC/0YDQtdC00YPRgdC80L7RgtGA0LXQvdCwINC00LXQudGB
|
||||
0YLQstGD0Y7RidC40Lwg0LfQsNC60L7QvdC+0LTQsNGC0LXQu9GM0YHRgtCy0L7QvCAo0L/QuNGB
|
||||
0YzQvNC+INCc0LjQvdGN0LrQvtC90L7QvNGA0LDQt9Cy0LjRgtC40Y8g0KDQvtGB0YHQuNC4INC+
|
||||
0YIgNSDQuNGO0LvRjyAyMDE2INCzLiDihJYg0JQyONC4LTE2ODcpLjxwPg0KPHA+DQrQodC10LPQ
|
||||
vtC00L3RjyDQtNC70Y8g0YLQvtCz0L4sINGH0YLQvtCx0Ysg0LfQsNC60LvRjtGH0LjRgtGMINGN
|
||||
0LvQtdC60YLRgNC+0L3QvdGL0Lkg0LrQvtC90YLRgNCw0LrRgiDQvdCwINGN0LvQtdC60YLRgNC+
|
||||
0L3QvdC+0Lwg0LDRg9C60YbQuNC+0L3QtSwg0LXQs9C+INC90LXQvtCx0YXQvtC00LjQvNC+INGA
|
||||
0LDQt9C80LXRgdGC0LjRgtGMINCyINC10LTQuNC90L7QuSDQuNC90YTQvtGA0LzQsNGG0LjQvtC9
|
||||
0L3QvtC5INGB0LjRgdGC0LXQvNC1ICjQldCY0KEpLCDQv9GA0LjRh9C10Lwg0L7QvSDQtNC+0LvQ
|
||||
ttC10L0g0LHRi9GC0Ywg0L/QvtC00L/QuNGB0LDQvSDRg9GB0LjQu9C10L3QvdC+0Lkg0Y3Qu9C1
|
||||
0LrRgtGA0L7QvdC90L7QuSDQv9C+0LTQv9C40YHRjNGOINC70LjRhtCwLCDQuNC80LXRjtGJ0LXQ
|
||||
s9C+INC/0YDQsNCy0L4g0LTQtdC50YHRgtCy0L7QstCw0YLRjCDQvtGCINC40LzQtdC90Lgg0LfQ
|
||||
sNC60LDQt9GH0LjQutCwLiDQodC00LXQu9Cw0YLRjCDRjdGC0L4g0L3QtdC+0LHRhdC+0LTQuNC8
|
||||
0L4g0LIg0YLQtdGH0LXQvdC40LUg0YLRgNC10YUg0YDQsNCx0L7Rh9C40YUg0LTQvdC10Lkg0YEg
|
||||
0LTQsNGC0Ysg0YDQsNC30LzQtdGJ0LXQvdC40Y8g0L/RgNC+0LXQutGC0LAg0YLQvtCz0L4g0LbQ
|
||||
tSDQutC+0L3RgtGA0LDQutGC0LAuPGJyPg0KPGJyPg0K0JIg0LrQsNC60LjRhSDRgdC70YPRh9Cw
|
||||
0Y/RhSDQt9Cw0LrQsNC30YfQuNC6INC+0LHRj9C30LDQvSDQv9GA0L7QstC+0LTQuNGC0Ywg0Y3Q
|
||||
u9C10LrRgtGA0L7QvdC90YvQuSDQsNGD0LrRhtC40L7QvT8g0KPQt9C90LDQudGC0LUg0LjQtyDQ
|
||||
vNCw0YLQtdGA0LjQsNC70LAgItCj0YHQu9C+0LLQuNGPINC/0YDQuNC80LXQvdC10L3QuNGPINGN
|
||||
0LvQtdC60YLRgNC+0L3QvdC+0LPQviDQsNGD0LrRhtC40L7QvdCwIiDQsiAi0K3QvdGG0LjQutC7
|
||||
0L7Qv9C10LTQuNC4INGA0LXRiNC10L3QuNC5LiDQk9C+0YHRg9C00LDRgNGB0YLQstC10L3QvdGL
|
||||
0LUg0Lgg0LrQvtGA0L/QvtGA0LDRgtC40LLQvdGL0LUg0LfQsNC60YPQv9C60LgiINC40L3RgtC1
|
||||
0YDQvdC10YIt0LLQtdGA0YHQuNC4INGB0LjRgdGC0LXQvNGLINCT0JDQoNCQ0J3Qoi4g0J/QvtC7
|
||||
0YPRh9C40YLQtSDQv9C+0LvQvdGL0Lkg0LTQvtGB0YLRg9C/INC90LAgMyDQtNC90Y8g0LHQtdGB
|
||||
0L/Qu9Cw0YLQvdC+ITxwPg0K0J/QvtC70YPRh9C40YLRjCDQtNC+0YHRgtGD0L88cD4NCtCj0LrQ
|
||||
sNC30LDQvdC90YvQuSDQv9GA0L7QtdC60YIg0L/RgNC4INGN0YLQvtC8INGC0L7QttC1INC00L7Q
|
||||
u9C20LXQvSDQsdGL0YLRjCDQv9C+0LTQv9C40YHQsNC9INGD0YHQuNC70LXQvdC90L7QuSDRjdC7
|
||||
0LXQutGC0YDQvtC90L3QvtC5INC/0L7QtNC/0LjRgdGM0Y4sINC90L4g0YPQttC1INC70LjRhtCw
|
||||
LCDQutC+0YLQvtGA0L7QtSDQuNC80LXQtdGCINC/0YDQsNCy0L4g0LTQtdC50YHRgtCy0L7QstCw
|
||||
0YLRjCDQvtGCINC40LzQtdC90Lgg0L/QvtCx0LXQtNC40YLQtdC70Y8g0Y3Qu9C10LrRgtGA0L7Q
|
||||
vdC90L7Qs9C+INCw0YPQutGG0LjQvtC90LAuINCf0L7QvNC40LzQviDQv9GA0L7QtdC60YLQsCDQ
|
||||
utC+0L3RgtGA0LDQutGC0LAg0YLQsNC60L7QuSDQv9C+0LHQtdC00LjRgtC10LvRjCDQtNC+0LvQ
|
||||
ttC10L0g0L/RgNC10LTQvtGB0YLQsNCy0LjRgtGMINC+0LHQtdGB0L/QtdGH0LXQvdC40LUg0LjR
|
||||
gdC/0L7Qu9C90LXQvdC40Y8g0LrQvtC90YLRgNCw0LrRgtCwICjRhy4gNyDRgdGCLiA3MCDQpNC1
|
||||
0LTQtdGA0LDQu9GM0L3QvtCz0L4g0LfQsNC60L7QvdCwINC+0YIgNSDQsNC/0YDQtdC70Y8gMjAx
|
||||
MyDQsy4g4oSWIDQ0LdCk0JcgItCeINC60L7QvdGC0YDQsNC60YLQvdC+0Lkg0YHQuNGB0YLQtdC8
|
||||
0LUg0LIg0YHRhNC10YDQtSDQt9Cw0LrRg9C/0L7QuiDRgtC+0LLQsNGA0L7Qsiwg0YDQsNCx0L7R
|
||||
giwg0YPRgdC70YPQsyDQtNC70Y8g0L7QsdC10YHQv9C10YfQtdC90LjRjyDQs9C+0YHRg9C00LDR
|
||||
gNGB0YLQstC10L3QvdGL0YUg0Lgg0LzRg9C90LjRhtC40L/QsNC70YzQvdGL0YUg0L3Rg9C20LQi
|
||||
OyDQtNCw0LvQtdC1IOKAkyDQl9Cw0LrQvtC9IOKEliA0NC3QpNCXKS48cD4NCjxicj4NCtChINC8
|
||||
0L7QvNC10L3RgtCwINGA0LDQt9C80LXRidC10L3QuNGPINCyINCV0JjQoSDQv9C+0LTQv9C40YHQ
|
||||
sNC90L3QvtCz0L4g0LfQsNC60LDQt9GH0LjQutC+0Lwg0LrQvtC90YLRgNCw0LrRgtCwINC+0L0g
|
||||
0YHRh9C40YLQsNC10YLRgdGPINC30LDQutC70Y7Rh9C10L3QvdGL0LwgKNGHLiA4INGB0YIuIDcw
|
||||
INCX0LDQutC+0L3QsCDihJYgNDQt0KTQlykuPGJyPg0KPGJyPg0K0J3QsNC/0L7QvNC90LjQvCwg
|
||||
0L/QviDQtNC10LnRgdGC0LLRg9GO0YnQtdC80YMg0LfQsNC60L7QvdC+0LTQsNGC0LXQu9GM0YHR
|
||||
gtCy0YMsINGN0LvQtdC60YLRgNC+0L3QvdGL0Lkg0LDRg9C60YbQuNC+0L0g0L/RgNC+0LLQvtC0
|
||||
0LjRgtGB0Y8g0L/Rg9GC0LXQvCDRgdC90LjQttC10L3QuNGPINC90LDRh9Cw0LvRjNC90L7QuSAo
|
||||
0LzQsNC60YHQuNC80LDQu9GM0L3QvtC5KSDRhtC10L3RiyDQutC+0L3RgtGA0LDQutGC0LAsINGD
|
||||
0LrQsNC30LDQvdC90L7QuSDQsiDQuNC30LLQtdGJ0LXQvdC40Lgg0L4g0L/RgNC+0LLQtdC00LXQ
|
||||
vdC40Lgg0YLQsNC60L7Qs9C+INCw0YPQutGG0LjQvtC90LAuINCSINC90LXQvCDQvNC+0LPRg9GC
|
||||
INGD0YfQsNGB0YLQstC+0LLQsNGC0Ywg0YLQvtC70YzQutC+INCw0LrQutGA0LXQtNC40YLQvtCy
|
||||
0LDQvdC90YvQtSDRg9GH0LDRgdGC0L3QuNC60LgsINCwINC80LXRgdGC0L7QvCDQtdCz0L4g0L/R
|
||||
gNC+0LLQtdC00LXQvdC40Y8g0Y/QstC70Y/QtdGC0YHRjyDRjdC70LXQutGC0YDQvtC90L3QsNGP
|
||||
INC/0LvQvtGJ0LDQtNC60LAgKNGB0YIuIDY4INCX0LDQutC+0L3QsCDihJYgNDQt0KTQlykuPGJy
|
||||
Pg0KPHA+DQo1INC00LXQutCw0LHRgNGPIDIwMTQg0LPQvtC00LAg0YHQvtGB0YLQvtGP0LvQvtGB
|
||||
0Ywg0LjQvdGC0LXRgNC90LXRgi3QuNC90YLQtdGA0LLRjNGOINGBINC90LDRh9Cw0LvRjNC90LjQ
|
||||
utC+0Lwg0KPQv9GA0LDQstC70LXQvdC40Y8g0LrQsNC80LXRgNCw0LvRjNC90L7Qs9C+INC60L7Q
|
||||
vdGC0YDQvtC70Y8g0KTQtdC00LXRgNCw0LvRjNC90L7QuSDQvdCw0LvQvtCz0L7QstC+0Lkg0YHQ
|
||||
u9GD0LbQsdGLINCh0LDRgtC40L3Ri9C8INCU0LzQuNGC0YDQuNC10Lwg0KHRgtCw0L3QuNGB0LvQ
|
||||
sNCy0L7QstC40YfQtdC8Ljxicj4NCjxicj4NCtCi0LXQvNCwINC40L3RgtC10YDQvdC10YIt0LjQ
|
||||
vdGC0LXRgNCy0YzRjjogItCd0LDQu9C+0LMg0L3QsCDQtNC+0LHQsNCy0LvQtdC90L3Rg9GOINGB
|
||||
0YLQvtC40LzQvtGB0YLRjDog0L3QvtCy0LDRhtC40LggMjAxNSDQs9C+0LTQsCIuPGJyPg0KPHA+
|
||||
DQrQktC10LTRg9GJ0LDRjzog0JTQvtCx0YDRi9C5INC00LXQvdGMLCDQlNC80LjRgtGA0LjQuSDQ
|
||||
odGC0LDQvdC40YHQu9Cw0LLQvtCy0LjRhyEg0KDQsNGB0YHQutCw0LbQuNGC0LUsINC/0L7QttCw
|
||||
0LvRg9C50YHRgtCwLCDQutCw0LrQuNC1INC90L7QstCw0YbQuNC4LCDRgdCy0Y/Qt9Cw0L3QvdGL
|
||||
0LUg0YEg0L/RgNC10LTRgdGC0LDQstC70LXQvdC40LXQvCDQvtGC0YfQtdGC0L3QvtGB0YLQuCDQ
|
||||
v9C+INCd0JTQoSwg0L7QttC40LTQsNGO0YIg0L3QsNC70L7Qs9C+0L/Qu9Cw0YLQtdC70YzRidC4
|
||||
0LrQvtCyINCyIDIwMTUg0LPQvtC00YM/PGJyPg0KPGJyPg0K0KHQsNGC0LjQvSDQlC7QoS46INCU
|
||||
0L7QsdGA0YvQuSDQtNC10L3RjCEg0J3QsNGH0LjQvdCw0Y8g0YEg0L3QsNC70L7Qs9C+0LLQvtCz
|
||||
0L4g0L/QtdGA0LjQvtC00LAg0LfQsCAxINC60LLQsNGA0YLQsNC7IDIwMTUg0LPQvtC00LAg0L3Q
|
||||
sCDQvtGB0L3QvtCy0LDQvdC40Lgg0L/Rg9C90LrRgtCwIDUuMSDRgdGC0LDRgtGM0LggMTc0INCa
|
||||
0L7QtNC10LrRgdCwICjQsiDRgNC10LTQsNC60YbQuNC4INCk0LXQtNC10YDQsNC70YzQvdC+0LPQ
|
||||
viDQt9Cw0LrQvtC90LAg0L7RgiAyOC4wNi4yMDEzIOKEliAxMzQt0KTQlykg0LIg0L3QsNC70L7Q
|
||||
s9C+0LLRg9GOINC00LXQutC70LDRgNCw0YbQuNGOINC/0L4g0J3QlNChINCy0LrQu9GO0YfQsNGO
|
||||
0YLRgdGPINGB0LLQtdC00LXQvdC40Y8sINGD0LrQsNC30LDQvdC90YvQtSDQsiDQutC90LjQs9C1
|
||||
INC/0L7QutGD0L/QvtC6INC4INC60L3QuNCz0LUg0L/RgNC+0LTQsNC2LiDQn9GA0Lgg0L7RgdGD
|
||||
0YnQtdGB0YLQstC70LXQvdC40Lgg0L/QvtGB0YDQtdC00L3QuNGH0LXRgdC60L7QuSDQtNC10Y/R
|
||||
gtC10LvRjNC90L7RgdGC0Lgg0LIg0L3QsNC70L7Qs9C+0LLRg9GOINC00LXQutC70LDRgNCw0YbQ
|
||||
uNGOINC/0L4g0J3QlNChINCy0LrQu9GO0YfQsNGO0YLRgdGPINGB0LLQtdC00LXQvdC40Y8sINGD
|
||||
0LrQsNC30LDQvdC90YvQtSDQsiDQttGD0YDQvdCw0LvQtSDRg9GH0LXRgtCwINC/0L7Qu9GD0YfQ
|
||||
tdC90L3Ri9GFINC4INCy0YvRgdGC0LDQstC70LXQvdC90YvRhSDRgdGH0LXRgtC+0LIt0YTQsNC6
|
||||
0YLRg9GALCDQsiDQvtGC0L3QvtGI0LXQvdC40Lgg0YPQutCw0LfQsNC90L3QvtC5INC00LXRj9GC
|
||||
0LXQu9GM0L3QvtGB0YLQuC48YnI+DQo8YnI+DQrQn9GA0LjQutCw0LfQvtC8INCk0J3QoSDQoNC+
|
||||
0YHRgdC40Lgg0L7RgiAyOS4xMC4yMDE0IOKEliDQnNCc0JItNy0zLzU1OEAg0YPRgtCy0LXRgNC2
|
||||
0LTQtdC90LAg0YTQvtGA0LzQsCDQvdCw0LvQvtCz0L7QstC+0Lkg0LTQtdC60LvQsNGA0LDRhtC4
|
||||
0Lgg0L/QviDQndCU0KEsINC/0L7RgNGP0LTQvtC6INC10LUg0LfQsNC/0L7Qu9C90LXQvdC40Y8g
|
||||
0Lgg0YTQvtGA0LzQsNGCINC/0YDQtdC00YHRgtCw0LLQu9C10L3QuNGPINCyINGN0LvQtdC60YLR
|
||||
gNC+0L3QvdC+0Lkg0YTQvtGA0LzQtS4g0JIg0L3QsNGB0YLQvtGP0YnQtdC1INCy0YDQtdC80Y8g
|
||||
0L/RgNC40LrQsNC3INC/0YDQvtGF0L7QtNC40YIg0LPQvtGB0YPQtNCw0YDRgdGC0LLQtdC90L3R
|
||||
g9GOINGA0LXQs9C40YHRgtGA0LDRhtC40Y4g0LIg0JzQuNC90Y7RgdGC0LUg0KDQvtGB0YHQuNC4
|
||||
LjxwPg0KPHA+DQrQkiDQvdC+0LLQvtC5INGE0L7RgNC80LUg0L3QsNC70L7Qs9C+0LLQvtC5INC0
|
||||
0LXQutC70LDRgNCw0YbQuNC4INC/0L4g0J3QlNChINC/0YDQtdC00YPRgdC80L7RgtGA0LXQvdGL
|
||||
INGA0LDQt9C00LXQu9GLLCDRgdC+0LTQtdGA0LbQsNGJ0LjQtSDRgdCy0LXQtNC10L3QuNGPINC4
|
||||
0Lcg0LrQvdC40LMg0L/QvtC60YPQv9C+0LosINC60L3QuNCzINC/0YDQvtC00LDQtiwg0LbRg9GA
|
||||
0L3QsNC70L7QsiDRg9GH0LXRgtCwINC/0L7Qu9GD0YfQtdC90L3Ri9GFINC4INCy0YvRgdGC0LDQ
|
||||
stC70LXQvdC90YvRhSDRgdGH0LXRgtC+0LIt0YTQsNC60YLRg9GALjxicj4NCjxicj4NCtCSINGB
|
||||
0L7QvtGC0LLQtdGC0YHRgtCy0LjQuCDRgSDQv9GD0L3QutGC0L7QvCAzINGB0YLQsNGC0YzQuCA4
|
||||
MCDQuCDQv9GD0L3QutGC0L7QvCA1INGB0YLQsNGC0YzQuCAxNzQg0JrQvtC00LXQutGB0LAg0L3Q
|
||||
sNC70L7Qs9C+0LLQsNGPINC00LXQutC70LDRgNCw0YbQuNGPINC/0L4g0J3QlNChINC00L7Qu9C2
|
||||
0L3QsCDQv9GA0LXQtNGB0YLQsNCy0LvRj9GC0YzRgdGPINCyINGN0LvQtdC60YLRgNC+0L3QvdC+
|
||||
0Lkg0YTQvtGA0LzQtSDQv9C+INGC0LXQu9C10LrQvtC80LzRg9C90LjQutCw0YbQuNC+0L3QvdGL
|
||||
0Lwg0LrQsNC90LDQu9Cw0Lwg0YHQstGP0LfQuCDRh9C10YDQtdC3INC+0L/QtdGA0LDRgtC+0YDQ
|
||||
sCDRjdC70LXQutGC0YDQvtC90L3QvtCz0L4g0LTQvtC60YPQvNC10L3RgtC+0L7QsdC+0YDQvtGC
|
||||
0LAuINCi0LDQutC40Lwg0L7QsdGA0LDQt9C+0LwsINGE0L7RgNC80LjRgNC+0LLQsNC90LjQtSDQ
|
||||
vdCw0LvQvtCz0L7QstC+0Lkg0LTQtdC60LvQsNGA0LDRhtC40Lgg0L/QviDQndCU0KEg0L3QsCDQ
|
||||
sdGD0LzQsNC20L3Ri9GFINC90L7RgdC40YLQtdC70Y/RhSDQt9Cw0LrQvtC90L7QtNCw0YLQtdC7
|
||||
0YzRgdGC0LLQvtC8INC+INC90LDQu9C+0LPQsNGFINC4INGB0LHQvtGA0LDRhSDQvdC1INC/0YDQ
|
||||
tdC00YPRgdC80L7RgtGA0LXQvdC+Ljxicj4NCjxicj4NCtCb0LjRhtCwLCDQvdC1INGP0LLQu9GP
|
||||
0Y7RidC40LXRgdGPINC90LDQu9C+0LPQvtC/0LvQsNGC0LXQu9GM0YnQuNC60LDQvNC4INCd0JTQ
|
||||
oSDQuNC70Lgg0L3QsNC70L7Qs9C+0LLRi9C80Lgg0LDQs9C10L3RgtCw0LzQuCDQv9C+INCd0JTQ
|
||||
oSwg0L3QviDQvtGB0YPRidC10YHRgtCy0LvRj9GO0YnQuNC1INC/0L7RgdGA0LXQtNC90LjRh9C1
|
||||
0YHQutGD0Y4g0LTQtdGP0YLQtdC70YzQvdC+0YHRgtGMLCDQtNC+0LvQttC90Ysg0L3QsCDQvtGB
|
||||
0L3QvtCy0LDQvdC40Lgg0L/Rg9C90LrRgtCwIDUuMiDRgdGC0LDRgtGM0LggMTc0INCa0L7QtNC1
|
||||
0LrRgdCwINC/0YDQtdC00YHRgtCw0LLQu9GP0YLRjCDQsiDQvdCw0LvQvtCz0L7QstGL0Lkg0L7R
|
||||
gNCz0LDQvSDQsiDQvtGC0L3QvtGI0LXQvdC40Lgg0YPQutCw0LfQsNC90L3QvtC5INC00LXRj9GC
|
||||
0LXQu9GM0L3QvtGB0YLQuCDQttGD0YDQvdCw0Lsg0YPRh9C10YLQsCDQv9C+0LvRg9GH0LXQvdC9
|
||||
0YvRhSDQuCDQstGL0YHRgtCw0LLQu9C10L3QvdGL0YUg0YHRh9C10YLQvtCyLdGE0LDQutGC0YPR
|
||||
gCDQv9C+INCi0JrQoSDRh9C10YDQtdC3INC+0L/QtdGA0LDRgtC+0YDQsCDQrdCU0J4uPHA+DQo8
|
||||
cD4NCtCk0LXQtNC10YDQsNC70YzQvdC+0Lkg0L3QsNC70L7Qs9C+0LLQvtC5INGB0LvRg9C20LHQ
|
||||
vtC5INGA0LDQt9GA0LDQsdC+0YLQsNC90LAg0LHQtdGB0L/Qu9Cw0YLQvdCw0Y8g0L/RgNC+0LPR
|
||||
gNCw0LzQvNCwICLQndCw0LvQvtCz0L7Qv9C70LDRgtC10LvRjNGJ0LjQuiDQrtCbIiDQtNC70Y8g
|
||||
0LLQtdC00LXQvdC40Y8g0LrQvdC40LMg0L/QvtC60YPQv9C+0Log0Lgg0L/RgNC+0LTQsNC2LCDQ
|
||||
sCDRgtCw0LrQttC1INGE0L7RgNC80LjRgNC+0LLQsNC90LjRjyDQvdCw0LvQvtCz0L7QstC+0Lkg
|
||||
0LTQtdC60LvQsNGA0LDRhtC40Lgg0L/QviDQndCU0KEuINCU0LDQvdC90YvQvCDQv9GA0L7Qs9GA
|
||||
0LDQvNC80L3Ri9C8INC/0YDQvtC00YPQutGC0L7QvCDQvNC+0LbQtdGCINCy0L7RgdC/0L7Qu9GM
|
||||
0LfQvtCy0LDRgtGM0YHRjyDQu9GO0LHQvtC5INC90LDQu9C+0LPQvtC/0LvQsNGC0LXQu9GM0YnQ
|
||||
uNC6INCx0LXRgdC/0LvQsNGC0L3QviDQt9Cw0LPRgNGD0LfQuNCyINC10LPQviDRgSDQvtGE0LjR
|
||||
htC40LDQu9GM0L3QvtCz0L4g0YHQsNC50YLQsCDQpNCd0KEg0KDQvtGB0YHQuNC4IChodHRwOi8v
|
||||
d3d3Lm5hbG9nLnJ1L3JuNzcvL3Byb2dyYW0vYWxsL25hbF91bC8pLjxicj4NCjxicj4NCtCS0LXQ
|
||||
tNGD0YnQsNGPOiDQlNC80LjRgtGA0LjQuSDQodGC0LDQvdC40YHQu9Cw0LLQvtCy0LjRhywg0LXR
|
||||
gdGC0Ywg0YLQsNC60LDRjyDRgdC40YLRg9Cw0YbQuNGPOiDQtNC+0LHRgNC+0YHQvtCy0LXRgdGC
|
||||
0L3QsNGPINC60L7QvNC/0LDQvdC40Y8g0L7RgtGA0LDQt9C40LvQsCDQsiDQtNC10LrQu9Cw0YDQ
|
||||
sNGG0LjQuCDRgNGP0LQg0YHRh9C10YLQvtCyLdGE0LDQutGC0YPRgCwg0L/QviDQutC+0YLQvtGA
|
||||
0YvQvCDQv9C+0YHRgtCw0LLRidC40Log0L7RgtGH0LXRgtC90L7RgdGC0Ywg0L3QtSDRgdC00LDQ
|
||||
uyDQstC+0L7QsdGJ0LUuINCl0L7RgtGPINC90LAg0LzQvtC80LXQvdGCINC/0YDQvtCy0LXQtNC1
|
||||
0L3QuNGPINGB0LTQtdC70LrQuCDQutC+0L3RgtGA0LDQs9C10L3RgiDQv9C+INC00LDQvdC90YvQ
|
||||
vCDQstGL0L/QuNGB0LrQuCDQuNC3INCV0JPQoNCu0Jsg0LHRi9C7INCy0L/QvtC70L3QtSDQtNC1
|
||||
0LnRgdGC0LLRg9GO0YnQuNC8LiDQmtCw0LrQuNC1INC/0L7RgdC70LXQtNGB0YLQstC40Y8g0L7Q
|
||||
ttC40LTQsNGO0YIg0L3QsNC70L7Qs9C+0L/Qu9Cw0YLQtdC70YzRidC40LrQsCDQsiDRgtCw0LrQ
|
||||
vtC8INGB0LvRg9GH0LDQtT8gPHA+DQo8YnI+DQrQodCw0YLQuNC9INCULtChLjog0JXRgdC70Lgg
|
||||
0LrQvtC90YLRgNCw0LPQtdC90YIg0L3QsNC70L7Qs9C+0L/Qu9Cw0YLQtdC70YzRidC40LrQsCDQ
|
||||
vdC1INC/0YDQtdC00YHRgtCw0LLQuNC7INC90LDQu9C+0LPQvtCy0YPRjiDQtNC10LrQu9Cw0YDQ
|
||||
sNGG0LjRjiDQv9C+INCd0JTQoSDQuNC70Lgg0LIg0LXQs9C+INC/0YDQtdC00YHRgtCw0LLQu9C1
|
||||
0L3QvdC+0Lkg0LTQtdC60LvQsNGA0LDRhtC40Lgg0L3QtSDQvtGC0YDQsNC20LXQvdGLINC60LDQ
|
||||
utC40LUt0LvQuNCx0L4g0YHRh9C10YLQsC3RhNCw0LrRgtGD0YDRiywg0YLQviDRgtCw0LrQvtC5
|
||||
INC60L7QvdGC0YDQsNCz0LXQvdGCINGB0YLQsNC90L7QstC40YLRgdGPINC+0LHRitC10LrRgtC+
|
||||
0Lwg0LLQvdC40LzQsNC90LjRjyDRgdC+INGB0YLQvtGA0L7QvdGLINC90LDQu9C+0LPQvtCy0L7Q
|
||||
s9C+INC+0YDQs9Cw0L3QsC4g0J3QsCDQv9C10YDQstC+0Lwg0Y3RgtCw0L/QtSDQsiDRgdC70YPR
|
||||
h9Cw0LUg0L7RgtGB0YPRgtGB0YLQstC40Y8g0L3QsNC70L7Qs9C+0LLQvtC5INC00LXQutC70LDR
|
||||
gNCw0YbQuNC4INC90LDQu9C+0LPQvtCy0YvQuSDQvtGA0LPQsNC9INCx0YPQtNC10YIg0LjQvNC1
|
||||
0YLRjCDQstC+0LfQvNC+0LbQvdC+0YHRgtGMINC/0YDQuNC+0YHRgtCw0L3QvtCy0LjRgtGMINC0
|
||||
0LLQuNC20LXQvdC40LUg0LTQtdC90LXQttC90YvRhSDRgdGA0LXQtNGB0YLQsiDQv9C+INGA0LDR
|
||||
gdGH0LXRgtC90YvQvCDRgdGH0LXRgtCw0LwuINCU0LDQu9C10LUg0L/RgNC+0LLQvtC00LjRgtGB
|
||||
0Y8g0YPRgdGC0LDQvdC+0LLQu9C10L3QvdGL0Lkg0LPQu9Cw0LLQvtC5IDE0INCd0LDQu9C+0LPQ
|
||||
vtCy0L7Qs9C+INC60L7QtNC10LrRgdCwINCg0L7RgdGB0LjQudGB0LrQvtC5INCk0LXQtNC10YDQ
|
||||
sNGG0LjQuCDQutC+0LzQv9C70LXQutGBINC60L7QvdGC0YDQvtC70YzQvdGL0YUg0LzQtdGA0L7Q
|
||||
v9GA0LjRj9GC0LjQuSDQv9C+INGE0L7RgNC80LjRgNC+0LLQsNC90LjRjiDQtNC+0LrQsNC30LDR
|
||||
gtC10LvRjNC90L7QuSDQsdCw0LfRiyDQvtCxINGD0LrQu9C+0L3QtdC90LjQuCDQvtGCINC90LDQ
|
||||
u9C+0LPQvtC+0LHQu9C+0LbQtdC90LjRjy4g0K3RgtC+INC40YHRgtGA0LXQsdC+0LLQsNC90LjQ
|
||||
tSDQuNC90YTQvtGA0LzQsNGG0LjQuCDQuCDQtNC+0LrRg9C80LXQvdGC0L7Qsiwg0LAg0L/RgNC4
|
||||
INC90LXQvtCx0YXQvtC00LjQvNC+0YHRgtC4INC00YDRg9Cz0LjQtSDQvNC10YDQvtC/0YDQuNGP
|
||||
0YLQuNGPINC90LDQu9C+0LPQvtCy0L7Qs9C+INC60L7QvdGC0YDQvtC70Y8uINCf0YDQuCDRjdGC
|
||||
0L7QvCDQsiDRgdC70YPRh9Cw0LUg0YPRgdGC0LDQvdC+0LLQu9C10L3QuNGPINGE0LDQutGC0LAg
|
||||
0YDQtdCw0LvRjNC90L7RgdGC0Lgg0L7Qv9C10YDQsNGG0LjQuCwg0LTQu9GPINGB0LDQvNC+0LPQ
|
||||
viDQvdCw0LvQvtCz0L7Qv9C70LDRgtC10LvRjNGJ0LjQutCwINC90Lgg0LrQsNC60LjRhSDQv9C+
|
||||
0YHQu9C10LTRgdGC0LLQuNC5INC90LUg0LHRg9C00LXRgi4g0Jog0L7RgtCy0LXRgtGB0YLQstC1
|
||||
0L3QvdC+0YHRgtC4INCx0YPQtNC10YIg0L/RgNC40LLQu9C10YfQtdC90L4g0YLQviDQu9C40YbQ
|
||||
viwg0LrQvtGC0L7RgNC+0LUg0L3QsNGA0YPRiNC40LvQviDQvdCw0LvQvtCz0L7QstC+0LUg0LfQ
|
||||
sNC60L7QvdC+0LTQsNGC0LXQu9GM0YHRgtCy0L4uPGJyPg0KPHA+DQrQn9GA0Lgg0Y3RgtC+0Lwg
|
||||
0LzRiyDRgNC10LrQvtC80LXQvdC00YPQtdC8INCw0LrQutGD0YDQsNGC0L3QviDQv9C+0LTRhdC+
|
||||
0LTQuNGC0Ywg0Log0LLRi9Cx0L7RgNGDINC/0L7RgtC10L3RhtC40LDQu9GM0L3Ri9GFINC60L7Q
|
||||
vdGC0YDQsNCz0LXQvdGC0L7QsiDQuCDQv9GA0L7Rj9Cy0LvRj9GC0Ywg0LTQvtC70LbQvdGD0Y4g
|
||||
0L7RgdC80L7RgtGA0LjRgtC10LvRjNC90L7RgdGC0YwsINGC0LDQuiDQutCw0Log0Y3RgtC+INC9
|
||||
0LXQvtCx0YXQvtC00LjQvNC+INC00LvRjyDRgdC+0YXRgNCw0L3QtdC90LjRjyDQtNC10LvQvtCy
|
||||
0L7QuSDRgNC10L/Rg9GC0LDRhtC40Lgg0Lgg0YTQvtGA0LzQuNGA0L7QstCw0L3QuNGPINC/0L7Q
|
||||
u9C+0LbQuNGC0LXQu9GM0L3QvtC5INC90LDQu9C+0LPQvtCy0L7QuSDQuNGB0YLQvtGA0LjQuC48
|
||||
cD4NCjxicj4NCtCS0LXQtNGD0YnQsNGPOiDQldGB0LvQuCDQsiDRgdCy0LXQtNC10L3QuNGP0YUg
|
||||
0YMg0LrQvtC90YLRgNCw0LPQtdC90YLQvtCyINCx0YPQtNGD0YIg0YDQsNGB0YXQvtC20LTQtdC9
|
||||
0LjRjywg0L3QsNC/0YDQuNC80LXRgCwg0YDQsNC30L3Ri9C1INC90L7QvNC10YDQsCDRgdGH0LXR
|
||||
gtC+0LIt0YTQsNC60YLRg9GAINC4INGA0LDQt9C90YvQtSDQtNCw0YLRiywg0LrQsNC6INGN0YLQ
|
||||
viDQsdGD0LTRg9GCINC+0YbQtdC90LjQstCw0YLRjCDQuNC90YHQv9C10LrRgtC+0YDRiz8g0JAg
|
||||
0LXRgdC70Lgg0L7RgtC70LjRh9Cw0Y7RgtGB0Y8g0YHRg9C80LzRiyDQv9C+INC30LDRgNC10LPQ
|
||||
uNGB0YLRgNC40YDQvtCy0LDQvdC90YvQvCDRgdGH0LXRgtCw0Lwt0YTQsNC60YLRg9GA0LDQvD8g
|
||||
PGJyPg0KPGJyPg0K0KHQsNGC0LjQvSDQlC7QoS46INCf0YDQtdC20LTQtSDQstGB0LXQs9C+INCy
|
||||
0L4g0LjQt9Cx0LXQttCw0L3QuNC1INGC0LXRhdC90LjRh9C10YHQutC40YUg0L7RiNC40LHQvtC6
|
||||
INCyINC/0YDQtdC00YHRgtCw0LLQu9C10L3QvdC+0Lkg0LTQtdC60LvQsNGA0LDRhtC40Lgg0L/Q
|
||||
viDQndCU0KEg0LzRiyDRgNC10LrQvtC80LXQvdC00YPQtdC8INC/0YDQvtCy0LXRgNC40YLRjCDQ
|
||||
uNC90YTQvtGA0LzQsNGG0LjRjiDQviDQutC+0L3RgtGA0LDQs9C10L3RgtCw0YUsINC60L7RgtC+
|
||||
0YDQsNGPINGB0L7QtNC10YDQttC40YLRgdGPINCyINCy0LDRiNC10Lkg0YPRh9C10YLQvdC+0Lkg
|
||||
KNCx0YPRhdCz0LDQu9GC0LXRgNGB0LrQvtC5KSDRgdC40YHRgtC10LzQtSwg0L3QsCDQv9GA0LXQ
|
||||
tNC80LXRgiDQv9GA0LDQstC40LvRjNC90L7RgdGC0Lgg0LfQsNC90LXRgdC10L3QuNGPINCyINGB
|
||||
0LjRgdGC0LXQvNGDINCY0J3QnSDQuCDQmtCf0J8g0LrQvtC90YLRgNCw0LPQtdC90YLQvtCyLiDQ
|
||||
nNC+0LbQvdC+INCy0L7RgdC/0L7Qu9GM0LfQvtCy0LDRgtGM0YHRjywg0L3QsNC/0YDQuNC80LXR
|
||||
gCwg0L7QvdC70LDQudC9LdGB0LXRgNCy0LjRgdC+0LwsINGA0LDQt9C80LXRidC10L3QvdGL0Lwg
|
||||
0L3QsCDQvtGE0LjRhtC40LDQu9GM0L3QvtC8INGB0LDQudGC0LUg0KTQndChINCg0L7RgdGB0LjQ
|
||||
uCAod3d3Lm5hbG9nLnJ1LCBodHRwOi8vbnBjaGsubmFsb2cucnUpLjxicj4NCjxicj4NCtCV0YHQ
|
||||
u9C4INGA0LDRgdGF0L7QttC00LXQvdC40Y8g0LIg0YHQstC10LTQtdC90LjRj9GFINC+0LEg0L7Q
|
||||
v9C10YDQsNGG0LjRj9GFINC60L7QvdGC0YDQsNCz0LXQvdGC0L7QsiDQsdGD0LTRg9GCINCy0YHQ
|
||||
tS3RgtCw0LrQuCDQstGL0Y/QstC70LXQvdGLLCDRgtC+INC80Ysg0LjRhSDQtNC10LvQuNC8INC9
|
||||
0LAg0LTQstC1INC60LDRgtC10LPQvtGA0LjQuC4g0J/QtdGA0LLQsNGPINGN0YLQviDRgtC10YXQ
|
||||
vdC40YfQtdGB0LrQuNC1INC+0YjQuNCx0LrQuCwg0YLQsNC6INC90LDQt9GL0LLQsNC10LzRi9C1
|
||||
ICLQvtGI0LjQsdC60Lgg0LLQstC+0LTQsCIsINC60L7RgtC+0YDRi9C1INC90LUg0LjQvNC10Y7R
|
||||
giDQstGL0YHQvtC60L7Qs9C+INC/0YDQuNC+0YDQuNGC0LXRgtCwINCyINC+0YLRgNCw0LHQvtGC
|
||||
0LrQtS4g0J7RiNC40LHQutC4INCyINC90L7QvNC10YDQtSwg0LTQsNGC0LUg0YHRh9C10YLQsC3R
|
||||
hNCw0LrRgtGD0YDRiyDQuNC70Lgg0LIg0LTRgNGD0LPQvtC8INGA0LXQutCy0LjQt9C40YLQtSwg
|
||||
0L3QtSDQstC70LjRj9GO0YnQtdC8INC90LAg0YHRg9C80LzRgyDQvdCw0LvQvtCz0LAsINC80Ysg
|
||||
0YDQsNGB0YHRh9C40YLRi9Cy0LDQtdC8INGD0YHRgtGA0LDQvdGP0YLRjCDQvdCwINGN0YLQsNC/
|
||||
0LUg0LfQsNC/0YDQvtGB0LAg0L/QvtGP0YHQvdC10L3QuNC5LiDQn9GA0Lgg0Y3RgtC+0Lwg0L3Q
|
||||
sNC70L7Qs9C+0L/Qu9Cw0YLQtdC70YzRidC40LrRgyDQv9GA0LXQtNGB0YLQsNCy0LjRgtGB0Y8g
|
||||
0LLQvtC30LzQvtC20L3QvtGB0YLRjCDQvdCw0L/RgNCw0LLQuNGC0Ywg0LIg0L3QsNC70L7Qs9C+
|
||||
0LLRi9C5INC+0YDQs9Cw0L0g0YTQvtGA0LzQsNC70LjQt9C+0LLQsNC90L3Ri9C1INC/0L7Rj9GB
|
||||
0L3QtdC90LjRjyDQsdC10Lcg0LrQsNC60LjRhS3Qu9C40LHQviDQvdCw0LvQvtCz0L7QstGL0YUg
|
||||
0L/QvtGB0LvQtdC00YHRgtCy0LjQuS4g0JTQu9GPINGN0YLQvtCz0L4g0L3QsNC00L4g0LHRg9C0
|
||||
0LXRgiDRg9C60LDQt9Cw0YLRjCDQsiDRgdC/0LXRhtC40LDQu9GM0L3QvtC5INGA0LXQutC+0LzQ
|
||||
tdC90LTQvtCy0LDQvdC90L7QuSDRhNC+0YDQvNC1INC60L7RgNGA0LXQutGC0L3Ri9C1INGB0LLQ
|
||||
tdC00LXQvdC40Y8g0Lgg0L3QsNC/0YDQsNCy0LjRgtGMINC40YUg0L/QviDQotCa0KEg0YfQtdGA
|
||||
0LXQtyDQvtC/0LXRgNCw0YLQvtGA0LAg0Y3Qu9C10LrRgtGA0L7QvdC90L7Qs9C+INC00L7QutGD
|
||||
0LzQtdC90YLQvtC+0LHQvtGA0L7RgtCwINCyINC90LDQu9C+0LPQvtCy0YvQtSDQvtGA0LPQsNC9
|
||||
0YsuINCf0YDQtdC00YHRgtCw0LLQu9GP0YLRjCDRg9GC0L7Rh9C90LXQvdC90YPRjiDQvdCw0LvQ
|
||||
vtCz0L7QstGD0Y4g0LTQtdC60LvQsNGA0LDRhtC40Y4g0L/QviDQndCU0KEg0LIg0YLQsNC60L7Q
|
||||
vCDRgdC70YPRh9Cw0LUg0L3QtSDRgtGA0LXQsdGD0LXRgtGB0Y8uPGJyPg0KPHA+DQrQldGB0LvQ
|
||||
uCDQttC1INGA0LDRgdGF0L7QttC00LXQvdC40Y8g0LLRi9C30LLQsNC90Ysg0L3QtdCy0LXRgNC9
|
||||
0YvQvCDRgNCw0YHRh9C10YLQvtC8INGB0YPQvNC80Ysg0L3QsNC70L7Qs9CwINC40LvQuCDRgdGH
|
||||
0LXRgi3RhNCw0LrRgtGD0YDQsCDQvtGC0YDQsNC20LXQvSDQvtGI0LjQsdC+0YfQvdC+LCDRgtC+
|
||||
INCyINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjQuCDRgdC+INGB0YLQsNGC0YzQtdC5IDgxINCd0LDQ
|
||||
u9C+0LPQvtCy0L7Qs9C+INC60L7QtNC10LrRgdCwINCg0L7RgdGB0LjQudGB0LrQvtC5INCk0LXQ
|
||||
tNC10YDQsNGG0LjQuCDQvdC10L7QsdGF0L7QtNC40LzQviDQsdGD0LTQtdGCINC/0YDQtdC00YHR
|
||||
gtCw0LLQuNGC0Ywg0YPRgtC+0YfQvdC10L3QvdGD0Y4g0L3QsNC70L7Qs9C+0LLRg9GOINC00LXQ
|
||||
utC70LDRgNCw0YbQuNGOINC/0L4g0J3QlNChLiDQmCDQt9C00LXRgdGMINC/0YDQtdC00YPRgdC8
|
||||
0LDRgtGA0LjQstCw0LXRgtGB0Y8g0L3QvtCy0YvQuSDQv9C+0LTRhdC+0LQuINCi0LXQv9C10YDR
|
||||
jCDQtNC+0L/Rg9GB0LrQsNC10YLRgdGPINC/0YDQtdC00YHRgtCw0LLQu9C10L3QuNC1INGD0YLQ
|
||||
vtGH0L3QtdC90L3QvtC5INC90LDQu9C+0LPQvtCy0L7QuSDQtNC10LrQu9Cw0YDQsNGG0LjQuCAi
|
||||
0L3QsCDQtNC10LvRjNGC0YMiLiDQotC+INC10YHRgtGMINCyINGD0YLQvtGH0L3QtdC90L3QvtC5
|
||||
INC90LDQu9C+0LPQvtCy0L7QuSDQtNC10LrQu9Cw0YDQsNGG0LjQuCDQt9Cw0L/QvtC70L3Rj9GO
|
||||
0YLRgdGPINGC0L7Qu9GM0LrQviDRgtC1INGA0LDQt9C00LXQu9GLLCDQsiDQutC+0YLQvtGA0YvQ
|
||||
tSDQstC90LXRgdC10L3RiyDQuNGB0L/RgNCw0LLQu9C10L3QuNGPLiDQn9C+0LTRgNC+0LHQvdC1
|
||||
0LUg0L/QvtGA0Y/QtNC+0Log0LfQsNC/0L7Qu9C90LXQvdC40Y8g0L3QsNC70L7Qs9C+0LLQvtC5
|
||||
INC00LXQutC70LDRgNCw0YbQuNC4INC40LfQu9C+0LbQtdC9INCyINC/0YDQuNC60LDQt9C1INCk
|
||||
0J3QoSDQoNC+0YHRgdC40Lgg0L7RgiAyOS4xMC4yMDE0IOKEliDQnNCc0JItNy0zLzU1OEAuPGJy
|
||||
Pg0KPHA+DQo8cD4NCtCd0LDQu9C+0LPQvtC/0LvQsNGC0LXQu9GM0YnQuNC60LDQvNC4INC4INC/
|
||||
0LvQsNGC0LXQu9GM0YnQuNC60LDQvNC4INGB0LHQvtGA0L7QsiDQv9GA0LjQt9C90LDRjtGC0YHR
|
||||
jyDQvtGA0LPQsNC90LjQt9Cw0YbQuNC4INC4INGE0LjQt9C40YfQtdGB0LrQuNC1INC70LjRhtCw
|
||||
LCDQvdCwINC60L7RgtC+0YDRi9GFINCyINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjQuCDRgSDQvdCw
|
||||
0YHRgtC+0Y/RidC40Lwg0JrQvtC00LXQutGB0L7QvCDQstC+0LfQu9C+0LbQtdC90LAg0L7QsdGP
|
||||
0LfQsNC90L3QvtGB0YLRjCDRg9C/0LvQsNGH0LjQstCw0YLRjCDRgdC+0L7RgtCy0LXRgtGB0YLQ
|
||||
stC10L3QvdC+INC90LDQu9C+0LPQuCDQuCAo0LjQu9C4KSDRgdCx0L7RgNGLLjxicj4NCtCSINC/
|
||||
0L7RgNGP0LTQutC1LCDQv9GA0LXQtNGD0YHQvNC+0YLRgNC10L3QvdC+0Lwg0L3QsNGB0YLQvtGP
|
||||
0YnQuNC8INCa0L7QtNC10LrRgdC+0LwsINGE0LjQu9C40LDQu9GLINC4INC40L3Ri9C1INC+0LHQ
|
||||
vtGB0L7QsdC70LXQvdC90YvQtSDQv9C+0LTRgNCw0LfQtNC10LvQtdC90LjRjyDRgNC+0YHRgdC4
|
||||
0LnRgdC60LjRhSDQvtGA0LPQsNC90LjQt9Cw0YbQuNC5INC40YHQv9C+0LvQvdGP0Y7RgiDQvtCx
|
||||
0Y/Qt9Cw0L3QvdC+0YHRgtC4INGN0YLQuNGFINC+0YDQs9Cw0L3QuNC30LDRhtC40Lkg0L/QviDR
|
||||
g9C/0LvQsNGC0LUg0L3QsNC70L7Qs9C+0LIg0Lgg0YHQsdC+0YDQvtCyINC/0L4g0LzQtdGB0YLR
|
||||
gyDQvdCw0YXQvtC20LTQtdC90LjRjyDRjdGC0LjRhSDRhNC40LvQuNCw0LvQvtCyINC4INC40L3R
|
||||
i9GFINC+0LHQvtGB0L7QsdC70LXQvdC90YvRhSDQv9C+0LTRgNCw0LfQtNC10LvQtdC90LjQuS48
|
||||
YnI+DQrQkiDRgdC70YPRh9Cw0Y/RhSwg0L/RgNC10LTRg9GB0LzQvtGC0YDQtdC90L3Ri9GFINC9
|
||||
0LDRgdGC0L7Rj9GJ0LjQvCDQmtC+0LTQtdC60YHQvtC8LCDQvdCw0LvQvtCz0L7Qv9C70LDRgtC1
|
||||
0LvRjNGJ0LjQutCw0LzQuCDQv9GA0LjQt9C90LDRjtGC0YHRjyDQuNC90L7RgdGC0YDQsNC90L3R
|
||||
i9C1INGB0YLRgNGD0LrRgtGD0YDRiyDQsdC10Lcg0L7QsdGA0LDQt9C+0LLQsNC90LjRjyDRjtGA
|
||||
0LjQtNC40YfQtdGB0LrQvtCz0L4g0LvQuNGG0LAuPHA+DQo8cD4NCtCh0YLQsNGC0YzRjyAyMC4g
|
||||
0JLQt9Cw0LjQvNC+0LfQsNCy0LjRgdC40LzRi9C1INC70LjRhtCwPHA+DQoxLiDQktC30LDQuNC8
|
||||
0L7Qt9Cw0LLQuNGB0LjQvNGL0LzQuCDQu9C40YbQsNC80Lgg0LTQu9GPINGG0LXQu9C10Lkg0L3Q
|
||||
sNC70L7Qs9C+0L7QsdC70L7QttC10L3QuNGPINC/0YDQuNC30L3QsNGO0YLRgdGPINGE0LjQt9C4
|
||||
0YfQtdGB0LrQuNC1INC70LjRhtCwINC4ICjQuNC70LgpINC+0YDQs9Cw0L3QuNC30LDRhtC40Lgs
|
||||
INC+0YLQvdC+0YjQtdC90LjRjyDQvNC10LbQtNGDINC60L7RgtC+0YDRi9C80Lgg0LzQvtCz0YPR
|
||||
giDQvtC60LDQt9GL0LLQsNGC0Ywg0LLQu9C40Y/QvdC40LUg0L3QsCDRg9GB0LvQvtCy0LjRjyDQ
|
||||
uNC70Lgg0Y3QutC+0L3QvtC80LjRh9C10YHQutC40LUg0YDQtdC30YPQu9GM0YLQsNGC0Ysg0LjR
|
||||
hSDQtNC10Y/RgtC10LvRjNC90L7RgdGC0Lgg0LjQu9C4INC00LXRj9GC0LXQu9GM0L3QvtGB0YLQ
|
||||
uCDQv9GA0LXQtNGB0YLQsNCy0LvRj9C10LzRi9GFINC40LzQuCDQu9C40YYsINCwINC40LzQtdC9
|
||||
0L3Qvjo8YnI+DQoxKSDQvtC00L3QsCDQvtGA0LPQsNC90LjQt9Cw0YbQuNGPINC90LXQv9C+0YHR
|
||||
gNC10LTRgdGC0LLQtdC90L3QviDQuCAo0LjQu9C4KSDQutC+0YHQstC10L3QvdC+INGD0YfQsNGB
|
||||
0YLQstGD0LXRgiDQsiDQtNGA0YPQs9C+0Lkg0L7RgNCz0LDQvdC40LfQsNGG0LjQuCwg0Lgg0YHR
|
||||
g9C80LzQsNGA0L3QsNGPINC00L7Qu9GPINGC0LDQutC+0LPQviDRg9GH0LDRgdGC0LjRjyDRgdC+
|
||||
0YHRgtCw0LLQu9GP0LXRgiDQsdC+0LvQtdC1IDIwINC/0YDQvtGG0LXQvdGC0L7Qsi4g0JTQvtC7
|
||||
0Y8g0LrQvtGB0LLQtdC90L3QvtCz0L4g0YPRh9Cw0YHRgtC40Y8g0L7QtNC90L7QuSDQvtGA0LPQ
|
||||
sNC90LjQt9Cw0YbQuNC4INCyINC00YDRg9Cz0L7QuSDRh9C10YDQtdC3INC/0L7RgdC70LXQtNC+
|
||||
0LLQsNGC0LXQu9GM0L3QvtGB0YLRjCDQuNC90YvRhSDQvtGA0LPQsNC90LjQt9Cw0YbQuNC5INC+
|
||||
0L/RgNC10LTQtdC70Y/QtdGC0YHRjyDQsiDQstC40LTQtSDQv9GA0L7QuNC30LLQtdC00LXQvdC4
|
||||
0Y8g0LTQvtC70LXQuSDQvdC10L/QvtGB0YDQtdC00YHRgtCy0LXQvdC90L7Qs9C+INGD0YfQsNGB
|
||||
0YLQuNGPINC+0YDQs9Cw0L3QuNC30LDRhtC40Lkg0Y3RgtC+0Lkg0L/QvtGB0LvQtdC00L7QstCw
|
||||
0YLQtdC70YzQvdC+0YHRgtC4INC+0LTQvdCwINCyINC00YDRg9Cz0L7QuTs8YnI+DQoyKSDQvtC0
|
||||
0L3QviDRhNC40LfQuNGH0LXRgdC60L7QtSDQu9C40YbQviDQv9C+0LTRh9C40L3Rj9C10YLRgdGP
|
||||
INC00YDRg9Cz0L7QvNGDINGE0LjQt9C40YfQtdGB0LrQvtC80YMg0LvQuNGG0YMg0L/QviDQtNC+
|
||||
0LvQttC90L7RgdGC0L3QvtC80YMg0L/QvtC70L7QttC10L3QuNGOOzxicj4NCjMpINC70LjRhtCw
|
||||
INGB0L7RgdGC0L7Rj9GCINCyINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjQuCDRgSDRgdC10LzQtdC5
|
||||
0L3Ri9C8INC30LDQutC+0L3QvtC00LDRgtC10LvRjNGB0YLQstC+0Lwg0KDQvtGB0YHQuNC50YHQ
|
||||
utC+0Lkg0KTQtdC00LXRgNCw0YbQuNC4INCyINCx0YDQsNGH0L3Ri9GFINC+0YLQvdC+0YjQtdC9
|
||||
0LjRj9GFLCDQvtGC0L3QvtGI0LXQvdC40Y/RhSDRgNC+0LTRgdGC0LLQsCDQuNC70Lgg0YHQstC+
|
||||
0LnRgdGC0LLQsCwg0YPRgdGL0L3QvtCy0LjRgtC10LvRjyDQuCDRg9GB0YvQvdC+0LLQu9C10L3Q
|
||||
vdC+0LPQviwg0LAg0YLQsNC60LbQtSDQv9C+0L/QtdGH0LjRgtC10LvRjyDQuCDQvtC/0LXQutCw
|
||||
0LXQvNC+0LPQvi48cD4NCjIuINCh0YPQtCDQvNC+0LbQtdGCINC/0YDQuNC30L3QsNGC0Ywg0LvQ
|
||||
uNGG0LAg0LLQt9Cw0LjQvNC+0LfQsNCy0LjRgdC40LzRi9C80Lgg0L/QviDQuNC90YvQvCDQvtGB
|
||||
0L3QvtCy0LDQvdC40Y/QvCwg0L3QtSDQv9GA0LXQtNGD0YHQvNC+0YLRgNC10L3QvdGL0Lwg0L/R
|
||||
g9C90LrRgtC+0LwgMSDQvdCw0YHRgtC+0Y/RidC10Lkg0YHRgtCw0YLRjNC4LCDQtdGB0LvQuCDQ
|
||||
vtGC0L3QvtGI0LXQvdC40Y8g0LzQtdC20LTRgyDRjdGC0LjQvNC4INC70LjRhtCw0LzQuCDQvNC+
|
||||
0LPRg9GCINC/0L7QstC70LjRj9GC0Ywg0L3QsCDRgNC10LfRg9C70YzRgtCw0YLRiyDRgdC00LXQ
|
||||
u9C+0Log0L/QviDRgNC10LDQu9C40LfQsNGG0LjQuCDRgtC+0LLQsNGA0L7QsiAo0YDQsNCx0L7R
|
||||
giwg0YPRgdC70YPQsykuPGJyPg0KPGJyPg0KPGJyPg0K0J3QtdC00LDQstC90L4gwqvRhtC40YTR
|
||||
gNC+0LLQsNGPINGN0LrQvtC90L7QvNC40LrQsMK7INC+0LTQtdGA0LbQsNC70LAg0LLQsNC20L3R
|
||||
g9GOINC/0L7QsdC10LTRgywg0LLRi9C60LjQvdGD0LIg0LjQtyDQv9GP0YLQtdGA0LrQuCDRgdCw
|
||||
0LzRi9GFINC00L7RgNC+0LPQuNGFINC60L7QvNC/0LDQvdC40Lkg0LzQuNGA0LAg0L/QvtGB0LvQ
|
||||
tdC00L3QtdCz0L4g0L/RgNC10LTRgdGC0LDQstC40YLQtdC70Y8gwqvRgNC10LDQu9GM0L3QvtCz
|
||||
0L4g0YHQtdC60YLQvtGA0LDCuyBFeHhvbk1vYmlsLiDQndC+INC/0L7QutCwINCx0LjRgtCy0LAg
|
||||
wqvRgdC10YDQstC10YDQsCDRgdC+INGB0YLQsNC90LrQvtC8wrsg0L/RgNC40LLQvtC00LjRgiDQ
|
||||
uiDRgtC+0YDQvNC+0LbQtdC90LjRjiDRjdC60L7QvdC+0LzQuNGH0LXRgdC60L7Qs9C+INGA0L7R
|
||||
gdGC0LAuINCe0YfQtdCy0LjQtNC90L4sINGH0YLQviDQtNC70Y8g0L7QutC+0L3Rh9Cw0YLQtdC7
|
||||
0YzQvdC+0LPQviDRhNC+0YDQvNC40YDQvtCy0LDQvdC40Y8g0L3QvtCy0L7Qs9C+INGN0LrQvtC9
|
||||
0L7QvNC40YfQtdGB0LrQvtCz0L4g0YPQutC70LDQtNCwINC80LjRgNGDINC/0YDQuNC00LXRgtGB
|
||||
0Y8g0L/QtdGA0LXQttC40YLRjCDQtdGJ0LUg0L3QtSDQvtC00L3QviDQv9C+0YLRgNGP0YHQtdC9
|
||||
0LjQtS48cD4NCiA8YnI+DQo8cD4NCjxwPg0KPHA+DQog0J/Rj9GC0LXRgNC60YMg0LrRgNGD0L/Q
|
||||
vdC10LnRiNC40YUg0L/QviDRgNGL0L3QvtGH0L3QvtC5INGB0YLQvtC40LzQvtGB0YLQuCDQutC+
|
||||
0LzQv9Cw0L3QuNC5INC+0LrQutGD0L/QuNGA0L7QstCw0LvQuCBJVC3Qs9C40LPQsNC90YLRizxi
|
||||
cj4NCtCd0LXRhNGC0Y/QvdC40LrQvtCyINGB0LzQtdC90LjQu9C4INGC0LXRhdC90L7Qu9C+0LPQ
|
||||
uNC4PGJyPg0K0J3QsCDRgdC80LXQvdGDINGA0LXRgdGD0YDRgdC90L7QuSDRjdC60L7QvdC+0LzQ
|
||||
uNC60LUg0L/RgNC40YXQvtC00LjRgiDRgtC10YXQvdC+0LvQvtCz0LjRh9C10YHQutCw0Y8uINCV
|
||||
0YHQu9C4INGA0LDQvdGM0YjQtSDQsiDRgtC+0L8tNSDQu9C40LTQtdGA0L7QsiDQv9C+INC60LDQ
|
||||
v9C40YLQsNC70LjQt9Cw0YbQuNC4IElULdCz0LjQs9Cw0L3RgtGLINC/0L7Qv9Cw0LTQsNC70Lgg
|
||||
0LvQuNGI0Ywg0LjQt9GA0LXQtNC60LAsINGC0L4g0YLQtdC/0LXRgNGMINCy0YHRji4uLiDihpI8
|
||||
YnI+DQrQndC+INC90Lgg0LIg0L7QtNC90L7QvCDQsdCw0L3QutC+0LzQsNGC0LUg0YHQvdGP0YLR
|
||||
jCDQtNC10L3QtdCzINC90LUg0L/QvtC70YPRh9C40LvQvtGB0YwuINCi0LXQu9C10YTQvtC9INCx
|
||||
0LDQvdC60LAg0L3QtSDQvtGC0LLQtdGH0LDQuywg0YHQsNC50YIg0LLQuNGB0LXQuy4g0KfQtdGA
|
||||
0LXQtyDQv9C+0LvRh9Cw0YHQsCDQstGL0Y/RgdC90LjQu9C+0YHRjCwg0YfRgtC+INCx0LDQvdC6
|
||||
0L7QstGB0LrQuNC1INGB0LjRgdGC0LXQvNGLINGA0YPRhdC90YPQu9C4LCDQsCDRgdGH0LXRgtCw
|
||||
INC00LXRgdGP0YLQutC+0LIg0LzQuNC70LvQuNC+0L3QvtCyINCy0LrQu9Cw0LTRh9C40LrQvtCy
|
||||
INC+0LHQvdGD0LvQtdC90YsuINCU0L7Qu9C70LDRgCwg0YTRg9C90YIg0Lgg0LXQstGA0L4g0L/R
|
||||
gNC10LLRgNCw0YLQuNC70LjRgdGMINCyINC/0YvQu9GMLCDQt9Cw0YLQviDRgtC1LCDQutGC0L4g
|
||||
0LfQsNC/0LDRgdCw0LvRgdGPINC30L7Qu9C+0YLQvtC8INC4INC/0L7QutGD0L/QsNC7INC60YDQ
|
||||
uNC/0YLQvtCy0LDQu9GO0YLRiywg0L/QvtGC0LjRgNCw0LvQuCDRgNGD0LrQuC4g0KHRgtGA0LDR
|
||||
hSDQuCDRgNCw0YHRgtC10YDRj9C90L3QvtGB0YLRjCDigJQg0LLQvtGCINC00LLQsCDQs9C70LDQ
|
||||
stC90YvRhSDRgdC70L7QstCwLCDQutC+0YLQvtGA0YvQtSDQsdGL0LvQuCDQsiDQt9Cw0LPQvtC7
|
||||
0L7QstC60LDRhSDQvdC+0LLQvtGB0YLQvdGL0YUg0YHQvtC+0LHRidC10L3QuNC5INCy0YHQtdGF
|
||||
INC40L3RhNC+0YDQvNCw0LPQtdC90YLRgdGC0LIuINCd0L7QstGL0Lkg0LTQuNCy0L3Ri9C5INC8
|
||||
0LjRgCDQstGB0YLRgNC10YfQsNC7INGB0LLQvtC40YUg0LjRgdC/0YPQs9Cw0L3QvdGL0YUg0L7Q
|
||||
sdC40YLQsNGC0LXQu9C10LnigKY8cD4NCjxicj4NCtCa0L7QvdC10YfQvdC+LCDQstC10YHRjNC8
|
||||
0LAg0LLQtdGA0L7Rj9GC0L3Qviwg0YfRgtC+INGB0YbQtdC90LDRgNC40LkgwqvRhNC40LvRjNC8
|
||||
0LAt0LrQsNGC0LDRgdGC0YDQvtGE0YvCuyDQsiDQttC40LfQvdC4INC90LjQutC+0LPQtNCwINC9
|
||||
0LUg0LHRg9C00LXRgiDRgNC10LDQu9C40LfQvtCy0LDQvS4g0J3QviDQstC+0YIg0YfRgtC+INC9
|
||||
0LDQtNC+INGH0LXRgtC60L4g0L/QvtC90LjQvNCw0YLRjDog0L3QvtCy0YvQuSDQvNC40YAsINC9
|
||||
0L7QstCw0Y8g0YbQuNGE0YDQvtCy0LDRjyDRgNC10LDQu9GM0L3QvtGB0YLRjCDRg9C20LUg0L/R
|
||||
gNC40LHQuNGA0LDQtdGCINC6INGA0YPQutCw0Lwg0L7QutGA0YPQttCw0Y7RidC40Lkg0LzQuNGA
|
||||
Ljxicj4NCjxwPg0K0J3QtdC00LDQstC90L4g0LLQv9C10YDQstGL0LUg0LIg0LjRgdGC0L7RgNC4
|
||||
0Lgg0L/Rj9GC0LXRgNC60LAg0YHQsNC80YvRhSDQtNC+0YDQvtCz0LjRhSDQutC+0LzQv9Cw0L3Q
|
||||
uNC5INC80LjRgNCwINGB0YLQsNC70LAg0LjRgdC60LvRjtGH0LjRgtC10LvRjNC90L4gwqvRhtC4
|
||||
0YTRgNC+0LLQvtC5wrsuINCf0L7RgdC70LXQtNC90LjQuSDQv9GA0LXQtNGB0YLQsNCy0LjRgtC1
|
||||
0LvRjCDCq9GB0YLQsNGA0L7Qs9C+INC80LjRgNCwwrsg4oCUINC90LXRhNGC0Y/QvdC+0Lkg0LPQ
|
||||
uNCz0LDQvdGCIEV4eG9uTW9iaWwg0LHRi9C7INCy0YvRgtC10YHQvdC10L0g0YEg0L/Rj9GC0L7Q
|
||||
s9C+INC80LXRgdGC0LAg0LjQvdGC0LXRgNC90LXRgi3QvNCw0LPQsNC30LjQvdC+0LwgQW1hem9u
|
||||
INC4INGB0L7RhtGB0LXRgtGM0Y4gRmFjZWJvb2suINCf0LXRgNCy0YvQtSDRgtGA0Lgg0L/QvtC3
|
||||
0LjRhtC40Lgg0L/RgNC40L3QsNC00LvQtdC20LDRgiBBcHBsZSwgQWxwaGFiZXQgKNC80LDRgtC1
|
||||
0YDQuNC90YHQutCw0Y8g0LrQvtC80L/QsNC90LjRjyBHb29nbGUpINC4IE1pY3Jvc29mdC4gwqvQ
|
||||
r9Cx0LvQvtGH0L3Ri9C5wrsg0LvQuNC00LXRgCDRgdGC0L7QuNGCINC+0LrQvtC70L4gJDU3MCDQ
|
||||
vNC70YDQtC48YnI+DQo8cD4NCtCf0YDQtdC40LzRg9GJ0LXRgdGC0LLQviBJVC3RgdC10LrRgtC+
|
||||
0YDQsCDQvdCw0YDQsNGB0YLQsNC10YIg0YEg0LrQvtC90YbQsCDQtNC10LLRj9C90L7RgdGC0YvR
|
||||
hSDQs9C+0LTQvtCyINC/0YDQvtGI0LvQvtCz0L4g0LLQtdC60LAuINCi0L7Qs9C00LAg0L/RgNC+
|
||||
0LjQt9C+0YjQtdC7INGE0LDQu9GM0YHRgtCw0YDRgiwg0LLRi9C70LjQstGI0LjQudGB0Y8g0LIg
|
||||
0LrRgNC40LfQuNGBINC00L7RgtC60L7QvNC+0LIsINGH0YPRgtGMINCx0YvQu9C+INC90LUg0L/Q
|
||||
vtCz0YDRg9C30LjQstGI0LjQuSDQsNC80LXRgNC40LrQsNC90YHQutGD0Y4g0Y3QutC+0L3QvtC8
|
||||
0LjQutGDINCyINGA0LXRhtC10YHRgdC40Y4g0LIg0L3QsNGH0LDQu9C1IDIwMDAt0YUg0LPQvtC0
|
||||
0L7Qsi4g0J3QviDQuCDRgdC10LnRh9Cw0YEg0L3QvtCy0LDRjyDRhtC40YTRgNC+0LLQsNGPINGN
|
||||
0YDQsCDQvdC40LrQsNC6INC90LUg0LLRi9Cy0LXQtNC10YIg0Y3QutC+0L3QvtC80LjQutGDINC9
|
||||
0LAg0YLRgNCw0LXQutGC0L7RgNC40Y4g0YPRgdGC0L7QudGH0LjQstC+0LPQviDRgNC+0YHRgtCw
|
||||
LjxwPg0KPHA+DQrQkdC+0LvQtdC1INGC0L7Qs9C+LCDRgdGA0LXQtNC90LjQtSDRgtC10LzQv9GL
|
||||
INGA0L7RgdGC0LAg0JLQktCfINCh0L7QtdC00LjQvdC10L3QvdGL0YUg0KjRgtCw0YLQvtCyICjQ
|
||||
uNC80LXQvdC90L4g0LIg0Y3RgtC+0Lkg0YHRgtGA0LDQvdC1INCx0LDQt9C40YDRg9C10YLRgdGP
|
||||
INCx0L7Qu9GM0YjQuNC90YHRgtCy0L4g0LrRgNGD0L/QvdC10LnRiNC40YUg0LLRi9GB0L7QutC+
|
||||
0YLQtdGF0L3QvtC70L7Qs9C40YfQvdGL0YUg0LrQvtGA0L/QvtGA0LDRhtC40LkpINCyIFhYSSDQ
|
||||
stC10LrQtSDQvdCw0YXQvtC00Y/RgtGB0Y8g0L3QsCDQvNC40L3QuNC80LDQu9GM0L3QvtC8INGD
|
||||
0YDQvtCy0L3QtSDQv9C+INGB0YDQsNCy0L3QtdC90LjRjiDRgdC+INCy0YLQvtGA0L7QuSDQv9C+
|
||||
0LvQvtCy0LjQvdC+0LkgWFgg0LLQtdC60LAuPHA+DQo8cD4NCtCSIDIwMDHigJMyMDE1INCz0L7Q
|
||||
tNCw0YUg0LDQvNC10YDQuNC60LDQvdGB0LrQsNGPINGN0LrQvtC90L7QvNC40LrQsCDRgNC+0YHQ
|
||||
u9CwINC90LAgMSw4JSDQsiDRgdGA0LXQtNC90LXQvCDQt9CwINCz0L7QtC4g0KDQvtGB0YIg0LIg
|
||||
0L/RgNC10LTRi9C00YPRidC40LUg0LTQstC1INC/0Y/RgtC90LDQtNGG0LDRgtC40LvQtdGC0LrQ
|
||||
uCDRgdC+0YHRgtCw0LLQu9GP0LsgMyw0INC4IDMsMyUsINCwINC00L4g0Y3RgtC+0LPQviDQsdGL
|
||||
0Lsg0LXRidC1INCy0YvRiNC1ICjQt9C00LXRgdGMINC4INC00LDQu9C10LUg4oCUINC00LDQvdC9
|
||||
0YvQtSDQktGB0LXQvNC40YDQvdC+0LPQviDQsdCw0L3QutCwKS48YnI+DQo8cD4NCtCYINGN0YLQ
|
||||
viDQvdC1INCy0YHQtSDQsNC90YLQuNGA0LXQutC+0YDQtNGLLiDQotC10LzQv9GLINGA0L7RgdGC
|
||||
0LAg0LfQsCDQv9C+0YHQu9C10LTQvdC40LUg0L/Rj9GC0L3QsNC00YbQsNGC0Ywg0LvQtdGCINC9
|
||||
0Lgg0YDQsNC30YMg0L3QtSDQv9GA0LXQstGL0YjQsNC70LggNCUgKNC80LDQutGB0LjQvNGD0Lwg
|
||||
4oCUIDMsOCUg4oCUINCx0YvQuyDQv9C+0LrQsNC30LDQvSDQsiAyMDA0INCz0L7QtNGDKSwg0YLQ
|
||||
vtCz0LTQsCDQutCw0Log0YDQsNC90LXQtSDRjdGC0LggNCUg0LHRi9C70Lgg0L7QsdGL0YfQvdGL
|
||||
0Lwg0LTQtdC70L7QvCAo0L/QuNC6INCyIDcsMjYlINCx0YvQuyDQv9C+0LrQsNC30LDQvSDQsiAx
|
||||
OTg0INCz0L7QtNGDKS48cD4NCjxwPg0KPHA+DQrQk9C70YPQsdC40L3QsCDQv9Cw0LTQtdC90LjR
|
||||
jyDRjdC60L7QvdC+0LzQuNC60Lgg0LIgMjAwOSDQs9C+0LTRgyAo4oCTMiw3OCUpINGB0YLQsNC7
|
||||
0LAg0LzQsNC60YHQuNC80LDQu9GM0L3QvtC5INGBIDE5NjEg0LPQvtC00LAuINCf0YDQuCDRjdGC
|
||||
0L7QvCDQvNCw0YHRiNGC0LDQsdGLIMKr0L7RgtGB0LrQvtC60LDCuyDQv9C+0YHQu9C1INGB0L/Q
|
||||
sNC00LAgKCsyLDUzJSDQsiAyMDEwINCz0L7QtNGDKSDQsdGL0LvQuCDQvNC40L3QuNC80LDQu9GM
|
||||
0L3Ri9C80Lgg0LfQsCDQstC10YHRjCDQvdCw0LHQu9GO0LTQsNC10LzRi9C5INC/0LXRgNC40L7Q
|
||||
tC4g0JIg0L7QsdGJ0LXQvCwg0L3QtSDQt9GA0Y8g0L/QvtGB0LvQtdC00L3QuNC5INC60YDQuNC3
|
||||
0LjRgSDQv9C+0LvRg9GH0LjQuyDQsiDQqNGC0LDRgtCw0YUg0L3QsNC30LLQsNC90LjQtSDCq9CS
|
||||
0LXQu9C40LrQsNGPINGA0LXRhtC10YHRgdC40Y/CuyAoR3JlYXQgUmVjZXNzaW9uKS48cD4NCjxi
|
||||
cj4NCiDQm9Cw0LfQtdGA0Ysg0Lgg0LTRgNC+0L3RiyDQvtGCIEZhY2Vib29rINC4INC00YDRg9Cz
|
||||
0LjQtSDRgdC/0L7RgdC+0LHRiyDQvtCx0LXRgdC/0LXRh9C40YLRjCDQsdC10LTQvdGL0LUg0YHR
|
||||
gtGA0LDQvdGLINC40L3RgtC10YDQvdC10YLQvtC8PGJyPg0K0JvQsNC30LXRgNGLINC90LAg0LHQ
|
||||
tdC00L3QvtGB0YLRjDxicj4NCtCW0LXQu9Cw0L3QuNC1INCc0LDRgNC60LAg0KbRg9C60LXRgNCx
|
||||
0LXRgNCz0LAg0L/QvtC60YDRi9GC0Ywg0LjQvdGC0LXRgNC90LXRgtC+0Lwg0LLQtdGB0Ywg0LzQ
|
||||
uNGAINC90LDQsdC40YDQsNC10YIg0L3QvtCy0YvQtSDQvtCx0L7RgNC+0YLRiyDigJQg0Log0LTR
|
||||
gNC+0L3QsNC8LCDQutC+0YLQvtGA0YvQtSDQsdGD0LTRg9GCINC+0LHQtdGB0L/QtdGH0LjQstCw
|
||||
0YLRjCDRgdC10YLRjCDQsiDRgtGA0YPQtNC90L7QtNC+0YHRgtGD0L/QvdGL0YUuLi4g4oaSPHA+
|
||||
DQrQn9GA0LXQt9C40LTQtdC90YLRgdGC0LLQviDQkdCw0YDQsNC60LAg0J7QsdCw0LzRiyDQvNC+
|
||||
0LbQvdC+INCy0L7QvtCx0YnQtSDRgdGH0LjRgtCw0YLRjCDRgdCw0LzRi9C8INC90LXRg9C00LDR
|
||||
h9C90YvQvCDRgSDRgtC+0YfQutC4INC30YDQtdC90LjRjyDRjdC60L7QvdC+0LzQuNGH0LXRgdC6
|
||||
0LjRhSDRg9GB0L/QtdGF0L7Qsi4g0JfQsCDQv9GA0LXQtNGL0LTRg9GJ0LjQtSDRgdC10LzRjCDQ
|
||||
u9C10YIg0JLQktCfINCh0KjQkCDRg9Cy0LXQu9C40YfQuNCy0LDQu9GB0Y8g0YHRgNC10LTQvdC1
|
||||
0LPQvtC00L7QstGL0LzQuCDRgtC10LzQv9Cw0LzQuCAxLDQlLiDQndC4INC/0YDQuCDQvtC00L3Q
|
||||
vtC8INC/0YDQtdC30LjQtNC10L3RgtC1LCDQvdCw0YfQuNC90LDRjyDRgSDQlNC20L7QvdCwINCa
|
||||
0LXQvdC90LXQtNC4LCDRgtCw0Log0LzQtdC00LvQtdC90L3QviDRjdC60L7QvdC+0LzQuNC60LAg
|
||||
0L3QtSDRgNC+0YHQu9CwLjxwPg0KPGJyPg0K0KLQtdC60YPRidC40Lkg0LPQvtC0INC90LUg0L/R
|
||||
gNC40L3QtdGB0LXRgiDQvdC40YfQtdCz0L4g0YXQvtGA0L7RiNC10LPQvi4g0JIg0L/QtdGA0LLQ
|
||||
vtC8INC60LLQsNGA0YLQsNC70LUg0JLQktCfINCy0YvRgNC+0YEg0L3QsCAxLDElINCyINCz0L7Q
|
||||
tNC+0LLQvtC8INC40YHRh9C40YHQu9C10L3QuNC4LCDQstC+INCy0YLQvtGA0L7QvCwg0L/QviDQ
|
||||
v9C10YDQstC+0Lkg0L7RhtC10L3QutC1LCDQvdCwIDEsMiUuINCf0YDQvtCz0L3QvtC3INC90LAg
|
||||
0LLQtdGB0Ywg0LPQvtC0IOKAlCAy4oCTMiwyJSwg0YfRgtC+INCx0YPQtNC10YIg0LzQtdC90YzR
|
||||
iNC1INC/0YDQvtGI0LvQvtCz0L7QtNC90LjRhSAyLDQlLjxwPg0KPHA+DQrQp9C70LXQvSDRgdC+
|
||||
0LLQtdGC0LAg0YPQv9GA0LDQstC70Y/RjtGJ0LjRhSDQpNC10LTQtdGA0LDQu9GM0L3QvtC5INGA
|
||||
0LXQt9C10YDQstC90L7QuSDRgdC40YHRgtC10LzRiyDQodCo0JAg0JTQttC10YDQvtC8INCf0LDR
|
||||
g9GN0LvQuyDQsiDQuNC90YLQtdGA0LLRjNGOIEZpbmFuY2lhbCBUaW1lcyDQt9Cw0Y/QstC40Lss
|
||||
INGH0YLQviDQtdGB0YLRjCDRgNC40YHQuiDQstGC0Y/Qs9C40LLQsNC90LjRjyDQsiDQtNC70LjR
|
||||
gtC10LvRjNC90YvQuSDQv9C10YDQuNC+0LQg0YHQu9Cw0LHQvtCz0L4g0YDQvtGB0YLQsC48YnI+
|
||||
DQo8cD4NCtCd0LDQtNC+INGC0LDQutC20LUg0YPRh9C40YLRi9Cy0LDRgtGMLCDRh9GC0L4g0Y3R
|
||||
gtC+0YIg0LzQuNC90LjQvNCw0LvRjNC90YvQuSDRgNC+0YHRgiDQv9C+0YHQu9C10LTQvdC40YUg
|
||||
0LvQtdGCINGB0L7Qv9GA0L7QstC+0LbQtNCw0LXRgtGB0Y8g0YPQstC10LvQuNGH0LXQvdC40LXQ
|
||||
vCDQtNC+0LvQs9CwLiDQk9C+0YHRg9C00LDRgNGB0YLQstC10L3QvdGL0Lkg0LTQvtC70LMg0LLR
|
||||
i9GA0L7RgSDQt9CwINCy0L7RgdC10LzRjCDQu9C10YIg0L/QvtGH0YLQuCDQsiDQtNCy0LAg0YDQ
|
||||
sNC30LAg0Lgg0YHQtdC50YfQsNGBINC/0YDQtdCy0YvRiNCw0LXRgiAxMDAlINCS0JLQnyDQodCo
|
||||
0JAg0Lgg0YHQvtGB0YLQsNCy0LvRj9C10YIgJDE5LDQg0YLRgNC70L0uINCSINGN0YLQvtC8INCz
|
||||
0L7QtNGDLCDQv9C+INC+0YbQtdC90LrQtSDQsNC80LXRgNC40LrQsNC90YHQutC+0LPQviDQvNC4
|
||||
0L3RhNC40L3QsCwg0L7QvSDQstGL0YDQsNGB0YLQtdGCINC10YnQtSDQvdCwICQzODMg0LzQu9GA
|
||||
0LQuPGJyPg0KPGJyPg0K0J7QtNC90L7QstGA0LXQvNC10L3QvdC+INCk0KDQoSDRg9C00LXRgNC2
|
||||
0LjQstCw0LXRgiDQutC70Y7Rh9C10LLRg9GOINGB0YLQsNCy0LrRgyDQvdCwINC80LjQvdC40LzQ
|
||||
sNC70YzQvdC+0Lwg0YPRgNC+0LLQvdC1INC90LjQttC1IDElINGBINC00LXQutCw0LHRgNGPIDIw
|
||||
MDgg0LPQvtC00LAuINCX0LAg0Y3RgtC+INCy0YDQtdC80Y8g0LHRi9C70L4g0YDQtdCw0LvQuNC3
|
||||
0L7QstCw0L3QviDRgtGA0Lgg0YDQsNGD0L3QtNCwINC/0YDQvtCz0YDQsNC80LzRiyDQutC+0LvQ
|
||||
uNGH0LXRgdGC0LLQtdC90L3QvtCz0L4g0YHQvNGP0LPRh9C10L3QuNGPIChRRSksINC60L7RgtC+
|
||||
0YDQsNGPINC30LDQstC10YDRiNC40LvQsNGB0Ywg0LIg0L7QutGC0Y/QsdGA0LUgMjAxNCDQs9C+
|
||||
0LTQsCAo0LIg0YLRgNC10YLRjNC10Lwg0YDQsNGD0L3QtNC1INCyINC80LXRgdGP0YYg0KTQoNCh
|
||||
INC/0L7QutGD0L/QsNC7INGDINCx0LDQvdC60L7QsiDQs9C+0YHRg9C00LDRgNGB0YLQstC10L3Q
|
||||
vdGL0YUg0Lgg0LjQv9C+0YLQtdGH0L3Ri9GFINC+0LHQu9C40LPQsNGG0LjQuSDQvdCwICQ4NSDQ
|
||||
vNC70YDQtCkuPGJyPg0KPHA+DQrQndC+LCDQv9C+0LTRh9C10YDQutC90LXQvCDQtdGJ0LUg0YDQ
|
||||
sNC3LCDQvdC10YHQvNC+0YLRgNGPINC90LAg0LLRgdC1INGN0YLQuCDRjdC60YHRgtGA0LDQvtGA
|
||||
0LTQuNC90LDRgNC90YvQtSDQvNC10YDRiywg0YLQtdC80L/RiyDRgNC+0YHRgtCwINC/0YDQtdCx
|
||||
0YvQstCw0Y7RgiDQvdCwINC80L3QvtCz0L7Qu9C10YLQvdC40YUg0LzQuNC90LjQvNGD0LzQsNGF
|
||||
LCDQsCDQstGB0Y8g0Y3RgtCwINC40YHRgtC+0YDQuNGPINCy0LXQu9C40LrQvtCz0L4g0YLQvtGA
|
||||
0LzQvtC20LXQvdC40Y8g0L/RgNC+0LjRgdGF0L7QtNC40YIg0L3QsCDRhNC+0L3QtSDRgNC10LfQ
|
||||
utC+0LPQviDRg9GB0LjQu9C10L3QuNGPINGA0L7Qu9C4IElULdGB0LXQutGC0L7RgNCwLCDQsCDR
|
||||
gtCw0LrQttC1INGA0L7RgdGC0LAg0YTQuNC90LDQvdGB0L7QstGL0YUg0YDRi9C90LrQvtCyLjxw
|
||||
Pg0KPHA+DQrQmtC70Y7Rh9C10LLQvtC5INCx0LjRgNC20LXQstC+0Lkg0LjQvdC00LXQutGBIFMm
|
||||
UDUwMCDQvdCw0YXQvtC00LjRgtGB0Y8g0L3QsCDQuNGB0YLQvtGA0LjRh9C10YHQutC40YUg0LzQ
|
||||
sNC60YHQuNC80YPQvNCw0YUuINCQ0L3QsNC70LjRgtC40LrQuCBHb2xkbWFuIFNhY2hzINC/0YDQ
|
||||
tdC00YPQv9GA0LXQttC00LDRjtGCOiDQutC+0L3QtdGGINGA0LDQu9C70Lgg0LHQu9C40LfQvtC6
|
||||
LiDQn9GA0LXQtNGL0LTRg9GJ0LjQtSDQv9C10YDQuNC+0LTRiyDRgNC+0YHRgtCwICjQsiAxOTg0
|
||||
4oCTMTk4NyDQuCAxOTk04oCTMTk5OSDQs9C+0LTQsNGFKSDQt9Cw0LrQsNC90YfQuNCy0LDQu9C4
|
||||
0YHRjCDQvtCx0LLQsNC70L7QvCDQutC+0YLQuNGA0L7QstC+0LouPHA+DQo8cD4NCtCi0L4sINGH
|
||||
0YLQviDQv9GA0L7QuNGB0YXQvtC00LjRgiDQsiDQodCo0JAsINCyINGC0L7QuSDQuNC70Lgg0LjQ
|
||||
vdC+0Lkg0YHRgtC10L/QtdC90Lgg0YXQsNGA0LDQutGC0LXRgNC90L4g0Lgg0LTQu9GPINC00YDR
|
||||
g9Cz0LjRhSDRgNCw0LfQstC40YLRi9GFINGB0YLRgNCw0L06INCy0LDQuyDQtNC+0LvQs9C+0LIs
|
||||
INC90LjQt9C60LjQtSDRgtC10LzQv9GLINGA0L7RgdGC0LAsINC+0YLRgdGD0YLRgdGC0LLQuNC1
|
||||
INC40L3RhNC70Y/RhtC40LgsINC/0L7RgdGC0LXQv9C10L3QvdC+0LUg0YHQvtC60YDQsNGJ0LXQ
|
||||
vdC40LUg0YHRgNC10LTQvdC10LPQviDQutC70LDRgdGB0LAg0Lgg0LrQvtC90YbQtdC90YLRgNCw
|
||||
0YbQuNGPINCx0L7Qs9Cw0YLRgdGC0LLQsCDQsiDRgNGD0LrQsNGFINC+0LPRgNCw0L3QuNGH0LXQ
|
||||
vdC90L7Qs9C+INC60YDRg9Cz0LAg0LvRjtC00LXQuS4g0J/RgNC4INGN0YLQvtC8INC40LfQvNC1
|
||||
0L3QtdC90LjQtSDRgdC40YLRg9Cw0YbQuNC4INGBINC/0L7QvNC+0YnRjNGOINC80LXRgCDRjdC6
|
||||
0L7QvdC+0LzQuNGH0LXRgdC60L7QuSDQuCDQtNC10L3QtdC20L3Qvi3QutGA0LXQtNC40YLQvdC+
|
||||
0Lkg0L/QvtC70LjRgtC40LrQuCDQvdC10LLQvtC30LzQvtC20L3Qvi4g0K3RgtC+INC90LUg0YHR
|
||||
h9C40YLQsNGPIMKr0YfQtdGA0L3Ri9GFINC70LXQsdC10LTQtdC5wrsg0LLRgNC+0LTQtSBCcmV4
|
||||
aXQsINC60L7RgtC+0YDRi9C1INGB0LXRjtGCINGB0LzRj9GC0LXQvdC40LUg0Lgg0YXQsNC+0YEg
|
||||
0L3QsCDRgNGL0L3QutCw0YUuPHA+DQo8YnI+DQrQlNC+0LvQs9C+0LUg0LLRgNC10LzRjyDQvNC4
|
||||
0YAg0LLRi9C/0LvRi9Cy0LDQuyDQt9CwINGB0YfQtdGCINCx0YPRgNC90L7Qs9C+INGA0L7RgdGC
|
||||
0LAg0LIg0YHRgtGA0LDQvdCw0YUg0YLRgNC10YLRjNC10LPQviDQvNC40YDQsCwg0LrQvtGC0L7R
|
||||
gNGL0LUg0Y3QutGB0L/QvtGA0YLQuNGA0L7QstCw0LvQuCDRgdGL0YDRjNC1INC/0L4g0LLRi9GB
|
||||
0L7QutC40Lwg0YbQtdC90LDQvCwg0L/QvtC60YPQv9Cw0LvQuCDRgtC+0LLQsNGA0Ysg0Lgg0YLQ
|
||||
tdGF0L3QvtC70L7Qs9C40Lgg0Lgg0YDQsNC30YDQtdGI0LDQu9C4INC+0YLQutGA0YvQstCw0YLR
|
||||
jCDRgyDRgdC10LHRjyDQv9GA0L7QuNC30LLQvtC00YHRgtCy0LAg0YLRgNCw0L3RgdC90LDRhtC4
|
||||
0L7QvdCw0LvRjNC90YvQvCDQutC+0YDQv9C+0YDQsNGG0LjRj9C8LCDRgdC90LDQsdC20LDRjyDQ
|
||||
uNGFINC00LXRiNC10LLQvtC5INGA0LDQsdC+0YfQtdC5INGB0LjQu9C+0LkuINCd0L4g0YHQtdCz
|
||||
0L7QtNC90Y8g0L7QvdC4INGC0LDQutC20LUg0LIg0LrRgNC40LfQuNGB0LUuPGJyPg0KPGJyPg0K
|
||||
INCV0LvQuNC30LDQstC10YLQsCDQkNC70LXQutGB0LDQvdC00YDQvtCy0LAt0JfQvtGA0LjQvdCw
|
||||
INC+INGC0L7QvCwg0LPQvtGC0L7QstC+INC70Lgg0YfQtdC70L7QstC10YfQtdGB0YLQstC+INC6
|
||||
INCz0LXQvdC10YLQuNGH0LXRgdC60L7QuSDQuCDRgtC10YXQvdC+0LvQvtCz0LjRh9C10YHQutC+
|
||||
0LkgwqvRgNC10LTQsNC60YLRg9GA0LXCuzxicj4NCtCn0LXQu9C+0LLQtdC6INC90L7QstGL0Lks
|
||||
INGD0LvRg9GH0YjQtdC90L3Ri9C5PHA+DQrQniDQvdC+0LLQvtC8INGH0LXQu9C+0LLQtdC60LUs
|
||||
IGwnaG9tbWUgbm91dmVhdSwg0LzQtdGH0YLQsNC70Lgg0YHQtdCy0LXRgNC+0LDQvNC10YDQuNC6
|
||||
0LDQvdGB0LrQuNC1INC/0L7RgdC10LvQtdC90YbRiywg0L3QsNGG0LjRgdGC0Ysg0Lgg0LrQvtC8
|
||||
0LzRg9C90LjRgdGC0YssINCi0YDQvtGG0LrQuNC5LCDQp9C1INCT0LXQstCw0YDQsCDQuCDQvNC9
|
||||
0L7Qs9C40LUg0LTRgNGD0LPQuNC1LiDQoNC10YfRjCDRiNC70LAg0L7QsS4uLiDihpI8YnI+DQrQ
|
||||
otCw0Log0L/QvtGH0LXQvNGDINC20LUgwqvRhtC40YTRgNCwwrsg0YLQsNC6INC4INC90LUg0YHR
|
||||
gtCw0LvQsCDRgtC10Lwg0YHQsNC80YvQvCDQtNGA0LDQudCy0LXRgNC+0LwsINC60L7RgtC+0YDR
|
||||
i9C5INCy0LXRgNC90YPQuyDQsdGLINGN0LrQvtC90L7QvNC40LrRgyDQvdCwINGC0YDQsNC10LrR
|
||||
gtC+0YDQuNGOINCy0YvRgdC+0LrQvtCz0L4g0Lgg0YPRgdGC0L7QudGH0LjQstC+0LPQviDRgNC+
|
||||
0YHRgtCwPyDQrdGC0L7QvNGDINC80LXRiNCw0Y7RgiDRgdGC0LDRgNGL0LUg0LjQvdGB0YLQuNGC
|
||||
0YPRgtGLINC4INC40L3RhNGA0LDRgdGC0YDRg9C60YLRg9GA0LAsINGF0L7RgNC+0YjQviDQv9GA
|
||||
0LjRgdC/0L7RgdC+0LHQu9C10L3QvdGL0LUg0LTQu9GPINGC0YDQsNC00LjRhtC40L7QvdC90L7Q
|
||||
uSDRjdC60L7QvdC+0LzQuNC60Lgg0YEg0LXQtSDQs9C70LDQstC10L3RgdGC0LLRg9GO0YnQtdC5
|
||||
INGA0L7Qu9GM0Y4gwqvRgNC10LDQu9GM0L3QvtCz0L4g0YHQtdC60YLQvtGA0LDCuyDQuCDQsdCw
|
||||
0L3QutC+0LLRgdC60L7Qs9C+INC60YDQtdC00LjRgtCwINC60LDQuiDQtNGA0LDQudCy0LXRgNCw
|
||||
INC40L3QstC10YHRgtC40YbQuNC5INC4INC/0L7RgtGA0LXQsdC70LXQvdC40Y8uPHA+DQo8cD4N
|
||||
CtCd0LAg0YHQvNC10L3RgyDRgdGC0LDRgNGL0Lwg0LLQsNC70Y7RgtCw0LwsINCx0LDQvdC60LDQ
|
||||
vCDQuCDQsdC40YDQttCw0LwsINGD0LPQu9C10LLQvtC00L7RgNC+0LTQvdC+0Lkg0Y3QvdC10YDQ
|
||||
s9C10YLQuNC60LUg0Lgg0YLRgNCw0LTQuNGG0LjQvtC90L3Ri9C8INGE0LDQsdGA0LjQutCw0Lwg
|
||||
0YPQttC1INC40LTRg9GCINC90L7QstGL0LUg0YLQtdGF0L3QvtC70L7Qs9C40LguINCd0LDQv9GA
|
||||
0LjQvNC10YAsINCx0LvQvtC60YfQtdC50L0g0Lgg0LHQuNGC0LrQvtC40L3Riywg0LrQvtGC0L7R
|
||||
gNGL0LUg0L/QvtC30LLQvtC70Y/RjtGCINCy0L7QvtCx0YnQtSDQstGL0LLQtdGB0YLQuCDQs9C+
|
||||
0YHRg9C00LDRgNGB0YLQstC+ICjQutCw0Log0Y3QvNC40YLQtdC90YLQsCDQstCw0LvRjtGC0Ysp
|
||||
INC4INCx0LDQvdC60LggKNC60LDQuiDQuNGB0YLQvtGH0L3QuNC6INC60YDQtdC00LjRgtCwINC4
|
||||
INGE0LjQvdCw0L3RgdC+0LLRi9C1INC/0L7RgdGA0LXQtNC90LjQutC4KSDQuNC3INC40LPRgNGL
|
||||
LjxwPg0KPGJyPg0K0J/RgNC10LfQuNC00LXQvdGCINCh0LHQtdGA0LHQsNC90LrQsCDQk9C10YDQ
|
||||
vNCw0L0g0JPRgNC10YQg0L3QtdC+0LTQvdC+0LrRgNCw0YLQvdC+INC/0L7QtNGH0LXRgNC60LjQ
|
||||
stCw0LssINGH0YLQviDRg9C20LUg0LIg0LHQu9C40LbQsNC50YjQtdC1INCy0YDQtdC80Y8g0YLR
|
||||
gNCw0LTQuNGG0LjQvtC90L3Ri9C8INGE0LjQvdCw0L3RgdC+0LLRi9C8INC+0YDQs9Cw0L3QuNC3
|
||||
0LDRhtC40Y/QvCDQv9GA0LjQtNC10YLRgdGPINC60L7QvdC60YPRgNC40YDQvtCy0LDRgtGMINGB
|
||||
INC40L3RgtC10YDQvdC10YIt0LjQvdC00YPRgdGC0YDQuNC10LkuPHA+DQo8cD4NCtCSINC40L3R
|
||||
gtC10YDQstGM0Y4g0LbRg9GA0L3QsNC70YMgSGFydmFyZCBCdXNpbmVzcyBSZXZpZXcg0L7QvSDQ
|
||||
vtGC0LzQtdGH0LDQuywg0YfRgtC+INCx0LDQvdC6IMKr0LDQsdGB0L7Qu9GO0YLQvdC+INC90LXQ
|
||||
utC+0L3QutGD0YDQtdC90YLQvtGB0L/QvtGB0L7QsdC10L3CuyDQsiDQv9C10YDRgdC/0LXQutGC
|
||||
0LjQstC1INC00LXRgdGP0YLQuCDQu9C10YIsINC/0L7RgdC60L7Qu9GM0LrRgyDQv9GA0L7QuNCz
|
||||
0YDRi9Cy0LDQtdGCINGC0LXRhdC90L7Qu9C+0LPQuNGH0LXRgdC60LjQvCDQutC+0LzQv9Cw0L3Q
|
||||
uNGP0LwgKEdvb2dsZSwgQWxpYmFiYSDQuCBBbWF6b24g0Lgg0L/RgC4pLCDQstGL0YXQvtC00Y/R
|
||||
idC40Lwg0L3QsCDRgNGL0L3QvtC6INC/0YDQtdC00L7RgdGC0LDQstC70LXQvdC40Y8g0YTQuNC9
|
||||
0LDQvdGB0L7QstGL0YUg0YPRgdC70YPQsy4gwqvQntC90Lgg0L3QsNC80L3QvtCz0L4g0YHQuNC7
|
||||
0YzQvdC10LUg0L/QvtGH0YLQuCDQstC+INCy0YHQtdC8wrssIOKAlCDQv9C+0LTRh9C10YDQutC4
|
||||
0LLQsNC7INCT0YDQtdGELjxicj4NCjxicj4NCsKr0JXRgdC70Lgg0YMg0L3QsNGBIHRpbWUgdG8g
|
||||
bWFya2V0ICjQstGA0LXQvNGPINCy0YvRhdC+0LTQsCDQv9GA0L7QtNGD0LrRgtCwINC90LAg0YDR
|
||||
i9C90L7QuiDRgSDQvNC+0LzQtdC90YLQsCDRgdC+0LfQtNCw0L3QuNGPLiDigJQgwqvQk9Cw0LfQ
|
||||
tdGC0LAuUnXCuykg0LzQtdGA0Y/QtdGC0YHRjyDQvNC90L7Qs9C40LzQuCDQvNC10YHRj9GG0LDQ
|
||||
vNC4LCDQsCDRgyDQvdC40YUg0YfQsNGB0LDQvNC4LCDRgtC+INC60LDQuiDQvdCw0Lwg0LrQvtC9
|
||||
0LrRg9GA0LjRgNC+0LLQsNGC0Yw/INCd0LjQutCw0LouINCe0L3QuCDQstGB0LXQs9C00LAg0L3Q
|
||||
sNGBINCx0YPQtNGD0YIg0L7Qv9C10YDQtdC20LDRgtGMLiDQp9GC0L4g0L3QsNC8INC90YPQttC9
|
||||
0L4g0YHQtNC10LvQsNGC0Yw/INCi0LDQutC+0Lkg0LbQtSB0aW1lIHRvIG1hcmtldMK7LCDigJQg
|
||||
0LfQsNGP0LLQuNC7INC/0YDQtdC30LjQtNC10L3RgiDQodCx0LXRgNCx0LDQvdC60LAuPHA+DQo8
|
||||
cD4NCtCR0LXQt9GD0YHQu9C+0LLQvdC+LCDQuCDQsdC70L7QutGH0LXQudC9LCDQuCDQsdC40YLQ
|
||||
utC+0LjQvdGLINC10YnQtSDQvdGD0LbQtNCw0Y7RgtGB0Y8g0LIg0LTQvtGA0LDQsdC+0YLQutC1
|
||||
LiDQndC10LTQsNCy0L3Rj9GPINGF0LDQutC10YDRgdC60LDRjyDQsNGC0LDQutCwINC90LAg0L7Q
|
||||
tNC90YMg0LjQtyDQutGA0YPQv9C90LXQudGI0LjRhSDQsdC40YLQutC+0LjQvS3QsdC40YDQtiDQ
|
||||
siDQk9C+0L3QutC+0L3Qs9C1INC/0YDQuNCy0LXQu9CwINC6INC/0LDQtNC10L3QuNGOINC60YPR
|
||||
gNGB0LAg0LrRgNC40L/RgtC+0LLQsNC70Y7RgtGLINC90LAgMjAlLiDQpdCw0LrQtdGA0LDQvCDR
|
||||
g9C00LDQu9C+0YHRjCDQv9C+0YXQuNGC0LjRgtGMIDEyMCDRgtGL0YEuINCx0LjRgtC60L7QuNC9
|
||||
0L7Qsiwg0LrQvtGC0L7RgNGL0LUg0L3QsCDRgtC+0YIg0LzQvtC80LXQvdGCINCx0YvQu9C4INGN
|
||||
0LrQstC40LLQsNC70LXQvdGC0L3RiyDQv9GA0LjQvNC10YDQvdC+ICQ3MCDQvNC70L0uPGJyPg0K
|
||||
PGJyPg0K0JLQv9GA0L7Rh9C10LwsINC/0L7QsdC10LTQvdGD0Y4g0L/QvtGB0YLRg9C/0Ywg0LHQ
|
||||
u9C+0LrRh9C10LnQvdCwINCy0YDRj9C0INC70Lgg0YPQtNCw0YHRgtGB0Y8g0L7RgdGC0LDQvdC+
|
||||
0LLQuNGC0YwuINCQINCy0LXQtNGMINC10YHRgtGMINC10YnQtSDRgtC10YXQvdC+0LvQvtCz0LjQ
|
||||
uCDQuNC3INGA0LXQsNC70YzQvdC+0LPQviDRgdC10LrRgtC+0YDQsCDigJQg0LDQu9GM0YLQtdGA
|
||||
0L3QsNGC0LjQstC90LDRjyDRjdC90LXRgNCz0LXRgtC40LrQsCDQuCDRjdC70LXQutGC0YDQvtC8
|
||||
0L7QsdC40LvQuCwg0LAg0YLQsNC60LbQtSAzRC3Qv9C10YfQsNGC0YwsINGA0L7QsdC+0YLQuNC3
|
||||
0LDRhtC40Y8g0L/RgNC+0LjQt9Cy0L7QtNGB0YLQstC10L3QvdGL0YUg0L/RgNC+0YbQtdGB0YHQ
|
||||
vtCyLCDRgdC40L3RgtC10Lcg0L/QuNGJ0LXQstGL0YUg0L/RgNC+0LTRg9C60YLQvtCyINC4INC8
|
||||
0L3QvtCz0L7QtSDQtNGA0YPQs9C+0LUuPHA+DQo8YnI+DQrQktC/0L7Qu9C90LUg0LLQtdGA0L7R
|
||||
j9GC0L3Qviwg0YfRgtC+INC90LAg0LPQvtGA0LjQt9C+0L3RgtC1INCx0LvQuNC20LDQudGI0LjR
|
||||
hSDQtNCy0YPRhS3RgtGA0LXRhSDQtNC10YHRj9GC0LjQu9C10YLQuNC5INCx0LDQt9C+0LLRi9C1
|
||||
INC/0L7RgtGA0LXQsdC90L7RgdGC0Lgg0YfQtdC70L7QstC10LrQsCDQsiDQv9C40YnQtSwg0L7Q
|
||||
tNC10LbQtNC1INC4INC/0LXRgNC10LTQstC40LbQtdC90LjQuCDQsdGD0LTRg9GCINGD0LTQvtCy
|
||||
0LvQtdGC0LLQvtGA0Y/RgtGM0YHRjyDQv9C+0YfRgtC4INCx0LXRgdC/0LvQsNGC0L3Qvi4g0J/Q
|
||||
u9Cw0YLQuNGC0Ywg0L/RgNC40LTQtdGC0YHRjyDQsiDQvtGB0L3QvtCy0L3QvtC8INC30LAgwqvQ
|
||||
utC+0L3RgtC10L3RgsK7Ljxicj4NCjxicj4NCtCR0LXQtNC90L7RgdGC0Ywg0LIg0YHRgtGA0LDQ
|
||||
vdCw0YUgwqvQt9C+0LvQvtGC0L7Qs9C+INC80LjQu9C70LjQsNGA0LTQsMK7INC+0LrQvtC90YfQ
|
||||
sNGC0LXQu9GM0L3QviDRg9C50LTQtdGCINCyINC/0YDQvtGI0LvQvtC1LCDQs9GA0LDQttC00LDQ
|
||||
vdC1INCx0YPQtNGD0YIg0L/QvtC70YPRh9Cw0YLRjCDQs9Cw0YDQsNC90YLQuNGA0L7QstCw0L3Q
|
||||
vdGL0Lkg0LTQvtGF0L7QtCDQv9GA0Y/QvNC+INGBINGA0L7QttC00LXQvdC40Y8gKNC/0YDQuNCy
|
||||
0YvRh9C90YvRhSDQsdGD0LzQsNC20L3Ri9GFINC00LXQvdC10LMg0L3QtSDQsdGD0LTQtdGCLCDQ
|
||||
uNGFINGBINCx0L7Qu9GM0YjQvtC5INC00L7Qu9C10Lkg0LLQtdGA0L7Rj9GC0L3QvtGB0YLQuCDQ
|
||||
vtGC0LzQtdC90Y/RgiDRg9C20LUg0LTQvtCy0L7Qu9GM0L3QviDRgdC60L7RgNC+KS4g0JIg0YLQ
|
||||
viDQttC1INCy0YDQtdC80Y8g0LLRi9Cx0LjRgtGM0YHRjyDQsiDRgdGA0LXQtNC90LjQuSDQutC7
|
||||
0LDRgdGBINC40LvQuCDRgNCw0LfQsdC+0LPQsNGC0LXRgtGMINGB0YLQsNC90LXRgiDQs9C+0YDQ
|
||||
sNC30LTQviDRgdC70L7QttC90LXQtSwg0YfQtdC8INGB0LXQudGH0LDRgS48YnI+DQo8YnI+DQrQ
|
||||
ndC+INCy0L7RgiDRh9GC0L4g0YHRgtC+0LjRgiDQv9C+0LTRh9C10YDQutC90YPRgtGMLiDQnNC4
|
||||
0YAg0LbQtNC10YIg0L3QvtCy0LDRjyDRjdC60L7QvdC+0LzQuNGH0LXRgdC60LDRjyDRgNC10LLQ
|
||||
vtC70Y7RhtC40Y8sINC60L7RgtC+0YDQsNGPINCx0YPQtNC10YIg0YHQvtC/0YDQvtCy0L7QttC0
|
||||
0LDRgtGM0YHRjyDQutGA0LjQt9C40YHQvdGL0LzQuCDQv9C10YDQuNC+0LTQsNC80LguINCi0LXQ
|
||||
vCDQsdC+0LvQtdC1INGH0YLQviwg0LrQsNC6INCx0YvQu9C+INC/0L7QutCw0LfQsNC90L4g0LLR
|
||||
i9GI0LUsINC/0YDQtdC00L/QvtGB0YvQu9C+0Log0LTQu9GPINC90L7QstC+0LPQviDQvtCx0LLQ
|
||||
sNC70LAg0LHQvtC70LXQtSDRh9C10Lwg0LTQvtGB0YLQsNGC0L7Rh9C90L4uINCS0L7Qv9GA0L7R
|
||||
gSDRgtC+0LvRjNC60L4g0LIg0YLQvtC8LCDQs9C00LUg0Lgg0LrQvtCz0LTQsCDQvdCw0YfQvdC1
|
||||
0YLRgdGPINC+0LHRgNGD0YjQtdC90LjQtS48YnI+DQo8cD4NCjxicj4NCg==
|
||||
--2NqJR3m2cLnhEraiqXA4Q9hqnmihx7b7
|
||||
Content-Type: application/octet-stream; name="ФНС_РФ558.zip"
|
||||
Content-Disposition:attachment; filename="ФНС_РФ558.zip"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
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
|
||||
--2NqJR3m2cLnhEraiqXA4Q9hqnmihx7b7
|
||||
Binary file not shown.
|
|
@ -4590,7 +4590,7 @@
|
|||
"org_id": "2",
|
||||
"orgc_id": "2",
|
||||
"proposal_email_lock": false,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"published": false,
|
||||
"sharing_group_id": "0",
|
||||
"threat_level_id": "3",
|
||||
|
|
|
|||
|
|
@ -4593,7 +4593,7 @@
|
|||
"org_id": "2",
|
||||
"orgc_id": "2",
|
||||
"proposal_email_lock": false,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"published": false,
|
||||
"sharing_group_id": "0",
|
||||
"threat_level_id": "3",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"distribution": "0",
|
||||
"proposal_email_lock": false,
|
||||
"locked": false,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"sharing_group_id": "0",
|
||||
"disable_correlation": false,
|
||||
"event_creator_email": "raphael.vinot@circl.lu",
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@
|
|||
"org_id": "1",
|
||||
"orgc_id": "1",
|
||||
"proposal_email_lock": true,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"published": false,
|
||||
"sharing_group_id": "0",
|
||||
"threat_level_id": "1",
|
||||
|
|
|
|||
|
|
@ -1,17 +1,33 @@
|
|||
from email.message import EmailMessage
|
||||
from __future__ import annotations
|
||||
|
||||
# import json
|
||||
import unittest
|
||||
|
||||
from email.message import EmailMessage
|
||||
from io import BytesIO
|
||||
from typing import List
|
||||
from pymisp.tools import EMailObject
|
||||
from pathlib import Path
|
||||
from os import urandom
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import TypeVar, Type
|
||||
from zipfile import ZipFile
|
||||
|
||||
from pymisp.tools import EMailObject
|
||||
from pymisp.exceptions import PyMISPNotImplementedYet, InvalidMISPObject
|
||||
|
||||
T = TypeVar('T', bound='TestEmailObject')
|
||||
|
||||
|
||||
class TestEmailObject(unittest.TestCase):
|
||||
def test_mail_1(self):
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_1.eml"))
|
||||
|
||||
eml_1: BytesIO
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls: type[T]) -> None:
|
||||
with ZipFile(Path("tests/email_testfiles/mail_1.eml.zip"), 'r') as myzip:
|
||||
with myzip.open('mail_1.eml', pwd=b'AVs are dumb') as myfile:
|
||||
cls.eml_1 = BytesIO(myfile.read())
|
||||
|
||||
def test_mail_1(self) -> None:
|
||||
email_object = EMailObject(pseudofile=self.eml_1)
|
||||
self.assertEqual(self._get_values(email_object, "subject")[0], "письмо уведом-е")
|
||||
self.assertEqual(self._get_values(email_object, "to")[0], "kinney@noth.com")
|
||||
self.assertEqual(self._get_values(email_object, "from")[0], "suvorov.s@nalg.ru")
|
||||
|
|
@ -27,7 +43,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertIsInstance(file_name, str)
|
||||
self.assertIsInstance(file_content, BytesIO)
|
||||
|
||||
def test_mail_1_headers_only(self):
|
||||
def test_mail_1_headers_only(self) -> None:
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_1_headers_only.eml"))
|
||||
self.assertEqual(self._get_values(email_object, "subject")[0], "письмо уведом-е")
|
||||
self.assertEqual(self._get_values(email_object, "to")[0], "kinney@noth.com")
|
||||
|
|
@ -38,7 +54,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertIsInstance(email_object.email, EmailMessage)
|
||||
self.assertEqual(len(email_object.attachments), 0)
|
||||
|
||||
def test_mail_multiple_to(self):
|
||||
def test_mail_multiple_to(self) -> None:
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_multiple_to.eml"))
|
||||
|
||||
to = self._get_values(email_object, "to")
|
||||
|
|
@ -48,9 +64,9 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertEqual(to[1], "jan.marek@example.com")
|
||||
self.assertEqual(to_display_name[1], "Marek, Jan")
|
||||
|
||||
def test_msg(self):
|
||||
def test_msg(self) -> None:
|
||||
# Test result of eml converted to msg is the same
|
||||
eml_email_object = EMailObject(Path("tests/email_testfiles/mail_1.eml"))
|
||||
eml_email_object = EMailObject(pseudofile=self.eml_1)
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_1.msg"))
|
||||
|
||||
self.assertIsInstance(email_object.email, EmailMessage)
|
||||
|
|
@ -64,20 +80,17 @@ class TestEmailObject(unittest.TestCase):
|
|||
self._get_values(eml_email_object, "to")[0])
|
||||
self.assertEqual(self._get_values(email_object, "from")[0],
|
||||
self._get_values(eml_email_object, "from")[0])
|
||||
dirty_display_name = self._get_values(email_object, "from-display-name")[0]
|
||||
dirty_display_name = dirty_display_name[:-2] + dirty_display_name[-1]
|
||||
self.assertEqual(dirty_display_name,
|
||||
self.assertEqual(self._get_values(email_object, "from-display-name")[0],
|
||||
self._get_values(eml_email_object, "from-display-name")[0])
|
||||
self.assertEqual(len(self._get_values(email_object, "email-body")), 2)
|
||||
|
||||
self.assertEqual(self._get_values(email_object, "received-header-ip"),
|
||||
self._get_values(eml_email_object, "received-header-ip"))
|
||||
|
||||
|
||||
def test_bom_encoded(self):
|
||||
def test_bom_encoded(self) -> None:
|
||||
"""Test utf-8-sig encoded email"""
|
||||
bom_email_object = EMailObject(Path("tests/email_testfiles/mail_1_bom.eml"))
|
||||
eml_email_object = EMailObject(Path("tests/email_testfiles/mail_1.eml"))
|
||||
eml_email_object = EMailObject(pseudofile=self.eml_1)
|
||||
|
||||
self.assertIsInstance(bom_email_object.email, EmailMessage)
|
||||
for file_name, file_content in bom_email_object.attachments:
|
||||
|
|
@ -97,7 +110,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertEqual(self._get_values(bom_email_object, "received-header-ip"),
|
||||
self._get_values(eml_email_object, "received-header-ip"))
|
||||
|
||||
def test_handling_of_various_email_types(self):
|
||||
def test_handling_of_various_email_types(self) -> None:
|
||||
self._does_not_fail(Path("tests/email_testfiles/mail_2.eml"),
|
||||
"ensuring all headers work")
|
||||
self._does_not_fail(Path('tests/email_testfiles/mail_3.eml'),
|
||||
|
|
@ -109,7 +122,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self._does_not_fail(Path('tests/email_testfiles/mail_5.msg'),
|
||||
"Check encapsulated HTML works")
|
||||
|
||||
def _does_not_fail(self, path, test_type="test"):
|
||||
def _does_not_fail(self, path: Path, test_type: str="test") -> None:
|
||||
found_error = None
|
||||
try:
|
||||
EMailObject(path)
|
||||
|
|
@ -121,7 +134,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
path,
|
||||
test_type))
|
||||
|
||||
def test_random_binary_blob(self):
|
||||
def test_random_binary_blob(self) -> None:
|
||||
"""Email parser fails correctly on random binary blob."""
|
||||
random_data = urandom(1024)
|
||||
random_blob = BytesIO(random_data)
|
||||
|
|
@ -136,10 +149,9 @@ class TestEmailObject(unittest.TestCase):
|
|||
broken_obj = EMailObject(pseudofile=random_blob)
|
||||
except Exception as _e:
|
||||
found_error = _e
|
||||
if not isinstance(found_error, PyMISPNotImplementedYet):
|
||||
self.fail("Expected PyMISPNotImplementedYet when EmailObject receives completely unknown binary input data in a pseudofile. But, did not get that exception.")
|
||||
|
||||
if not isinstance(found_error, InvalidMISPObject):
|
||||
self.fail("Expected InvalidMISPObject when EmailObject receives completely unknown binary input data in a pseudofile. But, did not get that exception.")
|
||||
|
||||
@staticmethod
|
||||
def _get_values(obj: EMailObject, relation: str) -> List[str]:
|
||||
def _get_values(obj: EMailObject, relation: str) -> list[str]:
|
||||
return [attr.value for attr in obj.attributes if attr['object_relation'] == relation]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
import json
|
||||
|
|
@ -8,7 +9,7 @@ import pathlib
|
|||
|
||||
|
||||
class TestFileObject(unittest.TestCase):
|
||||
def test_mimeType(self):
|
||||
def test_mimeType(self) -> None:
|
||||
file_object = FileObject(filepath=pathlib.Path(__file__))
|
||||
attributes = json.loads(file_object.to_json())['Attribute']
|
||||
mime = next(attr for attr in attributes if attr['object_relation'] == 'mimetype')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
import json
|
||||
|
|
@ -16,89 +17,89 @@ from pymisp.tools import GitVulnFinderObject
|
|||
|
||||
class TestMISPEvent(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self) -> None:
|
||||
self.maxDiff = None
|
||||
self.mispevent = MISPEvent()
|
||||
|
||||
def init_event(self):
|
||||
def init_event(self) -> None:
|
||||
self.mispevent.info = 'This is a test'
|
||||
self.mispevent.distribution = 1
|
||||
self.mispevent.threat_level_id = 1
|
||||
self.mispevent.analysis = 1
|
||||
self.mispevent.set_date("2017-12-31") # test the set date method
|
||||
|
||||
def test_simple(self):
|
||||
with open('tests/mispevent_testfiles/simple.json', 'r') as f:
|
||||
def test_simple(self) -> None:
|
||||
with open('tests/mispevent_testfiles/simple.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_event(self):
|
||||
def test_event(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.publish()
|
||||
with open('tests/mispevent_testfiles/event.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_loadfile(self):
|
||||
def test_loadfile(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/event.json')
|
||||
with open('tests/mispevent_testfiles/event.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_loadfile_validate(self):
|
||||
def test_loadfile_validate(self) -> None:
|
||||
misp_event = MISPEvent()
|
||||
misp_event.load_file('tests/mispevent_testfiles/event.json', validate=True)
|
||||
|
||||
def test_loadfile_validate_strict(self):
|
||||
def test_loadfile_validate_strict(self) -> None:
|
||||
misp_event = MISPEvent(strict_validation=True)
|
||||
misp_event.load_file('tests/mispevent_testfiles/event.json', validate=True)
|
||||
|
||||
def test_event_tag(self):
|
||||
def test_event_tag(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.add_tag('bar')
|
||||
self.mispevent.add_tag(name='baz')
|
||||
new_tag = MISPTag()
|
||||
new_tag.from_dict(name='foo')
|
||||
self.mispevent.add_tag(new_tag)
|
||||
with open('tests/mispevent_testfiles/event_tags.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_tags.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_event_galaxy(self):
|
||||
def test_event_galaxy(self) -> None:
|
||||
self.init_event()
|
||||
with open('tests/mispevent_testfiles/galaxy.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/galaxy.json') as f:
|
||||
galaxy = json.load(f)
|
||||
misp_galaxy = MISPGalaxy()
|
||||
misp_galaxy.from_dict(**galaxy)
|
||||
self.mispevent.add_galaxy(misp_galaxy)
|
||||
self.assertEqual(self.mispevent.galaxies[0].to_json(sort_keys=True, indent=2), json.dumps(galaxy, sort_keys=True, indent=2))
|
||||
|
||||
def test_attribute(self):
|
||||
def test_attribute(self) -> None:
|
||||
self.init_event()
|
||||
a = self.mispevent.add_attribute('filename', 'bar.exe')
|
||||
a: MISPAttribute = self.mispevent.add_attribute('filename', 'bar.exe') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.add_attribute_tag('osint', 'bar.exe')
|
||||
a = self.mispevent.add_attribute_tag('osint', 'bar.exe') # type: ignore[assignment]
|
||||
attr_tags = self.mispevent.get_attribute_tag('bar.exe')
|
||||
self.assertEqual(self.mispevent.attributes[0].tags[0].name, 'osint')
|
||||
self.assertEqual(attr_tags[0].name, 'osint')
|
||||
with open('tests/mispevent_testfiles/attribute.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/attribute.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
# Fake setting an attribute ID for testing
|
||||
self.mispevent.attributes[0].id = 42
|
||||
self.mispevent.delete_attribute(42)
|
||||
with open('tests/mispevent_testfiles/attribute_del.json', 'r') as f:
|
||||
self.mispevent.delete_attribute('42')
|
||||
with open('tests/mispevent_testfiles/attribute_del.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_attribute_galaxy(self):
|
||||
def test_attribute_galaxy(self) -> None:
|
||||
self.init_event()
|
||||
with open('tests/mispevent_testfiles/galaxy.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/galaxy.json') as f:
|
||||
galaxy = json.load(f)
|
||||
misp_galaxy = MISPGalaxy()
|
||||
misp_galaxy.from_dict(**galaxy)
|
||||
|
|
@ -111,7 +112,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
json.dumps(galaxy, sort_keys=True, indent=2)
|
||||
)
|
||||
|
||||
def test_to_dict_json_format(self):
|
||||
def test_to_dict_json_format(self) -> None:
|
||||
misp_event = MISPEvent()
|
||||
av_signature_object = MISPObject("av-signature")
|
||||
av_signature_object.add_attribute("signature", "EICAR")
|
||||
|
|
@ -120,47 +121,47 @@ class TestMISPEvent(unittest.TestCase):
|
|||
|
||||
self.assertEqual(json.loads(misp_event.to_json()), misp_event.to_dict(json_format=True))
|
||||
|
||||
def test_object_tag(self):
|
||||
def test_object_tag(self) -> None:
|
||||
self.mispevent.add_object(name='file', strict=True)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('filename', value='') # type: ignore[assignment]
|
||||
self.assertEqual(a, None)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value=None)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value=None) # type: ignore[assignment]
|
||||
self.assertEqual(a, None)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}])
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}]) # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.assertEqual(self.mispevent.objects[0].attributes[0].tags[0].name, 'blah')
|
||||
self.assertTrue(self.mispevent.objects[0].has_attributes_by_relation(['filename']))
|
||||
self.assertEqual(len(self.mispevent.objects[0].get_attributes_by_relation('filename')), 1)
|
||||
self.mispevent.add_object(name='url', strict=True)
|
||||
a = self.mispevent.objects[1].add_attribute('url', value='https://www.circl.lu')
|
||||
a = self.mispevent.objects[1].add_attribute('url', value='https://www.circl.lu') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
self.mispevent.objects[1].uuid = 'b'
|
||||
reference = self.mispevent.objects[0].add_reference(self.mispevent.objects[1], 'baz', comment='foo')
|
||||
del reference.uuid
|
||||
self.assertEqual(self.mispevent.objects[0].references[0].relationship_type, 'baz')
|
||||
with open('tests/mispevent_testfiles/event_obj_attr_tag.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_obj_attr_tag.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
@unittest.skip("Not supported on MISP: https://github.com/MISP/MISP/issues/2638 - https://github.com/MISP/PyMISP/issues/168")
|
||||
def test_object_level_tag(self):
|
||||
def test_object_level_tag(self) -> None:
|
||||
self.mispevent.add_object(name='file', strict=True)
|
||||
self.mispevent.objects[0].add_attribute('filename', value='bar')
|
||||
self.mispevent.objects[0].add_tag('osint')
|
||||
self.mispevent.objects[0].add_tag('osint') # type: ignore[attr-defined]
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/event_obj_tag.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_obj_tag.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_object_galaxy(self):
|
||||
def test_object_galaxy(self) -> None:
|
||||
self.init_event()
|
||||
misp_object = MISPObject('github-user')
|
||||
misp_object.add_attribute('username', 'adulau')
|
||||
misp_object.add_attribute('repository', 'cve-search')
|
||||
self.mispevent.add_object(misp_object)
|
||||
with open('tests/mispevent_testfiles/galaxy.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/galaxy.json') as f:
|
||||
galaxy = json.load(f)
|
||||
misp_galaxy = MISPGalaxy()
|
||||
misp_galaxy.from_dict(**galaxy)
|
||||
|
|
@ -170,95 +171,95 @@ class TestMISPEvent(unittest.TestCase):
|
|||
json.dumps(galaxy, sort_keys=True, indent=2)
|
||||
)
|
||||
|
||||
def test_malware(self):
|
||||
def test_malware(self) -> None:
|
||||
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
||||
pseudofile = BytesIO(f.read())
|
||||
self.init_event()
|
||||
a = self.mispevent.add_attribute('malware-sample', 'bar.exe', data=pseudofile)
|
||||
a: MISPAttribute = self.mispevent.add_attribute('malware-sample', 'bar.exe', data=pseudofile) # type: ignore[assignment]
|
||||
del a.uuid
|
||||
attribute = self.mispevent.attributes[0]
|
||||
self.assertEqual(attribute.malware_binary, pseudofile)
|
||||
with open('tests/mispevent_testfiles/malware.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/malware.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_existing_malware(self):
|
||||
def test_existing_malware(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/malware_exist.json')
|
||||
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
||||
pseudofile = BytesIO(f.read())
|
||||
self.assertEqual(
|
||||
self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary.read(),
|
||||
pseudofile.read())
|
||||
self.assertTrue(self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary)
|
||||
if _mb := self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary:
|
||||
self.assertEqual(_mb.read(), pseudofile.read())
|
||||
|
||||
def test_sighting(self):
|
||||
def test_sighting(self) -> None:
|
||||
sighting = MISPSighting()
|
||||
sighting.from_dict(value='1', type='bar', timestamp=11111111)
|
||||
with open('tests/mispevent_testfiles/sighting.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/sighting.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(sighting.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_existing_event(self):
|
||||
def test_existing_event(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
with open('tests/mispevent_testfiles/existing_event.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/existing_event.json') as f:
|
||||
ref_json = json.load(f)
|
||||
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_shadow_attributes_existing(self):
|
||||
def test_shadow_attributes_existing(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/shadow.json')
|
||||
with open('tests/mispevent_testfiles/shadow.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/shadow.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
@unittest.skip("Not supported on MISP.")
|
||||
def test_shadow_attributes(self):
|
||||
def test_shadow_attributes(self) -> None:
|
||||
self.init_event()
|
||||
p = self.mispevent.add_proposal(type='filename', value='baz.jpg')
|
||||
del p.uuid
|
||||
a = self.mispevent.add_attribute('filename', 'bar.exe')
|
||||
a: MISPAttribute = self.mispevent.add_attribute('filename', 'bar.exe') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
p = self.mispevent.attributes[0].add_proposal(type='filename', value='bar.pdf')
|
||||
del p.uuid
|
||||
with open('tests/mispevent_testfiles/proposals.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/proposals.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_default_attributes(self):
|
||||
def test_default_attributes(self) -> None:
|
||||
self.mispevent.add_object(name='file', strict=True)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}])
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}]) # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('pattern-in-file', value='baz')
|
||||
a = self.mispevent.objects[0].add_attribute('pattern-in-file', value='baz') # type: ignore[assignment]
|
||||
self.assertEqual(a.category, 'Artifacts dropped')
|
||||
del a.uuid
|
||||
self.mispevent.add_object(name='file', strict=False, default_attributes_parameters=self.mispevent.objects[0].attributes[0])
|
||||
a = self.mispevent.objects[1].add_attribute('filename', value='baz')
|
||||
a = self.mispevent.objects[1].add_attribute('filename', value='baz') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
self.mispevent.objects[1].uuid = 'b'
|
||||
with open('tests/mispevent_testfiles/event_obj_def_param.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_obj_def_param.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_obj_default_values(self):
|
||||
def test_obj_default_values(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.add_object(name='whois', strict=True)
|
||||
a = self.mispevent.objects[0].add_attribute('registrar', value='registar.example.com')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('registrar', value='registar.example.com') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('domain', value='domain.example.com')
|
||||
a = self.mispevent.objects[0].add_attribute('domain', value='domain.example.com') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns1.example.com')
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns1.example.com') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns2.example.com', disable_correlation=False, to_ids=True, category='External analysis')
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns2.example.com', disable_correlation=False, to_ids=True, category='External analysis') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/def_param.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/def_param.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_obj_references_export(self):
|
||||
def test_obj_references_export(self) -> None:
|
||||
self.init_event()
|
||||
obj1 = MISPObject(name="file")
|
||||
obj2 = MISPObject(name="url", standalone=False)
|
||||
|
|
@ -271,29 +272,29 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertTrue("ObjectReference" in obj1.jsonable())
|
||||
self.assertFalse("ObjectReference" in obj2.jsonable())
|
||||
|
||||
def test_event_not_edited(self):
|
||||
def test_event_not_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
|
||||
def test_event_edited(self):
|
||||
def test_event_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.mispevent.info = 'blah'
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_tag_edited(self):
|
||||
def test_event_tag_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.add_tag('foo')
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_attribute_edited(self):
|
||||
def test_event_attribute_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.mispevent.attributes[0].value = 'blah'
|
||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||
self.assertFalse(self.mispevent.attributes[1].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_attribute_tag_edited(self):
|
||||
def test_event_attribute_tag_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.attributes[0].tags[0].name = 'blah'
|
||||
|
|
@ -302,14 +303,14 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_attribute_tag_edited_second(self):
|
||||
def test_event_attribute_tag_edited_second(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.attributes[0].add_tag(name='blah')
|
||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_object_edited(self):
|
||||
def test_event_object_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.objects[0].comment = 'blah'
|
||||
|
|
@ -317,7 +318,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertFalse(self.mispevent.objects[1].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_object_attribute_edited(self):
|
||||
def test_event_object_attribute_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.objects[0].attributes[0].comment = 'blah'
|
||||
|
|
@ -325,23 +326,23 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertTrue(self.mispevent.objects[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_object_attribute_edited_tag(self):
|
||||
def test_event_object_attribute_edited_tag(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.objects[0].attributes[0].add_tag('blah')
|
||||
self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
|
||||
self.assertTrue(self.mispevent.objects[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
with open('tests/mispevent_testfiles/existing_event_edited.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/existing_event_edited.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_obj_by_id(self):
|
||||
def test_obj_by_id(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
misp_obj = self.mispevent.get_object_by_id(1556)
|
||||
self.assertEqual(misp_obj.uuid, '5a3cd604-e11c-4de5-bbbf-c170950d210f')
|
||||
|
||||
def test_userdefined_object_custom_template(self):
|
||||
def test_userdefined_object_custom_template(self) -> None:
|
||||
self.init_event()
|
||||
with open('tests/mispevent_testfiles/test_object_template/definition.json') as f:
|
||||
template = json.load(f)
|
||||
|
|
@ -352,16 +353,16 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, '{\'member3\'} are required.')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member3', value='foo')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('member3', value='foo') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# Fail on requiredOneOf
|
||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, 'At least one of the following attributes is required: member1, member2')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# member1 is not a multiple
|
||||
|
|
@ -370,12 +371,12 @@ class TestMISPEvent(unittest.TestCase):
|
|||
|
||||
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_userdefined_object_custom_dir(self):
|
||||
def test_userdefined_object_custom_dir(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.add_object(name='test_object_template', strict=True, misp_objects_path_custom='tests/mispevent_testfiles')
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
|
|
@ -383,16 +384,16 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, '{\'member3\'} are required.')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member3', value='foo')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('member3', value='foo') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# Fail on requiredOneOf
|
||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, 'At least one of the following attributes is required: member1, member2')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# member1 is not a multiple
|
||||
|
|
@ -401,15 +402,15 @@ class TestMISPEvent(unittest.TestCase):
|
|||
|
||||
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_first_last_seen(self):
|
||||
def test_first_last_seen(self) -> None:
|
||||
me = MISPEvent()
|
||||
me.info = 'Test First and Last Seen'
|
||||
me.date = '2020.01.12'
|
||||
me.date = '2020.01.12' # type: ignore[assignment]
|
||||
self.assertEqual(me.date.day, 12)
|
||||
me.add_attribute('ip-dst', '8.8.8.8', first_seen='06-21-1998', last_seen=1580213607.469571)
|
||||
self.assertEqual(me.attributes[0].first_seen.year, 1998)
|
||||
|
|
@ -417,11 +418,11 @@ class TestMISPEvent(unittest.TestCase):
|
|||
now = datetime.now().astimezone()
|
||||
me.attributes[0].last_seen = now
|
||||
today = date.today()
|
||||
me.attributes[0].first_seen = today
|
||||
me.attributes[0].first_seen = today # type: ignore[assignment]
|
||||
self.assertEqual(me.attributes[0].first_seen.year, today.year)
|
||||
self.assertEqual(me.attributes[0].last_seen, now)
|
||||
|
||||
def test_feed(self):
|
||||
def test_feed(self) -> None:
|
||||
me = MISPEvent()
|
||||
me.info = 'Test feed'
|
||||
org = MISPOrganisation()
|
||||
|
|
@ -439,7 +440,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertEqual(feed['Event']['_manifest'][me.uuid]['info'], 'Test feed')
|
||||
self.assertEqual(len(feed['Event']['Object'][0]['Attribute']), 2)
|
||||
|
||||
def test_object_templates(self):
|
||||
def test_object_templates(self) -> None:
|
||||
me = MISPEvent()
|
||||
for template in glob.glob(str(me.misp_objects_path / '*' / 'definition.json')):
|
||||
with open(template) as f:
|
||||
|
|
@ -458,7 +459,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
subset = set(entry['categories']).issubset(me.describe_types['categories'])
|
||||
self.assertTrue(subset, f'{t_json["name"]} - {obj_relation}')
|
||||
|
||||
def test_git_vuln_finder(self):
|
||||
def test_git_vuln_finder(self) -> None:
|
||||
with open('tests/git-vuln-finder-quagga.json') as f:
|
||||
dump = json.load(f)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,23 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import sys
|
||||
import unittest
|
||||
import subprocess
|
||||
|
||||
import urllib3 # type: ignore
|
||||
import urllib3
|
||||
import logging
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
try:
|
||||
from pymisp import ExpandedPyMISP, MISPOrganisation, MISPUser, MISPEvent, MISPObject, MISPSharingGroup, Distribution
|
||||
from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPEvent, MISPObject, MISPSharingGroup, Distribution
|
||||
except ImportError:
|
||||
if sys.version_info < (3, 6):
|
||||
print('This test suite requires Python 3.6+, breaking.')
|
||||
sys.exit(0)
|
||||
else:
|
||||
raise
|
||||
raise
|
||||
|
||||
key = 'eYQdGTEWZJ8C2lm9EpnMqxQGwGiPNyoR75JvLdlE'
|
||||
verifycert = False
|
||||
|
|
@ -73,7 +69,7 @@ fast_mode = True
|
|||
class MISPInstance():
|
||||
|
||||
def __init__(self, params):
|
||||
self.initial_user_connector = ExpandedPyMISP(params['url'], params['key'], ssl=False, debug=False)
|
||||
self.initial_user_connector = PyMISP(params['url'], params['key'], ssl=False, debug=False)
|
||||
# Git pull
|
||||
self.initial_user_connector.update_misp()
|
||||
# Set the default role (id 3 on the VM is normal user)
|
||||
|
|
@ -101,7 +97,7 @@ class MISPInstance():
|
|||
user.org_id = self.test_org.id
|
||||
user.role_id = 1 # Site admin
|
||||
self.test_site_admin = self.initial_user_connector.add_user(user)
|
||||
self.site_admin_connector = ExpandedPyMISP(params['url'], self.test_site_admin.authkey, ssl=False, debug=False)
|
||||
self.site_admin_connector = PyMISP(params['url'], self.test_site_admin.authkey, ssl=False, debug=False)
|
||||
self.site_admin_connector.toggle_global_pythonify()
|
||||
# Create org admin
|
||||
user = MISPUser()
|
||||
|
|
@ -109,14 +105,14 @@ class MISPInstance():
|
|||
user.org_id = self.test_org.id
|
||||
user.role_id = 2 # Org admin
|
||||
self.test_org_admin = self.site_admin_connector.add_user(user)
|
||||
self.org_admin_connector = ExpandedPyMISP(params['url'], self.test_org_admin.authkey, ssl=False, debug=False)
|
||||
self.org_admin_connector = PyMISP(params['url'], self.test_org_admin.authkey, ssl=False, debug=False)
|
||||
self.org_admin_connector.toggle_global_pythonify()
|
||||
# Create user
|
||||
user = MISPUser()
|
||||
user.email = params['email_user']
|
||||
user.org_id = self.test_org.id
|
||||
self.test_usr = self.org_admin_connector.add_user(user)
|
||||
self.user_connector = ExpandedPyMISP(params['url'], self.test_usr.authkey, ssl=False, debug=False)
|
||||
self.user_connector = PyMISP(params['url'], self.test_usr.authkey, ssl=False, debug=False)
|
||||
self.user_connector.toggle_global_pythonify()
|
||||
|
||||
# Setup external_baseurl
|
||||
|
|
@ -141,7 +137,7 @@ class MISPInstance():
|
|||
user.org_id = sync_org.id
|
||||
user.role_id = 5 # Org admin
|
||||
sync_user = self.site_admin_connector.add_user(user)
|
||||
sync_user_connector = ExpandedPyMISP(self.site_admin_connector.root_url, sync_user.authkey, ssl=False, debug=False)
|
||||
sync_user_connector = PyMISP(self.site_admin_connector.root_url, sync_user.authkey, ssl=False, debug=False)
|
||||
sync_server_config = sync_user_connector.get_sync_config(pythonify=True)
|
||||
self.sync.append((sync_org, sync_user, sync_server_config))
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue