psengine 2.0.4__py3-none-any.whl
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/__init__.py +22 -0
- psengine/_sdk_id.py +16 -0
- psengine/_version.py +14 -0
- psengine/analyst_notes/__init__.py +32 -0
- psengine/analyst_notes/constants.py +15 -0
- psengine/analyst_notes/errors.py +42 -0
- psengine/analyst_notes/helpers.py +90 -0
- psengine/analyst_notes/models.py +219 -0
- psengine/analyst_notes/note.py +149 -0
- psengine/analyst_notes/note_mgr.py +400 -0
- psengine/base_http_client.py +285 -0
- psengine/classic_alerts/__init__.py +24 -0
- psengine/classic_alerts/classic_alert.py +275 -0
- psengine/classic_alerts/classic_alert_mgr.py +507 -0
- psengine/classic_alerts/constants.py +31 -0
- psengine/classic_alerts/errors.py +38 -0
- psengine/classic_alerts/helpers.py +87 -0
- psengine/classic_alerts/markdown/__init__.py +13 -0
- psengine/classic_alerts/markdown/markdown.py +359 -0
- psengine/classic_alerts/models.py +141 -0
- psengine/collective_insights/__init__.py +29 -0
- psengine/collective_insights/collective_insights.py +164 -0
- psengine/collective_insights/constants.py +44 -0
- psengine/collective_insights/errors.py +18 -0
- psengine/collective_insights/insight.py +89 -0
- psengine/collective_insights/models.py +81 -0
- psengine/common_models.py +89 -0
- psengine/config/__init__.py +15 -0
- psengine/config/config.py +284 -0
- psengine/config/errors.py +18 -0
- psengine/constants.py +63 -0
- psengine/detection/__init__.py +17 -0
- psengine/detection/detection_mgr.py +135 -0
- psengine/detection/detection_rule.py +85 -0
- psengine/detection/errors.py +26 -0
- psengine/detection/helpers.py +56 -0
- psengine/detection/models.py +47 -0
- psengine/endpoints.py +98 -0
- psengine/enrich/__init__.py +28 -0
- psengine/enrich/constants.py +73 -0
- psengine/enrich/errors.py +26 -0
- psengine/enrich/lookup.py +299 -0
- psengine/enrich/lookup_mgr.py +341 -0
- psengine/enrich/models/__init__.py +13 -0
- psengine/enrich/models/base_enriched_entity.py +43 -0
- psengine/enrich/models/lookup.py +271 -0
- psengine/enrich/models/soar.py +138 -0
- psengine/enrich/soar.py +89 -0
- psengine/enrich/soar_mgr.py +176 -0
- psengine/entity_lists/__init__.py +16 -0
- psengine/entity_lists/constants.py +19 -0
- psengine/entity_lists/entity_list.py +435 -0
- psengine/entity_lists/entity_list_mgr.py +185 -0
- psengine/entity_lists/errors.py +26 -0
- psengine/entity_lists/models.py +87 -0
- psengine/entity_match/__init__.py +16 -0
- psengine/entity_match/entity_match.py +90 -0
- psengine/entity_match/entity_match_mgr.py +235 -0
- psengine/entity_match/errors.py +18 -0
- psengine/entity_match/models.py +22 -0
- psengine/errors.py +41 -0
- psengine/helpers/__init__.py +23 -0
- psengine/helpers/helpers.py +471 -0
- psengine/logger/__init__.py +15 -0
- psengine/logger/constants.py +39 -0
- psengine/logger/errors.py +18 -0
- psengine/logger/rf_logger.py +148 -0
- psengine/markdown/__init__.py +21 -0
- psengine/markdown/markdown.py +169 -0
- psengine/markdown/models.py +22 -0
- psengine/playbook_alerts/__init__.py +34 -0
- psengine/playbook_alerts/constants.py +35 -0
- psengine/playbook_alerts/errors.py +35 -0
- psengine/playbook_alerts/helpers.py +80 -0
- psengine/playbook_alerts/mappings.py +44 -0
- psengine/playbook_alerts/markdown/__init__.py +13 -0
- psengine/playbook_alerts/markdown/markdown.py +98 -0
- psengine/playbook_alerts/markdown/markdown_code_repo.py +64 -0
- psengine/playbook_alerts/markdown/markdown_domain_abuse.py +118 -0
- psengine/playbook_alerts/markdown/markdown_identity_exposure.py +158 -0
- psengine/playbook_alerts/models/__init__.py +36 -0
- psengine/playbook_alerts/models/common_models.py +18 -0
- psengine/playbook_alerts/models/panel_log.py +329 -0
- psengine/playbook_alerts/models/panel_status.py +70 -0
- psengine/playbook_alerts/models/pba_code_repo_leak.py +52 -0
- psengine/playbook_alerts/models/pba_cyber_vulnerability.py +53 -0
- psengine/playbook_alerts/models/pba_domain_abuse.py +139 -0
- psengine/playbook_alerts/models/pba_identity_exposures.py +93 -0
- psengine/playbook_alerts/models/pba_third_party_risk.py +103 -0
- psengine/playbook_alerts/models/search_endpoint.py +68 -0
- psengine/playbook_alerts/pa_category.py +37 -0
- psengine/playbook_alerts/playbook_alert_mgr.py +593 -0
- psengine/playbook_alerts/playbook_alerts.py +393 -0
- psengine/rf_client.py +430 -0
- psengine/risklists/__init__.py +17 -0
- psengine/risklists/constants.py +15 -0
- psengine/risklists/errors.py +20 -0
- psengine/risklists/models.py +65 -0
- psengine/risklists/risklist_mgr.py +156 -0
- psengine/stix2/__init__.py +21 -0
- psengine/stix2/base_stix_entity.py +62 -0
- psengine/stix2/complex_entity.py +372 -0
- psengine/stix2/constants.py +81 -0
- psengine/stix2/enriched_indicator.py +261 -0
- psengine/stix2/errors.py +22 -0
- psengine/stix2/helpers.py +68 -0
- psengine/stix2/rf_bundle.py +240 -0
- psengine/stix2/simple_entity.py +145 -0
- psengine/stix2/util.py +53 -0
- psengine-2.0.4.dist-info/METADATA +189 -0
- psengine-2.0.4.dist-info/RECORD +115 -0
- psengine-2.0.4.dist-info/WHEEL +5 -0
- psengine-2.0.4.dist-info/entry_points.txt +2 -0
- psengine-2.0.4.dist-info/licenses/LICENSE +21 -0
- psengine-2.0.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
##################################### TERMS OF USE ###########################################
|
|
2
|
+
# The following code is provided for demonstration purpose only, and should not be used #
|
|
3
|
+
# without independent verification. Recorded Future makes no representations or warranties, #
|
|
4
|
+
# express, implied, statutory, or otherwise, regarding any aspect of this code or of the #
|
|
5
|
+
# information it may retrieve, and provides it both strictly “as-is” and without assuming #
|
|
6
|
+
# responsibility for any information it may retrieve. Recorded Future shall not be liable #
|
|
7
|
+
# for, and you assume all risk of using, the foregoing. By using this code, Customer #
|
|
8
|
+
# represents that it is solely responsible for having all necessary licenses, permissions, #
|
|
9
|
+
# rights, and/or consents to connect to third party APIs, and that it is solely responsible #
|
|
10
|
+
# for having all necessary licenses, permissions, rights, and/or consents to any data #
|
|
11
|
+
# accessed from any third party API. #
|
|
12
|
+
##############################################################################################
|
|
13
|
+
|
|
14
|
+
import stix2
|
|
15
|
+
|
|
16
|
+
from .base_stix_entity import BaseStixEntity
|
|
17
|
+
from .constants import IDENTITY_TYPE_TO_CLASS
|
|
18
|
+
from .util import generate_uuid
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TTP(BaseStixEntity):
|
|
22
|
+
"""Converts MITRE T codes to AttackPattern."""
|
|
23
|
+
|
|
24
|
+
def create_stix_object(self) -> None:
|
|
25
|
+
"""Creates AttackPattern objects from object attributes."""
|
|
26
|
+
self.stix_obj = stix2.AttackPattern(
|
|
27
|
+
id=self._generate_id(),
|
|
28
|
+
name=self.name,
|
|
29
|
+
created_by_ref=self.author.id,
|
|
30
|
+
custom_properties={'x_mitre_id': self.name},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def _generate_id(self) -> str:
|
|
34
|
+
"""Generates an ID."""
|
|
35
|
+
return 'attack-pattern--' + generate_uuid(name=self.name)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Identity(BaseStixEntity):
|
|
39
|
+
"""Converts various RF entity types to a STIX2 Identity."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, name: str, rf_type: str, author: str = None) -> None:
|
|
42
|
+
"""Init Identity Class.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
name (str): Name of the Identity
|
|
46
|
+
rf_type (str): Recorded Future type of the identity
|
|
47
|
+
author (str, optional): Recorded Future author object
|
|
48
|
+
"""
|
|
49
|
+
self.rf_type = rf_type
|
|
50
|
+
super().__init__(name, author)
|
|
51
|
+
|
|
52
|
+
def create_stix_object(self) -> None:
|
|
53
|
+
"""Creates STIX objects from object attributes."""
|
|
54
|
+
self.stix_obj = stix2.Identity(
|
|
55
|
+
id=self._generate_id(),
|
|
56
|
+
name=self.name,
|
|
57
|
+
identity_class=self.create_id_class(),
|
|
58
|
+
created_by_ref=self.author.id,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def create_id_class(self):
|
|
62
|
+
"""Creates a STIX2 identity class."""
|
|
63
|
+
return IDENTITY_TYPE_TO_CLASS[self.rf_type]
|
|
64
|
+
|
|
65
|
+
def _generate_id(self) -> str:
|
|
66
|
+
"""Generates an ID."""
|
|
67
|
+
return 'identity--' + generate_uuid(name=self.name, identity_class=self.rf_type)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ThreatActor(BaseStixEntity):
|
|
71
|
+
"""Converts various RF Threat Actor Organization to a STIX2 Threat Actor."""
|
|
72
|
+
|
|
73
|
+
def create_stix_object(self) -> None:
|
|
74
|
+
"""Creates STIX objects from object attributes."""
|
|
75
|
+
self.stix_obj = stix2.ThreatActor(
|
|
76
|
+
id=self._generate_id(),
|
|
77
|
+
name=self.name,
|
|
78
|
+
created_by_ref=self.author.id,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def _generate_id(self) -> str:
|
|
82
|
+
"""Generates an ID."""
|
|
83
|
+
return 'threat-actor--' + generate_uuid(name=self.name)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class IntrusionSet(BaseStixEntity):
|
|
87
|
+
"""Converts Threat Actor to Intrusion Set SDO."""
|
|
88
|
+
|
|
89
|
+
def create_stix_object(self) -> None:
|
|
90
|
+
"""Creates STIX objects from object attributes."""
|
|
91
|
+
self.stix_obj = stix2.IntrusionSet(
|
|
92
|
+
id=self._generate_id(),
|
|
93
|
+
name=self.name,
|
|
94
|
+
created_by_ref=self.author.id,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def _generate_id(self) -> str:
|
|
98
|
+
"""Generates an ID."""
|
|
99
|
+
return 'intrusion-set--' + generate_uuid(name=self.name)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Malware(BaseStixEntity):
|
|
103
|
+
"""Converts Malware to a Malware SDO."""
|
|
104
|
+
|
|
105
|
+
def create_stix_object(self) -> None:
|
|
106
|
+
"""Creates STIX objects from object attributes."""
|
|
107
|
+
self.stix_obj = stix2.Malware(
|
|
108
|
+
id=self._generate_id(),
|
|
109
|
+
name=self.name,
|
|
110
|
+
is_family=False,
|
|
111
|
+
created_by_ref=self.author.id,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def _generate_id(self) -> str:
|
|
115
|
+
"""Generates an ID."""
|
|
116
|
+
return 'malware--' + generate_uuid(name=self.name)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Vulnerability(BaseStixEntity):
|
|
120
|
+
"""Converts a CyberVulnerability to a Vulnerability SDO."""
|
|
121
|
+
|
|
122
|
+
def __init__(self, name: str, description: str = None, author: str = None) -> None:
|
|
123
|
+
"""Init Vulnerability Class.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
name (str): Name of the Identity
|
|
127
|
+
description (str, optional): Vulnerability description
|
|
128
|
+
author (str, optional): Recorded Future author object
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
self.description = description
|
|
132
|
+
super().__init__(name, author)
|
|
133
|
+
|
|
134
|
+
def create_stix_object(self) -> None:
|
|
135
|
+
"""Creates STIX objects from object attributes."""
|
|
136
|
+
self.stix_obj = stix2.Vulnerability(
|
|
137
|
+
id=self._generate_id(),
|
|
138
|
+
description=self.description,
|
|
139
|
+
name=self.name,
|
|
140
|
+
created_by_ref=self.author.id,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _generate_id(self) -> str:
|
|
144
|
+
"""Generates an ID."""
|
|
145
|
+
return 'vulnerability--' + generate_uuid(name=self.name)
|
psengine/stix2/util.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
##################################### TERMS OF USE ###########################################
|
|
2
|
+
# The following code is provided for demonstration purpose only, and should not be used #
|
|
3
|
+
# without independent verification. Recorded Future makes no representations or warranties, #
|
|
4
|
+
# express, implied, statutory, or otherwise, regarding any aspect of this code or of the #
|
|
5
|
+
# information it may retrieve, and provides it both strictly “as-is” and without assuming #
|
|
6
|
+
# responsibility for any information it may retrieve. Recorded Future shall not be liable #
|
|
7
|
+
# for, and you assume all risk of using, the foregoing. By using this code, Customer #
|
|
8
|
+
# represents that it is solely responsible for having all necessary licenses, permissions, #
|
|
9
|
+
# rights, and/or consents to connect to third party APIs, and that it is solely responsible #
|
|
10
|
+
# for having all necessary licenses, permissions, rights, and/or consents to any data #
|
|
11
|
+
# accessed from any third party API. #.
|
|
12
|
+
##############################################################################################
|
|
13
|
+
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
import stix2
|
|
17
|
+
from stix2.canonicalization.Canonicalize import canonicalize
|
|
18
|
+
|
|
19
|
+
from .constants import RF_IDENTITY_UUID, RF_NAMESPACE
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def generate_uuid(**kwargs: dict) -> str:
|
|
23
|
+
"""Generated a unique UUID to be used as a STIX2 ID.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
**kwargs (dict): a list of parameters to be hashed for the UUId.
|
|
27
|
+
Usually just {name:'somename'}, but could be more complex
|
|
28
|
+
"""
|
|
29
|
+
data = {k: str(v) for k, v in kwargs.items()}
|
|
30
|
+
data = canonicalize(data, utf8=False)
|
|
31
|
+
return str(uuid.uuid5(uuid.UUID(RF_NAMESPACE), data))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_rf_author() -> stix2.v21.Identity:
|
|
35
|
+
"""Create the Recorded Future Author Identity.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
stix2.v21.Identity: Recorded Future, as an identity
|
|
39
|
+
"""
|
|
40
|
+
name = 'Recorded Future'
|
|
41
|
+
id_class = 'organization'
|
|
42
|
+
return stix2.Identity(
|
|
43
|
+
name=name,
|
|
44
|
+
identity_class=id_class,
|
|
45
|
+
id=RF_IDENTITY_UUID,
|
|
46
|
+
description=(
|
|
47
|
+
'Recorded Future is the most comprehensive and independent threat'
|
|
48
|
+
' intelligence cloud platform. We enable organizations to identify and mitigate'
|
|
49
|
+
' threats across cyber, supply-chain, physical and fraud domains; and are trusted'
|
|
50
|
+
' to get real-time, unbiased and actionable intelligence.'
|
|
51
|
+
),
|
|
52
|
+
contact_information='support@recordedfuture.com',
|
|
53
|
+
)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: psengine
|
|
3
|
+
Version: 2.0.4
|
|
4
|
+
Summary: psengine is a simple, yet elegant, library for rapid development of integrations with Recorded Future.
|
|
5
|
+
Author-email: Moise Medici <moise.medici@recordedfuture.com>, Patrick Kinsella <patrick.kinsella@recordedfuture.com>, Ernest Bartosevic <ernest.bartosevic@recordedfuture.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/RecordedFuture-ProfessionalServices/psengine-py
|
|
8
|
+
Project-URL: Changelog, https://github.com/RecordedFuture-ProfessionalServices/psengine-py/CHANGELOG.rst
|
|
9
|
+
Keywords: API,Recorded Future,Cyber Security Engineering,Threat Intelligence
|
|
10
|
+
Requires-Python: <3.14,>=3.9
|
|
11
|
+
Description-Content-Type: text/x-rst
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests>=2.27.1
|
|
14
|
+
Requires-Dist: jsonpath_ng<=1.6.1,>=1.5.3
|
|
15
|
+
Requires-Dist: stix2~=3.0.1
|
|
16
|
+
Requires-Dist: python-dateutil>=2.7.0
|
|
17
|
+
Requires-Dist: more-itertools<=10.2.0,>=9.0.0
|
|
18
|
+
Requires-Dist: pydantic<3.0.0,>=2.7
|
|
19
|
+
Requires-Dist: pydantic-settings[toml]~=2.5.2
|
|
20
|
+
Requires-Dist: markdown-strings==3.4.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: tox==4.12.1; extra == "dev"
|
|
23
|
+
Requires-Dist: build==1.0.3; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest==8.3.4; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov==6.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-mock==3.14.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-random-order==1.1.1; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-vcr==1.0.2; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-watch==4.2.0; extra == "dev"
|
|
30
|
+
Requires-Dist: requests==2.29.0; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff~=0.7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: wheel==0.37.1; extra == "dev"
|
|
33
|
+
Requires-Dist: setuptools==61.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: Sphinx==7.1.2; extra == "dev"
|
|
35
|
+
Requires-Dist: sphinxcontrib-confluencebuilder==2.3.0; extra == "dev"
|
|
36
|
+
Requires-Dist: atlassian-python-api==3.41.4; extra == "dev"
|
|
37
|
+
Requires-Dist: sphinx_autodoc_typehints==1.25.2; extra == "dev"
|
|
38
|
+
Requires-Dist: sphinxcontrib-napoleon; extra == "dev"
|
|
39
|
+
Requires-Dist: typer==0.12.5; extra == "dev"
|
|
40
|
+
Requires-Dist: cookiecutter==2.6.0; extra == "dev"
|
|
41
|
+
Requires-Dist: tomlkit>=0.13.2; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
==================================================
|
|
45
|
+
PSEngine
|
|
46
|
+
==================================================
|
|
47
|
+
**PSEngine** is a simple, yet elegant, library for rapid development of integrations with Recorded Future.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
.. code-block:: python
|
|
51
|
+
|
|
52
|
+
>>> from psengine.enrich import LookupMgr
|
|
53
|
+
>>> lookup_mgr = LookupMgr(rf_token='token')
|
|
54
|
+
>>> domain = lookup_mgr.lookup('cpejcogzznpudbsmaxxm.com', 'domain')
|
|
55
|
+
>>> domain
|
|
56
|
+
'EnrichedDomain: cpejcogzznpudbsmaxxm.com, Risk Score: 20, Last Seen: 2024-07-22 02:50:59PM'
|
|
57
|
+
>>> domain.entity
|
|
58
|
+
'cpejcogzznpudbsmaxxm.com'
|
|
59
|
+
>>> domain.content.risk
|
|
60
|
+
EntityRisk(criticality_label='Unusual', risk_string='4/52', score=20, rules=4...)
|
|
61
|
+
>>> domain.content.risk.score
|
|
62
|
+
20
|
|
63
|
+
>>>
|
|
64
|
+
domain.content.risk.risk_summary
|
|
65
|
+
'4 of 52 Risk Rules currently observed.'
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
PSEngine allows you to interact with the Recorded Future API extremely easily. There’s no need to manually build the URLs and query parameters - but nowadays, just use the modules dedicated to individual API endpoints!
|
|
69
|
+
|
|
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: Elasticsearch, QRadar, Anomali, Jira, TheHive, etc..
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Installation
|
|
74
|
+
==================================================
|
|
75
|
+
PSEngine is a Python package that can be installed using pip. To install PSengine, run the following command:
|
|
76
|
+
|
|
77
|
+
.. code-block:: bash
|
|
78
|
+
|
|
79
|
+
$ pip install psengine git+https://github.com/RecordedFuture-ProfessionalServices/psengine.git@main
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
PSEngine officially supports Python >= 3.9, < 3.14
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
Supported Features & Best–Practices
|
|
86
|
+
==================================================
|
|
87
|
+
|
|
88
|
+
PSEngine is ready for the demands of building robust and reliable integrations.
|
|
89
|
+
|
|
90
|
+
* Collective Insights
|
|
91
|
+
* Analyst Notes
|
|
92
|
+
* Classic & Playbook Alerts
|
|
93
|
+
* Risklists
|
|
94
|
+
* On demand IOC enrichment
|
|
95
|
+
* List management
|
|
96
|
+
* Detection Rules
|
|
97
|
+
* Built in logging
|
|
98
|
+
* Easy configuration management
|
|
99
|
+
* Proxy support
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Quick Start
|
|
103
|
+
==================================================
|
|
104
|
+
Excited, to get started?
|
|
105
|
+
|
|
106
|
+
The section below will give you the basic building blocks to start building integrations with PSEngine.
|
|
107
|
+
|
|
108
|
+
But first ensure that:
|
|
109
|
+
|
|
110
|
+
- PSEngine is installed
|
|
111
|
+
- PSEngine is up-to-date
|
|
112
|
+
|
|
113
|
+
Let’s get started with some core concepts and practices.
|
|
114
|
+
|
|
115
|
+
Config Management
|
|
116
|
+
--------------------------------------------------
|
|
117
|
+
The key requirement when building integrations with PSEngine is initializing `Config` as early as possible in your program,
|
|
118
|
+
before initializing any PSEngine managers. This way `rf_token` `app_id` and `platform_id` you set will be used by every manager
|
|
119
|
+
initialized after the Config.
|
|
120
|
+
|
|
121
|
+
.. code-block:: python
|
|
122
|
+
|
|
123
|
+
>>> from psengine.config import Config, get_config
|
|
124
|
+
# Name & version of the integration itself
|
|
125
|
+
>>> APP_ID = 'example-app/1.0.0'
|
|
126
|
+
# Name & version of the tool this integrates with (Optional)
|
|
127
|
+
>>> PLATFORM_ID = 'PSE/1.0.0'
|
|
128
|
+
>>> Config.init(rf_token='your_token', app_id=APP_ID, platform_id=PLATFORM_ID)
|
|
129
|
+
>>> config = get_config()
|
|
130
|
+
>>> config.app_id
|
|
131
|
+
'example-app/1.0.0'
|
|
132
|
+
|
|
133
|
+
The above will result in API calls made by the managers having the following headers set:
|
|
134
|
+
|
|
135
|
+
- 'X-RFToken' Header will contain the Recorded Future API Token
|
|
136
|
+
- 'User-Agent' Header will contain APP ID and Platform ID (if supplied) which is a Recorded Future requirement, which might look like this:
|
|
137
|
+
|
|
138
|
+
example-app/1.0.0 (macOS-14.1-arm64-arm-64bit) psengine-py/2.0.1 PSE/1.0.0
|
|
139
|
+
|
|
140
|
+
Authorization
|
|
141
|
+
--------------------------------------------------
|
|
142
|
+
In the example above we saw a token passed to the Config by the caller, but you can also omit the token during initialization and let
|
|
143
|
+
Config retrieve it from the environment variable `RF_TOKEN`. Just ensure that the environment variable is set before running your program:
|
|
144
|
+
|
|
145
|
+
export RF_TOKEN=your_token
|
|
146
|
+
|
|
147
|
+
Alternatively, if you want to set an rf_token separately for a single manager, you may pass it in the constructor:
|
|
148
|
+
|
|
149
|
+
.. code-block:: python
|
|
150
|
+
|
|
151
|
+
>>> note_mgr = AnalystNoteMgr(rf_token='your_token')
|
|
152
|
+
|
|
153
|
+
Logging
|
|
154
|
+
--------------------------------------------------
|
|
155
|
+
PSEngine also provides the capability for logging to console and files. If your program needs to show log output on the terminal and keep a .log file, just import and use psengine’s logger:
|
|
156
|
+
|
|
157
|
+
.. code-block:: python
|
|
158
|
+
|
|
159
|
+
>>> from psengine.logger import RFLogger
|
|
160
|
+
>>> LOG = RFLogger().get_logger()
|
|
161
|
+
>>> LOG.info('Hello, world!')
|
|
162
|
+
|
|
163
|
+
On the other hand, if your program’s log statements already have handlers setup, just log the normal way:
|
|
164
|
+
|
|
165
|
+
.. code-block:: python
|
|
166
|
+
|
|
167
|
+
>>> import logging
|
|
168
|
+
>>> LOG = logging.getLogger(__name__)
|
|
169
|
+
>>> LOG.info('Hello, world!')
|
|
170
|
+
|
|
171
|
+
In the second example, nothing is printed to terminal or file unless a handler is setup by another program running your code.
|
|
172
|
+
|
|
173
|
+
Proxies
|
|
174
|
+
--------------------------------------------------
|
|
175
|
+
If your environment requires a proxy to access the internet, you can set the proxy in the Config:
|
|
176
|
+
|
|
177
|
+
.. code-block:: python
|
|
178
|
+
|
|
179
|
+
>>> Config.init(
|
|
180
|
+
app_id=APP_ID,
|
|
181
|
+
platform_id=PLATFORM_ID,
|
|
182
|
+
http_proxy='http://proxy:8080',
|
|
183
|
+
https_proxy='http://proxy:8080',
|
|
184
|
+
client_ssl_verify=False,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
Examples
|
|
188
|
+
--------------------------------------------------
|
|
189
|
+
Please refer to `examples <examples>`_ for usage example of each module.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
psengine/__init__.py,sha256=-1iU4hixLTtVG9nKWuieGHxpcTxRknP7WpIwFgziMaU,1429
|
|
2
|
+
psengine/_sdk_id.py,sha256=YmQ1Y2x0aGRVCZ0AnunxruP7KmSWiS93D-puB7Um0_I,1218
|
|
3
|
+
psengine/_version.py,sha256=uj5AmvJ05gA6z3kL-bIjNnflYtRkjhQM9vBKLGo709w,1167
|
|
4
|
+
psengine/base_http_client.py,sha256=_D35OPDxsOjx3it9r3AyqUon5jNYbssUeqT1FUC8gu4,10944
|
|
5
|
+
psengine/common_models.py,sha256=jlD0rPgrq4jabesY4vCSSFCw9jCJqrz1NHZCi5T2TT0,3558
|
|
6
|
+
psengine/constants.py,sha256=3FwCRdjykbIMldagOOnsWvugv34Nx6LMIJIfNROKIf8,2222
|
|
7
|
+
psengine/endpoints.py,sha256=ciJ8ir9IPbTK4QCnCTph4nQYqBr_JciqPID6FD0B84Y,5450
|
|
8
|
+
psengine/errors.py,sha256=j16R6ivhVzVHFzVZAPmN0SFHWatLoFovKsIGgBgyPNw,2001
|
|
9
|
+
psengine/rf_client.py,sha256=kQhUnHAR40dOUxQzZKtJkKqL5pGXWDJ2Q0vOG2QWFVM,15766
|
|
10
|
+
psengine/analyst_notes/__init__.py,sha256=qTLLjBepUyXbNMU3Lkv6JxJibEmWp4oNcdF5tAeoBXw,1620
|
|
11
|
+
psengine/analyst_notes/constants.py,sha256=74bnXSAv-pSla-MW_znk8KS4U324WmyWft2pa9-XObo,1249
|
|
12
|
+
psengine/analyst_notes/errors.py,sha256=AYosDKdVgwy57shLJhSv-ptP4xXpw5WFrUuuTU3MeJE,1970
|
|
13
|
+
psengine/analyst_notes/helpers.py,sha256=_yoVhGU4Sl9DjjvmNP3xB0d0bLwXMi33oXqkOvWU09k,3769
|
|
14
|
+
psengine/analyst_notes/models.py,sha256=71kRGvbYYjw8PDmI0mjk1JZiGX_fsAuFAAlIGFD_pI8,7880
|
|
15
|
+
psengine/analyst_notes/note.py,sha256=P1w8KgrQlOqgLC8uwmOxKUq1qk3CwW36ftp9ZooFAiQ,5612
|
|
16
|
+
psengine/analyst_notes/note_mgr.py,sha256=fD7631TCQztONusIz6KRq5g8CcJZXcTyFLrTZr45CqM,14843
|
|
17
|
+
psengine/classic_alerts/__init__.py,sha256=lepL7n_agzve88BZWXTm9SKxPyTZ6_ru6GvXJqgKKdg,1482
|
|
18
|
+
psengine/classic_alerts/classic_alert.py,sha256=oQXDfqOpcAP_NyYUuUCshlrUQYwwCqMFAJyy6H8F-b8,10060
|
|
19
|
+
psengine/classic_alerts/classic_alert_mgr.py,sha256=L6Plx9pvJg0oGwrU_GnaH8bvCUlJ3aTm7pcAsRcs6H4,18812
|
|
20
|
+
psengine/classic_alerts/constants.py,sha256=mgwScwVhp_exZJJ4E8TwAQpy-cL2gBvc4aJmy2B4IJw,1520
|
|
21
|
+
psengine/classic_alerts/errors.py,sha256=Q_nPPpjoa7SB5EunXDmT83fKt21_pEVK04N_PDxP594,1985
|
|
22
|
+
psengine/classic_alerts/helpers.py,sha256=EmtDl0NwVxaXLPX_j07OCVXMZXpfj1_Mqh4joUF3VBQ,3630
|
|
23
|
+
psengine/classic_alerts/models.py,sha256=BrPjqTuMyIO6pRo_BErE33vCll2gylDrFsSNGEBlF64,4121
|
|
24
|
+
psengine/classic_alerts/markdown/__init__.py,sha256=_5VWV_JkQlwsJdNCtl_HgSPp8uqgYUqz-I4dRTqVJLs,1145
|
|
25
|
+
psengine/classic_alerts/markdown/markdown.py,sha256=jU_TE4heph0g77pkKAPskG_Z0dYolda6cBgx7yaRpOI,13884
|
|
26
|
+
psengine/collective_insights/__init__.py,sha256=jt3TLqVgBLLxFGmOLhwlksnFE05btd7Nv9LfX8bShkE,1590
|
|
27
|
+
psengine/collective_insights/collective_insights.py,sha256=wU2Lh0E-Qvm7QPFNx8DmQqoWSCm5hitZxtNPiVbq2Ls,5995
|
|
28
|
+
psengine/collective_insights/constants.py,sha256=S-wVDdYSJNM7oii_3PmMOEWK7GBe6XhnOk-dZCs1DPA,2249
|
|
29
|
+
psengine/collective_insights/errors.py,sha256=0PSxQkg-ZP9eK7uEdM60_RE6vk4Dll7CS8dLFMGAyYw,1321
|
|
30
|
+
psengine/collective_insights/insight.py,sha256=4KrMk2VyNnz2hFDTKJA1BG9eInJZtLv5vqkGuvnrhRk,3361
|
|
31
|
+
psengine/collective_insights/models.py,sha256=Z4W95P9NPWyz-SN71T7LP5i6wkzGPckXRKG_W-fkfpY,2876
|
|
32
|
+
psengine/config/__init__.py,sha256=tiMOVTkfJYvwq4aKhDgYEuM2zZjg0_oXZzICUdaJBXk,1233
|
|
33
|
+
psengine/config/config.py,sha256=tCt5rJM-Q_9KiDLBqa7LQYaTfcbP5wc8luGs1U9oc_Q,10928
|
|
34
|
+
psengine/config/errors.py,sha256=mSfyIva4LgpimKwDH0jRExVGc_El-YwuVUTO_gyn0Tk,1314
|
|
35
|
+
psengine/detection/__init__.py,sha256=0BMVvoanJR2sR9khm7TXwAHdVylUI8j-XQMUUOt4NlY,1352
|
|
36
|
+
psengine/detection/detection_mgr.py,sha256=FIlfPRhfN7i_5lGF5LO-TuPLL2NigaShCHM9vOjydAg,5771
|
|
37
|
+
psengine/detection/detection_rule.py,sha256=lxezNq427pJKgCVOGhHdTtGZARJ76YhZurQcLnJWQTY,3519
|
|
38
|
+
psengine/detection/errors.py,sha256=ywvt0HL5taB-vev0271UsqhnwT_oKLsCs6Jt8ftLO1w,1578
|
|
39
|
+
psengine/detection/helpers.py,sha256=3gAKamoBHrsKjRqyYx8IsnvCQ4y7enggsrvcBWIntD0,2631
|
|
40
|
+
psengine/detection/models.py,sha256=F624ydQ0YJpnEv1nv-XBiCKoPm52K4O8VBkBGsYtk8Q,1983
|
|
41
|
+
psengine/enrich/__init__.py,sha256=DRJjh2iAzc0K9SsXLzTX4iP0jWXmC5XjlgxO3Frb7oU,1523
|
|
42
|
+
psengine/enrich/constants.py,sha256=C2x8noT2dvtIN-6DC1q_0KdgA8nXq9DpTw-zxbpf-js,2394
|
|
43
|
+
psengine/enrich/errors.py,sha256=TOWh82BHiK7O958ceMADxNTUxvhmCBJViaL8TRGifpw,1581
|
|
44
|
+
psengine/enrich/lookup.py,sha256=Kek8EYIZCPMrGDMvWCZzzRkfkEZVgWr8BLTbNrMIdP8,12198
|
|
45
|
+
psengine/enrich/lookup_mgr.py,sha256=A-M0tWjwWPiFZoDc6oWJWWyeu4D_tstNIXbskQPfQIE,11969
|
|
46
|
+
psengine/enrich/soar.py,sha256=wHxZDF9u_mE4tZ4LOz677G3v4_NU8t5FEou8Ur29qMY,3715
|
|
47
|
+
psengine/enrich/soar_mgr.py,sha256=SZlBdSSAjKXqyRgpfph3uqhAn5Nw6jNJBIWvpxQC2LQ,7033
|
|
48
|
+
psengine/enrich/models/__init__.py,sha256=_5VWV_JkQlwsJdNCtl_HgSPp8uqgYUqz-I4dRTqVJLs,1145
|
|
49
|
+
psengine/enrich/models/base_enriched_entity.py,sha256=BSLmuZfIgkjzuZhf3jm6z2AC4mtucILmhCxfhQunTMQ,2173
|
|
50
|
+
psengine/enrich/models/lookup.py,sha256=6_Q0SfEA83fg9DEa4bnK9I3FiLJJ7X4sk19ldYgqyAw,8385
|
|
51
|
+
psengine/enrich/models/soar.py,sha256=hS86hYuvTksdtIWMygIVoTQcyCP-VC1M6PadwqbtNj8,4425
|
|
52
|
+
psengine/entity_lists/__init__.py,sha256=uqr-ocoNKYhG165sqLHC5OEcaFKQ6MbaPQL2jdf6338,1334
|
|
53
|
+
psengine/entity_lists/constants.py,sha256=As9Yd5DaP_ZTxubN9rQFu9UcrPC6zt83zRwE62ecnA4,1255
|
|
54
|
+
psengine/entity_lists/entity_list.py,sha256=joVClsjTBIt9Msd5RAkTuoKdeFuifR_PHD5aESYinjg,15662
|
|
55
|
+
psengine/entity_lists/entity_list_mgr.py,sha256=_wOANqY14E1Rb08_j9hfgIad2n6JA066gtiCq38l6tc,7692
|
|
56
|
+
psengine/entity_lists/errors.py,sha256=l2yOXSgYJPVGZJAtBompgPPiNF8mfbXQmcLckdgdqVc,1507
|
|
57
|
+
psengine/entity_lists/models.py,sha256=jDI1GPn-H091k0YWUtj20I_212sx17QJ3dbXQbhkdAE,2829
|
|
58
|
+
psengine/entity_match/__init__.py,sha256=gQCXszK-ZflcEMrnTcSlNnYaSiq9MIzNJl6NuP-UOns,1279
|
|
59
|
+
psengine/entity_match/entity_match.py,sha256=AcuQK44S35pxVkD1uDKI6UEThqnGDLP3qmP71PUfovQ,3416
|
|
60
|
+
psengine/entity_match/entity_match_mgr.py,sha256=mvPPwb4COstH5I8cfSI8wAWOQRaPII0tlor4bMa4R50,8942
|
|
61
|
+
psengine/entity_match/errors.py,sha256=cKSUG25KEBnnxQ5PuLteNiIWUvxlJqB1JTTI-e7yQaI,1312
|
|
62
|
+
psengine/entity_match/models.py,sha256=hlSlWCPDAwgv8oJub4DXqnFXYnZ65FIWcfe_EMPzoFY,1308
|
|
63
|
+
psengine/helpers/__init__.py,sha256=OLKNXYW5hDdCoNX0uRsCzICH8iSXwTslYq0KF5tpUvg,1328
|
|
64
|
+
psengine/helpers/helpers.py,sha256=57oqqiDh2LI1EHhz1i-WrJBwB_MZUFkZlTqL-f6deP0,16371
|
|
65
|
+
psengine/logger/__init__.py,sha256=2JlWQM2SKxCcQAy4teePslDcO3y7q8B3BlkKEVZ1Hok,1210
|
|
66
|
+
psengine/logger/constants.py,sha256=KMQWGzi9Cfs55EFAZZNTfdh8AuMX-XbZrbdLx6p3exg,1846
|
|
67
|
+
psengine/logger/errors.py,sha256=h0HjzCOw2svrasrltQC98LSxc1eKp9z7wHQ9sKrmplU,1290
|
|
68
|
+
psengine/logger/rf_logger.py,sha256=HwCT1VUZxrrQiWmSmWTGesMo8JFy9IK9D3Lvr3GQn3w,5486
|
|
69
|
+
psengine/markdown/__init__.py,sha256=sNtjQy20xOcWYjDIyb7ofqLQfh6G9cU1CIIRQ4b08zI,1287
|
|
70
|
+
psengine/markdown/markdown.py,sha256=HNxs8xipPNEKHqDkBfH2H2ERwTmxygG-CaW18mRJCj0,6304
|
|
71
|
+
psengine/markdown/models.py,sha256=7oVR6wvOJjMhgZqAq9JvdBuhGRl4oocvC9Jd4yfms9w,1304
|
|
72
|
+
psengine/playbook_alerts/__init__.py,sha256=G-J5gzTet_wG6mQQfkYSP1ZkKYjGZX-xBjKcFLDUCcw,1672
|
|
73
|
+
psengine/playbook_alerts/constants.py,sha256=2laFi9829w06hCxa_JzI34ewbqMrlJOpBpDtQu_G1rY,1650
|
|
74
|
+
psengine/playbook_alerts/errors.py,sha256=8qRojM-mLPDAFHy0lEXPzP_BspHjZGbTxcC_25fQ2ag,1746
|
|
75
|
+
psengine/playbook_alerts/helpers.py,sha256=9CnaxfGNTxWwjcPKIsNosY2bDeczByicVpTmaFTYkrA,3693
|
|
76
|
+
psengine/playbook_alerts/mappings.py,sha256=sFYmKASj0pp33BGH8kWez6OpAQmgLRVGJZXiKCMcuIs,2338
|
|
77
|
+
psengine/playbook_alerts/pa_category.py,sha256=cVR4aodVMDyey9H475mdzvajEac6gSHZq3krxfXVehw,1813
|
|
78
|
+
psengine/playbook_alerts/playbook_alert_mgr.py,sha256=Xws4x7xhmKPBMZgO56gDnjWqKl4F2GtobGWzHGpI8Qo,24030
|
|
79
|
+
psengine/playbook_alerts/playbook_alerts.py,sha256=0IKMPYdOc_CcUyqDaT8wYk3httn9bbrJxtYClUB63eg,14267
|
|
80
|
+
psengine/playbook_alerts/markdown/__init__.py,sha256=_5VWV_JkQlwsJdNCtl_HgSPp8uqgYUqz-I4dRTqVJLs,1145
|
|
81
|
+
psengine/playbook_alerts/markdown/markdown.py,sha256=d4Wy780H8wrUeOAZNqZgkxaxIkbeOzD_uykJB6b4TLA,4125
|
|
82
|
+
psengine/playbook_alerts/markdown/markdown_code_repo.py,sha256=7wUzXzzuEgiw0jkUmnoWJQ0rkx7-LMiLKAAiLFP4P80,2963
|
|
83
|
+
psengine/playbook_alerts/markdown/markdown_domain_abuse.py,sha256=AQbKb4xG9yp--wPWFyenNTfQ8qOijfbWWKOBzKQJLwc,5135
|
|
84
|
+
psengine/playbook_alerts/markdown/markdown_identity_exposure.py,sha256=yxly4gpR-fMmRTruP7frmeL8B9lWiJBqKN7JQYn8rsU,6148
|
|
85
|
+
psengine/playbook_alerts/models/__init__.py,sha256=0BYel1GfheIZ0XNGy1XPdwvvGuXxQACrmlaxib7FUH8,1870
|
|
86
|
+
psengine/playbook_alerts/models/common_models.py,sha256=PQWnd6qSgBdmeSE4woPUJ5lI6TEOJD7MKmy23XZz1e4,1237
|
|
87
|
+
psengine/playbook_alerts/models/panel_log.py,sha256=LAtlqyKRk2yDHj-xpzoey5LHJrAKJNP-dSMgfjJ7H1M,9384
|
|
88
|
+
psengine/playbook_alerts/models/panel_status.py,sha256=YFqFgZHJGg_MMg8PkVZPzGc_tXnJct9iOkwSzn558r8,2795
|
|
89
|
+
psengine/playbook_alerts/models/pba_code_repo_leak.py,sha256=vVswEwVypZYji-VMaUkr50xAOf6YRqF5btOr6wVvf6s,2170
|
|
90
|
+
psengine/playbook_alerts/models/pba_cyber_vulnerability.py,sha256=Rb5wrGCF17mH41-lEycJGencV_d1NHDWNbN_UTlajnI,2259
|
|
91
|
+
psengine/playbook_alerts/models/pba_domain_abuse.py,sha256=o-zofekpyxJ7wRTL1UyT6zMZoRebyFnUkU8-lzDhC9A,5118
|
|
92
|
+
psengine/playbook_alerts/models/pba_identity_exposures.py,sha256=bQNNXLPrruJwtq41h7_eEyTEsjFjjamzx8Z3n-dRQ0A,3532
|
|
93
|
+
psengine/playbook_alerts/models/pba_third_party_risk.py,sha256=zoK0lxaTK6pJKM3-BQ81a0h_pQAuxV_A5zG0G_ncuNI,3376
|
|
94
|
+
psengine/playbook_alerts/models/search_endpoint.py,sha256=fj6vIaT3FIzbpcm3DpMeOYWV32rGzx2qDZdn15rJYVo,2525
|
|
95
|
+
psengine/risklists/__init__.py,sha256=xohSIx-1HxI6GOhRPs5tapgNGO-XGxy8Xn9aPI83ueQ,1312
|
|
96
|
+
psengine/risklists/constants.py,sha256=S93uHFccyRV30i1dZ-H4PGsoeKDWC_1-XxPbRJShvOs,1185
|
|
97
|
+
psengine/risklists/errors.py,sha256=tlAub65rpwyCawy_j8rk-4qm-rLiUG1IgyG9Wlbf3vY,1353
|
|
98
|
+
psengine/risklists/models.py,sha256=zl0q9pf_IDqDxue3iZtY3uGTXRqgl1A6O4XpX-jJc-0,3095
|
|
99
|
+
psengine/risklists/risklist_mgr.py,sha256=ICwEeohOexQNFiDpcbJ4u8e2b_WuOFNVl4ISjbLwtP4,6160
|
|
100
|
+
psengine/stix2/__init__.py,sha256=Hsb89u_FyD_NBNC5HT_e-jBOJckDg5zXyibD7myBjPc,1580
|
|
101
|
+
psengine/stix2/base_stix_entity.py,sha256=qghRiNOGRBhVMMn_KNzxUuN0bZUpIdiNanMOoih8FDU,2611
|
|
102
|
+
psengine/stix2/complex_entity.py,sha256=sqWi6Axg4XQslFAI0NKXLHsj9WnRrH82WGnzFoRPjcU,13728
|
|
103
|
+
psengine/stix2/constants.py,sha256=nxhAdwfep4-1-yBYOM0Kl0ZUytDqTMSdJ4NLCVNdayU,3207
|
|
104
|
+
psengine/stix2/enriched_indicator.py,sha256=eFfQnyf-5cVHHwAot3IAg9frGAAshs8tTfocEs-2hw4,10316
|
|
105
|
+
psengine/stix2/errors.py,sha256=peIaG_oLqtn-lVaLcqicuZuCoY9FJjZoXVbnSOD795A,1475
|
|
106
|
+
psengine/stix2/helpers.py,sha256=FRLXiMMhLcqKzGLmis-Uw5yyQrbLjUnqVM0oyVaglAM,3064
|
|
107
|
+
psengine/stix2/rf_bundle.py,sha256=tb8MJQYZC9B2y6vPO9MQi351CxGdpqQ_-cU8BOoczPI,8739
|
|
108
|
+
psengine/stix2/simple_entity.py,sha256=sqZvgwLN6slzTqJSRn3RXpBv0stj33TpqLpsy_JkBss,5341
|
|
109
|
+
psengine/stix2/util.py,sha256=CqPjY5pGZ2MzUrdFHZfslv2tSUSHMRTsYleq3jtYkkQ,2484
|
|
110
|
+
psengine-2.0.4.dist-info/licenses/LICENSE,sha256=TVndR2xDgh4CIt3zBnOlTWoVYm2YQsBVrYgvTppmNkg,1092
|
|
111
|
+
psengine-2.0.4.dist-info/METADATA,sha256=ne6FVKmdGYQSFdLM7rmmw0UIamF0kk2WpEfU4IMf19M,7420
|
|
112
|
+
psengine-2.0.4.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
113
|
+
psengine-2.0.4.dist-info/entry_points.txt,sha256=uwDpwt0qM7cTygFw5iOKzqhur9rE-ejx8hkeiJ3_tFk,47
|
|
114
|
+
psengine-2.0.4.dist-info/top_level.txt,sha256=oxOppB0mENI7Z2EbHfN9G2gHlg08qJAxCdROl35ac_k,9
|
|
115
|
+
psengine-2.0.4.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 RecordedFuture-ProfessionalServices
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
psengine
|