mirror of https://github.com/MISP/PyMISP
Merge branch 'main' of github.com:MISP/PyMISP
commit
4c152ded19
|
@ -38,11 +38,11 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# 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).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ 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
|
# 📚 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
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|
|
@ -11,17 +11,18 @@ jobs:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.7, 3.8, 3.9, '3.10']
|
python-version: [3.8, 3.9, '3.10', '3.11', '3.12']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Set up Python ${{matrix.python-version}}
|
- name: Set up Python ${{matrix.python-version}}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{matrix.python-version}}
|
python-version: ${{matrix.python-version}}
|
||||||
|
|
||||||
|
@ -33,7 +34,12 @@ jobs:
|
||||||
- name: Test with nosetests
|
- name: Test with nosetests
|
||||||
run: |
|
run: |
|
||||||
poetry run pytest --cov=pymisp tests/test_*.py
|
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
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
version: 2
|
version: 2
|
||||||
|
build:
|
||||||
|
os: "ubuntu-22.04"
|
||||||
|
tools:
|
||||||
|
python: "3"
|
||||||
|
|
||||||
python:
|
python:
|
||||||
version: 3.7
|
|
||||||
install:
|
install:
|
||||||
- method: pip
|
- method: pip
|
||||||
path: .
|
path: .
|
||||||
extra_requirements:
|
extra_requirements:
|
||||||
- docs
|
- docs
|
||||||
|
|
||||||
build:
|
|
||||||
image: latest
|
|
||||||
|
|
||||||
formats: all
|
formats: all
|
||||||
|
|
863
CHANGELOG.txt
863
CHANGELOG.txt
|
@ -2,6 +2,866 @@ Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.194 (2024-06-21)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Make a response in the tests a MISPUser obj. [Raphaël Vinot]
|
||||||
|
- Tests failing du to missing error. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.193 (2024-06-06)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
New
|
||||||
|
~~~
|
||||||
|
- [analyst-data] Added initial support of analyst data concept and
|
||||||
|
functions - WiP. [Sami Mokaddem]
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- A bit more refactoring. [Raphaël Vinot]
|
||||||
|
- Use from_dict in the mixin to initialize the objects. [Raphaël Vinot]
|
||||||
|
- [analyst-data] Added improvements, API endpoints and tests. [Sami
|
||||||
|
Mokaddem]
|
||||||
|
- [analyst-data] Make sure to include note_type_name. [Sami Mokaddem]
|
||||||
|
- Make mypy happy, change inheritance. [Raphaël Vinot]
|
||||||
|
- Allow orgc context for search_galaxy_clusters. [Jeroen Pinoy]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- [analyst-data] Continued implementation of analyst-data support. [Sami
|
||||||
|
Mokaddem]
|
||||||
|
- Allow orgc context for search_galaxy_clusters. [Jeroen Pinoy]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Get the tests to pass. [Raphaël Vinot]
|
||||||
|
- Properly load AnalystData from dict. [Raphaël Vinot]
|
||||||
|
- More changes to get the tests to pass. [Raphaël Vinot]
|
||||||
|
- [event-report] Make sure to generate an UUID. [Sami Mokaddem]
|
||||||
|
- Pass kwargs to abstract. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Chg; Bump changelog. [Raphaël Vinot]
|
||||||
|
- Chg; Bump version. [Raphaël Vinot]
|
||||||
|
- Add test case. [Vincenzo]
|
||||||
|
- Add attach galaxy cluster method. [Vincenzo]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.190 (2024-04-18)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump object templates. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version, deps. [Raphaël Vinot]
|
||||||
|
- Bump deps, require python 3.9+ for doc. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- [data] describeTypes file updated. [Alexandre Dulaunoy]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- [internal] Correct way to convert bytes to string if orjson exists.
|
||||||
|
[Jakub Onderka]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.188 (2024-03-22)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
New
|
||||||
|
~~~
|
||||||
|
- Support X-MISP-AUTH Header. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Also, improve HTTP headers init
|
||||||
|
|
||||||
|
Fix #1179
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version, templates. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Strip API key before setting it. [Raphaël Vinot]
|
||||||
|
- Python 3.8 support & typing. [Raphaël Vinot]
|
||||||
|
- Typing for Python < 3.10. [Raphaël Vinot]
|
||||||
|
- Avoid issue when payload ist a list. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.187 (2024-03-07)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump templates, version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump extract-msg. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.186 (2024-02-27)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- 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]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Correct FileObject import. [Johannes Bader]
|
||||||
|
|
||||||
|
The FileObject import has been moved outside the try-except-block
|
||||||
|
related to lief, as the import is needed regardless whether lief
|
||||||
|
is available or not.
|
||||||
|
- Disable WL when calling the disable method, not toggle. [Raphaël
|
||||||
|
Vinot]
|
||||||
|
|
||||||
|
Fix #1159
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Build(deps): bump urllib3 from 2.2.0 to 2.2.1. [dependabot[bot]]
|
||||||
|
|
||||||
|
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.0 to 2.2.1.
|
||||||
|
- [Release notes](https://github.com/urllib3/urllib3/releases)
|
||||||
|
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
|
||||||
|
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.0...2.2.1)
|
||||||
|
|
||||||
|
---
|
||||||
|
updated-dependencies:
|
||||||
|
- dependency-name: urllib3
|
||||||
|
dependency-type: direct:production
|
||||||
|
update-type: version-update:semver-patch
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.185 (2024-02-16)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- 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)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
New
|
||||||
|
~~~
|
||||||
|
- Run tests on python 3.12 too. [Raphaël Vinot]
|
||||||
|
|
||||||
|
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]
|
||||||
|
- Make mypy happy. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Disable search logs tests for now. [Raphaël Vinot]
|
||||||
|
- Disable fastmode, reenable fetching files. [Raphaël Vinot]
|
||||||
|
- Try to speedup tests by not importing galaxies, taxos, ... [Raphaël
|
||||||
|
Vinot]
|
||||||
|
- Do not clone repo from test. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Make other fieldnames in CSV also valid... [Raphaël Vinot]
|
||||||
|
- Make fieldnames actually valid. [Raphaël Vinot]
|
||||||
|
- Remove CI for python 3.12, waiting for pydeep wheels. [Raphaël Vinot]
|
||||||
|
- Allow object-relation names with uppercase characters defined in the
|
||||||
|
templates. [Raphaël Vinot]
|
||||||
|
- Check if path exists in tests. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Ch: Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.176 (2023-09-15)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version, deps. [Raphaël Vinot]
|
||||||
|
- Bump objects. [Raphaël Vinot]
|
||||||
|
- Bump deps, objects. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Avoid exception when data is an empty iterator. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix #1053
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Revert "build(deps): bump codecov/codecov-action from 3 to 4" [Raphaël
|
||||||
|
Vinot]
|
||||||
|
|
||||||
|
This reverts commit b7bb6b74317b70613ed42ea234eaafb00da6e5c6.
|
||||||
|
- 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
|
||||||
|
...
|
||||||
|
- Build(deps): bump actions/checkout from 3 to 4. [dependabot[bot]]
|
||||||
|
|
||||||
|
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
|
||||||
|
- [Release notes](https://github.com/actions/checkout/releases)
|
||||||
|
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
|
||||||
|
- [Commits](https://github.com/actions/checkout/compare/v3...v4)
|
||||||
|
|
||||||
|
---
|
||||||
|
updated-dependencies:
|
||||||
|
- dependency-name: actions/checkout
|
||||||
|
dependency-type: direct:production
|
||||||
|
update-type: version-update:semver-major
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.175 (2023-08-23)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump objects, missed that. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps, readthedocs config. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Update Sharing group info from full object. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix #1049
|
||||||
|
- Changes in msg-extract strip a character. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.174 (2023-07-31)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version, templates. [Raphaël Vinot]
|
||||||
|
- Bump deps, fix code accordingly. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Push code changes related to deps upgrade... [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Git: Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.173 (2023-07-10)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump objects. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump objects. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Maybe fixing a CakePHP issue. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Maybe fixing #1014
|
||||||
|
- Use proper endpoint to unpublish event. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix #1012
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Feat: introduce setter for galaxies. [Sura De Silva]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.172 (2023-06-08)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- [misp-objects] Bumped latest version with updated templates.
|
||||||
|
[Christian Studer]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Proper changelog bump. [Raphaël Vinot]
|
||||||
|
- Properly bump version. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Build(deps-dev): bump jupyterlab from 3.6.3 to 4.0.0.
|
||||||
|
[dependabot[bot]]
|
||||||
|
|
||||||
|
Bumps [jupyterlab](https://github.com/jupyterlab/jupyterlab) from 3.6.3 to 4.0.0.
|
||||||
|
- [Release notes](https://github.com/jupyterlab/jupyterlab/releases)
|
||||||
|
- [Changelog](https://github.com/jupyterlab/jupyterlab/blob/master/CHANGELOG.md)
|
||||||
|
- [Commits](https://github.com/jupyterlab/jupyterlab/compare/@jupyterlab/vdom@3.6.3...@jupyterlab/lsp@4.0.0)
|
||||||
|
|
||||||
|
---
|
||||||
|
updated-dependencies:
|
||||||
|
- dependency-name: jupyterlab
|
||||||
|
dependency-type: direct:development
|
||||||
|
update-type: version-update:semver-major
|
||||||
|
...
|
||||||
|
- Update settings.default.py - tags not tag. [Alexandre Dulaunoy]
|
||||||
|
|
||||||
|
tags is now an array
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.171 (2023-05-16)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump deps, object templates. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Remove old setup files, bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Extra print breaking the CI on MISP side. [Raphaël Vinot]
|
||||||
|
- Properly use lief on a file. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Allow search by 'event_tags' and improve the handling of galaxy
|
||||||
|
clusters. [Stefano Ortolani]
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Add 'event_tags' parameter when searching for events
|
||||||
|
- Add new method to search for galaxies by value
|
||||||
|
- Add new parameter to fetch cluster information when retrieving clusters
|
||||||
|
- Add new parameter to hard-delete object references
|
||||||
|
- Using underscore name 'description_file' in setup.cfg. [Erhan]
|
||||||
|
|
||||||
|
Usage of dash-separated 'description-file' will not be supported in future versions. Please use the underscore name 'description_file' instead. By 2023-Sep-26, you need to update your project and remove deprecated calls or your builds will no longer be supported.
|
||||||
|
|
||||||
|
See https://setuptools.pypa.io/en/latest/userguide/declarative_config.html for details.
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.170.2 (2023-05-04)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.170.1 (2023-04-19)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Disable fail fast in GHA. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Update lief code to v0.13. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.170 (2023-04-12)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Add: support breakOnDuplicate option for attributes:add() [Luciano
|
||||||
|
Righetti]
|
||||||
|
- Update reportlab_generator.py. [CarlosLoureiro]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.169.3 (2023-03-27)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump deps, version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Invalid check if taxo is enabled. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.169.2 (2023-03-17)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Include event reports by default in feed. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Use proper parameter to trigger the request in search_galaxy_clusters.
|
||||||
|
[Raphaël Vinot]
|
||||||
|
- Use POST in search galaxy cluster. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Rename include_event_reports kwarg to with_event_reports, in-line with
|
||||||
|
other kwarg naming. [UFOSmuggler]
|
||||||
|
- Add kwarg to allow the inclusion of event reports into to_feed(),
|
||||||
|
honour with_distribution and valid_distributions kwargs. [UFOSmuggler]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.169.1 (2023-03-14)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Add greynoise-ip object. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix #951
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.169 (2023-03-10)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump templates. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Add local key in MISPTag. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Related #947
|
||||||
|
- Use pytest for the tests. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.168.1 (2023-02-28)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
New
|
||||||
|
~~~
|
||||||
|
- [doc] added the Jupyter notebook used in a.7-rest-api-extensive-
|
||||||
|
restsearch. [Alexandre Dulaunoy]
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog, version. [Raphaël Vinot]
|
||||||
|
- Bump templates, again. [Raphaël Vinot]
|
||||||
|
- Bump templates. [Raphaël Vinot]
|
||||||
|
- Bump deps, templates. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Properly handle missing parameter in CSV importer. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix #931
|
||||||
|
- Undefined variable in event delegation. [Raphaël Vinot]
|
||||||
|
- Remove reference to old pydeep. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix #914
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.168 (2023-01-23)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.167.2 (2023-01-17)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump deps, version. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Set relationship_type default in MISPTag to empty string. [Raphaël
|
||||||
|
Vinot]
|
||||||
|
- Another typo in readme. [Raphaël Vinot]
|
||||||
|
- Typo in readme. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.167.1 (2023-01-16)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
New
|
||||||
|
~~~
|
||||||
|
- Add relationship_type in Tag entries for feeds. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump requests. [Raphaël Vinot]
|
||||||
|
- Bump pyzmq. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump python version used by read the docs. [Raphaël Vinot]
|
||||||
|
- Bump warning to inform user that python 3.10 wil be required in 12
|
||||||
|
months. [Raphaël Vinot]
|
||||||
|
- Bump minimal PyMISP version to 3.8. [Raphaël Vinot]
|
||||||
|
- Re-bump changelog. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Update whl files. [Raphaël Vinot]
|
||||||
|
- Nvm, readthedocs requires python 3.8 at most. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
v2.4.167 (2022-12-22)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
~~~~~~~
|
||||||
|
- Bump objects. [Raphaël Vinot]
|
||||||
|
- Bump changelog. [Raphaël Vinot]
|
||||||
|
- Bump version. [Raphaël Vinot]
|
||||||
|
- Bump objects. [Raphaël Vinot]
|
||||||
|
- Bump dependencies, move to poetry 1.3. [Raphaël Vinot]
|
||||||
|
- Bump certifi. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Bump deps. [Raphaël Vinot]
|
||||||
|
- Re-order classes. [Raphaël Vinot]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
- Creation fo "add_attributes_from_csv.py" [Julien Mongenet]
|
||||||
|
|
||||||
|
The file aims to ingest a formated CSV file containing attributes for MISP ingestion.
|
||||||
|
- Graceful handling of tagging when name attribute is missing. [Sura De
|
||||||
|
Silva]
|
||||||
|
- Add: Galaxy test sample. [Christian Studer]
|
||||||
|
- Add: Added very straight forward tests to make sure the galaxy
|
||||||
|
clusters are properly defined. [Christian Studer]
|
||||||
|
- Add: Added the `Galaxy` field to MISPAttribute using the MISPGalaxy
|
||||||
|
class. [Christian Studer]
|
||||||
|
|
||||||
|
- Including an `add_galaxy` method similar to the
|
||||||
|
one used for events
|
||||||
|
- `attribute.galaxies` gives the list of attached
|
||||||
|
galaxy clusters
|
||||||
|
|
||||||
|
|
||||||
v2.4.166 (2022-11-28)
|
v2.4.166 (2022-11-28)
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -13,6 +873,7 @@ New
|
||||||
|
|
||||||
Changes
|
Changes
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
- Re-bump changelog. [Raphaël Vinot]
|
||||||
- Bump changelog. [Raphaël Vinot]
|
- Bump changelog. [Raphaël Vinot]
|
||||||
- Bump deps, version. [Raphaël Vinot]
|
- Bump deps, version. [Raphaël Vinot]
|
||||||
- [types] added azure-application-id. [iglocska]
|
- [types] added azure-application-id. [iglocska]
|
||||||
|
@ -4622,5 +5483,3 @@ v1.1.2 (2015-08-05)
|
||||||
- Json export is not supported everywhere. [Raphaël Vinot]
|
- Json export is not supported everywhere. [Raphaël Vinot]
|
||||||
- Some testing. [Raphaël Vinot]
|
- Some testing. [Raphaël Vinot]
|
||||||
- Initial commit. [Raphaël Vinot]
|
- Initial commit. [Raphaël Vinot]
|
||||||
|
|
||||||
|
|
||||||
|
|
42
README.md
42
README.md
|
@ -1,10 +1,10 @@
|
||||||
**IMPORTANT NOTE**: This library will require **at least** python 3.8 starting the 1st of January 2022. If you have legacy versions of python, please use the latest PyMISP version that will be released in December 2021, and consider updating your system(s). Anything released within the last 2 years will do, starting with Ubuntu 20.04.
|
**IMPORTANT NOTE**: This library will require **at least** Python 3.10 starting the 1st of January 2024. If you have legacy versions of python, please use the latest PyMISP version that will be released in December 2023, and consider updating your system(s). Anything released within the last 2 years will do, starting with Ubuntu 22.04.
|
||||||
|
|
||||||
# PyMISP - Python Library to access MISP
|
# PyMISP - Python Library to access MISP
|
||||||
|
|
||||||
[](http://pymisp.readthedocs.io/?badge=latest)
|
[](http://pymisp.readthedocs.io/?badge=latest)
|
||||||
[](https://coveralls.io/github/MISP/PyMISP?branch=main)
|
[](https://coveralls.io/github/MISP/PyMISP?branch=main)
|
||||||
[](https://www.python.org/downloads/release/python-360/)
|
[](https://www.python.org/downloads/release/python-380/)
|
||||||
[](https://pypi.python.org/pypi/pymisp/)
|
[](https://pypi.python.org/pypi/pymisp/)
|
||||||
[](https://pypi.python.org/pypi/pymisp/)
|
[](https://pypi.python.org/pypi/pymisp/)
|
||||||
|
|
||||||
|
@ -46,13 +46,13 @@ pip3 install pymisp[virustotal,email]
|
||||||
```
|
```
|
||||||
git clone https://github.com/MISP/PyMISP.git && cd PyMISP
|
git clone https://github.com/MISP/PyMISP.git && cd PyMISP
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
poetry install -E fileobjects -E openioc -E virustotal -E docs -E pdfexport
|
poetry install -E fileobjects -E openioc -E virustotal -E docs -E pdfexport -E email
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running the tests
|
### Running the tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry run nosetests-3.4 --with-coverage --cover-package=pymisp,tests --cover-tests tests/test_*.py
|
poetry run pytest --cov=pymisp tests/test_*.py
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have a MISP instance to test against, you can also run the live ones:
|
If you have a MISP instance to test against, you can also run the live ones:
|
||||||
|
@ -60,7 +60,7 @@ If you have a MISP instance to test against, you can also run the live ones:
|
||||||
**Note**: You need to update the key in `tests/testlive_comprehensive.py` to the automation key of your admin account.
|
**Note**: You need to update the key in `tests/testlive_comprehensive.py` to the automation key of your admin account.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry run nosetests-3.4 --with-coverage --cover-package=pymisp,tests --cover-tests tests/testlive_comprehensive.py
|
poetry run pytest --cov=pymisp tests/testlive_comprehensive.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Samples and how to use PyMISP
|
## Samples and how to use PyMISP
|
||||||
|
@ -124,8 +124,7 @@ logging.basicConfig(level=logging.DEBUG, filename="debug.log", filemode='w', for
|
||||||
```bash
|
```bash
|
||||||
# From poetry
|
# From poetry
|
||||||
|
|
||||||
nosetests-3.4 -s --with-coverage --cover-package=pymisp,tests --cover-tests tests/testlive_comprehensive.py:TestComprehensive.[test_name]
|
pytest --cov=pymisp tests/test_*.py tests/testlive_comprehensive.py:TestComprehensive.[test_name]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## 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.
|
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
|
# License
|
||||||
|
|
||||||
PyMISP is distributed under an [open source license](./LICENSE). A simplified 2-BSD license.
|
PyMISP is distributed under an [open source license](./LICENSE). A simplified 2-BSD license.
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,74 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import csv
|
||||||
|
from pymisp import PyMISP
|
||||||
|
from pymisp import ExpandedPyMISP, MISPAttribute
|
||||||
|
from keys import misp_url, misp_key, misp_verifycert
|
||||||
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||||
|
import argparse
|
||||||
|
import urllib3
|
||||||
|
import requests
|
||||||
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
Sample usage:
|
||||||
|
|
||||||
|
python3 add_filetype_object_from_csv.py -e <Event_UUID> -f <formated_file_with_attributes>.csv
|
||||||
|
|
||||||
|
|
||||||
|
Attribute CSV file (aach line is an entry):
|
||||||
|
|
||||||
|
value;category;type;comment;to_ids;first_seen;last_seen;tag1;tag2
|
||||||
|
test.pdf;Payload delivery;filename;Email attachment;0;1970-01-01;1970-01-01;tlp:green;ransomware
|
||||||
|
127.0.0.1;Network activity;ip-dst;C2 server;1;;;tlp:white;
|
||||||
|
|
||||||
|
value = IOC's value
|
||||||
|
category = its MISP category (https://www.circl.lu/doc/misp/categories-and-types/)
|
||||||
|
type = its MISP type (https://www.circl.lu/doc/misp/categories-and-types/)
|
||||||
|
comment = IOC's description
|
||||||
|
to_ids = Boolean expected (0 = IDS flag not checked // 1 = IDS flag checked)
|
||||||
|
first_seen = First seen date, if any (left empty if not)
|
||||||
|
last_seen = Last seen date, if any (left empty if not)
|
||||||
|
tag = IOC tag, if any
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Add attributes to a MISP event from a semi-colon formated csv file')
|
||||||
|
parser.add_argument("-e", "--event_uuid", required=True, help="Event UUID to update")
|
||||||
|
parser.add_argument("-f", "--attr_file", required=True, help="Attribute CSV file path")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
pymisp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
|
||||||
|
|
||||||
|
f = open(args.attr_file, newline='')
|
||||||
|
csv_reader = csv.reader(f, delimiter=";")
|
||||||
|
|
||||||
|
for line in csv_reader:
|
||||||
|
value = line[0]
|
||||||
|
category = line[1]
|
||||||
|
type = line[2]
|
||||||
|
comment = line[3]
|
||||||
|
ids = line[4]
|
||||||
|
fseen = line[5]
|
||||||
|
lseen = line[6]
|
||||||
|
tags = line[7:]
|
||||||
|
|
||||||
|
misp_attribute = MISPAttribute()
|
||||||
|
misp_attribute.value = str(value)
|
||||||
|
misp_attribute.category = str(category)
|
||||||
|
misp_attribute.type = str(type)
|
||||||
|
misp_attribute.comment = str(comment)
|
||||||
|
misp_attribute.to_ids = str(ids)
|
||||||
|
if fseen != '':
|
||||||
|
misp_attribute.first_seen = str(fseen)
|
||||||
|
if lseen != '':
|
||||||
|
misp_attribute.last_seen = str(lseen)
|
||||||
|
for x in tags:
|
||||||
|
misp_attribute.add_tag(x)
|
||||||
|
r = pymisp.add_attribute(args.event_uuid, misp_attribute)
|
||||||
|
print(line)
|
||||||
|
print("\nAttributes successfully saved :)")
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from pymisp import ExpandedPyMISP
|
from pymisp import PyMISP
|
||||||
from pymisp.tools import EMailObject
|
from pymisp.tools import EMailObject
|
||||||
import traceback
|
import traceback
|
||||||
from keys import misp_url, misp_key, misp_verifycert # type: ignore
|
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).")
|
parser.add_argument("-p", "--path", required=True, help="Path to process (expanded using glob).")
|
||||||
args = parser.parse_args()
|
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):
|
for f in glob.glob(args.path):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -16,7 +16,7 @@ outputdir = 'output'
|
||||||
# you can use on the event index, such as organisation, tags, etc.
|
# you can use on the event index, such as organisation, tags, etc.
|
||||||
# It uses the same joining and condition rules as the API parameters
|
# It uses the same joining and condition rules as the API parameters
|
||||||
# For example:
|
# For example:
|
||||||
# filters = {'tag':'tlp:white|feed-export|!privint','org':'CIRCL', 'published':1}
|
# filters = {'tags':['tlp:white','feed-export','!privint'],'org':'CIRCL', 'published':1}
|
||||||
# the above would generate a feed for all published events created by CIRCL,
|
# the above would generate a feed for all published events created by CIRCL,
|
||||||
# tagged tlp:white and/or feed-export but exclude anything tagged privint
|
# tagged tlp:white and/or feed-export but exclude anything tagged privint
|
||||||
filters = {'published':'true'}
|
filters = {'published':'true'}
|
||||||
|
|
|
@ -10,7 +10,7 @@ from pymisp import MISPEvent
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from keys import misp_url, misp_key, misp_verifycert
|
from keys import misp_url, misp_key, misp_verifycert
|
||||||
from pymisp import ExpandedPyMISP
|
from pymisp import PyMISP
|
||||||
offline = False
|
offline = False
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
offline = True
|
offline = True
|
||||||
|
@ -66,7 +66,7 @@ if __name__ == '__main__':
|
||||||
if offline:
|
if offline:
|
||||||
print('You are in offline mode, quitting.')
|
print('You are in offline mode, quitting.')
|
||||||
else:
|
else:
|
||||||
misp = ExpandedPyMISP(url=misp_url, key=misp_key, ssl=misp_verifycert)
|
misp = PyMISP(url=misp_url, key=misp_key, ssl=misp_verifycert)
|
||||||
if args.new_event:
|
if args.new_event:
|
||||||
event = MISPEvent()
|
event = MISPEvent()
|
||||||
event.info = args.new_event
|
event.info = args.new_event
|
||||||
|
@ -80,7 +80,7 @@ if __name__ == '__main__':
|
||||||
else:
|
else:
|
||||||
print('Something went wrong:')
|
print('Something went wrong:')
|
||||||
print(new_event)
|
print(new_event)
|
||||||
else:
|
elif args.update_event:
|
||||||
for o in objects:
|
for o in objects:
|
||||||
new_object = misp.add_object(args.update_event, o, pythonify=True)
|
new_object = misp.add_object(args.update_event, o, pythonify=True)
|
||||||
if isinstance(new_object, str):
|
if isinstance(new_object, str):
|
||||||
|
@ -90,3 +90,5 @@ if __name__ == '__main__':
|
||||||
else:
|
else:
|
||||||
print('Something went wrong:')
|
print('Something went wrong:')
|
||||||
print(new_event)
|
print(new_event)
|
||||||
|
else:
|
||||||
|
print('you need to pass either a event info field (flag -i), or the event ID you want to update (flag -u)')
|
||||||
|
|
15
mypy.ini
15
mypy.ini
|
@ -1,6 +1,15 @@
|
||||||
[mypy]
|
[mypy]
|
||||||
ignore_errors = False
|
strict = True
|
||||||
|
warn_return_any = False
|
||||||
show_error_context = True
|
show_error_context = True
|
||||||
pretty = 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,16 +1,21 @@
|
||||||
__version__ = '2.4.166'
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
import importlib.metadata
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
__version__ = importlib.metadata.version("pymisp")
|
||||||
|
|
||||||
def warning_2022():
|
|
||||||
if sys.version_info < (3, 8):
|
def warning_2024() -> None:
|
||||||
|
if sys.version_info < (3, 10):
|
||||||
warnings.warn("""
|
warnings.warn("""
|
||||||
As our baseline system is the latest Ubuntu LTS, and Ubuntu LTS 20.04 has Python 3.8 available,
|
As our baseline system is the latest Ubuntu LTS, and Ubuntu LTS 22.04 has Python 3.10 available,
|
||||||
we will officially deprecate python versions below 3.8 on January 1st 2022.
|
we will officially deprecate python versions below 3.10 on January 1st 2024.
|
||||||
**Please update your codebase.**""", DeprecationWarning, stacklevel=3)
|
**Please update your codebase.**""", DeprecationWarning, stacklevel=3)
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,26 +30,24 @@ Response (if any):
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
warning_2022()
|
warning_2024()
|
||||||
from .exceptions import PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey, InvalidMISPObject, UnknownMISPObjectTemplate, PyMISPInvalidFormat, MISPServerError, PyMISPNotImplementedYet, PyMISPUnexpectedResponse, PyMISPEmptyResponse # noqa
|
from .exceptions import (PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey, # noqa
|
||||||
from .abstract import AbstractMISP, MISPEncode, pymisp_json_default, MISPTag, Distribution, ThreatLevel, Analysis # noqa
|
InvalidMISPObject, UnknownMISPObjectTemplate, PyMISPInvalidFormat, MISPServerError, PyMISPNotImplementedYet, PyMISPUnexpectedResponse, PyMISPEmptyResponse)
|
||||||
|
from .abstract import AbstractMISP, MISPEncode, pymisp_json_default, MISPTag, Distribution, ThreatLevel, Analysis # noqa
|
||||||
from .mispevent import (MISPEvent, MISPAttribute, MISPObjectReference, MISPObjectAttribute, MISPObject, MISPUser, # noqa
|
from .mispevent import (MISPEvent, MISPAttribute, MISPObjectReference, MISPObjectAttribute, MISPObject, MISPUser, # noqa
|
||||||
MISPOrganisation, MISPSighting, MISPLog, MISPShadowAttribute, MISPWarninglist, MISPTaxonomy,
|
MISPOrganisation, MISPSighting, MISPLog, MISPShadowAttribute, MISPWarninglist, MISPTaxonomy,
|
||||||
MISPNoticelist, MISPObjectTemplate, MISPSharingGroup, MISPRole, MISPServer, MISPFeed,
|
MISPNoticelist, MISPObjectTemplate, MISPSharingGroup, MISPRole, MISPServer, MISPFeed,
|
||||||
MISPEventDelegation, MISPUserSetting, MISPInbox, MISPEventBlocklist, MISPOrganisationBlocklist,
|
MISPEventDelegation, MISPUserSetting, MISPInbox, MISPEventBlocklist, MISPOrganisationBlocklist,
|
||||||
MISPEventReport, MISPGalaxyCluster, MISPGalaxyClusterElement, MISPGalaxyClusterRelation,
|
MISPEventReport, MISPCorrelationExclusion, MISPDecayingModel, MISPGalaxy, MISPGalaxyCluster,
|
||||||
MISPCorrelationExclusion, MISPGalaxy, MISPDecayingModel)
|
MISPGalaxyClusterElement, MISPGalaxyClusterRelation, MISPNote, MISPOpinion, MISPRelationship)
|
||||||
|
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 AbstractMISPObjectGenerator # noqa
|
||||||
from .tools import Neo4j # noqa
|
|
||||||
from .tools import stix # noqa
|
|
||||||
from .tools import openioc # noqa
|
from .tools import openioc # noqa
|
||||||
from .tools import ext_lookups # noqa
|
from .tools import ext_lookups # noqa
|
||||||
from .tools import update_objects # 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
|
from .tools import load_warninglists # noqa
|
||||||
# Let's not bother with old python
|
|
||||||
try:
|
try:
|
||||||
from .tools import reportlab_generator # noqa
|
from .tools import reportlab_generator # noqa
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -55,4 +58,26 @@ try:
|
||||||
pass
|
pass
|
||||||
logger.debug('pymisp loaded properly')
|
logger.debug('pymisp loaded properly')
|
||||||
except ImportError as e:
|
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', 'MISPNote', 'MISPOpinion', 'MISPRelationship',
|
||||||
|
'PyMISPError', 'NewEventError', 'NewAttributeError',
|
||||||
|
'NoURL', 'NoKey', 'InvalidMISPObject', 'UnknownMISPObjectTemplate', 'PyMISPInvalidFormat',
|
||||||
|
'Distribution', 'ThreatLevel', 'Analysis', 'ExpandedPyMISP'
|
||||||
|
]
|
||||||
|
|
|
@ -1,53 +1,48 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
|
|
||||||
from deprecated import deprecated # type: ignore
|
from deprecated import deprecated # type: ignore
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from abc import ABCMeta
|
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 enum import Enum
|
||||||
from typing import Union, Optional, Any, Dict, List, Set, Mapping
|
from typing import Any, Mapping
|
||||||
|
|
||||||
from .exceptions import PyMISPInvalidFormat, PyMISPError
|
|
||||||
|
|
||||||
|
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
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')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
resources_path = Path(__file__).parent / 'data'
|
resources_path = Path(__file__).parent / 'data'
|
||||||
misp_objects_path = resources_path / 'misp-objects' / 'objects'
|
misp_objects_path = resources_path / 'misp-objects' / 'objects'
|
||||||
with (resources_path / 'describeTypes.json').open('r') as f:
|
with (resources_path / 'describeTypes.json').open('rb') as f:
|
||||||
describe_types = load(f)['result']
|
describe_types: dict[str, Any] = loads(f.read())['result']
|
||||||
|
|
||||||
|
|
||||||
class MISPFileCache(object):
|
class MISPFileCache:
|
||||||
# cache up to 150 JSON structures in class attribute
|
# cache up to 150 JSON structures in class attribute
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@lru_cache(maxsize=150)
|
@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():
|
if not path.exists():
|
||||||
return None
|
return None
|
||||||
with path.open('r', encoding='utf-8') as f:
|
with path.open('rb') as f:
|
||||||
data = load(f)
|
data = loads(f.read())
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +68,7 @@ class Analysis(Enum):
|
||||||
completed = 2
|
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
|
# transform all integer back to string
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
if isinstance(v, dict):
|
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')
|
@deprecated(reason=" Use method default=pymisp_json_default instead of cls=MISPEncode", version='2.4.117', action='default')
|
||||||
class MISPEncode(JSONEncoder):
|
class MISPEncode(JSONEncoder):
|
||||||
def default(self, obj):
|
def default(self, obj: Any) -> dict[str, Any] | str:
|
||||||
if isinstance(obj, AbstractMISP):
|
if isinstance(obj, AbstractMISP):
|
||||||
return obj.jsonable()
|
return obj.jsonable()
|
||||||
elif isinstance(obj, (datetime, date)):
|
elif isinstance(obj, (datetime, date)):
|
||||||
|
@ -97,12 +92,12 @@ class MISPEncode(JSONEncoder):
|
||||||
return JSONEncoder.default(self, obj)
|
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
|
__resources_path = resources_path
|
||||||
__misp_objects_path = misp_objects_path
|
__misp_objects_path = misp_objects_path
|
||||||
__describe_types = describe_types
|
__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.
|
"""Abstract class for all the MISP objects.
|
||||||
NOTE: Every method in every classes inheriting this one are doing
|
NOTE: Every method in every classes inheriting this one are doing
|
||||||
changes in memory and do not modify data on a remote MISP instance.
|
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__()
|
super().__init__()
|
||||||
self.__edited: bool = True # As we create a new object, we assume it is edited
|
self.__edited: bool = True # As we create a new object, we assume it is edited
|
||||||
self.__not_jsonable: List[str] = []
|
self.__not_jsonable: list[str] = []
|
||||||
self._fields_for_feed: Set
|
self._fields_for_feed: set[str]
|
||||||
self.__self_defined_describe_types: Optional[Dict] = None
|
self.__self_defined_describe_types: dict[str, Any] | None = None
|
||||||
self.uuid: str
|
self.uuid: str
|
||||||
|
|
||||||
if kwargs.get('force_timestamps') is not None:
|
if kwargs.get('force_timestamps') is not None:
|
||||||
|
@ -123,13 +118,13 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
self.__force_timestamps = False
|
self.__force_timestamps = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def describe_types(self) -> Dict:
|
def describe_types(self) -> dict[str, Any]:
|
||||||
if self.__self_defined_describe_types:
|
if self.__self_defined_describe_types:
|
||||||
return self.__self_defined_describe_types
|
return self.__self_defined_describe_types
|
||||||
return self.__describe_types
|
return self.__describe_types
|
||||||
|
|
||||||
@describe_types.setter
|
@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
|
self.__self_defined_describe_types = describe_types
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -141,12 +136,12 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
return self.__misp_objects_path
|
return self.__misp_objects_path
|
||||||
|
|
||||||
@misp_objects_path.setter
|
@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):
|
if isinstance(misp_objects_path, str):
|
||||||
misp_objects_path = Path(misp_objects_path)
|
misp_objects_path = Path(misp_objects_path)
|
||||||
self.__misp_objects_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`.
|
"""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
|
This method aims to be called when all the properties requiring a special
|
||||||
treatment are processed.
|
treatment are processed.
|
||||||
|
@ -159,15 +154,15 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
# We load an existing dictionary, marking it an not-edited
|
# We load an existing dictionary, marking it an not-edited
|
||||||
self.__edited = False
|
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"""
|
"""Add entries to the __not_jsonable list"""
|
||||||
self.__not_jsonable += args
|
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"""
|
"""Set __not_jsonable to a new list"""
|
||||||
self.__not_jsonable = args
|
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"""
|
"""Remove the entries that are in the __not_jsonable list"""
|
||||||
for entry in args:
|
for entry in args:
|
||||||
try:
|
try:
|
||||||
|
@ -179,7 +174,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
"""Load a JSON string"""
|
"""Load a JSON string"""
|
||||||
self.from_dict(**loads(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.
|
"""Dump the class to a dictionary.
|
||||||
This method automatically removes the timestamp recursively in every object
|
This method automatically removes the timestamp recursively in every object
|
||||||
that has been edited is order to let MISP update the event accordingly."""
|
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)
|
to_return = _int_to_str(to_return)
|
||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
def jsonable(self) -> Dict:
|
def jsonable(self) -> dict[str, Any]:
|
||||||
"""This method is used by the JSON encoder"""
|
"""This method is used by the JSON encoder"""
|
||||||
return self.to_dict()
|
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:
|
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.')
|
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
|
if hasattr(self, '_set_default') and callable(self._set_default):
|
||||||
self._set_default() # type: ignore
|
self._set_default()
|
||||||
to_return = {}
|
to_return = {}
|
||||||
for field in sorted(self._fields_for_feed):
|
for field in sorted(self._fields_for_feed):
|
||||||
if getattr(self, field, None) is not None:
|
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']:
|
if field in ['data', 'first_seen', 'last_seen', 'deleted']:
|
||||||
# special fields
|
# special fields
|
||||||
continue
|
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)
|
to_return = _int_to_str(to_return)
|
||||||
return 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"""
|
"""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 dumps(self, default=pymisp_json_default, option=option).decode()
|
||||||
|
|
||||||
return dumps(self, default=pymisp_json_default, sort_keys=sort_keys, indent=indent)
|
return dumps(self, default=pymisp_json_default, sort_keys=sort_keys, indent=indent)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key: str) -> Any:
|
||||||
try:
|
try:
|
||||||
if key[0] != '_' and key not in self.__not_jsonable:
|
if key[0] != '_' and key not in self.__not_jsonable:
|
||||||
return self.__dict__[key]
|
return self.__dict__[key]
|
||||||
|
@ -260,13 +265,13 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
# Expected by pop and other dict-related methods
|
# Expected by pop and other dict-related methods
|
||||||
raise KeyError
|
raise KeyError
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key: str, value: Any) -> None:
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key: str) -> None:
|
||||||
delattr(self, key)
|
delattr(self, key)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self) -> Any:
|
||||||
'''When we call **self, skip keys:
|
'''When we call **self, skip keys:
|
||||||
* starting with _
|
* starting with _
|
||||||
* in __not_jsonable
|
* in __not_jsonable
|
||||||
|
@ -285,7 +290,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
return self.__force_timestamps
|
return self.__force_timestamps
|
||||||
|
|
||||||
@force_timestamp.setter
|
@force_timestamp.setter
|
||||||
def force_timestamp(self, force: bool):
|
def force_timestamp(self, force: bool) -> None:
|
||||||
self.__force_timestamps = force
|
self.__force_timestamps = force
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -305,28 +310,28 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
return self.__edited
|
return self.__edited
|
||||||
|
|
||||||
@edited.setter
|
@edited.setter
|
||||||
def edited(self, val: bool):
|
def edited(self, val: bool) -> None:
|
||||||
"""Set the edit flag"""
|
"""Set the edit flag"""
|
||||||
if isinstance(val, bool):
|
if isinstance(val, bool):
|
||||||
self.__edited = val
|
self.__edited = val
|
||||||
else:
|
else:
|
||||||
raise PyMISPError('edited can only be True or False')
|
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:
|
if name[0] != '_' and not self.__edited and name in self:
|
||||||
# The private members don't matter
|
# The private members don't matter
|
||||||
# If we already have a key with that name, we're modifying it.
|
# If we already have a key with that name, we're modifying it.
|
||||||
self.__edited = True
|
self.__edited = True
|
||||||
super().__setattr__(name, value)
|
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)"""
|
"""Convert a datetime object to a timestamp (int)"""
|
||||||
if isinstance(d, (int, float, str)):
|
if isinstance(d, (int, float, str)):
|
||||||
# Assume we already have a timestamp
|
# Assume we already have a timestamp
|
||||||
return int(d)
|
return int(d)
|
||||||
return int(d.timestamp())
|
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)"""
|
"""Add a tag to the attribute (by name or a MISPTag object)"""
|
||||||
if isinstance(tag, str):
|
if isinstance(tag, str):
|
||||||
misp_tag = MISPTag()
|
misp_tag = MISPTag()
|
||||||
|
@ -346,14 +351,14 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
self.edited = True
|
self.edited = True
|
||||||
return misp_tag
|
return misp_tag
|
||||||
|
|
||||||
def _set_tags(self, tags: List['MISPTag']):
|
def _set_tags(self, tags: list[MISPTag]) -> None:
|
||||||
"""Set a list of prepared MISPTag."""
|
"""Set a list of prepared MISPTag."""
|
||||||
if all(isinstance(x, MISPTag) for x in tags):
|
if all(isinstance(x, MISPTag) for x in tags):
|
||||||
self.Tag = tags
|
self.Tag = tags
|
||||||
else:
|
else:
|
||||||
raise PyMISPInvalidFormat('All the attributes have to be of type MISPTag.')
|
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):
|
if isinstance(other, AbstractMISP):
|
||||||
return self.to_dict() == other.to_dict()
|
return self.to_dict() == other.to_dict()
|
||||||
elif isinstance(other, dict):
|
elif isinstance(other, dict):
|
||||||
|
@ -362,62 +367,57 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
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):
|
class MISPTag(AbstractMISP):
|
||||||
|
|
||||||
_fields_for_feed: set = {'name', 'colour'}
|
_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)
|
super().__init__(**kwargs)
|
||||||
self.name: str
|
self.name: str
|
||||||
self.exportable: bool
|
self.exportable: bool
|
||||||
self.local: bool
|
self.local: bool
|
||||||
|
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'):
|
if kwargs.get('Tag'):
|
||||||
kwargs = kwargs.get('Tag')
|
kwargs = kwargs.get('Tag') # type: ignore[assignment]
|
||||||
super().from_dict(**kwargs)
|
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'):
|
if not hasattr(self, 'colour'):
|
||||||
self.colour = '#ffffff'
|
self.colour = '#ffffff'
|
||||||
|
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:
|
if hasattr(self, 'exportable') and not self.exportable:
|
||||||
return {}
|
return {}
|
||||||
if with_local is False and hasattr(self, 'local') and self.local:
|
if with_local is False and hasattr(self, 'local') and self.local:
|
||||||
return {}
|
return {}
|
||||||
return super()._to_feed()
|
return super()._to_feed()
|
||||||
|
|
||||||
def delete(self):
|
def delete(self) -> None:
|
||||||
self.deleted = True
|
self.deleted = True
|
||||||
self.edited = True
|
self.edited = True
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
if hasattr(self, 'name'):
|
if hasattr(self, 'name'):
|
||||||
return '<{self.__class__.__name__}(name={self.name})>'.format(self=self)
|
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:
|
# UUID, datetime, date and Enum is serialized by ORJSON by default
|
||||||
def pymisp_json_default(obj: Union[AbstractMISP, datetime, date, Enum, UUID]) -> Union[Dict, str]:
|
def pymisp_json_default(obj: AbstractMISP | datetime | date | Enum | UUID) -> dict[str, Any] | str:
|
||||||
if isinstance(obj, AbstractMISP):
|
if isinstance(obj, AbstractMISP):
|
||||||
return obj.jsonable()
|
return obj.jsonable()
|
||||||
elif isinstance(obj, (datetime, date)):
|
elif isinstance(obj, (datetime, date)):
|
||||||
return obj.isoformat()
|
return obj.isoformat()
|
||||||
elif isinstance(obj, Enum):
|
elif isinstance(obj, Enum):
|
||||||
return obj.value
|
return obj.value
|
||||||
elif isinstance(obj, UUID):
|
elif isinstance(obj, UUID):
|
||||||
return str(obj)
|
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)
|
|
||||||
|
|
1262
pymisp/api.py
1262
pymisp/api.py
File diff suppressed because it is too large
Load Diff
|
@ -545,6 +545,10 @@
|
||||||
"default_category": "Other",
|
"default_category": "Other",
|
||||||
"to_ids": 0
|
"to_ids": 0
|
||||||
},
|
},
|
||||||
|
"integer": {
|
||||||
|
"default_category": "Other",
|
||||||
|
"to_ids": 0
|
||||||
|
},
|
||||||
"datetime": {
|
"datetime": {
|
||||||
"default_category": "Other",
|
"default_category": "Other",
|
||||||
"to_ids": 0
|
"to_ids": 0
|
||||||
|
@ -891,6 +895,7 @@
|
||||||
"dns-soa-email",
|
"dns-soa-email",
|
||||||
"size-in-bytes",
|
"size-in-bytes",
|
||||||
"counter",
|
"counter",
|
||||||
|
"integer",
|
||||||
"datetime",
|
"datetime",
|
||||||
"port",
|
"port",
|
||||||
"ip-dst|port",
|
"ip-dst|port",
|
||||||
|
@ -1460,6 +1465,7 @@
|
||||||
"other",
|
"other",
|
||||||
"size-in-bytes",
|
"size-in-bytes",
|
||||||
"counter",
|
"counter",
|
||||||
|
"integer",
|
||||||
"datetime",
|
"datetime",
|
||||||
"cpe",
|
"cpe",
|
||||||
"port",
|
"port",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2787dc45d7efbf32e0fbe81ea95f0af642ae8963
|
Subproject commit e3288ef6e516624e3e335939a2b7fe4aef5ce510
|
|
@ -1,9 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
class PyMISPError(Exception):
|
class PyMISPError(Exception):
|
||||||
def __init__(self, message):
|
def __init__(self, message: str) -> None:
|
||||||
super(PyMISPError, self).__init__(message)
|
super().__init__(message)
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,22 @@ class NewEventReportError(PyMISPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewAnalystDataError(PyMISPError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewNoteError(PyMISPError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewOpinionError(PyMISPError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewRelationshipError(PyMISPError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UpdateAttributeError(PyMISPError):
|
class UpdateAttributeError(PyMISPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -51,17 +67,29 @@ class NoKey(PyMISPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MISPObjectException(PyMISPError):
|
class MISPAttributeException(PyMISPError):
|
||||||
pass
|
"""A base class for attribute specific exceptions"""
|
||||||
|
|
||||||
|
class MISPObjectException(PyMISPError):
|
||||||
|
"""A base class for object specific exceptions"""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidMISPAttribute(MISPAttributeException):
|
||||||
|
"""Exception raised when an attribute doesn't respect the constraints in the definition"""
|
||||||
|
|
||||||
|
class InvalidMISPObjectAttribute(MISPAttributeException):
|
||||||
|
"""Exception raised when an object attribute doesn't respect the constraints in the definition"""
|
||||||
|
|
||||||
class InvalidMISPObject(MISPObjectException):
|
class InvalidMISPObject(MISPObjectException):
|
||||||
"""Exception raised when an object doesn't respect the contrains in the definition"""
|
"""Exception raised when an object doesn't respect the constraints in the definition"""
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownMISPObjectTemplate(MISPObjectException):
|
class UnknownMISPObjectTemplate(MISPObjectException):
|
||||||
"""Exception raised when the template is unknown"""
|
"""Exception raised when the template is unknown"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidMISPGalaxy(PyMISPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
1146
pymisp/mispevent.py
1146
pymisp/mispevent.py
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from .vtreportobject import VTReportObject # noqa
|
from .vtreportobject import VTReportObject # noqa
|
||||||
from .neo4j import Neo4j # noqa
|
from .neo4j import Neo4j # noqa
|
||||||
from .fileobject import FileObject # noqa
|
from .fileobject import FileObject # noqa
|
||||||
|
@ -41,3 +43,13 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Requires lief, optional [fileobjects]
|
# Requires lief, optional [fileobjects]
|
||||||
pass
|
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
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import socket
|
import socket
|
||||||
import idna
|
import idna
|
||||||
from publicsuffixlist import PublicSuffixList # type: ignore
|
from publicsuffixlist import PublicSuffixList # type: ignore
|
||||||
from urllib.parse import urlparse, urlunparse
|
from urllib.parse import urlparse, urlunparse, ParseResult
|
||||||
|
|
||||||
|
|
||||||
class UrlNotDecoded(Exception):
|
class UrlNotDecoded(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PSLFaup(object):
|
class PSLFaup:
|
||||||
"""
|
"""
|
||||||
Fake Faup Python Library using PSL for Windows support
|
Fake Faup Python Library using PSL for Windows support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self.decoded = False
|
self.decoded = False
|
||||||
self.psl = PublicSuffixList()
|
self.psl = PublicSuffixList()
|
||||||
self._url = None
|
self._url: ParseResult | None = None
|
||||||
self._retval = {}
|
self._retval: dict[str, str | int | None | bytes] = {}
|
||||||
self.ip_as_host = False
|
self.ip_as_host = ''
|
||||||
|
|
||||||
def _clear(self):
|
def _clear(self) -> None:
|
||||||
self.decoded = False
|
self.decoded = False
|
||||||
self._url = None
|
self._url = None
|
||||||
self._retval = {}
|
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.
|
This function creates a dict of all the url fields.
|
||||||
:param url: The URL to normalize
|
:param url: The URL to normalize
|
||||||
|
@ -42,10 +43,15 @@ class PSLFaup(object):
|
||||||
url = '//' + url
|
url = '//' + url
|
||||||
self._url = urlparse(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)
|
hostname = _ensure_str(self._url.hostname)
|
||||||
try:
|
try:
|
||||||
ipv4_bytes = socket.inet_aton(_ensure_str(hostname))
|
ipv4_bytes = socket.inet_aton(hostname)
|
||||||
ipv4 = ipaddress.IPv4Address(ipv4_bytes)
|
ipv4 = ipaddress.IPv4Address(ipv4_bytes)
|
||||||
self.ip_as_host = ipv4.compressed
|
self.ip_as_host = ipv4.compressed
|
||||||
except (OSError, ValueError):
|
except (OSError, ValueError):
|
||||||
|
@ -60,61 +66,70 @@ class PSLFaup(object):
|
||||||
self._retval = {}
|
self._retval = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self) -> bytes | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
netloc = self.get_host() + ('' if self.get_port() is None else ':{}'.format(self.get_port()))
|
if host := self.get_host():
|
||||||
return _ensure_bytes(
|
netloc = host + ('' if self.get_port() is None else f':{self.get_port()}')
|
||||||
urlunparse(
|
return _ensure_bytes(
|
||||||
(self.get_scheme(), netloc, self.get_resource_path(),
|
urlunparse(
|
||||||
'', self.get_query_string(), self.get_fragment(),)
|
(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
|
Get the scheme of the url given in the decode function
|
||||||
:returns: The URL scheme
|
:returns: The URL scheme
|
||||||
"""
|
"""
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
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):
|
def get_credential(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
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)
|
return _ensure_str(self._url.username) + ':' + _ensure_str(self._url.password)
|
||||||
if self._url.username:
|
if self._url.username:
|
||||||
return _ensure_str(self._url.username)
|
return _ensure_str(self._url.username)
|
||||||
|
return None
|
||||||
|
|
||||||
def get_subdomain(self):
|
def get_subdomain(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
if self.get_host() is not None and not self.ip_as_host:
|
if self.get_host() is not None and not self.ip_as_host:
|
||||||
if self.get_domain() in self.get_host():
|
domain = self.get_domain()
|
||||||
return self.get_host().rsplit(self.get_domain(), 1)[0].rstrip('.') or None
|
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):
|
def get_domain(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
if self.get_host() is not None and not self.ip_as_host:
|
if self.get_host() is not None and not self.ip_as_host:
|
||||||
return self.psl.privatesuffix(self.get_host())
|
return self.psl.privatesuffix(self.get_host())
|
||||||
|
return None
|
||||||
|
|
||||||
def get_domain_without_tld(self):
|
def get_domain_without_tld(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
if self.get_tld() is not None and not self.ip_as_host:
|
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):
|
def get_host(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
if self._url.hostname is None:
|
if self._url.hostname is None:
|
||||||
|
@ -124,45 +139,48 @@ class PSLFaup(object):
|
||||||
else:
|
else:
|
||||||
return _ensure_str(idna.encode(self._url.hostname, uts46=True))
|
return _ensure_str(idna.encode(self._url.hostname, uts46=True))
|
||||||
|
|
||||||
def get_unicode_host(self):
|
def get_unicode_host(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
if not self.ip_as_host:
|
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):
|
def get_tld(self) -> str | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
if self.get_host() is not None and not self.ip_as_host:
|
if self.get_host() is not None and not self.ip_as_host:
|
||||||
return self.psl.publicsuffix(self.get_host())
|
return self.psl.publicsuffix(self.get_host())
|
||||||
|
return None
|
||||||
|
|
||||||
def get_port(self):
|
def get_port(self) -> int | None:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
return self._url.port
|
return self._url.port
|
||||||
|
|
||||||
def get_resource_path(self):
|
def get_resource_path(self) -> str:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
return _ensure_str(self._url.path)
|
return _ensure_str(self._url.path)
|
||||||
|
|
||||||
def get_query_string(self):
|
def get_query_string(self) -> str:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
return _ensure_str(self._url.query)
|
return _ensure_str(self._url.query)
|
||||||
|
|
||||||
def get_fragment(self):
|
def get_fragment(self) -> str:
|
||||||
if not self.decoded:
|
if not self.decoded or not self._url:
|
||||||
raise UrlNotDecoded("You must call faup.decode() first")
|
raise UrlNotDecoded("You must call faup.decode() first")
|
||||||
|
|
||||||
return _ensure_str(self._url.fragment)
|
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["scheme"] = self.get_scheme()
|
||||||
self._retval["tld"] = self.get_tld()
|
self._retval["tld"] = self.get_tld()
|
||||||
self._retval["domain"] = self.get_domain()
|
self._retval["domain"] = self.get_domain()
|
||||||
|
@ -177,14 +195,14 @@ class PSLFaup(object):
|
||||||
return self._retval
|
return self._retval
|
||||||
|
|
||||||
|
|
||||||
def _ensure_bytes(binary) -> bytes:
|
def _ensure_bytes(binary: str | bytes) -> bytes:
|
||||||
if isinstance(binary, bytes):
|
if isinstance(binary, bytes):
|
||||||
return binary
|
return binary
|
||||||
else:
|
else:
|
||||||
return binary.encode('utf-8')
|
return binary.encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def _ensure_str(string) -> str:
|
def _ensure_str(string: str | bytes) -> str:
|
||||||
if isinstance(string, str):
|
if isinstance(string, str):
|
||||||
return string
|
return string
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
#!/usr/bin/env python
|
#!/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 .. import MISPObject
|
||||||
from ..exceptions import InvalidMISPObject
|
from ..exceptions import InvalidMISPObject
|
||||||
from datetime import datetime, date
|
|
||||||
from dateutil.parser import parse
|
|
||||||
from typing import Union, Optional
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractMISPObjectGenerator(MISPObject):
|
class AbstractMISPObjectGenerator(MISPObject):
|
||||||
|
|
||||||
def _detect_epoch(self, timestamp: Union[str, int, float]) -> bool:
|
def _detect_epoch(self, timestamp: str | int | float) -> bool:
|
||||||
try:
|
try:
|
||||||
tmp = float(timestamp)
|
tmp = float(timestamp)
|
||||||
if tmp < 30000000:
|
if tmp < 30000000:
|
||||||
|
@ -21,7 +24,7 @@ class AbstractMISPObjectGenerator(MISPObject):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
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:
|
if not timestamp:
|
||||||
return datetime.now()
|
return datetime.now()
|
||||||
|
|
||||||
|
@ -42,9 +45,9 @@ class AbstractMISPObjectGenerator(MISPObject):
|
||||||
else:
|
else:
|
||||||
raise Exception(f'Unable to convert {timestamp} to a datetime.')
|
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"""
|
"""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']:
|
for object_relation in self._definition['attributes']:
|
||||||
value = self._parameters.pop(object_relation, None)
|
value = self._parameters.pop(object_relation, None)
|
||||||
if not value:
|
if not value:
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class ASNObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('asn', strict=strict, **kwargs)
|
||||||
self._parameters = parameters
|
self._parameters = parameters
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||||
self._parameters['first-seen'] = first
|
self._parameters['first-seen'] = first
|
||||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from typing import Any, TYPE_CHECKING
|
||||||
|
|
||||||
from . import FileObject
|
|
||||||
from ..exceptions import MISPObjectException
|
from ..exceptions import MISPObjectException
|
||||||
import logging
|
from . import FileObject
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import lief # type: ignore
|
import lief
|
||||||
|
import lief.logging
|
||||||
lief.logging.disable()
|
lief.logging.disable()
|
||||||
HAS_LIEF = True
|
HAS_LIEF = True
|
||||||
|
|
||||||
from .peobject import make_pe_objects
|
from .peobject import make_pe_objects
|
||||||
from .elfobject import make_elf_objects
|
from .elfobject import make_elf_objects
|
||||||
from .machoobject import make_macho_objects
|
from .machoobject import make_macho_objects
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
HAS_LIEF = False
|
HAS_LIEF = False
|
||||||
logger.critical('You need lief >= 0.11.0. The quick and dirty fix is: pip3 install --force pymisp[fileobjects]')
|
logger.critical('You need lief >= 0.11.0. The quick and dirty fix is: pip3 install --force pymisp[fileobjects]')
|
||||||
|
@ -26,49 +27,43 @@ except AttributeError:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_LIEF = False
|
HAS_LIEF = False
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import PEObject, ELFObject, MachOObject, PESectionObject, ELFSectionObject, MachOSectionObject
|
||||||
|
|
||||||
|
|
||||||
class FileTypeNotImplemented(MISPObjectException):
|
class FileTypeNotImplemented(MISPObjectException):
|
||||||
pass
|
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,
|
misp_file = FileObject(filepath=filepath, pseudofile=pseudofile, filename=filename,
|
||||||
standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||||
if HAS_LIEF and (filepath or (pseudofile and filename)):
|
if HAS_LIEF and (filepath or pseudofile):
|
||||||
try:
|
if filepath:
|
||||||
if filepath:
|
lief_parsed = lief.parse(filepath=filepath)
|
||||||
lief_parsed = lief.parse(filepath=filepath)
|
elif pseudofile:
|
||||||
elif pseudofile and filename:
|
if isinstance(pseudofile, bytes):
|
||||||
lief_parsed = lief.parse(raw=pseudofile.getvalue(), name=filename)
|
lief_parsed = lief.parse(raw=pseudofile)
|
||||||
else:
|
else: # BytesIO
|
||||||
logger.critical('You need either a filepath, or a pseudofile and a filename.')
|
lief_parsed = lief.parse(obj=pseudofile)
|
||||||
lief_parsed = None
|
else:
|
||||||
if isinstance(lief_parsed, lief.PE.Binary):
|
logger.critical('You need either a filepath, or a pseudofile and a filename.')
|
||||||
return make_pe_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
lief_parsed = None
|
||||||
elif isinstance(lief_parsed, lief.ELF.Binary):
|
|
||||||
return make_elf_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
if isinstance(lief_parsed, lief.lief_errors):
|
||||||
elif isinstance(lief_parsed, lief.MachO.Binary):
|
logger.warning('Got an error parsing the file: {lief_parsed}')
|
||||||
return make_macho_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
elif isinstance(lief_parsed, lief.PE.Binary):
|
||||||
except lief.bad_format as e:
|
return make_pe_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||||
logger.warning('Bad format: {}'.format(e))
|
elif isinstance(lief_parsed, lief.ELF.Binary):
|
||||||
except lief.bad_file as e:
|
return make_elf_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||||
logger.warning('Bad file: {}'.format(e))
|
elif isinstance(lief_parsed, lief.MachO.Binary):
|
||||||
except lief.conversion_error as e:
|
return make_macho_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||||
logger.warning('Conversion file: {}'.format(e))
|
else:
|
||||||
except lief.builder_error as e:
|
logger.critical(f'Unexpected type from lief: {type(lief_parsed)}')
|
||||||
logger.warning('Builder file: {}'.format(e))
|
|
||||||
except lief.parser_error as e:
|
|
||||||
logger.warning('Parser error: {}'.format(e))
|
|
||||||
except lief.integrity_error as e:
|
|
||||||
logger.warning('Integrity error: {}'.format(e))
|
|
||||||
except lief.pe_error as e:
|
|
||||||
logger.warning('PE error: {}'.format(e))
|
|
||||||
except lief.type_error as e:
|
|
||||||
logger.warning('Type error: {}'.format(e))
|
|
||||||
except lief.exception as e:
|
|
||||||
logger.warning('Lief exception: {}'.format(e))
|
|
||||||
except FileTypeNotImplemented as e:
|
|
||||||
logger.warning(e)
|
|
||||||
if not HAS_LIEF:
|
if not HAS_LIEF:
|
||||||
logger.warning('Please install lief, documentation here: https://github.com/lief-project/LIEF')
|
logger.warning('Please install lief, documentation here: https://github.com/lief-project/LIEF')
|
||||||
return misp_file, None, []
|
return misp_file, None, []
|
||||||
|
|
|
@ -1,49 +1,55 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
from pymisp import MISPObject
|
from pymisp import MISPObject
|
||||||
|
|
||||||
|
|
||||||
class CSVLoader():
|
class CSVLoader():
|
||||||
|
|
||||||
def __init__(self, template_name: str, csv_path: Path, fieldnames: list = [], has_fieldnames=False,
|
def __init__(self, template_name: str, csv_path: Path,
|
||||||
delimiter: str = ',', quotechar: str = '"'):
|
fieldnames: list[str] | None = None, has_fieldnames: bool=False,
|
||||||
|
delimiter: str = ',', quotechar: str = '"') -> None:
|
||||||
self.template_name = template_name
|
self.template_name = template_name
|
||||||
self.delimiter = delimiter
|
self.delimiter = delimiter
|
||||||
self.quotechar = quotechar
|
self.quotechar = quotechar
|
||||||
self.csv_path = csv_path
|
self.csv_path = csv_path
|
||||||
self.fieldnames = [f.strip().lower() for f in fieldnames]
|
self.fieldnames = []
|
||||||
|
if fieldnames:
|
||||||
|
self.fieldnames = [f.strip() for f in fieldnames]
|
||||||
if not self.fieldnames:
|
if not self.fieldnames:
|
||||||
# If the user doesn't pass fieldnames, we assume the CSV has them.
|
# If the user doesn't pass fieldnames, they must be in the CSV.
|
||||||
self.has_fieldnames = True
|
self.has_fieldnames = True
|
||||||
else:
|
else:
|
||||||
self.has_fieldnames = has_fieldnames
|
self.has_fieldnames = has_fieldnames
|
||||||
|
|
||||||
def load(self):
|
def load(self) -> list[MISPObject]:
|
||||||
|
|
||||||
objects = []
|
objects = []
|
||||||
|
|
||||||
with open(self.csv_path, newline='') as csvfile:
|
with open(self.csv_path, newline='') as csvfile:
|
||||||
reader = csv.reader(csvfile, delimiter=self.delimiter, quotechar=self.quotechar)
|
reader = csv.reader(csvfile, delimiter=self.delimiter, quotechar=self.quotechar)
|
||||||
if self.has_fieldnames:
|
if self.has_fieldnames:
|
||||||
# The file has fieldnames, we either ignore it, or validate its validity
|
# The file has fieldnames, we either ignore it, or use them as object-relation
|
||||||
fieldnames = [f.strip().lower() for f in reader.__next__()]
|
fieldnames = [f.strip() for f in reader.__next__()]
|
||||||
if not self.fieldnames:
|
if not self.fieldnames:
|
||||||
self.fieldnames = fieldnames
|
self.fieldnames = fieldnames
|
||||||
|
|
||||||
if not self.fieldnames:
|
if not self.fieldnames:
|
||||||
raise Exception('No fieldnames, impossible to create objects.')
|
raise Exception('No fieldnames, impossible to create objects.')
|
||||||
else:
|
|
||||||
# Check if the CSV file has a header, and if it matches with the object template
|
# Check if the CSV file has a header, and if it matches with the object template
|
||||||
tmp_object = MISPObject(self.template_name)
|
tmp_object = MISPObject(self.template_name)
|
||||||
if not tmp_object._definition['attributes']:
|
|
||||||
raise Exception(f'Unable to find the object template ({self.template_name}), impossible to create objects.')
|
if not tmp_object._definition or not tmp_object._definition['attributes']:
|
||||||
allowed_fieldnames = list(tmp_object._definition['attributes'].keys())
|
raise Exception(f'Unable to find the object template ({self.template_name}), impossible to create objects.')
|
||||||
for fieldname in self.fieldnames:
|
allowed_fieldnames = list(tmp_object._definition['attributes'].keys())
|
||||||
if fieldname not in allowed_fieldnames:
|
for fieldname in self.fieldnames:
|
||||||
raise Exception(f'{fieldname} is not a valid object relation for {self.template_name}: {allowed_fieldnames}')
|
if fieldname not in allowed_fieldnames:
|
||||||
|
raise Exception(f'{fieldname} is not a valid object relation for {self.template_name}: {allowed_fieldnames}')
|
||||||
|
|
||||||
for row in reader:
|
for row in reader:
|
||||||
tmp_object = MISPObject(self.template_name)
|
tmp_object = MISPObject(self.template_name)
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class DomainIPObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('domain-ip', strict=strict, **kwargs)
|
||||||
self._parameters = parameters
|
self._parameters = parameters
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||||
self._parameters['first-seen'] = first
|
self._parameters['first-seen'] = first
|
||||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
#!/usr/bin/env python3
|
#!/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 .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
from ..exceptions import InvalidMISPObject
|
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 # type: ignore
|
import lief
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pydeep # type: ignore
|
import pydeep # type: ignore
|
||||||
|
@ -21,7 +24,10 @@ except ImportError:
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
def make_elf_objects(lief_parsed: lief.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)
|
elf_object = ELFObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||||
misp_file.add_reference(elf_object.uuid, 'includes', 'ELF indicators')
|
misp_file.add_reference(elf_object.uuid, 'includes', 'ELF indicators')
|
||||||
elf_sections = []
|
elf_sections = []
|
||||||
|
@ -32,29 +38,39 @@ def make_elf_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalone
|
||||||
|
|
||||||
class ELFObject(AbstractMISPObjectGenerator):
|
class ELFObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
def __init__(self, parsed: Optional[lief.ELF.Binary] = None, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[Union[BytesIO, bytes]] = 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"""
|
"""Creates an ELF object, with lief"""
|
||||||
super().__init__('elf', **kwargs)
|
super().__init__('elf', **kwargs)
|
||||||
if not HAS_PYDEEP:
|
if not HAS_PYDEEP:
|
||||||
logger.warning("Please install pydeep: pip install git+https://github.com/kbandla/pydeep.git")
|
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||||
if pseudofile:
|
if pseudofile:
|
||||||
if isinstance(pseudofile, BytesIO):
|
if isinstance(pseudofile, BytesIO):
|
||||||
self.__elf = lief.ELF.parse(raw=pseudofile.getvalue())
|
e = lief.ELF.parse(obj=pseudofile)
|
||||||
elif isinstance(pseudofile, bytes):
|
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:
|
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:
|
elif filepath:
|
||||||
self.__elf = lief.ELF.parse(filepath)
|
if e := lief.ELF.parse(filepath):
|
||||||
|
self.__elf = e
|
||||||
elif parsed:
|
elif parsed:
|
||||||
# Got an already parsed blob
|
# Got an already parsed blob
|
||||||
if isinstance(parsed, lief.ELF.Binary):
|
if isinstance(parsed, lief.ELF.Binary):
|
||||||
self.__elf = parsed
|
self.__elf = parsed
|
||||||
else:
|
else:
|
||||||
raise InvalidMISPObject('Not a lief.ELF.Binary: {}'.format(type(parsed)))
|
raise InvalidMISPObject(f'Not a lief.ELF.Binary: {type(parsed)}')
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
# General information
|
# General information
|
||||||
self.add_attribute('type', value=str(self.__elf.header.file_type).split('.')[1])
|
self.add_attribute('type', value=str(self.__elf.header.file_type).split('.')[1])
|
||||||
self.add_attribute('entrypoint-address', value=self.__elf.entrypoint)
|
self.add_attribute('entrypoint-address', value=self.__elf.entrypoint)
|
||||||
|
@ -68,7 +84,7 @@ class ELFObject(AbstractMISPObjectGenerator):
|
||||||
if not section.name:
|
if not section.name:
|
||||||
continue
|
continue
|
||||||
s = ELFSectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
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
|
pos += 1
|
||||||
self.sections.append(s)
|
self.sections.append(s)
|
||||||
self.add_attribute('number-sections', value=len(self.sections))
|
self.add_attribute('number-sections', value=len(self.sections))
|
||||||
|
@ -76,22 +92,22 @@ class ELFObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
class ELFSectionObject(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."""
|
"""Creates an ELF Section object. Object generated by ELFObject."""
|
||||||
# Python3 way
|
# Python3 way
|
||||||
# super().__init__('pe-section')
|
# super().__init__('pe-section')
|
||||||
super(ELFSectionObject, self).__init__('elf-section', **kwargs)
|
super().__init__('elf-section', **kwargs)
|
||||||
self.__section = section
|
self.__section = section
|
||||||
self.__data = bytes(self.__section.content)
|
self.__data = bytes(self.__section.content)
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('name', value=self.__section.name)
|
self.add_attribute('name', value=self.__section.name)
|
||||||
self.add_attribute('type', value=str(self.__section.type).split('.')[1])
|
self.add_attribute('type', value=str(self.__section.type).split('.')[1])
|
||||||
for flag in self.__section.flags_list:
|
for flag in self.__section.flags_list:
|
||||||
self.add_attribute('flag', value=str(flag).split('.')[1])
|
self.add_attribute('flag', value=str(flag).split('.')[1])
|
||||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||||
if int(size.value) > 0:
|
if int(self.__section.size) > 0:
|
||||||
self.add_attribute('entropy', value=self.__section.entropy)
|
self.add_attribute('entropy', value=self.__section.entropy)
|
||||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
@ -9,15 +10,17 @@ from email import policy, message_from_bytes
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
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 import openMsg
|
||||||
from extract_msg.message import Message as MsgObj
|
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.exceptions import MalformedEncapsulatedRtf, NotEncapsulatedRtf # type: ignore
|
||||||
from RTFDE.deencapsulate import DeEncapsulator # type: ignore
|
from RTFDE.deencapsulate import DeEncapsulator # type: ignore
|
||||||
from oletools.common.codepages import codepage2codec # 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
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
@ -28,15 +31,14 @@ class MISPMsgConverstionError(MISPObjectException):
|
||||||
|
|
||||||
|
|
||||||
class EMailObject(AbstractMISPObjectGenerator):
|
class EMailObject(AbstractMISPObjectGenerator):
|
||||||
def __init__(self, filepath: Optional[Union[Path, str]]=None, pseudofile: Optional[BytesIO]=None,
|
def __init__(self, filepath: Path | str | None=None, pseudofile: BytesIO | bytes | None=None, # type: ignore[no-untyped-def]
|
||||||
attach_original_email: bool = True, **kwargs):
|
attach_original_email: bool = True, **kwargs) -> None:
|
||||||
super().__init__('email', **kwargs)
|
super().__init__('email', **kwargs)
|
||||||
|
|
||||||
self.attach_original_email = attach_original_email
|
self.attach_original_email = attach_original_email
|
||||||
self.encapsulated_body: Union[str, None] = None
|
self.encapsulated_body: str | None = None
|
||||||
self.eml_from_msg: Union[bool, None] = None
|
self.eml_from_msg: bool | None = None
|
||||||
self.raw_emails: Dict[str, Union[BytesIO, None]] = {'msg': None,
|
self.raw_emails: dict[str, BytesIO | None] = {'msg': None, 'eml': None}
|
||||||
'eml': None}
|
|
||||||
|
|
||||||
self.__pseudofile = self.create_pseudofile(filepath, pseudofile)
|
self.__pseudofile = self.create_pseudofile(filepath, pseudofile)
|
||||||
self.email = self.parse_email()
|
self.email = self.parse_email()
|
||||||
|
@ -65,7 +67,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
return message
|
return message
|
||||||
except ValueError as _e: # Exception
|
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("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:
|
try:
|
||||||
if content_in_bytes[:3] == b'\xef\xbb\xbf': # utf-8-sig byte-order mark (BOM)
|
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")
|
eml_bytes = content_in_bytes.decode("utf_8_sig").encode("utf-8")
|
||||||
|
@ -77,11 +79,11 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
return eml
|
return eml
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
pass
|
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
|
@staticmethod
|
||||||
def create_pseudofile(filepath: Optional[Union[Path, str]] = None,
|
def create_pseudofile(filepath: Path | str | None = None,
|
||||||
pseudofile: Optional[BytesIO] = None) -> BytesIO:
|
pseudofile: BytesIO | bytes | None = None) -> BytesIO:
|
||||||
"""Creates a pseudofile using directly passed data or data loaded from file path.
|
"""Creates a pseudofile using directly passed data or data loaded from file path.
|
||||||
"""
|
"""
|
||||||
if filepath:
|
if filepath:
|
||||||
|
@ -89,18 +91,21 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
return BytesIO(f.read())
|
return BytesIO(f.read())
|
||||||
elif pseudofile and isinstance(pseudofile, BytesIO):
|
elif pseudofile and isinstance(pseudofile, BytesIO):
|
||||||
return pseudofile
|
return pseudofile
|
||||||
|
elif pseudofile and isinstance(pseudofile, bytes):
|
||||||
|
return BytesIO(pseudofile)
|
||||||
else:
|
else:
|
||||||
raise InvalidMISPObject('File buffer (BytesIO) or a path is required.')
|
raise InvalidMISPObject('File buffer (BytesIO) or a path is required.')
|
||||||
|
|
||||||
def _msg_to_eml(self, msg_bytes: bytes) -> EmailMessage:
|
def _msg_to_eml(self, msg_bytes: bytes) -> EmailMessage:
|
||||||
"""Converts a msg into an eml."""
|
"""Converts a msg into an eml."""
|
||||||
msg_obj = openMsg(msg_bytes)
|
# NOTE: openMsg returns a MessageBase, not a MSGFile
|
||||||
|
msg_obj: MessageBase = openMsg(msg_bytes) # type: ignore
|
||||||
# msg obj stores the original raw header here
|
# msg obj stores the original raw header here
|
||||||
message, body, attachments = self._extract_msg_objects(msg_obj)
|
message, body, attachments = self._extract_msg_objects(msg_obj)
|
||||||
eml = self._build_eml(message, body, attachments)
|
eml = self._build_eml(message, body, attachments)
|
||||||
return eml
|
return eml
|
||||||
|
|
||||||
def _extract_msg_objects(self, msg_obj: MsgObj) -> 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."""
|
"""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
|
message: EmailMessage = email.message_from_string(msg_obj.header.as_string(), policy=policy.default) # type: ignore
|
||||||
body = {}
|
body = {}
|
||||||
|
@ -111,8 +116,11 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
"cte": "base64"}
|
"cte": "base64"}
|
||||||
if msg_obj.htmlBody is not None:
|
if msg_obj.htmlBody is not None:
|
||||||
try:
|
try:
|
||||||
_html_encoding_raw = msg_obj.props['3FDE0003'].value
|
if isinstance(msg_obj.props['3FDE0003'], FixedLengthProp):
|
||||||
_html_encoding = codepage2codec(_html_encoding_raw)
|
_html_encoding_raw = msg_obj.props['3FDE0003'].value
|
||||||
|
_html_encoding = codepage2codec(_html_encoding_raw)
|
||||||
|
else:
|
||||||
|
_html_encoding = msg_obj.stringEncoding
|
||||||
except KeyError:
|
except KeyError:
|
||||||
_html_encoding = msg_obj.stringEncoding
|
_html_encoding = msg_obj.stringEncoding
|
||||||
body['html'] = {'obj': msg_obj.htmlBody.decode(),
|
body['html'] = {'obj': msg_obj.htmlBody.decode(),
|
||||||
|
@ -145,17 +153,16 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
attachments = msg_obj.attachments
|
attachments = msg_obj.attachments
|
||||||
return message, body, 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."""
|
"""Constructs an eml file from objects extracted from a msg."""
|
||||||
# Order the body objects by increasing complexity and toss any missing objects
|
# Order the body objects by increasing complexity and toss any missing objects
|
||||||
body_objects: List[dict] = [body.get('text', {}),
|
body_objects: list[dict[str, Any]] = [i for i in [body.get('text'),
|
||||||
body.get('html', {}),
|
body.get('html'),
|
||||||
body.get('rtf', {})]
|
body.get('rtf')] if i is not None]
|
||||||
body_objects = [i for i in body_objects if i != {}]
|
|
||||||
# If this a non-multipart email then we only need to attach the payload
|
# If this a non-multipart email then we only need to attach the payload
|
||||||
if message.get_content_maintype() != 'multipart':
|
if message.get_content_maintype() != 'multipart':
|
||||||
for _body in body_objects:
|
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)
|
message.set_content(**_body)
|
||||||
return message
|
return message
|
||||||
raise MISPMsgConverstionError("Unable to find appropriate eml payload in message body.")
|
raise MISPMsgConverstionError("Unable to find appropriate eml payload in message body.")
|
||||||
|
@ -167,8 +174,8 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
if isinstance(body.get('html', None), dict):
|
if isinstance(body.get('html', None), dict):
|
||||||
_html = body.get('html', {}).get('obj')
|
_html = body.get('html', {}).get('obj')
|
||||||
for attch in attachments:
|
for attch in attachments:
|
||||||
if _html.find("cid:{0}".format(attch.cid)) != -1:
|
if _html.find(f"cid:{attch.cid}") != -1:
|
||||||
_content_type = attch._getStringStream('__substg1.0_370E')
|
_content_type = attch.getStringStream('__substg1.0_370E')
|
||||||
maintype, subtype = _content_type.split("/", 1)
|
maintype, subtype = _content_type.split("/", 1)
|
||||||
related_content[attch.cid] = (attch,
|
related_content[attch.cid] = (attch,
|
||||||
{'obj': attch.data,
|
{'obj': attch.data,
|
||||||
|
@ -189,11 +196,19 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
for mime_items in related_content.values():
|
for mime_items in related_content.values():
|
||||||
if isinstance(mime_items[1], dict):
|
if isinstance(mime_items[1], dict):
|
||||||
message.add_related(**mime_items[1])
|
message.add_related(**mime_items[1])
|
||||||
cur_attach = message.get_payload()[-1]
|
if p := message.get_payload():
|
||||||
|
if isinstance(p, list):
|
||||||
|
cur_attach = p[-1]
|
||||||
|
else:
|
||||||
|
cur_attach = p
|
||||||
self._update_content_disp_properties(mime_items[0], cur_attach)
|
self._update_content_disp_properties(mime_items[0], cur_attach)
|
||||||
if body.get('text', None):
|
if body.get('text', None):
|
||||||
# Now add the HTML as an alternative within the related obj
|
# Now add the HTML as an alternative within the related obj
|
||||||
related = message.get_payload()[0]
|
if p := message.get_payload():
|
||||||
|
if isinstance(p, list):
|
||||||
|
related = p[0]
|
||||||
|
else:
|
||||||
|
related = p
|
||||||
related.add_alternative(**body.get('html'))
|
related.add_alternative(**body.get('html'))
|
||||||
else:
|
else:
|
||||||
for mime_dict in body_objects:
|
for mime_dict in body_objects:
|
||||||
|
@ -205,20 +220,25 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
message.add_alternative(**mime_dict)
|
message.add_alternative(**mime_dict)
|
||||||
for attch in attachments: # Add attachments at the end.
|
for attch in attachments: # Add attachments at the end.
|
||||||
if attch.cid not in related_content.keys():
|
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)
|
maintype, subtype = _content_type.split("/", 1)
|
||||||
message.add_attachment(attch.data,
|
message.add_attachment(attch.data,
|
||||||
maintype=maintype,
|
maintype=maintype,
|
||||||
subtype=subtype,
|
subtype=subtype,
|
||||||
cid=attch.cid,
|
cid=attch.cid,
|
||||||
filename=attch.longFilename)
|
filename=attch.longFilename)
|
||||||
cur_attach = message.get_payload()[-1]
|
if p := message.get_payload():
|
||||||
|
if isinstance(p, list):
|
||||||
|
cur_attach = p[-1]
|
||||||
|
else:
|
||||||
|
cur_attach = p
|
||||||
self._update_content_disp_properties(attch, cur_attach)
|
self._update_content_disp_properties(attch, cur_attach)
|
||||||
message.set_boundary(_orig_boundry) # Set back original boundary
|
if _orig_boundry is not None:
|
||||||
|
message.set_boundary(_orig_boundry) # Set back original boundary
|
||||||
return message
|
return message
|
||||||
|
|
||||||
@staticmethod
|
@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
|
"""Set Content-Disposition params on binary eml objects
|
||||||
|
|
||||||
You currently have to set non-filename content-disp params by hand in python.
|
You currently have to set non-filename content-disp params by hand in python.
|
||||||
|
@ -228,14 +248,14 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
for num, name in attch_cont_disp_props.items():
|
for num, name in attch_cont_disp_props.items():
|
||||||
try:
|
try:
|
||||||
eml_attch.set_param(name,
|
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')
|
header='Content-Disposition')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# It's fine if they don't have those values
|
# It's fine if they don't have those values
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attachments(self) -> List[Tuple[str, BytesIO]]:
|
def attachments(self) -> list[tuple[str | None, BytesIO]]:
|
||||||
to_return = []
|
to_return = []
|
||||||
try:
|
try:
|
||||||
for attachment in self.email.iter_attachments():
|
for attachment in self.email.iter_attachments():
|
||||||
|
@ -249,7 +269,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
pass
|
pass
|
||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
|
|
||||||
# Attach original & Converted
|
# Attach original & Converted
|
||||||
if self.attach_original_email is not None:
|
if self.attach_original_email is not None:
|
||||||
|
@ -261,21 +281,30 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
data=self.raw_emails.get('msg'))
|
data=self.raw_emails.get('msg'))
|
||||||
|
|
||||||
message = self.email
|
message = self.email
|
||||||
|
body: EmailMessage
|
||||||
|
|
||||||
for _pref, body in message._find_body(message, preferencelist=['plain', 'html']):
|
if body := message.get_body(preferencelist=['plain']):
|
||||||
comment = "{0} body".format(body.get_content_type())
|
comment = f"{body.get_content_type()} body"
|
||||||
if self.encapsulated_body == body.get_content_type():
|
if self.encapsulated_body == body.get_content_type():
|
||||||
comment += " De-Encapsulated from RTF in original msg."
|
comment += " De-Encapsulated from RTF in original msg."
|
||||||
self.add_attribute("email-body",
|
self.add_attribute("email-body",
|
||||||
body.get_content(),
|
body.get_content(),
|
||||||
comment=comment)
|
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:
|
if headers:
|
||||||
self.add_attribute("header", "\n".join(headers))
|
self.add_attribute("header", "\n".join(headers))
|
||||||
|
|
||||||
if "Date" in message and message.get('date').datetime is not None:
|
if "Date" in message and message['date'].datetime is not None:
|
||||||
self.add_attribute("send-date", message.get('date').datetime)
|
self.add_attribute("send-date", message['date'].datetime)
|
||||||
|
|
||||||
if "To" in message:
|
if "To" in message:
|
||||||
self.__add_emails("to", message["To"])
|
self.__add_emails("to", message["To"])
|
||||||
|
@ -319,31 +348,32 @@ class EMailObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
self.__generate_received()
|
self.__generate_received()
|
||||||
|
|
||||||
def __add_emails(self, typ: str, data: str, insert_display_names: bool = True):
|
def __add_emails(self, typ: str, data: str, insert_display_names: bool = True) -> None:
|
||||||
addresses = []
|
addresses: list[dict[str, str]] = []
|
||||||
display_names = []
|
display_names: list[dict[str, str]] = []
|
||||||
|
|
||||||
for realname, address in email.utils.getaddresses([data]):
|
for realname, address in email.utils.getaddresses([data]):
|
||||||
if address and realname:
|
if address and realname:
|
||||||
addresses.append({"value": address, "comment": "{} <{}>".format(realname, address)})
|
addresses.append({"value": address, "comment": f"{realname} <{address}>"})
|
||||||
elif address:
|
elif address:
|
||||||
addresses.append({"value": address})
|
addresses.append({"value": address})
|
||||||
else: # parsing failed, skip
|
else: # parsing failed, skip
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if realname:
|
if realname:
|
||||||
display_names.append({"value": realname, "comment": "{} <{}>".format(realname, address)})
|
display_names.append({"value": realname, "comment": f"{realname} <{address}>"})
|
||||||
|
|
||||||
if addresses:
|
for a in addresses:
|
||||||
self.add_attributes(typ, *addresses)
|
self.add_attribute(typ, **a)
|
||||||
if insert_display_names and display_names:
|
if insert_display_names and display_names:
|
||||||
try:
|
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:
|
except NewAttributeError:
|
||||||
# email object doesn't support display name for all email addrs
|
# email object doesn't support display name for all email addrs
|
||||||
pass
|
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.
|
Extract IP addresses from received headers that are not private. Also extract hostnames or domains.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pymispgalaxies import Clusters # type: ignore
|
from pymispgalaxies import Clusters # type: ignore
|
||||||
|
@ -14,7 +15,7 @@ except ImportError:
|
||||||
has_pymispgalaxies = False
|
has_pymispgalaxies = False
|
||||||
|
|
||||||
|
|
||||||
def revert_tag_from_galaxies(tag):
|
def revert_tag_from_galaxies(tag: str) -> list[str]:
|
||||||
clusters = Clusters()
|
clusters = Clusters()
|
||||||
try:
|
try:
|
||||||
return clusters.revert_machinetag(tag)
|
return clusters.revert_machinetag(tag)
|
||||||
|
@ -22,7 +23,7 @@ def revert_tag_from_galaxies(tag):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def revert_tag_from_taxonomies(tag):
|
def revert_tag_from_taxonomies(tag: str) -> list[str]:
|
||||||
taxonomies = Taxonomies()
|
taxonomies = Taxonomies()
|
||||||
try:
|
try:
|
||||||
return taxonomies.revert_machinetag(tag)
|
return taxonomies.revert_machinetag(tag)
|
||||||
|
@ -30,7 +31,7 @@ def revert_tag_from_taxonomies(tag):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def search_taxonomies(query):
|
def search_taxonomies(query: str) -> list[str]:
|
||||||
taxonomies = Taxonomies()
|
taxonomies = Taxonomies()
|
||||||
found = taxonomies.search(query)
|
found = taxonomies.search(query)
|
||||||
if not found:
|
if not found:
|
||||||
|
@ -38,6 +39,6 @@ def search_taxonomies(query):
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
|
||||||
def search_galaxies(query):
|
def search_galaxies(query: str) -> list[str]:
|
||||||
clusters = Clusters()
|
clusters = Clusters()
|
||||||
return clusters.search(query)
|
return clusters.search(query)
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class Fail2BanObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('fail2ban', strict=strict, **kwargs)
|
||||||
self._parameters = parameters
|
self._parameters = parameters
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
timestamp = self._sanitize_timestamp(self._parameters.pop('processing-timestamp', None))
|
timestamp = self._sanitize_timestamp(self._parameters.pop('processing-timestamp', None))
|
||||||
self._parameters['processing-timestamp'] = timestamp
|
self._parameters['processing-timestamp'] = timestamp
|
||||||
super().generate_attributes()
|
super().generate_attributes()
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pymisp import MISPEvent
|
from pymisp import MISPEvent
|
||||||
import json
|
import json
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
def feed_meta_generator(path: Path):
|
def feed_meta_generator(path: Path) -> None:
|
||||||
manifests = {}
|
manifests = {}
|
||||||
hashes: List[str] = []
|
hashes: list[str] = []
|
||||||
|
|
||||||
for f_name in path.glob('*.json'):
|
for f_name in path.glob('*.json'):
|
||||||
if str(f_name.name) == 'manifest.json':
|
if str(f_name.name) == 'manifest.json':
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from ..exceptions import InvalidMISPObject
|
from ..exceptions import InvalidMISPObject
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
|
@ -10,7 +11,6 @@ import math
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union, Optional
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ except ImportError:
|
||||||
HAS_PYDEEP = False
|
HAS_PYDEEP = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import magic # type: ignore
|
import magic
|
||||||
HAS_MAGIC = True
|
HAS_MAGIC = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_MAGIC = False
|
HAS_MAGIC = False
|
||||||
|
@ -30,12 +30,14 @@ except ImportError:
|
||||||
|
|
||||||
class FileObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('file', **kwargs)
|
||||||
if not HAS_PYDEEP:
|
if not HAS_PYDEEP:
|
||||||
logger.warning("Please install pydeep: pip install git+https://github.com/kbandla/pydeep.git")
|
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||||
if not HAS_MAGIC:
|
if not HAS_MAGIC:
|
||||||
logger.warning("Please install python-magic: pip install python-magic.")
|
logger.warning("python-magic is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||||
if filename:
|
if filename:
|
||||||
# Useful in case the file is copied with a pre-defined name by a script but we want to keep the original name
|
# Useful in case the file is copied with a pre-defined name by a script but we want to keep the original name
|
||||||
self.__filename = filename
|
self.__filename = filename
|
||||||
|
@ -55,10 +57,10 @@ class FileObject(AbstractMISPObjectGenerator):
|
||||||
self.__data = self.__pseudofile.getvalue()
|
self.__data = self.__pseudofile.getvalue()
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('filename', value=self.__filename)
|
self.add_attribute('filename', value=self.__filename)
|
||||||
size = self.add_attribute('size-in-bytes', value=len(self.__data))
|
self.add_attribute('size-in-bytes', value=len(self.__data))
|
||||||
if int(size.value) > 0:
|
if len(self.__data) > 0:
|
||||||
self.add_attribute('entropy', value=self.__entropy_H(self.__data))
|
self.add_attribute('entropy', value=self.__entropy_H(self.__data))
|
||||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
class GenericObjectGenerator(AbstractMISPObjectGenerator):
|
class GenericObjectGenerator(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
# FIXME: this method is different from the master one, and that's probably not a good idea.
|
# 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.
|
"""Generates MISPObjectAttributes from a list of dictionaries.
|
||||||
Each entry if the list must be in one of the two following formats:
|
Each entry if the list must be in one of the two following formats:
|
||||||
* {<object_relation>: <value>}
|
* {<object_relation>: <value>}
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class GeolocationObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('geolocation', strict=strict, **kwargs)
|
||||||
self._parameters = parameters
|
self._parameters = parameters
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||||
self._parameters['first-seen'] = first
|
self._parameters['first-seen'] = first
|
||||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class GitVulnFinderObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('git-vuln-finder', strict=strict, **kwargs)
|
||||||
self._parameters = parameters
|
self._parameters = parameters
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
authored_date = self._sanitize_timestamp(self._parameters.pop('authored_date', None))
|
authored_date = self._sanitize_timestamp(self._parameters.pop('authored_date', None))
|
||||||
self._parameters['authored_date'] = authored_date
|
self._parameters['authored_date'] = authored_date
|
||||||
committed_date = self._sanitize_timestamp(self._parameters.pop('committed_date', None))
|
committed_date = self._sanitize_timestamp(self._parameters.pop('committed_date', None))
|
||||||
|
|
|
@ -1,26 +1,30 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..api import PyMISP
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pymispwarninglists import WarningLists # type: ignore
|
from pymispwarninglists import WarningLists, WarningList # type: ignore
|
||||||
has_pymispwarninglists = True
|
has_pymispwarninglists = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_pymispwarninglists = False
|
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
|
"""Load the warnindlist from an existing MISP instance
|
||||||
:pymisp_instance: Already instantialized PyMISP instance."""
|
:pymisp_instance: Already instantialized PyMISP instance."""
|
||||||
|
|
||||||
warninglists_index = pymisp_instance.get_warninglists()['Warninglists']
|
warninglists_index = pymisp_instance.warninglists(pythonify=True)
|
||||||
all_warningslists = []
|
all_warningslists = []
|
||||||
for warninglist in warninglists_index:
|
for warninglist in warninglists_index:
|
||||||
wl = pymisp_instance.get_warninglist(warninglist['Warninglist']['id'])['Warninglist']
|
if isinstance(warninglist, WarningList):
|
||||||
wl['list'] = wl.pop('WarninglistEntry')
|
wl = pymisp_instance.get_warninglist(warninglist['Warninglist']['id'])['Warninglist']
|
||||||
all_warningslists.append(wl)
|
wl['list'] = wl.pop('WarninglistEntry')
|
||||||
|
all_warningslists.append(wl)
|
||||||
|
|
||||||
return WarningLists(slow_search, all_warningslists)
|
return WarningLists(slow_search, all_warningslists)
|
||||||
|
|
||||||
|
|
||||||
def from_package(slow_search=False):
|
def from_package(slow_search: bool=False) -> WarningLists:
|
||||||
return WarningLists(slow_search)
|
return WarningLists(slow_search)
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
#!/usr/bin/env python3
|
#!/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 ..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
|
|
||||||
|
|
||||||
import lief # type: ignore
|
from . import FileObject
|
||||||
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
|
|
||||||
|
import lief
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pydeep # type: ignore
|
import pydeep # type: ignore
|
||||||
|
@ -21,7 +25,10 @@ except ImportError:
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
def make_macho_objects(lief_parsed: lief.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)
|
macho_object = MachOObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||||
misp_file.add_reference(macho_object.uuid, 'includes', 'MachO indicators')
|
misp_file.add_reference(macho_object.uuid, 'includes', 'MachO indicators')
|
||||||
macho_sections = []
|
macho_sections = []
|
||||||
|
@ -32,31 +39,43 @@ def make_macho_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalo
|
||||||
|
|
||||||
class MachOObject(AbstractMISPObjectGenerator):
|
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"""
|
"""Creates an MachO object, with lief"""
|
||||||
super().__init__('macho', **kwargs)
|
super().__init__('macho', **kwargs)
|
||||||
if not HAS_PYDEEP:
|
if not HAS_PYDEEP:
|
||||||
logger.warning("Please install pydeep: pip install git+https://github.com/kbandla/pydeep.git")
|
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||||
if pseudofile:
|
if pseudofile:
|
||||||
if isinstance(pseudofile, BytesIO):
|
if isinstance(pseudofile, BytesIO):
|
||||||
self.__macho = lief.MachO.parse(raw=pseudofile.getvalue())
|
m = lief.MachO.parse(obj=pseudofile)
|
||||||
elif isinstance(pseudofile, bytes):
|
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:
|
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:
|
elif filepath:
|
||||||
self.__macho = lief.MachO.parse(filepath)
|
if m := lief.MachO.parse(filepath):
|
||||||
|
self.__macho = m.at(0)
|
||||||
elif parsed:
|
elif parsed:
|
||||||
# Got an already parsed blob
|
# 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
|
self.__macho = parsed
|
||||||
else:
|
else:
|
||||||
raise InvalidMISPObject('Not a lief.MachO.Binary: {}'.format(type(parsed)))
|
raise InvalidMISPObject(f'Not a lief.MachO.Binary: {type(parsed)}')
|
||||||
self.generate_attributes()
|
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('type', value=str(self.__macho.header.file_type).split('.')[1])
|
||||||
self.add_attribute('name', value=self.__macho.name)
|
|
||||||
# General information
|
# General information
|
||||||
if self.__macho.has_entrypoint:
|
if self.__macho.has_entrypoint:
|
||||||
self.add_attribute('entrypoint-address', value=self.__macho.entrypoint)
|
self.add_attribute('entrypoint-address', value=self.__macho.entrypoint)
|
||||||
|
@ -66,7 +85,7 @@ class MachOObject(AbstractMISPObjectGenerator):
|
||||||
pos = 0
|
pos = 0
|
||||||
for section in self.__macho.sections:
|
for section in self.__macho.sections:
|
||||||
s = MachOSectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
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
|
pos += 1
|
||||||
self.sections.append(s)
|
self.sections.append(s)
|
||||||
self.add_attribute('number-sections', value=len(self.sections))
|
self.add_attribute('number-sections', value=len(self.sections))
|
||||||
|
@ -74,19 +93,19 @@ class MachOObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
class MachOSectionObject(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."""
|
"""Creates an MachO Section object. Object generated by MachOObject."""
|
||||||
# Python3 way
|
# Python3 way
|
||||||
# super().__init__('pe-section')
|
# super().__init__('pe-section')
|
||||||
super(MachOSectionObject, self).__init__('macho-section', **kwargs)
|
super().__init__('macho-section', **kwargs)
|
||||||
self.__section = section
|
self.__section = section
|
||||||
self.__data = bytes(self.__section.content)
|
self.__data = bytes(self.__section.content)
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('name', value=self.__section.name)
|
self.add_attribute('name', value=self.__section.name)
|
||||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||||
if int(size.value) > 0:
|
if int(self.__section.size) > 0:
|
||||||
self.add_attribute('entropy', value=self.__section.entropy)
|
self.add_attribute('entropy', value=self.__section.entropy)
|
||||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
#!/usr/bin/env python
|
#!/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/
|
# NOTE: Reference on how this module is used: https://vvx7.io/posts/2020/05/misp-slack-bot/
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class MicroblogObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('microblog', strict=strict, **kwargs)
|
||||||
self._parameters = parameters
|
self._parameters = parameters
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
# Raw post.
|
# Raw post.
|
||||||
if 'post' in self._parameters:
|
if 'post' in self._parameters:
|
||||||
self.add_attribute('post', value=self._parameters['post'])
|
self.add_attribute('post', value=self._parameters['post'])
|
||||||
|
@ -32,7 +34,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
# Original URL location of the microblog post (potentially malicious.
|
# Original URL location of the microblog post (potentially malicious.
|
||||||
if 'url' in self._parameters:
|
if 'url' in self._parameters:
|
||||||
if isinstance(self._parameters.get('url'), list):
|
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)
|
self.add_attribute('url', value=i)
|
||||||
else:
|
else:
|
||||||
self.add_attribute('url', value=self._parameters['url'])
|
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).
|
# Archive of the original document (Internet Archive, Archive.is, etc).
|
||||||
if 'archive' in self._parameters:
|
if 'archive' in self._parameters:
|
||||||
if isinstance(self._parameters.get('archive'), list):
|
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)
|
self.add_attribute('archive', value=i)
|
||||||
else:
|
else:
|
||||||
self.add_attribute('archive', value=self._parameters['archive'])
|
self.add_attribute('archive', value=self._parameters['archive'])
|
||||||
|
@ -74,7 +76,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
"Instagram", "Forum", "Other"]
|
"Instagram", "Forum", "Other"]
|
||||||
if 'type' in self._parameters:
|
if 'type' in self._parameters:
|
||||||
if isinstance(self._parameters.get('type'), list):
|
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:
|
if i in type_allowed_values:
|
||||||
self.add_attribute('type', value=i)
|
self.add_attribute('type', value=i)
|
||||||
else:
|
else:
|
||||||
|
@ -85,7 +87,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
type_allowed_values = ["Informative", "Malicious", "Misinformation", "Disinformation", "Unknown"]
|
type_allowed_values = ["Informative", "Malicious", "Misinformation", "Disinformation", "Unknown"]
|
||||||
if 'state' in self._parameters:
|
if 'state' in self._parameters:
|
||||||
if isinstance(self._parameters.get('state'), list):
|
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:
|
if i in type_allowed_values:
|
||||||
self.add_attribute('state', value=i)
|
self.add_attribute('state', value=i)
|
||||||
else:
|
else:
|
||||||
|
@ -100,7 +102,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
type_allowed_values = ["Verified", "Unverified", "Unknown"]
|
type_allowed_values = ["Verified", "Unverified", "Unknown"]
|
||||||
if 'verified-username' in self._parameters:
|
if 'verified-username' in self._parameters:
|
||||||
if isinstance(self._parameters.get('verified-username'), list):
|
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:
|
if i in type_allowed_values:
|
||||||
self.add_attribute('verified-username', value=i)
|
self.add_attribute('verified-username', value=i)
|
||||||
else:
|
else:
|
||||||
|
@ -110,7 +112,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
# embedded-link.
|
# embedded-link.
|
||||||
if 'embedded-link' in self._parameters:
|
if 'embedded-link' in self._parameters:
|
||||||
if isinstance(self._parameters.get('embedded-link'), list):
|
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)
|
self.add_attribute('embedded-link', value=i)
|
||||||
else:
|
else:
|
||||||
self.add_attribute('embedded-link', value=self._parameters['embedded-link'])
|
self.add_attribute('embedded-link', value=self._parameters['embedded-link'])
|
||||||
|
@ -118,7 +120,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
# embedded-safe-link
|
# embedded-safe-link
|
||||||
if 'embedded-safe-link' in self._parameters:
|
if 'embedded-safe-link' in self._parameters:
|
||||||
if isinstance(self._parameters.get('embedded-safe-link'), list):
|
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)
|
self.add_attribute('embedded-safe-link', value=i)
|
||||||
else:
|
else:
|
||||||
self.add_attribute('embedded-safe-link', value=self._parameters['embedded-safe-link'])
|
self.add_attribute('embedded-safe-link', value=self._parameters['embedded-safe-link'])
|
||||||
|
@ -126,7 +128,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
# Hashtag into the microblog post.
|
# Hashtag into the microblog post.
|
||||||
if 'hashtag' in self._parameters:
|
if 'hashtag' in self._parameters:
|
||||||
if isinstance(self._parameters.get('hashtag'), list):
|
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)
|
self.add_attribute('hashtag', value=i)
|
||||||
else:
|
else:
|
||||||
self.add_attribute('hashtag', value=self._parameters['hashtag'])
|
self.add_attribute('hashtag', value=self._parameters['hashtag'])
|
||||||
|
@ -134,7 +136,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
||||||
# username quoted
|
# username quoted
|
||||||
if 'username-quoted' in self._parameters:
|
if 'username-quoted' in self._parameters:
|
||||||
if isinstance(self._parameters.get('username-quoted'), list):
|
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)
|
self.add_attribute('username-quoted', value=i)
|
||||||
else:
|
else:
|
||||||
self.add_attribute('username-quoted', value=self._parameters['username-quoted'])
|
self.add_attribute('username-quoted', value=self._parameters['username-quoted'])
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
from __future__ import annotations
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from .. import MISPEvent
|
from .. import MISPEvent
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -13,23 +14,23 @@ except ImportError:
|
||||||
|
|
||||||
class Neo4j():
|
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:
|
if not has_py2neo:
|
||||||
raise Exception('py2neo is required, please install: pip install py2neo')
|
raise Exception('py2neo is required, please install: pip install py2neo')
|
||||||
authenticate(host, username, password)
|
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):
|
def load_events_directory(self, directory: str) -> None:
|
||||||
self.events = []
|
self.events: list[MISPEvent] = []
|
||||||
for path in glob.glob(os.path.join(directory, '*.json')):
|
for path in glob.glob(os.path.join(directory, '*.json')):
|
||||||
e = MISPEvent()
|
e = MISPEvent()
|
||||||
e.load(path)
|
e.load(path)
|
||||||
self.import_event(e)
|
self.import_event(e)
|
||||||
|
|
||||||
def del_all(self):
|
def del_all(self) -> None:
|
||||||
self.graph.delete_all()
|
self.graph.delete_all()
|
||||||
|
|
||||||
def import_event(self, event):
|
def import_event(self, event: MISPEvent) -> None:
|
||||||
tx = self.graph.begin()
|
tx = self.graph.begin()
|
||||||
event_node = Node('Event', uuid=event.uuid, name=event.info)
|
event_node = Node('Event', uuid=event.uuid, name=event.info)
|
||||||
# event_node['distribution'] = event.distribution
|
# event_node['distribution'] = event.distribution
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
from __future__ import annotations
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -156,7 +155,7 @@ def extract_field(report, field_name):
|
||||||
def load_openioc_file(openioc_path):
|
def load_openioc_file(openioc_path):
|
||||||
if not os.path.exists(openioc_path):
|
if not os.path.exists(openioc_path):
|
||||||
raise Exception("Path doesn't exists.")
|
raise Exception("Path doesn't exists.")
|
||||||
with open(openioc_path, 'r') as f:
|
with open(openioc_path) as f:
|
||||||
return load_openioc(f)
|
return load_openioc(f)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from ..exceptions import InvalidMISPObject
|
from __future__ import annotations
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
|
||||||
from io import BytesIO
|
|
||||||
from hashlib import md5, sha1, sha256, sha512
|
|
||||||
from datetime import datetime
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Union
|
|
||||||
from pathlib import Path
|
|
||||||
from base64 import b64encode
|
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 . import FileObject
|
||||||
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
|
from ..exceptions import InvalidMISPObject
|
||||||
|
|
||||||
import lief # type: ignore
|
import lief
|
||||||
|
import lief.PE
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pydeep # type: ignore
|
import pydeep # type: ignore
|
||||||
|
@ -24,7 +27,10 @@ except ImportError:
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
def make_pe_objects(lief_parsed: lief.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)
|
pe_object = PEObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||||
misp_file.add_reference(pe_object.uuid, 'includes', 'PE indicators')
|
misp_file.add_reference(pe_object.uuid, 'includes', 'PE indicators')
|
||||||
pe_sections = []
|
pe_sections = []
|
||||||
|
@ -35,44 +41,57 @@ def make_pe_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalone:
|
||||||
|
|
||||||
class PEObject(AbstractMISPObjectGenerator):
|
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"""
|
"""Creates an PE object, with lief"""
|
||||||
super().__init__('pe', **kwargs)
|
super().__init__('pe', **kwargs)
|
||||||
if not HAS_PYDEEP:
|
if not HAS_PYDEEP:
|
||||||
logger.warning("Please install pydeep: pip install git+https://github.com/kbandla/pydeep.git")
|
logger.warning("pydeep is missing, please install pymisp this way: pip install pymisp[fileobjects]")
|
||||||
if pseudofile:
|
if pseudofile:
|
||||||
if isinstance(pseudofile, BytesIO):
|
if isinstance(pseudofile, BytesIO):
|
||||||
self.__pe = lief.PE.parse(raw=pseudofile.getvalue())
|
p = lief.PE.parse(obj=pseudofile)
|
||||||
elif isinstance(pseudofile, bytes):
|
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:
|
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:
|
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:
|
elif parsed:
|
||||||
# Got an already parsed blob
|
# Got an already parsed blob
|
||||||
if isinstance(parsed, lief.PE.Binary):
|
if isinstance(parsed, lief.PE.Binary):
|
||||||
self.__pe = parsed
|
self.__pe = parsed
|
||||||
else:
|
else:
|
||||||
raise InvalidMISPObject('Not a lief.PE.Binary: {}'.format(type(parsed)))
|
raise InvalidMISPObject(f'Not a lief.PE.Binary: {type(parsed)}')
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def _is_exe(self):
|
def _is_exe(self) -> bool:
|
||||||
if not self._is_dll() and not self._is_driver():
|
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
|
return False
|
||||||
|
|
||||||
def _is_dll(self):
|
def _is_dll(self) -> bool:
|
||||||
return self.__pe.header.has_characteristic(lief.PE.HEADER_CHARACTERISTICS.DLL)
|
return self.__pe.header.has_characteristic(lief.PE.Header.CHARACTERISTICS.DLL)
|
||||||
|
|
||||||
def _is_driver(self):
|
def _is_driver(self) -> bool:
|
||||||
# List from pefile
|
# 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]):
|
if system_DLLs.intersection([imp.lower() for imp in self.__pe.libraries]):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _get_pe_type(self):
|
def _get_pe_type(self) -> str:
|
||||||
if self._is_dll():
|
if self._is_dll():
|
||||||
return 'dll'
|
return 'dll'
|
||||||
elif self._is_driver():
|
elif self._is_driver():
|
||||||
|
@ -82,31 +101,27 @@ class PEObject(AbstractMISPObjectGenerator):
|
||||||
else:
|
else:
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('type', value=self._get_pe_type())
|
self.add_attribute('type', value=self._get_pe_type())
|
||||||
# General information
|
# General information
|
||||||
self.add_attribute('entrypoint-address', value=self.__pe.entrypoint)
|
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('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('imphash', value=lief.PE.get_imphash(self.__pe, lief.PE.IMPHASH_MODE.PEFILE))
|
||||||
self.add_attribute('authentihash', value=self.__pe.authentihash_sha256.hex())
|
self.add_attribute('authentihash', value=self.__pe.authentihash_sha256.hex())
|
||||||
try:
|
r_manager = self.__pe.resources_manager
|
||||||
if (self.__pe.has_resources
|
if isinstance(r_manager, lief.PE.ResourcesManager):
|
||||||
and self.__pe.resources_manager.has_version
|
version = r_manager.version
|
||||||
and self.__pe.resources_manager.version.has_string_file_info
|
if isinstance(version, lief.PE.ResourceVersion) and version.string_file_info is not None:
|
||||||
and self.__pe.resources_manager.version.string_file_info.langcode_items):
|
fileinfo = dict(version.string_file_info.langcode_items[0].items.items())
|
||||||
fileinfo = dict(self.__pe.resources_manager.version.string_file_info.langcode_items[0].items.items())
|
|
||||||
self.add_attribute('original-filename', value=fileinfo.get('OriginalFilename'))
|
self.add_attribute('original-filename', value=fileinfo.get('OriginalFilename'))
|
||||||
self.add_attribute('internal-filename', value=fileinfo.get('InternalName'))
|
self.add_attribute('internal-filename', value=fileinfo.get('InternalName'))
|
||||||
self.add_attribute('file-description', value=fileinfo.get('FileDescription'))
|
self.add_attribute('file-description', value=fileinfo.get('FileDescription'))
|
||||||
self.add_attribute('file-version', value=fileinfo.get('FileVersion'))
|
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-name', value=fileinfo.get('ProductName'))
|
||||||
self.add_attribute('product-version', value=fileinfo.get('ProductVersion'))
|
self.add_attribute('product-version', value=fileinfo.get('ProductVersion'))
|
||||||
self.add_attribute('company-name', value=fileinfo.get('CompanyName'))
|
self.add_attribute('company-name', value=fileinfo.get('CompanyName'))
|
||||||
self.add_attribute('legal-copyright', value=fileinfo.get('LegalCopyright'))
|
self.add_attribute('legal-copyright', value=fileinfo.get('LegalCopyright'))
|
||||||
except lief.read_out_of_bound:
|
self.add_attribute('lang-id', value=version.string_file_info.langcode_items[0].key)
|
||||||
# The file is corrupted
|
|
||||||
pass
|
|
||||||
# Sections
|
# Sections
|
||||||
self.sections = []
|
self.sections = []
|
||||||
if self.__pe.sections:
|
if self.__pe.sections:
|
||||||
|
@ -116,10 +131,14 @@ class PEObject(AbstractMISPObjectGenerator):
|
||||||
# Skip section if name is none AND size is 0.
|
# Skip section if name is none AND size is 0.
|
||||||
continue
|
continue
|
||||||
s = PESectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
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)
|
if ((self.__pe.entrypoint >= section.virtual_address)
|
||||||
and (self.__pe.entrypoint < (section.virtual_address + section.virtual_size))):
|
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
|
pos += 1
|
||||||
self.sections.append(s)
|
self.sections.append(s)
|
||||||
self.add_attribute('number-sections', value=len(self.sections))
|
self.add_attribute('number-sections', value=len(self.sections))
|
||||||
|
@ -139,16 +158,30 @@ class PEObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
class PECertificate(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')
|
super().__init__('x509')
|
||||||
self.__certificate = certificate
|
self.__certificate = certificate
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('issuer', value=self.__certificate.issuer)
|
self.add_attribute('issuer', value=self.__certificate.issuer)
|
||||||
self.add_attribute('serial-number', value=self.__certificate.serial_number)
|
self.add_attribute('serial-number', value=self.__certificate.serial_number)
|
||||||
self.add_attribute('validity-not-before', value=datetime(*self.__certificate.valid_from))
|
if len(self.__certificate.valid_from) == 6:
|
||||||
self.add_attribute('validity-not-after', value=datetime(*self.__certificate.valid_to))
|
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('version', value=self.__certificate.version)
|
||||||
self.add_attribute('subject', value=self.__certificate.subject)
|
self.add_attribute('subject', value=self.__certificate.subject)
|
||||||
self.add_attribute('signature_algorithm', value=self.__certificate.signature_algorithm)
|
self.add_attribute('signature_algorithm', value=self.__certificate.signature_algorithm)
|
||||||
|
@ -157,19 +190,19 @@ class PECertificate(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
class PESigners(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')
|
super().__init__('authenticode-signerinfo')
|
||||||
self.__signer = signer
|
self.__signer = signer
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('issuer', value=self.__signer.issuer)
|
self.add_attribute('issuer', value=self.__signer.issuer)
|
||||||
self.add_attribute('serial-number', value=self.__signer.serial_number)
|
self.add_attribute('serial-number', value=self.__signer.serial_number)
|
||||||
self.add_attribute('version', value=self.__signer.version)
|
self.add_attribute('version', value=self.__signer.version)
|
||||||
self.add_attribute('digest_algorithm', value=self.__signer.digest_algorithm.name)
|
self.add_attribute('digest_algorithm', value=str(self.__signer.digest_algorithm))
|
||||||
self.add_attribute('encryption_algorithm', value=self.__signer.encryption_algorithm.name)
|
self.add_attribute('encryption_algorithm', value=str(self.__signer.encryption_algorithm))
|
||||||
self.add_attribute('digest-base64', value=b64encode(self.__signer.encrypted_digest))
|
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:
|
if info:
|
||||||
self.add_attribute('program-name', value=info.program_name)
|
self.add_attribute('program-name', value=info.program_name)
|
||||||
self.add_attribute('url', value=info.more_info)
|
self.add_attribute('url', value=info.more_info)
|
||||||
|
@ -177,17 +210,17 @@ class PESigners(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
class PESectionObject(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."""
|
"""Creates an PE Section object. Object generated by PEObject."""
|
||||||
super().__init__('pe-section')
|
super().__init__('pe-section')
|
||||||
self.__section = section
|
self.__section = section
|
||||||
self.__data = bytes(self.__section.content)
|
self.__data = bytes(self.__section.content)
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('name', value=self.__section.name)
|
self.add_attribute('name', value=self.__section.name)
|
||||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||||
if int(size.value) > 0:
|
if int(self.__section.size) > 0:
|
||||||
# zero-filled sections can create too many correlations
|
# zero-filled sections can create too many correlations
|
||||||
to_ids = float(self.__section.entropy) > 0
|
to_ids = float(self.__section.entropy) > 0
|
||||||
disable_correlation = not to_ids
|
disable_correlation = not to_ids
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
# Standard imports
|
# Standard imports
|
||||||
import base64
|
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)]
|
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
|
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
|
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
|
LEFT_INTERNAL_PADDING = 2
|
||||||
ELONGATION = 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)
|
string_width = stringWidth(self.text, self.custom_style.fontName, self.custom_style.fontSize)
|
||||||
|
|
||||||
self.width = string_width + ELONGATION
|
self.width = string_width + ELONGATION
|
||||||
|
@ -615,7 +616,7 @@ class Value_Formatter():
|
||||||
curr_uuid = str(is_safe_value(uuid))
|
curr_uuid = str(is_safe_value(uuid))
|
||||||
curr_baseurl = self.config[moduleconfig[0]]
|
curr_baseurl = self.config[moduleconfig[0]]
|
||||||
curr_url = uuid_to_url(curr_baseurl, curr_uuid)
|
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:
|
if color:
|
||||||
# They want fancy colors
|
# They want fancy colors
|
||||||
|
@ -744,7 +745,7 @@ class Value_Formatter():
|
||||||
answer = YES_ANSWER
|
answer = YES_ANSWER
|
||||||
if is_safe_value(published_timestamp):
|
if is_safe_value(published_timestamp):
|
||||||
# Published and have published date
|
# Published and have published date
|
||||||
answer += '({})'.format(published_timestamp.strftime(EXPORT_DATE_FORMAT))
|
answer += f'({published_timestamp.strftime(EXPORT_DATE_FORMAT)})'
|
||||||
else:
|
else:
|
||||||
# Published without published date
|
# Published without published date
|
||||||
answer += "(no date)"
|
answer += "(no date)"
|
||||||
|
@ -1091,7 +1092,7 @@ class Event_Metadata():
|
||||||
Paragraph("Related Event #" + str(i + OFFSET), self.sample_style_sheet['Heading4']))
|
Paragraph("Related Event #" + str(i + OFFSET), self.sample_style_sheet['Heading4']))
|
||||||
flowable_table.append(Indenter(left=-INDENT_SIZE_HEADING))
|
flowable_table.append(Indenter(left=-INDENT_SIZE_HEADING))
|
||||||
|
|
||||||
flowable_table += self.create_reduced_flowable_table_from_event(evt)
|
flowable_table += self.create_reduced_flowable_table_from_event(evt['Event'])
|
||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
return flowable_table.append(self.value_formatter.get_unoverflowable_paragraph(DEFAULT_VALUE))
|
return flowable_table.append(self.value_formatter.get_unoverflowable_paragraph(DEFAULT_VALUE))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
|
|
||||||
|
@ -8,13 +9,13 @@ class SBSignatureObject(AbstractMISPObjectGenerator):
|
||||||
'''
|
'''
|
||||||
Sandbox Analyzer
|
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)
|
super().__init__('sb-signature', **kwargs)
|
||||||
self._software = software
|
self._software = software
|
||||||
self._report = report
|
self._report = report
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
''' Parse the report for relevant attributes '''
|
''' Parse the report for relevant attributes '''
|
||||||
self.add_attribute("software", value=self._software)
|
self.add_attribute("software", value=self._software)
|
||||||
for (signature_name, description) in self._report:
|
for (signature_name, description) in self._report:
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
#!/usr/bin/env python3
|
#!/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 ..exceptions import InvalidMISPObject
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
from io import StringIO
|
|
||||||
import logging
|
|
||||||
from typing import Optional, Union
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
logger = logging.getLogger('pymisp')
|
logger = logging.getLogger('pymisp')
|
||||||
|
|
||||||
|
|
||||||
class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
def __init__(self, authorized_keys_path: Optional[Union[Path, str]] = None, authorized_keys_pseudofile: Optional[StringIO] = None, **kwargs):
|
def __init__(self, authorized_keys_path: Path | str | None = None, # type: ignore[no-untyped-def]
|
||||||
# PY3 way:
|
authorized_keys_pseudofile: StringIO | None = None, **kwargs):
|
||||||
super().__init__('ssh-authorized-keys', **kwargs)
|
super().__init__('ssh-authorized-keys', **kwargs)
|
||||||
if authorized_keys_path:
|
if authorized_keys_path:
|
||||||
with open(authorized_keys_path, 'r') as f:
|
with open(authorized_keys_path) as f:
|
||||||
self.__pseudofile = StringIO(f.read())
|
self.__pseudofile = StringIO(f.read())
|
||||||
elif authorized_keys_pseudofile and isinstance(authorized_keys_pseudofile, StringIO):
|
elif authorized_keys_pseudofile and isinstance(authorized_keys_pseudofile, StringIO):
|
||||||
self.__pseudofile = authorized_keys_pseudofile
|
self.__pseudofile = authorized_keys_pseudofile
|
||||||
|
@ -26,7 +28,7 @@ class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
||||||
self.__data = self.__pseudofile.getvalue()
|
self.__data = self.__pseudofile.getvalue()
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
for line in self.__pseudofile:
|
for line in self.__pseudofile:
|
||||||
if line.startswith('ssh') or line.startswith('ecdsa'):
|
if line.startswith('ssh') or line.startswith('ecdsa'):
|
||||||
key = line.split(' ')[1]
|
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
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
@ -12,7 +13,7 @@ from ..abstract import resources_path
|
||||||
static_repo = "https://github.com/MISP/misp-objects/archive/main.zip"
|
static_repo = "https://github.com/MISP/misp-objects/archive/main.zip"
|
||||||
|
|
||||||
|
|
||||||
def update_objects():
|
def update_objects() -> None:
|
||||||
r = requests.get(static_repo)
|
r = requests.get(static_repo)
|
||||||
|
|
||||||
zipped_repo = BytesIO(r.content)
|
zipped_repo = BytesIO(r.content)
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from urllib.parse import unquote_plus
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
import logging
|
|
||||||
from urllib.parse import unquote_plus
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pyfaup.faup import Faup # type: ignore
|
from pyfaup.faup import Faup # type: ignore
|
||||||
|
@ -17,13 +20,13 @@ faup = Faup()
|
||||||
|
|
||||||
class URLObject(AbstractMISPObjectGenerator):
|
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)
|
super().__init__('url', **kwargs)
|
||||||
self._generate_all = True if generate_all is True else False
|
self._generate_all = True if generate_all is True else False
|
||||||
faup.decode(unquote_plus(url))
|
faup.decode(unquote_plus(url))
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
self.add_attribute('url', value=faup.url.decode())
|
self.add_attribute('url', value=faup.url.decode())
|
||||||
if faup.get_host():
|
if faup.get_host():
|
||||||
self.add_attribute('host', value=faup.get_host())
|
self.add_attribute('host', value=faup.get_host())
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
|
|
||||||
# Original sourcecode: https://github.com/hayk57/MISP_registration_check
|
# Original sourcecode: https://github.com/hayk57/MISP_registration_check
|
||||||
|
@ -11,14 +15,16 @@ from .abstractgenerator import AbstractMISPObjectGenerator
|
||||||
class VehicleObject(AbstractMISPObjectGenerator):
|
class VehicleObject(AbstractMISPObjectGenerator):
|
||||||
'''Vehicle object generator out of regcheck.org.uk'''
|
'''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",
|
'fr': "http://www.regcheck.org.uk/api/reg.asmx/CheckFrance",
|
||||||
'es': "http://www.regcheck.org.uk/api/reg.asmx/CheckSpain",
|
'es': "http://www.regcheck.org.uk/api/reg.asmx/CheckSpain",
|
||||||
'uk': "http://www.regcheck.org.uk/api/reg.asmx/Check"
|
'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)
|
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._country = country
|
||||||
self._registration = registration
|
self._registration = registration
|
||||||
self._username = username
|
self._username = username
|
||||||
|
@ -26,10 +32,10 @@ class VehicleObject(AbstractMISPObjectGenerator):
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def report(self):
|
def report(self) -> dict[str, Any]:
|
||||||
return self._report
|
return self._report
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
carDescription = self._report["Description"]
|
carDescription = self._report["Description"]
|
||||||
carMake = self._report["CarMake"]["CurrentTextValue"]
|
carMake = self._report["CarMake"]["CurrentTextValue"]
|
||||||
carModel = self._report["CarModel"]["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('date-first-registration', type='text', value=firstRegistration)
|
||||||
self.add_attribute('image-url', type='text', value=ImageUrl)
|
self.add_attribute('image-url', type='text', value=ImageUrl)
|
||||||
|
|
||||||
def _query(self):
|
def _query(self) -> dict[str, Any]:
|
||||||
payload = "RegistrationNumber={}&username={}".format(self._registration, self._username)
|
payload = f"RegistrationNumber={self._registration}&username={self._username}"
|
||||||
headers = {
|
headers = {
|
||||||
'Content-Type': "application/x-www-form-urlencoded",
|
'Content-Type': "application/x-www-form-urlencoded",
|
||||||
'cache-control': "no-cache",
|
'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.
|
# FIXME: Clean that up.
|
||||||
for item in response.text.split("</vehicleJson>"):
|
for item in response.text.split("</vehicleJson>"):
|
||||||
if "<vehicleJson>" in item:
|
if "<vehicleJson>" in item:
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Optional
|
from typing import Any
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
try:
|
try:
|
||||||
import validators # type: ignore
|
import validators
|
||||||
has_validators = True
|
has_validators = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_validators = False
|
has_validators = False
|
||||||
|
@ -24,7 +25,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
:indicator: IOC to search VirusTotal for
|
: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)
|
super().__init__('virustotal-report', **kwargs)
|
||||||
indicator = indicator.strip()
|
indicator = indicator.strip()
|
||||||
self._resource_type = self.__validate_resource(indicator)
|
self._resource_type = self.__validate_resource(indicator)
|
||||||
|
@ -33,20 +34,20 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
||||||
self._report = self.__query_virustotal(apikey, indicator)
|
self._report = self.__query_virustotal(apikey, indicator)
|
||||||
self.generate_attributes()
|
self.generate_attributes()
|
||||||
else:
|
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)
|
raise InvalidMISPObject(error_msg)
|
||||||
|
|
||||||
def get_report(self):
|
def get_report(self) -> dict[str, Any]:
|
||||||
return self._report
|
return self._report
|
||||||
|
|
||||||
def generate_attributes(self):
|
def generate_attributes(self) -> None:
|
||||||
''' Parse the VirusTotal report for relevant attributes '''
|
''' Parse the VirusTotal report for relevant attributes '''
|
||||||
self.add_attribute("last-submission", value=self._report["scan_date"])
|
self.add_attribute("last-submission", value=self._report["scan_date"])
|
||||||
self.add_attribute("permalink", value=self._report["permalink"])
|
self.add_attribute("permalink", value=self._report["permalink"])
|
||||||
ratio = "{}/{}".format(self._report["positives"], self._report["total"])
|
ratio = "{}/{}".format(self._report["positives"], self._report["total"])
|
||||||
self.add_attribute("detection-ratio", value=ratio)
|
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.
|
Validate the data type of an indicator.
|
||||||
Domains and IP addresses aren't supported because
|
Domains and IP addresses aren't supported because
|
||||||
|
@ -62,7 +63,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
||||||
return "file"
|
return "file"
|
||||||
return False
|
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
|
Query VirusTotal for information about an indicator
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
||||||
|
|
||||||
:resource: Indicator to search in VirusTotal
|
: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}
|
params = {"apikey": apikey, "resource": resource}
|
||||||
# for now assume we're using a public API key - we'll figure out private keys later
|
# for now assume we're using a public API key - we'll figure out private keys later
|
||||||
if self._proxies:
|
if self._proxies:
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pymisp"
|
name = "pymisp"
|
||||||
version = "2.4.166"
|
version = "2.4.194"
|
||||||
description = "Python API for MISP."
|
description = "Python API for MISP."
|
||||||
authors = ["Raphaël Vinot <raphael.vinot@circl.lu>"]
|
authors = ["Raphaël Vinot <raphael.vinot@circl.lu>"]
|
||||||
license = "BSD-2-Clause"
|
license = "BSD-2-Clause"
|
||||||
repository = "https://github.com/MISP/PyMISP"
|
repository = "https://github.com/MISP/PyMISP"
|
||||||
documentation = "https://pymisp.readthedocs.io"
|
documentation = "https://pymisp.readthedocs.io"
|
||||||
|
|
||||||
|
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
@ -18,16 +17,17 @@ classifiers=[
|
||||||
'Intended Audience :: Science/Research',
|
'Intended Audience :: Science/Research',
|
||||||
'Intended Audience :: Telecommunications Industry',
|
'Intended Audience :: Telecommunications Industry',
|
||||||
'Intended Audience :: Information Technology',
|
'Intended Audience :: Information Technology',
|
||||||
'Programming Language :: Python :: 3.7',
|
|
||||||
'Programming Language :: Python :: 3.8',
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: 3.9',
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
|
'Programming Language :: Python :: 3.11',
|
||||||
|
'Programming Language :: Python :: 3.12',
|
||||||
'Topic :: Security',
|
'Topic :: Security',
|
||||||
'Topic :: Internet'
|
'Topic :: Internet'
|
||||||
]
|
]
|
||||||
|
|
||||||
include = [
|
include = [
|
||||||
"CHANGELOG.txt",
|
"CHANGELOG.txt",
|
||||||
"README.md",
|
|
||||||
"pymisp/data/*.json",
|
"pymisp/data/*.json",
|
||||||
"pymisp/data/misp-objects/schema_objects.json",
|
"pymisp/data/misp-objects/schema_objects.json",
|
||||||
"pymisp/data/misp-objects/schema_relationships.json",
|
"pymisp/data/misp-objects/schema_relationships.json",
|
||||||
|
@ -41,47 +41,51 @@ include = [
|
||||||
"Source" = "https://github.com/MISP/PyMISP"
|
"Source" = "https://github.com/MISP/PyMISP"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7"
|
python = "^3.8"
|
||||||
requests = "^2.28.1"
|
requests = "^2.32.3"
|
||||||
python-dateutil = "^2.8.2"
|
python-dateutil = "^2.9.0.post0"
|
||||||
jsonschema = "^4.17.1"
|
deprecated = "^1.2.14"
|
||||||
deprecated = "^1.2.13"
|
extract_msg = {version = "^0.48.5", optional = true}
|
||||||
extract_msg = {version = "^0.37.1", optional = true}
|
RTFDE = {version = "^0.1.1", optional = true}
|
||||||
RTFDE = {version = "^0.0.2", optional = true}
|
|
||||||
oletools = {version = "^0.60.1", optional = true}
|
oletools = {version = "^0.60.1", optional = true}
|
||||||
python-magic = {version = "^0.4.27", optional = true}
|
python-magic = {version = "^0.4.27", optional = true}
|
||||||
pydeep2 = {version = "^0.5.1", optional = true}
|
pydeep2 = {version = "^0.5.1", optional = true}
|
||||||
lief = {version = "^0.12.3", optional = true}
|
lief = {version = "^0.14.1", optional = true}
|
||||||
beautifulsoup4 = {version = "^4.11.1", optional = true}
|
beautifulsoup4 = {version = "^4.12.3", optional = true}
|
||||||
validators = {version = "^0.20.0", optional = true}
|
validators = {version = "^0.30.0", optional = true}
|
||||||
sphinx-autodoc-typehints = {version = "^1.19.5", optional = true}
|
sphinx-autodoc-typehints = {version = "^2.2.2", optional = true, python = ">=3.9"}
|
||||||
recommonmark = {version = "^0.7.1", optional = true}
|
docutils = {version = "^0.21.1", optional = true, python = ">=3.9"}
|
||||||
reportlab = {version = "^3.6.12", optional = true}
|
recommonmark = {version = "^0.7.1", optional = true, python = ">=3.9"}
|
||||||
|
reportlab = {version = "^4.2.2", optional = true}
|
||||||
pyfaup = {version = "^1.2", optional = true}
|
pyfaup = {version = "^1.2", optional = true}
|
||||||
publicsuffixlist = {version = "^0.9.1", optional = true}
|
publicsuffixlist = {version = "^1.0.1.20240702", optional = true}
|
||||||
chardet = {version = "^5.0.0", optional = true}
|
urllib3 = {extras = ["brotli"], version = "*", optional = true}
|
||||||
urllib3 = {extras = ["brotli"], version = "^1.26.13", optional = true}
|
Sphinx = {version = "^7.3.7", python = ">=3.9", optional = true}
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
fileobjects = ['python-magic', 'pydeep2', 'lief']
|
fileobjects = ['python-magic', 'pydeep2', 'lief']
|
||||||
openioc = ['beautifulsoup4']
|
openioc = ['beautifulsoup4']
|
||||||
virustotal = ['validators']
|
virustotal = ['validators']
|
||||||
docs = ['sphinx-autodoc-typehints', 'recommonmark']
|
docs = ['sphinx-autodoc-typehints', 'recommonmark', 'sphinx', 'docutils']
|
||||||
pdfexport = ['reportlab']
|
pdfexport = ['reportlab']
|
||||||
url = ['pyfaup', 'chardet']
|
url = ['pyfaup']
|
||||||
email = ['extract_msg', "RTFDE", "oletools"]
|
email = ['extract_msg', "RTFDE", "oletools"]
|
||||||
brotli = ['urllib3']
|
brotli = ['urllib3']
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
requests-mock = "^1.10.0"
|
requests-mock = "^1.12.1"
|
||||||
mypy = "^0.991"
|
mypy = "^1.10.1"
|
||||||
ipython = "^7.34.0"
|
ipython = [
|
||||||
jupyterlab = "^3.5.0"
|
{version = "<8.13.0", python = "<3.9"},
|
||||||
types-requests = "^2.28.11.5"
|
{version = "^8.18.0", python = ">=3.9"},
|
||||||
types-python-dateutil = "^2.8.19.4"
|
{version = "^8.19.0", python = ">=3.10"}
|
||||||
types-redis = "^4.3.21.6"
|
]
|
||||||
|
jupyterlab = "^4.2.3"
|
||||||
|
types-requests = "^2.32.0.20240622"
|
||||||
|
types-python-dateutil = "^2.9.0.20240316"
|
||||||
|
types-redis = "^4.6.0.20240425"
|
||||||
types-Flask = "^1.1.6"
|
types-Flask = "^1.1.6"
|
||||||
pytest-cov = "^4.0.0"
|
pytest-cov = "^5.0.0"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry_core>=1.1", "setuptools"]
|
requires = ["poetry_core>=1.1", "setuptools"]
|
||||||
|
|
64
setup.py
64
setup.py
|
@ -1,64 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from os import path
|
|
||||||
|
|
||||||
from setuptools import setup # type: ignore
|
|
||||||
|
|
||||||
import pymisp
|
|
||||||
|
|
||||||
this_directory = path.abspath(path.dirname(__file__))
|
|
||||||
with open(path.join(this_directory, 'README.md'), 'r') as f:
|
|
||||||
long_description = f.read()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='pymisp',
|
|
||||||
version=pymisp.__version__,
|
|
||||||
author='Raphaël Vinot',
|
|
||||||
author_email='raphael.vinot@circl.lu',
|
|
||||||
maintainer='Raphaël Vinot',
|
|
||||||
url='https://github.com/MISP/PyMISP',
|
|
||||||
project_urls={
|
|
||||||
'Documentation': 'https://pymisp.readthedocs.io',
|
|
||||||
'Source': 'https://github.com/MISP/PyMISP',
|
|
||||||
'Tracker': 'https://github.com/MISP/PyMISP/issues',
|
|
||||||
},
|
|
||||||
description='Python API for MISP.',
|
|
||||||
long_description=long_description,
|
|
||||||
long_description_content_type='text/markdown',
|
|
||||||
packages=['pymisp', 'pymisp.tools'],
|
|
||||||
classifiers=[
|
|
||||||
'License :: OSI Approved :: BSD License',
|
|
||||||
'Development Status :: 5 - Production/Stable',
|
|
||||||
'Environment :: Console',
|
|
||||||
'Operating System :: POSIX :: Linux',
|
|
||||||
'Intended Audience :: Science/Research',
|
|
||||||
'Intended Audience :: Telecommunications Industry',
|
|
||||||
'Intended Audience :: Information Technology',
|
|
||||||
'Programming Language :: Python :: 3.6',
|
|
||||||
'Topic :: Security',
|
|
||||||
'Topic :: Internet',
|
|
||||||
],
|
|
||||||
install_requires=['requests',
|
|
||||||
'python-dateutil',
|
|
||||||
'jsonschema',
|
|
||||||
'deprecated'],
|
|
||||||
extras_require={'fileobjects': ['python-magic', 'pydeep2', 'lief>=0.11.0'],
|
|
||||||
'neo': ['py2neo'],
|
|
||||||
'openioc': ['beautifulsoup4'],
|
|
||||||
'virustotal': ['validators'],
|
|
||||||
'docs': ['sphinx-autodoc-typehints', 'recommonmark'],
|
|
||||||
'pdfexport': ['reportlab']},
|
|
||||||
tests_require=[
|
|
||||||
'jsonschema',
|
|
||||||
'python-magic',
|
|
||||||
'requests-mock'
|
|
||||||
],
|
|
||||||
test_suite="tests.test_mispevent",
|
|
||||||
include_package_data=True,
|
|
||||||
package_data={'pymisp': ['data/*.json',
|
|
||||||
'data/misp-objects/schema_objects.json',
|
|
||||||
'data/misp-objects/schema_relationships.json',
|
|
||||||
'data/misp-objects/objects/*/definition.json',
|
|
||||||
'data/misp-objects/relationships/definition.json',
|
|
||||||
'tools/pdf_fonts/Noto_TTF/*']},
|
|
||||||
)
|
|
|
@ -1,4 +1,4 @@
|
||||||
MD5, SHA1, SHA256
|
md5, sha1, sha256
|
||||||
644087ccca16d2a728ef7685a4106f09, eabd6974ac71efd72d9e0688d5a6131f336d169c, 385e31c97e3a07bbb81513f0cd0979e64e6b014943902efd002f57b21eadd41e
|
644087ccca16d2a728ef7685a4106f09, eabd6974ac71efd72d9e0688d5a6131f336d169c, 385e31c97e3a07bbb81513f0cd0979e64e6b014943902efd002f57b21eadd41e
|
||||||
34187a34d0a3c5d63016c26346371b54, ce8209ff9828aa8cb095bd7d1589fc4d394c298c, 5f815b8a8e77731c9ca2b3a07a27f880ef24d54e458d77bdabbbaf2269fe96c3
|
34187a34d0a3c5d63016c26346371b54, ce8209ff9828aa8cb095bd7d1589fc4d394c298c, 5f815b8a8e77731c9ca2b3a07a27f880ef24d54e458d77bdabbbaf2269fe96c3
|
||||||
871aa15f4d61c85e1284e1be3f99f705, 236eac0b19f91117b27f1b198a4d8490d99ec2e5, b434bccf0a5ff75b27184e661df751466aef69f35fbd7b8b8692302b8b886262
|
871aa15f4d61c85e1284e1be3f99f705, 236eac0b19f91117b27f1b198a4d8490d99ec2e5, b434bccf0a5ff75b27184e661df751466aef69f35fbd7b8b8692302b8b886262
|
||||||
|
|
|
|
@ -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
|
|
||||||
|
|
||||||
UEsDBBQAAAAIALtmFklP5Nc/ZCIAAGMiAAAPAAAA4avjpqGglI2ROTQuemlwdXpTcCUME+yJk41t
|
|
||||||
2zrhxhvbtm3bG+PEtq2NbVsb28nGTu73P9ynW3fmYWr6aaqmqru6qhVlICAxAAAALKDTHEvKj7O8
|
|
||||||
6ogUABhSBQCQ/kNNHUwM3NzNTB3sbJmsXWr4rOIPeJ3zdt4xZwBgfGcZMRRRoJgtRJJx0E2VX+c8
|
|
||||||
mO/489XTfP/Xs2pioejMtfnxryejUbJoBszoYd6KOIGnxYvz+tmXSacnm+OAr48DTgGL8xLjY4eM
|
|
||||||
1ufej1cYvh6rm7m3xk80LrcVXV2xDcOrV/1mOBvmhsgxPH3cv6y5abJ8IYKn+0G7nTP3F6Ne8ehf
|
|
||||||
dyc73zGQVk3J9T76c26zo6Y5vf+wyioxWrnl3m02Dyn0jp0WTacPNBPW+H5lPfeacps/yewb8H9x
|
|
||||||
UNBlWYRJBhCVmNpDpmSlS37C2zjiVUzrZItv7TPKk8lJ/g2b23ZUt+MWw5cfE9/DQlCFrVCj4BUA
|
|
||||||
G/3g3DYP+m1gMsUx9J5hBhndQ9ZTDd1Wkbtotcnjwy/CHOhvGlEznKOP+YZyx//L7iflYBWdqqed
|
|
||||||
1SKa+yaUYK0s73Uw+XvbME9jhE42+Ua7oRSUxuC7AdtzOu5zuhiCm7T8kQopNYOrsre8nPBsBe4i
|
|
||||||
ub5Tj0j0Y2n5jAbUMqdYM4onViMD074YoVdIXUXzSY0mildI9F42F40FgoNmdeKYxTP0NJQBC8R+
|
|
||||||
/EgA7Dfra3x/QLlowHPpHPGS9dmPqNcWTQi+kSrD/Ui5HqQNVLsejJ+J85hRMHlPoEiYfzB63t/S
|
|
||||||
Xp8DaThtXaEiQq0VLNeKrwCaSUnMbhoPgNf4oe3vIT+Z06pgz0wK7Odkv2z4gYoTZ1o4M4OCgmzl
|
|
||||||
ORwOSIhdVnOABv7aCqVY0JXmcopolsPJKxk3W6g53w0lOvIucrhngphN+Xm7v7RGMk2HJA2vuGhy
|
|
||||||
Wgm9NIbeJhmaX+Kcy2qbWAAZJ0I+4fBfxqgWIuJRf6kPm6HeUlEvHJrim3vas89oku6ASCmunn9Y
|
|
||||||
1FtzB6WJlXyWTn9d1J0381ROd5v02XwB7xz7BkddgmXnQQpnGepbovBPKl7nQtSBjZIdpz/FIU6v
|
|
||||||
AN4gdoaCuxFPcZQHchNN702eAUTwsWdZprIS15i7H7jXbWlojfZwI9U6rCYv8sIqaU/QmeEUHnMU
|
|
||||||
ZwIik7Kt2YTgpuh596C98rU6rO/M2zpbYCpda+pgGIV3xW082oFDusE+ZxmQOlO8o1/J+J1xHrjY
|
|
||||||
H/EWBE59GUDBF+xJEjA3HCJ7/avgDwkP3WDPms6IdUPiLHsldKD5+oLosztMN6zPOE5mShOkFg0J
|
|
||||||
4B3seDbmHYkpDmrsOYZ2g+dUI2K9+oSWx6e/zW/qT853WAv3Itn2LkPWtFd0caoJ8f2F7y6jv6EJ
|
|
||||||
xiYFw2iXWdOKMMvTYMhxLrvDbEb7ydSWi0vDSZnFLDJXVrxg46/7PVp4HeJrUA6KOBqxIlxr3cmy
|
|
||||||
/23e/Gs9xOztJbH5vS3T8yM2uev0x3JC9qcMxRtzoubDqmR13dlC9MSEzIXrHkRDUYinRMY2YcT9
|
|
||||||
BH74oR7SPiLjbTgtbrJwV0Gk1k0BBOvuO3D3J/uuD/euhGmfh11++fa2SIF7Gho+rHFIuuZvwiGS
|
|
||||||
0hXSruHxZUjMeYKc3MBe7t0lyCjTPtijFFDjjef+B+nqEM9Gfvz6bWNn8wWL8FWrMPHiHPqythAy
|
|
||||||
urDjA9CJYIvXFWtucIlxPKLTLSwiMUBm3HF4Qm8vklXunA56U22qFd8BYwffHtlJUHcGNo194Ah/
|
|
||||||
j+4cFpquWFeQUtn9PZgEmsjE2VlW2DgLXVR0UZKfyRakamC8KAvTpBJhJL35DYpsuIUhIwLekjgY
|
|
||||||
KsWqTYCKlGgRhpHY4qI+RTIAmqk1cq4rNyanPYXo5n8H0Iyxwf1NCVRs98C6W1gvScbHO1RiUe1B
|
|
||||||
1DjTRUOnU+QNkbK9RjppfCSWeFahkXIzLRmWmaDzYU65LVtbZzFv+T4Q3XBpUqU1BQL/Tds7JXrS
|
|
||||||
CLHsrWEEjB6f5n4/FrIo31hu+3n6TuZiMnHR77m2S7iv117X629+qpWOXiHTb2iyTy4Urd4wyW/C
|
|
||||||
xpYd3Af89DaeF0ypRmGcCqLRQkep1lLtPDilsFFN1Yx/RipsCGuHoHNmsazihTA5WvAhqrSlQfnm
|
|
||||||
AxaW/hGxZYWpL7AD4rhHSvTeHEHF/90gw7e2L4yO3OjGQwOf5ps135MVQeEzFPC9XIpjEGChySDf
|
|
||||||
S1D6vcLC+Pxz0Pj2g7Jxh4zR4luw9p6L9l3P3rKXSzatxH9Hzf0bxYx+h63G+uFRAZVpZ56hq8F0
|
|
||||||
rxM2k8VNdfwHPmsQlazXIEICTnBkFldqiFeACRwA7B8S8cBbcntGO7PGhheVz9QHpv7T46zg7vDm
|
|
||||||
g6flQbyhmcgT3YqW8ZJvxoDz5kXa82WicJztLvwI3NqQMKWl+na6zPt9uW8UEgRtPo3O24is6YLg
|
|
||||||
ZfWBGCvhZ66Y8Rc/uxT0w/nYsdVxr1P5qe8+PvfizjE3vHgD7lisguf9PWxNL/WmBS49bYFnApnE
|
|
||||||
A6jwEttYcXIOMRbPW5+9+SPNIYM5d8t+I+fqXqup9ZV5wb42pZuAoRilTpBwFId2SDjVJJOqhXDo
|
|
||||||
D2dmq86n8oVnmtX7UpxpKIuM8Ou8HCvns1pDomQbCvTUyH9cu51r6lzJf/o0jx5askZmmmXSF1ZP
|
|
||||||
nJHNdMczDUEVj/pE6IdEe+WlmDQkRnvFgz9SFeKoR47HSpLairjK4hjWnXTy9uircKFQF+ely3RC
|
|
||||||
75XIzkG9mOSL4cAdf/tXgIMjJHNJNKaFB3f2oa+WwVSURbt/SRI1pTVYQ7LFcJKoIa11PTf5B74x
|
|
||||||
ExRg11ELTkHfgJUObA7ZMSEMO9yfG7zPipMg8kdYeCl1CfldlUUXwfSC3XSza6HNn+szpobYbGHL
|
|
||||||
3MPm7GO04GjXWYnBHD6mbkN7Vw8Yr5JASa/JykTE1ciJPsz84r4XtWQwyeNeNuwzQxNNFGOikn5k
|
|
||||||
4xggvKZ2DLKjyPHGJF+O0lAXuU4UsKfN+NcROjDx8N7VJmr4PFzMd3DZd2O5vPZgXz7szVGM06Wj
|
|
||||||
fPYopZ/BzKwOBFz66OL+lXqlZHzQS2SclTby+HauXDnv65aAPw5rBIpypFJD/CtCl/CPAvfcOLgx
|
|
||||||
xy+leD8wFwFcn4VUTHSCzJKEGeyF3ARVgoDk8td/XFshM6/aVGLekga6X+/UCBuA49gwtS5tmYpl
|
|
||||||
3rv610HZ/mgKXE/LQ/YyY5GYjr6klNrAGApyRV290md6e+ujLvam18dU+ouxJGl2KdX+d0skW3CD
|
|
||||||
5TFQs0NBUdKv8qxnxF3ZZTXJounwrCyDnSvO3OX07A4e8BuPP/7zAerER6k6Cw3ZzICcN+lL3+f1
|
|
||||||
H1b82QKuK1NRPTu7PJt6erlhjuRU1xWUfJ5yLJoGvJCi76J4l9q9/l8c6AxaZecN+rUOD1qTb9jQ
|
|
||||||
JdTFlvs/Dke0Oq8vutUI2CM2fvyUZa9eZWE9VOR+5bg6tO0u8of2dFb2xJg3PJP6TLyDfKnOKA8g
|
|
||||||
VkYJdNCqSFF8HzFuUtTatmARGCSKJNHk2mioUG+lcYnrgHcr5ihwzqXDwbKteeKkRYmLt/N1vIYh
|
|
||||||
OPu1Mr19S1141YpBxfdLNX8RqAe/lxdoqT3zh8YWYnoRUG0pBJFjN2Lb0ssH02l4ceky35+JeQ71
|
|
||||||
KtaTyb4FqSxVocr7Yx7yY4iq9NiGygm4M3bByyprTtN8b/PM2nMDv5oag0NFn/5+lZK87rmN9jrv
|
|
||||||
FgXNKEFgvM6zETZIjCatd2oSZXEwL9V0+ihnII6Ms3Q5LWc854RmsuoWghd0jUOqbH/YR/USwb5T
|
|
||||||
Vc21qHbrGGd+ZDd5O/rjajSycpZ3wenHgGhsegfMKSPjqJ3j3pwDA/i75QYELq6sYr2cDijkFqVN
|
|
||||||
d5oN2HzcB6tjAa6s34UEP7pX7d4texxUxtBbEe4cayU1cf2IfltP+MLcTIhHJtKt64BRlLvxt7U6
|
|
||||||
LsZuTmPZXJ83c+tIN5uiWJcgUzqiGIJlcpuxYxV1pQcFVpuAobbcapl8aQ32M5F0CGU5iuiJDEE+
|
|
||||||
qaAMEkyooJxSTeBH8LfhCQHhl4IsikQa/OSoS4PVdG3suYSPK/eEDcNH8TgXiFufbnLovP+swQ7e
|
|
||||||
SS5N+7OHQG/CW7VOw4GNLT2fSkPBucd3E5PgVweUzV+YmHqwdLMb+UYFywHUJ1AQOyXC5r+JWmxg
|
|
||||||
Ay/5gxKmGh54jwXCqGRkRaVXfWZ2ZwAin1ExUJE/FVocqpCGe6xt4NjEPQtxNT6JkhnpuJyo4TBE
|
|
||||||
nyv4NdeOlGhCNG/p6v2B5Qq9Ixc66sBrq7zg+ZX2rCE+VCnGsh/kjD8NJtREbHViHZgLDA5U4tlC
|
|
||||||
ffQNCKqFFAlS+7OrdhIlzzsQqMWjAqqu0uaocafIUlrTC91nAf7Or2iWmgEY4GPzaKnlJpnw6z4d
|
|
||||||
TxrBNCvbs6lQ2HooKcrQX8G9NvKhX5aMAO8vzEoIn6m7WzcEfOZrpCVgoAwUUhruhOlQp3WCXWzB
|
|
||||||
gqX0MEq96N9lO+9wUoW+ZcLzC7CQltC08m4aMFokdZ5ZkcT/IwLyewSAdQVd3ufmErqBp/6wYxqP
|
|
||||||
30BhIZrhDLar7qzC9VGj/rFwt6nhF75LyHZ776Thv1l4Yx+Tr0kfTv014s+nhy5e/Z250h6LDNoY
|
|
||||||
vdOzF14988bOzENvZO86TaPYE5aQp1yzFepI2yKq84SLe25BUe6vWK14DzD7fD8zaWKfuV4L9C9o
|
|
||||||
gCeyD6qGOwPtTDMl0mF0X0pQGjKLJ+MifNs2yxCqkNPl43dMaE50QFCoXVcwBWLdoGJs3Y0yjWsM
|
|
||||||
GjUjcmKOhK/jl8g0S8a9RtHsCOfm3yI3PVa/xPNSvzjdcd2WsDrrN6x5W/q59NKuHlCPHqPDCdRw
|
|
||||||
ZNCetbD6AsU0CGW3U3WPwDCxWGuisZUqpwVVPfncMxGmOnRshLjMMh0FFrWIFrIz3AgjmKUxlSwk
|
|
||||||
09toMMleRvV6fy4krUh4qpxntwjVfqnsIP69Vucio0WE2rX07o7uvELTT71kqnc3yUonVdgvs0j2
|
|
||||||
gGjrybp9B0jEj6tSfD4WnLxEQ1FBUZfdWOwmfK/4MtaYRrWMaAob3BffYlUI7yCJ0LdbrhMspyGb
|
|
||||||
AgC0UWxsVWXCrzOWIDgVpXDTI3GnYwRjsGjyvthCMgQdn3vFDgv/VlnCkaX+fFWGU9KuHHXe9BVZ
|
|
||||||
+mA8cEtJxxM7TGPlMEkgUCNvxBp8VPh1sds0hYbkgZNoZgOygHggDr6songosZIa+13z2mFzGNMl
|
|
||||||
bDnW/TJsz0NX+zjzZisg3cGjgoQuqyZj7TIuRdhGv6d3comy6W6snUw4bWwi0Mkg37rbxlczU5W0
|
|
||||||
790H5MjJSLO8Ogxr1i2BAErYrY5brF+InUBN0wrpC4FHcKuQduBgA0vMnS12m0U4wFFcf5Yni2tW
|
|
||||||
ZO5TRE4UMXYhOV7ITddzRJyR8BqxbjlauqYufXA/usuVGWUcXGgmJkvomGWa5uPnNVGQseoJld+a
|
|
||||||
pjxzHxbqVLRW68WfdAqoztDKKi8rKnVcpqp6K9DazAXoe65XmWpuyAGL/B74JdFI/MngUe/IIRo6
|
|
||||||
x0wK3kaGcpmOHvWXJ63NaL9uInCiNTHA4fbRHJeE1NjdUHmnlVtf39bwViu7iuPwdJb9XtnO5Oth
|
|
||||||
yazQ2+yD0QfBZyZMAqZ3gnXcfFxketYTWAeAHP41MsM70YZoRbr/KMoNedw8xvcvkhiQOn1+kzBE
|
|
||||||
hoQwNbshwpBj3Oj9MG9hVfyFIr64EFb0By8wh9J/S16kRQSyUgXNGuvUVqGIYcCKuXLx7QNIQCN1
|
|
||||||
sCElhyP9mNcx5pGBVS47bUp3WaTu6V5WZGGGzetdZUQIUxUZg2xeURlAUsIGAp5q7S+bxjrfrpln
|
|
||||||
gU6Q+de+g8flVExjQlCWRQjXD2FnbdstXpkCfWfrm+iY+K6WWSpGQcnlsL1mb2F7rbKuas30i5i9
|
|
||||||
qnWeAqZXUZo6dAsBleb0EtWbBbu7ttsi0wgKslyj3Q7zmB+sohi16PExXvxj4mfDU1uvUsx1lIwq
|
|
||||||
5fDdBCK7Mf0RJaxZflZLZSePwu+nuJNdBzQCHd5bhRspySczMldZNRcdcAV+hqF89Qzii2EGjvjR
|
|
||||||
zhbrcLq4J/KIaVTKNEQUFvN/7twtUekNRztUiBBzuRbOju9bx6trMSjAH0neJGj3soZkYhxJJevG
|
|
||||||
zuzdyYb3FolyGQD59AlYRB2nNmQIulRyooqUnN2Anw7jA2rK0aVGKh1Iytp2aFmSQt6RIe3u5/eX
|
|
||||||
E4U8ldk2I2yNFxDPTco+vjTiTiXhoZ1VxfE6ZX7KgwWpZ2vJ59kWi6/smk8f5nVzbBLdZnuv8ykN
|
|
||||||
A6KPMTLY3yGL2v9ia8gqfuW3QBimJiZAMpefgUFms9tKWFbyTM7+Lr9ABOniS9iEJtzgZknbJ5fh
|
|
||||||
Zf/VnKifLSqOMTl0qwx5hIolPrVN7D43JhSIv+j0uZl9W533Fz4Sm1GTaGB6jUyxL8RifBgFh31K
|
|
||||||
d/C/Rrpa7uIub8HEVFeKLjSTCokF5fsylJzMlhOLr3f43joGEpEChJY3wsmzI95g4L+h0xvLwlG6
|
|
||||||
xTHjsf9TmtJkHqjA80xkSjNBlJ40IzwqKmYE4i7mvPpGfeq2PBn2Orxs/bymadq64i3DykwRXpmW
|
|
||||||
SvsESz9Gi1eMLwu2ZafVznsompr1TBqLrqrjjDx02vnLZTRsPxq9lWwj0h+BWk/m837aRd0SJMc+
|
|
||||||
U6LDgvMSv0gm0uqhKGXtAJD7ThkmJeEiMdmXBn66w/2h95Ysyxzz4ILasRZNqwPtinAuX2JO8YYT
|
|
||||||
ChoUJfigEzuQFGW4eJukm3bW5m8/Hz/NSfsagUnadtH0wnwbGcqp24qcQ3mOPMX9kMWis6VCdcA1
|
|
||||||
M4NuiEcgQ0O35QxvbKrLOeKxtBZ4Ag1zxIqy3gAxdQPBizISCbzt70NX85Uyf57q089PNyvOpzzp
|
|
||||||
yBENiLzSoYWQtKGupSaOSXCj8YZyDx1LBfeVos1UVkJVq2t+ChdHTY7QcpbKMEVguhM0+0YyXwU/
|
|
||||||
Ly8AjL+4LKXuAy8Fq+8/PyzjGl5NF5+fV4m2p+KGIdGvPoNM8Uhfn59OMGZD9KHd9cCmuyqYl+9S
|
|
||||||
s1H811d3wzpVSA7H4/B9t5AX7ARzwGCr8A+h5ZAhUqb42Wa9R41+fpLVhVuRasR/v1i8kRmt3o8u
|
|
||||||
7rkKrHkWVt3n0x72DiRrwX+4TBAJ6NGgCbOyBpLyoJSsWt0ViFOb+VbEqOMHaokLGTOyPAexcLep
|
|
||||||
LoIc2N88Z8Bg8UGu/l1kDqiQ+GQGz5CBeN1Sh7q8VstkCraLC/coMG28PxEOrIWIiZss2TfoIk/j
|
|
||||||
vEtsaF7ZfRKnmusxcouCru54hXzmNLkK9a5OfdFw1/WnDeRnF7gYR4Xqz0nEpLH57AGTjdjUlcg5
|
|
||||||
EICCQzzg6x+AT15XJpVuPFgqFdg/QgdwSdZabc9q+cbRg9u8zhZ65Hi4wJ10B5raXQSF0yWWHSlT
|
|
||||||
vr8v/q7wog+cohblfJISQEekyqchK4m7QptL1iaNT0SlUX323/Feuf9uJkj6MJHRIiVVq27W3cWk
|
|
||||||
fx1T42CwIJK2d8+9x5/+xR9X2XnUmsAX+g5CsWLqOAeoaQ03sB64xHkNP/T5YCEPcoWf/tywk8IK
|
|
||||||
PClrCoVZeVOT48CCGHfreQytzkDpRtsiB5NkrmpIcnZZ3km5ngxRYJKWgjavUEvO0QXKNcpxpsNi
|
|
||||||
G0upIDcEFpgso5MJ6tv0JPo53Wkx5616iKWUr5fmJGO5KeQ8LITK0zoh5HhCN+hOB3IvBD+zMP95
|
|
||||||
tk6JTvHv4cgRiPtdqOzLVzFa+0w3ryfdyAsd1DbELrvIPl8flomke/vAjWhMNDt/146XbdMq3q0G
|
|
||||||
V9hzF9dy3sNQks+B95T0EHWMj+Xmxr0LtFjbZfMRzxdWlbVbg3C03zXIgWaRL6vdUwkxQHzlc5UE
|
|
||||||
Lku/+bbb4YtRogPWln9KulXkqvphdg1qW4991p8+rWe1Oq8UiU/rv7Diy2KGqnG8N6QSBI9hTv0I
|
|
||||||
5LH95fJ69seDMFZGlfZBojaTPKXSI2yWAgmSeZir327Vlb8Hx417xa7O86rl5JHyir4g/43VrJPZ
|
|
||||||
zriThgDzXHEVkmvrByMV5sW018KFa/SXcdtkPP6r3YX9hKZR6XmOZH9srzxo26UCfGzTLNAMFy7S
|
|
||||||
27hlD5khEmVNskCs03MOBdrfmOnVGCWEKvDjkJppOcHSJUWk0I+2nUrsbokt3egFhzsFzk5eQGAl
|
|
||||||
Vt1TfVM0vaBDcoEab3mdtsJB2DV5cRd4hlC0aLD9wcj/wHzw2a2yuR5lOLXUNaXL9gwvnCclVAAJ
|
|
||||||
TZnu4s6ROk4Wb13XyXaDTOmoXw4Z7rztuIPyDlfW/0RaXewrmDbZIYiLwF+Fuy040gGyDtOyDl9M
|
|
||||||
YZZwXZhO7gqlLc0ES0UsuTS6Qpe7TTZgkeqBjRTXrPmF04ak+uB+G0oKa0uWqGys94jBL7Ng/wbl
|
|
||||||
9BvZWCoPEhIjPmwVkhEEQbyx6+PElHs9WelUi4CJDEdV1amY2Gj3aO29DLn0wyLgwPLq7lFqEA+X
|
|
||||||
i1KgporjqQcjZOdkENi5HwQxzGI3ez+mnXhR/lO/reYnIR3t+KtCKxZlWde8JBmcNKFIBNz5cthc
|
|
||||||
wlkCyQmWeM+OgDzVnv76fLg3L72zlNt35hqLShPi5MEqwBvb4RBz4qjGgEjseV25HRkuSRWPUs8L
|
|
||||||
l8ojwRtkYABPf0qEOKvl66PcLqYBLryrp52a4zu0KiqBFkhCJsVkHBsHL9LXnjzRbPOYqtVEgol/
|
|
||||||
XlCPYxiJMLUWOAyYPGmSsajXd/84qQvZa1IuTDG5/ofT+kT8S6x1ednhLisR428z3Ti8LDwE0/Uf
|
|
||||||
ounjLwGv45+Y3E6tRWSMNVHWe8Uy1CfISpahlvSARwu0NV3r+8DU8/7aXI/gFvWPjy5ZjZomZa7f
|
|
||||||
4+dH6e3J7XSpAkmikcLCps4dNUF1hexT3l+svNhMMGytWJiUlYwVK/t0W46bpe1pfKIRXvDchg2B
|
|
||||||
9w22rjdF2meS7yV2Lpg0TpdPZRiyTtR6zrQcIFVicsFIDCUzAItPytcMGL0DR/2QA25cBJwGKlU3
|
|
||||||
4IHcREckW7qcGuWO38TGWVxYDWy6PRkifWkBXoGGN/MXeELBHOA72OHOmHejBpnewTVP4QNCTZ5g
|
|
||||||
Sp25UYpjd9XJmXGGRUiDZ5OnYlkj5cKWMPwR5Bz5qhY7r7eu+4LuVICgS4q8L6/igjD6r+/R2Q2W
|
|
||||||
N8Nzp/mxVmtnijnqLevjoR90uBL+aXo0tKgLKuIdmmCq+ziSiG/k6XYnKxO3AtrQSVCq+pV3mzL4
|
|
||||||
7P8tP+vnsRA/XSdUjYtf9zShIZJW4J9zOWtLuYn0JemXOvUeMYUCNPmc0Q8zynSTItiwjRN3l/Ox
|
|
||||||
+kUKMeRHz9C+44ALafaDjNkA5ay9ruUC9NOHTL0FuD3mNrsxIgAUvFKjoMc/9Rv54TJ8XaETwXPu
|
|
||||||
Th0Xvgjvy7VpBivJtvi7KhB90aaTG5FSZFgvK5yLL+LvqQZ8NllH9QopCwo6mL2SE5jLovbRPNh/
|
|
||||||
4syk0Smy7KRal3O8Wz4N2X/YNs0TyWqIru9mZh79lQQTp2VujNYmErFXIOf8dba0Il6kjiOA2ftD
|
|
||||||
vMpL+izKqcZP9k5MuSNiHTLQjGthOZJ1EQcwMPtB2JDj9SRhiBKOG5CowbfX/2vuGSHl4DrJe6BU
|
|
||||||
Z9zralmsAI2AFSKmDS4cItvdsZUV6JJPTVKNs4GX5pGjpVHH721NiqVYLqM3JmDqbDMPPoo11Y4k
|
|
||||||
XywsHMGnj5mnClNUzt/9VFOorFBeKXG1a049S8H/r0P+S1ZEiZda+CvDd6/zIICO1B2crvWlEoAw
|
|
||||||
fk3xpU9XAHkmlett5A8imTSPQaLqalg7448mRQ6dm3VgIIhgFtsbWCd5mHzlKO+12IeXe33PZaWK
|
|
||||||
9B/ZmMwevm24iHAYaj8Ni86WWOub4ndRPogRGwLBzM0mNmRsC5GleiVKCRTy9RWpjVkYiVWjQ/ey
|
|
||||||
erD0lITZuZ7/C+F/cB0akttjOqWNiFblTvWCvdECUC/OTxy4ml+BnFxSCYKeqfUsqbcXufg0G0FV
|
|
||||||
H9gUqtMFUwcYPxzUqLWW5HccI3ccz7lqhdFs0auPdoXRnzsdtw0KdNq3ly3aXRPyudxR1iq/ACtd
|
|
||||||
rO1+s+DZButoaRYzxPcB7pMXZfymWTjYCt/DRN8VhclNTNQKd+/YeYWQle3aM0uPgBg2bX7zG4E0
|
|
||||||
Uw1BqLdSgdPc5+5jtVdzwHHl9eqviafpBQx7Q5hUM1zOyaNs9RZzrht5b30W/atk8E3lGi+oO726
|
|
||||||
w5s9lvOkzgEau2mWaAcsv2h4++lMPRnWpEmIPcQD6tDPsfRIfF/+otmY+hunGr69zExBvubDuuTg
|
|
||||||
B07ksO8IpOgifd327K34NH496BE1WvxBhkcRERW6bo7OQhzr19KioKAiUmQHXmS/PwvSdp9G2UjS
|
|
||||||
bj+Oy59MGMTIvjjGW6S5ag0kTrteVxl8vAp91/SGUf1loXjX0CKXh5M/24No/TmSNtPhWW/D/jMD
|
|
||||||
sBkUFmLT5oTFgnLq77IHgj8Gwjy8CKaPduIJLAYHRPIYn4iV9IlpV7utKzt9E63Xqh5Jx3uFiw+0
|
|
||||||
maZv6SObDYivZmvEms9+jjZy1cbKSCPBmlcyRQw1qZCZwJzVBT8qZuUNXIq9PZ1OCGSCw+JJg9aG
|
|
||||||
MPWbf+HoH0vdvQj8IArsU5zQqC0w0DPwIWdU1GTz57Lart6xOC5VzgNl7R9Vqw4+JhS7vkkuEJfK
|
|
||||||
94+cQ6dG6uUcbUzZWGIIWOhcAMn1KIvA75d+RFEPY+TZkZYtcVQc65AepTwWre94yrQuK7d9rquL
|
|
||||||
uZLxjCQaoF76S5LxDiVAGGo9bR/emE85sgIGo3hFLwp71MRlQoUi7JLYGqKlwYRfPNiKN2DtH8s2
|
|
||||||
YNFag6UzAVNK0q5bXHDLMdn9nIadLd0lufQvPsOdSEv/mnYShvxX6Shm4gqCiBHmq4yUo08wZq/5
|
|
||||||
FtzlHUQ9xJ0Oj+ynRaTGbVpVR/1ov+EY9lyVdVzsjHk3uWKcry0GCbenovirDapqytCKRCubvKxl
|
|
||||||
YCeLOtd+/AkdOmPOjwfMXSq/vSsiA4tgg7tJ/iT41UVyA57If5Ur1ulh+IX6zo5hW3Wm5YmI0eos
|
|
||||||
Xbou5NT3tRSbX14ZMM1oOSh0OWuphRCqPQL/rA+50csqYQTHFxOo5O4JsMu9pj3IL3LaNXb9yB16
|
|
||||||
iLYRvdPIxk1T4Fb+eA6DA3YmhYIh+2vx11BNc1EZ3miNWFcEowXQzah/EvlozaVtFyv7gR3FLQmu
|
|
||||||
edVf2ApsN/aplVUa/suQ0/szocWyHMIdhjMUjrI6ZcWL4/dxHKecpcc2g88ZtLYJfqLVKqvg0kzQ
|
|
||||||
Iodi8G8V4twEAT4oXeI+EwLfxJopXRC92sDyZdfGBAjV0GRVIBYKmClloiFCFuGszAqHCeq1h8kM
|
|
||||||
Ix4LvCMpT10bOX6/qsQVy6KxmD7vys4KjTlJdjIBdtrqBSRivXntSMLHivajRSC67jhOZcvJMRC+
|
|
||||||
CxWXhEH7n4kjgpfphsz1UfK+Z2NXgLFJzTwgEh/zovApnoP6mhElkGMVXSLE+TXTSo4Q4YM9Fx2N
|
|
||||||
aJMhUco6gCwEspBK6vXzgRAYvW//HudCuX8HB7GxLgwtiGQbACcn+81ccZTrgPYJ+kflDD87iidz
|
|
||||||
C6bNq2LCjnZIBHBhDObnQ8vWs7GX0/X+1uFCAHOsUeK+mRVlwMAxIP7/kZD/W3+CAP9PQERRBgr6
|
|
||||||
fyjYf83330Ql+9/2fwBQSwECFAMUAAAACAC7ZhZJT+TXP2QiAABjIgAADwAAAAAAAAAAAAAAtoEA
|
|
||||||
AAAA4avjpqGglI2ROTQuemlwUEsFBgAAAAABAAEAPQAAAJEiAAAAAA==
|
|
||||||
--2NqJR3m2cLnhEraiqXA4Q9hqnmihx7b7
|
|
Binary file not shown.
|
@ -4590,7 +4590,7 @@
|
||||||
"org_id": "2",
|
"org_id": "2",
|
||||||
"orgc_id": "2",
|
"orgc_id": "2",
|
||||||
"proposal_email_lock": false,
|
"proposal_email_lock": false,
|
||||||
"publish_timestamp": 0,
|
"publish_timestamp": "0",
|
||||||
"published": false,
|
"published": false,
|
||||||
"sharing_group_id": "0",
|
"sharing_group_id": "0",
|
||||||
"threat_level_id": "3",
|
"threat_level_id": "3",
|
||||||
|
|
|
@ -4593,7 +4593,7 @@
|
||||||
"org_id": "2",
|
"org_id": "2",
|
||||||
"orgc_id": "2",
|
"orgc_id": "2",
|
||||||
"proposal_email_lock": false,
|
"proposal_email_lock": false,
|
||||||
"publish_timestamp": 0,
|
"publish_timestamp": "0",
|
||||||
"published": false,
|
"published": false,
|
||||||
"sharing_group_id": "0",
|
"sharing_group_id": "0",
|
||||||
"threat_level_id": "3",
|
"threat_level_id": "3",
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"uuid": "c5f2dfb4-21a1-42d8-a452-1d3c36a204ff",
|
||||||
|
"name": "Tea Matrix",
|
||||||
|
"type": "tea-matrix",
|
||||||
|
"description": "Tea Matrix",
|
||||||
|
"namespace": "tea-matrix",
|
||||||
|
"GalaxyCluster": [
|
||||||
|
{
|
||||||
|
"collection_uuid": "7eacd736-b093-4cc0-a56c-5f84de725dfb",
|
||||||
|
"type": "tea-matrix",
|
||||||
|
"value": "Milk in tea",
|
||||||
|
"tag_name": "misp-galaxy:tea-matrix=\"Milk in tea\"",
|
||||||
|
"description": "Milk in tea",
|
||||||
|
"uuid": "24430dc6-9c27-4b3c-a5e7-6dda478fffa0",
|
||||||
|
"distribution": "3",
|
||||||
|
"default": true,
|
||||||
|
"meta": {
|
||||||
|
"kill_chain": [
|
||||||
|
"tea:black"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"relationship_type": "ennemy-of"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
"distribution": "0",
|
"distribution": "0",
|
||||||
"proposal_email_lock": false,
|
"proposal_email_lock": false,
|
||||||
"locked": false,
|
"locked": false,
|
||||||
"publish_timestamp": 0,
|
"publish_timestamp": "0",
|
||||||
"sharing_group_id": "0",
|
"sharing_group_id": "0",
|
||||||
"disable_correlation": false,
|
"disable_correlation": false,
|
||||||
"event_creator_email": "raphael.vinot@circl.lu",
|
"event_creator_email": "raphael.vinot@circl.lu",
|
||||||
|
|
|
@ -138,7 +138,7 @@
|
||||||
"org_id": "1",
|
"org_id": "1",
|
||||||
"orgc_id": "1",
|
"orgc_id": "1",
|
||||||
"proposal_email_lock": true,
|
"proposal_email_lock": true,
|
||||||
"publish_timestamp": 0,
|
"publish_timestamp": "0",
|
||||||
"published": false,
|
"published": false,
|
||||||
"sharing_group_id": "0",
|
"sharing_group_id": "0",
|
||||||
"threat_level_id": "1",
|
"threat_level_id": "1",
|
||||||
|
|
|
@ -1,17 +1,33 @@
|
||||||
from email.message import EmailMessage
|
from __future__ import annotations
|
||||||
|
|
||||||
|
# import json
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from email.message import EmailMessage
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import List
|
|
||||||
from pymisp.tools import EMailObject
|
|
||||||
from pathlib import Path
|
|
||||||
from os import urandom
|
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
|
from pymisp.exceptions import PyMISPNotImplementedYet, InvalidMISPObject
|
||||||
|
|
||||||
|
T = TypeVar('T', bound='TestEmailObject')
|
||||||
|
|
||||||
|
|
||||||
class TestEmailObject(unittest.TestCase):
|
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, "subject")[0], "письмо уведом-е")
|
||||||
self.assertEqual(self._get_values(email_object, "to")[0], "kinney@noth.com")
|
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")
|
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_name, str)
|
||||||
self.assertIsInstance(file_content, BytesIO)
|
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"))
|
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, "subject")[0], "письмо уведом-е")
|
||||||
self.assertEqual(self._get_values(email_object, "to")[0], "kinney@noth.com")
|
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.assertIsInstance(email_object.email, EmailMessage)
|
||||||
self.assertEqual(len(email_object.attachments), 0)
|
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"))
|
email_object = EMailObject(Path("tests/email_testfiles/mail_multiple_to.eml"))
|
||||||
|
|
||||||
to = self._get_values(email_object, "to")
|
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[1], "jan.marek@example.com")
|
||||||
self.assertEqual(to_display_name[1], "Marek, Jan")
|
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
|
# 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"))
|
email_object = EMailObject(Path("tests/email_testfiles/mail_1.msg"))
|
||||||
|
|
||||||
self.assertIsInstance(email_object.email, EmailMessage)
|
self.assertIsInstance(email_object.email, EmailMessage)
|
||||||
|
@ -71,11 +87,10 @@ class TestEmailObject(unittest.TestCase):
|
||||||
self.assertEqual(self._get_values(email_object, "received-header-ip"),
|
self.assertEqual(self._get_values(email_object, "received-header-ip"),
|
||||||
self._get_values(eml_email_object, "received-header-ip"))
|
self._get_values(eml_email_object, "received-header-ip"))
|
||||||
|
|
||||||
|
def test_bom_encoded(self) -> None:
|
||||||
def test_bom_encoded(self):
|
|
||||||
"""Test utf-8-sig encoded email"""
|
"""Test utf-8-sig encoded email"""
|
||||||
bom_email_object = EMailObject(Path("tests/email_testfiles/mail_1_bom.eml"))
|
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)
|
self.assertIsInstance(bom_email_object.email, EmailMessage)
|
||||||
for file_name, file_content in bom_email_object.attachments:
|
for file_name, file_content in bom_email_object.attachments:
|
||||||
|
@ -95,7 +110,7 @@ class TestEmailObject(unittest.TestCase):
|
||||||
self.assertEqual(self._get_values(bom_email_object, "received-header-ip"),
|
self.assertEqual(self._get_values(bom_email_object, "received-header-ip"),
|
||||||
self._get_values(eml_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"),
|
self._does_not_fail(Path("tests/email_testfiles/mail_2.eml"),
|
||||||
"ensuring all headers work")
|
"ensuring all headers work")
|
||||||
self._does_not_fail(Path('tests/email_testfiles/mail_3.eml'),
|
self._does_not_fail(Path('tests/email_testfiles/mail_3.eml'),
|
||||||
|
@ -107,7 +122,7 @@ class TestEmailObject(unittest.TestCase):
|
||||||
self._does_not_fail(Path('tests/email_testfiles/mail_5.msg'),
|
self._does_not_fail(Path('tests/email_testfiles/mail_5.msg'),
|
||||||
"Check encapsulated HTML works")
|
"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
|
found_error = None
|
||||||
try:
|
try:
|
||||||
EMailObject(path)
|
EMailObject(path)
|
||||||
|
@ -119,7 +134,7 @@ class TestEmailObject(unittest.TestCase):
|
||||||
path,
|
path,
|
||||||
test_type))
|
test_type))
|
||||||
|
|
||||||
def test_random_binary_blob(self):
|
def test_random_binary_blob(self) -> None:
|
||||||
"""Email parser fails correctly on random binary blob."""
|
"""Email parser fails correctly on random binary blob."""
|
||||||
random_data = urandom(1024)
|
random_data = urandom(1024)
|
||||||
random_blob = BytesIO(random_data)
|
random_blob = BytesIO(random_data)
|
||||||
|
@ -134,10 +149,9 @@ class TestEmailObject(unittest.TestCase):
|
||||||
broken_obj = EMailObject(pseudofile=random_blob)
|
broken_obj = EMailObject(pseudofile=random_blob)
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
found_error = _e
|
found_error = _e
|
||||||
if not isinstance(found_error, PyMISPNotImplementedYet):
|
if not isinstance(found_error, InvalidMISPObject):
|
||||||
self.fail("Expected PyMISPNotImplementedYet when EmailObject receives completely unknown binary input data in a pseudofile. But, did not get that exception.")
|
self.fail("Expected InvalidMISPObject when EmailObject receives completely unknown binary input data in a pseudofile. But, did not get that exception.")
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@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]
|
return [attr.value for attr in obj.attributes if attr['object_relation'] == relation]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import json
|
import json
|
||||||
|
@ -8,7 +9,7 @@ import pathlib
|
||||||
|
|
||||||
|
|
||||||
class TestFileObject(unittest.TestCase):
|
class TestFileObject(unittest.TestCase):
|
||||||
def test_mimeType(self):
|
def test_mimeType(self) -> None:
|
||||||
file_object = FileObject(filepath=pathlib.Path(__file__))
|
file_object = FileObject(filepath=pathlib.Path(__file__))
|
||||||
attributes = json.loads(file_object.to_json())['Attribute']
|
attributes = json.loads(file_object.to_json())['Attribute']
|
||||||
mime = next(attr for attr in attributes if attr['object_relation'] == 'mimetype')
|
mime = next(attr for attr in attributes if attr['object_relation'] == 'mimetype')
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import json
|
import json
|
||||||
|
@ -8,86 +9,110 @@ import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
|
|
||||||
from pymisp import (MISPEvent, MISPSighting, MISPTag, MISPOrganisation,
|
from pymisp import (MISPAttribute, MISPEvent, MISPGalaxy, MISPObject, MISPOrganisation,
|
||||||
MISPObject)
|
MISPSighting, MISPTag)
|
||||||
from pymisp.exceptions import InvalidMISPObject
|
from pymisp.exceptions import InvalidMISPObject
|
||||||
from pymisp.tools import GitVulnFinderObject
|
from pymisp.tools import GitVulnFinderObject
|
||||||
|
|
||||||
|
|
||||||
class TestMISPEvent(unittest.TestCase):
|
class TestMISPEvent(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self) -> None:
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.mispevent = MISPEvent()
|
self.mispevent = MISPEvent()
|
||||||
|
|
||||||
def init_event(self):
|
def init_event(self) -> None:
|
||||||
self.mispevent.info = 'This is a test'
|
self.mispevent.info = 'This is a test'
|
||||||
self.mispevent.distribution = 1
|
self.mispevent.distribution = 1
|
||||||
self.mispevent.threat_level_id = 1
|
self.mispevent.threat_level_id = 1
|
||||||
self.mispevent.analysis = 1
|
self.mispevent.analysis = 1
|
||||||
self.mispevent.set_date("2017-12-31") # test the set date method
|
self.mispevent.set_date("2017-12-31") # test the set date method
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self) -> None:
|
||||||
with open('tests/mispevent_testfiles/simple.json', 'r') as f:
|
with open('tests/mispevent_testfiles/simple.json') as f:
|
||||||
ref_json = json.load(f)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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.init_event()
|
||||||
self.mispevent.publish()
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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')
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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 = MISPEvent()
|
||||||
misp_event.load_file('tests/mispevent_testfiles/event.json', validate=True)
|
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 = MISPEvent(strict_validation=True)
|
||||||
misp_event.load_file('tests/mispevent_testfiles/event.json', validate=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.init_event()
|
||||||
self.mispevent.add_tag('bar')
|
self.mispevent.add_tag('bar')
|
||||||
self.mispevent.add_tag(name='baz')
|
self.mispevent.add_tag(name='baz')
|
||||||
new_tag = MISPTag()
|
new_tag = MISPTag()
|
||||||
new_tag.from_dict(name='foo')
|
new_tag.from_dict(name='foo')
|
||||||
self.mispevent.add_tag(new_tag)
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||||
|
|
||||||
def test_attribute(self):
|
def test_event_galaxy(self) -> None:
|
||||||
self.init_event()
|
self.init_event()
|
||||||
a = self.mispevent.add_attribute('filename', 'bar.exe')
|
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) -> None:
|
||||||
|
self.init_event()
|
||||||
|
a: MISPAttribute = self.mispevent.add_attribute('filename', 'bar.exe') # type: ignore[assignment]
|
||||||
del a.uuid
|
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')
|
attr_tags = self.mispevent.get_attribute_tag('bar.exe')
|
||||||
self.assertEqual(self.mispevent.attributes[0].tags[0].name, 'osint')
|
self.assertEqual(self.mispevent.attributes[0].tags[0].name, 'osint')
|
||||||
self.assertEqual(attr_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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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
|
# Fake setting an attribute ID for testing
|
||||||
self.mispevent.attributes[0].id = 42
|
self.mispevent.attributes[0].id = 42
|
||||||
self.mispevent.delete_attribute(42)
|
self.mispevent.delete_attribute('42')
|
||||||
with open('tests/mispevent_testfiles/attribute_del.json', 'r') as f:
|
with open('tests/mispevent_testfiles/attribute_del.json') as f:
|
||||||
ref_json = json.load(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))
|
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||||
|
|
||||||
def test_to_dict_json_format(self):
|
def test_attribute_galaxy(self) -> None:
|
||||||
|
self.init_event()
|
||||||
|
with open('tests/mispevent_testfiles/galaxy.json') as f:
|
||||||
|
galaxy = json.load(f)
|
||||||
|
misp_galaxy = MISPGalaxy()
|
||||||
|
misp_galaxy.from_dict(**galaxy)
|
||||||
|
attribute = MISPAttribute()
|
||||||
|
attribute.from_dict(**{'type': 'github-username', 'value': 'adulau'})
|
||||||
|
attribute.add_galaxy(misp_galaxy)
|
||||||
|
self.mispevent.add_attribute(**attribute)
|
||||||
|
self.assertEqual(
|
||||||
|
self.mispevent.attributes[0].galaxies[0].to_json(sort_keys=True, indent=2),
|
||||||
|
json.dumps(galaxy, sort_keys=True, indent=2)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_to_dict_json_format(self) -> None:
|
||||||
misp_event = MISPEvent()
|
misp_event = MISPEvent()
|
||||||
av_signature_object = MISPObject("av-signature")
|
av_signature_object = MISPObject("av-signature")
|
||||||
av_signature_object.add_attribute("signature", "EICAR")
|
av_signature_object.add_attribute("signature", "EICAR")
|
||||||
|
@ -96,129 +121,145 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(json.loads(misp_event.to_json()), misp_event.to_dict(json_format=True))
|
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)
|
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)
|
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)
|
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
|
del a.uuid
|
||||||
self.assertEqual(self.mispevent.objects[0].attributes[0].tags[0].name, 'blah')
|
self.assertEqual(self.mispevent.objects[0].attributes[0].tags[0].name, 'blah')
|
||||||
self.assertTrue(self.mispevent.objects[0].has_attributes_by_relation(['filename']))
|
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.assertEqual(len(self.mispevent.objects[0].get_attributes_by_relation('filename')), 1)
|
||||||
self.mispevent.add_object(name='url', strict=True)
|
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
|
del a.uuid
|
||||||
self.mispevent.objects[0].uuid = 'a'
|
self.mispevent.objects[0].uuid = 'a'
|
||||||
self.mispevent.objects[1].uuid = 'b'
|
self.mispevent.objects[1].uuid = 'b'
|
||||||
reference = self.mispevent.objects[0].add_reference(self.mispevent.objects[1], 'baz', comment='foo')
|
reference = self.mispevent.objects[0].add_reference(self.mispevent.objects[1], 'baz', comment='foo')
|
||||||
del reference.uuid
|
del reference.uuid
|
||||||
self.assertEqual(self.mispevent.objects[0].references[0].relationship_type, 'baz')
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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")
|
@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.add_object(name='file', strict=True)
|
||||||
self.mispevent.objects[0].add_attribute('filename', value='bar')
|
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'
|
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)
|
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))
|
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||||
|
|
||||||
def test_malware(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') as f:
|
||||||
|
galaxy = json.load(f)
|
||||||
|
misp_galaxy = MISPGalaxy()
|
||||||
|
misp_galaxy.from_dict(**galaxy)
|
||||||
|
self.mispevent.objects[0].attributes[0].add_galaxy(misp_galaxy)
|
||||||
|
self.assertEqual(
|
||||||
|
self.mispevent.objects[0].attributes[0].galaxies[0].to_json(sort_keys=True, indent=2),
|
||||||
|
json.dumps(galaxy, sort_keys=True, indent=2)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_malware(self) -> None:
|
||||||
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
||||||
pseudofile = BytesIO(f.read())
|
pseudofile = BytesIO(f.read())
|
||||||
self.init_event()
|
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
|
del a.uuid
|
||||||
attribute = self.mispevent.attributes[0]
|
attribute = self.mispevent.attributes[0]
|
||||||
self.assertEqual(attribute.malware_binary, pseudofile)
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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')
|
self.mispevent.load_file('tests/mispevent_testfiles/malware_exist.json')
|
||||||
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
||||||
pseudofile = BytesIO(f.read())
|
pseudofile = BytesIO(f.read())
|
||||||
self.assertEqual(
|
self.assertTrue(self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary)
|
||||||
self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary.read(),
|
if _mb := self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary:
|
||||||
pseudofile.read())
|
self.assertEqual(_mb.read(), pseudofile.read())
|
||||||
|
|
||||||
def test_sighting(self):
|
def test_sighting(self) -> None:
|
||||||
sighting = MISPSighting()
|
sighting = MISPSighting()
|
||||||
sighting.from_dict(value='1', type='bar', timestamp=11111111)
|
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)
|
ref_json = json.load(f)
|
||||||
self.assertEqual(sighting.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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')
|
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)
|
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))
|
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')
|
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)
|
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))
|
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.")
|
@unittest.skip("Not supported on MISP.")
|
||||||
def test_shadow_attributes(self):
|
def test_shadow_attributes(self) -> None:
|
||||||
self.init_event()
|
self.init_event()
|
||||||
p = self.mispevent.add_proposal(type='filename', value='baz.jpg')
|
p = self.mispevent.add_proposal(type='filename', value='baz.jpg')
|
||||||
del p.uuid
|
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
|
del a.uuid
|
||||||
p = self.mispevent.attributes[0].add_proposal(type='filename', value='bar.pdf')
|
p = self.mispevent.attributes[0].add_proposal(type='filename', value='bar.pdf')
|
||||||
del p.uuid
|
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)
|
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))
|
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)
|
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
|
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')
|
self.assertEqual(a.category, 'Artifacts dropped')
|
||||||
del a.uuid
|
del a.uuid
|
||||||
self.mispevent.add_object(name='file', strict=False, default_attributes_parameters=self.mispevent.objects[0].attributes[0])
|
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
|
del a.uuid
|
||||||
self.mispevent.objects[0].uuid = 'a'
|
self.mispevent.objects[0].uuid = 'a'
|
||||||
self.mispevent.objects[1].uuid = 'b'
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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.init_event()
|
||||||
self.mispevent.add_object(name='whois', strict=True)
|
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
|
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
|
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
|
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
|
del a.uuid
|
||||||
self.mispevent.objects[0].uuid = 'a'
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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()
|
self.init_event()
|
||||||
obj1 = MISPObject(name="file")
|
obj1 = MISPObject(name="file")
|
||||||
obj2 = MISPObject(name="url", standalone=False)
|
obj2 = MISPObject(name="url", standalone=False)
|
||||||
|
@ -231,29 +272,29 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.assertTrue("ObjectReference" in obj1.jsonable())
|
self.assertTrue("ObjectReference" in obj1.jsonable())
|
||||||
self.assertFalse("ObjectReference" in obj2.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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
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.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.mispevent.info = 'blah'
|
self.mispevent.info = 'blah'
|
||||||
self.assertTrue(self.mispevent.edited)
|
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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
self.assertFalse(self.mispevent.edited)
|
||||||
self.mispevent.add_tag('foo')
|
self.mispevent.add_tag('foo')
|
||||||
self.assertTrue(self.mispevent.edited)
|
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.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.mispevent.attributes[0].value = 'blah'
|
self.mispevent.attributes[0].value = 'blah'
|
||||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||||
self.assertFalse(self.mispevent.attributes[1].edited)
|
self.assertFalse(self.mispevent.attributes[1].edited)
|
||||||
self.assertTrue(self.mispevent.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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
self.assertFalse(self.mispevent.edited)
|
||||||
self.mispevent.attributes[0].tags[0].name = 'blah'
|
self.mispevent.attributes[0].tags[0].name = 'blah'
|
||||||
|
@ -262,14 +303,14 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||||
self.assertTrue(self.mispevent.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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
self.assertFalse(self.mispevent.edited)
|
||||||
self.mispevent.attributes[0].add_tag(name='blah')
|
self.mispevent.attributes[0].add_tag(name='blah')
|
||||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||||
self.assertTrue(self.mispevent.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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
self.assertFalse(self.mispevent.edited)
|
||||||
self.mispevent.objects[0].comment = 'blah'
|
self.mispevent.objects[0].comment = 'blah'
|
||||||
|
@ -277,7 +318,7 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.assertFalse(self.mispevent.objects[1].edited)
|
self.assertFalse(self.mispevent.objects[1].edited)
|
||||||
self.assertTrue(self.mispevent.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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
self.assertFalse(self.mispevent.edited)
|
||||||
self.mispevent.objects[0].attributes[0].comment = 'blah'
|
self.mispevent.objects[0].attributes[0].comment = 'blah'
|
||||||
|
@ -285,23 +326,23 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.assertTrue(self.mispevent.objects[0].edited)
|
self.assertTrue(self.mispevent.objects[0].edited)
|
||||||
self.assertTrue(self.mispevent.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.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
self.assertFalse(self.mispevent.edited)
|
self.assertFalse(self.mispevent.edited)
|
||||||
self.mispevent.objects[0].attributes[0].add_tag('blah')
|
self.mispevent.objects[0].attributes[0].add_tag('blah')
|
||||||
self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
|
self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
|
||||||
self.assertTrue(self.mispevent.objects[0].edited)
|
self.assertTrue(self.mispevent.objects[0].edited)
|
||||||
self.assertTrue(self.mispevent.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)
|
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))
|
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')
|
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||||
misp_obj = self.mispevent.get_object_by_id(1556)
|
misp_obj = self.mispevent.get_object_by_id(1556)
|
||||||
self.assertEqual(misp_obj.uuid, '5a3cd604-e11c-4de5-bbbf-c170950d210f')
|
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()
|
self.init_event()
|
||||||
with open('tests/mispevent_testfiles/test_object_template/definition.json') as f:
|
with open('tests/mispevent_testfiles/test_object_template/definition.json') as f:
|
||||||
template = json.load(f)
|
template = json.load(f)
|
||||||
|
@ -312,16 +353,16 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||||
self.assertEqual(e.exception.message, '{\'member3\'} are required.')
|
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
|
del a.uuid
|
||||||
with self.assertRaises(InvalidMISPObject) as e:
|
with self.assertRaises(InvalidMISPObject) as e:
|
||||||
# Fail on requiredOneOf
|
# Fail on requiredOneOf
|
||||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
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')
|
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
|
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
|
del a.uuid
|
||||||
with self.assertRaises(InvalidMISPObject) as e:
|
with self.assertRaises(InvalidMISPObject) as e:
|
||||||
# member1 is not a multiple
|
# member1 is not a multiple
|
||||||
|
@ -330,12 +371,12 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
|
|
||||||
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
||||||
self.mispevent.objects[0].uuid = 'a'
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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.init_event()
|
||||||
self.mispevent.add_object(name='test_object_template', strict=True, misp_objects_path_custom='tests/mispevent_testfiles')
|
self.mispevent.add_object(name='test_object_template', strict=True, misp_objects_path_custom='tests/mispevent_testfiles')
|
||||||
with self.assertRaises(InvalidMISPObject) as e:
|
with self.assertRaises(InvalidMISPObject) as e:
|
||||||
|
@ -343,16 +384,16 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||||
self.assertEqual(e.exception.message, '{\'member3\'} are required.')
|
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
|
del a.uuid
|
||||||
with self.assertRaises(InvalidMISPObject) as e:
|
with self.assertRaises(InvalidMISPObject) as e:
|
||||||
# Fail on requiredOneOf
|
# Fail on requiredOneOf
|
||||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
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')
|
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
|
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
|
del a.uuid
|
||||||
with self.assertRaises(InvalidMISPObject) as e:
|
with self.assertRaises(InvalidMISPObject) as e:
|
||||||
# member1 is not a multiple
|
# member1 is not a multiple
|
||||||
|
@ -361,15 +402,15 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
|
|
||||||
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
||||||
self.mispevent.objects[0].uuid = 'a'
|
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)
|
ref_json = json.load(f)
|
||||||
del self.mispevent.uuid
|
del self.mispevent.uuid
|
||||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
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 = MISPEvent()
|
||||||
me.info = 'Test First and Last Seen'
|
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)
|
self.assertEqual(me.date.day, 12)
|
||||||
me.add_attribute('ip-dst', '8.8.8.8', first_seen='06-21-1998', last_seen=1580213607.469571)
|
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)
|
self.assertEqual(me.attributes[0].first_seen.year, 1998)
|
||||||
|
@ -377,11 +418,11 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
now = datetime.now().astimezone()
|
now = datetime.now().astimezone()
|
||||||
me.attributes[0].last_seen = now
|
me.attributes[0].last_seen = now
|
||||||
today = date.today()
|
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].first_seen.year, today.year)
|
||||||
self.assertEqual(me.attributes[0].last_seen, now)
|
self.assertEqual(me.attributes[0].last_seen, now)
|
||||||
|
|
||||||
def test_feed(self):
|
def test_feed(self) -> None:
|
||||||
me = MISPEvent()
|
me = MISPEvent()
|
||||||
me.info = 'Test feed'
|
me.info = 'Test feed'
|
||||||
org = MISPOrganisation()
|
org = MISPOrganisation()
|
||||||
|
@ -399,7 +440,7 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
self.assertEqual(feed['Event']['_manifest'][me.uuid]['info'], 'Test feed')
|
self.assertEqual(feed['Event']['_manifest'][me.uuid]['info'], 'Test feed')
|
||||||
self.assertEqual(len(feed['Event']['Object'][0]['Attribute']), 2)
|
self.assertEqual(len(feed['Event']['Object'][0]['Attribute']), 2)
|
||||||
|
|
||||||
def test_object_templates(self):
|
def test_object_templates(self) -> None:
|
||||||
me = MISPEvent()
|
me = MISPEvent()
|
||||||
for template in glob.glob(str(me.misp_objects_path / '*' / 'definition.json')):
|
for template in glob.glob(str(me.misp_objects_path / '*' / 'definition.json')):
|
||||||
with open(template) as f:
|
with open(template) as f:
|
||||||
|
@ -418,7 +459,7 @@ class TestMISPEvent(unittest.TestCase):
|
||||||
subset = set(entry['categories']).issubset(me.describe_types['categories'])
|
subset = set(entry['categories']).issubset(me.describe_types['categories'])
|
||||||
self.assertTrue(subset, f'{t_json["name"]} - {obj_relation}')
|
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:
|
with open('tests/git-vuln-finder-quagga.json') as f:
|
||||||
dump = json.load(f)
|
dump = json.load(f)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +1,19 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import sys
|
|
||||||
import unittest
|
import unittest
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import urllib3 # type: ignore
|
import urllib3
|
||||||
import logging
|
import logging
|
||||||
logging.disable(logging.CRITICAL)
|
logging.disable(logging.CRITICAL)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pymisp import ExpandedPyMISP, MISPOrganisation, MISPUser, MISPEvent, MISPObject, MISPSharingGroup, Distribution
|
from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPEvent, MISPObject, MISPSharingGroup, Distribution
|
||||||
except ImportError:
|
except ImportError:
|
||||||
if sys.version_info < (3, 6):
|
raise
|
||||||
print('This test suite requires Python 3.6+, breaking.')
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
key = 'eYQdGTEWZJ8C2lm9EpnMqxQGwGiPNyoR75JvLdlE'
|
key = 'eYQdGTEWZJ8C2lm9EpnMqxQGwGiPNyoR75JvLdlE'
|
||||||
verifycert = False
|
verifycert = False
|
||||||
|
@ -73,7 +69,7 @@ fast_mode = True
|
||||||
class MISPInstance():
|
class MISPInstance():
|
||||||
|
|
||||||
def __init__(self, params):
|
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
|
# Git pull
|
||||||
self.initial_user_connector.update_misp()
|
self.initial_user_connector.update_misp()
|
||||||
# Set the default role (id 3 on the VM is normal user)
|
# 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.org_id = self.test_org.id
|
||||||
user.role_id = 1 # Site admin
|
user.role_id = 1 # Site admin
|
||||||
self.test_site_admin = self.initial_user_connector.add_user(user)
|
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()
|
self.site_admin_connector.toggle_global_pythonify()
|
||||||
# Create org admin
|
# Create org admin
|
||||||
user = MISPUser()
|
user = MISPUser()
|
||||||
|
@ -109,14 +105,14 @@ class MISPInstance():
|
||||||
user.org_id = self.test_org.id
|
user.org_id = self.test_org.id
|
||||||
user.role_id = 2 # Org admin
|
user.role_id = 2 # Org admin
|
||||||
self.test_org_admin = self.site_admin_connector.add_user(user)
|
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()
|
self.org_admin_connector.toggle_global_pythonify()
|
||||||
# Create user
|
# Create user
|
||||||
user = MISPUser()
|
user = MISPUser()
|
||||||
user.email = params['email_user']
|
user.email = params['email_user']
|
||||||
user.org_id = self.test_org.id
|
user.org_id = self.test_org.id
|
||||||
self.test_usr = self.org_admin_connector.add_user(user)
|
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()
|
self.user_connector.toggle_global_pythonify()
|
||||||
|
|
||||||
# Setup external_baseurl
|
# Setup external_baseurl
|
||||||
|
@ -141,7 +137,7 @@ class MISPInstance():
|
||||||
user.org_id = sync_org.id
|
user.org_id = sync_org.id
|
||||||
user.role_id = 5 # Org admin
|
user.role_id = 5 # Org admin
|
||||||
sync_user = self.site_admin_connector.add_user(user)
|
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)
|
sync_server_config = sync_user_connector.get_sync_config(pythonify=True)
|
||||||
self.sync.append((sync_org, sync_user, sync_server_config))
|
self.sync.append((sync_org, sync_user, sync_server_config))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue