polyswarm-api 4.7.0__tar.gz → 4.8.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.
- {polyswarm_api-4.7.0/src/polyswarm_api.egg-info → polyswarm_api-4.8.0}/PKG-INFO +1 -1
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/pyproject.toml +2 -2
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/__init__.py +1 -1
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/aio/api.py +50 -2
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/api.py +48 -1
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/resources.py +21 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0/src/polyswarm_api.egg-info}/PKG-INFO +1 -1
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/LICENSE +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/README.md +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/setup.cfg +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/aio/__init__.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/aio/session.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/core.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/exceptions.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/refang.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/session.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api/settings.py +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api.egg-info/SOURCES.txt +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api.egg-info/dependency_links.txt +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api.egg-info/requires.txt +0 -0
- {polyswarm_api-4.7.0 → polyswarm_api-4.8.0}/src/polyswarm_api.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "polyswarm_api"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.8.0"
|
|
8
8
|
description = "Client library to simplify interacting with the PolySwarm consumer API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10,<4"
|
|
@@ -55,7 +55,7 @@ package-dir = { "" = "src" }
|
|
|
55
55
|
where = ["src"]
|
|
56
56
|
|
|
57
57
|
[tool.bumpversion]
|
|
58
|
-
current_version = "4.
|
|
58
|
+
current_version = "4.8.0"
|
|
59
59
|
commit = true
|
|
60
60
|
tag = false
|
|
61
61
|
sign_tags = true
|
|
@@ -288,6 +288,51 @@ class PolySwarmAsyncAPI:
|
|
|
288
288
|
result_parser=resources.MetadataMapping,
|
|
289
289
|
)
|
|
290
290
|
|
|
291
|
+
async def metadata_raw_search(self, body, community=None):
|
|
292
|
+
"""Run a raw OpenSearch query body against the metadata corpus.
|
|
293
|
+
|
|
294
|
+
The server overlays the community filter itself and caps the work, so
|
|
295
|
+
``body`` is a plain ``_search`` body and the community travels in the
|
|
296
|
+
query string rather than in it. The accepted grammar is closed: a
|
|
297
|
+
construct the server does not recognise is refused rather than passed
|
|
298
|
+
through, and so is a top-level ``community``/``communities`` key.
|
|
299
|
+
|
|
300
|
+
Entitlement-gated and metered per query, including a query the server
|
|
301
|
+
refuses after running it.
|
|
302
|
+
|
|
303
|
+
:param body: An OpenSearch ``_search`` body (``query``, ``aggs``,
|
|
304
|
+
``size``, ``sort``, ``_source``, ...). Omitting ``size`` returns
|
|
305
|
+
**no documents**: the server defaults it to 0 so an aggregation
|
|
306
|
+
caller does not pay for hits they did not ask for. Ask for hits
|
|
307
|
+
by setting it.
|
|
308
|
+
:param community: Community to search; defaults to the client's.
|
|
309
|
+
:return: A ``MetadataRawSearch`` whose ``json`` carries the engine's
|
|
310
|
+
``hits`` and ``aggregations``, plus ``partial`` when the server
|
|
311
|
+
could not establish that the answer is complete.
|
|
312
|
+
|
|
313
|
+
``partial`` is a **list to inspect, not a flag**. Its entries are
|
|
314
|
+
``{'code': ..., 'message': ...}`` and they make different claims,
|
|
315
|
+
so branch on ``code`` - ``message`` is for a human and may be
|
|
316
|
+
reworded. The codes are a closed set:
|
|
317
|
+
|
|
318
|
+
* ``timed_out`` / ``terminated_early`` - the search stopped early.
|
|
319
|
+
* ``shards_failed`` - a shard was lost; also carries ``failed``.
|
|
320
|
+
* ``buckets_clamped`` - a ``terms`` list dropped terms.
|
|
321
|
+
* ``composite_paged`` - a ``composite`` may have more pages; page
|
|
322
|
+
it with its ``after_key``.
|
|
323
|
+
* ``bucket_count_approximate`` - a ``terms`` count is approximate
|
|
324
|
+
rather than truncated.
|
|
325
|
+
* ``hit_total_capped`` - the hit counter stopped at
|
|
326
|
+
``track_total_hits``. This is what *every* large query that asks
|
|
327
|
+
for hits returns, so on its own it says nothing about the
|
|
328
|
+
aggregations; the server omits it when the body asked for no
|
|
329
|
+
documents. ``hits.total.relation`` carries the same fact as the
|
|
330
|
+
engine's own field, ``eq`` exact and ``gte`` capped.
|
|
331
|
+
"""
|
|
332
|
+
logger.info('Running a raw metadata search')
|
|
333
|
+
return await self._single(
|
|
334
|
+
resources.MetadataRawSearch.search(self, body, community))
|
|
335
|
+
|
|
291
336
|
async def metadata_field_properties_write(self, field_path, description,
|
|
292
337
|
example=None, category=None, aliases=None):
|
|
293
338
|
"""Upsert a metadata field properties entry (the single write path).
|
|
@@ -597,18 +642,21 @@ class PolySwarmAsyncAPI:
|
|
|
597
642
|
"""
|
|
598
643
|
return await self._single(resources.LiveHuntResult.get(self, id=result_id))
|
|
599
644
|
|
|
600
|
-
async def historical_create(self, rule=None, ruleset_name=None):
|
|
645
|
+
async def historical_create(self, rule=None, ruleset_name=None, window_days=None):
|
|
601
646
|
"""
|
|
602
647
|
Run a new historical hunt.
|
|
603
648
|
|
|
604
649
|
:param rule: YaraRuleset object or string containing YARA rules to install
|
|
605
650
|
:param ruleset_name: Name of the ruleset.
|
|
651
|
+
:param window_days: Days back the hunt reaches: 30, 60, 90 or 180. Omit it for the
|
|
652
|
+
platform maximum. The server validates the value; the SDK does not.
|
|
606
653
|
:return: The created Hunt resource
|
|
607
654
|
"""
|
|
608
655
|
logger.info('Create historical hunt %s', rule)
|
|
609
656
|
rule, rule_id = self._parse_rule(rule)
|
|
610
657
|
return await self._single(resources.HistoricalHunt.create(self, yara=rule.yara if rule else None, rule_id=rule_id,
|
|
611
|
-
ruleset_name=ruleset_name,
|
|
658
|
+
ruleset_name=ruleset_name, window_days=window_days,
|
|
659
|
+
community=self.community))
|
|
612
660
|
|
|
613
661
|
async def historical_get(self, hunt=None):
|
|
614
662
|
"""
|
|
@@ -303,6 +303,50 @@ class PolyswarmAPI:
|
|
|
303
303
|
result_parser=resources.MetadataMapping,
|
|
304
304
|
)
|
|
305
305
|
|
|
306
|
+
def metadata_raw_search(self, body, community=None):
|
|
307
|
+
"""Run a raw OpenSearch query body against the metadata corpus.
|
|
308
|
+
|
|
309
|
+
The server overlays the community filter itself and caps the work, so
|
|
310
|
+
``body`` is a plain ``_search`` body and the community travels in the
|
|
311
|
+
query string rather than in it. The accepted grammar is closed: a
|
|
312
|
+
construct the server does not recognise is refused rather than passed
|
|
313
|
+
through, and so is a top-level ``community``/``communities`` key.
|
|
314
|
+
|
|
315
|
+
Entitlement-gated and metered per query, including a query the server
|
|
316
|
+
refuses after running it.
|
|
317
|
+
|
|
318
|
+
:param body: An OpenSearch ``_search`` body (``query``, ``aggs``,
|
|
319
|
+
``size``, ``sort``, ``_source``, ...). Omitting ``size`` returns
|
|
320
|
+
**no documents**: the server defaults it to 0 so an aggregation
|
|
321
|
+
caller does not pay for hits they did not ask for. Ask for hits
|
|
322
|
+
by setting it.
|
|
323
|
+
:param community: Community to search; defaults to the client's.
|
|
324
|
+
:return: A ``MetadataRawSearch`` whose ``json`` carries the engine's
|
|
325
|
+
``hits`` and ``aggregations``, plus ``partial`` when the server
|
|
326
|
+
could not establish that the answer is complete.
|
|
327
|
+
|
|
328
|
+
``partial`` is a **list to inspect, not a flag**. Its entries are
|
|
329
|
+
``{'code': ..., 'message': ...}`` and they make different claims,
|
|
330
|
+
so branch on ``code`` - ``message`` is for a human and may be
|
|
331
|
+
reworded. The codes are a closed set:
|
|
332
|
+
|
|
333
|
+
* ``timed_out`` / ``terminated_early`` - the search stopped early.
|
|
334
|
+
* ``shards_failed`` - a shard was lost; also carries ``failed``.
|
|
335
|
+
* ``buckets_clamped`` - a ``terms`` list dropped terms.
|
|
336
|
+
* ``composite_paged`` - a ``composite`` may have more pages; page
|
|
337
|
+
it with its ``after_key``.
|
|
338
|
+
* ``bucket_count_approximate`` - a ``terms`` count is approximate
|
|
339
|
+
rather than truncated.
|
|
340
|
+
* ``hit_total_capped`` - the hit counter stopped at
|
|
341
|
+
``track_total_hits``. This is what *every* large query that asks
|
|
342
|
+
for hits returns, so on its own it says nothing about the
|
|
343
|
+
aggregations; the server omits it when the body asked for no
|
|
344
|
+
documents. ``hits.total.relation`` carries the same fact as the
|
|
345
|
+
engine's own field, ``eq`` exact and ``gte`` capped.
|
|
346
|
+
"""
|
|
347
|
+
logger.info("Running a raw metadata search")
|
|
348
|
+
return self._single(resources.MetadataRawSearch.search(self, body, community))
|
|
349
|
+
|
|
306
350
|
def metadata_field_properties_write(
|
|
307
351
|
self, field_path, description, example=None, category=None, aliases=None
|
|
308
352
|
):
|
|
@@ -691,12 +735,14 @@ class PolyswarmAPI:
|
|
|
691
735
|
"""
|
|
692
736
|
return self._single(resources.LiveHuntResult.get(self, id=result_id))
|
|
693
737
|
|
|
694
|
-
def historical_create(self, rule=None, ruleset_name=None):
|
|
738
|
+
def historical_create(self, rule=None, ruleset_name=None, window_days=None):
|
|
695
739
|
"""
|
|
696
740
|
Run a new historical hunt.
|
|
697
741
|
|
|
698
742
|
:param rule: YaraRuleset object or string containing YARA rules to install
|
|
699
743
|
:param ruleset_name: Name of the ruleset.
|
|
744
|
+
:param window_days: Days back the hunt reaches: 30, 60, 90 or 180. Omit it for the
|
|
745
|
+
platform maximum. The server validates the value; the SDK does not.
|
|
700
746
|
:return: The created Hunt resource
|
|
701
747
|
"""
|
|
702
748
|
logger.info("Create historical hunt %s", rule)
|
|
@@ -707,6 +753,7 @@ class PolyswarmAPI:
|
|
|
707
753
|
yara=rule.yara if rule else None,
|
|
708
754
|
rule_id=rule_id,
|
|
709
755
|
ruleset_name=ruleset_name,
|
|
756
|
+
window_days=window_days,
|
|
710
757
|
community=self.community,
|
|
711
758
|
)
|
|
712
759
|
)
|
|
@@ -77,6 +77,25 @@ class MetadataMapping(core.BaseJsonResource):
|
|
|
77
77
|
RESOURCE_ENDPOINT = '/search/metadata/mappings'
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
class MetadataRawSearch(core.BaseJsonResource):
|
|
81
|
+
RESOURCE_ENDPOINT = '/search/metadata/raw'
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def search(cls, api, body, community=None):
|
|
85
|
+
"""``body`` is the caller's whole query and travels untouched. The
|
|
86
|
+
community is scope, not query, and the server reads it from the query
|
|
87
|
+
string - folding it into the body would pick scope with a key the
|
|
88
|
+
server's grammar then rejects."""
|
|
89
|
+
return core.PolyswarmRequest(
|
|
90
|
+
api=api,
|
|
91
|
+
method='POST',
|
|
92
|
+
url=f'{api.uri}{cls.RESOURCE_ENDPOINT}',
|
|
93
|
+
params={'community': community or api.community},
|
|
94
|
+
json=body,
|
|
95
|
+
result_parser=cls,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
80
99
|
class MetadataFieldProperties(core.BaseJsonResource):
|
|
81
100
|
RESOURCE_ENDPOINT = '/search/metadata/properties'
|
|
82
101
|
RESOURCE_ID_KEYS = ['field_path']
|
|
@@ -853,6 +872,8 @@ class HistoricalHunt(core.BaseJsonResource):
|
|
|
853
872
|
# unknown (no source rule, or nothing to compare against) — not
|
|
854
873
|
# "unchanged".
|
|
855
874
|
self.source_rule_changed = content.get('source_rule_changed')
|
|
875
|
+
# None: no window requested (platform maximum), or a server predating the field.
|
|
876
|
+
self.window_days = content.get('window_days')
|
|
856
877
|
|
|
857
878
|
|
|
858
879
|
class HistoricalHuntList(HistoricalHunt):
|
|
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
|