psengine 2.5.1__tar.gz → 2.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {psengine-2.5.1 → psengine-2.6.0}/PKG-INFO +23 -40
- {psengine-2.5.1 → psengine-2.6.0}/README.md +10 -2
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/helpers.py +5 -7
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/models.py +64 -60
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/note.py +16 -16
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/note_mgr.py +25 -25
- {psengine-2.5.1 → psengine-2.6.0}/psengine/asi/asi.py +20 -21
- {psengine-2.5.1 → psengine-2.6.0}/psengine/asi/asi_mgr.py +133 -137
- {psengine-2.5.1 → psengine-2.6.0}/psengine/asi/client.py +15 -15
- psengine-2.6.0/psengine/asi/models.py +341 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/base_http_client.py +10 -9
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/classic_alert.py +11 -11
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/classic_alert_mgr.py +35 -32
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/helpers.py +3 -3
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/models.py +27 -28
- {psengine-2.5.1 → psengine-2.6.0}/psengine/collective_insights/collective_insights.py +13 -13
- {psengine-2.5.1 → psengine-2.6.0}/psengine/collective_insights/insight.py +6 -8
- {psengine-2.5.1 → psengine-2.6.0}/psengine/collective_insights/models.py +6 -7
- {psengine-2.5.1 → psengine-2.6.0}/psengine/common_models.py +4 -4
- {psengine-2.5.1 → psengine-2.6.0}/psengine/config/config.py +22 -21
- {psengine-2.5.1 → psengine-2.6.0}/psengine/detection/detection_mgr.py +13 -15
- {psengine-2.5.1 → psengine-2.6.0}/psengine/detection/detection_rule.py +4 -5
- {psengine-2.5.1 → psengine-2.6.0}/psengine/detection/helpers.py +2 -2
- {psengine-2.5.1 → psengine-2.6.0}/psengine/detection/models.py +12 -12
- {psengine-2.5.1 → psengine-2.6.0}/psengine/endpoints.py +13 -4
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/lookup.py +59 -60
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/lookup_mgr.py +4 -6
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/models/base_enriched_entity.py +9 -10
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/models/lookup.py +81 -86
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/models/soar.py +7 -8
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/soar.py +7 -8
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/soar_mgr.py +9 -11
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_lists/entity_list.py +12 -12
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_lists/entity_list_mgr.py +6 -8
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_lists/models.py +8 -9
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_match/entity_match.py +2 -3
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_match/entity_match_mgr.py +11 -15
- {psengine-2.5.1 → psengine-2.6.0}/psengine/fusion/fusion_mgr.py +4 -4
- {psengine-2.5.1 → psengine-2.6.0}/psengine/fusion/models.py +11 -12
- {psengine-2.5.1 → psengine-2.6.0}/psengine/helpers/helpers.py +49 -27
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/constants.py +1 -1
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/identity.py +28 -28
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/identity_mgr.py +111 -121
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/models/common_models.py +44 -44
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/models/detections.py +18 -18
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/models/incident_report.py +5 -6
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/models/lookup.py +7 -8
- {psengine-2.5.1 → psengine-2.6.0}/psengine/logger/rf_logger.py +2 -2
- {psengine-2.5.1 → psengine-2.6.0}/psengine/malware_intel/__init__.py +34 -2
- psengine-2.6.0/psengine/malware_intel/auto_sigma_mgr.py +242 -0
- psengine-2.6.0/psengine/malware_intel/auto_yara_mgr.py +226 -0
- psengine-2.6.0/psengine/malware_intel/constants.py +16 -0
- psengine-2.6.0/psengine/malware_intel/errors.py +66 -0
- psengine-2.6.0/psengine/malware_intel/helpers.py +85 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/malware_intel/malware_intel.py +146 -6
- {psengine-2.5.1 → psengine-2.6.0}/psengine/malware_intel/malware_intel_mgr.py +2 -2
- psengine-2.6.0/psengine/malware_intel/models.py +357 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/markdown/markdown.py +3 -5
- {psengine-2.5.1 → psengine-2.6.0}/psengine/markdown/models.py +1 -3
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/constants.py +16 -14
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/helpers.py +11 -12
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_cyber_vulnerability.py +3 -3
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_domain_abuse.py +1 -1
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_third_party_risk.py +5 -5
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/common_models.py +5 -6
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/panel_log.py +74 -75
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/panel_status.py +18 -19
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/pba_code_repo_leak.py +10 -11
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/pba_cyber_vulnerability.py +10 -11
- psengine-2.6.0/psengine/playbook_alerts/models/pba_domain_abuse.py +138 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/pba_geopolitics_facility.py +4 -5
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/pba_identity_exposures.py +33 -34
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/pba_malware_report.py +7 -8
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/pba_third_party_risk.py +16 -18
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/search_endpoint.py +6 -7
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/playbook_alert_mgr.py +80 -82
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/playbook_alerts.py +60 -51
- {psengine-2.5.1 → psengine-2.6.0}/psengine/rf_client.py +13 -15
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risk_history/models.py +15 -15
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risk_history/risk_history_mgr.py +4 -4
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/complex_entity.py +2 -2
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/helpers.py +2 -2
- {psengine-2.5.1 → psengine-2.6.0}/pyproject.toml +70 -41
- psengine-2.5.1/LICENSE +0 -21
- psengine-2.5.1/psengine/asi/models.py +0 -345
- psengine-2.5.1/psengine/malware_intel/errors.py +0 -18
- psengine-2.5.1/psengine/malware_intel/models.py +0 -275
- psengine-2.5.1/psengine/playbook_alerts/models/pba_domain_abuse.py +0 -139
- psengine-2.5.1/psengine.egg-info/PKG-INFO +0 -110
- psengine-2.5.1/psengine.egg-info/SOURCES.txt +0 -154
- psengine-2.5.1/psengine.egg-info/dependency_links.txt +0 -1
- psengine-2.5.1/psengine.egg-info/requires.txt +0 -35
- psengine-2.5.1/psengine.egg-info/top_level.txt +0 -1
- psengine-2.5.1/setup.cfg +0 -4
- {psengine-2.5.1 → psengine-2.6.0}/psengine/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/_sdk_id.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/analyst_notes/markdown.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/asi/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/asi/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/asi/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/markdown/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/classic_alerts/markdown/markdown.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/collective_insights/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/collective_insights/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/collective_insights/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/config/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/config/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/detection/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/detection/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/enrich/models/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_lists/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_lists/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_lists/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_match/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_match/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/entity_match/models.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/fusion/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/fusion/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/helpers/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/identity/models/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/logger/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/logger/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/logger/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/markdown/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/mappings.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_code_repo.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_geopolitics_facility.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_identity_exposure.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/markdown/markdown_malware_report.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/models/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/playbook_alerts/pa_category.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/py.typed +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risk_history/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risk_history/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risklists/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risklists/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risklists/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risklists/models.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/risklists/risklist_mgr.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/__init__.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/base_stix_entity.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/constants.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/enriched_indicator.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/errors.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/rf_bundle.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/simple_entity.py +0 -0
- {psengine-2.5.1 → psengine-2.6.0}/psengine/stix2/util.py +0 -0
|
@@ -1,59 +1,34 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: psengine
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: psengine is a simple, yet elegant, library for rapid development of integrations with Recorded Future.
|
|
5
|
+
Keywords: API,Recorded Future,Cyber Security Engineering,Threat Intelligence
|
|
6
|
+
Author: Moise Medici, Patrick Kinsella, Ernest Bartosevic
|
|
5
7
|
Author-email: Moise Medici <moise.medici@recordedfuture.com>, Patrick Kinsella <patrick.kinsella@recordedfuture.com>, Ernest Bartosevic <ernest.bartosevic@recordedfuture.com>
|
|
6
8
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://recordedfuture-professionalservices.github.io/psengine/latest/
|
|
8
|
-
Project-URL: Changelog, https://recordedfuture-professionalservices.github.io/psengine/latest/CHANGELOG/
|
|
9
|
-
Keywords: API,Recorded Future,Cyber Security Engineering,Threat Intelligence
|
|
10
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
10
|
Classifier: Programming Language :: Python
|
|
12
11
|
Classifier: Intended Audience :: Developers
|
|
13
12
|
Classifier: Topic :: Security
|
|
14
13
|
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: typing_extensions>=4.8.0
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Requires-Dist: typing-extensions>=4.8.0
|
|
24
20
|
Requires-Dist: requests>=2.27.1
|
|
25
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: jsonpath-ng>=1.5.3,<=1.6.1
|
|
26
22
|
Requires-Dist: stix2~=3.0.1
|
|
27
23
|
Requires-Dist: python-dateutil>=2.7.0
|
|
28
|
-
Requires-Dist: more-itertools
|
|
29
|
-
Requires-Dist: pydantic
|
|
30
|
-
Requires-Dist: pydantic-settings[toml]
|
|
24
|
+
Requires-Dist: more-itertools>=9.0.0,<=10.2.0
|
|
25
|
+
Requires-Dist: pydantic>=2.7,<3.0.0
|
|
26
|
+
Requires-Dist: pydantic-settings[toml]>=2.12.2,<2.13.1
|
|
31
27
|
Requires-Dist: markdown-strings==3.4.0
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Requires-Dist: pytest-md==0.2.0; extra == "dev"
|
|
37
|
-
Requires-Dist: pytest-random-order==1.1.1; extra == "dev"
|
|
38
|
-
Requires-Dist: pytest-httpdbg==0.9.0; extra == "dev"
|
|
39
|
-
Requires-Dist: ruff~=0.11.0; extra == "dev"
|
|
40
|
-
Requires-Dist: mimesis>=12.1.0; extra == "dev"
|
|
41
|
-
Requires-Dist: build==1.3.0; extra == "dev"
|
|
42
|
-
Requires-Dist: wheel==0.45.1; extra == "dev"
|
|
43
|
-
Requires-Dist: setuptools==80.9.0; extra == "dev"
|
|
44
|
-
Provides-Extra: docs
|
|
45
|
-
Requires-Dist: ruff~=0.11.0; extra == "docs"
|
|
46
|
-
Requires-Dist: mike~=2.1.3; extra == "docs"
|
|
47
|
-
Requires-Dist: mkdocs~=1.6.1; extra == "docs"
|
|
48
|
-
Requires-Dist: mkdocs-material~=9.6.18; extra == "docs"
|
|
49
|
-
Requires-Dist: mkdocstrings[python]>=0.18; extra == "docs"
|
|
50
|
-
Requires-Dist: griffe-typingdoc~=0.2.8; extra == "docs"
|
|
51
|
-
Requires-Dist: mkdocs-codeinclude-plugin~=0.2.1; extra == "docs"
|
|
52
|
-
Requires-Dist: markdown-include~=0.8.1; extra == "docs"
|
|
53
|
-
Requires-Dist: mkdocs-exclude~=1.0.2; extra == "docs"
|
|
54
|
-
Requires-Dist: rich; extra == "docs"
|
|
55
|
-
Requires-Dist: logging-tree; extra == "docs"
|
|
56
|
-
Dynamic: license-file
|
|
28
|
+
Requires-Python: >=3.10, <3.15
|
|
29
|
+
Project-URL: Homepage, https://recordedfuture-professionalservices.github.io/psengine/latest/
|
|
30
|
+
Project-URL: Changelog, https://recordedfuture-professionalservices.github.io/psengine/latest/CHANGELOG/
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
57
32
|
|
|
58
33
|
**Documentation**: <https://recordedfuture-professionalservices.github.io/psengine/>
|
|
59
34
|
|
|
@@ -67,7 +42,8 @@ PSEngine is a simple, yet elegant, library for rapid development of integrations
|
|
|
67
42
|
|
|
68
43
|
PSEngine allows you to interact with the Recorded Future API extremely easily. There’s no need to manually build the URLs and query parameters, just use the modules dedicated to individual API endpoints.
|
|
69
44
|
|
|
70
|
-
PSEngine is a Python package solely built and maintained by the Cyber Security Engineering team powering a number of high profile integrations, such as:
|
|
45
|
+
PSEngine is a Python package solely built and maintained by the Recorded Future Cyber Security Engineering team powering a number of high profile integrations, such as: [Banshee](https://recordedfuture-professionalservices.github.io/banshee); [Recorded Future Alerts for QRadar](https://apps.xforce.ibmcloud.com/extension/b36efdf42b7bf5e3759d036dbcdbf606); Anomali ThreatStream: [Alerts](https://support.recordedfuture.com/hc/en-us/articles/29255683708691-Recorded-Future-Alerts-for-Anomali-ThreatStream) and [Analyst Notes](https://support.recordedfuture.com/hc/en-us/articles/12928414947475-Recorded-Future-Analyst-Notes-for-Anomali-ThreatStream) integrations; [Google SecOps](https://app.recordedfuture.com/portal/integration-center/detail/google-chronicle-nbfi?organization=uhash%3A5cJsHMHeSM&filter_tab=all) and many more.
|
|
46
|
+
|
|
71
47
|
|
|
72
48
|
## Installation
|
|
73
49
|
|
|
@@ -77,7 +53,7 @@ PSEngine is a Python package that can be installed using `pip`. To install PSeng
|
|
|
77
53
|
pip install psengine
|
|
78
54
|
```
|
|
79
55
|
|
|
80
|
-
PSEngine officially supports Python >= 3.
|
|
56
|
+
PSEngine officially supports Python >= 3.10, < 3.15.
|
|
81
57
|
|
|
82
58
|
|
|
83
59
|
## Supported Features & Best Practices
|
|
@@ -94,6 +70,7 @@ It can easily interact with the following Recorded Future datasets:
|
|
|
94
70
|
- Fusion File management
|
|
95
71
|
- Identity Exposures management
|
|
96
72
|
- List management
|
|
73
|
+
- Malware Intelligence (including Auto Yara and Auto Sigma)
|
|
97
74
|
- Malware Sandbox reports download
|
|
98
75
|
- On demand IOC enrichment
|
|
99
76
|
- Risklists
|
|
@@ -108,3 +85,9 @@ And facilitate the development with features like:
|
|
|
108
85
|
- Markdown creation from certain data types
|
|
109
86
|
- Proxy support
|
|
110
87
|
|
|
88
|
+
## Previous versions and version documentation
|
|
89
|
+
|
|
90
|
+
PSEngine has been made public from our internal version 2.0.4. Previous versions, including version 1, are not publicly available.
|
|
91
|
+
|
|
92
|
+
The documentation arrived at version 2.1.1. Older versions are not explicitly documented and changes can be found in the [Release History](./CHANGELOG.md) section.
|
|
93
|
+
|
|
@@ -10,7 +10,8 @@ PSEngine is a simple, yet elegant, library for rapid development of integrations
|
|
|
10
10
|
|
|
11
11
|
PSEngine allows you to interact with the Recorded Future API extremely easily. There’s no need to manually build the URLs and query parameters, just use the modules dedicated to individual API endpoints.
|
|
12
12
|
|
|
13
|
-
PSEngine is a Python package solely built and maintained by the Cyber Security Engineering team powering a number of high profile integrations, such as:
|
|
13
|
+
PSEngine is a Python package solely built and maintained by the Recorded Future Cyber Security Engineering team powering a number of high profile integrations, such as: [Banshee](https://recordedfuture-professionalservices.github.io/banshee); [Recorded Future Alerts for QRadar](https://apps.xforce.ibmcloud.com/extension/b36efdf42b7bf5e3759d036dbcdbf606); Anomali ThreatStream: [Alerts](https://support.recordedfuture.com/hc/en-us/articles/29255683708691-Recorded-Future-Alerts-for-Anomali-ThreatStream) and [Analyst Notes](https://support.recordedfuture.com/hc/en-us/articles/12928414947475-Recorded-Future-Analyst-Notes-for-Anomali-ThreatStream) integrations; [Google SecOps](https://app.recordedfuture.com/portal/integration-center/detail/google-chronicle-nbfi?organization=uhash%3A5cJsHMHeSM&filter_tab=all) and many more.
|
|
14
|
+
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -20,7 +21,7 @@ PSEngine is a Python package that can be installed using `pip`. To install PSeng
|
|
|
20
21
|
pip install psengine
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
PSEngine officially supports Python >= 3.
|
|
24
|
+
PSEngine officially supports Python >= 3.10, < 3.15.
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
## Supported Features & Best Practices
|
|
@@ -37,6 +38,7 @@ It can easily interact with the following Recorded Future datasets:
|
|
|
37
38
|
- Fusion File management
|
|
38
39
|
- Identity Exposures management
|
|
39
40
|
- List management
|
|
41
|
+
- Malware Intelligence (including Auto Yara and Auto Sigma)
|
|
40
42
|
- Malware Sandbox reports download
|
|
41
43
|
- On demand IOC enrichment
|
|
42
44
|
- Risklists
|
|
@@ -51,3 +53,9 @@ And facilitate the development with features like:
|
|
|
51
53
|
- Markdown creation from certain data types
|
|
52
54
|
- Proxy support
|
|
53
55
|
|
|
56
|
+
## Previous versions and version documentation
|
|
57
|
+
|
|
58
|
+
PSEngine has been made public from our internal version 2.0.4. Previous versions, including version 1, are not publicly available.
|
|
59
|
+
|
|
60
|
+
The documentation arrived at version 2.1.1. Older versions are not explicitly documented and changes can be found in the [Release History](./CHANGELOG.md) section.
|
|
61
|
+
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import json
|
|
15
15
|
import logging
|
|
16
16
|
from pathlib import Path
|
|
17
|
-
from typing import Annotated
|
|
17
|
+
from typing import Annotated
|
|
18
18
|
|
|
19
19
|
from pydantic import validate_call
|
|
20
20
|
from typing_extensions import Doc
|
|
@@ -30,9 +30,9 @@ LOG = logging.getLogger('psengine.analyst_notes.helpers')
|
|
|
30
30
|
@validate_call
|
|
31
31
|
def save_attachment(
|
|
32
32
|
note_id: Annotated[str, Doc('The ID of the AnalystNote.')],
|
|
33
|
-
data: Annotated[
|
|
33
|
+
data: Annotated[bytes | str, Doc('The data returned from `fetch_attachment`.')],
|
|
34
34
|
ext: Annotated[str, Doc('The extension of the attachment, returned by `fetch_attachment`.')],
|
|
35
|
-
output_directory: Annotated[
|
|
35
|
+
output_directory: Annotated[str | Path, Doc('The directory to save the file into.')],
|
|
36
36
|
) -> None:
|
|
37
37
|
"""Save a YARA, Sigma, Snort, or PDF attachment to a file.
|
|
38
38
|
|
|
@@ -48,7 +48,7 @@ def save_attachment(
|
|
|
48
48
|
@validate_call
|
|
49
49
|
def save_note(
|
|
50
50
|
note: Annotated[AnalystNote, Doc('The note to save.')],
|
|
51
|
-
output_directory: Annotated[
|
|
51
|
+
output_directory: Annotated[str | Path, Doc('The directory to save the file into.')],
|
|
52
52
|
) -> None:
|
|
53
53
|
"""Save an `AnalystNote` object to a file named with the note ID."""
|
|
54
54
|
output_directory = (
|
|
@@ -62,9 +62,7 @@ def save_note(
|
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def _save_attachment(
|
|
66
|
-
note_id: str, data: Union[bytes, str], ext: str, output_directory: str
|
|
67
|
-
) -> None:
|
|
65
|
+
def _save_attachment(note_id: str, data: bytes | str, ext: str, output_directory: str) -> None:
|
|
68
66
|
"""Save attachment from bytes or note itself from json.
|
|
69
67
|
|
|
70
68
|
Raises:
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import logging
|
|
15
15
|
from datetime import datetime
|
|
16
|
-
from typing import Annotated, Any
|
|
16
|
+
from typing import Annotated, Any
|
|
17
17
|
|
|
18
18
|
from pydantic import BeforeValidator, Field, ValidationError, field_validator, model_validator
|
|
19
19
|
|
|
@@ -21,18 +21,22 @@ from ..common_models import IdNameType, IdNameTypeDescription, RFBaseModel
|
|
|
21
21
|
from ..helpers import Validators
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class NoteEntity(IdNameTypeDescription):
|
|
25
|
+
is_threat_actor: bool | None = None
|
|
26
|
+
|
|
27
|
+
|
|
24
28
|
class DiamondModel(RFBaseModel):
|
|
25
|
-
start:
|
|
26
|
-
stop:
|
|
27
|
-
malicious_infrastructure:
|
|
28
|
-
capabilities:
|
|
29
|
-
adversary:
|
|
30
|
-
target:
|
|
29
|
+
start: datetime | None = None
|
|
30
|
+
stop: datetime | None = None
|
|
31
|
+
malicious_infrastructure: list[NoteEntity] | None = []
|
|
32
|
+
capabilities: list[NoteEntity] | None = []
|
|
33
|
+
adversary: list[NoteEntity] | None = []
|
|
34
|
+
target: list[NoteEntity] | None = []
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
class Query(RFBaseModel):
|
|
34
38
|
title: str
|
|
35
|
-
url:
|
|
39
|
+
url: NoteEntity | None = None
|
|
36
40
|
|
|
37
41
|
|
|
38
42
|
class Position(RFBaseModel):
|
|
@@ -43,35 +47,35 @@ class Position(RFBaseModel):
|
|
|
43
47
|
class PositionEvent(RFBaseModel):
|
|
44
48
|
start: datetime
|
|
45
49
|
stop: datetime
|
|
46
|
-
location:
|
|
47
|
-
event_positions:
|
|
50
|
+
location: list[NoteEntity] | None = []
|
|
51
|
+
event_positions: list[Position] | None = []
|
|
48
52
|
|
|
49
53
|
|
|
50
54
|
class CyberAttackEvent(RFBaseModel):
|
|
51
55
|
start: datetime
|
|
52
56
|
stop: datetime
|
|
53
|
-
adversary:
|
|
54
|
-
target:
|
|
55
|
-
capabilities: list[
|
|
56
|
-
malicious_infrastructure:
|
|
57
|
-
operation:
|
|
57
|
+
adversary: list[NoteEntity] | None = []
|
|
58
|
+
target: list[NoteEntity] | None = []
|
|
59
|
+
capabilities: list[NoteEntity] = []
|
|
60
|
+
malicious_infrastructure: list[NoteEntity] | None = []
|
|
61
|
+
operation: list[NoteEntity] | None = []
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
class ArmedConflictEvent(PositionEvent):
|
|
61
|
-
attacker:
|
|
62
|
-
target:
|
|
65
|
+
attacker: list[NoteEntity] | None = []
|
|
66
|
+
target: list[NoteEntity] | None = []
|
|
63
67
|
|
|
64
68
|
|
|
65
69
|
class ArmsPurchaseSaleEvent(RFBaseModel):
|
|
66
70
|
start: datetime
|
|
67
71
|
stop: datetime
|
|
68
|
-
arms_seller:
|
|
69
|
-
arms_purchaser:
|
|
72
|
+
arms_seller: list[NoteEntity] | None = []
|
|
73
|
+
arms_purchaser: list[NoteEntity] | None = []
|
|
70
74
|
|
|
71
75
|
|
|
72
76
|
class DiseaseOutbreakEvent(PositionEvent):
|
|
73
|
-
disease:
|
|
74
|
-
facility:
|
|
77
|
+
disease: list[NoteEntity] | None = []
|
|
78
|
+
facility: list[NoteEntity] | None = []
|
|
75
79
|
|
|
76
80
|
|
|
77
81
|
class EnvironmentalIssueEvent(PositionEvent):
|
|
@@ -79,45 +83,45 @@ class EnvironmentalIssueEvent(PositionEvent):
|
|
|
79
83
|
|
|
80
84
|
|
|
81
85
|
class ManMadeDisasterEvent(PositionEvent):
|
|
82
|
-
facility: list[
|
|
83
|
-
manmade_disaster:
|
|
86
|
+
facility: list[NoteEntity]
|
|
87
|
+
manmade_disaster: list[NoteEntity] | list[str]
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
class MilitaryManeuverEvent(PositionEvent):
|
|
87
|
-
actors:
|
|
91
|
+
actors: list[NoteEntity] | None = []
|
|
88
92
|
|
|
89
93
|
|
|
90
94
|
class NaturalDisasterEvent(PositionEvent):
|
|
91
|
-
natural_disaster: list[
|
|
95
|
+
natural_disaster: list[NoteEntity]
|
|
92
96
|
|
|
93
97
|
|
|
94
98
|
class NuclearMaterialTransactionEvent(PositionEvent):
|
|
95
99
|
material: list[str]
|
|
96
|
-
location_origin:
|
|
97
|
-
location_destination:
|
|
100
|
+
location_origin: list[str] | None = []
|
|
101
|
+
location_destination: list[str] | None = []
|
|
98
102
|
|
|
99
103
|
|
|
100
104
|
class PersonThreatEvent(RFBaseModel):
|
|
101
105
|
start: datetime
|
|
102
106
|
stop: datetime
|
|
103
|
-
threatened: list[
|
|
104
|
-
actor:
|
|
107
|
+
threatened: list[NoteEntity]
|
|
108
|
+
actor: list[NoteEntity] | None = []
|
|
105
109
|
|
|
106
110
|
|
|
107
111
|
class ProtestEvent(RFBaseModel):
|
|
108
|
-
protest_target:
|
|
112
|
+
protest_target: list[NoteEntity] | None = []
|
|
109
113
|
|
|
110
114
|
|
|
111
115
|
class MalwareAnalysisEvent(RFBaseModel):
|
|
112
116
|
start: datetime
|
|
113
117
|
stop: datetime
|
|
114
|
-
malware: list[
|
|
115
|
-
attacker:
|
|
116
|
-
malicious_infrastructure:
|
|
117
|
-
ttp:
|
|
118
|
-
target:
|
|
119
|
-
exploit:
|
|
120
|
-
hash_:
|
|
118
|
+
malware: list[NoteEntity]
|
|
119
|
+
attacker: list[NoteEntity] | None = []
|
|
120
|
+
malicious_infrastructure: list[NoteEntity] | None = []
|
|
121
|
+
ttp: list[NoteEntity] | None = []
|
|
122
|
+
target: list[NoteEntity] | None = []
|
|
123
|
+
exploit: list[NoteEntity] | None = []
|
|
124
|
+
hash_: list[NoteEntity] | None = Field(alias='hash', default=[])
|
|
121
125
|
|
|
122
126
|
|
|
123
127
|
ATTRIBUTES_MAPPING = {
|
|
@@ -143,8 +147,8 @@ ATTRIBUTES_MAPPING = {
|
|
|
143
147
|
|
|
144
148
|
|
|
145
149
|
class NoteEvent(RFBaseModel):
|
|
146
|
-
type_:
|
|
147
|
-
attributes:
|
|
150
|
+
type_: str | None = Field(alias='type', default=None)
|
|
151
|
+
attributes: Any | None = None
|
|
148
152
|
|
|
149
153
|
@model_validator(mode='before')
|
|
150
154
|
@classmethod
|
|
@@ -175,17 +179,17 @@ class Attributes(RFBaseModel):
|
|
|
175
179
|
title: str
|
|
176
180
|
text: str
|
|
177
181
|
published: datetime
|
|
178
|
-
attachment:
|
|
179
|
-
events:
|
|
180
|
-
validated_on:
|
|
181
|
-
note_entities:
|
|
182
|
-
context_entities:
|
|
183
|
-
topic:
|
|
184
|
-
labels:
|
|
185
|
-
validation_urls:
|
|
186
|
-
diamond_model:
|
|
187
|
-
recommended_queries:
|
|
188
|
-
header_image:
|
|
182
|
+
attachment: str | None = None
|
|
183
|
+
events: list[NoteEvent] | None = []
|
|
184
|
+
validated_on: datetime | None = None
|
|
185
|
+
note_entities: list[NoteEntity] | None = []
|
|
186
|
+
context_entities: list[NoteEntity] | None = []
|
|
187
|
+
topic: list[NoteEntity] | NoteEntity | None = []
|
|
188
|
+
labels: list[NoteEntity] | None = []
|
|
189
|
+
validation_urls: list[NoteEntity] | None = []
|
|
190
|
+
diamond_model: list[DiamondModel] | None = []
|
|
191
|
+
recommended_queries: list[Query] | None = []
|
|
192
|
+
header_image: IdNameType | None = None
|
|
189
193
|
|
|
190
194
|
@field_validator('events', mode='after')
|
|
191
195
|
@classmethod
|
|
@@ -197,24 +201,24 @@ class Attributes(RFBaseModel):
|
|
|
197
201
|
class PreviewAttributesIn(RFBaseModel):
|
|
198
202
|
title: str
|
|
199
203
|
text: str
|
|
200
|
-
note_entities:
|
|
201
|
-
context_entities:
|
|
204
|
+
note_entities: list[str] | None = []
|
|
205
|
+
context_entities: list[str] | None = []
|
|
202
206
|
topic: Annotated[
|
|
203
|
-
|
|
207
|
+
list[str] | str | None,
|
|
204
208
|
BeforeValidator(Validators.convert_str_to_list),
|
|
205
209
|
] = []
|
|
206
|
-
labels:
|
|
207
|
-
validation_urls:
|
|
210
|
+
labels: list[str] | None = []
|
|
211
|
+
validation_urls: list[str] | None = []
|
|
208
212
|
|
|
209
213
|
|
|
210
214
|
class PreviewAttributesOut(RFBaseModel):
|
|
211
215
|
title: str
|
|
212
216
|
text: str
|
|
213
|
-
note_entities:
|
|
214
|
-
context_entities:
|
|
215
|
-
topic:
|
|
216
|
-
labels:
|
|
217
|
-
validation_urls:
|
|
217
|
+
note_entities: list[NoteEntity] | None = []
|
|
218
|
+
context_entities: list[NoteEntity] | None = []
|
|
219
|
+
topic: list[NoteEntity] | None = []
|
|
220
|
+
labels: list[NoteEntity] | None = []
|
|
221
|
+
validation_urls: list[NoteEntity] | None = []
|
|
218
222
|
|
|
219
223
|
|
|
220
224
|
class RequestAttachment(RFBaseModel):
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
##############################################################################################
|
|
13
13
|
|
|
14
14
|
from functools import total_ordering
|
|
15
|
-
from typing import Annotated
|
|
15
|
+
from typing import Annotated
|
|
16
16
|
|
|
17
17
|
from pydantic import Field
|
|
18
18
|
from typing_extensions import Doc
|
|
@@ -57,7 +57,7 @@ class AnalystNote(RFBaseModel):
|
|
|
57
57
|
criterion.
|
|
58
58
|
"""
|
|
59
59
|
|
|
60
|
-
external_id:
|
|
60
|
+
external_id: str | None = None
|
|
61
61
|
source: IdNameTypeDescription
|
|
62
62
|
attributes: Attributes
|
|
63
63
|
id_: str = Field(alias='id')
|
|
@@ -78,7 +78,7 @@ class AnalystNote(RFBaseModel):
|
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
@property
|
|
81
|
-
def detection_rule_type(self) ->
|
|
81
|
+
def detection_rule_type(self) -> str | None:
|
|
82
82
|
"""Returns the attachment type if present, else None. It checks for specific types like
|
|
83
83
|
`sigma rule`, `yara rule`, and `snort rule` in the topics of the note.
|
|
84
84
|
"""
|
|
@@ -113,7 +113,7 @@ class AnalystNote(RFBaseModel):
|
|
|
113
113
|
bool, Doc('Defang URLs or other malicious indicators.')
|
|
114
114
|
] = False,
|
|
115
115
|
character_limit: Annotated[
|
|
116
|
-
|
|
116
|
+
int | None,
|
|
117
117
|
Doc('Limit the output to a specified number of characters.'),
|
|
118
118
|
] = None,
|
|
119
119
|
) -> Annotated[str, Doc('The generated markdown string.')]:
|
|
@@ -132,7 +132,7 @@ class AnalystNotePreviewIn(RFBaseModel):
|
|
|
132
132
|
"""Validate data sent to `/preview` endpoint."""
|
|
133
133
|
|
|
134
134
|
attributes: PreviewAttributesIn
|
|
135
|
-
source:
|
|
135
|
+
source: str | None
|
|
136
136
|
tagged_text: bool = False
|
|
137
137
|
serialization: str = 'full'
|
|
138
138
|
|
|
@@ -148,12 +148,12 @@ class AnalystNotePublishIn(AnalystNotePreviewIn):
|
|
|
148
148
|
"""Validate data sent to `/publish` endpoint."""
|
|
149
149
|
|
|
150
150
|
attributes: PreviewAttributesIn
|
|
151
|
-
source:
|
|
151
|
+
source: str | None = None
|
|
152
152
|
tagged_text: bool = False
|
|
153
153
|
serialization: str = 'full'
|
|
154
|
-
note_id:
|
|
154
|
+
note_id: str | None = None
|
|
155
155
|
resolve_entities: bool = True
|
|
156
|
-
attachment_content_details:
|
|
156
|
+
attachment_content_details: RequestAttachment | None = None
|
|
157
157
|
|
|
158
158
|
|
|
159
159
|
class AnalystNotePublishOut(RFBaseModel):
|
|
@@ -165,14 +165,14 @@ class AnalystNotePublishOut(RFBaseModel):
|
|
|
165
165
|
class AnalystNoteSearchIn(RFBaseModel):
|
|
166
166
|
"""Validate data sent to `/search` endpoint."""
|
|
167
167
|
|
|
168
|
-
published:
|
|
169
|
-
entity:
|
|
170
|
-
author:
|
|
171
|
-
title:
|
|
172
|
-
topic:
|
|
173
|
-
label:
|
|
174
|
-
source:
|
|
168
|
+
published: str | None = None
|
|
169
|
+
entity: str | None = None
|
|
170
|
+
author: str | None = None
|
|
171
|
+
title: str | None = None
|
|
172
|
+
topic: list[str] | str | None = []
|
|
173
|
+
label: str | None = None
|
|
174
|
+
source: str | None = None
|
|
175
175
|
serialization: str = None
|
|
176
176
|
tagged_text: bool = None
|
|
177
177
|
limit: int = NOTES_PER_PAGE
|
|
178
|
-
from_:
|
|
178
|
+
from_: str | None = Field(alias='from', default=None)
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import logging
|
|
15
15
|
import re
|
|
16
16
|
from itertools import chain
|
|
17
|
-
from typing import Annotated
|
|
17
|
+
from typing import Annotated
|
|
18
18
|
|
|
19
19
|
from pydantic import Field, validate_call
|
|
20
20
|
from typing_extensions import Doc
|
|
@@ -66,23 +66,23 @@ class AnalystNoteMgr:
|
|
|
66
66
|
@validate_call
|
|
67
67
|
def search(
|
|
68
68
|
self,
|
|
69
|
-
published: Annotated[
|
|
70
|
-
entity: Annotated[
|
|
71
|
-
author: Annotated[
|
|
72
|
-
title: Annotated[
|
|
73
|
-
topic: Annotated[
|
|
74
|
-
label: Annotated[
|
|
75
|
-
source: Annotated[
|
|
69
|
+
published: Annotated[str | None, Doc('Notes published after a date.')] = None,
|
|
70
|
+
entity: Annotated[str | None, Doc('An entity the note refers to, RF ID.')] = None,
|
|
71
|
+
author: Annotated[str | None, Doc('An author of the note, RF ID.')] = None,
|
|
72
|
+
title: Annotated[str | None, Doc('A title of the note.')] = None,
|
|
73
|
+
topic: Annotated[str | list | None, Doc('A topic of the note, RF ID.')] = None,
|
|
74
|
+
label: Annotated[str | None, Doc('A label of the note, by name.')] = None,
|
|
75
|
+
source: Annotated[str | None, Doc('The source of the note.')] = None,
|
|
76
76
|
serialization: Annotated[
|
|
77
|
-
|
|
77
|
+
str | None, Doc('An entity serializer (id, min, full, raw).')
|
|
78
78
|
] = None,
|
|
79
|
-
tagged_text: Annotated[
|
|
79
|
+
tagged_text: Annotated[bool | None, Doc('Should the text contain tags.')] = None,
|
|
80
80
|
max_results: Annotated[
|
|
81
|
-
|
|
81
|
+
int | None,
|
|
82
82
|
Doc('The maximum number of references (not notes), max 1000.'),
|
|
83
83
|
] = Field(ge=1, le=1000, default=DEFAULT_LIMIT),
|
|
84
84
|
notes_per_page: Annotated[
|
|
85
|
-
|
|
85
|
+
int | None, Doc('The number of notes for each paged request.')
|
|
86
86
|
] = Field(ge=1, le=1000, default=NOTES_PER_PAGE),
|
|
87
87
|
) -> Annotated[list[AnalystNote], Doc('A list of deduplicated AnalystNote objects.')]:
|
|
88
88
|
"""Execute a search for the analyst notes based on the parameters provided.
|
|
@@ -189,16 +189,16 @@ class AnalystNoteMgr:
|
|
|
189
189
|
self,
|
|
190
190
|
title: Annotated[str, Doc('The title of the note.')],
|
|
191
191
|
text: Annotated[str, Doc('The text of the note.')],
|
|
192
|
-
published: Annotated[
|
|
193
|
-
topic: Annotated[
|
|
192
|
+
published: Annotated[str | None, Doc('The date when the note was published.')] = None,
|
|
193
|
+
topic: Annotated[str | list[str] | None, Doc('The topic of the note.')] = None,
|
|
194
194
|
context_entities: Annotated[
|
|
195
|
-
|
|
195
|
+
list[str] | None, Doc('The context entities of the note.')
|
|
196
196
|
] = None,
|
|
197
|
-
note_entities: Annotated[
|
|
197
|
+
note_entities: Annotated[list[str] | None, Doc('The note entities of the note.')] = None,
|
|
198
198
|
validation_urls: Annotated[
|
|
199
|
-
|
|
199
|
+
list[str] | None, Doc('The validation URLs of the note.')
|
|
200
200
|
] = None,
|
|
201
|
-
source: Annotated[
|
|
201
|
+
source: Annotated[str | None, Doc('The source of the note.')] = None,
|
|
202
202
|
) -> Annotated[AnalystNotePreviewOut, Doc('The note that will be created.')]:
|
|
203
203
|
"""Preview of the AnalystNote. It does not create a note; it just returns how the note
|
|
204
204
|
will look.
|
|
@@ -236,18 +236,18 @@ class AnalystNoteMgr:
|
|
|
236
236
|
self,
|
|
237
237
|
title: Annotated[str, Doc('The title of the note.')],
|
|
238
238
|
text: Annotated[str, Doc('The text of the note.')],
|
|
239
|
-
published: Annotated[
|
|
240
|
-
topic: Annotated[
|
|
239
|
+
published: Annotated[str | None, Doc('The date when the note was published.')] = None,
|
|
240
|
+
topic: Annotated[str | list[str] | None, Doc('The topic of the note.')] = None,
|
|
241
241
|
context_entities: Annotated[
|
|
242
|
-
|
|
242
|
+
list[str] | None, Doc('The context entities of the note.')
|
|
243
243
|
] = None,
|
|
244
|
-
note_entities: Annotated[
|
|
244
|
+
note_entities: Annotated[list[str] | None, Doc('The note entities of the note.')] = None,
|
|
245
245
|
validation_urls: Annotated[
|
|
246
|
-
|
|
246
|
+
list[str] | None, Doc('The validation URLs of the note.')
|
|
247
247
|
] = None,
|
|
248
|
-
source: Annotated[
|
|
248
|
+
source: Annotated[str | None, Doc('The source of the note.')] = None,
|
|
249
249
|
note_id: Annotated[
|
|
250
|
-
|
|
250
|
+
str | None, Doc('The ID of the note. Use if you want to modify an existing note.')
|
|
251
251
|
] = None,
|
|
252
252
|
) -> Annotated[AnalystNotePublishOut, Doc('The published note.')]:
|
|
253
253
|
"""Publish data. This method creates a note and returns its ID.
|