Skip to content

Commit

Permalink
Merge pull request #18 from InQuest/compatibility-updates
Browse files Browse the repository at this point in the history
Compatibility updates
  • Loading branch information
Hifumi1337 authored Dec 20, 2022
2 parents 8433e43 + ee1f998 commit cd0d110
Show file tree
Hide file tree
Showing 19 changed files with 738 additions and 618 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: sandbox-workflow

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["2.7", "3.8", "3.9"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-mock coverage requests-mock responses collective.checkdocs Pygments nose
- name: Test scripts
run: |
coverage run -m unittest discover
nosetests tests/*
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ dist/
_build/
/build_dist.sh
.vscode/
/.virtualenv/
/.pytest_cache/
Pipfile.lock
42 changes: 21 additions & 21 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
language: python
python:
# - "2.6" # -- responses module requires 2.7+
- "2.7"
# TODO: resolve issues with older 3.x versions
# - "3.3"
# - "3.4"
- "3.5"
install:
- "pip install -r requirements.txt"
- "pip install nose"
- "pip install responses"
- "pip install coveralls"
- "pip install codacy-coverage"
- "pip install collective.checkdocs Pygments"
script:
- nosetests --with-coverage --cover-package=sandboxapi
- python setup.py checkdocs
after_success:
- coveralls
- coverage xml
- bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r coverage.xml
jobs:
include:
- name: "Python 3.9"
python: 3.9
install:
- "pip install -r requirements.txt"
- "pip install pytest pytest-mock coverage requests-mock responses collective.checkdocs Pygments"
script:
- coverage run -m pytest
- python setup.py checkdocs
after_success:
- coveralls
- coverage xml
- if [ "$TRAVIS_BRANCH" = "master" ]; then bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r coverage.xml; fi
- name: "Python 2.7"
python: 2.7
install:
- "pip install -r requirements.txt"
- "pip install nose mock requests-mock responses collective.checkdocs Pygments"
script:
- nosetests
19 changes: 19 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
requests = "*"
jbxapi = "*"
xmltodict = "*"

[dev-packages]
pytest = "*"
coverage = "*"
responses = "*"
"collective.checkdocs" = "*"
pygments = "*"

[requires]
python_version = "3.9"
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ sandboxapi
.. image:: https://app.travis-ci.com/InQuest/python-sandboxapi.svg?branch=master
:target: https://app.travis-ci.com/InQuest/python-sandboxapi
:alt: Build Status
.. image:: https://github.com/InQuest/python-sandboxapi/workflows/sandbox-workflow/badge.svg?branch=master
:target: https://github.com/InQuest/python-sandboxapi/actions
:alt: Build Status (GitHub Workflow)
.. image:: https://github.com/InQuest/python-sandboxapi/workflows/sandbox-workflow/badge.svg?branch=develop
:target: https://github.com/InQuest/python-sandboxapi/actions
:alt: Build Status - Dev (GitHub Workflow)
.. image:: https://readthedocs.org/projects/sandboxapi/badge/?version=latest
:target: https://inquest.readthedocs.io/projects/sandboxapi/en/latest/?badge=latest
:alt: Documentation Status
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
requests
jbxapi==2.10.1
jbxapi
xmltodict
30 changes: 13 additions & 17 deletions sandboxapi/fireeye.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,25 +144,21 @@ def is_available(self):
:rtype: bool
:return: True if service is available, False otherwise.
"""
# if the availability flag is raised, return True immediately.
# NOTE: subsequent API failures will lower this flag. we do this here
# to ensure we don't keep hitting FireEye with requests while
# availability is there.
if self.server_available:
return True

# otherwise, we have to check with the cloud.
else:
try:
response = self._request("/config")

# we've got fireeye.
if response.status_code == 200:
self.server_available = True
return True
try:
response = self._request("/config")

except sandboxapi.SandboxError:
pass
# Successfully connected to FireEye
if response.status_code == 200:
self.server_available = True
return True

# Unable to connect to FireEye
if response.status_code >= 500:
self.server_available = False
return False
except sandboxapi.SandboxError:
pass

self.server_available = False
return False
Expand Down
23 changes: 15 additions & 8 deletions sandboxapi/joe.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import json

import jbxapi

import sandboxapi

class JoeAPI(sandboxapi.SandboxAPI):
Expand All @@ -10,9 +8,11 @@ class JoeAPI(sandboxapi.SandboxAPI):
This class is actually just a convenience wrapper around jbxapi.JoeSandbox.
"""

def __init__(self, apikey, apiurl, accept_tac, timeout=None, verify_ssl=True, retries=3, **kwargs):
def __init__(self, apikey, apiurl, accept_tac, timeout=None, verify_ssl=True, retries=3, chunked=False, **kwargs):
"""Initialize the interface to Joe Sandbox API."""
sandboxapi.SandboxAPI.__init__(self)
if not jbxapi.__version__.startswith("2"):
self._chunked = chunked
self.jbx = jbxapi.JoeSandbox(apikey, apiurl or jbxapi.API_URL, accept_tac, timeout, bool(int(verify_ssl)), retries, **kwargs)

def analyze(self, handle, filename):
Expand All @@ -30,7 +30,10 @@ def analyze(self, handle, filename):
handle.seek(0)

try:
return self.jbx.submit_sample(handle)['webids'][0]
if not jbxapi.__version__.startswith("2"):
return self.jbx.submit_sample(handle, _chunked_upload=self._chunked)['submission_id']
else:
return self.jbx.submit_sample(handle)['webids'][0]
except (jbxapi.JoeException, KeyError, IndexError) as e:
raise sandboxapi.SandboxError("error in analyze: {e}".format(e=e))

Expand All @@ -44,12 +47,13 @@ def check(self, item_id):
:return: Boolean indicating if a report is done or not.
"""
try:
return self.jbx.info(item_id).get('status').lower() == 'finished'
if not jbxapi.__version__.startswith("2"):
return self.jbx.analysis_info(item_id).get('status').lower() == 'finished'
else:
return self.jbx.info(item_id).get('status').lower() == 'finished'
except jbxapi.JoeException:
return False

return False

def is_available(self):
"""Determine if the Joe Sandbox API server is alive.
Expand Down Expand Up @@ -93,7 +97,10 @@ def report(self, item_id, report_format="json"):
report_format = "jsonfixed"

try:
return json.loads(self.jbx.download(item_id, report_format)[1].decode('utf-8'))
if not jbxapi.__version__.startswith("2"):
return json.loads(self.jbx.analysis_download(item_id, report_format)[1].decode('utf-8'))
else:
return json.loads(self.jbx.download(item_id, report_format)[1].decode('utf-8'))
except (jbxapi.JoeException, ValueError, IndexError) as e:
raise sandboxapi.SandboxError("error in report fetch: {e}".format(e=e))

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

setup(
name='sandboxapi',
version='1.6.0',
version='1.6.1',
include_package_data=True,
packages=[
'sandboxapi',
Expand Down
6 changes: 6 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import os
import json

def read_resource(resource):
with open(os.path.join('tests', 'resources', '{r}.json'.format(r=resource)), 'r') as f:
return json.loads(f.read())
5 changes: 5 additions & 0 deletions tests/resources/joe_submission_new.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"submission_id": "100001"
}
}
98 changes: 98 additions & 0 deletions tests/test_cuckoo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import io
from unittest import TestCase

try:
from unittest.mock import patch, ANY as MOCK_ANY
except ImportError:
from mock import patch, ANY as MOCK_ANY

import responses
import sandboxapi.cuckoo
from . import read_resource

class TestCuckoo(TestCase):

def setUp(self):
self.sandbox = sandboxapi.cuckoo.CuckooAPI('http://cuckoo.mock:8090/')

@responses.activate
def test_analyses(self):
responses.add(responses.GET, 'http://cuckoo.mock:8090/tasks/list',
json=read_resource('cuckoo_tasks_list'))
self.assertEqual(len(self.sandbox.analyses()), 2)

@responses.activate
def test_analyze(self):
responses.add(responses.POST, 'http://cuckoo.mock:8090/tasks/create/file',
json=read_resource('cuckoo_tasks_create_file'))
self.assertEqual(self.sandbox.analyze(io.BytesIO('test'.encode('ascii')), 'filename'), '1')

@responses.activate
def test_check(self):
responses.add(responses.GET, 'http://cuckoo.mock:8090/tasks/view/1',
json=read_resource('cuckoo_tasks_view'))
self.assertEqual(self.sandbox.check('1'), True)

@responses.activate
def test_is_available(self):
responses.add(responses.GET, 'http://cuckoo.mock:8090/cuckoo/status',
json=read_resource('cuckoo_status'))
self.assertTrue(self.sandbox.is_available())

@responses.activate
def test_not_is_available(self):
self.assertFalse(self.sandbox.is_available())
responses.add(responses.GET, 'http://cuckoo.mock:8090/cuckoo/status',
status=500)
self.assertFalse(self.sandbox.is_available())

@responses.activate
def test_report(self):
responses.add(responses.GET, 'http://cuckoo.mock:8090/tasks/report/8/json',
json=read_resource('cuckoo_tasks_report'))
self.assertEqual(self.sandbox.report(8)['info']['id'], 8)

@responses.activate
def test_score(self):
responses.add(responses.GET, 'http://cuckoo.mock:8090/tasks/report/8/json',
json=read_resource('cuckoo_tasks_report'))
self.assertEqual(self.sandbox.score(self.sandbox.report(8)), 5)

@patch('requests.post')
@patch('requests.get')
def test_proxies_is_passed_to_requests(self, m_get, m_post):

m_get.return_value.status_code = 200
m_post.return_value.status_code = 200

proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}

api = sandboxapi.cuckoo.CuckooAPI('cuckoo.mock',
proxies=proxies)
api._request('/test')

m_get.assert_called_once_with(api.api_url + '/test', auth=MOCK_ANY,
headers=MOCK_ANY, params=MOCK_ANY,
proxies=proxies, verify=MOCK_ANY)

api._request('/test', method='POST')

m_post.assert_called_once_with(api.api_url + '/test', auth=MOCK_ANY,
headers=MOCK_ANY, data=MOCK_ANY,
files=None, proxies=proxies,
verify=MOCK_ANY)

@responses.activate
def test_cuckoo_old_style_host_port_path(self):
sandbox = sandboxapi.cuckoo.CuckooAPI('cuckoo.mock')
responses.add(responses.GET, 'http://cuckoo.mock:8090/tasks/list',
json=read_resource('cuckoo_tasks_list'))
self.assertEqual(len(self.sandbox.analyses()), 2)

sandbox = sandboxapi.cuckoo.CuckooAPI('cuckoo.mock', 9090, '/test')
responses.add(responses.GET, 'http://cuckoo.mock:9090/test/tasks/list',
json=read_resource('cuckoo_tasks_list'))
self.assertEqual(len(self.sandbox.analyses()), 2)
Loading

0 comments on commit cd0d110

Please sign in to comment.