diff --git a/pyproject.toml b/pyproject.toml index 2af7a2d0..03817648 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "polyswarm_api" -version = "4.4.0" +version = "4.5.0" description = "Client library to simplify interacting with the PolySwarm consumer API" readme = "README.md" requires-python = ">=3.10,<4" @@ -55,7 +55,7 @@ package-dir = { "" = "src" } where = ["src"] [tool.bumpversion] -current_version = "4.4.0" +current_version = "4.5.0" commit = true tag = false sign_tags = true diff --git a/specs/02-resources.md b/specs/02-resources.md index 9f1e6499..18c28a20 100644 --- a/specs/02-resources.md +++ b/specs/02-resources.md @@ -327,6 +327,50 @@ Classmethod builders (each returns a `PolyswarmRequest` descriptor): **No instance methods** that issue HTTP. Uploading to the pre-signed S3 URL is done via the session: `await api.session.upload_file(instance.upload_url, artifact)` (or `api.session.upload_file(...)` for sync). +### `LiveHuntResult` / `HistoricalHuntResult` + +**`matched_strings`.** The yara strings behind a hunt hit, so a consumer can see *why* +a rule fired rather than only which one did. Additive and optional, parsed with `.get()` +like `known_good` / `state` above — a server too old to emit it parses to `None` with no +behaviour change, and a subscript would raise on every result instead. + +It is **three-state** and the states are not interchangeable; the table, the per-entry +dict shape and the lower-bound caveat live in +[`05-downstream-contract.md`](./05-downstream-contract.md) +§"`matched_strings` on hunt results" — read it there rather than inferring from the +attribute. The short version a parser needs: `None` means *not reported* — **four** +distinct causes, enumerated in that table and revisited under "four places, not three" +below — `[]` means *matched with no byte evidence*, and a populated list is evidence. + +**`matched_strings_dropped`.** A sibling `int`/`None`, parsed the same additive way: +how many matched strings the server's byte budget withheld from this result. `None` is +**ambiguous in the same way as `matched_strings`** and must be read the same way: on a +**detail** route it means nothing was withheld; under any of the other three causes it +means nothing looked. It is never a claim that the +evidence is complete — which matters because the list endpoints always send `None` (below), +so on a list row the two readings are not interchangeable. + +A non-null count always means "the list you have is short by this much". It never +accompanies an empty `matched_strings` — a match's first string is never withheld. + +Both `…List` subclasses inherit these from their parent's `__init__`, so all four +hunt-result classes carry them — but on the list endpoints the values are always `None` +by design. + +**Do not read the class as telling you the route.** For the live pair it does not: +`live_feed()` builds its request with `LiveHuntResult.list(...)`, which hits +`/hunt/live/list` but parses rows as **`LiveHuntResult`** (see +[`03-endpoints.md`](./03-endpoints.md)). `LiveHuntResultList` is only ever a *delete* +**builder** — but the delete response is parsed **through** it (`_build_request` sets +`result_parser=cls`), so `live_feed_delete()` and `historical_results_delete()` both yield +`…List` instances, with both fields `None`. From a *read*, only `historical_results()` +yields them. + +So `None` reaches a caller from four places, not three: an older server, deleted evidence, +a list route, and a **delete response**. A `LiveHuntResult` carrying +`matched_strings is None` may well have come from the list route — which is exactly why +that `None` is ambiguous and must not be read as "nothing to show". + ### `LocalArtifact` A file-system or in-memory artifact prepared for upload. Constructed via: diff --git a/specs/03-endpoints.md b/specs/03-endpoints.md index c7762522..4eb0c86d 100644 --- a/specs/03-endpoints.md +++ b/specs/03-endpoints.md @@ -190,7 +190,7 @@ refusal. | `live_feed(since=None, …, livescan_id=None, max_results=None)` | `LiveHuntResult.list` — `livescan_id` scopes the feed to one live hunt (the hunt-page per-ruleset feed); `since` is in **SECONDS** (the server converts with `timedelta(seconds=since)`; the 3.x/4.x docstring said minutes and was wrong), and absent-or-`0` means no time filter at all — the server applies it on a truthiness test; `max_results` bounds how many results the generator yields — `None`/`0`/negative means no bound; it does not alter the request | | `historical_list(since=None)` | `HistoricalHunt.list` | | `historical_results(hunt=None, …)` | `HistoricalHuntResultList.get` | -| `ruleset_list(name=None, status=None, favorites_only=None, has_new_results=None)` | `YaraRuleset.list` — the hunt-page filters, conjunctive and optional; unset filters are omitted from the query so the no-filter request is byte-compatible with the old contract. `has_new_results` selects on the server's STORED counter (no window parameter — the window belongs to the server's scheduled refresh; rows carry `new_results_count` + `new_results_counted_at`) | +| `ruleset_list(name=None, status=None, favorites_only=None, has_new_results=None, sort=None, exclude_favorites=None)` | `YaraRuleset.list` — the hunt-page filters, conjunctive and optional; unset filters are omitted from the query so the no-filter request is byte-compatible with the old contract. `exclude_favorites=True` is the inverse of `favorites_only` and refused together with it — it exists for clients that render the favorites as their own list, where leaving them in the paginated list too makes a page repeat a row or come back short. Appended to the signature rather than placed beside `favorites_only`, so a positional caller keeps working. `has_new_results` selects on the server's STORED counter (no window parameter — the window belongs to the server's scheduled refresh; rows carry `new_results_count` + `new_results_counted_at`). `sort='active_first'` (4.5.0) asks the SERVER for the hunt page's order — rulesets carrying a live hunt link first, newest first within each block — as an opt-in token; unset sends no `sort`, keeping the default newest-first. The SDK never re-orders rows: the list is keyset-paginated, so a client-side sort would reorder one page and lie about the rest. The rank is the stored link, a WIDER predicate than the one `livescan_id` is rendered under, so a legacy row whose hunt was stopped without clearing the link leads the list while serializing `livescan_id` as `null` — read the field, not the position. The rendered `id` is unique but UNORDERED (the server renders a random `number`, and orders on its own insertion key), so dedupe with it and never resume or bound a walk with it. The key is also MUTABLE, unlike that default — a ruleset whose live hunt stops mid-walk is yielded twice, one started mid-walk is skipped, in any walk including a fresh one — and the generator does not dedupe; callers consuming more than one page dedupe by `id`. | | `tag_list()` | `Tag.list` | | `family_list()` | `MalwareFamily.list` | | `assertions_list(engine_id)` | `AssertionsJob.list` | diff --git a/specs/04-testing.md b/specs/04-testing.md index 8228c689..79dee474 100644 --- a/specs/04-testing.md +++ b/specs/04-testing.md @@ -24,6 +24,8 @@ How the test suite is organised. Three layers: pure unit tests (no HTTP at all - `test/metadata_field_properties_test.py` — the canonical example of the parametrised `ClientTestCase` harness with `respx`-backed mocking. - `test/client_scan_test.py` — sync, VCR-backed integration tests (not yet on the parametrised harness — follow-up work). - `test/async_client_test.py` — async, VCR-backed integration tests (not yet on the parametrised harness — follow-up work). +- `test/hunt_matched_strings_test.py` — pure-unit tests for the three-state `matched_strings` contract and its `matched_strings_dropped` sibling, across all four hunt-result classes. The endpoint behaviour they cannot see (that the detail route actually emits the keys and the list route sends `null`) is pinned live in `client_scan_test.py::test_live` / `async_client_test.py::test_async_live` — the e2e-first + pure-unit pairing invariant 1 asks for. +- `test/known_good_test.py` — pure-unit tests for the known-good resource fields. - `test/jmespath_test.py` — unit tests for `BaseJsonResource.jmespath`. - `test/vcr/*.vcr` — recorded cassettes. - `test/malicious` — fixture file for upload tests (`test/eicar.yara` was retired when the rules tests moved to per-test `uid_yara` bodies). diff --git a/specs/05-downstream-contract.md b/specs/05-downstream-contract.md index 77d0be8c..5bbc461a 100644 --- a/specs/05-downstream-contract.md +++ b/specs/05-downstream-contract.md @@ -273,6 +273,61 @@ What is **not** part of the contract: - The exact server JSON shape — that lives in the artifact-index repo's contract. - The order of fields in the JSON. +### `matched_strings` on hunt results — a three-state attribute + +`LiveHuntResult.matched_strings` / `HistoricalHuntResult.matched_strings` (and therefore +their `…List` subclasses) carry the yara strings behind a hunt hit. It is read with +`.get()` rather than a subscript, deliberately: the key is **additive**, so a server +older than it omits the key entirely and a subscript would raise on every result. + +Three values are possible and consumers **must not** collapse them: + +| Value | Meaning | +|---|---| +| `None` | Not reported. **Four** causes, which `.get()` collapses: the **list** endpoints send an explicit `null` rather than fetch a blob per row; **delete** responses (`live_feed_delete` / `historical_results_delete`) are parsed through the `…List` classes and carry `null` the same way; a server predating the field omits it entirely; and stored evidence may have been deleted. "We don't know", *not* "there was nothing". | +| `[]` | The rule matched and there is no byte evidence to show — a rule with no strings section, one whose matching strings are all `private`, or one that matched on absence (`not $a`, `none of them`). | +| `[…]` | The evidence. A **lower bound**, not a match count: `any of them` prints only the strings that hit, `private` strings never appear, and the server may withhold some past a size limit (see `matched_strings_dropped`). | + +Each entry is a dict: + +```python +{'offset': 78, 'identifier': '$stub', 'length': 14, 'data': '54 68 69 …', 'truncated': False} +``` + +- `data` is kept **exactly as yara rendered it** — a hex string comes back as byte pairs, a text string as ASCII with `\xNN` escapes. Only yara knows which applies, so it is not decoded back to bytes. +- `length` is the **stored** length, capped server-side. Past the cap the true length is unrecoverable. +- `truncated` means "there was more than this". It over-reports at exactly the cap, because nothing in the output distinguishes a match that ended there from one that was cut. + +### `matched_strings_dropped` — the count that keeps a short list honest + +A sibling attribute on the same four classes, `int` or `None`. It is how many matched +strings the server's per-result byte budget withheld, and it exists because a truncated +list is otherwise indistinguishable from a complete one: a consumer reading twelve +entries would conclude the rule hit twelve times when it hit thirty-one. + +`None` carries the same ambiguity as `matched_strings` itself and should be read the same +way: on a **detail** route it means nothing was withheld; under any of the other three +causes in the table above, it means nothing looked. It is not +a claim that the evidence is complete. It is deliberately a +**sibling** rather than a key inside `matched_strings`, which stays a plain list. + +A populated `matched_strings` with a non-null count is the normal shape for a verbose +ruleset. The first string of a match is never withheld, so this can never accompany an +empty list. + +**How much of this is pinned against a real server.** The live pair +(`LiveHuntResult` / `LiveHuntResultList`) is verified end to end — `test_live` / +`test_async_live` assert the detail route carries evidence, that list rows do not, and +that the server serves `matched_strings_dropped`. The **historical** pair follows by +symmetry, not by measurement: the e2e stack does not reliably populate historical results +inside a test window, so nothing pins that those routes emit either key. The server +renders both pairs through the same helpers, which is why symmetry is a reasonable +assumption — but it is an assumption. See `specs/99-open-questions.md`. + +**Evidence lives on the detail routes only.** `live_feed()` and `historical_results()` +page over list endpoints and will always yield `None` here; fetch a single result +(`live_result(id)` / `historical_result(id)`) to get the strings. + ## Pagination Generator endpoints return an iterable: diff --git a/specs/99-open-questions.md b/specs/99-open-questions.md index 6655405a..4882a14f 100644 --- a/specs/99-open-questions.md +++ b/specs/99-open-questions.md @@ -170,3 +170,43 @@ def test_rescans(self): ``` Cleanup with `try/finally` + `except NotFoundException: pass` tolerates the ioc-cache divergence (GET-by-host can serve a cached id that DELETE-by-id no longer finds). When that artifact-index bug is fixed the `except` becomes redundant. + +## Historical hunt-result fields are not pinned against a live server + +**Status:** gap, blocked on the e2e stack. + +`matched_strings` / `matched_strings_dropped` are asserted end to end for the **live** +hunt pair only. `test_historical_results` tolerates an empty result set by design — the +stack does not reliably populate historical results inside a test window — so nothing +verifies that `/hunt/historical/results` emits either key, or that +`/hunt/historical/results/list` sends the explicit `null`. + +Both specs previously stated the contract for "all four classes" as established fact; +they now say the historical half follows by symmetry. The server renders both pairs +through the same serializer helpers, so the assumption is reasonable — but a fabricated +response asserts what we *think* the server returns (invariant 1), and that is the state +the historical half is in. + +**Action:** if the stack gains a way to produce a historical result deterministically, +add the same assertions to a historical live test and delete this entry. + +## The non-null `matched_strings_dropped` path is not pinned against a live server + +**Status:** gap, probably not worth closing with a test. + +`test_live` / `test_async_live` assert only the `is None` arm — correctly, since the +per-test rule is small and the server withholds nothing from it. So the two strongest +claims `05-downstream-contract.md` makes about this field rest entirely on hand-written +pure-unit dicts: + +- a non-null count means "the list you have is short by this much", and +- it can never accompany an empty `matched_strings`, because a match's first string is + never withheld. + +That is the same "asserts what we *think* the server returns" gap invariant 1 exists to +close, and it sits alongside the historical-pair entry above. + +Producing an over-budget match on the e2e stack means a rule whose matches exceed the +server's per-hunt byte budget across a single artifact — engineering a fixture for that is +disproportionate to what it would pin. **Recorded rather than tested, deliberately.** If a +stack fixture ever produces one cheaply, assert both claims there and delete this entry. diff --git a/src/polyswarm_api/__init__.py b/src/polyswarm_api/__init__.py index dadcc971..d9cf1695 100644 --- a/src/polyswarm_api/__init__.py +++ b/src/polyswarm_api/__init__.py @@ -1,5 +1,5 @@ # https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names -__version__ = '4.4.0' +__version__ = '4.5.0' __release_url__ = 'https://api.github.com/repos/polyswarm/polyswarm-api/releases/latest' from . import api diff --git a/src/polyswarm_api/aio/api.py b/src/polyswarm_api/aio/api.py index 22a3ad0a..d9377401 100644 --- a/src/polyswarm_api/aio/api.py +++ b/src/polyswarm_api/aio/api.py @@ -697,7 +697,8 @@ async def ruleset_delete(self, ruleset_id): return await self._single(resources.YaraRuleset.delete(self, id=ruleset_id, community=self.community)) async def ruleset_list(self, name=None, status=None, favorites_only=None, - has_new_results=None): + has_new_results=None, sort=None, + exclude_favorites=None): """ List all YaraRulesets for the current account. @@ -706,17 +707,53 @@ async def ruleset_list(self, name=None, status=None, favorites_only=None, :param status: 'active' returns only rulesets whose live hunt is currently running. :param favorites_only: True returns only favorited rulesets. + :param exclude_favorites: True returns only the rulesets that are NOT + favorited — the inverse of ``favorites_only``, and refused together + with it (a contradiction, answered with an error rather than an + empty list). It exists for clients that render the favorites as + their own list: the favorites are a separate, unpaginated fetch + bounded by the account's budget, so leaving them in the paginated + list too makes a page either repeat a row or come back short. + Appended to the signature rather than placed beside + ``favorites_only`` so a positional caller keeps working. :param has_new_results: True returns only rulesets whose stored new-results counter is positive. The counter (and its window) is maintained server-side by a scheduled refresh; rows carry it as ``new_results_count`` with ``new_results_counted_at`` marking when it was last refreshed. There is no per-request window parameter. + :param sort: ``'active_first'`` returns the rulesets that carry a live + hunt link first, newest first within each block. Default (None) is + newest first. "Newest first" is the server's own insertion key, NOT + the ``id`` on the rows you get back — that one is unique but + unordered, so dedupe with it and never resume or bound a walk with + it. Applied SERVER-side, across pages — the list is + keyset-paginated, so a client-side sort would only ever reorder one + page; the SDK never re-orders rows. Reuse a page's ``offset`` only + with the same ``sort``: the server refuses a cursor minted under + the other order. + + Two server-side properties of that key, neither of them SDK + behaviour. It ranks on the stored link, which is a WIDER predicate + than the one ``livescan_id`` is rendered under: a legacy row whose + hunt was stopped without clearing the link ranks in the leading + block while still serializing ``livescan_id`` as ``None``. Read the + field to decide whether a ruleset is running; never the position. + + And the key is MUTABLE, unlike that default: a ruleset whose + live hunt stops mid-walk falls back into the idle block below the + cursor and is yielded twice, and one started mid-walk moves above + the cursor and is skipped for the rest of that walk. That is a + property of the walk, so starting fresh from the first page does + not avoid it. This generator streams pages and does not dedupe — + dedupe by ``id`` if you consume more than one page. :return: A generator of YaraRuleset resources """ logger.info('List rulesets') async for item in self._paginate(resources.YaraRuleset.list( self, name=name, status=status, favorites_only=favorites_only, - has_new_results=has_new_results, community=self.community)): + has_new_results=has_new_results, sort=sort, + exclude_favorites=exclude_favorites, + community=self.community)): yield item async def ruleset_favorite(self, ruleset_id, favorite=True): diff --git a/src/polyswarm_api/api.py b/src/polyswarm_api/api.py index 3e67d983..3f98cc5d 100644 --- a/src/polyswarm_api/api.py +++ b/src/polyswarm_api/api.py @@ -837,7 +837,13 @@ def ruleset_delete(self, ruleset_id): ) def ruleset_list( - self, name=None, status=None, favorites_only=None, has_new_results=None + self, + name=None, + status=None, + favorites_only=None, + has_new_results=None, + sort=None, + exclude_favorites=None, ): """ List all YaraRulesets for the current account. @@ -847,11 +853,45 @@ def ruleset_list( :param status: 'active' returns only rulesets whose live hunt is currently running. :param favorites_only: True returns only favorited rulesets. + :param exclude_favorites: True returns only the rulesets that are NOT + favorited — the inverse of ``favorites_only``, and refused together + with it (a contradiction, answered with an error rather than an + empty list). It exists for clients that render the favorites as + their own list: the favorites are a separate, unpaginated fetch + bounded by the account's budget, so leaving them in the paginated + list too makes a page either repeat a row or come back short. + Appended to the signature rather than placed beside + ``favorites_only`` so a positional caller keeps working. :param has_new_results: True returns only rulesets whose stored new-results counter is positive. The counter (and its window) is maintained server-side by a scheduled refresh; rows carry it as ``new_results_count`` with ``new_results_counted_at`` marking when it was last refreshed. There is no per-request window parameter. + :param sort: ``'active_first'`` returns the rulesets that carry a live + hunt link first, newest first within each block. Default (None) is + newest first. "Newest first" is the server's own insertion key, NOT + the ``id`` on the rows you get back — that one is unique but + unordered, so dedupe with it and never resume or bound a walk with + it. Applied SERVER-side, across pages — the list is + keyset-paginated, so a client-side sort would only ever reorder one + page; the SDK never re-orders rows. Reuse a page's ``offset`` only + with the same ``sort``: the server refuses a cursor minted under + the other order. + + Two server-side properties of that key, neither of them SDK + behaviour. It ranks on the stored link, which is a WIDER predicate + than the one ``livescan_id`` is rendered under: a legacy row whose + hunt was stopped without clearing the link ranks in the leading + block while still serializing ``livescan_id`` as ``None``. Read the + field to decide whether a ruleset is running; never the position. + + And the key is MUTABLE, unlike that default: a ruleset whose + live hunt stops mid-walk falls back into the idle block below the + cursor and is yielded twice, and one started mid-walk moves above + the cursor and is skipped for the rest of that walk. That is a + property of the walk, so starting fresh from the first page does + not avoid it. This generator streams pages and does not dedupe — + dedupe by ``id`` if you consume more than one page. :return: A generator of YaraRuleset resources """ logger.info("List rulesets") @@ -862,6 +902,8 @@ def ruleset_list( status=status, favorites_only=favorites_only, has_new_results=has_new_results, + sort=sort, + exclude_favorites=exclude_favorites, community=self.community, ) ): diff --git a/src/polyswarm_api/resources.py b/src/polyswarm_api/resources.py index b898ac8f..19a26e10 100644 --- a/src/polyswarm_api/resources.py +++ b/src/polyswarm_api/resources.py @@ -805,6 +805,11 @@ def __init__(self, content, api=None): self.sha1 = content.get('sha1') self.rule_name = content['rule_name'] self.tags = content['tags'] + # `.get()`, not a subscript -- both keys are additive, so an older server omits + # them. None is AMBIGUOUS on both (four causes) and is never a claim that the + # evidence is complete. Contract: specs/05-downstream-contract.md. + self.matched_strings = content.get('matched_strings') + self.matched_strings_dropped = content.get('matched_strings_dropped') self.polyscore = content['polyscore'] self.malware_family = content['malware_family'] self.detections = content['detections'] @@ -863,6 +868,11 @@ def __init__(self, content, api=None): self.created = core.parse_isoformat(content['created']) self.rule_name = content['rule_name'] self.tags = content['tags'] + # `.get()`, not a subscript -- both keys are additive, so an older server omits + # them. None is AMBIGUOUS on both (four causes) and is never a claim that the + # evidence is complete. Contract: specs/05-downstream-contract.md. + self.matched_strings = content.get('matched_strings') + self.matched_strings_dropped = content.get('matched_strings_dropped') self.polyscore = content['polyscore'] self.malware_family = content['malware_family'] self.detections = content['detections'] diff --git a/test/async_client_test.py b/test/async_client_test.py index 8d7bf462..9dd98348 100644 --- a/test/async_client_test.py +++ b/test/async_client_test.py @@ -607,6 +607,47 @@ async def test_async_sample(self, uid): # ── YARA Rulesets ───────────────────────────────────────────────────────── + @vcr.use_cassette() + async def test_async_rules_sort_active_first(self, uid): + """Async twin of the sync ``test_rules_sort_active_first``: the + canonical transport must send the same token and read the same + server-applied order.""" + async with self._api() as api: + running = await api.ruleset_create(f'{uid}-running', uid_yara(f'{uid}-running')) + idle = None + try: + idle = await api.ruleset_create(f'{uid}-idle', uid_yara(f'{uid}-idle')) + await api.live_start(int(running.id)) + try: + async def _enabled(): + return (await api.ruleset_get(running.id)).livescan_id is not None + assert await poll_equals_async(_enabled, True) + + async def _running_precedes_idle(**kwargs): + # Membership-tolerant on purpose — see the sync twin: + # a replica missing `idle` must read as "not yet true" + # and be retried, not raise out of the poll. + ids = [r.id async for r in api.ruleset_list(**kwargs)] + if running.id not in ids or idle.id not in ids: + return None + return ids.index(running.id) < ids.index(idle.id) + + async def _sorted(): + return await _running_precedes_idle(sort='active_first') + assert await poll_equals_async(_sorted, True) + # Polled like the sorted arm — see the sync twin. + async def _unsorted(): + return await _running_precedes_idle() + assert await poll_equals_async(_unsorted, False) is False + with pytest.raises(exceptions.RequestException): + _ = [r async for r in api.ruleset_list(sort='bogus')] + finally: + await api.live_stop(int(running.id)) + finally: + await api.ruleset_delete(int(running.id)) + if idle is not None: + await api.ruleset_delete(int(idle.id)) + @vcr.use_cassette() async def test_async_rules(self, uid): async with self._api() as api: @@ -865,6 +906,30 @@ async def test_async_live(self, uid): result = await api.live_result(result_id) assert result.download_url + # The list/detail split, pinned against the real server rather than prose. + # The pure-unit tests exercise dict.get and would pass identically if the + # server never grew the field; only a cassette shows what it actually sent. + assert result.matched_strings, ( + 'detail route should carry the yara evidence. A null here against an\n' + 'otherwise-green stack means the analyzer image predates the change\n' + 'that emits `strings` -- check the analyzer, not this repo.') + # On .json for the same reason as the count below: the attribute cannot + # distinguish a served null from an absent key, and specs/05 claims the + # list route sends an explicit null. + assert my_results[0].json['matched_strings'] is None, \ + 'list rows carry the key as null, not the evidence' + # On .json, not the attribute: `is None` cannot tell a served null from an + # absent key, and what needs pinning is that the server SENDS this field. + assert 'matched_strings_dropped' in result.json, \ + 'server must serve the withheld-count field' + assert result.matched_strings_dropped is None, \ + 'nothing withheld for a match this small' + # The per-entry shape is contract (specs/05) but was pinned only by a hand-written + # fixture -- i.e. what we THINK the server sends. This asserts it against what the + # server actually sent, so a key rename cannot pass the suite VCR-off. + assert set(result.matched_strings[0]) == { + 'offset', 'identifier', 'length', 'data', 'truncated'}, result.matched_strings[0] + await api.live_feed_delete([result_id]) with pytest.raises(exceptions.NotFoundException): await api.live_result(result_id) diff --git a/test/client_scan_test.py b/test/client_scan_test.py index 912bed55..26f6e6aa 100644 --- a/test/client_scan_test.py +++ b/test/client_scan_test.py @@ -508,6 +508,30 @@ def test_live(self): result = api.live_result(result_id) assert result.download_url + # The list/detail split, pinned against the real server rather than prose. + # The pure-unit tests exercise dict.get and would pass identically if the + # server never grew the field; only a cassette shows what it actually sent. + assert result.matched_strings, ( + 'detail route should carry the yara evidence. A null here against an\n' + 'otherwise-green stack means the analyzer image predates the change\n' + 'that emits `strings` -- check the analyzer, not this repo.') + # On .json for the same reason as the count below: the attribute cannot + # distinguish a served null from an absent key, and specs/05 claims the + # list route sends an explicit null. + assert my_results[0].json['matched_strings'] is None, \ + 'list rows carry the key as null, not the evidence' + # On .json, not the attribute: `is None` cannot tell a served null from an + # absent key, and what needs pinning is that the server SENDS this field. + assert 'matched_strings_dropped' in result.json, \ + 'server must serve the withheld-count field' + assert result.matched_strings_dropped is None, \ + 'nothing withheld for a match this small' + # The per-entry shape is contract (specs/05) but was pinned only by a hand-written + # fixture -- i.e. what we THINK the server sends. This asserts it against what the + # server actually sent, so a key rename cannot pass the suite VCR-off. + assert set(result.matched_strings[0]) == { + 'offset', 'identifier', 'length', 'data', 'truncated'}, result.matched_strings[0] + api.live_feed_delete([result_id]) with pytest.raises(exceptions.NotFoundException): api.live_result(result_id) @@ -577,6 +601,74 @@ def test_historical_results(self): except (exceptions.NotFoundException, exceptions.NoResultsException): pass + @vcr.use_cassette() + # NO e2e arm for `exclude_favorites`, deliberately and with a cost. + # specs/04 invariant 1 wants endpoint behaviour tested against the real + # server, and the reason is spelled out below: the server ignores unknown + # query args, so a renamed token leaves builder tests green and the list + # unfiltered. What covers it instead: + # * `TestRulesetListSortOnTheWire` drives BOTH client methods and fails if + # either stops forwarding the keyword (verified by deleting the + # pass-through: one test fails, the rest stay green); + # * the server side pins the filter itself, and the 400 for the + # contradictory pair, in its own HTTP suite against a real database. + # What stays uncovered is a rename that both sides make in lockstep with + # the server's spelling — the case only a live request catches. Recording + # the cassette needs a stack whose AKM carries the fixture account; ours + # answers 500 for a hand-seeded one, so it is honest to say this is + # missing rather than to fake a recording. + def test_rules_sort_active_first(self): + """``sort='active_first'`` is an order the SERVER applies: two rulesets + owned by this test, the older one with a live hunt running, the newer + one idle. Newest-first (the default) puts the idle one ahead; the + active-first order puts the running one ahead — a relation the server + must actually satisfy, which no pure-unit test can express (the server + ignores unknown query args, so a renamed token would leave the builder + tests green and the list unsorted). Relative positions only: the + shared stack carries other tests' rulesets.""" + api = PolyswarmAPI(self.test_api_key, uri=f'http://ai:9696/{self.api_version}', community='gamma') + uid = self._testMethodName + running = api.ruleset_create(f'{uid}-running', uid_yara(f'{uid}-running')) + idle = None + try: + idle = api.ruleset_create(f'{uid}-idle', uid_yara(f'{uid}-idle')) + api.live_start(int(running.id)) + try: + # the enable lands asynchronously and reads come off the + # replica — poll (specs/04) + assert poll_equals( + lambda: api.ruleset_get(running.id).livescan_id is not None, True) + + def _running_precedes_idle(**kwargs): + # Membership-tolerant on purpose: this is polled, and a + # replica that has not applied `idle` yet must read as "not + # yet true" and be retried. `.index()` would raise + # ValueError, which poll_equals does not absorb, and the + # lag the poll exists for would surface as an error on the + # first attempt instead. + ids = [r.id for r in api.ruleset_list(**kwargs)] + if running.id not in ids or idle.id not in ids: + return None + return ids.index(running.id) < ids.index(idle.id) + + assert poll_equals(lambda: _running_precedes_idle(sort='active_first'), True) + # The default order is untouched: the newer (idle) ruleset + # first. Polled like the sorted arm above — the helper returns + # None while either row is missing, so an unpolled read would + # assert `None is False` on a lagging replica instead of + # retrying. `want=False` is not None, so poll_equals accepts it. + assert poll_equals(_running_precedes_idle, False) is False + # a sort the server does not know is refused, never ignored + with self.assertRaises(exceptions.RequestException): + list(api.ruleset_list(sort='bogus')) + finally: + # a running live hunt blocks deletion server-side + api.live_stop(int(running.id)) + finally: + api.ruleset_delete(int(running.id)) + if idle is not None: + api.ruleset_delete(int(idle.id)) + @vcr.use_cassette() def test_rules(self): api = PolyswarmAPI(self.test_api_key, uri=f'http://ai:9696/{self.api_version}', community='gamma') diff --git a/test/hunt_matched_strings_test.py b/test/hunt_matched_strings_test.py new file mode 100644 index 00000000..d2149486 --- /dev/null +++ b/test/hunt_matched_strings_test.py @@ -0,0 +1,136 @@ +"""Tests for the `matched_strings` attribute on hunt-result resources. + +Pure-unit: the resources parse a dict, so no HTTP and no stack are involved. The +point of these is the THREE-state contract (absent / empty / populated) documented +in specs/05-downstream-contract.md — the states are not interchangeable and a +consumer that collapses them loses the distinction between "we don't know" and +"the rule matched with no byte evidence". +""" + +import copy + +import pytest + +from polyswarm_api.resources import ( + HistoricalHuntResult, + HistoricalHuntResultList, + LiveHuntResult, + LiveHuntResultList, +) + +_COMMON = { + "id": 1, + "instance_id": 2, + "created": "2022-05-26T19:41:33.797898", + "sha256": "f" * 64, + "rule_name": "dos_stub_message", + "tags": "{pe,stub}", + "polyscore": 0.5, + "malware_family": None, + "detections": {"malicious": 1, "total": 1}, +} + +_STRINGS = [ + {"offset": 78, "identifier": "$stub", "length": 14, + "data": "54 68 69 73 20 70 72 6F 67 72 61 6D 20 63", "truncated": False}, + {"offset": 0, "identifier": "$mz", "length": 512, + "data": "4D 5A 90 00 ...", "truncated": True}, +] + +# Both concrete classes plus their `…List` subclasses, which inherit __init__ and must +# behave identically. NB the subclass does not imply the route: live_feed parses list +# rows as LiveHuntResult, while the ...List classes parse DELETE responses. +ALL_CLASSES = [ + LiveHuntResult, LiveHuntResultList, + HistoricalHuntResult, HistoricalHuntResultList, +] + + +def _content(cls, **extra): + content = dict(_COMMON, **extra) + if issubclass(cls, LiveHuntResult): + content["livescan_id"] = 3 + else: + content["historicalscan_id"] = 3 + return content + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_absent_key_parses_as_none(cls): + """A server predating the field omits the key; a subscript would raise here.""" + assert cls(_content(cls)).matched_strings is None + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_explicit_null_parses_as_none(cls): + """List endpoints send the key with a null value rather than omitting it.""" + assert cls(_content(cls, matched_strings=None)).matched_strings is None + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_empty_list_is_preserved_and_is_not_none(cls): + """`[]` means "matched, no byte evidence" — distinct from "not reported".""" + result = cls(_content(cls, matched_strings=[])) + assert result.matched_strings == [] + assert result.matched_strings is not None + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_populated_list_is_passed_through_verbatim(cls): + """The SDK does not reshape entries — `data` in particular stays as yara rendered it. + + Deep-copied: passing the module-level _STRINGS stores it BY REFERENCE, so these + assertions compared the object with itself and no in-place reshape could fail them. + Same trap as test_parsing_does_not_mutate_the_raw_json. + """ + result = cls(_content(cls, matched_strings=copy.deepcopy(_STRINGS))) + assert result.matched_strings == _STRINGS + assert result.matched_strings[0]["identifier"] == "$stub" + assert result.matched_strings[1]["truncated"] is True + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_parsing_does_not_mutate_the_raw_json(cls): + """`.json` is part of the contract: JSON-mode consumers read the server's payload + unchanged, so parsing must not reshape or strip what it read. + + (Asserting `result.json[k] == content[k]` alone would be tautological -- __init__ + assigns `self.json = content` -- so this compares the parsed attributes against the + raw dict instead, which is the property that would actually break.) + """ + raw = _content(cls, matched_strings=_STRINGS, matched_strings_dropped=19) + content = copy.deepcopy(raw) + result = cls(content) + # Compared against an independent copy, so both of these can actually fail. Asserting + # `result.json is content` and then `set(content) <= set(result.json)` only restated + # the identity -- the tautology moved rather than went away. + assert content == raw, "parsing must not mutate the payload it was handed" + assert set(raw) <= set(result.json), "parsing must not drop keys from .json" + assert result.matched_strings == raw["matched_strings"] + assert result.matched_strings_dropped == raw["matched_strings_dropped"] + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_dropped_count_parses(cls): + """The count that lets a consumer say "12 shown, 19 more".""" + assert cls(_content(cls, matched_strings=_STRINGS, matched_strings_dropped=19)) \ + .matched_strings_dropped == 19 + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_dropped_is_none_when_absent(cls): + """Nothing dropped, and every result predating the budget -- same answer.""" + assert cls(_content(cls, matched_strings=_STRINGS)).matched_strings_dropped is None + + +@pytest.mark.parametrize("cls", ALL_CLASSES) +def test_a_populated_list_and_a_count_coexist(cls): + """The normal shape for a verbose ruleset, and the pairing the contract turns on. + + A truncated list is still a list -- nothing marks it short from the inside -- so the + count is the only thing that says so. Both must survive parsing together. + """ + result = cls(_content(cls, matched_strings=_STRINGS, matched_strings_dropped=19)) + assert isinstance(result.matched_strings, list) + assert len(result.matched_strings) == 2 + assert result.matched_strings_dropped == 19 diff --git a/test/hunt_tracking_builder_test.py b/test/hunt_tracking_builder_test.py index 1aefe79d..6d95b6ea 100644 --- a/test/hunt_tracking_builder_test.py +++ b/test/hunt_tracking_builder_test.py @@ -65,12 +65,28 @@ def test_list_routes_filters_to_the_query_with_int_bools(self): 'name': 'alpha', 'status': 'active', 'favorites_only': 1, 'has_new_results': 1, 'community': 'gamma'} + def test_exclude_favorites_rides_the_query_as_an_int_bool(self): + """The inverse filter, for clients that render the favorites as their + own list: leaving them in the paginated list too makes a page repeat a + row or come back short. Same int-bool coercion as its sibling. + + NOTE this exercises the generic builder, which this change does not + touch — the test that actually covers the new code is + ``TestRulesetListSortOnTheWire`` below, which drives both CLIENT + methods and would fail if either stopped forwarding the keyword.""" + api = _FakeApi() + req = resources.YaraRuleset.list( + api, exclude_favorites=True, sort='active_first', + community=api.community) + assert req.params == { + 'exclude_favorites': 1, 'sort': 'active_first', 'community': 'gamma'} + def test_list_omits_every_unset_filter(self): # The no-filter request is byte-compatible with the pre-filter # contract: nothing but community rides the query string. req = resources.YaraRuleset.list( _FakeApi(), name=None, status=None, favorites_only=None, - has_new_results=None, community='gamma') + has_new_results=None, exclude_favorites=None, community='gamma') assert req.params == {'community': 'gamma'} @@ -238,6 +254,119 @@ def test_zero_is_sent_and_absent_is_omitted(self): omitted = resources.LiveHuntResult.list(_FakeApi(), since=None, community='gamma') assert 'since' not in omitted.params + +class TestRulesetListSortOnTheWire: + """``ruleset_list(sort='active_first')`` — the hunt page's active-first + order is an opt-in server token, and it must REACH the server + exactly as such: the unsorted call sends no ``sort`` at all (the request + stays byte-compatible with the pre-sort contract and the list keeps the + server's default newest-first order — by the server's own insertion key, + NOT by the ``id`` on the rows, which is unique but unordered), and the SDK + never re-orders client-side — the list is keyset-paginated, so a local sort + would only ever reorder one page. + + Both transports are driven: the sync mirror (what ``polyswarm-cli`` + calls) and the canonical async source unasync generates it from.""" + + @staticmethod + def _sync_params(**kwargs): + api = PolyswarmAPI.__new__(PolyswarmAPI) + api.uri = _FakeApi.uri + api.community = _FakeApi.community + captured = {} + + def capture(request, *a, **kw): + captured.update(request.params) + captured['__url__'] = request.url + return iter(()) + + api._paginate = capture + list(api.ruleset_list(**kwargs)) + return captured + + @staticmethod + def _async_params(**kwargs): + api = PolySwarmAsyncAPI.__new__(PolySwarmAsyncAPI) + api.uri = _FakeApi.uri + api.community = _FakeApi.community + captured = {} + + async def paginate(request, *a, **kw): + captured.update(request.params) + return + yield # pragma: no cover — makes this an async generator + + api._paginate = paginate + + async def run(): + return [item async for item in api.ruleset_list(**kwargs)] + + asyncio.run(run()) + return captured + + def test_sync_sends_the_server_token_and_nothing_else_new(self): + sent = self._sync_params(sort='active_first') + assert sent['__url__'] == f'{_FakeApi.uri}/hunt/rule/list' + assert {k: v for k, v in sent.items() if k != '__url__'} == { + 'sort': 'active_first', 'community': 'gamma'} + + def test_sync_default_sends_no_sort(self): + sent = self._sync_params() + assert 'sort' not in sent + + def test_sort_composes_with_the_filters(self): + sent = self._sync_params(sort='active_first', status='active', + favorites_only=True) + assert sent['sort'] == 'active_first' + assert sent['status'] == 'active' + assert sent['favorites_only'] == 1 + + def test_async_canonical_sends_the_same_token(self): + sent = self._async_params(sort='active_first') + assert sent == {'sort': 'active_first', 'community': 'gamma'} + assert 'sort' not in self._async_params() + + def test_exclude_favorites_reaches_the_server_on_both_clients(self): + """The keyword the hunt page pairs with the sort, driven through the + CLIENT methods rather than the shared builder: dropping it from either + transport's signature or its pass-through fails here, which is what the + builder-level test cannot see.""" + sent = self._sync_params(sort='active_first', exclude_favorites=True) + assert sent['exclude_favorites'] == 1 + assert sent['sort'] == 'active_first' + assert self._async_params(sort='active_first', exclude_favorites=True) == { + 'exclude_favorites': 1, 'sort': 'active_first', 'community': 'gamma'} + + def test_exclude_favorites_is_omitted_when_unset(self): + # Same rule as every other filter: the unfiltered request stays + # byte-compatible with the pre-change contract. + assert 'exclude_favorites' not in self._sync_params() + assert 'exclude_favorites' not in self._async_params(sort='active_first') + + def test_sort_survives_onto_the_next_page(self): + # The order is only meaningful across pages, and page 2 is built by + # _next_page cloning the descriptor's params — the one place a + # rewrite could rebuild them from scratch and drop `sort` silently. + # The _paginate stubs above never reach it, so drive it directly. + api = PolySwarmAsyncAPI.__new__(PolySwarmAsyncAPI) + api.uri = _FakeApi.uri + api.community = _FakeApi.community + dispatched = [] + + class _Session: + async def execute(self, request, *a, **kw): + dispatched.append(request) + return request + + api.session = _Session() + first = resources.YaraRuleset.list(api, sort='active_first', community='gamma') + first.offset, first.limit = 'opaque-cursor', 25 + asyncio.run(api._next_page(first)) + assert len(dispatched) == 1 + assert dispatched[0].params == {'sort': 'active_first', 'community': 'gamma', + 'offset': 'opaque-cursor', 'limit': 25} + + class TestAsyncLiveFeedMaxResults: """The CANONICAL async bound loop, not the generated mirror. diff --git a/test/vcr/test_async_live.vcr b/test/vcr/test_async_live.vcr index 9becae4c..3b7d9f69 100644 --- a/test/vcr/test_async_live.vcr +++ b/test/vcr/test_async_live.vcr @@ -18,12 +18,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: POST uri: http://ai:9696/v3/hunt/rule response: body: - string: '{"result":{"created":"2026-06-03T22:37:20.835178+00:00","deleted":false,"description":null,"id":"21319123963942093","livescan_created":null,"livescan_id":null,"modified":"2026-06-03T22:37:20.835178+00:00","name":"sdk-test_async_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:42.713162+00:00","deleted":false,"description":null,"id":"82290800532833154","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:42.713162+00:00","name":"sdk-test_async_live","yara":"rule sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} ' @@ -39,7 +39,7 @@ interactions: Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:20 GMT + - Sat, 29 Aug 2026 00:11:42 GMT Server: - gunicorn X-Billing-ID: @@ -48,7 +48,7 @@ interactions: code: 200 message: OK - request: - body: '{"rule_id":"21319123963942093"}' + body: '{"rule_id":"82290800532833154"}' headers: accept: - '*/*' @@ -65,12 +65,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: POST uri: http://ai:9696/v3/hunt/rule/live response: body: - string: '{"result":{"created":"2026-06-03T22:37:20.835178+00:00","deleted":false,"description":null,"id":"21319123963942093","livescan_created":"2026-06-03T22:37:20.890721+00:00","livescan_id":null,"modified":"2026-06-03T22:37:20.835178+00:00","name":"sdk-test_async_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:42.713162+00:00","deleted":false,"description":null,"id":"82290800532833154","livescan_created":"2026-08-29T00:11:42.802951+00:00","livescan_id":63056499228390147,"modified":"2026-08-29T00:11:42.800524+00:00","name":"sdk-test_async_live","yara":"rule sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} ' @@ -82,11 +82,11 @@ interactions: Connection: - keep-alive Content-Length: - - '366' + - '379' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:20 GMT + - Sat, 29 Aug 2026 00:11:42 GMT Server: - gunicorn X-Billing-ID: @@ -108,12 +108,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/rule?id=21319123963942093&community=gamma + uri: http://ai:9696/v3/hunt/rule?id=82290800532833154&community=gamma response: body: - string: '{"result":{"created":"2026-06-03T22:37:20.835178+00:00","deleted":false,"description":null,"id":"21319123963942093","livescan_created":"2026-06-03T22:37:20.890721+00:00","livescan_id":88206418106491329,"modified":"2026-06-03T22:37:20.863779+00:00","name":"sdk-test_async_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:42.713162+00:00","deleted":false,"description":null,"id":"82290800532833154","livescan_created":"2026-08-29T00:11:42.802951+00:00","livescan_id":63056499228390147,"modified":"2026-08-29T00:11:42.800524+00:00","name":"sdk-test_async_live","yara":"rule sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} ' @@ -129,7 +129,7 @@ interactions: Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:21 GMT + - Sat, 29 Aug 2026 00:11:43 GMT Server: - gunicorn X-Billing-ID: @@ -155,12 +155,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: POST uri: http://ai:9696/v3/instance response: body: - string: '{"result":{"artifact_id":"94943400355321313","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-06-03T22:37:21.946455+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-06-03T22:37:21.946455+00:00","id":"94943400355321313","last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/94943400355321313","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/7e/34/70/7e34708c-caf6-4dd7-b5d2-9e1029e0dafc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223721Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=ef5a83090f1169483ad45b80d4b6bf9bf2a46ca939f4070b11ccc19254e68a8e","votes":[],"window_closed":false},"status":"OK"} + string: '{"result":{"artifact_id":"78118450880280801","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-08-29T00:11:43.904670+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-08-29T00:11:43.904670+00:00","id":"78118450880280801","known_good":null,"last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/78118450880280801","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"state":"CREATED","type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/1a/f1/ca/1af1ca3a-7869-4653-b775-16bf39d98d73?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001143Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=db5df1a1ef8dfb317ced098d31b77525ecf0f29fdcc708983a274b7b780efd12","votes":[],"window_closed":false},"status":"OK"} ' headers: @@ -171,11 +171,11 @@ interactions: Connection: - keep-alive Content-Length: - - '1008' + - '1044' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:21 GMT + - Sat, 29 Aug 2026 00:11:43 GMT Server: - gunicorn X-Billing-ID: @@ -199,9 +199,9 @@ interactions: host: - minio:9000 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: PUT - uri: http://minio:9000/artifact-index/instances/7e/34/70/7e34708c-caf6-4dd7-b5d2-9e1029e0dafc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223721Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=ef5a83090f1169483ad45b80d4b6bf9bf2a46ca939f4070b11ccc19254e68a8e + uri: http://minio:9000/artifact-index/instances/1a/f1/ca/1af1ca3a-7869-4653-b775-16bf39d98d73?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001143Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=db5df1a1ef8dfb317ced098d31b77525ecf0f29fdcc708983a274b7b780efd12 response: body: string: '' @@ -211,7 +211,7 @@ interactions: Content-Length: - '0' Date: - - Wed, 03 Jun 2026 22:37:21 GMT + - Sat, 29 Aug 2026 00:11:43 GMT ETag: - '"6ac59cd96a9a3ee9d52f9ebc3ec22f70"' Server: @@ -224,17 +224,17 @@ interactions: X-Amz-Id-2: - dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8 X-Amz-Request-Id: - - 18B5B32EF4EBFFF4 + - 18D01E3E0CBB5837 X-Content-Type-Options: - nosniff X-Ratelimit-Limit: - - '13624' + - '5171' X-Ratelimit-Remaining: - - '13624' + - '5171' X-Xss-Protection: - 1; mode=block x-amz-expiration: - - expiry-date="Fri, 05 Jun 2026 00:00:00 GMT", rule-id="expiration-artifact-index_0-instances" + - expiry-date="Mon, 31 Aug 2026 00:00:00 GMT", rule-id="expiration-artifact-index_0-instances" status: code: 200 message: OK @@ -256,12 +256,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: PUT - uri: http://ai:9696/v3/instance?id=94943400355321313 + uri: http://ai:9696/v3/instance?id=78118450880280801 response: body: - string: '{"result":{"artifact_id":"94943400355321313","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-06-03T22:37:21.946455+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-06-03T22:37:21.946455+00:00","id":"94943400355321313","last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/94943400355321313","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/7e/34/70/7e34708c-caf6-4dd7-b5d2-9e1029e0dafc?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223721Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=ef5a83090f1169483ad45b80d4b6bf9bf2a46ca939f4070b11ccc19254e68a8e","votes":[],"window_closed":false},"status":"OK"} + string: '{"result":{"artifact_id":"78118450880280801","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-08-29T00:11:43.904670+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-08-29T00:11:43.904670+00:00","id":"78118450880280801","known_good":null,"last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/78118450880280801","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"state":"CREATED","type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/1a/f1/ca/1af1ca3a-7869-4653-b775-16bf39d98d73?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001143Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=db5df1a1ef8dfb317ced098d31b77525ecf0f29fdcc708983a274b7b780efd12","votes":[],"window_closed":false},"status":"OK"} ' headers: @@ -272,11 +272,11 @@ interactions: Connection: - keep-alive Content-Length: - - '1008' + - '1044' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:21 GMT + - Sat, 29 Aug 2026 00:11:43 GMT Server: - gunicorn X-Billing-ID: @@ -298,151 +298,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:22 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:24 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:25 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:26 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -458,7 +314,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:37:27 GMT + - Sat, 29 Aug 2026 00:11:44 GMT Server: - gunicorn status: @@ -478,7 +334,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -494,7 +350,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:37:28 GMT + - Sat, 29 Aug 2026 00:11:46 GMT Server: - gunicorn status: @@ -514,7 +370,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -530,7 +386,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:37:29 GMT + - Sat, 29 Aug 2026 00:11:47 GMT Server: - gunicorn status: @@ -550,7 +406,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -566,7 +422,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:37:30 GMT + - Sat, 29 Aug 2026 00:11:48 GMT Server: - gunicorn status: @@ -586,7 +442,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -602,7 +458,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:37:31 GMT + - Sat, 29 Aug 2026 00:11:49 GMT Server: - gunicorn status: @@ -622,7 +478,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -638,7 +494,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:37:32 GMT + - Sat, 29 Aug 2026 00:11:50 GMT Server: - gunicorn status: @@ -658,12 +514,14 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: body: - string: '' + string: '{"has_more":false,"limit":50,"result":[{"community":"gamma","created":"2026-08-29T00:11:51.184667+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-08-29T00:11:43.904670+00:00","id":"27253623675037251","instance_id":"78118450880280801","livescan_id":"63056499228390147","malware_family":"EICAR","matched_strings":null,"matched_strings_dropped":null,"md5":"6ac59cd96a9a3ee9d52f9ebc3ec22f70","polyscore":null,"rule_name":"sdk_test_async_live","sha1":"fade22953ce5af251e2dbd7491124c818bf35e5e","sha256":"ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5","tags":"{}","yara":null}],"status":"OK"} + + ' headers: Access-Control-Allow-Origin: - '*' @@ -671,15 +529,19 @@ interactions: - Authorization Connection: - keep-alive + Content-Length: + - '654' Content-Type: - - text/html; charset=utf-8 + - application/json Date: - - Wed, 03 Jun 2026 22:37:33 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn + X-Billing-ID: + - '111' status: - code: 204 - message: NO CONTENT + code: 200 + message: OK - request: body: '' headers: @@ -694,12 +556,15 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma + uri: http://ai:9696/v3/hunt/live?id=27253623675037251 response: body: - string: '' + string: '{"result":{"community":"gamma","created":"2026-08-29T00:11:51.184667+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":"http://minio:9000/public-cache/ff/2b/25/ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5fade22953ce5af251e2dbd7491124c818bf35e5e6ac59cd96a9a3ee9d52f9ebc3ec22f70?response-content-disposition=attachment%3Bfilename%3Dinfected&response-content-type=application%2Foctet-stream&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001151Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=adc60fb87017ea3a84210c55f388665cb85763ec44687439643e81451da65e48","first_seen":"2026-08-29T00:11:43.904670+00:00","id":"27253623675037251","instance_id":"78118450880280801","livescan_id":"63056499228390147","malware_family":"EICAR","matched_strings":[{"data":"test_async_live","identifier":"$u","length":15,"offset":69,"truncated":false}],"matched_strings_dropped":null,"md5":"6ac59cd96a9a3ee9d52f9ebc3ec22f70","polyscore":null,"rule_name":"sdk_test_async_live","sha1":"fade22953ce5af251e2dbd7491124c818bf35e5e","sha256":"ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5","tags":"{}","yara":"rule + sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} + + ' headers: Access-Control-Allow-Origin: - '*' @@ -707,17 +572,21 @@ interactions: - Authorization Connection: - keep-alive + Content-Length: + - '1334' Content-Type: - - text/html; charset=utf-8 + - application/json Date: - - Wed, 03 Jun 2026 22:37:34 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn + X-Billing-ID: + - '111' status: - code: 204 - message: NO CONTENT + code: 200 + message: OK - request: - body: '' + body: '{"result_ids":["27253623675037251"]}' headers: accept: - '*/*' @@ -727,15 +596,21 @@ interactions: - '11111111111111111111111111111111' connection: - keep-alive + content-length: + - '36' + content-type: + - application/json host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) + method: DELETE + uri: http://ai:9696/v3/hunt/live/list response: body: - string: '' + string: '{"has_more":true,"limit":50,"result":[{"community":"gamma","created":"2026-08-29T00:11:51.184667+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-08-29T00:11:43.904670+00:00","id":"27253623675037251","instance_id":"78118450880280801","livescan_id":"63056499228390147","malware_family":"EICAR","matched_strings":null,"matched_strings_dropped":null,"md5":"6ac59cd96a9a3ee9d52f9ebc3ec22f70","polyscore":null,"rule_name":"sdk_test_async_live","sha1":"fade22953ce5af251e2dbd7491124c818bf35e5e","sha256":"ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5","tags":"{}","yara":null}],"status":"OK"} + + ' headers: Access-Control-Allow-Origin: - '*' @@ -743,15 +618,19 @@ interactions: - Authorization Connection: - keep-alive + Content-Length: + - '653' Content-Type: - - text/html; charset=utf-8 + - application/json Date: - - Wed, 03 Jun 2026 22:37:35 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn + X-Billing-ID: + - '111' status: - code: 204 - message: NO CONTENT + code: 200 + message: OK - request: body: '' headers: @@ -766,12 +645,15 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma + uri: http://ai:9696/v3/hunt/live?id=27253623675037251 response: body: - string: '' + string: '{"errors":null,"result":"Could not find requested live hunt result: + 27253623675037251.","status":"error"} + + ' headers: Access-Control-Allow-Origin: - '*' @@ -779,17 +661,19 @@ interactions: - Authorization Connection: - keep-alive + Content-Length: + - '106' Content-Type: - - text/html; charset=utf-8 + - application/json Date: - - Wed, 03 Jun 2026 22:37:36 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn status: - code: 204 - message: NO CONTENT + code: 404 + message: NOT FOUND - request: - body: '' + body: '{"rule_id":"82290800532833154"}' headers: accept: - '*/*' @@ -799,15 +683,22 @@ interactions: - '11111111111111111111111111111111' connection: - keep-alive + content-length: + - '31' + content-type: + - application/json host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) + method: DELETE + uri: http://ai:9696/v3/hunt/rule/live response: body: - string: '' + string: '{"result":{"created":"2026-08-29T00:11:42.713162+00:00","deleted":false,"description":null,"id":"82290800532833154","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:51.325659+00:00","name":"sdk-test_async_live","yara":"rule + sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} + + ' headers: Access-Control-Allow-Origin: - '*' @@ -815,15 +706,19 @@ interactions: - Authorization Connection: - keep-alive + Content-Length: + - '336' Content-Type: - - text/html; charset=utf-8 + - application/json Date: - - Wed, 03 Jun 2026 22:37:37 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn + X-Billing-ID: + - '111' status: - code: 204 - message: NO CONTENT + code: 200 + message: OK - request: body: '' headers: @@ -838,738 +733,15 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma + uri: http://ai:9696/v3/hunt/rule?id=82290800532833154&community=gamma response: body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:38 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:39 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:40 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:41 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:42 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:43 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:44 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:45 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:46 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:47 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:48 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:49 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:50 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:37:51 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '{"has_more":false,"limit":50,"result":[{"community":"gamma","created":"2026-06-03T22:37:52.083918+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-06-03T22:37:21.946455+00:00","id":"74599466085661100","instance_id":"94943400355321313","livescan_id":"88206418106491329","malware_family":"EICAR","md5":"6ac59cd96a9a3ee9d52f9ebc3ec22f70","polyscore":null,"rule_name":"sdk_test_async_live","sha1":"fade22953ce5af251e2dbd7491124c818bf35e5e","sha256":"ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5","tags":"{}","yara":null}],"status":"OK"} - - ' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Length: - - '600' - Content-Type: - - application/json - Date: - - Wed, 03 Jun 2026 22:37:52 GMT - Server: - - gunicorn - X-Billing-ID: - - '111' - status: - code: 200 - message: OK -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live?id=74599466085661100 - response: - body: - string: '{"result":{"community":"gamma","created":"2026-06-03T22:37:52.083918+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":"http://minio:9000/public-cache/ff/2b/25/ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5fade22953ce5af251e2dbd7491124c818bf35e5e6ac59cd96a9a3ee9d52f9ebc3ec22f70?response-content-disposition=attachment%3Bfilename%3Dinfected&response-content-type=application%2Foctet-stream&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223752Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=d5b9d22359ba7a730490da90c750dafa5f483a11e6d0631784f1badf5ef753ac","first_seen":"2026-06-03T22:37:21.946455+00:00","id":"74599466085661100","instance_id":"94943400355321313","livescan_id":"88206418106491329","malware_family":"EICAR","md5":"6ac59cd96a9a3ee9d52f9ebc3ec22f70","polyscore":null,"rule_name":"sdk_test_async_live","sha1":"fade22953ce5af251e2dbd7491124c818bf35e5e","sha256":"ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5","tags":"{}","yara":"rule - sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} - - ' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Length: - - '1196' - Content-Type: - - application/json - Date: - - Wed, 03 Jun 2026 22:37:52 GMT - Server: - - gunicorn - X-Billing-ID: - - '111' - status: - code: 200 - message: OK -- request: - body: '{"result_ids":["74599466085661100"]}' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - content-length: - - '36' - content-type: - - application/json - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: DELETE - uri: http://ai:9696/v3/hunt/live/list - response: - body: - string: '{"has_more":true,"limit":50,"result":[{"community":"gamma","created":"2026-06-03T22:37:52.083918+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-06-03T22:37:21.946455+00:00","id":"74599466085661100","instance_id":"94943400355321313","livescan_id":"88206418106491329","malware_family":"EICAR","md5":"6ac59cd96a9a3ee9d52f9ebc3ec22f70","polyscore":null,"rule_name":"sdk_test_async_live","sha1":"fade22953ce5af251e2dbd7491124c818bf35e5e","sha256":"ff2b25f3bff7613e391b4d87e32f18c172d531bee5ae562bd13b6421d8b104a5","tags":"{}","yara":null}],"status":"OK"} - - ' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Length: - - '599' - Content-Type: - - application/json - Date: - - Wed, 03 Jun 2026 22:37:52 GMT - Server: - - gunicorn - X-Billing-ID: - - '111' - status: - code: 200 - message: OK -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live?id=74599466085661100 - response: - body: - string: '{"errors":null,"result":"Could not find requested live hunt result: - 74599466085661100.","status":"error"} - - ' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Length: - - '106' - Content-Type: - - application/json - Date: - - Wed, 03 Jun 2026 22:37:52 GMT - Server: - - gunicorn - status: - code: 404 - message: NOT FOUND -- request: - body: '{"rule_id":"21319123963942093"}' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - content-length: - - '31' - content-type: - - application/json - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: DELETE - uri: http://ai:9696/v3/hunt/rule/live - response: - body: - string: '{"result":{"created":"2026-06-03T22:37:20.835178+00:00","deleted":false,"description":null,"id":"21319123963942093","livescan_created":"2026-06-03T22:37:20.890721+00:00","livescan_id":88206418106491329,"modified":"2026-06-03T22:37:20.863779+00:00","name":"sdk-test_async_live","yara":"rule - sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} - - ' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Length: - - '379' - Content-Type: - - application/json - Date: - - Wed, 03 Jun 2026 22:37:52 GMT - Server: - - gunicorn - X-Billing-ID: - - '111' - status: - code: 200 - message: OK -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/rule?id=21319123963942093&community=gamma - response: - body: - string: '{"result":{"created":"2026-06-03T22:37:20.835178+00:00","deleted":false,"description":null,"id":"21319123963942093","livescan_created":"2026-06-03T22:37:20.890721+00:00","livescan_id":null,"modified":"2026-06-03T22:37:52.675202+00:00","name":"sdk-test_async_live","yara":"rule - sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} - - ' + string: '{"result":{"created":"2026-08-29T00:11:42.713162+00:00","deleted":false,"description":null,"id":"82290800532833154","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:51.325659+00:00","name":"sdk-test_async_live","yara":"rule + sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} + + ' headers: Access-Control-Allow-Origin: - '*' @@ -1578,11 +750,11 @@ interactions: Connection: - keep-alive Content-Length: - - '366' + - '336' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:52 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn X-Billing-ID: @@ -1591,7 +763,7 @@ interactions: code: 200 message: OK - request: - body: '{"rule_id":"21319123963942093"}' + body: '{"rule_id":"82290800532833154"}' headers: accept: - '*/*' @@ -1608,7 +780,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: DELETE uri: http://ai:9696/v3/hunt/rule/live response: @@ -1628,7 +800,7 @@ interactions: Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:52 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn status: @@ -1652,12 +824,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: DELETE - uri: http://ai:9696/v3/hunt/rule?id=21319123963942093 + uri: http://ai:9696/v3/hunt/rule?id=82290800532833154 response: body: - string: '{"result":{"created":"2026-06-03T22:37:20.835178+00:00","deleted":true,"description":null,"id":"21319123963942093","livescan_created":"2026-06-03T22:37:20.890721+00:00","livescan_id":null,"modified":"2026-06-03T22:37:52.675202+00:00","name":"sdk-test_async_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:42.713162+00:00","deleted":true,"description":null,"id":"82290800532833154","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:51.427206+00:00","name":"sdk-test_async_live","yara":"rule sdk_test_async_live { strings: $u = \"test_async_live\" condition: $u }"},"status":"OK"} ' @@ -1669,11 +841,11 @@ interactions: Connection: - keep-alive Content-Length: - - '365' + - '335' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:37:52 GMT + - Sat, 29 Aug 2026 00:11:51 GMT Server: - gunicorn X-Billing-ID: diff --git a/test/vcr/test_async_rules_sort_active_first.vcr b/test/vcr/test_async_rules_sort_active_first.vcr new file mode 100644 index 00000000..dddfcdee --- /dev/null +++ b/test/vcr/test_async_rules_sort_active_first.vcr @@ -0,0 +1,460 @@ +interactions: +- request: + body: '{"yara":"rule sdk_test_async_rules_sort_active_first_running { strings: + $u = \"test_async_rules_sort_active_first-running\" condition: $u }","name":"test_async_rules_sort_active_first-running"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '193' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: POST + uri: http://ai:9696/v3/hunt/rule + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:16.616869+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:16.616869+00:00","name":"test_async_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_running { strings: $u = \"test_async_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '491' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:16 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"yara":"rule sdk_test_async_rules_sort_active_first_idle { strings: $u + = \"test_async_rules_sort_active_first-idle\" condition: $u }","name":"test_async_rules_sort_active_first-idle"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '184' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: POST + uri: http://ai:9696/v3/hunt/rule + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:17.281514+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"11708616356758131","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:17.281514+00:00","name":"test_async_rules_sort_active_first-idle","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_idle { strings: $u = \"test_async_rules_sort_active_first-idle\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '482' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:17 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"rule_id":"43837104550931486"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '31' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: POST + uri: http://ai:9696/v3/hunt/rule/live + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:16.616869+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":"2026-09-02T00:53:18.269679+00:00","livescan_id":"20350915266052341","modified":"2026-09-02T00:53:17.891619+00:00","name":"test_async_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_running { strings: $u = \"test_async_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '536' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:18 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule?id=43837104550931486&community=gamma + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:16.616869+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":"2026-09-02T00:53:18.269679+00:00","livescan_id":"20350915266052341","modified":"2026-09-02T00:53:17.891619+00:00","name":"test_async_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_running { strings: $u = \"test_async_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '536' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:18 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule/list?sort=active_first&community=gamma + response: + body: + string: '{"has_more":false,"limit":50,"result":[{"created":"2026-09-02T00:53:16.616869+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":"2026-09-02T00:53:18.269679+00:00","livescan_id":"20350915266052341","modified":"2026-09-02T00:53:17.891619+00:00","name":"test_async_rules_sort_active_first-running","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null},{"created":"2026-09-02T00:53:17.281514+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"11708616356758131","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:17.281514+00:00","name":"test_async_rules_sort_active_first-idle","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null}],"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '883' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:18 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule/list?community=gamma + response: + body: + string: '{"has_more":false,"limit":50,"result":[{"created":"2026-09-02T00:53:17.281514+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"11708616356758131","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:17.281514+00:00","name":"test_async_rules_sort_active_first-idle","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null},{"created":"2026-09-02T00:53:16.616869+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":"2026-09-02T00:53:18.269679+00:00","livescan_id":"20350915266052341","modified":"2026-09-02T00:53:17.891619+00:00","name":"test_async_rules_sort_active_first-running","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null}],"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '883' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:19 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule/list?sort=bogus&community=gamma + response: + body: + string: '{"errors":null,"result":"Invalid sort: only ''active_first'' is supported.","status":"error"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '92' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:19 GMT + server: + - gunicorn + status: + code: 400 + message: BAD REQUEST +- request: + body: '{"rule_id":"43837104550931486"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '31' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: DELETE + uri: http://ai:9696/v3/hunt/rule/live + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:16.616869+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:19.285097+00:00","name":"test_async_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_running { strings: $u = \"test_async_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '491' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:19 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"community":"gamma"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '21' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: DELETE + uri: http://ai:9696/v3/hunt/rule?id=43837104550931486 + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:16.616869+00:00","deleted":true,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"43837104550931486","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:19.472057+00:00","name":"test_async_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_running { strings: $u = \"test_async_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '490' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:19 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"community":"gamma"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '21' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: DELETE + uri: http://ai:9696/v3/hunt/rule?id=11708616356758131 + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:17.281514+00:00","deleted":true,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"11708616356758131","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:19.616902+00:00","name":"test_async_rules_sort_active_first-idle","rule_count":1,"yara":"rule + sdk_test_async_rules_sort_active_first_idle { strings: $u = \"test_async_rules_sort_active_first-idle\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '481' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:19 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +version: 1 diff --git a/test/vcr/test_live.vcr b/test/vcr/test_live.vcr index 8d4df416..165b2f26 100644 --- a/test/vcr/test_live.vcr +++ b/test/vcr/test_live.vcr @@ -18,12 +18,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: POST uri: http://ai:9696/v3/hunt/rule response: body: - string: '{"result":{"created":"2026-06-03T22:35:46.641576+00:00","deleted":false,"description":null,"id":"63962466091392300","livescan_created":null,"livescan_id":null,"modified":"2026-06-03T22:35:46.641576+00:00","name":"sdk-test_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:28.650982+00:00","deleted":false,"description":null,"id":"79693411044316426","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:28.650982+00:00","name":"sdk-test_live","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -39,7 +39,7 @@ interactions: Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:35:46 GMT + - Sat, 29 Aug 2026 00:11:28 GMT Server: - gunicorn X-Billing-ID: @@ -48,7 +48,7 @@ interactions: code: 200 message: OK - request: - body: '{"rule_id":"63962466091392300"}' + body: '{"rule_id":"79693411044316426"}' headers: accept: - '*/*' @@ -65,12 +65,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: POST uri: http://ai:9696/v3/hunt/rule/live response: body: - string: '{"result":{"created":"2026-06-03T22:35:46.641576+00:00","deleted":false,"description":null,"id":"63962466091392300","livescan_created":"2026-06-03T22:35:46.690992+00:00","livescan_id":null,"modified":"2026-06-03T22:35:46.641576+00:00","name":"sdk-test_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:28.650982+00:00","deleted":false,"description":null,"id":"79693411044316426","livescan_created":"2026-08-29T00:11:28.814637+00:00","livescan_id":6053794961344849,"modified":"2026-08-29T00:11:28.769801+00:00","name":"sdk-test_live","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -82,11 +82,11 @@ interactions: Connection: - keep-alive Content-Length: - - '348' + - '360' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:35:46 GMT + - Sat, 29 Aug 2026 00:11:28 GMT Server: - gunicorn X-Billing-ID: @@ -108,12 +108,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/rule?id=63962466091392300&community=gamma + uri: http://ai:9696/v3/hunt/rule?id=79693411044316426&community=gamma response: body: - string: '{"result":{"created":"2026-06-03T22:35:46.641576+00:00","deleted":false,"description":null,"id":"63962466091392300","livescan_created":"2026-06-03T22:35:46.690992+00:00","livescan_id":68378491128619407,"modified":"2026-06-03T22:35:46.666460+00:00","name":"sdk-test_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:28.650982+00:00","deleted":false,"description":null,"id":"79693411044316426","livescan_created":"2026-08-29T00:11:28.814637+00:00","livescan_id":6053794961344849,"modified":"2026-08-29T00:11:28.769801+00:00","name":"sdk-test_live","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -125,11 +125,11 @@ interactions: Connection: - keep-alive Content-Length: - - '361' + - '360' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:35:47 GMT + - Sat, 29 Aug 2026 00:11:29 GMT Server: - gunicorn X-Billing-ID: @@ -155,12 +155,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: POST uri: http://ai:9696/v3/instance response: body: - string: '{"result":{"artifact_id":"27835191001619039","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-06-03T22:35:47.744500+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-06-03T22:35:47.744500+00:00","id":"27835191001619039","last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/27835191001619039","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/41/9e/e7/419ee76d-f760-45c0-bc48-f9c7f1cf44e6?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223547Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=4a67c52be38b98e5fe6ccc916e1b8072e87fe9f0c266e81617c6969a74f14469","votes":[],"window_closed":false},"status":"OK"} + string: '{"result":{"artifact_id":"3936300515643678","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-08-29T00:11:30.179957+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-08-29T00:11:30.179957+00:00","id":"3936300515643678","known_good":null,"last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/3936300515643678","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"state":"CREATED","type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/d9/23/e4/d923e4f0-44a2-4b0a-8812-165751c5e8e9?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001130Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=6d98f2ae0827d37cc153870c2587b24b39989492f22166255b6d9ac784abf347","votes":[],"window_closed":false},"status":"OK"} ' headers: @@ -171,11 +171,11 @@ interactions: Connection: - keep-alive Content-Length: - - '1008' + - '1041' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:35:47 GMT + - Sat, 29 Aug 2026 00:11:30 GMT Server: - gunicorn X-Billing-ID: @@ -199,9 +199,9 @@ interactions: host: - minio:9000 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: PUT - uri: http://minio:9000/artifact-index/instances/41/9e/e7/419ee76d-f760-45c0-bc48-f9c7f1cf44e6?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223547Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=4a67c52be38b98e5fe6ccc916e1b8072e87fe9f0c266e81617c6969a74f14469 + uri: http://minio:9000/artifact-index/instances/d9/23/e4/d923e4f0-44a2-4b0a-8812-165751c5e8e9?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001130Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=6d98f2ae0827d37cc153870c2587b24b39989492f22166255b6d9ac784abf347 response: body: string: '' @@ -211,7 +211,7 @@ interactions: Content-Length: - '0' Date: - - Wed, 03 Jun 2026 22:35:47 GMT + - Sat, 29 Aug 2026 00:11:30 GMT ETag: - '"564296ca17d5e063856e0b92c8ad6ca6"' Server: @@ -224,17 +224,17 @@ interactions: X-Amz-Id-2: - dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8 X-Amz-Request-Id: - - 18B5B31905F65115 + - 18D01E3ADC7F16BA X-Content-Type-Options: - nosniff X-Ratelimit-Limit: - - '13624' + - '5171' X-Ratelimit-Remaining: - - '13624' + - '5171' X-Xss-Protection: - 1; mode=block x-amz-expiration: - - expiry-date="Fri, 05 Jun 2026 00:00:00 GMT", rule-id="expiration-artifact-index_0-instances" + - expiry-date="Mon, 31 Aug 2026 00:00:00 GMT", rule-id="expiration-artifact-index_0-instances" status: code: 200 message: OK @@ -256,12 +256,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: PUT - uri: http://ai:9696/v3/instance?id=27835191001619039 + uri: http://ai:9696/v3/instance?id=3936300515643678 response: body: - string: '{"result":{"artifact_id":"27835191001619039","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-06-03T22:35:47.744500+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-06-03T22:35:47.744500+00:00","id":"27835191001619039","last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/27835191001619039","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/41/9e/e7/419ee76d-f760-45c0-bc48-f9c7f1cf44e6?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223547Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=4a67c52be38b98e5fe6ccc916e1b8072e87fe9f0c266e81617c6969a74f14469","votes":[],"window_closed":false},"status":"OK"} + string: '{"result":{"artifact_id":"3936300515643678","assertions":[],"bounty_state":0,"community":"gamma","country":"","created":"2026-08-29T00:11:30.179957+00:00","detections":null,"expiration_window":null,"expire_at":null,"extended_type":null,"failed":false,"filename":"artifact","first_seen":"2026-08-29T00:11:30.179957+00:00","id":"3936300515643678","known_good":null,"last_scanned":null,"last_seen":null,"md5":null,"metadata":[],"mimetype":null,"permalink":"https://polyswarm.network/scan/results/file/None/3936300515643678","polyscore":null,"result":null,"sha1":null,"sha256":null,"size":null,"state":"CREATED","type":"FILE","upload_url":"http://minio:9000/artifact-index/instances/d9/23/e4/d923e4f0-44a2-4b0a-8812-165751c5e8e9?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001130Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=6d98f2ae0827d37cc153870c2587b24b39989492f22166255b6d9ac784abf347","votes":[],"window_closed":false},"status":"OK"} ' headers: @@ -272,11 +272,11 @@ interactions: Connection: - keep-alive Content-Length: - - '1008' + - '1041' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:35:47 GMT + - Sat, 29 Aug 2026 00:11:30 GMT Server: - gunicorn X-Billing-ID: @@ -298,7 +298,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -314,7 +314,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:48 GMT + - Sat, 29 Aug 2026 00:11:31 GMT Server: - gunicorn status: @@ -334,7 +334,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -350,7 +350,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:49 GMT + - Sat, 29 Aug 2026 00:11:32 GMT Server: - gunicorn status: @@ -370,7 +370,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -386,7 +386,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:50 GMT + - Sat, 29 Aug 2026 00:11:33 GMT Server: - gunicorn status: @@ -406,7 +406,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -422,7 +422,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:51 GMT + - Sat, 29 Aug 2026 00:11:34 GMT Server: - gunicorn status: @@ -442,7 +442,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -458,7 +458,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:52 GMT + - Sat, 29 Aug 2026 00:11:35 GMT Server: - gunicorn status: @@ -478,7 +478,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -494,7 +494,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:53 GMT + - Sat, 29 Aug 2026 00:11:36 GMT Server: - gunicorn status: @@ -514,7 +514,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -530,7 +530,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:54 GMT + - Sat, 29 Aug 2026 00:11:37 GMT Server: - gunicorn status: @@ -550,7 +550,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -566,7 +566,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:55 GMT + - Sat, 29 Aug 2026 00:11:38 GMT Server: - gunicorn status: @@ -586,7 +586,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -602,7 +602,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:56 GMT + - Sat, 29 Aug 2026 00:11:39 GMT Server: - gunicorn status: @@ -622,7 +622,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: @@ -638,7 +638,7 @@ interactions: Content-Type: - text/html; charset=utf-8 Date: - - Wed, 03 Jun 2026 22:35:58 GMT + - Sat, 29 Aug 2026 00:11:40 GMT Server: - gunicorn status: @@ -658,696 +658,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma response: body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:35:59 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:00 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:01 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:02 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:03 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:04 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:05 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:06 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:07 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:08 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:09 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:10 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:11 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:12 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:13 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:14 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:15 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:16 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '' - headers: - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - Authorization - Connection: - - keep-alive - Content-Type: - - text/html; charset=utf-8 - Date: - - Wed, 03 Jun 2026 22:36:17 GMT - Server: - - gunicorn - status: - code: 204 - message: NO CONTENT -- request: - body: '' - headers: - accept: - - '*/*' - accept-encoding: - - gzip, deflate - authorization: - - '11111111111111111111111111111111' - connection: - - keep-alive - host: - - ai:9696 - user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) - method: GET - uri: http://ai:9696/v3/hunt/live/list?since=600&community=gamma - response: - body: - string: '{"has_more":false,"limit":50,"result":[{"community":"gamma","created":"2026-06-03T22:36:17.918027+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-06-03T22:33:00.053959+00:00","id":"8018942810283908","instance_id":"27835191001619039","livescan_id":"68378491128619407","malware_family":"EICAR","md5":"564296ca17d5e063856e0b92c8ad6ca6","polyscore":null,"rule_name":"sdk_test_live","sha1":"adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a","sha256":"14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190","tags":"{}","yara":null}],"status":"OK"} + string: '{"has_more":false,"limit":50,"result":[{"community":"gamma","created":"2026-08-29T00:11:40.978433+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-08-29T00:11:30.179957+00:00","id":"56607280072443305","instance_id":"3936300515643678","livescan_id":"6053794961344849","malware_family":"EICAR","matched_strings":null,"matched_strings_dropped":null,"md5":"564296ca17d5e063856e0b92c8ad6ca6","polyscore":null,"rule_name":"sdk_test_live","sha1":"adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a","sha256":"14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190","tags":"{}","yara":null}],"status":"OK"} ' headers: @@ -1358,11 +674,11 @@ interactions: Connection: - keep-alive Content-Length: - - '593' + - '646' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:41 GMT Server: - gunicorn X-Billing-ID: @@ -1384,12 +700,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/live?id=8018942810283908 + uri: http://ai:9696/v3/hunt/live?id=56607280072443305 response: body: - string: '{"result":{"community":"gamma","created":"2026-06-03T22:36:17.918027+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":"http://minio:9000/public-cache/14/e3/a4/14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a564296ca17d5e063856e0b92c8ad6ca6?response-content-disposition=attachment%3Bfilename%3Dinfected&response-content-type=application%2Foctet-stream&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260603%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260603T223618Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=dc5cf083031090e9c5266463a7f34d3180cc5cde2fccfc6204cc23d4481b22a5","first_seen":"2026-06-03T22:33:00.053959+00:00","id":"8018942810283908","instance_id":"27835191001619039","livescan_id":"68378491128619407","malware_family":"EICAR","md5":"564296ca17d5e063856e0b92c8ad6ca6","polyscore":null,"rule_name":"sdk_test_live","sha1":"adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a","sha256":"14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190","tags":"{}","yara":"rule + string: '{"result":{"community":"gamma","created":"2026-08-29T00:11:40.978433+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":"http://minio:9000/public-cache/14/e3/a4/14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a564296ca17d5e063856e0b92c8ad6ca6?response-content-disposition=attachment%3Bfilename%3Dinfected&response-content-type=application%2Foctet-stream&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20260829%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260829T001141Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=f38991abb3474ac5c5d7630b2b2eac0e361bd140cdbdede2d30d52da32efccdc","first_seen":"2026-08-29T00:11:30.179957+00:00","id":"56607280072443305","instance_id":"3936300515643678","livescan_id":"6053794961344849","malware_family":"EICAR","matched_strings":[{"data":"test_live","identifier":"$u","length":9,"offset":69,"truncated":false}],"matched_strings_dropped":null,"md5":"564296ca17d5e063856e0b92c8ad6ca6","polyscore":null,"rule_name":"sdk_test_live","sha1":"adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a","sha256":"14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190","tags":"{}","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -1401,11 +717,11 @@ interactions: Connection: - keep-alive Content-Length: - - '1177' + - '1307' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:41 GMT Server: - gunicorn X-Billing-ID: @@ -1414,7 +730,7 @@ interactions: code: 200 message: OK - request: - body: '{"result_ids":["8018942810283908"]}' + body: '{"result_ids":["56607280072443305"]}' headers: accept: - '*/*' @@ -1425,18 +741,18 @@ interactions: connection: - keep-alive content-length: - - '35' + - '36' content-type: - application/json host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: DELETE uri: http://ai:9696/v3/hunt/live/list response: body: - string: '{"has_more":true,"limit":50,"result":[{"community":"gamma","created":"2026-06-03T22:36:17.918027+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-06-03T22:33:00.053959+00:00","id":"8018942810283908","instance_id":"27835191001619039","livescan_id":"68378491128619407","malware_family":"EICAR","md5":"564296ca17d5e063856e0b92c8ad6ca6","polyscore":null,"rule_name":"sdk_test_live","sha1":"adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a","sha256":"14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190","tags":"{}","yara":null}],"status":"OK"} + string: '{"has_more":true,"limit":50,"result":[{"community":"gamma","created":"2026-08-29T00:11:40.978433+00:00","detections":{"benign":0,"malicious":1,"total":1},"download_url":null,"first_seen":"2026-08-29T00:11:30.179957+00:00","id":"56607280072443305","instance_id":"3936300515643678","livescan_id":"6053794961344849","malware_family":"EICAR","matched_strings":null,"matched_strings_dropped":null,"md5":"564296ca17d5e063856e0b92c8ad6ca6","polyscore":null,"rule_name":"sdk_test_live","sha1":"adec7bf2f04d1b710cbc3304f8dccd0f8d09d34a","sha256":"14e3a40dc4da0e6d9b331976e9d5611d51b25e64f5010bbdfc6c8a32b23eb190","tags":"{}","yara":null}],"status":"OK"} ' headers: @@ -1447,11 +763,11 @@ interactions: Connection: - keep-alive Content-Length: - - '592' + - '645' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:41 GMT Server: - gunicorn X-Billing-ID: @@ -1473,13 +789,13 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/live?id=8018942810283908 + uri: http://ai:9696/v3/hunt/live?id=56607280072443305 response: body: string: '{"errors":null,"result":"Could not find requested live hunt result: - 8018942810283908.","status":"error"} + 56607280072443305.","status":"error"} ' headers: @@ -1490,18 +806,18 @@ interactions: Connection: - keep-alive Content-Length: - - '105' + - '106' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:41 GMT Server: - gunicorn status: code: 404 message: NOT FOUND - request: - body: '{"rule_id":"63962466091392300"}' + body: '{"rule_id":"79693411044316426"}' headers: accept: - '*/*' @@ -1518,12 +834,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: DELETE uri: http://ai:9696/v3/hunt/rule/live response: body: - string: '{"result":{"created":"2026-06-03T22:35:46.641576+00:00","deleted":false,"description":null,"id":"63962466091392300","livescan_created":"2026-06-03T22:35:46.690992+00:00","livescan_id":68378491128619407,"modified":"2026-06-03T22:35:46.666460+00:00","name":"sdk-test_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:28.650982+00:00","deleted":false,"description":null,"id":"79693411044316426","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:41.856223+00:00","name":"sdk-test_live","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -1535,11 +851,11 @@ interactions: Connection: - keep-alive Content-Length: - - '361' + - '318' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:41 GMT Server: - gunicorn X-Billing-ID: @@ -1561,12 +877,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: GET - uri: http://ai:9696/v3/hunt/rule?id=63962466091392300&community=gamma + uri: http://ai:9696/v3/hunt/rule?id=79693411044316426&community=gamma response: body: - string: '{"result":{"created":"2026-06-03T22:35:46.641576+00:00","deleted":false,"description":null,"id":"63962466091392300","livescan_created":"2026-06-03T22:35:46.690992+00:00","livescan_id":null,"modified":"2026-06-03T22:36:18.435557+00:00","name":"sdk-test_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:28.650982+00:00","deleted":false,"description":null,"id":"79693411044316426","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:41.856223+00:00","name":"sdk-test_live","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -1578,11 +894,11 @@ interactions: Connection: - keep-alive Content-Length: - - '348' + - '318' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:42 GMT Server: - gunicorn X-Billing-ID: @@ -1591,7 +907,7 @@ interactions: code: 200 message: OK - request: - body: '{"rule_id":"63962466091392300"}' + body: '{"rule_id":"79693411044316426"}' headers: accept: - '*/*' @@ -1608,7 +924,7 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: DELETE uri: http://ai:9696/v3/hunt/rule/live response: @@ -1628,7 +944,7 @@ interactions: Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:42 GMT Server: - gunicorn status: @@ -1652,12 +968,12 @@ interactions: host: - ai:9696 user-agent: - - polyswarm_api/3.21.0 (x86_64-Linux-CPython-3.14.4) + - polyswarm_api/4.3.0 (x86_64-Linux-CPython-3.14.4) method: DELETE - uri: http://ai:9696/v3/hunt/rule?id=63962466091392300 + uri: http://ai:9696/v3/hunt/rule?id=79693411044316426 response: body: - string: '{"result":{"created":"2026-06-03T22:35:46.641576+00:00","deleted":true,"description":null,"id":"63962466091392300","livescan_created":"2026-06-03T22:35:46.690992+00:00","livescan_id":null,"modified":"2026-06-03T22:36:18.435557+00:00","name":"sdk-test_live","yara":"rule + string: '{"result":{"created":"2026-08-29T00:11:28.650982+00:00","deleted":true,"description":null,"id":"79693411044316426","livescan_created":null,"livescan_id":null,"modified":"2026-08-29T00:11:42.102078+00:00","name":"sdk-test_live","yara":"rule sdk_test_live { strings: $u = \"test_live\" condition: $u }"},"status":"OK"} ' @@ -1669,11 +985,11 @@ interactions: Connection: - keep-alive Content-Length: - - '347' + - '317' Content-Type: - application/json Date: - - Wed, 03 Jun 2026 22:36:18 GMT + - Sat, 29 Aug 2026 00:11:42 GMT Server: - gunicorn X-Billing-ID: diff --git a/test/vcr/test_rules_sort_active_first.vcr b/test/vcr/test_rules_sort_active_first.vcr new file mode 100644 index 00000000..d2de7789 --- /dev/null +++ b/test/vcr/test_rules_sort_active_first.vcr @@ -0,0 +1,460 @@ +interactions: +- request: + body: '{"yara":"rule sdk_test_rules_sort_active_first_running { strings: $u = + \"test_rules_sort_active_first-running\" condition: $u }","name":"test_rules_sort_active_first-running"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '175' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: POST + uri: http://ai:9696/v3/hunt/rule + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.506285+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:09.506285+00:00","name":"test_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_running { strings: $u = \"test_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '473' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:09 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"yara":"rule sdk_test_rules_sort_active_first_idle { strings: $u = \"test_rules_sort_active_first-idle\" + condition: $u }","name":"test_rules_sort_active_first-idle"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '166' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: POST + uri: http://ai:9696/v3/hunt/rule + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.975229+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"70979412008168996","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:09.975229+00:00","name":"test_rules_sort_active_first-idle","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_idle { strings: $u = \"test_rules_sort_active_first-idle\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '464' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:10 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"rule_id":"91100246556341871"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '31' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: POST + uri: http://ai:9696/v3/hunt/rule/live + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.506285+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":"2026-09-02T00:53:12.090276+00:00","livescan_id":"69497058204233968","modified":"2026-09-02T00:53:10.279078+00:00","name":"test_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_running { strings: $u = \"test_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '518' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:12 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule?id=91100246556341871&community=gamma + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.506285+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":"2026-09-02T00:53:12.090276+00:00","livescan_id":"69497058204233968","modified":"2026-09-02T00:53:10.279078+00:00","name":"test_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_running { strings: $u = \"test_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '518' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:12 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule/list?sort=active_first&community=gamma + response: + body: + string: '{"has_more":false,"limit":50,"result":[{"created":"2026-09-02T00:53:09.506285+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":"2026-09-02T00:53:12.090276+00:00","livescan_id":"69497058204233968","modified":"2026-09-02T00:53:10.279078+00:00","name":"test_rules_sort_active_first-running","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null},{"created":"2026-09-02T00:53:09.975229+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"70979412008168996","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:09.975229+00:00","name":"test_rules_sort_active_first-idle","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null}],"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '871' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:12 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule/list?community=gamma + response: + body: + string: '{"has_more":false,"limit":50,"result":[{"created":"2026-09-02T00:53:09.975229+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"70979412008168996","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:09.975229+00:00","name":"test_rules_sort_active_first-idle","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null},{"created":"2026-09-02T00:53:09.506285+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":"2026-09-02T00:53:12.090276+00:00","livescan_id":"69497058204233968","modified":"2026-09-02T00:53:10.279078+00:00","name":"test_rules_sort_active_first-running","new_results_count":null,"new_results_counted_at":null,"rule_count":1,"yara":null}],"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '871' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:12 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: GET + uri: http://ai:9696/v3/hunt/rule/list?sort=bogus&community=gamma + response: + body: + string: '{"errors":null,"result":"Invalid sort: only ''active_first'' is supported.","status":"error"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '92' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:12 GMT + server: + - gunicorn + status: + code: 400 + message: BAD REQUEST +- request: + body: '{"rule_id":"91100246556341871"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '31' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: DELETE + uri: http://ai:9696/v3/hunt/rule/live + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.506285+00:00","deleted":false,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:12.776051+00:00","name":"test_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_running { strings: $u = \"test_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '473' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:12 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"community":"gamma"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '21' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: DELETE + uri: http://ai:9696/v3/hunt/rule?id=91100246556341871 + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.506285+00:00","deleted":true,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"91100246556341871","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:12.974978+00:00","name":"test_rules_sort_active_first-running","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_running { strings: $u = \"test_rules_sort_active_first-running\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '472' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:13 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +- request: + body: '{"community":"gamma"}' + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + authorization: + - '11111111111111111111111111111111' + connection: + - keep-alive + content-length: + - '21' + content-type: + - application/json + host: + - ai:9696 + user-agent: + - polyswarm_api/4.5.0 (x86_64-Darwin-CPython-3.11.3) + method: DELETE + uri: http://ai:9696/v3/hunt/rule?id=70979412008168996 + response: + body: + string: '{"result":{"created":"2026-09-02T00:53:09.975229+00:00","deleted":true,"description":null,"favorite":false,"favorited_at":null,"historical_hunt_count":0,"id":"70979412008168996","livescan_created":null,"livescan_id":null,"modified":"2026-09-02T00:53:13.214903+00:00","name":"test_rules_sort_active_first-idle","rule_count":1,"yara":"rule + sdk_test_rules_sort_active_first_idle { strings: $u = \"test_rules_sort_active_first-idle\" + condition: $u }"},"status":"OK"} + + ' + headers: + access-control-allow-origin: + - '*' + access-control-expose-headers: + - Authorization + connection: + - keep-alive + content-length: + - '463' + content-type: + - application/json + date: + - Wed, 02 Sep 2026 00:53:13 GMT + server: + - gunicorn + x-billing-id: + - '111' + status: + code: 200 + message: OK +version: 1