mirror of https://github.com/MISP/PyMISP
Merge branch 'main' of github.com:MISP/PyMISP
commit
4c152ded19
|
@ -38,11 +38,11 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
@ -56,7 +56,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
@ -69,6 +69,6 @@ jobs:
|
|||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
@ -11,17 +11,18 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [3.7, 3.8, 3.9, '3.10']
|
||||
python-version: [3.8, 3.9, '3.10', '3.11', '3.12']
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Python ${{matrix.python-version}}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{matrix.python-version}}
|
||||
|
||||
|
@ -33,7 +34,12 @@ jobs:
|
|||
- name: Test with nosetests
|
||||
run: |
|
||||
poetry run pytest --cov=pymisp tests/test_*.py
|
||||
poetry run mypy tests/testlive_comprehensive.py tests/test_mispevent.py tests/testlive_sync.py pymisp
|
||||
poetry run mypy .
|
||||
|
||||
- name: Test with nosetests with orjson
|
||||
run: |
|
||||
pip3 install orjson
|
||||
poetry run pytest --cov=pymisp tests/test_*.py
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
version: 2
|
||||
build:
|
||||
os: "ubuntu-22.04"
|
||||
tools:
|
||||
python: "3"
|
||||
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
|
||||
build:
|
||||
image: latest
|
||||
|
||||
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)
|
||||
---------------------
|
||||
|
||||
|
@ -13,6 +873,7 @@ New
|
|||
|
||||
Changes
|
||||
~~~~~~~
|
||||
- Re-bump changelog. [Raphaël Vinot]
|
||||
- Bump changelog. [Raphaël Vinot]
|
||||
- Bump deps, version. [Raphaël Vinot]
|
||||
- [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]
|
||||
- Some testing. [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
|
||||
|
||||
[](http://pymisp.readthedocs.io/?badge=latest)
|
||||
[](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/)
|
||||
|
||||
|
@ -46,13 +46,13 @@ pip3 install pymisp[virustotal,email]
|
|||
```
|
||||
git clone https://github.com/MISP/PyMISP.git && cd PyMISP
|
||||
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
|
||||
|
||||
```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:
|
||||
|
@ -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.
|
||||
|
||||
```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
|
||||
|
@ -124,8 +124,7 @@ logging.basicConfig(level=logging.DEBUG, filename="debug.log", filemode='w', for
|
|||
```bash
|
||||
# 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
|
||||
|
@ -159,6 +158,35 @@ Your new MISPObject generator must generate attributes and add them as class pro
|
|||
|
||||
When the object is sent to MISP, all the class properties will be exported to the JSON export.
|
||||
|
||||
## Installing PyMISP on a machine with no internet access
|
||||
|
||||
This is done using poetry and you need to have this repository cloned on your machine.
|
||||
The commands below have to be run from inside the cloned directory.
|
||||
|
||||
|
||||
1. From a machine with access to the internet, get the dependencies:
|
||||
|
||||
```bash
|
||||
mkdir offline
|
||||
poetry export --all-extras > offline/requirements.txt
|
||||
poetry run pip download -r offline/requirements.txt -d offline/packages/
|
||||
```
|
||||
|
||||
2. Prepare the PyMISP Package
|
||||
|
||||
```bash
|
||||
poetry build
|
||||
mv dist/*.whl offline/packages/
|
||||
```
|
||||
|
||||
3. Copy the content of `offline/packages/` to the machine with no internet access.
|
||||
|
||||
4. Install the packages:
|
||||
|
||||
```bash
|
||||
python -m pip install --no-index --no-deps packages/*.whl
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
PyMISP is distributed under an [open source license](./LICENSE). A simplified 2-BSD license.
|
||||
|
|
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
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from pymisp import ExpandedPyMISP
|
||||
from pymisp import PyMISP
|
||||
from pymisp.tools import EMailObject
|
||||
import traceback
|
||||
from keys import misp_url, misp_key, misp_verifycert # type: ignore
|
||||
|
@ -15,7 +14,7 @@ if __name__ == '__main__':
|
|||
parser.add_argument("-p", "--path", required=True, help="Path to process (expanded using glob).")
|
||||
args = parser.parse_args()
|
||||
|
||||
pymisp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert, debug=True)
|
||||
pymisp = PyMISP(misp_url, misp_key, misp_verifycert, debug=True)
|
||||
|
||||
for f in glob.glob(args.path):
|
||||
try:
|
||||
|
|
|
@ -16,7 +16,7 @@ outputdir = 'output'
|
|||
# you can use on the event index, such as organisation, tags, etc.
|
||||
# It uses the same joining and condition rules as the API parameters
|
||||
# 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,
|
||||
# tagged tlp:white and/or feed-export but exclude anything tagged privint
|
||||
filters = {'published':'true'}
|
||||
|
|
|
@ -10,7 +10,7 @@ from pymisp import MISPEvent
|
|||
|
||||
try:
|
||||
from keys import misp_url, misp_key, misp_verifycert
|
||||
from pymisp import ExpandedPyMISP
|
||||
from pymisp import PyMISP
|
||||
offline = False
|
||||
except ImportError as e:
|
||||
offline = True
|
||||
|
@ -66,7 +66,7 @@ if __name__ == '__main__':
|
|||
if offline:
|
||||
print('You are in offline mode, quitting.')
|
||||
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:
|
||||
event = MISPEvent()
|
||||
event.info = args.new_event
|
||||
|
@ -80,7 +80,7 @@ if __name__ == '__main__':
|
|||
else:
|
||||
print('Something went wrong:')
|
||||
print(new_event)
|
||||
else:
|
||||
elif args.update_event:
|
||||
for o in objects:
|
||||
new_object = misp.add_object(args.update_event, o, pythonify=True)
|
||||
if isinstance(new_object, str):
|
||||
|
@ -90,3 +90,5 @@ if __name__ == '__main__':
|
|||
else:
|
||||
print('Something went wrong:')
|
||||
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]
|
||||
ignore_errors = False
|
||||
|
||||
strict = True
|
||||
warn_return_any = False
|
||||
show_error_context = True
|
||||
pretty = True
|
||||
exclude = pymisp/data|example|docs
|
||||
exclude = tests/testlive_comprehensive.py|tests/testlive_sync.py|feed-generator|examples|pymisp/data|docs|pymisp/tools/openioc.py|pymisp/tools/reportlab_generator.py|tests/test_reportlab.py
|
||||
|
||||
# Stuff to remove gradually
|
||||
disallow_untyped_defs = False
|
||||
disallow_untyped_calls = False
|
||||
disable_error_code = arg-type,return-value,assignment,call-overload,union-attr
|
||||
|
||||
|
||||
[mypy-docs.source.*]
|
||||
ignore_errors = True
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +1,21 @@
|
|||
__version__ = '2.4.166'
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import importlib.metadata
|
||||
|
||||
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("""
|
||||
As our baseline system is the latest Ubuntu LTS, and Ubuntu LTS 20.04 has Python 3.8 available,
|
||||
we will officially deprecate python versions below 3.8 on January 1st 2022.
|
||||
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.10 on January 1st 2024.
|
||||
**Please update your codebase.**""", DeprecationWarning, stacklevel=3)
|
||||
|
||||
|
||||
|
@ -25,26 +30,24 @@ Response (if any):
|
|||
|
||||
|
||||
try:
|
||||
warning_2022()
|
||||
from .exceptions import PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey, InvalidMISPObject, UnknownMISPObjectTemplate, PyMISPInvalidFormat, MISPServerError, PyMISPNotImplementedYet, PyMISPUnexpectedResponse, PyMISPEmptyResponse # noqa
|
||||
from .abstract import AbstractMISP, MISPEncode, pymisp_json_default, MISPTag, Distribution, ThreatLevel, Analysis # noqa
|
||||
warning_2024()
|
||||
from .exceptions import (PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey, # 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
|
||||
MISPOrganisation, MISPSighting, MISPLog, MISPShadowAttribute, MISPWarninglist, MISPTaxonomy,
|
||||
MISPNoticelist, MISPObjectTemplate, MISPSharingGroup, MISPRole, MISPServer, MISPFeed,
|
||||
MISPEventDelegation, MISPUserSetting, MISPInbox, MISPEventBlocklist, MISPOrganisationBlocklist,
|
||||
MISPEventReport, MISPGalaxyCluster, MISPGalaxyClusterElement, MISPGalaxyClusterRelation,
|
||||
MISPCorrelationExclusion, MISPGalaxy, MISPDecayingModel)
|
||||
MISPEventReport, MISPCorrelationExclusion, MISPDecayingModel, MISPGalaxy, MISPGalaxyCluster,
|
||||
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 Neo4j # noqa
|
||||
from .tools import stix # noqa
|
||||
from .tools import openioc # noqa
|
||||
from .tools import ext_lookups # noqa
|
||||
from .tools import update_objects # noqa
|
||||
|
||||
from .api import PyMISP, register_user # noqa
|
||||
from .api import PyMISP as ExpandedPyMISP # noqa
|
||||
from .tools import load_warninglists # noqa
|
||||
# Let's not bother with old python
|
||||
|
||||
try:
|
||||
from .tools import reportlab_generator # noqa
|
||||
except ImportError:
|
||||
|
@ -55,4 +58,26 @@ try:
|
|||
pass
|
||||
logger.debug('pymisp loaded properly')
|
||||
except ImportError as e:
|
||||
logger.warning('Unable to load pymisp properly: {}'.format(e))
|
||||
logger.warning(f'Unable to load pymisp properly: {e}')
|
||||
|
||||
|
||||
class ExpandedPyMISP(PyMISP):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn('This class is deprecated, use PyMISP instead', FutureWarning)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
__all__ = ['PyMISP', 'register_user', 'AbstractMISP', 'MISPTag',
|
||||
'MISPEvent', 'MISPAttribute', 'MISPObjectReference', 'MISPObjectAttribute',
|
||||
'MISPObject', 'MISPUser', 'MISPOrganisation', 'MISPSighting', 'MISPLog',
|
||||
'MISPShadowAttribute', 'MISPWarninglist', 'MISPTaxonomy', 'MISPNoticelist',
|
||||
'MISPObjectTemplate', 'MISPSharingGroup', 'MISPRole', 'MISPServer', 'MISPFeed',
|
||||
'MISPEventDelegation', 'MISPUserSetting', 'MISPInbox', 'MISPEventBlocklist',
|
||||
'MISPOrganisationBlocklist', 'MISPEventReport', 'MISPCorrelationExclusion',
|
||||
'MISPDecayingModel', 'MISPGalaxy', 'MISPGalaxyCluster', 'MISPGalaxyClusterElement',
|
||||
'MISPGalaxyClusterRelation', 'MISPNote', 'MISPOpinion', 'MISPRelationship',
|
||||
'PyMISPError', 'NewEventError', 'NewAttributeError',
|
||||
'NoURL', 'NoKey', 'InvalidMISPObject', 'UnknownMISPObjectTemplate', 'PyMISPInvalidFormat',
|
||||
'Distribution', 'ThreatLevel', 'Analysis', 'ExpandedPyMISP'
|
||||
]
|
||||
|
|
|
@ -1,53 +1,48 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import date, datetime
|
||||
|
||||
from deprecated import deprecated # type: ignore
|
||||
from json import JSONEncoder
|
||||
from uuid import UUID
|
||||
from abc import ABCMeta
|
||||
|
||||
try:
|
||||
from rapidjson import load # type: ignore
|
||||
from rapidjson import loads # type: ignore
|
||||
from rapidjson import dumps # type: ignore
|
||||
HAS_RAPIDJSON = True
|
||||
except ImportError:
|
||||
from json import load
|
||||
from json import loads
|
||||
from json import dumps
|
||||
HAS_RAPIDJSON = False
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import Union, Optional, Any, Dict, List, Set, Mapping
|
||||
|
||||
from .exceptions import PyMISPInvalidFormat, PyMISPError
|
||||
|
||||
|
||||
from typing import Any, Mapping
|
||||
from collections.abc import MutableMapping
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import orjson # type: ignore
|
||||
from orjson import loads, dumps
|
||||
HAS_ORJSON = True
|
||||
except ImportError:
|
||||
from json import loads, dumps
|
||||
HAS_ORJSON = False
|
||||
|
||||
from .exceptions import PyMISPInvalidFormat, PyMISPError
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
resources_path = Path(__file__).parent / 'data'
|
||||
misp_objects_path = resources_path / 'misp-objects' / 'objects'
|
||||
with (resources_path / 'describeTypes.json').open('r') as f:
|
||||
describe_types = load(f)['result']
|
||||
with (resources_path / 'describeTypes.json').open('rb') as f:
|
||||
describe_types: dict[str, Any] = loads(f.read())['result']
|
||||
|
||||
|
||||
class MISPFileCache(object):
|
||||
class MISPFileCache:
|
||||
# cache up to 150 JSON structures in class attribute
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=150)
|
||||
def _load_json(path: Path) -> Union[dict, None]:
|
||||
def _load_json(path: Path) -> dict[str, Any] | None:
|
||||
if not path.exists():
|
||||
return None
|
||||
with path.open('r', encoding='utf-8') as f:
|
||||
data = load(f)
|
||||
with path.open('rb') as f:
|
||||
data = loads(f.read())
|
||||
return data
|
||||
|
||||
|
||||
|
@ -73,7 +68,7 @@ class Analysis(Enum):
|
|||
completed = 2
|
||||
|
||||
|
||||
def _int_to_str(d: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def _int_to_str(d: dict[str, Any]) -> dict[str, Any]:
|
||||
# transform all integer back to string
|
||||
for k, v in d.items():
|
||||
if isinstance(v, dict):
|
||||
|
@ -85,7 +80,7 @@ def _int_to_str(d: Dict[str, Any]) -> Dict[str, Any]:
|
|||
|
||||
@deprecated(reason=" Use method default=pymisp_json_default instead of cls=MISPEncode", version='2.4.117', action='default')
|
||||
class MISPEncode(JSONEncoder):
|
||||
def default(self, obj):
|
||||
def default(self, obj: Any) -> dict[str, Any] | str:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
|
@ -97,12 +92,12 @@ class MISPEncode(JSONEncoder):
|
|||
return JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
||||
class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta): # type: ignore[type-arg]
|
||||
__resources_path = resources_path
|
||||
__misp_objects_path = misp_objects_path
|
||||
__describe_types = describe_types
|
||||
|
||||
def __init__(self, **kwargs: Dict):
|
||||
def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Abstract class for all the MISP objects.
|
||||
NOTE: Every method in every classes inheriting this one are doing
|
||||
changes in memory and do not modify data on a remote MISP instance.
|
||||
|
@ -111,9 +106,9 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
"""
|
||||
super().__init__()
|
||||
self.__edited: bool = True # As we create a new object, we assume it is edited
|
||||
self.__not_jsonable: List[str] = []
|
||||
self._fields_for_feed: Set
|
||||
self.__self_defined_describe_types: Optional[Dict] = None
|
||||
self.__not_jsonable: list[str] = []
|
||||
self._fields_for_feed: set[str]
|
||||
self.__self_defined_describe_types: dict[str, Any] | None = None
|
||||
self.uuid: str
|
||||
|
||||
if kwargs.get('force_timestamps') is not None:
|
||||
|
@ -123,13 +118,13 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
self.__force_timestamps = False
|
||||
|
||||
@property
|
||||
def describe_types(self) -> Dict:
|
||||
def describe_types(self) -> dict[str, Any]:
|
||||
if self.__self_defined_describe_types:
|
||||
return self.__self_defined_describe_types
|
||||
return self.__describe_types
|
||||
|
||||
@describe_types.setter
|
||||
def describe_types(self, describe_types: Dict):
|
||||
def describe_types(self, describe_types: dict[str, Any]) -> None:
|
||||
self.__self_defined_describe_types = describe_types
|
||||
|
||||
@property
|
||||
|
@ -141,12 +136,12 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return self.__misp_objects_path
|
||||
|
||||
@misp_objects_path.setter
|
||||
def misp_objects_path(self, misp_objects_path: Union[str, Path]):
|
||||
def misp_objects_path(self, misp_objects_path: str | Path) -> None:
|
||||
if isinstance(misp_objects_path, str):
|
||||
misp_objects_path = Path(misp_objects_path)
|
||||
self.__misp_objects_path = misp_objects_path
|
||||
|
||||
def from_dict(self, **kwargs) -> None:
|
||||
def from_dict(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Loading all the parameters as class properties, if they aren't `None`.
|
||||
This method aims to be called when all the properties requiring a special
|
||||
treatment are processed.
|
||||
|
@ -159,15 +154,15 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
# We load an existing dictionary, marking it an not-edited
|
||||
self.__edited = False
|
||||
|
||||
def update_not_jsonable(self, *args) -> None:
|
||||
def update_not_jsonable(self, *args) -> None: # type: ignore[no-untyped-def]
|
||||
"""Add entries to the __not_jsonable list"""
|
||||
self.__not_jsonable += args
|
||||
|
||||
def set_not_jsonable(self, args: List[str]) -> None:
|
||||
def set_not_jsonable(self, args: list[str]) -> None:
|
||||
"""Set __not_jsonable to a new list"""
|
||||
self.__not_jsonable = args
|
||||
|
||||
def _remove_from_not_jsonable(self, *args) -> None:
|
||||
def _remove_from_not_jsonable(self, *args) -> None: # type: ignore[no-untyped-def]
|
||||
"""Remove the entries that are in the __not_jsonable list"""
|
||||
for entry in args:
|
||||
try:
|
||||
|
@ -179,7 +174,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
"""Load a JSON string"""
|
||||
self.from_dict(**loads(json_string))
|
||||
|
||||
def to_dict(self, json_format: bool = False) -> Dict:
|
||||
def to_dict(self, json_format: bool = False) -> dict[str, Any]:
|
||||
"""Dump the class to a dictionary.
|
||||
This method automatically removes the timestamp recursively in every object
|
||||
that has been edited is order to let MISP update the event accordingly."""
|
||||
|
@ -221,15 +216,15 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
to_return = _int_to_str(to_return)
|
||||
return to_return
|
||||
|
||||
def jsonable(self) -> Dict:
|
||||
def jsonable(self) -> dict[str, Any]:
|
||||
"""This method is used by the JSON encoder"""
|
||||
return self.to_dict()
|
||||
|
||||
def _to_feed(self) -> Dict:
|
||||
def _to_feed(self) -> dict[str, Any]:
|
||||
if not hasattr(self, '_fields_for_feed') or not self._fields_for_feed:
|
||||
raise PyMISPError('Unable to export in the feed format, _fields_for_feed is missing.')
|
||||
if hasattr(self, '_set_default') and callable(self._set_default): # type: ignore
|
||||
self._set_default() # type: ignore
|
||||
if hasattr(self, '_set_default') and callable(self._set_default):
|
||||
self._set_default()
|
||||
to_return = {}
|
||||
for field in sorted(self._fields_for_feed):
|
||||
if getattr(self, field, None) is not None:
|
||||
|
@ -243,15 +238,25 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
if field in ['data', 'first_seen', 'last_seen', 'deleted']:
|
||||
# special fields
|
||||
continue
|
||||
raise PyMISPError('The field {} is required in {} when generating a feed.'.format(field, self.__class__.__name__))
|
||||
raise PyMISPError(f'The field {field} is required in {self.__class__.__name__} when generating a feed.')
|
||||
to_return = _int_to_str(to_return)
|
||||
return to_return
|
||||
|
||||
def to_json(self, sort_keys: bool = False, indent: Optional[int] = None) -> str:
|
||||
def to_json(self, sort_keys: bool = False, indent: int | None = None) -> str:
|
||||
"""Dump recursively any class of type MISPAbstract to a json string"""
|
||||
if HAS_ORJSON:
|
||||
option = 0
|
||||
if sort_keys:
|
||||
option |= orjson.OPT_SORT_KEYS
|
||||
if indent:
|
||||
option |= orjson.OPT_INDENT_2
|
||||
# orjson dumps method returns bytes instead of bytes, to keep compatibility with json
|
||||
# we have to convert output to str
|
||||
return dumps(self, default=pymisp_json_default, option=option).decode()
|
||||
|
||||
return dumps(self, default=pymisp_json_default, sort_keys=sort_keys, indent=indent)
|
||||
|
||||
def __getitem__(self, key):
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
try:
|
||||
if key[0] != '_' and key not in self.__not_jsonable:
|
||||
return self.__dict__[key]
|
||||
|
@ -260,13 +265,13 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
# Expected by pop and other dict-related methods
|
||||
raise KeyError
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
setattr(self, key, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
def __delitem__(self, key: str) -> None:
|
||||
delattr(self, key)
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Any:
|
||||
'''When we call **self, skip keys:
|
||||
* starting with _
|
||||
* in __not_jsonable
|
||||
|
@ -285,7 +290,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return self.__force_timestamps
|
||||
|
||||
@force_timestamp.setter
|
||||
def force_timestamp(self, force: bool):
|
||||
def force_timestamp(self, force: bool) -> None:
|
||||
self.__force_timestamps = force
|
||||
|
||||
@property
|
||||
|
@ -305,28 +310,28 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return self.__edited
|
||||
|
||||
@edited.setter
|
||||
def edited(self, val: bool):
|
||||
def edited(self, val: bool) -> None:
|
||||
"""Set the edit flag"""
|
||||
if isinstance(val, bool):
|
||||
self.__edited = val
|
||||
else:
|
||||
raise PyMISPError('edited can only be True or False')
|
||||
|
||||
def __setattr__(self, name: str, value: Any):
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if name[0] != '_' and not self.__edited and name in self:
|
||||
# The private members don't matter
|
||||
# If we already have a key with that name, we're modifying it.
|
||||
self.__edited = True
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def _datetime_to_timestamp(self, d: Union[int, float, str, datetime]) -> int:
|
||||
def _datetime_to_timestamp(self, d: int | float | str | datetime) -> int:
|
||||
"""Convert a datetime object to a timestamp (int)"""
|
||||
if isinstance(d, (int, float, str)):
|
||||
# Assume we already have a timestamp
|
||||
return int(d)
|
||||
return int(d.timestamp())
|
||||
|
||||
def _add_tag(self, tag: Optional[Union[str, 'MISPTag', Mapping]] = None, **kwargs):
|
||||
def _add_tag(self, tag: str | MISPTag | Mapping[str, Any] | None = None, **kwargs): # type: ignore[no-untyped-def]
|
||||
"""Add a tag to the attribute (by name or a MISPTag object)"""
|
||||
if isinstance(tag, str):
|
||||
misp_tag = MISPTag()
|
||||
|
@ -346,14 +351,14 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
self.edited = True
|
||||
return misp_tag
|
||||
|
||||
def _set_tags(self, tags: List['MISPTag']):
|
||||
def _set_tags(self, tags: list[MISPTag]) -> None:
|
||||
"""Set a list of prepared MISPTag."""
|
||||
if all(isinstance(x, MISPTag) for x in tags):
|
||||
self.Tag = tags
|
||||
else:
|
||||
raise PyMISPInvalidFormat('All the attributes have to be of type MISPTag.')
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, AbstractMISP):
|
||||
return self.to_dict() == other.to_dict()
|
||||
elif isinstance(other, dict):
|
||||
|
@ -362,62 +367,57 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
|
|||
return False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return '<{self.__class__.__name__} - please define me>'.format(self=self)
|
||||
return f'<{self.__class__.__name__} - please define me>'
|
||||
|
||||
|
||||
class MISPTag(AbstractMISP):
|
||||
|
||||
_fields_for_feed: set = {'name', 'colour'}
|
||||
_fields_for_feed: set[str] = {'name', 'colour', 'relationship_type', 'local'}
|
||||
|
||||
def __init__(self, **kwargs: Dict):
|
||||
def __init__(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__(**kwargs)
|
||||
self.name: str
|
||||
self.exportable: bool
|
||||
self.local: bool
|
||||
self.relationship_type: str | None
|
||||
|
||||
def from_dict(self, **kwargs):
|
||||
def from_dict(self, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
if kwargs.get('Tag'):
|
||||
kwargs = kwargs.get('Tag')
|
||||
kwargs = kwargs.get('Tag') # type: ignore[assignment]
|
||||
super().from_dict(**kwargs)
|
||||
|
||||
def _set_default(self):
|
||||
def _set_default(self) -> None:
|
||||
if not hasattr(self, 'relationship_type'):
|
||||
self.relationship_type = ''
|
||||
if not hasattr(self, 'colour'):
|
||||
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:
|
||||
return {}
|
||||
if with_local is False and hasattr(self, 'local') and self.local:
|
||||
return {}
|
||||
return super()._to_feed()
|
||||
|
||||
def delete(self):
|
||||
def delete(self) -> None:
|
||||
self.deleted = True
|
||||
self.edited = True
|
||||
|
||||
def __repr__(self) -> str:
|
||||
if hasattr(self, 'name'):
|
||||
return '<{self.__class__.__name__}(name={self.name})>'.format(self=self)
|
||||
return '<{self.__class__.__name__}(NotInitialized)>'.format(self=self)
|
||||
return f'<{self.__class__.__name__}(NotInitialized)>'
|
||||
|
||||
|
||||
if HAS_RAPIDJSON:
|
||||
def pymisp_json_default(obj: Union[AbstractMISP, datetime, date, Enum, UUID]) -> Union[Dict, str]:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, Enum):
|
||||
return obj.value
|
||||
elif isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
else:
|
||||
def pymisp_json_default(obj: Union[AbstractMISP, datetime, date, Enum, UUID]) -> Union[Dict, str]:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, Enum):
|
||||
return obj.value
|
||||
elif isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
# UUID, datetime, date and Enum is serialized by ORJSON by default
|
||||
def pymisp_json_default(obj: AbstractMISP | datetime | date | Enum | UUID) -> dict[str, Any] | str:
|
||||
if isinstance(obj, AbstractMISP):
|
||||
return obj.jsonable()
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
elif isinstance(obj, Enum):
|
||||
return obj.value
|
||||
elif isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
|
|
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",
|
||||
"to_ids": 0
|
||||
},
|
||||
"integer": {
|
||||
"default_category": "Other",
|
||||
"to_ids": 0
|
||||
},
|
||||
"datetime": {
|
||||
"default_category": "Other",
|
||||
"to_ids": 0
|
||||
|
@ -891,6 +895,7 @@
|
|||
"dns-soa-email",
|
||||
"size-in-bytes",
|
||||
"counter",
|
||||
"integer",
|
||||
"datetime",
|
||||
"port",
|
||||
"ip-dst|port",
|
||||
|
@ -1460,6 +1465,7 @@
|
|||
"other",
|
||||
"size-in-bytes",
|
||||
"counter",
|
||||
"integer",
|
||||
"datetime",
|
||||
"cpe",
|
||||
"port",
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2787dc45d7efbf32e0fbe81ea95f0af642ae8963
|
||||
Subproject commit e3288ef6e516624e3e335939a2b7fe4aef5ce510
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
class PyMISPError(Exception):
|
||||
def __init__(self, message):
|
||||
super(PyMISPError, self).__init__(message)
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
|
||||
|
||||
|
@ -23,6 +23,22 @@ class NewEventReportError(PyMISPError):
|
|||
pass
|
||||
|
||||
|
||||
class NewAnalystDataError(PyMISPError):
|
||||
pass
|
||||
|
||||
|
||||
class NewNoteError(PyMISPError):
|
||||
pass
|
||||
|
||||
|
||||
class NewOpinionError(PyMISPError):
|
||||
pass
|
||||
|
||||
|
||||
class NewRelationshipError(PyMISPError):
|
||||
pass
|
||||
|
||||
|
||||
class UpdateAttributeError(PyMISPError):
|
||||
pass
|
||||
|
||||
|
@ -51,17 +67,29 @@ class NoKey(PyMISPError):
|
|||
pass
|
||||
|
||||
|
||||
class MISPObjectException(PyMISPError):
|
||||
pass
|
||||
class MISPAttributeException(PyMISPError):
|
||||
"""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):
|
||||
"""Exception raised when an object doesn't respect the contrains in the definition"""
|
||||
pass
|
||||
"""Exception raised when an object doesn't respect the constraints in the definition"""
|
||||
|
||||
|
||||
class UnknownMISPObjectTemplate(MISPObjectException):
|
||||
"""Exception raised when the template is unknown"""
|
||||
|
||||
|
||||
|
||||
class InvalidMISPGalaxy(PyMISPError):
|
||||
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 .neo4j import Neo4j # noqa
|
||||
from .fileobject import FileObject # noqa
|
||||
|
@ -41,3 +43,13 @@ try:
|
|||
except ImportError:
|
||||
# Requires lief, optional [fileobjects]
|
||||
pass
|
||||
|
||||
__all__ = ['VTReportObject', 'Neo4j', 'FileObject', 'make_binary_objects',
|
||||
'AbstractMISPObjectGenerator', 'GenericObjectGenerator',
|
||||
'load_openioc', 'load_openioc_file', 'SBSignatureObject',
|
||||
'Fail2BanObject', 'DomainIPObject', 'ASNObject', 'GeolocationObject',
|
||||
'GitVulnFinderObject', 'VehicleObject', 'CSVLoader',
|
||||
'SSHAuthorizedKeysObject', 'feed_meta_generator', 'update_objects',
|
||||
'EMailObject', 'URLObject', 'PEObject', 'PESectionObject', 'ELFObject',
|
||||
'ELFSectionObject', 'MachOObject', 'MachOSectionObject'
|
||||
]
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
import socket
|
||||
import idna
|
||||
from publicsuffixlist import PublicSuffixList # type: ignore
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
from urllib.parse import urlparse, urlunparse, ParseResult
|
||||
|
||||
|
||||
class UrlNotDecoded(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PSLFaup(object):
|
||||
class PSLFaup:
|
||||
"""
|
||||
Fake Faup Python Library using PSL for Windows support
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.decoded = False
|
||||
self.psl = PublicSuffixList()
|
||||
self._url = None
|
||||
self._retval = {}
|
||||
self.ip_as_host = False
|
||||
self._url: ParseResult | None = None
|
||||
self._retval: dict[str, str | int | None | bytes] = {}
|
||||
self.ip_as_host = ''
|
||||
|
||||
def _clear(self):
|
||||
def _clear(self) -> None:
|
||||
self.decoded = False
|
||||
self._url = None
|
||||
self._retval = {}
|
||||
self.ip_as_host = False
|
||||
self.ip_as_host = ''
|
||||
|
||||
def decode(self, url) -> None:
|
||||
def decode(self, url: str) -> None:
|
||||
"""
|
||||
This function creates a dict of all the url fields.
|
||||
:param url: The URL to normalize
|
||||
|
@ -42,10 +43,15 @@ class PSLFaup(object):
|
|||
url = '//' + url
|
||||
self._url = urlparse(url)
|
||||
|
||||
self.ip_as_host = False
|
||||
if self._url is None:
|
||||
raise UrlNotDecoded("Unable to parse URL")
|
||||
|
||||
self.ip_as_host = ''
|
||||
if self._url.hostname is None:
|
||||
raise UrlNotDecoded("Unable to parse URL")
|
||||
hostname = _ensure_str(self._url.hostname)
|
||||
try:
|
||||
ipv4_bytes = socket.inet_aton(_ensure_str(hostname))
|
||||
ipv4_bytes = socket.inet_aton(hostname)
|
||||
ipv4 = ipaddress.IPv4Address(ipv4_bytes)
|
||||
self.ip_as_host = ipv4.compressed
|
||||
except (OSError, ValueError):
|
||||
|
@ -60,61 +66,70 @@ class PSLFaup(object):
|
|||
self._retval = {}
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
if not self.decoded:
|
||||
def url(self) -> bytes | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
netloc = self.get_host() + ('' if self.get_port() is None else ':{}'.format(self.get_port()))
|
||||
return _ensure_bytes(
|
||||
urlunparse(
|
||||
(self.get_scheme(), netloc, self.get_resource_path(),
|
||||
'', self.get_query_string(), self.get_fragment(),)
|
||||
if host := self.get_host():
|
||||
netloc = host + ('' if self.get_port() is None else f':{self.get_port()}')
|
||||
return _ensure_bytes(
|
||||
urlunparse(
|
||||
(self.get_scheme(), netloc, self.get_resource_path(),
|
||||
'', self.get_query_string(), self.get_fragment(),)
|
||||
)
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
def get_scheme(self):
|
||||
def get_scheme(self) -> str:
|
||||
"""
|
||||
Get the scheme of the url given in the decode function
|
||||
:returns: The URL scheme
|
||||
"""
|
||||
if not self.decoded:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.scheme)
|
||||
return _ensure_str(self._url.scheme if self._url.scheme else '')
|
||||
|
||||
def get_credential(self):
|
||||
if not self.decoded:
|
||||
def get_credential(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self._url.password:
|
||||
if self._url.username and self._url.password:
|
||||
return _ensure_str(self._url.username) + ':' + _ensure_str(self._url.password)
|
||||
if self._url.username:
|
||||
return _ensure_str(self._url.username)
|
||||
return None
|
||||
|
||||
def get_subdomain(self):
|
||||
if not self.decoded:
|
||||
def get_subdomain(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_host() is not None and not self.ip_as_host:
|
||||
if self.get_domain() in self.get_host():
|
||||
return self.get_host().rsplit(self.get_domain(), 1)[0].rstrip('.') or None
|
||||
domain = self.get_domain()
|
||||
host = self.get_host()
|
||||
if domain and host and domain in host:
|
||||
return host.rsplit(domain, 1)[0].rstrip('.') or None
|
||||
return None
|
||||
|
||||
def get_domain(self):
|
||||
if not self.decoded:
|
||||
def get_domain(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_host() is not None and not self.ip_as_host:
|
||||
return self.psl.privatesuffix(self.get_host())
|
||||
return None
|
||||
|
||||
def get_domain_without_tld(self):
|
||||
if not self.decoded:
|
||||
def get_domain_without_tld(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_tld() is not None and not self.ip_as_host:
|
||||
return self.get_domain().rsplit(self.get_tld(), 1)[0].rstrip('.')
|
||||
if domain := self.get_domain():
|
||||
return domain.rsplit(self.get_tld(), 1)[0].rstrip('.')
|
||||
return None
|
||||
|
||||
def get_host(self):
|
||||
if not self.decoded:
|
||||
def get_host(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self._url.hostname is None:
|
||||
|
@ -124,45 +139,48 @@ class PSLFaup(object):
|
|||
else:
|
||||
return _ensure_str(idna.encode(self._url.hostname, uts46=True))
|
||||
|
||||
def get_unicode_host(self):
|
||||
if not self.decoded:
|
||||
def get_unicode_host(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if not self.ip_as_host:
|
||||
return idna.decode(self.get_host(), uts46=True)
|
||||
if host := self.get_host():
|
||||
return idna.decode(host, uts46=True)
|
||||
return None
|
||||
|
||||
def get_tld(self):
|
||||
if not self.decoded:
|
||||
def get_tld(self) -> str | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
if self.get_host() is not None and not self.ip_as_host:
|
||||
return self.psl.publicsuffix(self.get_host())
|
||||
return None
|
||||
|
||||
def get_port(self):
|
||||
if not self.decoded:
|
||||
def get_port(self) -> int | None:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return self._url.port
|
||||
|
||||
def get_resource_path(self):
|
||||
if not self.decoded:
|
||||
def get_resource_path(self) -> str:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.path)
|
||||
|
||||
def get_query_string(self):
|
||||
if not self.decoded:
|
||||
def get_query_string(self) -> str:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.query)
|
||||
|
||||
def get_fragment(self):
|
||||
if not self.decoded:
|
||||
def get_fragment(self) -> str:
|
||||
if not self.decoded or not self._url:
|
||||
raise UrlNotDecoded("You must call faup.decode() first")
|
||||
|
||||
return _ensure_str(self._url.fragment)
|
||||
|
||||
def get(self):
|
||||
def get(self) -> dict[str, str | int | None | bytes]:
|
||||
self._retval["scheme"] = self.get_scheme()
|
||||
self._retval["tld"] = self.get_tld()
|
||||
self._retval["domain"] = self.get_domain()
|
||||
|
@ -177,14 +195,14 @@ class PSLFaup(object):
|
|||
return self._retval
|
||||
|
||||
|
||||
def _ensure_bytes(binary) -> bytes:
|
||||
def _ensure_bytes(binary: str | bytes) -> bytes:
|
||||
if isinstance(binary, bytes):
|
||||
return binary
|
||||
else:
|
||||
return binary.encode('utf-8')
|
||||
|
||||
|
||||
def _ensure_str(string) -> str:
|
||||
def _ensure_str(string: str | bytes) -> str:
|
||||
if isinstance(string, str):
|
||||
return string
|
||||
else:
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, date
|
||||
from dateutil.parser import parse
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .. import MISPObject
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from datetime import datetime, date
|
||||
from dateutil.parser import parse
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
class AbstractMISPObjectGenerator(MISPObject):
|
||||
|
||||
def _detect_epoch(self, timestamp: Union[str, int, float]) -> bool:
|
||||
def _detect_epoch(self, timestamp: str | int | float) -> bool:
|
||||
try:
|
||||
tmp = float(timestamp)
|
||||
if tmp < 30000000:
|
||||
|
@ -21,7 +24,7 @@ class AbstractMISPObjectGenerator(MISPObject):
|
|||
except ValueError:
|
||||
return False
|
||||
|
||||
def _sanitize_timestamp(self, timestamp: Optional[Union[datetime, date, dict, str, int, float]] = None) -> datetime:
|
||||
def _sanitize_timestamp(self, timestamp: datetime | date | dict[str, Any] | str | int | float | None = None) -> datetime:
|
||||
if not timestamp:
|
||||
return datetime.now()
|
||||
|
||||
|
@ -42,9 +45,9 @@ class AbstractMISPObjectGenerator(MISPObject):
|
|||
else:
|
||||
raise Exception(f'Unable to convert {timestamp} to a datetime.')
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
"""Contains the logic where all the values of the object are gathered"""
|
||||
if hasattr(self, '_parameters'):
|
||||
if hasattr(self, '_parameters') and self._definition is not None:
|
||||
for object_relation in self._definition['attributes']:
|
||||
value = self._parameters.pop(object_relation, None)
|
||||
if not value:
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class ASNObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('asn', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||
self._parameters['first-seen'] = first
|
||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from io import BytesIO
|
||||
from typing import Any, TYPE_CHECKING
|
||||
|
||||
from . import FileObject
|
||||
from ..exceptions import MISPObjectException
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from . import FileObject
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
try:
|
||||
import lief # type: ignore
|
||||
import lief
|
||||
import lief.logging
|
||||
lief.logging.disable()
|
||||
HAS_LIEF = True
|
||||
|
||||
from .peobject import make_pe_objects
|
||||
from .elfobject import make_elf_objects
|
||||
from .machoobject import make_macho_objects
|
||||
|
||||
except AttributeError:
|
||||
HAS_LIEF = False
|
||||
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:
|
||||
HAS_LIEF = False
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import PEObject, ELFObject, MachOObject, PESectionObject, ELFSectionObject, MachOSectionObject
|
||||
|
||||
|
||||
class FileTypeNotImplemented(MISPObjectException):
|
||||
pass
|
||||
|
||||
|
||||
def make_binary_objects(filepath: Optional[str] = None, pseudofile: Optional[BytesIO] = None, filename: Optional[str] = None, standalone: bool = True, default_attributes_parameters: dict = {}):
|
||||
def make_binary_objects(filepath: str | None = None,
|
||||
pseudofile: BytesIO | bytes | None = None,
|
||||
filename: str | None = None,
|
||||
standalone: bool = True,
|
||||
default_attributes_parameters: dict[str, Any] = {}) -> tuple[FileObject, PEObject | ELFObject | MachOObject | None, list[PESectionObject] | list[ELFSectionObject] | list[MachOSectionObject]]:
|
||||
misp_file = FileObject(filepath=filepath, pseudofile=pseudofile, filename=filename,
|
||||
standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||
if HAS_LIEF and (filepath or (pseudofile and filename)):
|
||||
try:
|
||||
if filepath:
|
||||
lief_parsed = lief.parse(filepath=filepath)
|
||||
elif pseudofile and filename:
|
||||
lief_parsed = lief.parse(raw=pseudofile.getvalue(), name=filename)
|
||||
else:
|
||||
logger.critical('You need either a filepath, or a pseudofile and a filename.')
|
||||
lief_parsed = None
|
||||
if isinstance(lief_parsed, lief.PE.Binary):
|
||||
return make_pe_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||
elif isinstance(lief_parsed, lief.ELF.Binary):
|
||||
return make_elf_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||
elif isinstance(lief_parsed, lief.MachO.Binary):
|
||||
return make_macho_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||
except lief.bad_format as e:
|
||||
logger.warning('Bad format: {}'.format(e))
|
||||
except lief.bad_file as e:
|
||||
logger.warning('Bad file: {}'.format(e))
|
||||
except lief.conversion_error as e:
|
||||
logger.warning('Conversion file: {}'.format(e))
|
||||
except lief.builder_error as e:
|
||||
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 HAS_LIEF and (filepath or pseudofile):
|
||||
if filepath:
|
||||
lief_parsed = lief.parse(filepath=filepath)
|
||||
elif pseudofile:
|
||||
if isinstance(pseudofile, bytes):
|
||||
lief_parsed = lief.parse(raw=pseudofile)
|
||||
else: # BytesIO
|
||||
lief_parsed = lief.parse(obj=pseudofile)
|
||||
else:
|
||||
logger.critical('You need either a filepath, or a pseudofile and a filename.')
|
||||
lief_parsed = None
|
||||
|
||||
if isinstance(lief_parsed, lief.lief_errors):
|
||||
logger.warning('Got an error parsing the file: {lief_parsed}')
|
||||
elif isinstance(lief_parsed, lief.PE.Binary):
|
||||
return make_pe_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||
elif isinstance(lief_parsed, lief.ELF.Binary):
|
||||
return make_elf_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||
elif isinstance(lief_parsed, lief.MachO.Binary):
|
||||
return make_macho_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
|
||||
else:
|
||||
logger.critical(f'Unexpected type from lief: {type(lief_parsed)}')
|
||||
if not HAS_LIEF:
|
||||
logger.warning('Please install lief, documentation here: https://github.com/lief-project/LIEF')
|
||||
return misp_file, None, []
|
||||
|
|
|
@ -1,49 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import csv
|
||||
from pymisp import MISPObject
|
||||
|
||||
|
||||
class CSVLoader():
|
||||
|
||||
def __init__(self, template_name: str, csv_path: Path, fieldnames: list = [], has_fieldnames=False,
|
||||
delimiter: str = ',', quotechar: str = '"'):
|
||||
def __init__(self, template_name: str, csv_path: Path,
|
||||
fieldnames: list[str] | None = None, has_fieldnames: bool=False,
|
||||
delimiter: str = ',', quotechar: str = '"') -> None:
|
||||
self.template_name = template_name
|
||||
self.delimiter = delimiter
|
||||
self.quotechar = quotechar
|
||||
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 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
|
||||
else:
|
||||
self.has_fieldnames = has_fieldnames
|
||||
|
||||
def load(self):
|
||||
def load(self) -> list[MISPObject]:
|
||||
|
||||
objects = []
|
||||
|
||||
with open(self.csv_path, newline='') as csvfile:
|
||||
reader = csv.reader(csvfile, delimiter=self.delimiter, quotechar=self.quotechar)
|
||||
if self.has_fieldnames:
|
||||
# The file has fieldnames, we either ignore it, or validate its validity
|
||||
fieldnames = [f.strip().lower() for f in reader.__next__()]
|
||||
# The file has fieldnames, we either ignore it, or use them as object-relation
|
||||
fieldnames = [f.strip() for f in reader.__next__()]
|
||||
if not self.fieldnames:
|
||||
self.fieldnames = fieldnames
|
||||
|
||||
if not self.fieldnames:
|
||||
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
|
||||
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.')
|
||||
allowed_fieldnames = list(tmp_object._definition['attributes'].keys())
|
||||
for fieldname in self.fieldnames:
|
||||
if fieldname not in allowed_fieldnames:
|
||||
raise Exception(f'{fieldname} is not a valid object relation for {self.template_name}: {allowed_fieldnames}')
|
||||
|
||||
# Check if the CSV file has a header, and if it matches with the object template
|
||||
tmp_object = MISPObject(self.template_name)
|
||||
|
||||
if not tmp_object._definition or not tmp_object._definition['attributes']:
|
||||
raise Exception(f'Unable to find the object template ({self.template_name}), impossible to create objects.')
|
||||
allowed_fieldnames = list(tmp_object._definition['attributes'].keys())
|
||||
for fieldname in self.fieldnames:
|
||||
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:
|
||||
tmp_object = MISPObject(self.template_name)
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class DomainIPObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('domain-ip', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||
self._parameters['first-seen'] = first
|
||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from . import FileObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from io import BytesIO
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
import logging
|
||||
from typing import Union, Optional
|
||||
from pathlib import Path
|
||||
from . import FileObject
|
||||
|
||||
import lief # type: ignore
|
||||
import lief
|
||||
|
||||
try:
|
||||
import pydeep # type: ignore
|
||||
|
@ -21,7 +24,10 @@ except ImportError:
|
|||
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)
|
||||
misp_file.add_reference(elf_object.uuid, 'includes', 'ELF indicators')
|
||||
elf_sections = []
|
||||
|
@ -32,29 +38,39 @@ def make_elf_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalone
|
|||
|
||||
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"""
|
||||
super().__init__('elf', **kwargs)
|
||||
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 isinstance(pseudofile, BytesIO):
|
||||
self.__elf = lief.ELF.parse(raw=pseudofile.getvalue())
|
||||
e = lief.ELF.parse(obj=pseudofile)
|
||||
elif isinstance(pseudofile, bytes):
|
||||
self.__elf = lief.ELF.parse(raw=pseudofile)
|
||||
e = lief.ELF.parse(raw=list(pseudofile))
|
||||
elif isinstance(pseudofile, list):
|
||||
e = lief.ELF.parse(raw=pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('Pseudo file can be BytesIO or bytes got {}'.format(type(pseudofile)))
|
||||
raise InvalidMISPObject(f'Pseudo file can be BytesIO or bytes got {type(pseudofile)}')
|
||||
if not e:
|
||||
raise InvalidMISPObject('Unable to parse pseudofile')
|
||||
self.__elf = e
|
||||
elif filepath:
|
||||
self.__elf = lief.ELF.parse(filepath)
|
||||
if e := lief.ELF.parse(filepath):
|
||||
self.__elf = e
|
||||
elif parsed:
|
||||
# Got an already parsed blob
|
||||
if isinstance(parsed, lief.ELF.Binary):
|
||||
self.__elf = parsed
|
||||
else:
|
||||
raise InvalidMISPObject('Not a lief.ELF.Binary: {}'.format(type(parsed)))
|
||||
raise InvalidMISPObject(f'Not a lief.ELF.Binary: {type(parsed)}')
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
# General information
|
||||
self.add_attribute('type', value=str(self.__elf.header.file_type).split('.')[1])
|
||||
self.add_attribute('entrypoint-address', value=self.__elf.entrypoint)
|
||||
|
@ -68,7 +84,7 @@ class ELFObject(AbstractMISPObjectGenerator):
|
|||
if not section.name:
|
||||
continue
|
||||
s = ELFSectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
||||
self.add_reference(s.uuid, 'includes', 'Section {} of ELF'.format(pos))
|
||||
self.add_reference(s.uuid, 'includes', f'Section {pos} of ELF')
|
||||
pos += 1
|
||||
self.sections.append(s)
|
||||
self.add_attribute('number-sections', value=len(self.sections))
|
||||
|
@ -76,22 +92,22 @@ class ELFObject(AbstractMISPObjectGenerator):
|
|||
|
||||
class ELFSectionObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, section: lief.ELF.Section, **kwargs):
|
||||
def __init__(self, section: lief.ELF.Section, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Creates an ELF Section object. Object generated by ELFObject."""
|
||||
# Python3 way
|
||||
# super().__init__('pe-section')
|
||||
super(ELFSectionObject, self).__init__('elf-section', **kwargs)
|
||||
super().__init__('elf-section', **kwargs)
|
||||
self.__section = section
|
||||
self.__data = bytes(self.__section.content)
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('name', value=self.__section.name)
|
||||
self.add_attribute('type', value=str(self.__section.type).split('.')[1])
|
||||
for flag in self.__section.flags_list:
|
||||
self.add_attribute('flag', value=str(flag).split('.')[1])
|
||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(self.__section.size) > 0:
|
||||
self.add_attribute('entropy', value=self.__section.entropy)
|
||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
@ -9,15 +10,17 @@ from email import policy, message_from_bytes
|
|||
from email.message import EmailMessage
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Union, List, Tuple, Dict, cast, Any, Optional
|
||||
from typing import cast, Any
|
||||
|
||||
from extract_msg import openMsg
|
||||
from extract_msg.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.deencapsulate import DeEncapsulator # type: ignore
|
||||
from oletools.common.codepages import codepage2codec # type: ignore
|
||||
|
||||
from ..exceptions import InvalidMISPObject, PyMISPNotImplementedYet, MISPObjectException, NewAttributeError
|
||||
from ..exceptions import InvalidMISPObject, MISPObjectException, NewAttributeError
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
@ -28,15 +31,14 @@ class MISPMsgConverstionError(MISPObjectException):
|
|||
|
||||
|
||||
class EMailObject(AbstractMISPObjectGenerator):
|
||||
def __init__(self, filepath: Optional[Union[Path, str]]=None, pseudofile: Optional[BytesIO]=None,
|
||||
attach_original_email: bool = True, **kwargs):
|
||||
def __init__(self, filepath: Path | str | None=None, pseudofile: BytesIO | bytes | None=None, # type: ignore[no-untyped-def]
|
||||
attach_original_email: bool = True, **kwargs) -> None:
|
||||
super().__init__('email', **kwargs)
|
||||
|
||||
self.attach_original_email = attach_original_email
|
||||
self.encapsulated_body: Union[str, None] = None
|
||||
self.eml_from_msg: Union[bool, None] = None
|
||||
self.raw_emails: Dict[str, Union[BytesIO, None]] = {'msg': None,
|
||||
'eml': None}
|
||||
self.encapsulated_body: str | None = None
|
||||
self.eml_from_msg: bool | None = None
|
||||
self.raw_emails: dict[str, BytesIO | None] = {'msg': None, 'eml': None}
|
||||
|
||||
self.__pseudofile = self.create_pseudofile(filepath, pseudofile)
|
||||
self.email = self.parse_email()
|
||||
|
@ -65,7 +67,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return message
|
||||
except ValueError as _e: # Exception
|
||||
logger.debug("Email not in .msg format or is a corrupted .msg. Attempting to decode email from other formats.")
|
||||
logger.debug("Error: {} ".format(_e))
|
||||
logger.debug(f"Error: {_e} ")
|
||||
try:
|
||||
if content_in_bytes[:3] == b'\xef\xbb\xbf': # utf-8-sig byte-order mark (BOM)
|
||||
eml_bytes = content_in_bytes.decode("utf_8_sig").encode("utf-8")
|
||||
|
@ -77,11 +79,11 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return eml
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
raise PyMISPNotImplementedYet("EmailObject does not know how to decode data passed to it. Object may not be an email. If this is an email please submit it as an issue to PyMISP so we can add support.")
|
||||
raise InvalidMISPObject("EmailObject does not know how to decode data passed to it. Object may not be an email. If this is an email please submit it as an issue to PyMISP so we can add support.")
|
||||
|
||||
@staticmethod
|
||||
def create_pseudofile(filepath: Optional[Union[Path, str]] = None,
|
||||
pseudofile: Optional[BytesIO] = None) -> BytesIO:
|
||||
def create_pseudofile(filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | bytes | None = None) -> BytesIO:
|
||||
"""Creates a pseudofile using directly passed data or data loaded from file path.
|
||||
"""
|
||||
if filepath:
|
||||
|
@ -89,18 +91,21 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
return BytesIO(f.read())
|
||||
elif pseudofile and isinstance(pseudofile, BytesIO):
|
||||
return pseudofile
|
||||
elif pseudofile and isinstance(pseudofile, bytes):
|
||||
return BytesIO(pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('File buffer (BytesIO) or a path is required.')
|
||||
|
||||
def _msg_to_eml(self, msg_bytes: bytes) -> EmailMessage:
|
||||
"""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
|
||||
message, body, attachments = self._extract_msg_objects(msg_obj)
|
||||
eml = self._build_eml(message, body, attachments)
|
||||
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."""
|
||||
message: EmailMessage = email.message_from_string(msg_obj.header.as_string(), policy=policy.default) # type: ignore
|
||||
body = {}
|
||||
|
@ -111,8 +116,11 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
"cte": "base64"}
|
||||
if msg_obj.htmlBody is not None:
|
||||
try:
|
||||
_html_encoding_raw = msg_obj.props['3FDE0003'].value
|
||||
_html_encoding = codepage2codec(_html_encoding_raw)
|
||||
if isinstance(msg_obj.props['3FDE0003'], FixedLengthProp):
|
||||
_html_encoding_raw = msg_obj.props['3FDE0003'].value
|
||||
_html_encoding = codepage2codec(_html_encoding_raw)
|
||||
else:
|
||||
_html_encoding = msg_obj.stringEncoding
|
||||
except KeyError:
|
||||
_html_encoding = msg_obj.stringEncoding
|
||||
body['html'] = {'obj': msg_obj.htmlBody.decode(),
|
||||
|
@ -145,17 +153,16 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
attachments = msg_obj.attachments
|
||||
return message, body, attachments
|
||||
|
||||
def _build_eml(self, message: EmailMessage, body: dict, attachments: list) -> EmailMessage:
|
||||
def _build_eml(self, message: EmailMessage, body: dict[str, Any], attachments: list[Any]) -> EmailMessage:
|
||||
"""Constructs an eml file from objects extracted from a msg."""
|
||||
# Order the body objects by increasing complexity and toss any missing objects
|
||||
body_objects: List[dict] = [body.get('text', {}),
|
||||
body.get('html', {}),
|
||||
body.get('rtf', {})]
|
||||
body_objects = [i for i in body_objects if i != {}]
|
||||
body_objects: list[dict[str, Any]] = [i for i in [body.get('text'),
|
||||
body.get('html'),
|
||||
body.get('rtf')] if i is not None]
|
||||
# If this a non-multipart email then we only need to attach the payload
|
||||
if message.get_content_maintype() != 'multipart':
|
||||
for _body in body_objects:
|
||||
if "text/{0}".format(_body['subtype']) == message.get_content_type():
|
||||
if "text/{}".format(_body['subtype']) == message.get_content_type():
|
||||
message.set_content(**_body)
|
||||
return message
|
||||
raise MISPMsgConverstionError("Unable to find appropriate eml payload in message body.")
|
||||
|
@ -167,8 +174,8 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
if isinstance(body.get('html', None), dict):
|
||||
_html = body.get('html', {}).get('obj')
|
||||
for attch in attachments:
|
||||
if _html.find("cid:{0}".format(attch.cid)) != -1:
|
||||
_content_type = attch._getStringStream('__substg1.0_370E')
|
||||
if _html.find(f"cid:{attch.cid}") != -1:
|
||||
_content_type = attch.getStringStream('__substg1.0_370E')
|
||||
maintype, subtype = _content_type.split("/", 1)
|
||||
related_content[attch.cid] = (attch,
|
||||
{'obj': attch.data,
|
||||
|
@ -189,11 +196,19 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
for mime_items in related_content.values():
|
||||
if isinstance(mime_items[1], dict):
|
||||
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)
|
||||
if body.get('text', None):
|
||||
# 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'))
|
||||
else:
|
||||
for mime_dict in body_objects:
|
||||
|
@ -205,20 +220,25 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
message.add_alternative(**mime_dict)
|
||||
for attch in attachments: # Add attachments at the end.
|
||||
if attch.cid not in related_content.keys():
|
||||
_content_type = attch._getStringStream('__substg1.0_370E')
|
||||
_content_type = attch.getStringStream('__substg1.0_370E')
|
||||
maintype, subtype = _content_type.split("/", 1)
|
||||
message.add_attachment(attch.data,
|
||||
maintype=maintype,
|
||||
subtype=subtype,
|
||||
cid=attch.cid,
|
||||
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)
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
def _update_content_disp_properties(msg_attch, eml_attch):
|
||||
def _update_content_disp_properties(msg_attch: AttachmentBase, eml_attch: EmailMessage) -> None:
|
||||
"""Set Content-Disposition params on binary eml objects
|
||||
|
||||
You currently have to set non-filename content-disp params by hand in python.
|
||||
|
@ -228,14 +248,14 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
for num, name in attch_cont_disp_props.items():
|
||||
try:
|
||||
eml_attch.set_param(name,
|
||||
email.utils.format_datetime(msg_attch.props[num].value),
|
||||
email.utils.format_datetime(msg_attch.props.getValue(num)),
|
||||
header='Content-Disposition')
|
||||
except KeyError:
|
||||
# It's fine if they don't have those values
|
||||
pass
|
||||
|
||||
@property
|
||||
def attachments(self) -> List[Tuple[str, BytesIO]]:
|
||||
def attachments(self) -> list[tuple[str | None, BytesIO]]:
|
||||
to_return = []
|
||||
try:
|
||||
for attachment in self.email.iter_attachments():
|
||||
|
@ -249,7 +269,7 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
pass
|
||||
return to_return
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
|
||||
# Attach original & Converted
|
||||
if self.attach_original_email is not None:
|
||||
|
@ -261,21 +281,30 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
data=self.raw_emails.get('msg'))
|
||||
|
||||
message = self.email
|
||||
body: EmailMessage
|
||||
|
||||
for _pref, body in message._find_body(message, preferencelist=['plain', 'html']):
|
||||
comment = "{0} body".format(body.get_content_type())
|
||||
if body := message.get_body(preferencelist=['plain']):
|
||||
comment = f"{body.get_content_type()} body"
|
||||
if self.encapsulated_body == body.get_content_type():
|
||||
comment += " De-Encapsulated from RTF in original msg."
|
||||
self.add_attribute("email-body",
|
||||
body.get_content(),
|
||||
comment=comment)
|
||||
|
||||
headers = ["{}: {}".format(k, v) for k, v in message.items()]
|
||||
if body := message.get_body(preferencelist=['html']):
|
||||
comment = f"{body.get_content_type()} body"
|
||||
if self.encapsulated_body == body.get_content_type():
|
||||
comment += " De-Encapsulated from RTF in original msg."
|
||||
self.add_attribute("email-body",
|
||||
body.get_content(),
|
||||
comment=comment)
|
||||
|
||||
headers = [f"{k}: {v}" for k, v in message.items()]
|
||||
if headers:
|
||||
self.add_attribute("header", "\n".join(headers))
|
||||
|
||||
if "Date" in message and message.get('date').datetime is not None:
|
||||
self.add_attribute("send-date", message.get('date').datetime)
|
||||
if "Date" in message and message['date'].datetime is not None:
|
||||
self.add_attribute("send-date", message['date'].datetime)
|
||||
|
||||
if "To" in message:
|
||||
self.__add_emails("to", message["To"])
|
||||
|
@ -319,31 +348,32 @@ class EMailObject(AbstractMISPObjectGenerator):
|
|||
|
||||
self.__generate_received()
|
||||
|
||||
def __add_emails(self, typ: str, data: str, insert_display_names: bool = True):
|
||||
addresses = []
|
||||
display_names = []
|
||||
def __add_emails(self, typ: str, data: str, insert_display_names: bool = True) -> None:
|
||||
addresses: list[dict[str, str]] = []
|
||||
display_names: list[dict[str, str]] = []
|
||||
|
||||
for realname, address in email.utils.getaddresses([data]):
|
||||
if address and realname:
|
||||
addresses.append({"value": address, "comment": "{} <{}>".format(realname, address)})
|
||||
addresses.append({"value": address, "comment": f"{realname} <{address}>"})
|
||||
elif address:
|
||||
addresses.append({"value": address})
|
||||
else: # parsing failed, skip
|
||||
continue
|
||||
|
||||
if realname:
|
||||
display_names.append({"value": realname, "comment": "{} <{}>".format(realname, address)})
|
||||
display_names.append({"value": realname, "comment": f"{realname} <{address}>"})
|
||||
|
||||
if addresses:
|
||||
self.add_attributes(typ, *addresses)
|
||||
for a in addresses:
|
||||
self.add_attribute(typ, **a)
|
||||
if insert_display_names and display_names:
|
||||
try:
|
||||
self.add_attributes("{}-display-name".format(typ), *display_names)
|
||||
for d in display_names:
|
||||
self.add_attribute(f"{typ}-display-name", **d)
|
||||
except NewAttributeError:
|
||||
# email object doesn't support display name for all email addrs
|
||||
pass
|
||||
|
||||
def __generate_received(self):
|
||||
def __generate_received(self) -> None:
|
||||
"""
|
||||
Extract IP addresses from received headers that are not private. Also extract hostnames or domains.
|
||||
"""
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
from pymispgalaxies import Clusters # type: ignore
|
||||
|
@ -14,7 +15,7 @@ except ImportError:
|
|||
has_pymispgalaxies = False
|
||||
|
||||
|
||||
def revert_tag_from_galaxies(tag):
|
||||
def revert_tag_from_galaxies(tag: str) -> list[str]:
|
||||
clusters = Clusters()
|
||||
try:
|
||||
return clusters.revert_machinetag(tag)
|
||||
|
@ -22,7 +23,7 @@ def revert_tag_from_galaxies(tag):
|
|||
return []
|
||||
|
||||
|
||||
def revert_tag_from_taxonomies(tag):
|
||||
def revert_tag_from_taxonomies(tag: str) -> list[str]:
|
||||
taxonomies = Taxonomies()
|
||||
try:
|
||||
return taxonomies.revert_machinetag(tag)
|
||||
|
@ -30,7 +31,7 @@ def revert_tag_from_taxonomies(tag):
|
|||
return []
|
||||
|
||||
|
||||
def search_taxonomies(query):
|
||||
def search_taxonomies(query: str) -> list[str]:
|
||||
taxonomies = Taxonomies()
|
||||
found = taxonomies.search(query)
|
||||
if not found:
|
||||
|
@ -38,6 +39,6 @@ def search_taxonomies(query):
|
|||
return found
|
||||
|
||||
|
||||
def search_galaxies(query):
|
||||
def search_galaxies(query: str) -> list[str]:
|
||||
clusters = Clusters()
|
||||
return clusters.search(query)
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class Fail2BanObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs): # type: ignore[no-untyped-def]
|
||||
super().__init__('fail2ban', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
timestamp = self._sanitize_timestamp(self._parameters.pop('processing-timestamp', None))
|
||||
self._parameters['processing-timestamp'] = timestamp
|
||||
super().generate_attributes()
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from pymisp import MISPEvent
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
|
||||
def feed_meta_generator(path: Path):
|
||||
def feed_meta_generator(path: Path) -> None:
|
||||
manifests = {}
|
||||
hashes: List[str] = []
|
||||
hashes: list[str] = []
|
||||
|
||||
for f_name in path.glob('*.json'):
|
||||
if str(f_name.name) == 'manifest.json':
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
@ -10,7 +11,6 @@ import math
|
|||
from collections import Counter
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
@ -22,7 +22,7 @@ except ImportError:
|
|||
HAS_PYDEEP = False
|
||||
|
||||
try:
|
||||
import magic # type: ignore
|
||||
import magic
|
||||
HAS_MAGIC = True
|
||||
except ImportError:
|
||||
HAS_MAGIC = False
|
||||
|
@ -30,12 +30,14 @@ except ImportError:
|
|||
|
||||
class FileObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, filename: Optional[str] = None, **kwargs) -> None:
|
||||
def __init__(self, filepath: Path | str | None = None, # type: ignore[no-untyped-def]
|
||||
pseudofile: BytesIO | bytes | None = None,
|
||||
filename: str | None = None, **kwargs) -> None:
|
||||
super().__init__('file', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("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:
|
||||
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:
|
||||
# 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
|
||||
|
@ -55,10 +57,10 @@ class FileObject(AbstractMISPObjectGenerator):
|
|||
self.__data = self.__pseudofile.getvalue()
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('filename', value=self.__filename)
|
||||
size = self.add_attribute('size-in-bytes', value=len(self.__data))
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=len(self.__data))
|
||||
if len(self.__data) > 0:
|
||||
self.add_attribute('entropy', value=self.__entropy_H(self.__data))
|
||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from typing import List
|
||||
|
||||
|
||||
class GenericObjectGenerator(AbstractMISPObjectGenerator):
|
||||
|
||||
# FIXME: this method is different from the master one, and that's probably not a good idea.
|
||||
def generate_attributes(self, attributes: List[dict]): # type: ignore
|
||||
def generate_attributes(self, attributes: list[dict[str, Any]]) -> None: # type: ignore[override]
|
||||
"""Generates MISPObjectAttributes from a list of dictionaries.
|
||||
Each entry if the list must be in one of the two following formats:
|
||||
* {<object_relation>: <value>}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class GeolocationObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('geolocation', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
first = self._sanitize_timestamp(self._parameters.pop('first-seen', None))
|
||||
self._parameters['first-seen'] = first
|
||||
last = self._sanitize_timestamp(self._parameters.pop('last-seen', None))
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class GitVulnFinderObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('git-vuln-finder', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
authored_date = self._sanitize_timestamp(self._parameters.pop('authored_date', None))
|
||||
self._parameters['authored_date'] = authored_date
|
||||
committed_date = self._sanitize_timestamp(self._parameters.pop('committed_date', None))
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ..api import PyMISP
|
||||
|
||||
try:
|
||||
from pymispwarninglists import WarningLists # type: ignore
|
||||
from pymispwarninglists import WarningLists, WarningList # type: ignore
|
||||
has_pymispwarninglists = True
|
||||
except ImportError:
|
||||
has_pymispwarninglists = False
|
||||
|
||||
|
||||
def from_instance(pymisp_instance, slow_search=False):
|
||||
def from_instance(pymisp_instance: PyMISP, slow_search: bool=False) -> WarningLists:
|
||||
"""Load the warnindlist from an existing MISP instance
|
||||
:pymisp_instance: Already instantialized PyMISP instance."""
|
||||
|
||||
warninglists_index = pymisp_instance.get_warninglists()['Warninglists']
|
||||
warninglists_index = pymisp_instance.warninglists(pythonify=True)
|
||||
all_warningslists = []
|
||||
for warninglist in warninglists_index:
|
||||
wl = pymisp_instance.get_warninglist(warninglist['Warninglist']['id'])['Warninglist']
|
||||
wl['list'] = wl.pop('WarninglistEntry')
|
||||
all_warningslists.append(wl)
|
||||
if isinstance(warninglist, WarningList):
|
||||
wl = pymisp_instance.get_warninglist(warninglist['Warninglist']['id'])['Warninglist']
|
||||
wl['list'] = wl.pop('WarninglistEntry')
|
||||
all_warningslists.append(wl)
|
||||
|
||||
return WarningLists(slow_search, all_warningslists)
|
||||
|
||||
|
||||
def from_package(slow_search=False):
|
||||
def from_package(slow_search: bool=False) -> WarningLists:
|
||||
return WarningLists(slow_search)
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from io import BytesIO
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
from pathlib import Path
|
||||
from . import FileObject
|
||||
|
||||
import lief # type: ignore
|
||||
from . import FileObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
import lief
|
||||
|
||||
try:
|
||||
import pydeep # type: ignore
|
||||
|
@ -21,7 +25,10 @@ except ImportError:
|
|||
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)
|
||||
misp_file.add_reference(macho_object.uuid, 'includes', 'MachO indicators')
|
||||
macho_sections = []
|
||||
|
@ -32,31 +39,43 @@ def make_macho_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalo
|
|||
|
||||
class MachOObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parsed: Optional[lief.MachO.Binary] = None, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, **kwargs):
|
||||
__macho: lief.MachO.Binary
|
||||
|
||||
def __init__(self, parsed: lief.MachO.Binary | lief.MachO.FatBinary | None = None, # type: ignore[no-untyped-def]
|
||||
filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | list[int] | None = None,
|
||||
**kwargs) -> None:
|
||||
"""Creates an MachO object, with lief"""
|
||||
super().__init__('macho', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("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 isinstance(pseudofile, BytesIO):
|
||||
self.__macho = lief.MachO.parse(raw=pseudofile.getvalue())
|
||||
m = lief.MachO.parse(obj=pseudofile)
|
||||
elif isinstance(pseudofile, bytes):
|
||||
self.__macho = lief.MachO.parse(raw=pseudofile)
|
||||
m = lief.MachO.parse(raw=list(pseudofile))
|
||||
elif isinstance(pseudofile, list):
|
||||
m = lief.MachO.parse(raw=pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('Pseudo file can be BytesIO or bytes got {}'.format(type(pseudofile)))
|
||||
raise InvalidMISPObject(f'Pseudo file can be BytesIO or bytes got {type(pseudofile)}')
|
||||
if not m:
|
||||
raise InvalidMISPObject('Unable to parse pseudofile')
|
||||
self.__macho = m.at(0)
|
||||
elif filepath:
|
||||
self.__macho = lief.MachO.parse(filepath)
|
||||
if m := lief.MachO.parse(filepath):
|
||||
self.__macho = m.at(0)
|
||||
elif parsed:
|
||||
# Got an already parsed blob
|
||||
if isinstance(parsed, lief.MachO.Binary):
|
||||
if isinstance(parsed, lief.MachO.FatBinary):
|
||||
self.__macho = parsed.at(0)
|
||||
elif isinstance(parsed, lief.MachO.Binary):
|
||||
self.__macho = parsed
|
||||
else:
|
||||
raise InvalidMISPObject('Not a lief.MachO.Binary: {}'.format(type(parsed)))
|
||||
raise InvalidMISPObject(f'Not a lief.MachO.Binary: {type(parsed)}')
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('type', value=str(self.__macho.header.file_type).split('.')[1])
|
||||
self.add_attribute('name', value=self.__macho.name)
|
||||
# General information
|
||||
if self.__macho.has_entrypoint:
|
||||
self.add_attribute('entrypoint-address', value=self.__macho.entrypoint)
|
||||
|
@ -66,7 +85,7 @@ class MachOObject(AbstractMISPObjectGenerator):
|
|||
pos = 0
|
||||
for section in self.__macho.sections:
|
||||
s = MachOSectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
||||
self.add_reference(s.uuid, 'includes', 'Section {} of MachO'.format(pos))
|
||||
self.add_reference(s.uuid, 'includes', f'Section {pos} of MachO')
|
||||
pos += 1
|
||||
self.sections.append(s)
|
||||
self.add_attribute('number-sections', value=len(self.sections))
|
||||
|
@ -74,19 +93,19 @@ class MachOObject(AbstractMISPObjectGenerator):
|
|||
|
||||
class MachOSectionObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, section: lief.MachO.Section, **kwargs):
|
||||
def __init__(self, section: lief.MachO.Section, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Creates an MachO Section object. Object generated by MachOObject."""
|
||||
# Python3 way
|
||||
# super().__init__('pe-section')
|
||||
super(MachOSectionObject, self).__init__('macho-section', **kwargs)
|
||||
super().__init__('macho-section', **kwargs)
|
||||
self.__section = section
|
||||
self.__data = bytes(self.__section.content)
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('name', value=self.__section.name)
|
||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(self.__section.size) > 0:
|
||||
self.add_attribute('entropy', value=self.__section.entropy)
|
||||
self.add_attribute('md5', value=md5(self.__data).hexdigest())
|
||||
self.add_attribute('sha1', value=sha1(self.__data).hexdigest())
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
# NOTE: Reference on how this module is used: https://vvx7.io/posts/2020/05/misp-slack-bot/
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class MicroblogObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parameters: dict, strict: bool = True, **kwargs):
|
||||
def __init__(self, parameters: dict[str, Any], strict: bool = True, **kwargs): # type: ignore[no-untyped-def]
|
||||
super().__init__('microblog', strict=strict, **kwargs)
|
||||
self._parameters = parameters
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
# Raw post.
|
||||
if 'post' in self._parameters:
|
||||
self.add_attribute('post', value=self._parameters['post'])
|
||||
|
@ -32,7 +34,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# Original URL location of the microblog post (potentially malicious.
|
||||
if 'url' in self._parameters:
|
||||
if isinstance(self._parameters.get('url'), list):
|
||||
for i in self._parameters.get('url'):
|
||||
for i in self._parameters['url']:
|
||||
self.add_attribute('url', value=i)
|
||||
else:
|
||||
self.add_attribute('url', value=self._parameters['url'])
|
||||
|
@ -40,7 +42,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# Archive of the original document (Internet Archive, Archive.is, etc).
|
||||
if 'archive' in self._parameters:
|
||||
if isinstance(self._parameters.get('archive'), list):
|
||||
for i in self._parameters.get('archive'):
|
||||
for i in self._parameters['archive']:
|
||||
self.add_attribute('archive', value=i)
|
||||
else:
|
||||
self.add_attribute('archive', value=self._parameters['archive'])
|
||||
|
@ -74,7 +76,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
"Instagram", "Forum", "Other"]
|
||||
if 'type' in self._parameters:
|
||||
if isinstance(self._parameters.get('type'), list):
|
||||
for i in self._parameters.get('type'):
|
||||
for i in self._parameters['type']:
|
||||
if i in type_allowed_values:
|
||||
self.add_attribute('type', value=i)
|
||||
else:
|
||||
|
@ -85,7 +87,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
type_allowed_values = ["Informative", "Malicious", "Misinformation", "Disinformation", "Unknown"]
|
||||
if 'state' in self._parameters:
|
||||
if isinstance(self._parameters.get('state'), list):
|
||||
for i in self._parameters.get('state'):
|
||||
for i in self._parameters['state']:
|
||||
if i in type_allowed_values:
|
||||
self.add_attribute('state', value=i)
|
||||
else:
|
||||
|
@ -100,7 +102,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
type_allowed_values = ["Verified", "Unverified", "Unknown"]
|
||||
if 'verified-username' in self._parameters:
|
||||
if isinstance(self._parameters.get('verified-username'), list):
|
||||
for i in self._parameters.get('verified-username'):
|
||||
for i in self._parameters['verified-username']:
|
||||
if i in type_allowed_values:
|
||||
self.add_attribute('verified-username', value=i)
|
||||
else:
|
||||
|
@ -110,7 +112,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# embedded-link.
|
||||
if 'embedded-link' in self._parameters:
|
||||
if isinstance(self._parameters.get('embedded-link'), list):
|
||||
for i in self._parameters.get('embedded-link'):
|
||||
for i in self._parameters['embedded-link']:
|
||||
self.add_attribute('embedded-link', value=i)
|
||||
else:
|
||||
self.add_attribute('embedded-link', value=self._parameters['embedded-link'])
|
||||
|
@ -118,7 +120,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# embedded-safe-link
|
||||
if 'embedded-safe-link' in self._parameters:
|
||||
if isinstance(self._parameters.get('embedded-safe-link'), list):
|
||||
for i in self._parameters.get('embedded-safe-link'):
|
||||
for i in self._parameters['embedded-safe-link']:
|
||||
self.add_attribute('embedded-safe-link', value=i)
|
||||
else:
|
||||
self.add_attribute('embedded-safe-link', value=self._parameters['embedded-safe-link'])
|
||||
|
@ -126,7 +128,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# Hashtag into the microblog post.
|
||||
if 'hashtag' in self._parameters:
|
||||
if isinstance(self._parameters.get('hashtag'), list):
|
||||
for i in self._parameters.get('hashtag'):
|
||||
for i in self._parameters['hashtag']:
|
||||
self.add_attribute('hashtag', value=i)
|
||||
else:
|
||||
self.add_attribute('hashtag', value=self._parameters['hashtag'])
|
||||
|
@ -134,7 +136,7 @@ class MicroblogObject(AbstractMISPObjectGenerator):
|
|||
# username quoted
|
||||
if 'username-quoted' in self._parameters:
|
||||
if isinstance(self._parameters.get('username-quoted'), list):
|
||||
for i in self._parameters.get('username-quoted'):
|
||||
for i in self._parameters['username-quoted']:
|
||||
self.add_attribute('username-quoted', value=i)
|
||||
else:
|
||||
self.add_attribute('username-quoted', value=self._parameters['username-quoted'])
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
from .. import MISPEvent
|
||||
|
||||
try:
|
||||
|
@ -13,23 +14,23 @@ except ImportError:
|
|||
|
||||
class Neo4j():
|
||||
|
||||
def __init__(self, host='localhost:7474', username='neo4j', password='neo4j'):
|
||||
def __init__(self, host: str='localhost:7474', username: str='neo4j', password: str='neo4j') -> None:
|
||||
if not has_py2neo:
|
||||
raise Exception('py2neo is required, please install: pip install py2neo')
|
||||
authenticate(host, username, password)
|
||||
self.graph = Graph("http://{}/db/data/".format(host))
|
||||
self.graph = Graph(f"http://{host}/db/data/")
|
||||
|
||||
def load_events_directory(self, directory):
|
||||
self.events = []
|
||||
def load_events_directory(self, directory: str) -> None:
|
||||
self.events: list[MISPEvent] = []
|
||||
for path in glob.glob(os.path.join(directory, '*.json')):
|
||||
e = MISPEvent()
|
||||
e.load(path)
|
||||
self.import_event(e)
|
||||
|
||||
def del_all(self):
|
||||
def del_all(self) -> None:
|
||||
self.graph.delete_all()
|
||||
|
||||
def import_event(self, event):
|
||||
def import_event(self, event: MISPEvent) -> None:
|
||||
tx = self.graph.begin()
|
||||
event_node = Node('Event', uuid=event.uuid, name=event.info)
|
||||
# event_node['distribution'] = event.distribution
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
|
@ -156,7 +155,7 @@ def extract_field(report, field_name):
|
|||
def load_openioc_file(openioc_path):
|
||||
if not os.path.exists(openioc_path):
|
||||
raise Exception("Path doesn't exists.")
|
||||
with open(openioc_path, 'r') as f:
|
||||
with open(openioc_path) as f:
|
||||
return load_openioc(f)
|
||||
|
||||
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from io import BytesIO
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from datetime import datetime
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
from pathlib import Path
|
||||
|
||||
from base64 import b64encode
|
||||
from datetime import datetime
|
||||
from hashlib import md5, sha1, sha256, sha512
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from . import FileObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from ..exceptions import InvalidMISPObject
|
||||
|
||||
import lief # type: ignore
|
||||
import lief
|
||||
import lief.PE
|
||||
|
||||
try:
|
||||
import pydeep # type: ignore
|
||||
|
@ -24,7 +27,10 @@ except ImportError:
|
|||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
def make_pe_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalone: bool = True, default_attributes_parameters: dict = {}):
|
||||
def make_pe_objects(lief_parsed: lief.PE.Binary,
|
||||
misp_file: FileObject,
|
||||
standalone: bool = True,
|
||||
default_attributes_parameters: dict[str, Any] = {}) -> tuple[FileObject, PEObject, list[PESectionObject]]:
|
||||
pe_object = PEObject(parsed=lief_parsed, standalone=standalone, default_attributes_parameters=default_attributes_parameters)
|
||||
misp_file.add_reference(pe_object.uuid, 'includes', 'PE indicators')
|
||||
pe_sections = []
|
||||
|
@ -35,44 +41,57 @@ def make_pe_objects(lief_parsed: lief.Binary, misp_file: FileObject, standalone:
|
|||
|
||||
class PEObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, parsed: Optional[lief.PE.Binary] = None, filepath: Optional[Union[Path, str]] = None, pseudofile: Optional[BytesIO] = None, **kwargs):
|
||||
__pe: lief.PE.Binary
|
||||
|
||||
def __init__(self, parsed: lief.PE.Binary | None = None, # type: ignore[no-untyped-def]
|
||||
filepath: Path | str | None = None,
|
||||
pseudofile: BytesIO | list[int] | None = None,
|
||||
**kwargs) -> None:
|
||||
"""Creates an PE object, with lief"""
|
||||
super().__init__('pe', **kwargs)
|
||||
if not HAS_PYDEEP:
|
||||
logger.warning("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 isinstance(pseudofile, BytesIO):
|
||||
self.__pe = lief.PE.parse(raw=pseudofile.getvalue())
|
||||
p = lief.PE.parse(obj=pseudofile)
|
||||
elif isinstance(pseudofile, bytes):
|
||||
self.__pe = lief.PE.parse(raw=pseudofile)
|
||||
p = lief.PE.parse(raw=list(pseudofile))
|
||||
elif isinstance(pseudofile, list):
|
||||
p = lief.PE.parse(raw=pseudofile)
|
||||
else:
|
||||
raise InvalidMISPObject('Pseudo file can be BytesIO or bytes got {}'.format(type(pseudofile)))
|
||||
raise InvalidMISPObject(f'Pseudo file can be BytesIO or bytes got {type(pseudofile)}')
|
||||
if not p:
|
||||
raise InvalidMISPObject('Unable to parse pseudofile')
|
||||
self.__pe = p
|
||||
elif filepath:
|
||||
self.__pe = lief.PE.parse(filepath)
|
||||
if p := lief.PE.parse(filepath):
|
||||
self.__pe = p
|
||||
else:
|
||||
raise InvalidMISPObject(f'Unable to parse {filepath}')
|
||||
elif parsed:
|
||||
# Got an already parsed blob
|
||||
if isinstance(parsed, lief.PE.Binary):
|
||||
self.__pe = parsed
|
||||
else:
|
||||
raise InvalidMISPObject('Not a lief.PE.Binary: {}'.format(type(parsed)))
|
||||
raise InvalidMISPObject(f'Not a lief.PE.Binary: {type(parsed)}')
|
||||
self.generate_attributes()
|
||||
|
||||
def _is_exe(self):
|
||||
def _is_exe(self) -> bool:
|
||||
if not self._is_dll() and not self._is_driver():
|
||||
return self.__pe.header.has_characteristic(lief.PE.HEADER_CHARACTERISTICS.EXECUTABLE_IMAGE)
|
||||
return self.__pe.header.has_characteristic(lief.PE.Header.CHARACTERISTICS.EXECUTABLE_IMAGE)
|
||||
return False
|
||||
|
||||
def _is_dll(self):
|
||||
return self.__pe.header.has_characteristic(lief.PE.HEADER_CHARACTERISTICS.DLL)
|
||||
def _is_dll(self) -> bool:
|
||||
return self.__pe.header.has_characteristic(lief.PE.Header.CHARACTERISTICS.DLL)
|
||||
|
||||
def _is_driver(self):
|
||||
def _is_driver(self) -> bool:
|
||||
# List from pefile
|
||||
system_DLLs = set(('ntoskrnl.exe', 'hal.dll', 'ndis.sys', 'bootvid.dll', 'kdcom.dll'))
|
||||
system_DLLs = {'ntoskrnl.exe', 'hal.dll', 'ndis.sys', 'bootvid.dll', 'kdcom.dll'}
|
||||
if system_DLLs.intersection([imp.lower() for imp in self.__pe.libraries]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_pe_type(self):
|
||||
def _get_pe_type(self) -> str:
|
||||
if self._is_dll():
|
||||
return 'dll'
|
||||
elif self._is_driver():
|
||||
|
@ -82,31 +101,27 @@ class PEObject(AbstractMISPObjectGenerator):
|
|||
else:
|
||||
return 'unknown'
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('type', value=self._get_pe_type())
|
||||
# General information
|
||||
self.add_attribute('entrypoint-address', value=self.__pe.entrypoint)
|
||||
self.add_attribute('compilation-timestamp', value=datetime.utcfromtimestamp(self.__pe.header.time_date_stamps).isoformat())
|
||||
self.add_attribute('imphash', value=lief.PE.get_imphash(self.__pe, lief.PE.IMPHASH_MODE.PEFILE))
|
||||
self.add_attribute('authentihash', value=self.__pe.authentihash_sha256.hex())
|
||||
try:
|
||||
if (self.__pe.has_resources
|
||||
and self.__pe.resources_manager.has_version
|
||||
and self.__pe.resources_manager.version.has_string_file_info
|
||||
and self.__pe.resources_manager.version.string_file_info.langcode_items):
|
||||
fileinfo = dict(self.__pe.resources_manager.version.string_file_info.langcode_items[0].items.items())
|
||||
r_manager = self.__pe.resources_manager
|
||||
if isinstance(r_manager, lief.PE.ResourcesManager):
|
||||
version = r_manager.version
|
||||
if isinstance(version, lief.PE.ResourceVersion) and version.string_file_info is not None:
|
||||
fileinfo = dict(version.string_file_info.langcode_items[0].items.items())
|
||||
self.add_attribute('original-filename', value=fileinfo.get('OriginalFilename'))
|
||||
self.add_attribute('internal-filename', value=fileinfo.get('InternalName'))
|
||||
self.add_attribute('file-description', value=fileinfo.get('FileDescription'))
|
||||
self.add_attribute('file-version', value=fileinfo.get('FileVersion'))
|
||||
self.add_attribute('lang-id', value=self.__pe.resources_manager.version.string_file_info.langcode_items[0].key)
|
||||
self.add_attribute('product-name', value=fileinfo.get('ProductName'))
|
||||
self.add_attribute('product-version', value=fileinfo.get('ProductVersion'))
|
||||
self.add_attribute('company-name', value=fileinfo.get('CompanyName'))
|
||||
self.add_attribute('legal-copyright', value=fileinfo.get('LegalCopyright'))
|
||||
except lief.read_out_of_bound:
|
||||
# The file is corrupted
|
||||
pass
|
||||
self.add_attribute('lang-id', value=version.string_file_info.langcode_items[0].key)
|
||||
# Sections
|
||||
self.sections = []
|
||||
if self.__pe.sections:
|
||||
|
@ -116,10 +131,14 @@ class PEObject(AbstractMISPObjectGenerator):
|
|||
# Skip section if name is none AND size is 0.
|
||||
continue
|
||||
s = PESectionObject(section, standalone=self._standalone, default_attributes_parameters=self._default_attributes_parameters)
|
||||
self.add_reference(s.uuid, 'includes', 'Section {} of PE'.format(pos))
|
||||
self.add_reference(s.uuid, 'includes', f'Section {pos} of PE')
|
||||
if ((self.__pe.entrypoint >= section.virtual_address)
|
||||
and (self.__pe.entrypoint < (section.virtual_address + section.virtual_size))):
|
||||
self.add_attribute('entrypoint-section-at-position', value='{}|{}'.format(section.name, pos))
|
||||
if isinstance(section.name, bytes):
|
||||
section_name = section.name.decode()
|
||||
else:
|
||||
section_name = section.name
|
||||
self.add_attribute('entrypoint-section-at-position', value=f'{section_name}|{pos}')
|
||||
pos += 1
|
||||
self.sections.append(s)
|
||||
self.add_attribute('number-sections', value=len(self.sections))
|
||||
|
@ -139,16 +158,30 @@ class PEObject(AbstractMISPObjectGenerator):
|
|||
|
||||
class PECertificate(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, certificate: lief.PE.x509, **kwargs):
|
||||
def __init__(self, certificate: lief.PE.x509, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('x509')
|
||||
self.__certificate = certificate
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('issuer', value=self.__certificate.issuer)
|
||||
self.add_attribute('serial-number', value=self.__certificate.serial_number)
|
||||
self.add_attribute('validity-not-before', value=datetime(*self.__certificate.valid_from))
|
||||
self.add_attribute('validity-not-after', value=datetime(*self.__certificate.valid_to))
|
||||
if len(self.__certificate.valid_from) == 6:
|
||||
self.add_attribute('validity-not-before',
|
||||
value=datetime(year=self.__certificate.valid_from[0],
|
||||
month=self.__certificate.valid_from[1],
|
||||
day=self.__certificate.valid_from[2],
|
||||
hour=self.__certificate.valid_from[3],
|
||||
minute=self.__certificate.valid_from[4],
|
||||
second=self.__certificate.valid_from[5]))
|
||||
if len(self.__certificate.valid_to) == 6:
|
||||
self.add_attribute('validity-not-after',
|
||||
value=datetime(year=self.__certificate.valid_to[0],
|
||||
month=self.__certificate.valid_to[1],
|
||||
day=self.__certificate.valid_to[2],
|
||||
hour=self.__certificate.valid_to[3],
|
||||
minute=self.__certificate.valid_to[4],
|
||||
second=self.__certificate.valid_to[5]))
|
||||
self.add_attribute('version', value=self.__certificate.version)
|
||||
self.add_attribute('subject', value=self.__certificate.subject)
|
||||
self.add_attribute('signature_algorithm', value=self.__certificate.signature_algorithm)
|
||||
|
@ -157,19 +190,19 @@ class PECertificate(AbstractMISPObjectGenerator):
|
|||
|
||||
class PESigners(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, signer: lief.PE.SignerInfo, **kwargs):
|
||||
def __init__(self, signer: lief.PE.SignerInfo, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('authenticode-signerinfo')
|
||||
self.__signer = signer
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('issuer', value=self.__signer.issuer)
|
||||
self.add_attribute('serial-number', value=self.__signer.serial_number)
|
||||
self.add_attribute('version', value=self.__signer.version)
|
||||
self.add_attribute('digest_algorithm', value=self.__signer.digest_algorithm.name)
|
||||
self.add_attribute('encryption_algorithm', value=self.__signer.encryption_algorithm.name)
|
||||
self.add_attribute('digest_algorithm', value=str(self.__signer.digest_algorithm))
|
||||
self.add_attribute('encryption_algorithm', value=str(self.__signer.encryption_algorithm))
|
||||
self.add_attribute('digest-base64', value=b64encode(self.__signer.encrypted_digest))
|
||||
info = self.__signer.get_attribute(lief.PE.SIG_ATTRIBUTE_TYPES.SPC_SP_OPUS_INFO)
|
||||
info: lief.PE.SpcSpOpusInfo = self.__signer.get_attribute(lief.PE.Attribute.TYPE.SPC_SP_OPUS_INFO) # type: ignore[assignment]
|
||||
if info:
|
||||
self.add_attribute('program-name', value=info.program_name)
|
||||
self.add_attribute('url', value=info.more_info)
|
||||
|
@ -177,17 +210,17 @@ class PESigners(AbstractMISPObjectGenerator):
|
|||
|
||||
class PESectionObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, section: lief.PE.Section, **kwargs):
|
||||
def __init__(self, section: lief.PE.Section, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
"""Creates an PE Section object. Object generated by PEObject."""
|
||||
super().__init__('pe-section')
|
||||
self.__section = section
|
||||
self.__data = bytes(self.__section.content)
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('name', value=self.__section.name)
|
||||
size = self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(size.value) > 0:
|
||||
self.add_attribute('size-in-bytes', value=self.__section.size)
|
||||
if int(self.__section.size) > 0:
|
||||
# zero-filled sections can create too many correlations
|
||||
to_ids = float(self.__section.entropy) > 0
|
||||
disable_correlation = not to_ids
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# Standard imports
|
||||
import base64
|
||||
|
@ -49,7 +50,7 @@ def create_flowable_tag(misp_tag):
|
|||
return [Flowable_Tag(text=misp_tag.name, color=misp_tag.colour, custom_style=col1_style)]
|
||||
|
||||
|
||||
class Flowable_Tag(Flowable):
|
||||
class Flowable_Tag(Flowable): # type: ignore[misc]
|
||||
"""
|
||||
Custom flowable to handle tags. Draw one Tag with the webview formatting
|
||||
Modified from : http://two.pairlist.net/pipermail/reportlab-users/2005-February/003695.html
|
||||
|
@ -108,7 +109,7 @@ class Flowable_Tag(Flowable):
|
|||
LEFT_INTERNAL_PADDING = 2
|
||||
ELONGATION = LEFT_INTERNAL_PADDING * 2
|
||||
|
||||
p = Paragraph("<font color='{}'>{}</font>".format(self.choose_good_text_color(), self.text), style=self.custom_style)
|
||||
p = Paragraph(f"<font color='{self.choose_good_text_color()}'>{self.text}</font>", style=self.custom_style)
|
||||
string_width = stringWidth(self.text, self.custom_style.fontName, self.custom_style.fontSize)
|
||||
|
||||
self.width = string_width + ELONGATION
|
||||
|
@ -615,7 +616,7 @@ class Value_Formatter():
|
|||
curr_uuid = str(is_safe_value(uuid))
|
||||
curr_baseurl = self.config[moduleconfig[0]]
|
||||
curr_url = uuid_to_url(curr_baseurl, curr_uuid)
|
||||
html_url = "<a href={}>{}</a>".format(curr_url, safe_string(text))
|
||||
html_url = f"<a href={curr_url}>{safe_string(text)}</a>"
|
||||
|
||||
if color:
|
||||
# They want fancy colors
|
||||
|
@ -744,7 +745,7 @@ class Value_Formatter():
|
|||
answer = YES_ANSWER
|
||||
if is_safe_value(published_timestamp):
|
||||
# Published and have published date
|
||||
answer += '({})'.format(published_timestamp.strftime(EXPORT_DATE_FORMAT))
|
||||
answer += f'({published_timestamp.strftime(EXPORT_DATE_FORMAT)})'
|
||||
else:
|
||||
# Published without published date
|
||||
answer += "(no date)"
|
||||
|
@ -1091,7 +1092,7 @@ class Event_Metadata():
|
|||
Paragraph("Related Event #" + str(i + OFFSET), self.sample_style_sheet['Heading4']))
|
||||
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
|
||||
else:
|
||||
return flowable_table.append(self.value_formatter.get_unoverflowable_paragraph(DEFAULT_VALUE))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
|
@ -8,13 +9,13 @@ class SBSignatureObject(AbstractMISPObjectGenerator):
|
|||
'''
|
||||
Sandbox Analyzer
|
||||
'''
|
||||
def __init__(self, software: str, report: list, **kwargs):
|
||||
def __init__(self, software: str, report: list[tuple[str, str]], **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('sb-signature', **kwargs)
|
||||
self._software = software
|
||||
self._report = report
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
''' Parse the report for relevant attributes '''
|
||||
self.add_attribute("software", value=self._software)
|
||||
for (signature_name, description) in self._report:
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
|
||||
from ..exceptions import InvalidMISPObject
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
from io import StringIO
|
||||
import logging
|
||||
from typing import Optional, Union
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger('pymisp')
|
||||
|
||||
|
||||
class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, authorized_keys_path: Optional[Union[Path, str]] = None, authorized_keys_pseudofile: Optional[StringIO] = None, **kwargs):
|
||||
# PY3 way:
|
||||
def __init__(self, authorized_keys_path: Path | str | None = None, # type: ignore[no-untyped-def]
|
||||
authorized_keys_pseudofile: StringIO | None = None, **kwargs):
|
||||
super().__init__('ssh-authorized-keys', **kwargs)
|
||||
if authorized_keys_path:
|
||||
with open(authorized_keys_path, 'r') as f:
|
||||
with open(authorized_keys_path) as f:
|
||||
self.__pseudofile = StringIO(f.read())
|
||||
elif authorized_keys_pseudofile and isinstance(authorized_keys_pseudofile, StringIO):
|
||||
self.__pseudofile = authorized_keys_pseudofile
|
||||
|
@ -26,7 +28,7 @@ class SSHAuthorizedKeysObject(AbstractMISPObjectGenerator):
|
|||
self.__data = self.__pseudofile.getvalue()
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
for line in self.__pseudofile:
|
||||
if line.startswith('ssh') or line.startswith('ecdsa'):
|
||||
key = line.split(' ')[1]
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
try:
|
||||
from misp_stix_converter.converters.buildMISPAttribute import buildEvent # type: ignore
|
||||
from misp_stix_converter.converters import convert # type: ignore
|
||||
from misp_stix_converter.converters.convert import MISPtoSTIX # type: ignore
|
||||
has_misp_stix_converter = True
|
||||
except ImportError:
|
||||
has_misp_stix_converter = False
|
||||
|
||||
|
||||
def load_stix(stix, distribution: int = 3, threat_level_id: int = 2, analysis: int = 0):
|
||||
'''Returns a MISPEvent object from a STIX package'''
|
||||
if not has_misp_stix_converter:
|
||||
raise Exception('You need to install misp_stix_converter: pip install git+https://github.com/MISP/MISP-STIX-Converter.git')
|
||||
stix = convert.load_stix(stix)
|
||||
return buildEvent(stix, distribution=distribution,
|
||||
threat_level_id=threat_level_id, analysis=analysis)
|
||||
|
||||
|
||||
def make_stix_package(misp_event, to_json: bool = False, to_xml: bool = False):
|
||||
'''Returns a STIXPackage from a MISPEvent.
|
||||
|
||||
Optionally can return the package in json or xml.
|
||||
|
||||
'''
|
||||
if not has_misp_stix_converter:
|
||||
raise Exception('You need to install misp_stix_converter: pip install git+https://github.com/MISP/MISP-STIX-Converter.git')
|
||||
package = MISPtoSTIX(misp_event)
|
||||
if to_json:
|
||||
return package.to_json()
|
||||
elif to_xml:
|
||||
return package.to_xml()
|
||||
else:
|
||||
return package
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
|
@ -12,7 +13,7 @@ from ..abstract import resources_path
|
|||
static_repo = "https://github.com/MISP/misp-objects/archive/main.zip"
|
||||
|
||||
|
||||
def update_objects():
|
||||
def update_objects() -> None:
|
||||
r = requests.get(static_repo)
|
||||
|
||||
zipped_repo = BytesIO(r.content)
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
import logging
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
try:
|
||||
from pyfaup.faup import Faup # type: ignore
|
||||
|
@ -17,13 +20,13 @@ faup = Faup()
|
|||
|
||||
class URLObject(AbstractMISPObjectGenerator):
|
||||
|
||||
def __init__(self, url: str, generate_all=False, **kwargs):
|
||||
def __init__(self, url: str, generate_all=False, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('url', **kwargs)
|
||||
self._generate_all = True if generate_all is True else False
|
||||
faup.decode(unquote_plus(url))
|
||||
self.generate_attributes()
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
self.add_attribute('url', value=faup.url.decode())
|
||||
if faup.get_host():
|
||||
self.add_attribute('host', value=faup.get_host())
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .abstractgenerator import AbstractMISPObjectGenerator
|
||||
|
||||
# Original sourcecode: https://github.com/hayk57/MISP_registration_check
|
||||
|
@ -11,14 +15,16 @@ from .abstractgenerator import AbstractMISPObjectGenerator
|
|||
class VehicleObject(AbstractMISPObjectGenerator):
|
||||
'''Vehicle object generator out of regcheck.org.uk'''
|
||||
|
||||
country_urls = {
|
||||
country_urls: dict[str, str] = {
|
||||
'fr': "http://www.regcheck.org.uk/api/reg.asmx/CheckFrance",
|
||||
'es': "http://www.regcheck.org.uk/api/reg.asmx/CheckSpain",
|
||||
'uk': "http://www.regcheck.org.uk/api/reg.asmx/Check"
|
||||
}
|
||||
|
||||
def __init__(self, country: str, registration: str, username: str, **kwargs):
|
||||
def __init__(self, country: str, registration: str, username: str, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('vehicle', **kwargs)
|
||||
if country not in self.country_urls:
|
||||
raise ValueError(f"Country {country} not supportet, must be one of {self.country_urls.keys()}")
|
||||
self._country = country
|
||||
self._registration = registration
|
||||
self._username = username
|
||||
|
@ -26,10 +32,10 @@ class VehicleObject(AbstractMISPObjectGenerator):
|
|||
self.generate_attributes()
|
||||
|
||||
@property
|
||||
def report(self):
|
||||
def report(self) -> dict[str, Any]:
|
||||
return self._report
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
carDescription = self._report["Description"]
|
||||
carMake = self._report["CarMake"]["CurrentTextValue"]
|
||||
carModel = self._report["CarModel"]["CurrentTextValue"]
|
||||
|
@ -65,14 +71,14 @@ class VehicleObject(AbstractMISPObjectGenerator):
|
|||
self.add_attribute('date-first-registration', type='text', value=firstRegistration)
|
||||
self.add_attribute('image-url', type='text', value=ImageUrl)
|
||||
|
||||
def _query(self):
|
||||
payload = "RegistrationNumber={}&username={}".format(self._registration, self._username)
|
||||
def _query(self) -> dict[str, Any]:
|
||||
payload = f"RegistrationNumber={self._registration}&username={self._username}"
|
||||
headers = {
|
||||
'Content-Type': "application/x-www-form-urlencoded",
|
||||
'cache-control': "no-cache",
|
||||
}
|
||||
|
||||
response = requests.request("POST", self.country_urls.get(self._country), data=payload, headers=headers)
|
||||
response = requests.request("POST", self.country_urls[self._country], data=payload, headers=headers)
|
||||
# FIXME: Clean that up.
|
||||
for item in response.text.split("</vehicleJson>"):
|
||||
if "<vehicleJson>" in item:
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Optional
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
try:
|
||||
import validators # type: ignore
|
||||
import validators
|
||||
has_validators = True
|
||||
except ImportError:
|
||||
has_validators = False
|
||||
|
@ -24,7 +25,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
|
||||
:indicator: IOC to search VirusTotal for
|
||||
'''
|
||||
def __init__(self, apikey: str, indicator: str, vt_proxies: Optional[dict] = None, **kwargs):
|
||||
def __init__(self, apikey: str, indicator: str, vt_proxies: dict[str, str] | None = None, **kwargs) -> None: # type: ignore[no-untyped-def]
|
||||
super().__init__('virustotal-report', **kwargs)
|
||||
indicator = indicator.strip()
|
||||
self._resource_type = self.__validate_resource(indicator)
|
||||
|
@ -33,20 +34,20 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
self._report = self.__query_virustotal(apikey, indicator)
|
||||
self.generate_attributes()
|
||||
else:
|
||||
error_msg = "A valid indicator is required. (One of type url, md5, sha1, sha256). Received '{}' instead".format(indicator)
|
||||
error_msg = f"A valid indicator is required. (One of type url, md5, sha1, sha256). Received '{indicator}' instead"
|
||||
raise InvalidMISPObject(error_msg)
|
||||
|
||||
def get_report(self):
|
||||
def get_report(self) -> dict[str, Any]:
|
||||
return self._report
|
||||
|
||||
def generate_attributes(self):
|
||||
def generate_attributes(self) -> None:
|
||||
''' Parse the VirusTotal report for relevant attributes '''
|
||||
self.add_attribute("last-submission", value=self._report["scan_date"])
|
||||
self.add_attribute("permalink", value=self._report["permalink"])
|
||||
ratio = "{}/{}".format(self._report["positives"], self._report["total"])
|
||||
self.add_attribute("detection-ratio", value=ratio)
|
||||
|
||||
def __validate_resource(self, ioc: str):
|
||||
def __validate_resource(self, ioc: str) -> str | bool:
|
||||
'''
|
||||
Validate the data type of an indicator.
|
||||
Domains and IP addresses aren't supported because
|
||||
|
@ -62,7 +63,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
return "file"
|
||||
return False
|
||||
|
||||
def __query_virustotal(self, apikey: str, resource: str):
|
||||
def __query_virustotal(self, apikey: str, resource: str) -> dict[str, Any]:
|
||||
'''
|
||||
Query VirusTotal for information about an indicator
|
||||
|
||||
|
@ -70,7 +71,7 @@ class VTReportObject(AbstractMISPObjectGenerator):
|
|||
|
||||
:resource: Indicator to search in VirusTotal
|
||||
'''
|
||||
url = "https://www.virustotal.com/vtapi/v2/{}/report".format(self._resource_type)
|
||||
url = f"https://www.virustotal.com/vtapi/v2/{self._resource_type}/report"
|
||||
params = {"apikey": apikey, "resource": resource}
|
||||
# for now assume we're using a public API key - we'll figure out private keys later
|
||||
if self._proxies:
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
[tool.poetry]
|
||||
name = "pymisp"
|
||||
version = "2.4.166"
|
||||
version = "2.4.194"
|
||||
description = "Python API for MISP."
|
||||
authors = ["Raphaël Vinot <raphael.vinot@circl.lu>"]
|
||||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/MISP/PyMISP"
|
||||
documentation = "https://pymisp.readthedocs.io"
|
||||
|
||||
|
||||
readme = "README.md"
|
||||
|
||||
classifiers=[
|
||||
|
@ -18,16 +17,17 @@ classifiers=[
|
|||
'Intended Audience :: Science/Research',
|
||||
'Intended Audience :: Telecommunications Industry',
|
||||
'Intended Audience :: Information Technology',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Security',
|
||||
'Topic :: Internet'
|
||||
]
|
||||
|
||||
include = [
|
||||
"CHANGELOG.txt",
|
||||
"README.md",
|
||||
"pymisp/data/*.json",
|
||||
"pymisp/data/misp-objects/schema_objects.json",
|
||||
"pymisp/data/misp-objects/schema_relationships.json",
|
||||
|
@ -41,47 +41,51 @@ include = [
|
|||
"Source" = "https://github.com/MISP/PyMISP"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
requests = "^2.28.1"
|
||||
python-dateutil = "^2.8.2"
|
||||
jsonschema = "^4.17.1"
|
||||
deprecated = "^1.2.13"
|
||||
extract_msg = {version = "^0.37.1", optional = true}
|
||||
RTFDE = {version = "^0.0.2", optional = true}
|
||||
python = "^3.8"
|
||||
requests = "^2.32.3"
|
||||
python-dateutil = "^2.9.0.post0"
|
||||
deprecated = "^1.2.14"
|
||||
extract_msg = {version = "^0.48.5", optional = true}
|
||||
RTFDE = {version = "^0.1.1", optional = true}
|
||||
oletools = {version = "^0.60.1", optional = true}
|
||||
python-magic = {version = "^0.4.27", optional = true}
|
||||
pydeep2 = {version = "^0.5.1", optional = true}
|
||||
lief = {version = "^0.12.3", optional = true}
|
||||
beautifulsoup4 = {version = "^4.11.1", optional = true}
|
||||
validators = {version = "^0.20.0", optional = true}
|
||||
sphinx-autodoc-typehints = {version = "^1.19.5", optional = true}
|
||||
recommonmark = {version = "^0.7.1", optional = true}
|
||||
reportlab = {version = "^3.6.12", optional = true}
|
||||
lief = {version = "^0.14.1", optional = true}
|
||||
beautifulsoup4 = {version = "^4.12.3", optional = true}
|
||||
validators = {version = "^0.30.0", optional = true}
|
||||
sphinx-autodoc-typehints = {version = "^2.2.2", optional = true, python = ">=3.9"}
|
||||
docutils = {version = "^0.21.1", optional = true, python = ">=3.9"}
|
||||
recommonmark = {version = "^0.7.1", optional = true, python = ">=3.9"}
|
||||
reportlab = {version = "^4.2.2", optional = true}
|
||||
pyfaup = {version = "^1.2", optional = true}
|
||||
publicsuffixlist = {version = "^0.9.1", optional = true}
|
||||
chardet = {version = "^5.0.0", optional = true}
|
||||
urllib3 = {extras = ["brotli"], version = "^1.26.13", optional = true}
|
||||
publicsuffixlist = {version = "^1.0.1.20240702", optional = true}
|
||||
urllib3 = {extras = ["brotli"], version = "*", optional = true}
|
||||
Sphinx = {version = "^7.3.7", python = ">=3.9", optional = true}
|
||||
|
||||
[tool.poetry.extras]
|
||||
fileobjects = ['python-magic', 'pydeep2', 'lief']
|
||||
openioc = ['beautifulsoup4']
|
||||
virustotal = ['validators']
|
||||
docs = ['sphinx-autodoc-typehints', 'recommonmark']
|
||||
docs = ['sphinx-autodoc-typehints', 'recommonmark', 'sphinx', 'docutils']
|
||||
pdfexport = ['reportlab']
|
||||
url = ['pyfaup', 'chardet']
|
||||
url = ['pyfaup']
|
||||
email = ['extract_msg', "RTFDE", "oletools"]
|
||||
brotli = ['urllib3']
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
requests-mock = "^1.10.0"
|
||||
mypy = "^0.991"
|
||||
ipython = "^7.34.0"
|
||||
jupyterlab = "^3.5.0"
|
||||
types-requests = "^2.28.11.5"
|
||||
types-python-dateutil = "^2.8.19.4"
|
||||
types-redis = "^4.3.21.6"
|
||||
requests-mock = "^1.12.1"
|
||||
mypy = "^1.10.1"
|
||||
ipython = [
|
||||
{version = "<8.13.0", python = "<3.9"},
|
||||
{version = "^8.18.0", python = ">=3.9"},
|
||||
{version = "^8.19.0", python = ">=3.10"}
|
||||
]
|
||||
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"
|
||||
pytest-cov = "^4.0.0"
|
||||
pytest-cov = "^5.0.0"
|
||||
|
||||
[build-system]
|
||||
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
|
||||
34187a34d0a3c5d63016c26346371b54, ce8209ff9828aa8cb095bd7d1589fc4d394c298c, 5f815b8a8e77731c9ca2b3a07a27f880ef24d54e458d77bdabbbaf2269fe96c3
|
||||
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
|
||||
|
||||
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
|
||||
--2NqJR3m2cLnhEraiqXA4Q9hqnmihx7b7
|
Binary file not shown.
|
@ -4590,7 +4590,7 @@
|
|||
"org_id": "2",
|
||||
"orgc_id": "2",
|
||||
"proposal_email_lock": false,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"published": false,
|
||||
"sharing_group_id": "0",
|
||||
"threat_level_id": "3",
|
||||
|
|
|
@ -4593,7 +4593,7 @@
|
|||
"org_id": "2",
|
||||
"orgc_id": "2",
|
||||
"proposal_email_lock": false,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"published": false,
|
||||
"sharing_group_id": "0",
|
||||
"threat_level_id": "3",
|
||||
|
|
|
@ -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",
|
||||
"proposal_email_lock": false,
|
||||
"locked": false,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"sharing_group_id": "0",
|
||||
"disable_correlation": false,
|
||||
"event_creator_email": "raphael.vinot@circl.lu",
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
"org_id": "1",
|
||||
"orgc_id": "1",
|
||||
"proposal_email_lock": true,
|
||||
"publish_timestamp": 0,
|
||||
"publish_timestamp": "0",
|
||||
"published": false,
|
||||
"sharing_group_id": "0",
|
||||
"threat_level_id": "1",
|
||||
|
|
|
@ -1,17 +1,33 @@
|
|||
from email.message import EmailMessage
|
||||
from __future__ import annotations
|
||||
|
||||
# import json
|
||||
import unittest
|
||||
|
||||
from email.message import EmailMessage
|
||||
from io import BytesIO
|
||||
from typing import List
|
||||
from pymisp.tools import EMailObject
|
||||
from pathlib import Path
|
||||
from os import urandom
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import TypeVar, Type
|
||||
from zipfile import ZipFile
|
||||
|
||||
from pymisp.tools import EMailObject
|
||||
from pymisp.exceptions import PyMISPNotImplementedYet, InvalidMISPObject
|
||||
|
||||
T = TypeVar('T', bound='TestEmailObject')
|
||||
|
||||
|
||||
class TestEmailObject(unittest.TestCase):
|
||||
def test_mail_1(self):
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_1.eml"))
|
||||
|
||||
eml_1: BytesIO
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls: type[T]) -> None:
|
||||
with ZipFile(Path("tests/email_testfiles/mail_1.eml.zip"), 'r') as myzip:
|
||||
with myzip.open('mail_1.eml', pwd=b'AVs are dumb') as myfile:
|
||||
cls.eml_1 = BytesIO(myfile.read())
|
||||
|
||||
def test_mail_1(self) -> None:
|
||||
email_object = EMailObject(pseudofile=self.eml_1)
|
||||
self.assertEqual(self._get_values(email_object, "subject")[0], "письмо уведом-е")
|
||||
self.assertEqual(self._get_values(email_object, "to")[0], "kinney@noth.com")
|
||||
self.assertEqual(self._get_values(email_object, "from")[0], "suvorov.s@nalg.ru")
|
||||
|
@ -27,7 +43,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertIsInstance(file_name, str)
|
||||
self.assertIsInstance(file_content, BytesIO)
|
||||
|
||||
def test_mail_1_headers_only(self):
|
||||
def test_mail_1_headers_only(self) -> None:
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_1_headers_only.eml"))
|
||||
self.assertEqual(self._get_values(email_object, "subject")[0], "письмо уведом-е")
|
||||
self.assertEqual(self._get_values(email_object, "to")[0], "kinney@noth.com")
|
||||
|
@ -38,7 +54,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertIsInstance(email_object.email, EmailMessage)
|
||||
self.assertEqual(len(email_object.attachments), 0)
|
||||
|
||||
def test_mail_multiple_to(self):
|
||||
def test_mail_multiple_to(self) -> None:
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_multiple_to.eml"))
|
||||
|
||||
to = self._get_values(email_object, "to")
|
||||
|
@ -48,9 +64,9 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertEqual(to[1], "jan.marek@example.com")
|
||||
self.assertEqual(to_display_name[1], "Marek, Jan")
|
||||
|
||||
def test_msg(self):
|
||||
def test_msg(self) -> None:
|
||||
# Test result of eml converted to msg is the same
|
||||
eml_email_object = EMailObject(Path("tests/email_testfiles/mail_1.eml"))
|
||||
eml_email_object = EMailObject(pseudofile=self.eml_1)
|
||||
email_object = EMailObject(Path("tests/email_testfiles/mail_1.msg"))
|
||||
|
||||
self.assertIsInstance(email_object.email, EmailMessage)
|
||||
|
@ -71,11 +87,10 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertEqual(self._get_values(email_object, "received-header-ip"),
|
||||
self._get_values(eml_email_object, "received-header-ip"))
|
||||
|
||||
|
||||
def test_bom_encoded(self):
|
||||
def test_bom_encoded(self) -> None:
|
||||
"""Test utf-8-sig encoded email"""
|
||||
bom_email_object = EMailObject(Path("tests/email_testfiles/mail_1_bom.eml"))
|
||||
eml_email_object = EMailObject(Path("tests/email_testfiles/mail_1.eml"))
|
||||
eml_email_object = EMailObject(pseudofile=self.eml_1)
|
||||
|
||||
self.assertIsInstance(bom_email_object.email, EmailMessage)
|
||||
for file_name, file_content in bom_email_object.attachments:
|
||||
|
@ -95,7 +110,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self.assertEqual(self._get_values(bom_email_object, "received-header-ip"),
|
||||
self._get_values(eml_email_object, "received-header-ip"))
|
||||
|
||||
def test_handling_of_various_email_types(self):
|
||||
def test_handling_of_various_email_types(self) -> None:
|
||||
self._does_not_fail(Path("tests/email_testfiles/mail_2.eml"),
|
||||
"ensuring all headers work")
|
||||
self._does_not_fail(Path('tests/email_testfiles/mail_3.eml'),
|
||||
|
@ -107,7 +122,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
self._does_not_fail(Path('tests/email_testfiles/mail_5.msg'),
|
||||
"Check encapsulated HTML works")
|
||||
|
||||
def _does_not_fail(self, path, test_type="test"):
|
||||
def _does_not_fail(self, path: Path, test_type: str="test") -> None:
|
||||
found_error = None
|
||||
try:
|
||||
EMailObject(path)
|
||||
|
@ -119,7 +134,7 @@ class TestEmailObject(unittest.TestCase):
|
|||
path,
|
||||
test_type))
|
||||
|
||||
def test_random_binary_blob(self):
|
||||
def test_random_binary_blob(self) -> None:
|
||||
"""Email parser fails correctly on random binary blob."""
|
||||
random_data = urandom(1024)
|
||||
random_blob = BytesIO(random_data)
|
||||
|
@ -134,10 +149,9 @@ class TestEmailObject(unittest.TestCase):
|
|||
broken_obj = EMailObject(pseudofile=random_blob)
|
||||
except Exception as _e:
|
||||
found_error = _e
|
||||
if not isinstance(found_error, PyMISPNotImplementedYet):
|
||||
self.fail("Expected PyMISPNotImplementedYet when EmailObject receives completely unknown binary input data in a pseudofile. But, did not get that exception.")
|
||||
|
||||
if not isinstance(found_error, InvalidMISPObject):
|
||||
self.fail("Expected InvalidMISPObject when EmailObject receives completely unknown binary input data in a pseudofile. But, did not get that exception.")
|
||||
|
||||
@staticmethod
|
||||
def _get_values(obj: EMailObject, relation: str) -> List[str]:
|
||||
def _get_values(obj: EMailObject, relation: str) -> list[str]:
|
||||
return [attr.value for attr in obj.attributes if attr['object_relation'] == relation]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
import json
|
||||
|
@ -8,7 +9,7 @@ import pathlib
|
|||
|
||||
|
||||
class TestFileObject(unittest.TestCase):
|
||||
def test_mimeType(self):
|
||||
def test_mimeType(self) -> None:
|
||||
file_object = FileObject(filepath=pathlib.Path(__file__))
|
||||
attributes = json.loads(file_object.to_json())['Attribute']
|
||||
mime = next(attr for attr in attributes if attr['object_relation'] == 'mimetype')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
import json
|
||||
|
@ -8,86 +9,110 @@ import glob
|
|||
import hashlib
|
||||
from datetime import date, datetime
|
||||
|
||||
from pymisp import (MISPEvent, MISPSighting, MISPTag, MISPOrganisation,
|
||||
MISPObject)
|
||||
from pymisp import (MISPAttribute, MISPEvent, MISPGalaxy, MISPObject, MISPOrganisation,
|
||||
MISPSighting, MISPTag)
|
||||
from pymisp.exceptions import InvalidMISPObject
|
||||
from pymisp.tools import GitVulnFinderObject
|
||||
|
||||
|
||||
class TestMISPEvent(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self) -> None:
|
||||
self.maxDiff = None
|
||||
self.mispevent = MISPEvent()
|
||||
|
||||
def init_event(self):
|
||||
def init_event(self) -> None:
|
||||
self.mispevent.info = 'This is a test'
|
||||
self.mispevent.distribution = 1
|
||||
self.mispevent.threat_level_id = 1
|
||||
self.mispevent.analysis = 1
|
||||
self.mispevent.set_date("2017-12-31") # test the set date method
|
||||
|
||||
def test_simple(self):
|
||||
with open('tests/mispevent_testfiles/simple.json', 'r') as f:
|
||||
def test_simple(self) -> None:
|
||||
with open('tests/mispevent_testfiles/simple.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_event(self):
|
||||
def test_event(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.publish()
|
||||
with open('tests/mispevent_testfiles/event.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_loadfile(self):
|
||||
def test_loadfile(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/event.json')
|
||||
with open('tests/mispevent_testfiles/event.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_loadfile_validate(self):
|
||||
def test_loadfile_validate(self) -> None:
|
||||
misp_event = MISPEvent()
|
||||
misp_event.load_file('tests/mispevent_testfiles/event.json', validate=True)
|
||||
|
||||
def test_loadfile_validate_strict(self):
|
||||
def test_loadfile_validate_strict(self) -> None:
|
||||
misp_event = MISPEvent(strict_validation=True)
|
||||
misp_event.load_file('tests/mispevent_testfiles/event.json', validate=True)
|
||||
|
||||
def test_event_tag(self):
|
||||
def test_event_tag(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.add_tag('bar')
|
||||
self.mispevent.add_tag(name='baz')
|
||||
new_tag = MISPTag()
|
||||
new_tag.from_dict(name='foo')
|
||||
self.mispevent.add_tag(new_tag)
|
||||
with open('tests/mispevent_testfiles/event_tags.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_tags.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_attribute(self):
|
||||
def test_event_galaxy(self) -> None:
|
||||
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
|
||||
a = self.mispevent.add_attribute_tag('osint', 'bar.exe')
|
||||
a = self.mispevent.add_attribute_tag('osint', 'bar.exe') # type: ignore[assignment]
|
||||
attr_tags = self.mispevent.get_attribute_tag('bar.exe')
|
||||
self.assertEqual(self.mispevent.attributes[0].tags[0].name, 'osint')
|
||||
self.assertEqual(attr_tags[0].name, 'osint')
|
||||
with open('tests/mispevent_testfiles/attribute.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/attribute.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
# Fake setting an attribute ID for testing
|
||||
self.mispevent.attributes[0].id = 42
|
||||
self.mispevent.delete_attribute(42)
|
||||
with open('tests/mispevent_testfiles/attribute_del.json', 'r') as f:
|
||||
self.mispevent.delete_attribute('42')
|
||||
with open('tests/mispevent_testfiles/attribute_del.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_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()
|
||||
av_signature_object = MISPObject("av-signature")
|
||||
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))
|
||||
|
||||
def test_object_tag(self):
|
||||
def test_object_tag(self) -> None:
|
||||
self.mispevent.add_object(name='file', strict=True)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('filename', value='') # type: ignore[assignment]
|
||||
self.assertEqual(a, None)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value=None)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value=None) # type: ignore[assignment]
|
||||
self.assertEqual(a, None)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}])
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}]) # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.assertEqual(self.mispevent.objects[0].attributes[0].tags[0].name, 'blah')
|
||||
self.assertTrue(self.mispevent.objects[0].has_attributes_by_relation(['filename']))
|
||||
self.assertEqual(len(self.mispevent.objects[0].get_attributes_by_relation('filename')), 1)
|
||||
self.mispevent.add_object(name='url', strict=True)
|
||||
a = self.mispevent.objects[1].add_attribute('url', value='https://www.circl.lu')
|
||||
a = self.mispevent.objects[1].add_attribute('url', value='https://www.circl.lu') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
self.mispevent.objects[1].uuid = 'b'
|
||||
reference = self.mispevent.objects[0].add_reference(self.mispevent.objects[1], 'baz', comment='foo')
|
||||
del reference.uuid
|
||||
self.assertEqual(self.mispevent.objects[0].references[0].relationship_type, 'baz')
|
||||
with open('tests/mispevent_testfiles/event_obj_attr_tag.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_obj_attr_tag.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
@unittest.skip("Not supported on MISP: https://github.com/MISP/MISP/issues/2638 - https://github.com/MISP/PyMISP/issues/168")
|
||||
def test_object_level_tag(self):
|
||||
def test_object_level_tag(self) -> None:
|
||||
self.mispevent.add_object(name='file', strict=True)
|
||||
self.mispevent.objects[0].add_attribute('filename', value='bar')
|
||||
self.mispevent.objects[0].add_tag('osint')
|
||||
self.mispevent.objects[0].add_tag('osint') # type: ignore[attr-defined]
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/event_obj_tag.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_obj_tag.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_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:
|
||||
pseudofile = BytesIO(f.read())
|
||||
self.init_event()
|
||||
a = self.mispevent.add_attribute('malware-sample', 'bar.exe', data=pseudofile)
|
||||
a: MISPAttribute = self.mispevent.add_attribute('malware-sample', 'bar.exe', data=pseudofile) # type: ignore[assignment]
|
||||
del a.uuid
|
||||
attribute = self.mispevent.attributes[0]
|
||||
self.assertEqual(attribute.malware_binary, pseudofile)
|
||||
with open('tests/mispevent_testfiles/malware.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/malware.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_existing_malware(self):
|
||||
def test_existing_malware(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/malware_exist.json')
|
||||
with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
|
||||
pseudofile = BytesIO(f.read())
|
||||
self.assertEqual(
|
||||
self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary.read(),
|
||||
pseudofile.read())
|
||||
self.assertTrue(self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary)
|
||||
if _mb := self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary:
|
||||
self.assertEqual(_mb.read(), pseudofile.read())
|
||||
|
||||
def test_sighting(self):
|
||||
def test_sighting(self) -> None:
|
||||
sighting = MISPSighting()
|
||||
sighting.from_dict(value='1', type='bar', timestamp=11111111)
|
||||
with open('tests/mispevent_testfiles/sighting.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/sighting.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(sighting.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_existing_event(self):
|
||||
def test_existing_event(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
with open('tests/mispevent_testfiles/existing_event.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/existing_event.json') as f:
|
||||
ref_json = json.load(f)
|
||||
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_shadow_attributes_existing(self):
|
||||
def test_shadow_attributes_existing(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/shadow.json')
|
||||
with open('tests/mispevent_testfiles/shadow.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/shadow.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
@unittest.skip("Not supported on MISP.")
|
||||
def test_shadow_attributes(self):
|
||||
def test_shadow_attributes(self) -> None:
|
||||
self.init_event()
|
||||
p = self.mispevent.add_proposal(type='filename', value='baz.jpg')
|
||||
del p.uuid
|
||||
a = self.mispevent.add_attribute('filename', 'bar.exe')
|
||||
a: MISPAttribute = self.mispevent.add_attribute('filename', 'bar.exe') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
p = self.mispevent.attributes[0].add_proposal(type='filename', value='bar.pdf')
|
||||
del p.uuid
|
||||
with open('tests/mispevent_testfiles/proposals.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/proposals.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_default_attributes(self):
|
||||
def test_default_attributes(self) -> None:
|
||||
self.mispevent.add_object(name='file', strict=True)
|
||||
a = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}])
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}]) # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('pattern-in-file', value='baz')
|
||||
a = self.mispevent.objects[0].add_attribute('pattern-in-file', value='baz') # type: ignore[assignment]
|
||||
self.assertEqual(a.category, 'Artifacts dropped')
|
||||
del a.uuid
|
||||
self.mispevent.add_object(name='file', strict=False, default_attributes_parameters=self.mispevent.objects[0].attributes[0])
|
||||
a = self.mispevent.objects[1].add_attribute('filename', value='baz')
|
||||
a = self.mispevent.objects[1].add_attribute('filename', value='baz') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
self.mispevent.objects[1].uuid = 'b'
|
||||
with open('tests/mispevent_testfiles/event_obj_def_param.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/event_obj_def_param.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_obj_default_values(self):
|
||||
def test_obj_default_values(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.add_object(name='whois', strict=True)
|
||||
a = self.mispevent.objects[0].add_attribute('registrar', value='registar.example.com')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('registrar', value='registar.example.com') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('domain', value='domain.example.com')
|
||||
a = self.mispevent.objects[0].add_attribute('domain', value='domain.example.com') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns1.example.com')
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns1.example.com') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns2.example.com', disable_correlation=False, to_ids=True, category='External analysis')
|
||||
a = self.mispevent.objects[0].add_attribute('nameserver', value='ns2.example.com', disable_correlation=False, to_ids=True, category='External analysis') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/def_param.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/def_param.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_obj_references_export(self):
|
||||
def test_obj_references_export(self) -> None:
|
||||
self.init_event()
|
||||
obj1 = MISPObject(name="file")
|
||||
obj2 = MISPObject(name="url", standalone=False)
|
||||
|
@ -231,29 +272,29 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertTrue("ObjectReference" in obj1.jsonable())
|
||||
self.assertFalse("ObjectReference" in obj2.jsonable())
|
||||
|
||||
def test_event_not_edited(self):
|
||||
def test_event_not_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
|
||||
def test_event_edited(self):
|
||||
def test_event_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.mispevent.info = 'blah'
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_tag_edited(self):
|
||||
def test_event_tag_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.add_tag('foo')
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_attribute_edited(self):
|
||||
def test_event_attribute_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.mispevent.attributes[0].value = 'blah'
|
||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||
self.assertFalse(self.mispevent.attributes[1].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_attribute_tag_edited(self):
|
||||
def test_event_attribute_tag_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.attributes[0].tags[0].name = 'blah'
|
||||
|
@ -262,14 +303,14 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_attribute_tag_edited_second(self):
|
||||
def test_event_attribute_tag_edited_second(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.attributes[0].add_tag(name='blah')
|
||||
self.assertTrue(self.mispevent.attributes[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_object_edited(self):
|
||||
def test_event_object_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.objects[0].comment = 'blah'
|
||||
|
@ -277,7 +318,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertFalse(self.mispevent.objects[1].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_object_attribute_edited(self):
|
||||
def test_event_object_attribute_edited(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.objects[0].attributes[0].comment = 'blah'
|
||||
|
@ -285,23 +326,23 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertTrue(self.mispevent.objects[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
|
||||
def test_event_object_attribute_edited_tag(self):
|
||||
def test_event_object_attribute_edited_tag(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
self.assertFalse(self.mispevent.edited)
|
||||
self.mispevent.objects[0].attributes[0].add_tag('blah')
|
||||
self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
|
||||
self.assertTrue(self.mispevent.objects[0].edited)
|
||||
self.assertTrue(self.mispevent.edited)
|
||||
with open('tests/mispevent_testfiles/existing_event_edited.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/existing_event_edited.json') as f:
|
||||
ref_json = json.load(f)
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_obj_by_id(self):
|
||||
def test_obj_by_id(self) -> None:
|
||||
self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
|
||||
misp_obj = self.mispevent.get_object_by_id(1556)
|
||||
self.assertEqual(misp_obj.uuid, '5a3cd604-e11c-4de5-bbbf-c170950d210f')
|
||||
|
||||
def test_userdefined_object_custom_template(self):
|
||||
def test_userdefined_object_custom_template(self) -> None:
|
||||
self.init_event()
|
||||
with open('tests/mispevent_testfiles/test_object_template/definition.json') as f:
|
||||
template = json.load(f)
|
||||
|
@ -312,16 +353,16 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, '{\'member3\'} are required.')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member3', value='foo')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('member3', value='foo') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# Fail on requiredOneOf
|
||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, 'At least one of the following attributes is required: member1, member2')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# member1 is not a multiple
|
||||
|
@ -330,12 +371,12 @@ class TestMISPEvent(unittest.TestCase):
|
|||
|
||||
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_userdefined_object_custom_dir(self):
|
||||
def test_userdefined_object_custom_dir(self) -> None:
|
||||
self.init_event()
|
||||
self.mispevent.add_object(name='test_object_template', strict=True, misp_objects_path_custom='tests/mispevent_testfiles')
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
|
@ -343,16 +384,16 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, '{\'member3\'} are required.')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member3', value='foo')
|
||||
a: MISPAttribute = self.mispevent.objects[0].add_attribute('member3', value='foo') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# Fail on requiredOneOf
|
||||
self.mispevent.to_json(sort_keys=True, indent=2)
|
||||
self.assertEqual(e.exception.message, 'At least one of the following attributes is required: member1, member2')
|
||||
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='bar') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz')
|
||||
a = self.mispevent.objects[0].add_attribute('member1', value='baz') # type: ignore[assignment]
|
||||
del a.uuid
|
||||
with self.assertRaises(InvalidMISPObject) as e:
|
||||
# member1 is not a multiple
|
||||
|
@ -361,15 +402,15 @@ class TestMISPEvent(unittest.TestCase):
|
|||
|
||||
self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
|
||||
self.mispevent.objects[0].uuid = 'a'
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json', 'r') as f:
|
||||
with open('tests/mispevent_testfiles/misp_custom_obj.json') as f:
|
||||
ref_json = json.load(f)
|
||||
del self.mispevent.uuid
|
||||
self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2), json.dumps(ref_json, sort_keys=True, indent=2))
|
||||
|
||||
def test_first_last_seen(self):
|
||||
def test_first_last_seen(self) -> None:
|
||||
me = MISPEvent()
|
||||
me.info = 'Test First and Last Seen'
|
||||
me.date = '2020.01.12'
|
||||
me.date = '2020.01.12' # type: ignore[assignment]
|
||||
self.assertEqual(me.date.day, 12)
|
||||
me.add_attribute('ip-dst', '8.8.8.8', first_seen='06-21-1998', last_seen=1580213607.469571)
|
||||
self.assertEqual(me.attributes[0].first_seen.year, 1998)
|
||||
|
@ -377,11 +418,11 @@ class TestMISPEvent(unittest.TestCase):
|
|||
now = datetime.now().astimezone()
|
||||
me.attributes[0].last_seen = now
|
||||
today = date.today()
|
||||
me.attributes[0].first_seen = today
|
||||
me.attributes[0].first_seen = today # type: ignore[assignment]
|
||||
self.assertEqual(me.attributes[0].first_seen.year, today.year)
|
||||
self.assertEqual(me.attributes[0].last_seen, now)
|
||||
|
||||
def test_feed(self):
|
||||
def test_feed(self) -> None:
|
||||
me = MISPEvent()
|
||||
me.info = 'Test feed'
|
||||
org = MISPOrganisation()
|
||||
|
@ -399,7 +440,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
self.assertEqual(feed['Event']['_manifest'][me.uuid]['info'], 'Test feed')
|
||||
self.assertEqual(len(feed['Event']['Object'][0]['Attribute']), 2)
|
||||
|
||||
def test_object_templates(self):
|
||||
def test_object_templates(self) -> None:
|
||||
me = MISPEvent()
|
||||
for template in glob.glob(str(me.misp_objects_path / '*' / 'definition.json')):
|
||||
with open(template) as f:
|
||||
|
@ -418,7 +459,7 @@ class TestMISPEvent(unittest.TestCase):
|
|||
subset = set(entry['categories']).issubset(me.describe_types['categories'])
|
||||
self.assertTrue(subset, f'{t_json["name"]} - {obj_relation}')
|
||||
|
||||
def test_git_vuln_finder(self):
|
||||
def test_git_vuln_finder(self) -> None:
|
||||
with open('tests/git-vuln-finder-quagga.json') as f:
|
||||
dump = json.load(f)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import sys
|
||||
import unittest
|
||||
import subprocess
|
||||
|
||||
import urllib3 # type: ignore
|
||||
import urllib3
|
||||
import logging
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
try:
|
||||
from pymisp import ExpandedPyMISP, MISPOrganisation, MISPUser, MISPEvent, MISPObject, MISPSharingGroup, Distribution
|
||||
from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPEvent, MISPObject, MISPSharingGroup, Distribution
|
||||
except ImportError:
|
||||
if sys.version_info < (3, 6):
|
||||
print('This test suite requires Python 3.6+, breaking.')
|
||||
sys.exit(0)
|
||||
else:
|
||||
raise
|
||||
raise
|
||||
|
||||
key = 'eYQdGTEWZJ8C2lm9EpnMqxQGwGiPNyoR75JvLdlE'
|
||||
verifycert = False
|
||||
|
@ -73,7 +69,7 @@ fast_mode = True
|
|||
class MISPInstance():
|
||||
|
||||
def __init__(self, params):
|
||||
self.initial_user_connector = ExpandedPyMISP(params['url'], params['key'], ssl=False, debug=False)
|
||||
self.initial_user_connector = PyMISP(params['url'], params['key'], ssl=False, debug=False)
|
||||
# Git pull
|
||||
self.initial_user_connector.update_misp()
|
||||
# Set the default role (id 3 on the VM is normal user)
|
||||
|
@ -101,7 +97,7 @@ class MISPInstance():
|
|||
user.org_id = self.test_org.id
|
||||
user.role_id = 1 # Site admin
|
||||
self.test_site_admin = self.initial_user_connector.add_user(user)
|
||||
self.site_admin_connector = ExpandedPyMISP(params['url'], self.test_site_admin.authkey, ssl=False, debug=False)
|
||||
self.site_admin_connector = PyMISP(params['url'], self.test_site_admin.authkey, ssl=False, debug=False)
|
||||
self.site_admin_connector.toggle_global_pythonify()
|
||||
# Create org admin
|
||||
user = MISPUser()
|
||||
|
@ -109,14 +105,14 @@ class MISPInstance():
|
|||
user.org_id = self.test_org.id
|
||||
user.role_id = 2 # Org admin
|
||||
self.test_org_admin = self.site_admin_connector.add_user(user)
|
||||
self.org_admin_connector = ExpandedPyMISP(params['url'], self.test_org_admin.authkey, ssl=False, debug=False)
|
||||
self.org_admin_connector = PyMISP(params['url'], self.test_org_admin.authkey, ssl=False, debug=False)
|
||||
self.org_admin_connector.toggle_global_pythonify()
|
||||
# Create user
|
||||
user = MISPUser()
|
||||
user.email = params['email_user']
|
||||
user.org_id = self.test_org.id
|
||||
self.test_usr = self.org_admin_connector.add_user(user)
|
||||
self.user_connector = ExpandedPyMISP(params['url'], self.test_usr.authkey, ssl=False, debug=False)
|
||||
self.user_connector = PyMISP(params['url'], self.test_usr.authkey, ssl=False, debug=False)
|
||||
self.user_connector.toggle_global_pythonify()
|
||||
|
||||
# Setup external_baseurl
|
||||
|
@ -141,7 +137,7 @@ class MISPInstance():
|
|||
user.org_id = sync_org.id
|
||||
user.role_id = 5 # Org admin
|
||||
sync_user = self.site_admin_connector.add_user(user)
|
||||
sync_user_connector = ExpandedPyMISP(self.site_admin_connector.root_url, sync_user.authkey, ssl=False, debug=False)
|
||||
sync_user_connector = PyMISP(self.site_admin_connector.root_url, sync_user.authkey, ssl=False, debug=False)
|
||||
sync_server_config = sync_user_connector.get_sync_config(pythonify=True)
|
||||
self.sync.append((sync_org, sync_user, sync_server_config))
|
||||
|
||||
|
|
Loading…
Reference in New Issue