psengine 2.4.0__tar.gz → 2.4.2__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.
Files changed (150) hide show
  1. {psengine-2.4.0 → psengine-2.4.2}/PKG-INFO +2 -1
  2. {psengine-2.4.0 → psengine-2.4.2}/psengine/_version.py +1 -1
  3. {psengine-2.4.0 → psengine-2.4.2}/psengine/constants.py +1 -1
  4. {psengine-2.4.0 → psengine-2.4.2}/psengine/detection/detection_mgr.py +1 -1
  5. {psengine-2.4.0 → psengine-2.4.2}/psengine/malware_intel/malware_intel.py +6 -2
  6. {psengine-2.4.0 → psengine-2.4.2}/psengine/malware_intel/malware_intel_mgr.py +4 -2
  7. {psengine-2.4.0 → psengine-2.4.2}/psengine/malware_intel/models.py +6 -1
  8. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/playbook_alert_mgr.py +5 -5
  9. {psengine-2.4.0 → psengine-2.4.2}/psengine/rf_client.py +15 -1
  10. {psengine-2.4.0 → psengine-2.4.2}/psengine.egg-info/PKG-INFO +2 -1
  11. {psengine-2.4.0 → psengine-2.4.2}/psengine.egg-info/requires.txt +1 -0
  12. {psengine-2.4.0 → psengine-2.4.2}/pyproject.toml +2 -1
  13. {psengine-2.4.0 → psengine-2.4.2}/LICENSE +0 -0
  14. {psengine-2.4.0 → psengine-2.4.2}/README.md +0 -0
  15. {psengine-2.4.0 → psengine-2.4.2}/psengine/__init__.py +0 -0
  16. {psengine-2.4.0 → psengine-2.4.2}/psengine/_sdk_id.py +0 -0
  17. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/__init__.py +0 -0
  18. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/constants.py +0 -0
  19. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/errors.py +0 -0
  20. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/helpers.py +0 -0
  21. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/markdown.py +0 -0
  22. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/models.py +0 -0
  23. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/note.py +0 -0
  24. {psengine-2.4.0 → psengine-2.4.2}/psengine/analyst_notes/note_mgr.py +0 -0
  25. {psengine-2.4.0 → psengine-2.4.2}/psengine/base_http_client.py +0 -0
  26. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/__init__.py +0 -0
  27. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/classic_alert.py +0 -0
  28. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/classic_alert_mgr.py +0 -0
  29. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/constants.py +0 -0
  30. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/errors.py +0 -0
  31. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/helpers.py +0 -0
  32. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/markdown/__init__.py +0 -0
  33. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/markdown/markdown.py +0 -0
  34. {psengine-2.4.0 → psengine-2.4.2}/psengine/classic_alerts/models.py +0 -0
  35. {psengine-2.4.0 → psengine-2.4.2}/psengine/collective_insights/__init__.py +0 -0
  36. {psengine-2.4.0 → psengine-2.4.2}/psengine/collective_insights/collective_insights.py +0 -0
  37. {psengine-2.4.0 → psengine-2.4.2}/psengine/collective_insights/constants.py +0 -0
  38. {psengine-2.4.0 → psengine-2.4.2}/psengine/collective_insights/errors.py +0 -0
  39. {psengine-2.4.0 → psengine-2.4.2}/psengine/collective_insights/insight.py +0 -0
  40. {psengine-2.4.0 → psengine-2.4.2}/psengine/collective_insights/models.py +0 -0
  41. {psengine-2.4.0 → psengine-2.4.2}/psengine/common_models.py +0 -0
  42. {psengine-2.4.0 → psengine-2.4.2}/psengine/config/__init__.py +0 -0
  43. {psengine-2.4.0 → psengine-2.4.2}/psengine/config/config.py +0 -0
  44. {psengine-2.4.0 → psengine-2.4.2}/psengine/config/errors.py +0 -0
  45. {psengine-2.4.0 → psengine-2.4.2}/psengine/detection/__init__.py +0 -0
  46. {psengine-2.4.0 → psengine-2.4.2}/psengine/detection/detection_rule.py +0 -0
  47. {psengine-2.4.0 → psengine-2.4.2}/psengine/detection/errors.py +0 -0
  48. {psengine-2.4.0 → psengine-2.4.2}/psengine/detection/helpers.py +0 -0
  49. {psengine-2.4.0 → psengine-2.4.2}/psengine/detection/models.py +0 -0
  50. {psengine-2.4.0 → psengine-2.4.2}/psengine/endpoints.py +0 -0
  51. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/__init__.py +0 -0
  52. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/constants.py +0 -0
  53. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/errors.py +0 -0
  54. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/lookup.py +0 -0
  55. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/lookup_mgr.py +0 -0
  56. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/models/__init__.py +0 -0
  57. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/models/base_enriched_entity.py +0 -0
  58. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/models/lookup.py +0 -0
  59. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/models/soar.py +0 -0
  60. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/soar.py +0 -0
  61. {psengine-2.4.0 → psengine-2.4.2}/psengine/enrich/soar_mgr.py +0 -0
  62. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_lists/__init__.py +0 -0
  63. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_lists/constants.py +0 -0
  64. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_lists/entity_list.py +0 -0
  65. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_lists/entity_list_mgr.py +0 -0
  66. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_lists/errors.py +0 -0
  67. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_lists/models.py +0 -0
  68. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_match/__init__.py +0 -0
  69. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_match/entity_match.py +0 -0
  70. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_match/entity_match_mgr.py +0 -0
  71. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_match/errors.py +0 -0
  72. {psengine-2.4.0 → psengine-2.4.2}/psengine/entity_match/models.py +0 -0
  73. {psengine-2.4.0 → psengine-2.4.2}/psengine/errors.py +0 -0
  74. {psengine-2.4.0 → psengine-2.4.2}/psengine/fusion/__init__.py +0 -0
  75. {psengine-2.4.0 → psengine-2.4.2}/psengine/fusion/errors.py +0 -0
  76. {psengine-2.4.0 → psengine-2.4.2}/psengine/fusion/fusion_mgr.py +0 -0
  77. {psengine-2.4.0 → psengine-2.4.2}/psengine/fusion/models.py +0 -0
  78. {psengine-2.4.0 → psengine-2.4.2}/psengine/helpers/__init__.py +0 -0
  79. {psengine-2.4.0 → psengine-2.4.2}/psengine/helpers/helpers.py +0 -0
  80. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/__init__.py +0 -0
  81. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/constants.py +0 -0
  82. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/errors.py +0 -0
  83. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/identity.py +0 -0
  84. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/identity_mgr.py +0 -0
  85. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/models/__init__.py +0 -0
  86. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/models/common_models.py +0 -0
  87. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/models/detections.py +0 -0
  88. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/models/incident_report.py +0 -0
  89. {psengine-2.4.0 → psengine-2.4.2}/psengine/identity/models/lookup.py +0 -0
  90. {psengine-2.4.0 → psengine-2.4.2}/psengine/logger/__init__.py +0 -0
  91. {psengine-2.4.0 → psengine-2.4.2}/psengine/logger/constants.py +0 -0
  92. {psengine-2.4.0 → psengine-2.4.2}/psengine/logger/errors.py +0 -0
  93. {psengine-2.4.0 → psengine-2.4.2}/psengine/logger/rf_logger.py +0 -0
  94. {psengine-2.4.0 → psengine-2.4.2}/psengine/malware_intel/__init__.py +0 -0
  95. {psengine-2.4.0 → psengine-2.4.2}/psengine/malware_intel/errors.py +0 -0
  96. {psengine-2.4.0 → psengine-2.4.2}/psengine/markdown/__init__.py +0 -0
  97. {psengine-2.4.0 → psengine-2.4.2}/psengine/markdown/markdown.py +0 -0
  98. {psengine-2.4.0 → psengine-2.4.2}/psengine/markdown/models.py +0 -0
  99. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/__init__.py +0 -0
  100. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/constants.py +0 -0
  101. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/errors.py +0 -0
  102. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/helpers.py +0 -0
  103. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/mappings.py +0 -0
  104. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/__init__.py +0 -0
  105. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown.py +0 -0
  106. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_code_repo.py +0 -0
  107. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_cyber_vulnerability.py +0 -0
  108. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_domain_abuse.py +0 -0
  109. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_geopolitics_facility.py +0 -0
  110. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_identity_exposure.py +0 -0
  111. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_malware_report.py +0 -0
  112. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/markdown/markdown_third_party_risk.py +0 -0
  113. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/__init__.py +0 -0
  114. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/common_models.py +0 -0
  115. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/panel_log.py +0 -0
  116. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/panel_status.py +0 -0
  117. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_code_repo_leak.py +0 -0
  118. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_cyber_vulnerability.py +0 -0
  119. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_domain_abuse.py +0 -0
  120. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_geopolitics_facility.py +0 -0
  121. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_identity_exposures.py +0 -0
  122. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_malware_report.py +0 -0
  123. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/pba_third_party_risk.py +0 -0
  124. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/models/search_endpoint.py +0 -0
  125. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/pa_category.py +0 -0
  126. {psengine-2.4.0 → psengine-2.4.2}/psengine/playbook_alerts/playbook_alerts.py +0 -0
  127. {psengine-2.4.0 → psengine-2.4.2}/psengine/py.typed +0 -0
  128. {psengine-2.4.0 → psengine-2.4.2}/psengine/risk_history/__init__.py +0 -0
  129. {psengine-2.4.0 → psengine-2.4.2}/psengine/risk_history/errors.py +0 -0
  130. {psengine-2.4.0 → psengine-2.4.2}/psengine/risk_history/models.py +0 -0
  131. {psengine-2.4.0 → psengine-2.4.2}/psengine/risk_history/risk_history_mgr.py +0 -0
  132. {psengine-2.4.0 → psengine-2.4.2}/psengine/risklists/__init__.py +0 -0
  133. {psengine-2.4.0 → psengine-2.4.2}/psengine/risklists/constants.py +0 -0
  134. {psengine-2.4.0 → psengine-2.4.2}/psengine/risklists/errors.py +0 -0
  135. {psengine-2.4.0 → psengine-2.4.2}/psengine/risklists/models.py +0 -0
  136. {psengine-2.4.0 → psengine-2.4.2}/psengine/risklists/risklist_mgr.py +0 -0
  137. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/__init__.py +0 -0
  138. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/base_stix_entity.py +0 -0
  139. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/complex_entity.py +0 -0
  140. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/constants.py +0 -0
  141. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/enriched_indicator.py +0 -0
  142. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/errors.py +0 -0
  143. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/helpers.py +0 -0
  144. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/rf_bundle.py +0 -0
  145. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/simple_entity.py +0 -0
  146. {psengine-2.4.0 → psengine-2.4.2}/psengine/stix2/util.py +0 -0
  147. {psengine-2.4.0 → psengine-2.4.2}/psengine.egg-info/SOURCES.txt +0 -0
  148. {psengine-2.4.0 → psengine-2.4.2}/psengine.egg-info/dependency_links.txt +0 -0
  149. {psengine-2.4.0 → psengine-2.4.2}/psengine.egg-info/top_level.txt +0 -0
  150. {psengine-2.4.0 → psengine-2.4.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: psengine
3
- Version: 2.4.0
3
+ Version: 2.4.2
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
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.13
20
20
  Requires-Python: <3.14,>=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ Requires-Dist: typing_extensions>=4.8.0
23
24
  Requires-Dist: requests>=2.27.1
24
25
  Requires-Dist: jsonpath_ng<=1.6.1,>=1.5.3
25
26
  Requires-Dist: stix2~=3.0.1
@@ -11,4 +11,4 @@
11
11
  # accessed from any third party API. #
12
12
  ##############################################################################################
13
13
 
14
- __version__ = '2.4.0'
14
+ __version__ = '2.4.1'
@@ -23,7 +23,7 @@ DEFAULT_MAX_WORKERS = 10
23
23
  # Recorded Future API
24
24
  #####################
25
25
  RF_TOKEN_ENV_VAR = 'RF_TOKEN' # noqa: S105
26
- RF_TOKEN_VALIDATION_REGEX = r'^[a-f0-9]{32}$' # noqa: S105
26
+ RF_TOKEN_VALIDATION_REGEX = r'^[a-z0-9]{32}$' # noqa: S105
27
27
 
28
28
  #####################
29
29
  # Recorded Future Portal
@@ -93,7 +93,7 @@ class DetectionMgr:
93
93
  data = {
94
94
  'filter': filters,
95
95
  'tagged_entities': tagged_entities,
96
- 'limit': SEARCH_LIMIT,
96
+ 'limit': min(SEARCH_LIMIT, max_results) if max_results else SEARCH_LIMIT,
97
97
  }
98
98
 
99
99
  data = DetectionRuleSearchOut.model_validate(data)
@@ -89,6 +89,8 @@ class SandboxReport(RFBaseModel):
89
89
 
90
90
 
91
91
  def _split_time(value: str):
92
+ if not value:
93
+ return value
92
94
  return value.split('T')[0]
93
95
 
94
96
 
@@ -96,12 +98,14 @@ class MalwareReportIn(RFBaseModel):
96
98
  """Validate data sent to the `/v1/reports` endpoint."""
97
99
 
98
100
  query: str
99
- sha256: str
101
+ sha256: Optional[str] = None
100
102
  start_date: Annotated[
101
103
  str, BeforeValidator(Validators.convert_relative_time), AfterValidator(_split_time)
102
104
  ]
103
105
  end_date: Annotated[
104
- str, BeforeValidator(Validators.convert_relative_time), AfterValidator(_split_time)
106
+ Optional[str],
107
+ BeforeValidator(Validators.convert_relative_time),
108
+ AfterValidator(_split_time),
105
109
  ]
106
110
  my_enterprise: bool
107
111
  limit: int = Field(ge=1, le=10)
@@ -12,7 +12,7 @@
12
12
  ##############################################################################################
13
13
 
14
14
  import logging
15
- from typing import Annotated
15
+ from typing import Annotated, Optional
16
16
 
17
17
  from pydantic import validate_call
18
18
  from typing_extensions import Doc
@@ -48,7 +48,9 @@ class MalwareIntelMgr:
48
48
  start_date: Annotated[
49
49
  str, Doc('The starting date, format YYYY-MM-DD or relative like -1d.')
50
50
  ],
51
- end_date: Annotated[str, Doc('The ending date, format YYYY-MM-DD or relative like -1d.')],
51
+ end_date: Annotated[
52
+ Optional[str], Doc('The ending date, format YYYY-MM-DD or relative like -1d.')
53
+ ] = None,
52
54
  my_enterprise: Annotated[
53
55
  bool, Doc('If the report has been submitted by your enterprise.')
54
56
  ] = False,
@@ -247,7 +247,8 @@ class SampleInfo(RFBaseModel):
247
247
 
248
248
 
249
249
  class StaticInfo(RFBaseModel):
250
- exts: Optional[str] = None
250
+ extracted: Optional[list[ExtractedItem]] = []
251
+ exts: Optional[list[str]] = None
251
252
  sha1: Optional[str] = None
252
253
  md5: Optional[str] = None
253
254
  sha256: Optional[str] = None
@@ -256,6 +257,10 @@ class StaticInfo(RFBaseModel):
256
257
  ssdeep: Optional[str] = None
257
258
  tags: Optional[list[str]] = []
258
259
  target: Optional[str] = None
260
+ registry: RegistryInfo = Field(default_factory=RegistryInfo)
261
+ registry_count: Optional[int] = None
262
+ signatures: Optional[list[Signature]] = []
263
+ signatures_count: Optional[int] = None
259
264
 
260
265
 
261
266
  class DynamicInfo(RFBaseModel):
@@ -141,9 +141,9 @@ class PlaybookAlertMgr:
141
141
  alerts_per_page: Annotated[
142
142
  Optional[int], Doc('Number of alerts per page (pagination).')
143
143
  ] = Field(ge=1, le=10000, default=ALERTS_PER_PAGE),
144
- max_results: Annotated[
145
- Optional[int], Doc('Maximum number of alerts to fetch.')
146
- ] = DEFAULT_LIMIT,
144
+ max_results: Annotated[Optional[int], Doc('Maximum number of alerts to fetch.')] = Field(
145
+ ge=1, le=10_000, default=DEFAULT_LIMIT
146
+ ),
147
147
  order_by: Annotated[
148
148
  Optional[str], Doc('Field to order alerts by, e.g. `created` or `updated`.')
149
149
  ] = None,
@@ -229,7 +229,7 @@ class PlaybookAlertMgr:
229
229
  ),
230
230
  max_results: Annotated[
231
231
  Optional[int], Doc('Maximum total number of alerts to fetch.')
232
- ] = DEFAULT_LIMIT,
232
+ ] = Field(ge=1, le=10_000, default=DEFAULT_LIMIT),
233
233
  order_by: Annotated[
234
234
  Optional[str], Doc('Field to order alerts by, e.g. `created` or `updated`.')
235
235
  ] = None,
@@ -286,7 +286,7 @@ class PlaybookAlertMgr:
286
286
  data=request_body,
287
287
  max_results=max_results,
288
288
  results_path='data',
289
- offset_key='from',
289
+ offset_key='offset',
290
290
  )
291
291
 
292
292
  # To avoid a breaking change have to reconstruct the SearchResponse model manually
@@ -184,8 +184,13 @@ class RFClient(BaseHTTPClient):
184
184
  **kwargs,
185
185
  ):
186
186
  if 'next_offset' in json_response:
187
+ current_len = 0
187
188
  while 'next_offset' in json_response:
188
189
  data[offset_key] = json_response['next_offset']
190
+ data['limit'] = min(data['limit'], max_results - current_len)
191
+ if data['limit'] <= 0:
192
+ break
193
+
189
194
  json_response = self.request(
190
195
  method=method,
191
196
  url=url,
@@ -202,11 +207,15 @@ class RFClient(BaseHTTPClient):
202
207
  if any(len(v) >= max_results for v in dict_results.values()):
203
208
  dict_results = {k: v[:max_results] for k, v in dict_results.items()}
204
209
  break
210
+ current_len = max(len(v) for v in dict_results.values())
211
+
205
212
  else:
206
213
  all_results += self._get_matches(results_expr, json_response)
207
- if len(all_results) >= max_results:
214
+ current_len = len(all_results)
215
+ if current_len >= max_results:
208
216
  all_results = all_results[:max_results]
209
217
  break
218
+
210
219
  else:
211
220
  seen = json_response['counts']['returned']
212
221
  if json_response['counts']['total'] > max_results:
@@ -324,6 +333,9 @@ class RFClient(BaseHTTPClient):
324
333
  with suppress(KeyError):
325
334
  dict_results[str(expr)].extend(self._get_matches(expr, json_response))
326
335
 
336
+ if len(all_results) >= max_results:
337
+ return all_results[:max_results]
338
+
327
339
  if method.lower() == 'get':
328
340
  return self._request_paged_get(
329
341
  url=url,
@@ -340,6 +352,8 @@ class RFClient(BaseHTTPClient):
340
352
  )
341
353
 
342
354
  if method.lower() == 'post':
355
+ data['limit'] = min(data['limit'], max_results - len(all_results))
356
+
343
357
  return self._request_paged_post(
344
358
  url=url,
345
359
  method=method,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: psengine
3
- Version: 2.4.0
3
+ Version: 2.4.2
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
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.13
20
20
  Requires-Python: <3.14,>=3.9
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ Requires-Dist: typing_extensions>=4.8.0
23
24
  Requires-Dist: requests>=2.27.1
24
25
  Requires-Dist: jsonpath_ng<=1.6.1,>=1.5.3
25
26
  Requires-Dist: stix2~=3.0.1
@@ -1,3 +1,4 @@
1
+ typing_extensions>=4.8.0
1
2
  requests>=2.27.1
2
3
  jsonpath_ng<=1.6.1,>=1.5.3
3
4
  stix2~=3.0.1
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "psengine"
3
- version = "2.4.0"
3
+ version = "2.4.2"
4
4
  readme = "README.md"
5
5
  license = "MIT"
6
6
  requires-python = ">=3.9, <3.14"
@@ -27,6 +27,7 @@ classifiers=[
27
27
 
28
28
 
29
29
  dependencies = [
30
+ "typing_extensions >= 4.8.0",
30
31
  "requests>=2.27.1",
31
32
  "jsonpath_ng>=1.5.3, <=1.6.1",
32
33
  "stix2~=3.0.1",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes