Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "polyswarm"
version = "3.18.0"
version = "3.19.0"
description = "CLI for using the PolySwarm Customer APIs"
readme = "README.md"
authors = [{ name = "PolySwarm Developers", email = "info@polyswarm.io" }]
Expand All @@ -22,7 +22,7 @@ classifiers = [
]

dependencies = [
"polyswarm_api>=3.18.0",
"polyswarm_api>=3.20.0",
"click>=7.1",
"colorama>=0.4.6",
"click-log>=0.4.0",
Expand Down Expand Up @@ -51,7 +51,7 @@ include-package-data = true
where = ["src"]

[tool.bumpversion]
current_version = "3.18.0"
current_version = "3.19.0"
commit = true
tag = false
sign_tags = true
Expand Down
2 changes: 1 addition & 1 deletion src/polyswarm/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '3.18.0'
__version__ = '3.19.0'
8 changes: 6 additions & 2 deletions src/polyswarm/client/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
help='Specific Triage sandbox task ID to retrieve.')
@click.option('--artifact-metadata-id', type=int, default=None,
help='Specific artifact metadata ID to retrieve.')
@click.option('--llm-report-id', type=int, default=None,
help='Specific LLM report task ID to retrieve.')
@click.pass_context
def sample(ctx, sha256, artifact_instance_id, sandbox_task_id_cape,
sandbox_task_id_triage, artifact_metadata_id):
sandbox_task_id_triage, artifact_metadata_id, llm_report_id):
"""
Get aggregated sample information including artifact instance, sandbox tasks, and metadata.
Get aggregated sample information including artifact instance, sandbox tasks, metadata,
and LLM report.

SHA256 is the SHA256 hash of the artifact to retrieve information for.
"""
Expand All @@ -32,5 +35,6 @@ def sample(ctx, sha256, artifact_instance_id, sandbox_task_id_cape,
sandbox_task_id_cape=sandbox_task_id_cape,
sandbox_task_id_triage=sandbox_task_id_triage,
artifact_metadata_id=artifact_metadata_id,
llm_report_id=llm_report_id,
)
output.sample(result)
30 changes: 20 additions & 10 deletions src/polyswarm/formatters/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ def artifact_instance(self, instance, write=True, timeout=False):
output.append(self._red(malicious))
else:
output.append(self._white(malicious))
elif instance.json.get('bounty_state') == 6 and not instance.failed:
output.append(self._white('Detections: This artifact was stored but not submitted for scanning.'))
elif not instance.window_closed and not instance.failed:
output.append(self._white('Detections: This scan has not finished running yet.'))
else:
Expand Down Expand Up @@ -116,6 +118,8 @@ def artifact_instance(self, instance, write=True, timeout=False):
output.append(self._white('Status: This artifact has not been scanned. You can trigger a scan now.'))
elif timeout:
output.append(self._yellow('Status: Lookup timed-out, please retry'))
elif instance.json.get('bounty_state') == 6:
output.append(self._white('Status: Stored'))
else:
output.append(self._white('Status: Running'))
if instance.type == 'URL':
Expand Down Expand Up @@ -799,22 +803,28 @@ def sample(self, result, write=True):
self._open_group()
task_items = [
('Artifact Instance', 'artifact_instance'),
('IP Analyzer', 'ip_analyzer'),
('LLM Report', 'llm_report'),
('Metadata', 'metadata'),
('Sandbox Cape', 'sandbox_cape'),
('Sandbox Triage', 'sandbox_triage'),
]
for label, key in task_items:
task = tasks.get(key)
if task is not None:
rendered_id = task.get('rendered_id')
requested_id = task.get('requested_id')
requested_status = task.get('requested_status')
if requested_id and requested_status:
output.append(self._yellow(f'{label}: ID {requested_id} ({requested_status})'))
elif rendered_id:
output.append(self._green(f'{label}: ID {rendered_id} (ready)'))
else:
output.append(self._white(f'{label}: not available'))
if task is None:
continue
rendered_id = task.get('rendered_id')
requested_id = task.get('requested_id')
requested_status = task.get('requested_status')
id_to_show = requested_id or rendered_id
if requested_status and id_to_show:
output.append(self._yellow(f'{label}: ID {id_to_show} ({requested_status})'))
elif requested_status:
output.append(self._yellow(f'{label}: {requested_status}'))
elif id_to_show:
output.append(self._green(f'{label}: ID {id_to_show} (ready)'))
else:
output.append(self._white(f'{label}: not available'))
self._close_group()

return self._output(output, write)
Expand Down
15 changes: 14 additions & 1 deletion tests/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,4 +437,17 @@ def test_sandboxtask_list(self):
def test_sandboxtask_latest(self):
result = self._run_cli([
'--output-format', 'json', 'sandbox', 'lookup', 'triage', 'a709f37b3a50608f2e9830f92ea25da04bfa4f34d2efecfd061de9f29af02427'])
self._assert_text_result(result, self.click_vcr(result))
self._assert_text_result(result, self.click_vcr(result))


class SampleTest(BaseTestCase):
@vcr.use_cassette()
def test_sample_text(self):
result = self._run_cli(['sample', self.eicar_hash])
self._assert_text_result(result, self.click_vcr(result))

@vcr.use_cassette()
def test_sample_json(self):
result = self._run_cli([
'--output-format', 'json', 'sample', self.eicar_hash])
self._assert_json_result(result, self.click_vcr(result))
9 changes: 9 additions & 0 deletions tests/vcr/test_sample_json.click
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
result: '{"artifact_instance": {}, "llm_report_task": {}, "metadata": {}, "sandbox":
{"cape": {}, "triage": {}}, "tasks": {"artifact_instance": {"rendered_id": null,
"requested_id": null, "requested_status": null}, "llm_report": {"rendered_id": null,
"requested_id": null, "requested_status": null}, "metadata": {"rendered_id": null,
"requested_id": null, "requested_status": null}, "sandbox_cape": {"rendered_id":
null, "requested_id": null, "requested_status": null}, "sandbox_triage": {"rendered_id":
null, "requested_id": null, "requested_status": null}}}

'
46 changes: 46 additions & 0 deletions tests/vcr/test_sample_json.vcr
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
interactions:
- request:
body: '{"community": "gamma"}'
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, zstd
Authorization:
- '11111111111111111111111111111111'
Connection:
- keep-alive
Content-Length:
- '22'
Content-Type:
- application/json
User-Agent:
- polyswarm_api/3.19.1 (x86_64-Linux-CPython-3.14.2)
method: POST
uri: http://artifact-index-e2e:9696/v3/sample/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
response:
body:
string: '{"result":{"artifact_instance":{},"llm_report_task":{},"metadata":{},"sandbox":{"cape":{},"triage":{}},"tasks":{"artifact_instance":{"rendered_id":null,"requested_id":null,"requested_status":null},"llm_report":{"rendered_id":null,"requested_id":null,"requested_status":null},"metadata":{"rendered_id":null,"requested_id":null,"requested_status":null},"sandbox_cape":{"rendered_id":null,"requested_id":null,"requested_status":null},"sandbox_triage":{"rendered_id":null,"requested_id":null,"requested_status":null}}},"status":"OK"}

'
headers:
Access-Control-Allow-Origin:
- '*'
Access-Control-Expose-Headers:
- Authorization
Connection:
- keep-alive
Content-Length:
- '530'
Content-Type:
- application/json
Date:
- Wed, 06 May 2026 16:37:06 GMT
Server:
- gunicorn
X-Billing-ID:
- '111'
status:
code: 200
message: OK
version: 1
5 changes: 5 additions & 0 deletions tests/vcr/test_sample_text.click
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
result: "============================= Sample =============================\n--- Artifact\
\ Instance: Not found ---\n--- Sandbox ---\n Cape: Not found\n Triage: Not found\n\
--- Metadata: Not found ---\n--- Tasks ---\n\tArtifact Instance: not available\n\
\tLLM Report: not available\n\tMetadata: not available\n\tSandbox Cape: not available\n\
\tSandbox Triage: not available\n\n"
46 changes: 46 additions & 0 deletions tests/vcr/test_sample_text.vcr
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
interactions:
- request:
body: '{"community": "gamma"}'
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, zstd
Authorization:
- '11111111111111111111111111111111'
Connection:
- keep-alive
Content-Length:
- '22'
Content-Type:
- application/json
User-Agent:
- polyswarm_api/3.19.1 (x86_64-Linux-CPython-3.14.2)
method: POST
uri: http://artifact-index-e2e:9696/v3/sample/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f
response:
body:
string: '{"result":{"artifact_instance":{},"llm_report_task":{},"metadata":{},"sandbox":{"cape":{},"triage":{}},"tasks":{"artifact_instance":{"rendered_id":null,"requested_id":null,"requested_status":null},"llm_report":{"rendered_id":null,"requested_id":null,"requested_status":null},"metadata":{"rendered_id":null,"requested_id":null,"requested_status":null},"sandbox_cape":{"rendered_id":null,"requested_id":null,"requested_status":null},"sandbox_triage":{"rendered_id":null,"requested_id":null,"requested_status":null}}},"status":"OK"}

'
headers:
Access-Control-Allow-Origin:
- '*'
Access-Control-Expose-Headers:
- Authorization
Connection:
- keep-alive
Content-Length:
- '530'
Content-Type:
- application/json
Date:
- Wed, 06 May 2026 16:37:06 GMT
Server:
- gunicorn
X-Billing-ID:
- '111'
status:
code: 200
message: OK
version: 1
Loading