psengine 2.11.0__tar.gz → 2.12.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.11.0 → psengine-2.12.0}/PKG-INFO +5 -1
- {psengine-2.11.0 → psengine-2.12.0}/README.md +4 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/note_mgr.py +2 -2
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/soar_mgr.py +3 -9
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_lists/entity_list.py +5 -3
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_match/entity_match_mgr.py +2 -2
- {psengine-2.11.0 → psengine-2.12.0}/psengine/helpers/__init__.py +1 -0
- psengine-2.12.0/psengine/helpers/validation.py +111 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/identity_mgr.py +7 -7
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_history/risk_history_mgr.py +2 -2
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_rules/risk_rule_mgr.py +2 -2
- {psengine-2.11.0 → psengine-2.12.0}/psengine/threat_maps/threat_map_mgr.py +4 -4
- {psengine-2.11.0 → psengine-2.12.0}/pyproject.toml +1 -1
- {psengine-2.11.0 → psengine-2.12.0}/pyproject.toml.orig +1 -1
- {psengine-2.11.0 → psengine-2.12.0}/psengine/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/_sdk_id.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/markdown.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/analyst_notes/note.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/asi.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/asi_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/client.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/asi/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/base_http_client.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/classic_alert.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/classic_alert_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/markdown/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/markdown/markdown.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/classic_alerts/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/collective_insights/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/collective_insights/collective_insights.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/collective_insights/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/collective_insights/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/collective_insights/insight.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/collective_insights/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/common_models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/config/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/config/config.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/config/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/detection/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/detection/detection_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/detection/detection_rule.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/detection/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/detection/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/detection/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/endpoints.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/lookup.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/lookup_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/models/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/models/base_enriched_entity.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/models/lookup.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/models/soar.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/enrich/soar.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_lists/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_lists/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_lists/entity_list_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_lists/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_lists/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_match/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_match/entity_match.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_match/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/entity_match/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/fusion/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/fusion/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/fusion/fusion_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/fusion/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/helpers/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/identity.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/models/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/models/common_models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/models/detections.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/models/incident_report.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/identity/models/lookup.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/links/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/links/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/links/links.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/links/links_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/links/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/logger/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/logger/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/logger/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/logger/rf_logger.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/auto_sigma_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/auto_yara_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/malware_intel.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/malware_intel_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/malware_intel/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/markdown/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/markdown/markdown.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/markdown/markdown_strings.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/markdown/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/mappings.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_code_repo.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_compromised_bank_checks.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_cyber_vulnerability.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_domain_abuse.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_geopolitics_facility.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_identity_exposure.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_malicious_sites.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_malware_report.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_third_party_risk.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/common_models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/code_repo.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/common.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/domain_abuse.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/generic.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/geopolitics.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/malicious_sites.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/panel_log_v2.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/tpr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/vulnerability.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_status.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_code_repo_leak.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_compromised_bank_checks.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_cyber_vulnerability.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_domain_abuse.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_geopolitics_facility.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_identity_exposures.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_malicious_sites.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_malware_report.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_third_party_risk.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/search_endpoint.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/pa_category.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/playbook_alert_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/playbook_alerts.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/rf_client.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_history/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_history/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_history/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_rules/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_rules/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_rules/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risk_rules/risk_rule.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risklists/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risklists/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risklists/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risklists/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/risklists/risklist_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/client.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/models/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/models/analysis.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/models/behavioral_report.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/models/overview_report.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/models/static_report.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/sandbox.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/sandbox/sandbox_mgr.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/base_stix_entity.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/complex_entity.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/constants.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/enriched_indicator.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/helpers.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/rf_bundle.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/simple_entity.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/stix2/util.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/threat_maps/__init__.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/threat_maps/errors.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/threat_maps/models.py +0 -0
- {psengine-2.11.0 → psengine-2.12.0}/psengine/threat_maps/threat_map.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: psengine
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.0
|
|
4
4
|
Summary: psengine is a simple, yet elegant, library for rapid development of integrations with Recorded Future.
|
|
5
5
|
Keywords: API,Recorded Future,Cyber Security Engineering,Threat Intelligence
|
|
6
6
|
Author: Moise Medici, Patrick Kinsella, Ernest Bartosevic
|
|
@@ -93,3 +93,7 @@ PSEngine has been made public from our internal version 2.0.4. Previous versions
|
|
|
93
93
|
|
|
94
94
|
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.
|
|
95
95
|
|
|
96
|
+
## Contributing
|
|
97
|
+
|
|
98
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, code standards, testing conventions, and the CI workflow.
|
|
99
|
+
|
|
@@ -60,3 +60,7 @@ PSEngine has been made public from our internal version 2.0.4. Previous versions
|
|
|
60
60
|
|
|
61
61
|
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.
|
|
62
62
|
|
|
63
|
+
## Contributing
|
|
64
|
+
|
|
65
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, code standards, testing conventions, and the CI workflow.
|
|
66
|
+
|
|
@@ -28,7 +28,7 @@ from ..endpoints import (
|
|
|
28
28
|
EP_ANALYST_NOTE_PUBLISH,
|
|
29
29
|
EP_ANALYST_NOTE_SEARCH,
|
|
30
30
|
)
|
|
31
|
-
from ..helpers import debug_call
|
|
31
|
+
from ..helpers import debug_call, validate_list
|
|
32
32
|
from ..helpers.helpers import connection_exceptions
|
|
33
33
|
from ..rf_client import RFClient
|
|
34
34
|
from .constants import NOTES_PER_PAGE
|
|
@@ -358,4 +358,4 @@ class AnalystNoteMgr:
|
|
|
358
358
|
max_results=max_results,
|
|
359
359
|
)
|
|
360
360
|
|
|
361
|
-
return
|
|
361
|
+
return validate_list(AnalystNote, response, id_path='id', log=self.log)
|
|
@@ -19,7 +19,7 @@ from pydantic import validate_call
|
|
|
19
19
|
from typing_extensions import Doc
|
|
20
20
|
|
|
21
21
|
from ..endpoints import EP_SOAR_ENRICHMENT
|
|
22
|
-
from ..helpers import MultiThreadingHelper, connection_exceptions, debug_call
|
|
22
|
+
from ..helpers import MultiThreadingHelper, connection_exceptions, debug_call, validate_list
|
|
23
23
|
from ..rf_client import RFClient
|
|
24
24
|
from .constants import SOAR_POST_ROWS
|
|
25
25
|
from .errors import EnrichmentSoarError
|
|
@@ -158,11 +158,5 @@ class SoarMgr:
|
|
|
158
158
|
res = self.rf_client.request('post', EP_SOAR_ENRICHMENT, data=data.json()).json()['data'][
|
|
159
159
|
'results'
|
|
160
160
|
]
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
for d in res:
|
|
164
|
-
content = SOAREnrichedEntity.model_validate(d)
|
|
165
|
-
entity = content.entity.name
|
|
166
|
-
result.append(SOAREnrichOut(entity=entity, is_enriched=True, content=content))
|
|
167
|
-
|
|
168
|
-
return result
|
|
161
|
+
contents = validate_list(SOAREnrichedEntity, res, id_path='entity.name', log=self.log)
|
|
162
|
+
return [SOAREnrichOut(entity=c.entity.name, is_enriched=True, content=c) for c in contents]
|
|
@@ -24,7 +24,7 @@ from ..common_models import IdNameType, RFBaseModel
|
|
|
24
24
|
from ..constants import TIMESTAMP_STR
|
|
25
25
|
from ..endpoints import EP_LIST, EP_LIST_ENTITIES_WITH_TAGS, EP_LIST_ENTITY_TAGS
|
|
26
26
|
from ..entity_match import EntityMatchMgr, MatchApiError
|
|
27
|
-
from ..helpers import debug_call
|
|
27
|
+
from ..helpers import debug_call, validate_list
|
|
28
28
|
from ..helpers.helpers import connection_exceptions
|
|
29
29
|
from ..rf_client import RFClient
|
|
30
30
|
from .constants import ADD_OP, ERROR_NAME, IS_READY_INCREMENT, REMOVE_OP, UNCHANGED_NAME
|
|
@@ -286,7 +286,7 @@ class EntityList(ListInfoOut):
|
|
|
286
286
|
"""
|
|
287
287
|
url = EP_LIST + '/' + self.id_ + '/entities'
|
|
288
288
|
response = self.rf_client.request('get', url)
|
|
289
|
-
return
|
|
289
|
+
return validate_list(ListEntity, response.json(), id_path='entity.name', log=self.log)
|
|
290
290
|
|
|
291
291
|
@debug_call
|
|
292
292
|
@connection_exceptions(ignore_status_code=[], exception_to_raise=ListApiError)
|
|
@@ -319,7 +319,9 @@ class EntityList(ListInfoOut):
|
|
|
319
319
|
"""
|
|
320
320
|
url = EP_LIST_ENTITIES_WITH_TAGS.format(self.id_)
|
|
321
321
|
response = self.rf_client.request('get', url)
|
|
322
|
-
return
|
|
322
|
+
return validate_list(
|
|
323
|
+
ListEntityWithTags, response.json(), id_path='entity.name', log=self.log
|
|
324
|
+
)
|
|
323
325
|
|
|
324
326
|
@debug_call
|
|
325
327
|
@validate_call
|
|
@@ -21,7 +21,7 @@ from typing_extensions import Doc
|
|
|
21
21
|
from ..common_models import IdNameType
|
|
22
22
|
from ..constants import DEFAULT_LIMIT, DEFAULT_MAX_WORKERS
|
|
23
23
|
from ..endpoints import EP_ENTITY_LOOKUP, EP_ENTITY_MATCH
|
|
24
|
-
from ..helpers import MultiThreadingHelper, connection_exceptions, debug_call
|
|
24
|
+
from ..helpers import MultiThreadingHelper, connection_exceptions, debug_call, validate_list
|
|
25
25
|
from ..rf_client import RFClient
|
|
26
26
|
from .entity_match import EntityLookup, EntityMatchIn, ResolvedEntity
|
|
27
27
|
from .errors import MatchApiError
|
|
@@ -63,7 +63,7 @@ class EntityMatchMgr:
|
|
|
63
63
|
|
|
64
64
|
request_body = EntityMatchIn(name=entity_name, type=entity_type, limit=limit)
|
|
65
65
|
response = self.rf_client.request('post', EP_ENTITY_MATCH, data=request_body.json())
|
|
66
|
-
response =
|
|
66
|
+
response = validate_list(IdNameType, response.json(), id_path='name', log=self.log)
|
|
67
67
|
return (
|
|
68
68
|
list({ResolvedEntity(entity=d.name, is_found=bool(d.id_), content=d) for d in response})
|
|
69
69
|
if response
|
|
@@ -0,0 +1,111 @@
|
|
|
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 logging
|
|
15
|
+
from collections.abc import Iterable
|
|
16
|
+
from typing import Annotated, Any, TypeVar
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, TypeAdapter, ValidationError
|
|
19
|
+
from typing_extensions import Doc
|
|
20
|
+
|
|
21
|
+
LOG = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
T = TypeVar('T', bound=BaseModel)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate_list(
|
|
27
|
+
model_cls: Annotated[type[T], Doc('Pydantic model each item should validate against.')],
|
|
28
|
+
items: Annotated[
|
|
29
|
+
Iterable[Any], Doc('Raw items to validate, typically dicts from an API response.')
|
|
30
|
+
],
|
|
31
|
+
id_path: Annotated[
|
|
32
|
+
str | None,
|
|
33
|
+
Doc(
|
|
34
|
+
'Dotted path into each raw item used to build a friendly identifier for log lines '
|
|
35
|
+
"and exception notes, e.g. `'entity.name'`. If unresolvable for a given item, only "
|
|
36
|
+
'the index is reported.'
|
|
37
|
+
),
|
|
38
|
+
] = None,
|
|
39
|
+
log: Annotated[
|
|
40
|
+
logging.Logger | None,
|
|
41
|
+
Doc("Logger for the warnings. Defaults to this module's logger."),
|
|
42
|
+
] = None,
|
|
43
|
+
) -> Annotated[list[T], Doc('The list of validated `model_cls` instances.')]:
|
|
44
|
+
"""Validate a list of dicts against `model_cls`.
|
|
45
|
+
|
|
46
|
+
Uses pydantic's `TypeAdapter` so a single validation pass collects all per-item errors with an
|
|
47
|
+
index in their `loc`.
|
|
48
|
+
|
|
49
|
+
Before re-raising, each failing item is (a) logged individually as a warning and (b) on
|
|
50
|
+
Python 3.11+ attached to the exception via `add_note`, so the entity identifier shows up in
|
|
51
|
+
the traceback too. On Python 3.10 the note step is skipped since `BaseException.add_note`
|
|
52
|
+
is not available.
|
|
53
|
+
|
|
54
|
+
If `id_path` is provided, a human-readable identifier is extracted from the raw dict
|
|
55
|
+
(e.g. `entity.name`); otherwise only the index is reported.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
pydantic.ValidationError: If any item fails validation. Unchanged
|
|
59
|
+
from `[model_cls.model_validate(x) for x in items]`.
|
|
60
|
+
"""
|
|
61
|
+
if not isinstance(items, list):
|
|
62
|
+
items = list(items)
|
|
63
|
+
try:
|
|
64
|
+
return TypeAdapter(list[model_cls]).validate_python(items)
|
|
65
|
+
except ValidationError as e:
|
|
66
|
+
for msg in _format_failures(e, items, id_path, model_cls):
|
|
67
|
+
(log or LOG).warning(msg)
|
|
68
|
+
if hasattr(e, 'add_note'):
|
|
69
|
+
e.add_note(msg)
|
|
70
|
+
raise
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _format_failures(
|
|
74
|
+
exc: ValidationError,
|
|
75
|
+
items: list[Any],
|
|
76
|
+
id_path: str | None,
|
|
77
|
+
model_cls: type[BaseModel],
|
|
78
|
+
) -> list[str]:
|
|
79
|
+
"""Build one message per failing item, keyed by index and optional id."""
|
|
80
|
+
seen: set[int] = set()
|
|
81
|
+
messages: list[str] = []
|
|
82
|
+
for err in exc.errors():
|
|
83
|
+
loc = err.get('loc') or ()
|
|
84
|
+
if not loc or not isinstance(loc[0], int):
|
|
85
|
+
continue
|
|
86
|
+
idx = loc[0]
|
|
87
|
+
if idx in seen:
|
|
88
|
+
continue
|
|
89
|
+
seen.add(idx)
|
|
90
|
+
|
|
91
|
+
raw = items[idx] if 0 <= idx < len(items) else None
|
|
92
|
+
identifier = _resolve_id_path(raw, id_path) if id_path else None
|
|
93
|
+
id_hint = f' ({id_path}={identifier})' if identifier is not None else ''
|
|
94
|
+
field_path = '.'.join(str(p) for p in loc[1:]) or '<root>'
|
|
95
|
+
messages.append(
|
|
96
|
+
f'{model_cls.__name__} validation failed at index {idx}{id_hint}: '
|
|
97
|
+
f"field '{field_path}' {err.get('msg', '')}"
|
|
98
|
+
)
|
|
99
|
+
return messages
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _resolve_id_path(item: Any, id_path: str) -> Any:
|
|
103
|
+
"""Walk `id_path` (dot-separated) into `item`. Returns None on miss."""
|
|
104
|
+
value: Any = item
|
|
105
|
+
for part in id_path.split('.'):
|
|
106
|
+
if not isinstance(value, dict):
|
|
107
|
+
return None
|
|
108
|
+
value = value.get(part)
|
|
109
|
+
if value is None:
|
|
110
|
+
return None
|
|
111
|
+
return value
|
|
@@ -29,7 +29,7 @@ from ..endpoints import (
|
|
|
29
29
|
EP_IDENTITY_IP_LOOKUP,
|
|
30
30
|
EP_IDENTITY_PASSWORD_LOOKUP,
|
|
31
31
|
)
|
|
32
|
-
from ..helpers import TimeHelpers, connection_exceptions, debug_call
|
|
32
|
+
from ..helpers import TimeHelpers, connection_exceptions, debug_call, validate_list
|
|
33
33
|
from ..rf_client import RFClient
|
|
34
34
|
from .constants import DETECTIONS_PER_PAGE, MAXIMUM_IDENTITIES
|
|
35
35
|
from .errors import (
|
|
@@ -252,7 +252,7 @@ class IdentityMgr:
|
|
|
252
252
|
results_path='identities',
|
|
253
253
|
max_results=max_results or DEFAULT_LIMIT,
|
|
254
254
|
)
|
|
255
|
-
resp =
|
|
255
|
+
resp = validate_list(LeakedIdentity, resp, log=self.log)
|
|
256
256
|
self.log.info(f'Returned {len(resp)} identities')
|
|
257
257
|
return resp
|
|
258
258
|
|
|
@@ -321,7 +321,7 @@ class IdentityMgr:
|
|
|
321
321
|
resp = self.rf_client.request('post', url=EP_IDENTITY_PASSWORD_LOOKUP, data=data).json()[
|
|
322
322
|
'results'
|
|
323
323
|
]
|
|
324
|
-
resp =
|
|
324
|
+
resp = validate_list(PasswordLookup, resp, id_path='password.hash_prefix', log=self.log)
|
|
325
325
|
self.log.info(f'Returned {len(resp)} passwords')
|
|
326
326
|
return resp
|
|
327
327
|
|
|
@@ -436,7 +436,7 @@ class IdentityMgr:
|
|
|
436
436
|
max_results=max_results or DEFAULT_LIMIT,
|
|
437
437
|
results_path='identities',
|
|
438
438
|
)
|
|
439
|
-
resp =
|
|
439
|
+
resp = validate_list(LeakedIdentity, resp, log=self.log)
|
|
440
440
|
self.log.info(f'Returned {len(resp)} identities')
|
|
441
441
|
return resp
|
|
442
442
|
|
|
@@ -561,7 +561,7 @@ class IdentityMgr:
|
|
|
561
561
|
max_results=max_results or DEFAULT_LIMIT,
|
|
562
562
|
results_path='identities',
|
|
563
563
|
)
|
|
564
|
-
resp =
|
|
564
|
+
resp = validate_list(LeakedIdentity, resp, log=self.log)
|
|
565
565
|
self.log.info(f'Returned {len(resp)} identities')
|
|
566
566
|
return resp
|
|
567
567
|
|
|
@@ -663,7 +663,7 @@ class IdentityMgr:
|
|
|
663
663
|
max_results=max_results or DEFAULT_LIMIT,
|
|
664
664
|
results_path='identities',
|
|
665
665
|
)
|
|
666
|
-
resp =
|
|
666
|
+
resp = validate_list(CredentialSearch, resp, id_path='login', log=self.log)
|
|
667
667
|
self.log.info(f'Returned {len(resp)} credentials')
|
|
668
668
|
return resp
|
|
669
669
|
|
|
@@ -707,7 +707,7 @@ class IdentityMgr:
|
|
|
707
707
|
resp = self.rf_client.request('post', url=EP_IDENTITY_DUMP_SEARCH, data=payload).json()[
|
|
708
708
|
'dumps'
|
|
709
709
|
]
|
|
710
|
-
resp =
|
|
710
|
+
resp = validate_list(DumpSearchOut, resp, id_path='name', log=self.log)
|
|
711
711
|
self.log.info(f'Returned {len(resp)} dump results')
|
|
712
712
|
return resp
|
|
713
713
|
|
|
@@ -18,7 +18,7 @@ from pydantic import validate_call
|
|
|
18
18
|
from typing_extensions import Doc
|
|
19
19
|
|
|
20
20
|
from ..endpoints import EP_RISK_HISTORY
|
|
21
|
-
from ..helpers import debug_call
|
|
21
|
+
from ..helpers import debug_call, validate_list
|
|
22
22
|
from ..helpers.helpers import connection_exceptions
|
|
23
23
|
from ..rf_client import RFClient
|
|
24
24
|
from .errors import RiskHistoryError
|
|
@@ -57,4 +57,4 @@ class RiskHistoryMgr:
|
|
|
57
57
|
data = RiskHistoryIn.model_validate({'entities': entities, 'from': from_, 'to': to})
|
|
58
58
|
attrs = self.rf_client.request('post', EP_RISK_HISTORY, data=data.json()).json()['data']
|
|
59
59
|
|
|
60
|
-
return
|
|
60
|
+
return validate_list(RiskHistory, attrs, id_path='entity.id', log=self.log)
|
|
@@ -18,7 +18,7 @@ from pydantic import validate_call
|
|
|
18
18
|
from typing_extensions import Doc
|
|
19
19
|
|
|
20
20
|
from ..endpoints import EP_RISK_RULES
|
|
21
|
-
from ..helpers import debug_call
|
|
21
|
+
from ..helpers import debug_call, validate_list
|
|
22
22
|
from ..helpers.helpers import connection_exceptions
|
|
23
23
|
from ..rf_client import RFClient
|
|
24
24
|
from .errors import RiskRuleFetchError
|
|
@@ -60,4 +60,4 @@ class RiskRuleMgr:
|
|
|
60
60
|
self.log.info(f'Fetching risk rules for entity type: {entity_type.value}')
|
|
61
61
|
response = self.rf_client.request('get', url).json()
|
|
62
62
|
results = response['data']['results']
|
|
63
|
-
return
|
|
63
|
+
return validate_list(RiskRule, results, id_path='name', log=self.log)
|
|
@@ -25,7 +25,7 @@ from ..endpoints import (
|
|
|
25
25
|
EP_THREAT_MAP_ORG,
|
|
26
26
|
EP_THREAT_MAPS_LIST,
|
|
27
27
|
)
|
|
28
|
-
from ..helpers import debug_call
|
|
28
|
+
from ..helpers import debug_call, validate_list
|
|
29
29
|
from ..helpers.helpers import connection_exceptions
|
|
30
30
|
from ..rf_client import RFClient
|
|
31
31
|
from .errors import (
|
|
@@ -73,7 +73,7 @@ class ThreatMapMgr:
|
|
|
73
73
|
ThreatMapInfoError: If connection error occurs.
|
|
74
74
|
"""
|
|
75
75
|
maps_response = self.rf_client.request(method='get', url=EP_THREAT_MAPS_LIST).json()['data']
|
|
76
|
-
return
|
|
76
|
+
return validate_list(ThreatMapInfo, maps_response, id_path='name', log=self.log)
|
|
77
77
|
|
|
78
78
|
@debug_call
|
|
79
79
|
@validate_call
|
|
@@ -94,7 +94,7 @@ class ThreatMapMgr:
|
|
|
94
94
|
map_type = ThreatMapType(map_type)
|
|
95
95
|
url = EP_CATEGORIES.format(map_type.category_slug)
|
|
96
96
|
cat_response = self.rf_client.request(method='get', url=url).json()['data']
|
|
97
|
-
return
|
|
97
|
+
return validate_list(EntityCategory, cat_response, id_path='id', log=self.log)
|
|
98
98
|
|
|
99
99
|
@debug_call
|
|
100
100
|
@validate_call
|
|
@@ -134,7 +134,7 @@ class ThreatMapMgr:
|
|
|
134
134
|
offset_key='offset',
|
|
135
135
|
max_results=max_results or DEFAULT_LIMIT,
|
|
136
136
|
)
|
|
137
|
-
return
|
|
137
|
+
return validate_list(ThreatActorProfile, search_response, id_path='id', log=self.log)
|
|
138
138
|
|
|
139
139
|
@debug_call
|
|
140
140
|
@validate_call
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_domain_abuse.py
RENAMED
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_identity_exposure.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_malicious_sites.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_malware_report.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/markdown/markdown_third_party_risk.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/domain_abuse.py
RENAMED
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/geopolitics.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/malicious_sites.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/panel_log_v2.py
RENAMED
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/panel_log/vulnerability.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_compromised_bank_checks.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_cyber_vulnerability.py
RENAMED
|
File without changes
|
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_geopolitics_facility.py
RENAMED
|
File without changes
|
{psengine-2.11.0 → psengine-2.12.0}/psengine/playbook_alerts/models/pba_identity_exposures.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|