modules/reporting/mongodb.py
# Copyright (C) 2010-2012 Cuckoo Sandbox Developers.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import os
from lib.cuckoo.common.abstracts import Report
from lib.cuckoo.common.exceptions import CuckooDependencyError, CuckooReportError
from lib.cuckoo.common.utils import File
try:
from pymongo.connection import Connection
from pymongo.errors import ConnectionFailure
from gridfs import GridFS
from gridfs.errors import FileExists
except ImportError:
raise CuckooDependencyError("Unable to import pymongo")
class MongoDb(Report):
"""Stores report in MongoDB."""
def run(self, results):
"""Writes report.
@param results: Cuckoo results dict.
@raise CuckooReportError: if fails to connect or write to MongoDB.
"""
self._connect()
# Set an unique index on stored files, to avoid duplicates.
if not self._db.fs.files.ensure_index("md5", unique=True):
self._db.fs.files.create_index("md5", unique=True, name="md5_unique")
# Add pcap file, check for dups and in case add only reference.
pcap_file = os.path.join(self.analysis_path, "dump.pcap")
if os.path.exists(pcap_file) and os.path.getsize(pcap_file) != 0:
pcap = File(pcap_file)
try:
pcap_id = self._fs.put(pcap.get_data(), filename=pcap.get_name())
except FileExists:
pcap_id = self._db.fs.files.find({"md5": pcap.get_md5()})[0][u"_id"]
# Preventive key check.
if "network" in results:
results["network"]["pcap_id"] = pcap_id
else:
results["network"] = {"pcap_id": pcap_id}
# Add dropped files, check for dups and in case add only reference.
if "dropped" in results:
for dropped in results["dropped"]:
if "name" in dropped:
drop_file = os.path.join(self.analysis_path,
"files",
dropped["name"])
if os.path.exists(drop_file) and os.path.getsize(drop_file) != 0:
try:
drop = open(drop_file, 'r')
except IOError as e:
raise CuckooReportError("Failed to read file %s: %s" % (drop_file, e))
try:
drop_id = self._fs.put(drop, filename=dropped["name"])
except FileExists:
drop_id = self._db.fs.files.find({"md5": dropped["md5"]})[0][u"_id"]
dropped["dropped_id"] = drop_id
# Add screenshots.
results["shots"] = []
shots_path = os.path.join(self.analysis_path, "shots")
if os.path.exists(shots_path):
shots = [f for f in os.listdir(shots_path) if f.endswith(".jpg")]
for shot_file in shots:
shot_path = os.path.join(self.analysis_path, "shots", shot_file)
try:
shot = File(shot_path)
except IOError as e:
raise CuckooReportError("Failed to read screenshot %s: %s" % (shot_path, e))
try:
shot_id = self._fs.put(shot.get_data(), filename=shot.get_name())
except FileExists:
shot_id = self._db.fs.files.find({"md5": shot.get_md5()})[0][u"_id"]
results["shots"].append(shot_id)
# Save all remaining results.
self._db.analysis.save(results)
def _connect(self):
"""Connects to Mongo database, loads options and set connectors.
@raise CuckooReportError: if unable to connect.
"""
if "host" in self.options:
host = self.options["host"]
else:
host = "127.0.0.1"
if "port" in self.options:
port = self.options["port"]
else:
port = 27017
try:
self._conn = Connection(host, port)
self._db = self._conn.cuckoo
self._fs = GridFS(self._db)
except TypeError:
raise CuckooReportError("Mongo connection port must be integer")
except ConnectionFailure:
raise CuckooReportError("Cannot connect to MongoDB")