Skip to content

Commit

Permalink
Add support for newer FireEye appliances
Browse files Browse the repository at this point in the history
**Important note**: The new API support is now the default! Be sure
to check which FireEye version you are using when upgrading this
library.
  • Loading branch information
rshipp committed Sep 19, 2018
1 parent 804147b commit 833e350
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
8 changes: 7 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,20 @@ FireEye AX

Constructor signature::

FireEyeAPI(username, password, url, profile, verify_ssl=True)
FireEyeAPI(username, password, url, profile, legacy_api=False, verify_ssl=True)

Example::

FireEyeAPI('myusername', 'mypassword', 'https://192.168.0.20', 'winxp-sp3')

By default, the ``FireEyeAPI`` class uses v1.2.0 of the FireEye API, which is
available on v8.x FireEye AX series appliances. The v1.1.0 API, which is
available on v7.x appliances, is also supported - just set ``legacy_api=True``
to use the older version.

There is some limited `FireEye API documentation`_ on their blog. For more
information on FireEye's sandbox systems, see the `AX Series product page`_.
FireEye customers have access to more API documentation.

Joe Sandbox
~~~~~~~~~~~
Expand Down
9 changes: 7 additions & 2 deletions sandboxapi/fireeye.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
class FireEyeAPI(sandboxapi.SandboxAPI):
"""FireEye Sandbox API wrapper."""

def __init__(self, username, password, url, profile, verify_ssl=True, **kwargs):
def __init__(self, username, password, url, profile, legacy_api=False, verify_ssl=True, **kwargs):
"""Initialize the interface to FireEye Sandbox API."""
sandboxapi.SandboxAPI.__init__(self, **kwargs)

self.base_url = url
self.api_url = url + '/wsapis/v1.1.0'
self.username = username
self.password = password
self.profile = profile or 'winxp-sp3'
self.api_token = None
self.verify_ssl = verify_ssl

if legacy_api:
# Use v1.1.0 endpoints for v7.x appliances.
self.api_url = url + '/wsapis/v1.1.0'
else:
self.api_url = url + '/wsapis/v1.2.0'

def _request(self, uri, method='GET', params=None, files=None, headers=None, auth=None):
"""Override the parent _request method.
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.3.2',
version='1.4.0',
include_package_data=True,
packages=[
'sandboxapi',
Expand Down
68 changes: 61 additions & 7 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,56 +172,110 @@ class TestFireEye(unittest.TestCase):

def setUp(self):
self.sandbox = sandboxapi.fireeye.FireEyeAPI('username', 'password', 'http://fireeye.mock', 'profile')
self.legacy_sandbox = sandboxapi.fireeye.FireEyeAPI('username', 'password',
'http://fireeye.mock', 'profile',
legacy_api=True)

@responses.activate
def test_analyze(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/submissions',
json=read_resource('fireeye_submissions'))
self.assertEquals(self.sandbox.analyze(io.BytesIO('test'.encode('ascii')), 'filename'), 1)

@responses.activate
def test_check(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.2.0/submissions/status/1',
json=read_resource('fireeye_submissions_status'))
self.assertEquals(self.sandbox.check('1'), True)

@responses.activate
def test_is_available(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.2.0/config',
json=read_resource('fireeye_config'))
self.assertTrue(self.sandbox.is_available())

@responses.activate
def test_not_is_available(self):
self.assertFalse(self.sandbox.is_available())
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.2.0/config',
status=500)
self.assertFalse(self.sandbox.is_available())

@responses.activate
def test_report(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.2.0/submissions/results/1',
json=read_resource('fireeye_submissions_results'))
self.assertEquals(self.sandbox.report(1)['msg'], 'concise')

@responses.activate
def test_score(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.2.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.2.0/submissions/results/1',
json=read_resource('fireeye_submissions_results'))
self.assertEquals(self.sandbox.score(self.sandbox.report(1)), 8)

# Legacy API support.
@responses.activate
def test_analyze(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/submissions',
json=read_resource('fireeye_submissions'))
self.assertEquals(self.sandbox.analyze(io.BytesIO('test'.encode('ascii')), 'filename'), 1)
self.assertEquals(self.legacy_sandbox.analyze(io.BytesIO('test'.encode('ascii')), 'filename'), 1)

@responses.activate
def test_check(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.1.0/submissions/status/1',
json=read_resource('fireeye_submissions_status'))
self.assertEquals(self.sandbox.check('1'), True)
self.assertEquals(self.legacy_sandbox.check('1'), True)

@responses.activate
def test_is_available(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.1.0/config',
json=read_resource('fireeye_config'))
self.assertTrue(self.sandbox.is_available())
self.assertTrue(self.legacy_sandbox.is_available())

@responses.activate
def test_not_is_available(self):
self.assertFalse(self.sandbox.is_available())
self.assertFalse(self.legacy_sandbox.is_available())
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.1.0/config',
status=500)
self.assertFalse(self.sandbox.is_available())
self.assertFalse(self.legacy_sandbox.is_available())

@responses.activate
def test_report(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.1.0/submissions/results/1',
json=read_resource('fireeye_submissions_results'))
self.assertEquals(self.sandbox.report(1)['msg'], 'concise')
self.assertEquals(self.legacy_sandbox.report(1)['msg'], 'concise')

@responses.activate
def test_score(self):
responses.add(responses.POST, 'http://fireeye.mock/wsapis/v1.1.0/auth/login',
headers={'X-FeApi-Token': 'MOCK'})
responses.add(responses.GET, 'http://fireeye.mock/wsapis/v1.1.0/submissions/results/1',
json=read_resource('fireeye_submissions_results'))
self.assertEquals(self.sandbox.score(self.sandbox.report(1)), 8)
self.assertEquals(self.legacy_sandbox.score(self.legacy_sandbox.report(1)), 8)

# Core functionality.
@patch('requests.post')
@patch('requests.get')
def test_proxies_is_passed_to_requests(self, m_get, m_post):
Expand Down

0 comments on commit 833e350

Please sign in to comment.