Пример #1
0
def download_crash(crash_id):
    """Download testcase for the specified FuzzManager crash.

    Args:
        crash_id (int): ID of the requested crash on the server side

    Returns:
        str: Temporary filename of the testcase. Caller must remove when finished.
    """
    coll = Collector()

    LOG.debug("crash %d, downloading testcase...", crash_id)

    url = "%s://%s:%d/crashmanager/rest/crashes/%s/download/" \
        % (coll.serverProtocol, coll.serverHost, coll.serverPort, crash_id)

    response = coll.get(url)

    disp_m = re.match(r'^attachment; filename="(.*)"$',
                      response.headers.get("content-disposition", ""))

    if disp_m is None:
        raise RuntimeError("Server sent malformed response: %r" % (response,))

    prefix = "crash.%d." % (crash_id,)
    suffix = os.path.splitext(disp_m.group(1))[1]
    testcase_fd, testcase_fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
    with os.fdopen(testcase_fd, "wb") as testcase_fp:
        testcase_fp.write(response.content)

    return testcase_fn
Пример #2
0
def crashentry_data(crash_id, raw=False):
    """Get the CrashEntry data for the specified FuzzManager crash

    Args:
        crash_id (int): ID of the requested crash on the server side
        raw (bool): include rawCrashData, rawStderr, rawStdout in result

    Returns:
        dict: crash entry data (crashmanager.models.CrashEntry)
    """
    coll = Collector()

    LOG.debug("crash %d, downloading metadata...", crash_id)

    url = "%s://%s:%d/crashmanager/rest/crashes/%s/" \
        % (coll.serverProtocol, coll.serverHost, coll.serverPort, crash_id)

    return coll.get(url, params={"include_raw": "1" if raw else "0"}).json()
Пример #3
0
def get_signature(bucket_id):
    """
    Download the signature for the specified FuzzManager bucket.

    Args:
        bucket_id (int): ID of the requested bucket on the server side

    Returns:
        str: temp filename to the JSON signature. caller must remove filename when done
    """
    coll = Collector()

    url = "%s://%s:%d/crashmanager/rest/buckets/%d/" \
        % (coll.serverProtocol, coll.serverHost, coll.serverPort, bucket_id)

    response = coll.get(url).json()

    sig_fd, sig_fn = tempfile.mkstemp(suffix=".json")
    with os.fdopen(sig_fd, "w") as sig_fp:
        sig_fp.write(response["signature"])

    return sig_fn
Пример #4
0
class Bucket(object):
    """Get Bucket data for a specified CrashManager bucket."""

    def __init__(self, bucket_id):
        """Initialize a Bucket instance.

        Arguments:
            bucket_id (int): ID of the requested bucket on the server side
        """
        self._bucket_id = bucket_id
        self._sig_filename = None
        self._coll = Collector()
        self._url = "%s://%s:%d/crashmanager/rest/buckets/%d/" % (
            self._coll.serverProtocol,
            self._coll.serverHost,
            self._coll.serverPort,
            bucket_id,
        )
        self._data = None

    @property
    def bucket_id(self):
        return self._bucket_id

    def __getattr__(self, name):
        if self._data is None:
            self._data = self._coll.get(self._url).json()
        if name not in self._data:
            raise AttributeError(
                "'%s' object has no attribute '%s' (has: %s)"
                % (type(self).__name__, name, list(self._data))
            )
        return self._data[name]

    def __setattr__(self, name, value):
        if name.startswith("_"):
            super().__setattr__(name, value)
            return
        raise AttributeError("can't set attribute")

    def cleanup(self):
        """Cleanup any resources held by this instance.

        Arguments:
            None

        Returns:
            None
        """
        if self._sig_filename is not None:
            rmtree(str(self._sig_filename.parent))

    def iter_crashes(self, quality_filter=None):
        """Fetch all crash IDs for this FuzzManager bucket.
        Only crashes with testcases are returned.

        Arguments:
            quality_filter (int): Filter crashes by quality value (None for all)

        Returns:
            generator: generator of CrashEntry
        """

        def _get_results(endpoint, params=None):
            """
            Function to get paginated results from FuzzManager

            Args:
                endpoint (str): FuzzManager REST API to query (eg. "crashes").
                params (dict): Params to pass through to requests.get

            Returns:
                generator: objects returned by FuzzManager (as dicts)
            """
            LOG.debug("first request to /%s/", endpoint)

            url = "%s://%s:%d/crashmanager/rest/%s/" \
                % (self._coll.serverProtocol, self._coll.serverHost,
                   self._coll.serverPort, endpoint)

            response = self._coll.get(url, params=params).json()

            while True:
                LOG.debug("got %d/%d %s", len(response["results"]), response["count"], endpoint)
                while response["results"]:
                    yield response["results"].pop()

                if response["next"] is None:
                    break

                LOG.debug("next request to /%s/", endpoint)
                response = self._coll.get(response["next"]).json()

        # Get all crashes for bucket
        query_args = [
            ("op", "AND"),
            ("bucket", self.bucket_id),
        ]
        if quality_filter is not None:
            query_args.append(("testcase__quality", quality_filter))
        query = json.dumps(dict(query_args))

        n_yielded = 0
        for crash in _get_results("crashes", params={"query": query, "include_raw": "0"}):

            if not crash["testcase"]:
                LOG.warning("crash %d has no testcase, skipping", crash["id"])
                continue

            n_yielded += 1
            LOG.debug("yielding crash #%d", n_yielded)
            result = CrashEntry(crash["id"])
            result._data = crash  # pylint: disable=protected-access
            yield result

    def signature_path(self):
        """Download the bucket data from CrashManager.

        Arguments:
            None

        Returns:
            Path: Path on disk where signature exists.
        """
        if self._sig_filename is not None:
            return self._sig_filename

        tmpd = Path(mkdtemp(prefix="bucket-%d-" % (self._bucket_id,),
                            dir=grz_tmp("fuzzmanager")))
        try:
            sig_basename = "%d.signature" % (self._bucket_id,)
            sig_filename = tmpd / sig_basename
            sig_filename.write_text(self.signature)
            sigmeta_filename = sig_filename.with_suffix(".metadata")
            sigmeta_filename.write_text(
                json.dumps(
                    {
                        "size": self.size,
                        "frequent": self.frequent,
                        "shortDescription": self.shortDescription,
                        "testcase__quality": self.best_quality,
                    }
                )
            )
        except:  # noqa pragma: no cover pylint: disable=bare-except
            rmtree(str(tmpd))
            raise

        self._sig_filename = sig_filename
        return self._sig_filename
Пример #5
0
class CrashEntry(object):
    """Get the CrashEntry data for the specified CrashManager crash.

    Attributes:
        crash_id (int): the server ID for the crash
        see crashmanager.serializers.CrashEntrySerializer
    """
    RAW_FIELDS = frozenset({"rawCrashData", "rawStderr", "rawStdout"})

    def __init__(self, crash_id):
        """Initialize CrashEntry.

        Arguments:
            crash_id (int): ID of the requested crash on the server side
        """
        self._crash_id = crash_id
        self._coll = Collector()
        self._url = "%s://%s:%d/crashmanager/rest/crashes/%d/" % (
            self._coll.serverProtocol,
            self._coll.serverHost,
            self._coll.serverPort,
            crash_id,
        )
        self._data = None
        self._tc_filename = None

    @property
    def crash_id(self):
        return self._crash_id

    def __getattr__(self, name):
        if self._data is None or (name in self.RAW_FIELDS and name not in self._data):
            need_raw = "1" if name in self.RAW_FIELDS else "0"
            self._data = self._coll.get(self._url, params={"include_raw": need_raw}).json()
        if name not in self._data:
            raise AttributeError(
                "'%s' object has no attribute '%s' (has: %s)"
                % (type(self).__name__, name, list(self._data))
            )
        return self._data[name]

    def __setattr__(self, name, value):
        if name.startswith("_"):
            super().__setattr__(name, value)
            return
        if name != "testcase_quality":
            raise AttributeError("can't set attribute")
        self._coll.patch(self._url, data={name: value})
        if self._data:
            self._data[name] = value

    def cleanup(self):
        """Cleanup any resources held by this instance.

        Arguments:
            None

        Returns:
            None
        """
        if self._tc_filename is not None:
            self._tc_filename.unlink()

    def testcase_path(self):
        """Download the testcase data from CrashManager.

        Arguments:
            None

        Returns:
            Path: Path on disk where testcase exists_
        """
        if self._tc_filename is not None:
            return self._tc_filename

        dlurl = self._url + "download/"
        response = self._coll.get(dlurl)

        if "content-disposition" not in response.headers:
            raise RuntimeError("Server sent malformed response: %r" % (response,))  # pragma: no cover

        handle, filename = mkstemp(dir=grz_tmp("fuzzmanager"),
            prefix="crash-%d-" % (self.crash_id,), suffix=Path(self.testcase).suffix
        )
        try:
            with open(handle, "wb") as output:
                output.write(response.content)
        except:  # noqa pragma: no cover pylint: disable=bare-except
            unlink(filename)
            raise
        self._tc_filename = Path(filename)
        return self._tc_filename