psengine 2.0.6__tar.gz → 2.0.7__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.0.6 → psengine-2.0.7}/PKG-INFO +1 -1
- {psengine-2.0.6 → psengine-2.0.7}/psengine/_version.py +1 -1
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/markdown/markdown.py +2 -1
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_lists/entity_list.py +2 -1
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/__init__.py +3 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/constants.py +28 -5
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/errors.py +4 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/helpers.py +5 -3
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown.py +5 -23
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown_code_repo.py +1 -1
- psengine-2.0.7/psengine/playbook_alerts/markdown/markdown_malware_report.py +66 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_malware_report.py +6 -6
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/pa_category.py +0 -1
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/playbook_alert_mgr.py +190 -195
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/playbook_alerts.py +13 -19
- {psengine-2.0.6 → psengine-2.0.7}/psengine/rf_client.py +37 -16
- {psengine-2.0.6 → psengine-2.0.7}/psengine/risklists/risklist_mgr.py +13 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine.egg-info/PKG-INFO +1 -1
- {psengine-2.0.6 → psengine-2.0.7}/psengine.egg-info/SOURCES.txt +1 -1
- {psengine-2.0.6 → psengine-2.0.7}/pyproject.toml +1 -5
- psengine-2.0.6/psengine.egg-info/entry_points.txt +0 -2
- {psengine-2.0.6 → psengine-2.0.7}/LICENSE +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/README.rst +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/_sdk_id.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/helpers.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/markdown.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/note.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/analyst_notes/note_mgr.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/base_http_client.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/classic_alert.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/classic_alert_mgr.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/helpers.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/markdown/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/classic_alerts/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/collective_insights/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/collective_insights/collective_insights.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/collective_insights/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/collective_insights/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/collective_insights/insight.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/collective_insights/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/common_models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/config/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/config/config.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/config/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/detection/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/detection/detection_mgr.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/detection/detection_rule.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/detection/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/detection/helpers.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/detection/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/endpoints.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/lookup.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/lookup_mgr.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/models/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/models/base_enriched_entity.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/models/lookup.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/models/soar.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/soar.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/enrich/soar_mgr.py +3 -3
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_lists/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_lists/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_lists/entity_list_mgr.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_lists/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_lists/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_match/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_match/entity_match.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_match/entity_match_mgr.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_match/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/entity_match/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/helpers/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/helpers/helpers.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/logger/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/logger/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/logger/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/logger/rf_logger.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/markdown/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/markdown/markdown.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/markdown/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/mappings.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown_cyber_vulnerability.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown_domain_abuse.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown_geopolitics_facility.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown_identity_exposure.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/markdown/markdown_third_party_risk.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/common_models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/panel_log.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/panel_status.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_code_repo_leak.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_cyber_vulnerability.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_domain_abuse.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_geopolitics_facility.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_identity_exposures.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/pba_third_party_risk.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/playbook_alerts/models/search_endpoint.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/risklists/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/risklists/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/risklists/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/risklists/models.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/__init__.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/base_stix_entity.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/complex_entity.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/constants.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/enriched_indicator.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/errors.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/helpers.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/rf_bundle.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/simple_entity.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine/stix2/util.py +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine.egg-info/dependency_links.txt +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine.egg-info/requires.txt +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/psengine.egg-info/top_level.txt +0 -0
- {psengine-2.0.6 → psengine-2.0.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: psengine
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.7
|
|
4
4
|
Summary: psengine is a simple, yet elegant, library for rapid development of integrations with Recorded Future.
|
|
5
5
|
Author-email: Moise Medici <moise.medici@recordedfuture.com>, Patrick Kinsella <patrick.kinsella@recordedfuture.com>, Ernest Bartosevic <ernest.bartosevic@recordedfuture.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -94,7 +94,8 @@ def _process_hit_fragment(
|
|
|
94
94
|
content.append(f'{blockquote(fragment)}\n')
|
|
95
95
|
else:
|
|
96
96
|
content.append(
|
|
97
|
-
|
|
97
|
+
'_Reference text is missing, check the Recorded Future '
|
|
98
|
+
f'{link("Portal", str(classic_alert.url.portal))} for more information._\n'
|
|
98
99
|
)
|
|
99
100
|
|
|
100
101
|
if include_triggered_by:
|
|
@@ -314,7 +314,8 @@ class EntityList(RFBaseModel):
|
|
|
314
314
|
response = self.rf_client.request('get', url)
|
|
315
315
|
validated_status = ListStatusOut.model_validate(response.json())
|
|
316
316
|
self.log.debug(
|
|
317
|
-
f"List '{self.name}' status: {validated_status.status},
|
|
317
|
+
f"List '{self.name}' status: {validated_status.status}, "
|
|
318
|
+
f'entities: {validated_status.size}'
|
|
318
319
|
)
|
|
319
320
|
|
|
320
321
|
return validated_status
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
# accessed from any third party API. #
|
|
12
12
|
##############################################################################################
|
|
13
13
|
|
|
14
|
+
from .constants import PBA_WITH_IMAGES_INST
|
|
14
15
|
from .errors import (
|
|
16
|
+
PlaybookAlertBulkFetchError,
|
|
15
17
|
PlaybookAlertError,
|
|
16
18
|
PlaybookAlertFetchError,
|
|
17
19
|
PlaybookAlertRetrieveImageError,
|
|
@@ -27,6 +29,7 @@ from .playbook_alerts import (
|
|
|
27
29
|
PBA_DomainAbuse,
|
|
28
30
|
PBA_Generic,
|
|
29
31
|
PBA_IdentityNovelExposure,
|
|
32
|
+
PBA_MalwareReport,
|
|
30
33
|
PBA_ThirdPartyRisk,
|
|
31
34
|
PreviewAlertOut,
|
|
32
35
|
SearchIn,
|
|
@@ -10,30 +10,53 @@
|
|
|
10
10
|
# for having all necessary licenses, permissions, rights, and/or consents to any data #
|
|
11
11
|
# accessed from any third party API. #
|
|
12
12
|
##############################################################################################
|
|
13
|
-
from
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Literal, Union
|
|
14
15
|
|
|
16
|
+
from ..constants import ROOT_DIR
|
|
17
|
+
from ..playbook_alerts.pa_category import PACategory
|
|
15
18
|
from .playbook_alerts import (
|
|
16
19
|
PBA_CodeRepoLeakage,
|
|
17
20
|
PBA_CyberVulnerability,
|
|
18
21
|
PBA_DomainAbuse,
|
|
19
|
-
PBA_Generic,
|
|
20
22
|
PBA_GeopoliticsFacility,
|
|
21
23
|
PBA_IdentityNovelExposure,
|
|
24
|
+
PBA_MalwareReport,
|
|
22
25
|
PBA_ThirdPartyRisk,
|
|
23
26
|
)
|
|
24
27
|
|
|
25
28
|
STATUS_PANEL_NAME = 'status'
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
|
|
30
|
+
DEFAULT_ALERTS_OUTPUT_DIR = Path(ROOT_DIR) / 'playbook_alerts'
|
|
31
|
+
PLAYBOOK_ALERTS_OUTPUT_FNAME = 'rf_playbook_alerts_'
|
|
32
|
+
|
|
28
33
|
|
|
29
34
|
PLAYBOOK_ALERT_TYPE = Union[
|
|
30
|
-
PBA_Generic,
|
|
31
35
|
PBA_CodeRepoLeakage,
|
|
32
36
|
PBA_CyberVulnerability,
|
|
33
37
|
PBA_DomainAbuse,
|
|
34
38
|
PBA_IdentityNovelExposure,
|
|
35
39
|
PBA_ThirdPartyRisk,
|
|
40
|
+
PBA_GeopoliticsFacility,
|
|
41
|
+
PBA_MalwareReport,
|
|
36
42
|
]
|
|
43
|
+
PLAYBOOK_ALERT_INST = (
|
|
44
|
+
PBA_CodeRepoLeakage,
|
|
45
|
+
PBA_CyberVulnerability,
|
|
46
|
+
PBA_DomainAbuse,
|
|
47
|
+
PBA_IdentityNovelExposure,
|
|
48
|
+
PBA_ThirdPartyRisk,
|
|
49
|
+
PBA_GeopoliticsFacility,
|
|
50
|
+
PBA_MalwareReport,
|
|
51
|
+
)
|
|
52
|
+
|
|
37
53
|
|
|
38
54
|
PBA_WITH_IMAGES_TYPE = Union[PBA_DomainAbuse, PBA_GeopoliticsFacility]
|
|
55
|
+
PBA_WITH_IMAGES_VALIDATOR = Union[
|
|
56
|
+
Literal[PACategory.DOMAIN_ABUSE.value], Literal[PACategory.GEOPOLITICS_FACILITY.value]
|
|
57
|
+
]
|
|
39
58
|
PBA_WITH_IMAGES_INST = (PBA_DomainAbuse, PBA_GeopoliticsFacility)
|
|
59
|
+
|
|
60
|
+
ALERTS_PER_PAGE = 50
|
|
61
|
+
|
|
62
|
+
BULK_LOOKUP_BATCH_SIZE = 200
|
|
@@ -31,5 +31,9 @@ class PlaybookAlertFetchError(PlaybookAlertError):
|
|
|
31
31
|
"""Error raised when playbook alert fetch fails."""
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class PlaybookAlertBulkFetchError(PlaybookAlertError):
|
|
35
|
+
"""Error raised when playbook alert bulk fetch fails."""
|
|
36
|
+
|
|
37
|
+
|
|
34
38
|
class PlaybookAlertRetrieveImageError(PlaybookAlertError):
|
|
35
39
|
"""Error raised when playbook alert image fetch fails."""
|
|
@@ -16,7 +16,7 @@ from typing import Union
|
|
|
16
16
|
|
|
17
17
|
from ..errors import WriteFileError
|
|
18
18
|
from ..helpers import OSHelpers, debug_call
|
|
19
|
-
from .constants import
|
|
19
|
+
from .constants import DEFAULT_ALERTS_OUTPUT_DIR
|
|
20
20
|
from .playbook_alerts import PBA_DomainAbuse
|
|
21
21
|
|
|
22
22
|
LOG = logging.getLogger('psengine.playbook_alerts.helpers')
|
|
@@ -25,7 +25,7 @@ LOG = logging.getLogger('psengine.playbook_alerts.helpers')
|
|
|
25
25
|
@debug_call
|
|
26
26
|
def save_pba_images(
|
|
27
27
|
playbook_alerts: Union[PBA_DomainAbuse, list[PBA_DomainAbuse]],
|
|
28
|
-
output_directory: str =
|
|
28
|
+
output_directory: str = DEFAULT_ALERTS_OUTPUT_DIR,
|
|
29
29
|
) -> None:
|
|
30
30
|
"""Save Domain Abuse images/screenshots to disk as a .png file.
|
|
31
31
|
|
|
@@ -56,7 +56,9 @@ def save_pba_images(
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def _save_image(
|
|
59
|
-
file_name: str,
|
|
59
|
+
file_name: str,
|
|
60
|
+
image_bytes: bytes,
|
|
61
|
+
output_directory: Union[str, Path] = DEFAULT_ALERTS_OUTPUT_DIR,
|
|
60
62
|
) -> None:
|
|
61
63
|
"""Save image to disk as a .png file.
|
|
62
64
|
|
|
@@ -17,13 +17,13 @@ from ...constants import TIMESTAMP_STR, TRUNCATE_COMMENT
|
|
|
17
17
|
from ...markdown import (
|
|
18
18
|
MarkdownMaker,
|
|
19
19
|
)
|
|
20
|
-
from ..models.common_models import ResolvedEntity
|
|
21
20
|
from ..pa_category import PACategory
|
|
22
21
|
from .markdown_code_repo import _code_repo_markdown
|
|
23
22
|
from .markdown_cyber_vulnerability import _cyber_vulnerability_markdown
|
|
24
23
|
from .markdown_domain_abuse import _domain_abuse_markdown
|
|
25
24
|
from .markdown_geopolitics_facility import _geopolitics_facility_markdown
|
|
26
25
|
from .markdown_identity_exposure import _identity_exposure_markdown
|
|
26
|
+
from .markdown_malware_report import _malware_report_markdown
|
|
27
27
|
from .markdown_third_party_risk import _third_party_risk_markdown
|
|
28
28
|
|
|
29
29
|
PORTAL_URL = 'https://app.recordedfuture.com/portal/playbook-alerts/{}'
|
|
@@ -37,6 +37,7 @@ MARKDOWN_BY_PBA_TYPE = {
|
|
|
37
37
|
PACategory.THIRD_PARTY_RISK.value: _third_party_risk_markdown,
|
|
38
38
|
PACategory.GEOPOLITICS_FACILITY.value: _geopolitics_facility_markdown,
|
|
39
39
|
PACategory.CYBER_VULNERABILITY.value: _cyber_vulnerability_markdown,
|
|
40
|
+
PACategory.MALWARE_REPORT.value: _malware_report_markdown,
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
|
|
@@ -56,27 +57,8 @@ def _generic_pba_summary(pba, md_maker: MarkdownMaker):
|
|
|
56
57
|
md_maker.add_section('Summary', general_info)
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
0,
|
|
62
|
-
md_maker.validate_section(
|
|
63
|
-
'Warning',
|
|
64
|
-
[
|
|
65
|
-
(
|
|
66
|
-
'> 🚨 The markdown of this alert is not fully supported yet. '
|
|
67
|
-
'For full details check the portal 🚨'
|
|
68
|
-
)
|
|
69
|
-
],
|
|
70
|
-
),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
if targets := pba.panel_status.targets:
|
|
74
|
-
if any(isinstance(t, str) for t in targets):
|
|
75
|
-
md_maker.add_section('Targets', ', '.join(t for t in targets))
|
|
76
|
-
elif any(isinstance(t, ResolvedEntity) for t in targets):
|
|
77
|
-
md_maker.add_section('Targets', [t.name for t in targets])
|
|
78
|
-
|
|
79
|
-
return md_maker.format_output()
|
|
60
|
+
def _unsupported_pba(pba, *args, **kwargs) -> str: # noqa: ARG001
|
|
61
|
+
raise NotImplementedError(f'No markdown function for playbook alert category: {pba.category}')
|
|
80
62
|
|
|
81
63
|
|
|
82
64
|
def _markdown_playbook_alert(
|
|
@@ -97,7 +79,7 @@ def _markdown_playbook_alert(
|
|
|
97
79
|
)
|
|
98
80
|
_generic_pba_summary(playbook_alert, md_maker)
|
|
99
81
|
|
|
100
|
-
if markdown := MARKDOWN_BY_PBA_TYPE.get(playbook_alert.category,
|
|
82
|
+
if markdown := MARKDOWN_BY_PBA_TYPE.get(playbook_alert.category, _unsupported_pba)(
|
|
101
83
|
playbook_alert, md_maker, html_tags, extra_context
|
|
102
84
|
):
|
|
103
85
|
return markdown
|
|
@@ -20,7 +20,7 @@ from ...constants import TIMESTAMP_STR
|
|
|
20
20
|
from ...markdown import MarkdownMaker, clean_text, divider, html_textarea
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
|
-
from
|
|
23
|
+
from ...playbook_alerts.playbook_alerts import PBA_CodeRepoLeakage
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _add_repository(pba, md_maker: MarkdownMaker):
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
from markdown_strings import bold, header
|
|
17
|
+
|
|
18
|
+
from ...markdown import MarkdownMaker, divider
|
|
19
|
+
from ...markdown.markdown import html_collapsible
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ...playbook_alerts.playbook_alerts import PBA_MalwareReport
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _add_hashes(pba: 'PBA_MalwareReport', md_maker: MarkdownMaker, html_tags: bool):
|
|
26
|
+
matched_hashes = []
|
|
27
|
+
|
|
28
|
+
detected_malwares = ', '.join(
|
|
29
|
+
[f'{m.name.upper()} ({m.count})' for m in pba.panel_evidence_summary.detected_malwares]
|
|
30
|
+
)
|
|
31
|
+
if detected_malwares:
|
|
32
|
+
matched_hashes.append(bold('Detected Malwares') + f' {detected_malwares} ')
|
|
33
|
+
|
|
34
|
+
sorted_hashes = sorted(
|
|
35
|
+
pba.panel_evidence_summary.matched_hashes, key=lambda h: h.risk_score, reverse=True
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
for _, _hash in enumerate(sorted_hashes):
|
|
39
|
+
matched_hashes.append(divider())
|
|
40
|
+
hash_header = f'{header((f"{_hash.sha256} ({_hash.risk_score})"), 5)}'
|
|
41
|
+
matched_hashes.append(hash_header)
|
|
42
|
+
|
|
43
|
+
sandbox_reports = []
|
|
44
|
+
for report in _hash.report_overviews:
|
|
45
|
+
report_header = f'{header(report.report_id.split("-", 1)[1], 6)} '
|
|
46
|
+
score = f'{bold("Sandbox Score:")} {report.sandbox_score} '
|
|
47
|
+
tags = f'{bold("Tags:")} {", ".join(report.tags).upper()} '
|
|
48
|
+
sandbox_reports.extend([report_header, score, tags])
|
|
49
|
+
|
|
50
|
+
if html_tags:
|
|
51
|
+
html = html_collapsible('Sandbox Reports', '\n\n' + '\n'.join(sandbox_reports) + '\n')
|
|
52
|
+
matched_hashes.append(html)
|
|
53
|
+
else:
|
|
54
|
+
matched_hashes.extend(sandbox_reports)
|
|
55
|
+
|
|
56
|
+
md_maker.add_section(f'Matched Hashes ({len(sorted_hashes)})', matched_hashes)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _malware_report_markdown(
|
|
60
|
+
pba: 'PBA_MalwareReport',
|
|
61
|
+
md_maker: MarkdownMaker,
|
|
62
|
+
html_tags: bool,
|
|
63
|
+
*args, # noqa: ARG001
|
|
64
|
+
) -> str:
|
|
65
|
+
_add_hashes(pba, md_maker, html_tags)
|
|
66
|
+
return md_maker.format_output()
|
|
@@ -47,9 +47,9 @@ class SandboxScore(RFBaseModel):
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class MalwareReportPanelEvidence(RFBaseModel):
|
|
50
|
-
notification_title: str
|
|
51
|
-
report_limit_reached: bool
|
|
52
|
-
number_of_reports: int
|
|
53
|
-
matched_hashes: list[MatchedHash]
|
|
54
|
-
detected_malwares: list[DetectedMalware]
|
|
55
|
-
sandbox_scores: list[SandboxScore]
|
|
50
|
+
notification_title: Optional[str] = None
|
|
51
|
+
report_limit_reached: Optional[bool] = None
|
|
52
|
+
number_of_reports: Optional[int] = None
|
|
53
|
+
matched_hashes: Optional[list[MatchedHash]] = []
|
|
54
|
+
detected_malwares: Optional[list[DetectedMalware]] = []
|
|
55
|
+
sandbox_scores: Optional[list[SandboxScore]] = []
|