Skip to content

Commit

Permalink
Merge pull request #244 from polyswarm/develop
Browse files Browse the repository at this point in the history
Release 3.9.0
  • Loading branch information
mrsarm authored Aug 7, 2024
2 parents ae75097 + 91e47c9 commit 9aaa556
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 3.8.0
current_version = 3.9.0
commit = True
tag = False
sign_tags = True
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setup(
name='polyswarm-api',
version='3.8.0',
version='3.9.0',
description='Client library to simplify interacting with the PolySwarm consumer API',
long_description=long_description,
long_description_content_type='text/markdown',
Expand Down
2 changes: 1 addition & 1 deletion src/polyswarm_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names
__version__ = '3.8.0'
__version__ = '3.9.0'
__release_url__ = 'https://api.github.com/repos/polyswarm/polyswarm-api/releases/latest'

from . import api
Expand Down
136 changes: 110 additions & 26 deletions src/polyswarm_api/api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import io
import logging
import time

Expand Down Expand Up @@ -217,8 +218,7 @@ def submit(
artifact_type=resources.ArtifactType.FILE,
artifact_name=None,
scan_config=None,
is_zip=False,
zip_password=None,
preprocessing=None,
):
"""
Submit artifacts to polyswarm
Expand All @@ -227,27 +227,35 @@ def submit(
:param artifact_type: The ArtifactType or strings containing "file" or "url"
:param artifact_name: An appropriate filename for the Artifact
:param scan_config: The scan configuration to be used, e.g.: "default", "more-time", "most-time"
:param is_zip: If this flag is set, will handle the file as a zip
in the server and decompress before processing.
:param zip_password: Will use this password to decompress the zip file.
If provided, will handle the file as a zip.
:param preprocessing: Preprocessing settings to be applied to the artifact, None means no preprocessing,
otherwise a dict with the following attributes can be passed:
- type (string): either "zip" or "qrcode", the first mean the file is a zip that
the server has to decompress to then scan the content (only one file inside allowed).
"qrcode" means the file is a QR Code image with a URL as payload, and you want
to scan the URL, not the actual file (artifact_type has to be "URL").
- password (string, optional): will use this password to decompress the zip file.
:return: An ArtifactInstance resource
"""
logger.info('Submitting artifact of type %s', artifact_type)
artifact_type = resources.ArtifactType.parse(artifact_type)
# TODO This is a python 2.7 check if artifact is a file-like instance, consider changing
# to isinstance(artifact, io.IOBase) when deprecating 2.7 and implementing making LocalHandle
# inherit io.IOBase, although this will change the method delegation logic in the resource
if hasattr(artifact, 'read') and hasattr(artifact.read, '__call__'):
if isinstance(artifact, io.IOBase):
artifact = resources.LocalArtifact.from_handle(self, artifact, artifact_name=artifact_name or '',
artifact_type=artifact_type)
elif isinstance(artifact, str):
if artifact_type == resources.ArtifactType.FILE:
artifact = resources.LocalArtifact.from_path(self, artifact, artifact_type=artifact_type,
artifact_name=artifact_name)
elif artifact_type == resources.ArtifactType.URL:
artifact = resources.LocalArtifact.from_content(self, artifact, artifact_name=artifact_name or artifact,
artifact_type=artifact_type)
if preprocessing and preprocessing['type'] == 'qrcode':
artifact = resources.LocalArtifact.from_path(self,
artifact,
artifact_type=artifact_type,
artifact_name=artifact_name)
else:
artifact = resources.LocalArtifact.from_content(self,
artifact,
artifact_name=artifact_name or artifact,
artifact_type=artifact_type)
if artifact_type == resources.ArtifactType.URL:
scan_config = scan_config or 'more-time'
if isinstance(artifact, resources.LocalArtifact):
Expand All @@ -257,8 +265,7 @@ def submit(
artifact_type=artifact.artifact_type.name,
scan_config=scan_config,
community=self.community,
is_zip=is_zip,
zip_password=zip_password,
preprocessing=preprocessing,
).result()
instance.upload_file(artifact)
return resources.ArtifactInstance.update(self, id=instance.id, community=self.community).result()
Expand Down Expand Up @@ -714,15 +721,35 @@ def sandbox_file(
artifact_type=resources.ArtifactType.FILE,
artifact_name=None,
network_enabled=True,
is_zip=False,
zip_password=None,
preprocessing=None,
):
logger.info('Sandboxing file in provider %s vm %s', provider_slug, vm_slug)
"""
Submit artifacts to Polyswarm Sandboxing system.
:param artifact: A file-like, path to file, url or LocalArtifact instance
:param artifact_name: An appropriate filename for the Artifact
:artifact_type: the type of artifact to be submitted, default FILE. Use URL type
with the preprocessing field to scan URLs inside QR Code images
:param network_enabled: Whether the VM has to have Internet connection enabled or not.
:param provider_slug: the slug of the provider that is going to execute the sandboxing, e.g.
'cape' or 'triage'.
Use `sandbox_providers()` to get the list of providers.
:param vm_slug: The slug of the virtual machine used for the sandboxing, depending on the
VM chosen is the OS and software that is going to host the sandboxing. Each
provider (provider_slug) support different VMs, so check with `sandbox_providers()`
the list of VMs available.
:param preprocessing: Preprocessing settings to be applied to the artifact, None means no preprocessing,
otherwise a dict with the following attributes can be passed:
- type (string): either "zip" or "qrcode", the first mean the file is a zip that
the server has to decompress to then scan the content (only one file inside allowed).
"qrcode" means the file is a QR Code image with a URL as payload, and you want
to scan the URL, not the actual file (artifact_type has to be "URL").
- password (string, optional): will use this password to decompress the zip file.
:return: An ArtifactInstance resource
"""
logger.info('Sandboxing %s in provider %s vm %s', artifact_type.name.lower(), provider_slug, vm_slug)
artifact_type = resources.ArtifactType.parse(artifact_type)
# TODO This is a python 2.7 check if artifact is a file-like instance, consider changing
# to isinstance(artifact, io.IOBase) when deprecating 2.7 and implementing making LocalHandle
# inherit io.IOBase, although this will change the method delegation logic in the resource
if hasattr(artifact, 'read') and hasattr(artifact.read, '__call__'):
if isinstance(artifact, io.IOBase):
artifact = resources.LocalArtifact.from_handle(self, artifact, artifact_name=artifact_name or '',
artifact_type=artifact_type)
elif isinstance(artifact, str):
Expand All @@ -742,25 +769,76 @@ def sandbox_file(
provider_slug=provider_slug,
vm_slug=vm_slug,
network_enabled=network_enabled,
is_zip=is_zip,
zip_password=zip_password,
preprocessing=preprocessing,
).result()
task.upload_file(artifact)
return resources.SandboxTask.update_file(self, id=task.id, community=self.community).result()
else:
raise exceptions.InvalidValueException(
'Artifacts should be a path to a file or a LocalArtifact instance')

def sandbox_url(self, url, provider_slug, vm_slug, browser=None):
def sandbox_url(self,
url,
provider_slug,
vm_slug,
browser=None,
artifact=None,
artifact_name=None,
preprocessing=None):
"""
Submit URL to Polyswarm Sandboxing system.
:param url: A string with the URL to be submitted. Set to `None` if the URL is provided
through a QR Code image in the artifact param (see artifact and preprocessing params).
:param provider_slug: the slug of the provider that is going to execute the sandboxing, e.g.
'cape' or 'triage'.
Use `sandbox_providers()` to get the list of providers.
:param vm_slug: The slug of the virtual machine used for the sandboxing, depending on the
VM chosen is the OS and software that is going to host the sandboxing. Each
provider (provider_slug) support different VMs, so check with `sandbox_providers()`
the list of VMs available.
:param browser: browser name, e.g. 'edge' or 'firefox'.
:param artifact: A file-like, path to file, or LocalArtifact instance (default None). The url
param has to be None to use this param. For now QR Code image files
are supported
:param artifact_name: name of the artifact, if None (default) the URL string is used as a name,
or the file name if artifact is provided (QR Code image file).
:param preprocessing: Preprocessing settings to be applied to the artifact, None means no preprocessing,
otherwise a dict with the following attributes can be passed:
- type (string): only "qrcode" is supported for URL sandboxing:
the file is a QR Code image with a URL as payload, and you want
to scan the URL, not the actual file. This argument has to be used with the
`artifact` param, and `url` set to `None`.
"""
logger.info('Sandboxing url in provider %s vm %s', provider_slug, vm_slug)
artifact = resources.LocalArtifact.from_content(self, url, artifact_name=url, artifact_type='URL')
if artifact and url:
raise exceptions.InvalidValueException("Cannot use artifact with url param")
if artifact:
if not preprocessing or preprocessing.get('type') != 'qrcode':
raise exceptions.InvalidValueException("Only artifact of type qrcode is supported")
if isinstance(artifact, str):
artifact = resources.LocalArtifact.from_path(self,
artifact,
artifact_type='URL',
artifact_name=artifact_name)
else:
artifact = resources.LocalArtifact.from_handle(self,
artifact,
artifact_name=artifact_name or '',
artifact_type='URL')
else:
artifact_name = url
artifact = resources.LocalArtifact.from_content(self,
url,
artifact_name=artifact_name,
artifact_type='URL')
task = resources.SandboxTask.create_file(self,
artifact_name=url,
artifact_name=artifact_name,
artifact_type="URL",
community=self.community,
provider_slug=provider_slug,
vm_slug=vm_slug,
browser=browser,
preprocessing=preprocessing,
network_enabled=True).result()
task.upload_file(artifact)
return resources.SandboxTask.update_file(self, id=task.id, community=self.community).result()
Expand Down Expand Up @@ -978,3 +1056,9 @@ def report_template_delete(self, template_id):

def report_template_list(self, is_default=None, **kwargs):
return resources.ReportTemplate.list(self, is_default=is_default, **kwargs).result()

def account_whois(self, **kwargs):
return resources.WhoIs.get(self, **kwargs).result()

def account_features(self, **kwargs):
return resources.AccountFeatures.get(self, **kwargs).result()
47 changes: 47 additions & 0 deletions src/polyswarm_api/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,8 @@ def __init__(self, content, api=None):
self.sha256 = content['sha256']
self.report = content['report']
self.upload_url = content['upload_url']
self.config = content['config']
self.artifact = content['artifact']
self.sandbox_artifacts = [SandboxArtifact(a, api=api) for a in content.get('sandbox_artifacts', [])]

def upload_file(self, artifact, attempts=3, **kwargs):
Expand Down Expand Up @@ -1203,3 +1205,48 @@ def upload_logo(self, logo_file, content_tpe):
},
result_parser=self.__class__
).execute()


class AccountFeatures(core.BaseJsonResource):
RESOURCE_ENDPOINT = "/public/accounts"

def __init__(self, content, api=None):
super().__init__(content, api=api)
self.account_number = content['account_number']
self.user_account_number = content.get('user_account_number')
self.account_plan_name = content['account_plan_name']
self.plan_period_start = content['plan_period_start']
self.plan_period_end = content['plan_period_end']
self.window_start = content['window_start']
self.window_end = content['window_end']
self.tenant = content.get('tenant')
self.daily_api_limit = content['daily_api_limit']
self.daily_api_remaining = content['daily_api_remaining']
self.has_stream_access = content['has_stream_access']
self.is_trial = content['is_trial']
self.is_trial_expired = content['is_trial_expired']
self.trial_started_at = content['trial_started_at']
self.trial_ended_at = content['trial_ended_at']
self.features = []
for feature in content['features']:
self.features.append({
'base_uses': feature['base_uses'],
'name': feature['name'],
'overage': feature['overage'],
'remaining_uses': feature['remaining_uses'],
'tag': feature['tag'],
'value': feature['value'],
})


class WhoIs(core.BaseJsonResource):
RESOURCE_ENDPOINT = "/public/accounts/whois"

def __init__(self, content, api=None):
super().__init__(content, api=api)
self.account_number = content['account_number']
self.user_account_number = content.get('user_account_number')
self.account_name = content['account_name']
self.account_type = content['account_type']
self.communities = content['communities']
self.tenant = content.get('tenant')
Loading

0 comments on commit 9aaa556

Please # to comment.