Files
cuckoo/tests/test_init.py
T

387 lines
12 KiB
Python

# Copyright (C) 2016-2017 Cuckoo Foundation.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import jinja2
import mock
import os
import pytest
import shutil
import tempfile
import cuckoo
from cuckoo.common.config import config, Config
from cuckoo.common.exceptions import CuckooConfigurationError
from cuckoo.common.files import Folders, Files
from cuckoo.common.utils import Singleton
from cuckoo.core.init import write_supervisor_conf, write_cuckoo_conf
from cuckoo.core.resultserver import ResultServer
from cuckoo.main import main, cuckoo_create
from cuckoo.misc import set_cwd, cwd, mkdir
class TestInit(object):
def setup(self):
set_cwd(tempfile.mkdtemp())
def test_exists(self):
filepath = cwd("supervisord.conf")
open(filepath, "wb").write("foo")
write_supervisor_conf(None)
assert open(filepath, "rb").read() == "foo"
def test_new(self):
venv = os.environ.pop("VIRTUAL_ENV", None)
write_supervisor_conf(None)
buf = open(cwd("supervisord.conf"), "rb").read()
assert "command = cuckoo -d -m 10000" in buf
if venv:
os.environ["VIRTUAL_ENV"] = venv
@pytest.mark.skipif("sys.platform != 'linux2'")
def test_venv_new(self):
venv = os.environ.pop("VIRTUAL_ENV", None)
os.environ["VIRTUAL_ENV"] = cwd()
write_supervisor_conf(None)
buf = open(cwd("supervisord.conf"), "rb").read()
cuckoo_path = "%s/bin/cuckoo" % cwd()
assert "command = %s -d -m 10000" % cuckoo_path in buf
os.environ["VIRTUAL_ENV"] = venv
def test_venv_new_unicode(self):
set_cwd(tempfile.mkdtemp(u"a\u202eb"))
write_supervisor_conf(None)
def test_cuckoo_init(self):
"""Test that 'cuckoo init' works with a new CWD."""
with pytest.raises(SystemExit):
main.main(
("--cwd", cwd(), "--nolog", "init"),
standalone_mode=False
)
assert os.path.exists(os.path.join(cwd(), "conf"))
assert os.path.exists(os.path.join(cwd(), "log"))
assert os.path.exists(os.path.join(cwd(), "pidfiles", ".gitignore"))
assert os.path.exists(os.path.join(cwd(), "stuff", "mitm.py"))
assert os.path.exists(os.path.join(cwd(), "storage"))
assert os.path.exists(os.path.join(cwd(), "storage", "binaries"))
assert os.path.exists(os.path.join(cwd(), "storage", "analyses"))
assert os.path.exists(os.path.join(cwd(), "storage", "baseline"))
def test_cuckoo_init_main(self):
"""Test that 'cuckoo' works with a new CWD."""
main.main(
("--cwd", cwd(), "--nolog"),
standalone_mode=False
)
assert os.path.exists(os.path.join(cwd(), "stuff", "mitm.py"))
@mock.patch("cuckoo.main.load_signatures")
def test_cuckoo_init_main_nosigs(self, p):
"""Ensure load_signatures() isn't called for 'cuckoo' with new CWD."""
main.main(
("--cwd", cwd(), "--nolog"),
standalone_mode=False
)
assert os.path.exists(os.path.join(cwd(), "stuff", "mitm.py"))
p.assert_not_called()
@mock.patch("cuckoo.main.check_version")
def test_cuckoo_init_no_resultserver(self, cv):
"""Test that 'cuckoo init' doesn't launch the ResultServer."""
with pytest.raises(SystemExit):
main.main(
("--cwd", cwd(), "--nolog", "init"),
standalone_mode=False
)
# We copy the monitor binary directory over from user-CWD (which is
# also present in the Travis CI environment, etc) as otherwise the
# following call will raise an exception about not having found the
# monitoring binaries.
shutil.rmtree(os.path.join(cwd(), "monitor"))
shutil.copytree(
os.path.expanduser("~/.cuckoo/monitor"),
os.path.join(cwd(), "monitor")
)
# Raises CuckooCriticalError if ResultServer can't bind (which no
# longer happens now, naturally).
main.main(
("--cwd", cwd(), "--nolog", "init"),
standalone_mode=False
)
assert ResultServer not in Singleton._instances
def test_cuckoo_conf(self):
Folders.create(cwd(), "conf")
write_cuckoo_conf()
def test_cuckoo_create(self):
# Specifically try to create $CWD/signatures/__init__.pyc to ensure
# that our .pyc filtering works.
initpyc = os.path.join(
cuckoo.__path__[0], "data", "signatures", "__init__.pyc"
)
open(initpyc, "wb").close()
cuckoo_create("derpy")
assert os.path.exists(cwd(".cwd"))
assert os.path.exists(cwd("conf", "esx.conf"))
assert os.path.exists(cwd("analyzer", "windows", "analyzer.py"))
assert os.path.exists(cwd("monitor", "latest"))
assert os.path.exists(cwd("distributed", "settings.py"))
assert not os.path.exists(cwd("signatures", "__init__.pyc"))
assert os.path.exists(initpyc)
os.unlink(initpyc)
def test_cuckoo_create2(self):
cuckoo_create(cfg={
"auxiliary": {
"sniffer": {
"tcpdump": "dumping.elf",
},
},
})
buf = open(cwd("conf", "auxiliary.conf"), "rb").read()
assert "tcpdump = dumping.elf" in buf
def test_cuckoo_init_kv_conf(self):
filepath = Files.temp_put(
"cuckoo.cuckoo.version_check = no"
)
# Create a new CWD as Files.temp_put() indexes - or tries to - the
# original cuckoo.conf (even though it doesn't exist yet).
set_cwd(tempfile.mkdtemp())
with pytest.raises(SystemExit):
main.main(
("--cwd", cwd(), "init", "--conf", filepath),
standalone_mode=False
)
assert config("cuckoo:cuckoo:version_check") is False
def test_init_star_existing(self):
cuckoo_create(cfg={
"virtualbox": {
"cuckoo1": {
"ip": "192.168.56.102",
},
},
})
assert config("virtualbox:cuckoo1:ip") == "192.168.56.102"
def test_init_star_multiple(self):
cuckoo_create(cfg={
"virtualbox": {
"virtualbox": {
"machines": [
"cuckoo2", "cuckoo3",
],
},
"cuckoo2": {
"ip": "192.168.56.102",
},
"cuckoo3": {
"ip": "192.168.56.103",
},
"notexistingvm": {
"ip": "1.2.3.4",
},
},
})
assert config("virtualbox:virtualbox:machines") == [
"cuckoo2", "cuckoo3"
]
assert config("virtualbox:cuckoo2:ip") == "192.168.56.102"
assert config("virtualbox:cuckoo3:ip") == "192.168.56.103"
assert config("virtualbox:notexistingvm:ip") is None
class TestWriteCuckooConfiguration(object):
def setup(self):
set_cwd(tempfile.mkdtemp())
mkdir(cwd("conf"))
self.h = mock.patch("cuckoo.core.init.jinja2")
self.p = self.h.start()
self.render().return_value = ""
def teardown(self):
self.h.stop()
def render(self):
return self.p.Template.return_value.render
def value(self, s):
return self.render().call_args[0][0]["config"](s)
def rawvalue(self, s):
a, b, c = s.split(":")
return self.render().call_args[0][0][a][b][c]
def test_simple(self):
write_cuckoo_conf(cfg={
"cuckoo": {
"cuckoo": {
"version_check": False,
},
},
})
assert self.value("cuckoo:cuckoo:version_check") is False
with pytest.raises(ValueError):
self.value("a")
with pytest.raises(KeyError):
self.value("a:b:c")
with pytest.raises(KeyError):
self.value("cuckoo:a:b")
with pytest.raises(KeyError):
self.value("cuckoo:cuckoo:c")
def test_default_simple(self):
write_cuckoo_conf()
assert self.value("cuckoo:feedback:name") is None
assert self.value("cuckoo:cuckoo:max_vmstartup_count") == 10
assert self.rawvalue("cuckoo:cuckoo:max_vmstartup_count") == "10"
def test_default_star(self):
write_cuckoo_conf()
assert self.value("routing:vpn0:name") == "vpn0"
assert self.rawvalue("routing:vpn0:name") == "vpn0"
assert self.value("virtualbox:cuckoo1:ip") == "192.168.56.101"
assert self.value("avd:cuckoo1:platform") == "android"
assert self.value("esx:analysis1:ip") == "192.168.122.101"
assert self.value("physical:physical1:label") == "physical1"
assert self.value("qemu:vm1:label") == "vm1"
assert self.value("vmware:cuckoo1:vmx_path") == "../cuckoo1/cuckoo1.vmx"
assert self.value("vsphere:analysis1:snapshot") == "snapshot_name"
assert self.value("xenserver:cuckoo1:uuid") == "00000000-0000-0000-0000-000000000000"
def test_star_not_found(self):
with pytest.raises(CuckooConfigurationError) as e:
write_cuckoo_conf(cfg={
"routing": {
"vpn": {
"vpns": [
"a",
],
},
},
})
e.match("A section was defined that")
def test_star_single(self):
write_cuckoo_conf(cfg={
"routing": {
"vpn": {
"vpns": [
"a"
],
},
"a": {
"description": "VPN a",
},
},
})
assert self.value("routing:vpn:vpns") == ["a"]
assert self.rawvalue("routing:vpn:vpns") == "a"
assert self.value("routing:a:name") is None
assert self.rawvalue("routing:a:name") == ""
assert self.value("routing:a:description") == "VPN a"
assert self.rawvalue("routing:a:description") == "VPN a"
def test_star_multiple(self):
write_cuckoo_conf(cfg={
"virtualbox": {
"virtualbox": {
"machines": [
"a", "b"
],
},
"a": {
"ip": "1.2.3.4",
},
"b": {
"ip": "5.6.7.8",
},
},
})
assert self.value("virtualbox:virtualbox:machines") == ["a", "b"]
assert self.rawvalue("virtualbox:virtualbox:machines") == "a, b"
assert self.value("virtualbox:a:ip") == "1.2.3.4"
assert self.rawvalue("virtualbox:a:ip") == "1.2.3.4"
def test_all_config_written():
set_cwd(tempfile.mkdtemp())
cuckoo_create()
cfg = Config.from_confdir(cwd("conf"))
# This one is extra, ignore.
cfg["virtualbox"].pop("honeyd", None)
set_cwd(tempfile.mkdtemp())
mkdir(cwd("conf"))
lookups = []
class LookupDict(dict):
parents = []
def __getitem__(self, key):
lookups.append(":".join(self.parents + [key]))
return dict.__getitem__(key)
class Template(jinja2.Template):
def render(self, kw):
orig_config = kw["config"]
def lookup_config(s):
# For some reason this is called multiple times (?).
if s not in lookups:
lookups.append(s)
return orig_config(s)
kw["config"] = lookup_config
for key, value in kw.items():
if key == "config":
continue
for key2, value2 in value.items():
value[key2] = LookupDict(value2)
value[key2].parents = [key, key2]
return jinja2.Template.render(self, kw)
with mock.patch("cuckoo.core.init.jinja2") as p:
p.Template = Template
write_cuckoo_conf(cfg)
print cfg["cuckoo"]
# Force port was removed/now unused for backwards compatibility
cfg["cuckoo"]["resultserver"].pop("force_port", None)
# Pool size is a hidden option for now
cfg["cuckoo"]["resultserver"].pop("pool_size", None)
for key, value in cfg.items():
for key2, value2 in value.items():
for key3, value3 in value2.items():
assert "%s:%s:%s" % (key, key2, key3) in lookups
assert sorted(lookups) == sorted(set(lookups))