Пример #1
0
def signature_summary(request, params):
    """Return a list of specific aggregations"""
    context = {}

    params['signature'] = '=' + params['signature'][0]
    params['_aggs.signature'] = [
        'hang_type',
        'process_type',
        'startup_crash',
        '_histogram.uptime',
    ]
    params['_results_number'] = 0
    params['_facets'] = [
        'platform_pretty_version',
        'cpu_arch',
        'process_type',
        'flash_version',
    ]
    params['_histogram.uptime'] = ['product']
    params['_histogram_interval.uptime'] = 60
    params['_aggs.adapter_vendor_id'] = ['adapter_device_id']
    params['_aggs.android_cpu_abi.android_manufacturer.android_model'] = [
        'android_version'
    ]
    params['_aggs.product.version'] = ['_cardinality.install_time']

    # If the user has permissions, show exploitability.
    all_fields = SuperSearchFields().get()
    if has_permissions(request.user,
                       all_fields['exploitability']['permissions_needed']):
        params['_histogram.date'] = ['exploitability']

    api = SuperSearchUnredacted()

    # Now make the actual request with all expected parameters.
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    facets = search_results['facets']

    _transform_uptime_summary(facets)
    _transform_graphics_summary(facets)
    _transform_mobile_summary(facets)
    _transform_exploitability_summary(facets)

    context['query'] = search_results
    context['product_version_total'] = search_results['total']
    if 'signature' in facets and len(facets['signature']) > 0:
        context['signature_stats'] = SignatureStats(
            search_results['facets']['signature'][0], search_results['total'])

    return render(request, 'signature/signature_summary.html', context)
Пример #2
0
def signature_comments(request, params):
    """Return a list of non-empty comments."""
    # Users can't see comments unless they have view_pii permissions.
    if not request.user.has_perm('crashstats.view_pii'):
        return http.HttpResponseForbidden()

    signature = params['signature'][0]

    context = {}
    context['query'] = {'total': 0, 'total_count': 0, 'total_pages': 0}

    current_query = request.GET.copy()
    if 'page' in current_query:
        del current_query['page']

    context['params'] = current_query.copy()

    try:
        current_page = int(request.GET.get('page', 1))
    except ValueError:
        return http.HttpResponseBadRequest('Invalid page')

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    context['current_page'] = current_page
    context['results_offset'] = results_per_page * (current_page - 1)

    params['signature'] = '=' + signature
    params['user_comments'] = '!__null__'
    params['_columns'] = ['uuid', 'user_comments', 'date', 'useragent_locale']
    params['_sort'] = '-date'
    params['_results_number'] = results_per_page
    params['_results_offset'] = context['results_offset']
    params['_facets'] = []

    context['current_url'] = '%s?%s' % (reverse('signature:signature_report'),
                                        urlencode_obj(current_query))

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    search_results['total_pages'] = int(
        math.ceil(search_results['total'] / float(results_per_page)))
    search_results['total_count'] = search_results['total']

    context['query'] = search_results

    return render(request, 'signature/signature_comments.html', context)
Пример #3
0
def signature_reports(request):
    '''Return the results of a search. '''
    params = get_validated_params(request)
    if isinstance(params, http.HttpResponseBadRequest):
        # There was an error in the form, let's return it.
        return params

    signature = params['signature'][0]

    data = {}
    data['query'] = {'total': 0, 'total_count': 0, 'total_pages': 0}

    allowed_fields = get_allowed_fields(request.user)

    current_query = request.GET.copy()
    if 'page' in current_query:
        del current_query['page']

    data['params'] = current_query.copy()

    if '_columns' in data['params']:
        del data['params']['_columns']

    data['columns'] = request.GET.getlist('_columns') or DEFAULT_COLUMNS

    # Make sure only allowed fields are used
    data['columns'] = [x for x in data['columns'] if x in allowed_fields]

    try:
        current_page = int(request.GET.get('page', 1))
    except ValueError:
        return http.HttpResponseBadRequest('Invalid page')

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    data['current_page'] = current_page
    data['results_offset'] = results_per_page * (current_page - 1)

    params['signature'] = '=' + signature
    params['_results_number'] = results_per_page
    params['_results_offset'] = data['results_offset']
    params['_facets'] = []  # We don't need no facets.

    data['current_url'] = '%s?%s' % (reverse('signature:signature_report'),
                                     current_query.urlencode())

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except models.BadStatusCodeError, e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest('<ul><li>%s</li></ul>' % e)
Пример #4
0
def signature_comments(request):
    '''Return a list of non-empty comments. '''
    params = get_validated_params(request)
    if isinstance(params, http.HttpResponseBadRequest):
        # There was an error in the form, let's return it.
        return params

    signature = params['signature'][0]

    data = {}
    data['query'] = {
        'total': 0,
        'total_count': 0,
        'total_pages': 0
    }

    current_query = request.GET.copy()
    if 'page' in current_query:
        del current_query['page']

    data['params'] = current_query.copy()

    try:
        current_page = int(request.GET.get('page', 1))
    except ValueError:
        return http.HttpResponseBadRequest('Invalid page')

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    data['current_page'] = current_page
    data['results_offset'] = results_per_page * (current_page - 1)

    params['signature'] = '=' + signature
    params['user_comments'] = '!__null__'
    params['_columns'] = ['uuid', 'user_comments', 'date', 'useragent_locale']
    params['_results_number'] = results_per_page
    params['_results_offset'] = data['results_offset']
    params['_facets'] = []  # We don't need no facets.

    data['current_url'] = '%s?%s' % (
        reverse('signature:signature_report'),
        current_query.urlencode()
    )

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except models.BadStatusCodeError, e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest('<ul><li>%s</li></ul>' % e)
Пример #5
0
def signature_aggregation(request, aggregation):
    '''Return the aggregation of a field. '''
    params = get_params(request)
    if isinstance(params, http.HttpResponseBadRequest):
        # There was an error in the form, let's return it.
        return params

    if len(params['signature']) > 1:
        return http.HttpResponseBadRequest(
            'Invalid value for "signature" parameter, '
            'only one value is accepted'
        )
    signature = params['signature'][0]

    if not signature:
        return http.HttpResponseBadRequest(
            '"signature" parameter is mandatory'
        )

    data = {}
    data['aggregation'] = aggregation

    allowed_fields = get_allowed_fields(request.user)

    # Make sure the field we want to aggregate on is allowed.
    if aggregation not in allowed_fields:
        return http.HttpResponseBadRequest(
            '<ul><li>'
            'You are not allowed to aggregate on the "%s" field'
            '</li></ul>' % aggregation
        )

    current_query = request.GET.copy()
    data['params'] = current_query.copy()

    params['signature'] = '=' + signature
    params['_results_number'] = 0
    params['_results_offset'] = 0
    params['_facets'] = [aggregation]

    data['current_url'] = '%s?%s' % (
        reverse('signature:signature_report'),
        current_query.urlencode()
    )

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except models.BadStatusCodeError, e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest('<ul><li>%s</li></ul>' % e)
Пример #6
0
def signature_aggregation(request, params, aggregation):
    '''Return the aggregation of a field. '''

    signature = params['signature'][0]

    context = {}
    context['aggregation'] = aggregation

    allowed_fields = get_allowed_fields(request.user)

    # Make sure the field we want to aggregate on is allowed.
    if aggregation not in allowed_fields:
        return http.HttpResponseBadRequest(
            '<ul><li>'
            'You are not allowed to aggregate on the "%s" field'
            '</li></ul>' % aggregation
        )

    current_query = request.GET.copy()
    context['params'] = current_query.copy()

    params['signature'] = '=' + signature
    params['_results_number'] = 0
    params['_results_offset'] = 0
    params['_facets'] = [aggregation]

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    context['aggregates'] = []
    if aggregation in search_results['facets']:
        context['aggregates'] = search_results['facets'][aggregation]

    context['total_count'] = search_results['total']

    return render(request, 'signature/signature_aggregation.html', context)
Пример #7
0
def profile(request, default_context=None):
    context = default_context or {}
    context["permissions"] = Permission.objects.filter(content_type__model="").order_by(
        "name"
    )

    start_date = (datetime.datetime.utcnow() - datetime.timedelta(weeks=4)).isoformat()

    api = SuperSearchUnredacted()
    results = api.get(
        email=request.user.email,
        date=">%s" % start_date,
        _columns=["date", "uuid"],
        _sort="-date",
    )

    context["crashes_list"] = [
        dict(zip(("crash_id", "date"), (x["uuid"], x["date"]))) for x in results["hits"]
    ]

    return render(request, "profile/profile.html", context)
Пример #8
0
def signature_aggregation(request, params, aggregation):
    """Return the aggregation of a field. """

    signature = params["signature"][0]

    context = {}
    context["aggregation"] = aggregation

    allowed_fields = get_allowed_fields(request.user)

    # Make sure the field we want to aggregate on is allowed.
    if aggregation not in allowed_fields:
        return http.HttpResponseBadRequest(
            "<ul><li>"
            'You are not allowed to aggregate on the "%s" field'
            "</li></ul>" % aggregation)

    current_query = request.GET.copy()
    context["params"] = current_query.copy()

    params["signature"] = "=" + signature
    params["_results_number"] = 0
    params["_results_offset"] = 0
    params["_facets"] = [aggregation]

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    context["aggregates"] = []
    if aggregation in search_results["facets"]:
        context["aggregates"] = search_results["facets"][aggregation]

    context["total_count"] = search_results["total"]

    return render(request, "signature/signature_aggregation.html", context)
Пример #9
0
def signature_graphs(request, params, field):
    '''Return a multi-line graph of crashes per day grouped by field. '''

    signature = params['signature'][0]

    context = {}
    context['aggregation'] = field

    allowed_fields = get_allowed_fields(request.user)

    # Make sure the field we want to aggregate on is allowed.
    if field not in allowed_fields:
        return http.HttpResponseBadRequest(
            '<ul><li>'
            'You are not allowed to group by the "%s" field'
            '</li></ul>' % field
        )

    current_query = request.GET.copy()
    context['params'] = current_query.copy()

    params['signature'] = '=' + signature
    params['_results_number'] = 0
    params['_results_offset'] = 0
    params['_histogram.date'] = [field]
    params['_facets'] = [field]

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    context['aggregates'] = search_results['facets'].get('histogram_date', [])
    context['term_counts'] = search_results['facets'].get(field, [])

    return context
Пример #10
0
def profile(request, default_context=None):
    context = default_context or {}
    context['permissions'] = (Permission.objects.filter(
        content_type__model='').order_by('name'))

    start_date = (datetime.datetime.utcnow() -
                  datetime.timedelta(weeks=4)).isoformat()

    api = SuperSearchUnredacted()
    results = api.get(
        email=request.user.email,
        date='>%s' % start_date,
        _columns=['date', 'uuid'],
        _sort='-date',
    )

    context['crashes_list'] = [
        dict(zip(('crash_id', 'date'), (x['uuid'], x['date'])))
        for x in results['hits']
    ]

    return render(request, 'profile/profile.html', context)
Пример #11
0
def check_crashids(entropy_chunk, date):
    """Checks crash ids for a given entropy and date."""
    s3_context = get_s3_context()
    bucket = s3_context.config.bucket_name
    s3_client = s3_context.client

    supersearch = SuperSearchUnredacted()

    missing = []
    for entropy in entropy_chunk:
        raw_crash_key_prefix = RAW_CRASH_PREFIX_TEMPLATE % (entropy, date)

        paginator = s3_client.get_paginator("list_objects_v2")
        page_iterator = paginator.paginate(Bucket=bucket,
                                           Prefix=raw_crash_key_prefix)

        for page in page_iterator:
            # NOTE(willkg): Keys look like /v2/raw_crash/ENTRPOY/DATE/CRASHID
            crash_ids = [
                item["Key"].split("/")[-1]
                for item in page.get("Contents", [])
            ]

            if not crash_ids:
                continue

            # Check S3 first
            for crash_id in crash_ids:
                if not is_in_s3(s3_client, bucket, crash_id):
                    missing.append(crash_id)

            # Check Elasticsearch in batches
            for crash_ids_batch in chunked(crash_ids, 100):
                missing_in_es = check_elasticsearch(supersearch,
                                                    crash_ids_batch)
                missing.extend(missing_in_es)

    return list(set(missing))
Пример #12
0
def signature_graphs(request, params, field):
    """Return a multi-line graph of crashes per day grouped by field. """

    signature = params["signature"][0]

    context = {}
    context["aggregation"] = field

    allowed_fields = get_allowed_fields(request.user)

    # Make sure the field we want to aggregate on is allowed.
    if field not in allowed_fields:
        return http.HttpResponseBadRequest(
            "<ul><li>"
            'You are not allowed to group by the "%s" field'
            "</li></ul>" % field)

    current_query = request.GET.copy()
    context["params"] = current_query.copy()

    params["signature"] = "=" + signature
    params["_results_number"] = 0
    params["_results_offset"] = 0
    params["_histogram.date"] = [field]
    params["_facets"] = [field]

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    context["aggregates"] = search_results["facets"].get("histogram_date", [])
    context["term_counts"] = search_results["facets"].get(field, [])

    return context
Пример #13
0
def signature_graphs(request, field):
    '''Return a multi-line graph of crashes per day grouped by field. '''
    params = get_validated_params(request)
    if isinstance(params, http.HttpResponseBadRequest):
        # There was an error in the form, let's return it.
        return params

    signature = params['signature'][0]

    data = {}
    data['aggregation'] = field

    allowed_fields = get_allowed_fields(request.user)

    # Make sure the field we want to aggregate on is allowed.
    if field not in allowed_fields:
        return http.HttpResponseBadRequest(
            '<ul><li>'
            'You are not allowed to group by the "%s" field'
            '</li></ul>' % field
        )

    current_query = request.GET.copy()
    data['params'] = current_query.copy()

    params['signature'] = '=' + signature
    params['_results_number'] = 0
    params['_results_offset'] = 0
    params['_histogram.date'] = [field]
    params['_facets'] = [field]

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except models.BadStatusCodeError, e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest('<ul><li>%s</li></ul>' % e)
Пример #14
0
def search_custom(request, default_context=None):
    """Return the basic search page, without any result"""
    error = None
    query = None

    try:
        params = get_params(request)
    except ValidationError as e:
        # There was an error in the form, but we want to do the default
        # behavior and just display an error message.
        error = str(e)
    else:
        # Get the JSON query that supersearch generates and show it.
        params["_return_query"] = "true"
        api = SuperSearchUnredacted()
        try:
            query = api.get(**params)
        except BadArgumentError as e:
            error = e

    schema = settings.ELASTICSEARCH_INDEX_SCHEMA
    now = timezone.now()

    possible_indices = []
    for i in range(26):
        index = (now - datetime.timedelta(weeks=i)).strftime(schema)
        possible_indices.append({"id": index, "text": index})

    context = default_context
    context["elasticsearch_indices"] = possible_indices

    if query:
        context["query"] = json.dumps(query["query"])
        context["indices"] = ",".join(query["indices"])

    context["error"] = error

    return render(request, "supersearch/search_custom.html", context)
Пример #15
0
def get_topcrashers_results(**kwargs):
    """Return the results of a search. """
    results = []

    params = kwargs
    range_type = params.pop('_range_type')
    dates = get_date_boundaries(params)

    params['_aggs.signature'] = [
        'platform',
        'is_garbage_collecting',
        'hang_type',
        'process_type',
        'startup_crash',
        '_histogram.uptime',
        '_cardinality.install_time',
    ]
    params['_histogram_interval.uptime'] = 60

    # We don't care about no results, only facets.
    params['_results_number'] = 0

    if params.get('process_type') in ('any', 'all'):
        params['process_type'] = None

    if range_type == 'build':
        params['build_id'] = [
            '>=' + datetime_to_build_id(dates[0]),
            '<' + datetime_to_build_id(dates[1])
        ]

    api = SuperSearchUnredacted()
    search_results = api.get(**params)

    if search_results['total'] > 0:
        results = search_results['facets']['signature']

        platforms = models.Platforms().get_all()['hits']
        platform_codes = [
            x['code'] for x in platforms if x['code'] != 'unknown'
        ]

        for i, hit in enumerate(results):
            hit['signature'] = hit['term']
            hit['rank'] = i + 1
            hit['percent'] = 100.0 * hit['count'] / search_results['total']

            # Number of crash per platform.
            for platform in platform_codes:
                hit[platform + '_count'] = 0

            sig_platforms = hit['facets']['platform']
            for platform in sig_platforms:
                code = platform['term'][:3].lower()
                if code in platform_codes:
                    hit[code + '_count'] = platform['count']

            # Number of crashes happening during garbage collection.
            hit['is_gc_count'] = 0
            sig_gc = hit['facets']['is_garbage_collecting']
            for row in sig_gc:
                if row['term'].lower() == 't':
                    hit['is_gc_count'] = row['count']

            # Number of plugin crashes.
            hit['plugin_count'] = 0
            sig_process = hit['facets']['process_type']
            for row in sig_process:
                if row['term'].lower() == 'plugin':
                    hit['plugin_count'] = row['count']

            # Number of hang crashes.
            hit['hang_count'] = 0
            sig_hang = hit['facets']['hang_type']
            for row in sig_hang:
                # Hangs have weird values in the database: a value of 1 or -1
                # means it is a hang, a value of 0 or missing means it is not.
                if row['term'] in (1, -1):
                    hit['hang_count'] += row['count']

            # Number of crashes happening during startup. This is defined by
            # the client, as opposed to the next method which relies on
            # the uptime of the client.
            hit['startup_count'] = sum(
                row['count'] for row in hit['facets']['startup_crash']
                if row['term'] in ('T', '1'))

            # Is a startup crash if more than half of the crashes are happening
            # in the first minute after launch.
            hit['startup_crash'] = False
            sig_uptime = hit['facets']['histogram_uptime']
            for row in sig_uptime:
                # Aggregation buckets use the lowest value of the bucket as
                # term. So for everything between 0 and 60 excluded, the
                # term will be `0`.
                if row['term'] < 60:
                    ratio = 1.0 * row['count'] / hit['count']
                    hit['startup_crash'] = ratio > 0.5

            # Number of distinct installations.
            hit['installs_count'] = (
                hit['facets']['cardinality_install_time']['value'])

        # Run the same query but for the previous date range, so we can
        # compare the rankings and show rank changes.
        delta = (dates[1] - dates[0]) * 2
        params['date'] = [
            '>=' + (dates[1] - delta).isoformat(), '<' + dates[0].isoformat()
        ]
        params['_aggs.signature'] = [
            'platform',
        ]
        params['_facets_size'] *= 2

        if range_type == 'build':
            params['date'][1] = '<' + dates[1].isoformat()
            params['build_id'] = [
                '>=' + datetime_to_build_id(dates[1] - delta),
                '<' + datetime_to_build_id(dates[0])
            ]

        previous_range_results = api.get(**params)
        total = previous_range_results['total']

        compare_signatures = {}
        if total > 0 and 'signature' in previous_range_results['facets']:
            signatures = previous_range_results['facets']['signature']
            for i, hit in enumerate(signatures):
                compare_signatures[hit['term']] = {
                    'count': hit['count'],
                    'rank': i + 1,
                    'percent': 100.0 * hit['count'] / total
                }

        for hit in results:
            sig = compare_signatures.get(hit['term'])
            if sig:
                hit['diff'] = sig['percent'] - hit['percent']
                hit['rank_diff'] = sig['rank'] - hit['rank']
                hit['previous_percent'] = sig['percent']
            else:
                hit['diff'] = 'new'
                hit['rank_diff'] = 0
                hit['previous_percent'] = 0

    return search_results
Пример #16
0
def signature_reports(request, params):
    '''Return the results of a search. '''

    signature = params['signature'][0]

    context = {}
    context['query'] = {'total': 0, 'total_count': 0, 'total_pages': 0}

    allowed_fields = get_allowed_fields(request.user)

    current_query = request.GET.copy()
    if 'page' in current_query:
        del current_query['page']

    context['params'] = current_query.copy()

    if '_sort' in context['params']:
        del context['params']['_sort']

    if '_columns' in context['params']:
        del context['params']['_columns']

    context['sort'] = request.GET.getlist('_sort')
    context['columns'] = request.GET.getlist('_columns') or DEFAULT_COLUMNS

    # Make sure only allowed fields are used.
    context['sort'] = [
        x for x in context['sort'] if x in allowed_fields or (
            x.startswith('-') and x[1:] in allowed_fields)
    ]
    context['columns'] = [x for x in context['columns'] if x in allowed_fields]

    params['_sort'] = context['sort']

    # Copy the list of columns so that they can differ.
    params['_columns'] = list(context['columns'])

    # The uuid is always displayed in the UI so we need to make sure it is
    # always returned by the model.
    if 'uuid' not in params['_columns']:
        params['_columns'].append('uuid')

    # We require the cpu_info field to show a special marker on some AMD CPU
    # related crash reports.
    if 'cpu_info' not in params['_columns']:
        params['_columns'].append('cpu_info')

    # The `uuid` field is a special case, it is always displayed in the first
    # column of the table. Hence we do not want to show it again in the
    # auto-generated list of columns, so we its name from the list of
    # columns to display.
    if 'uuid' in context['columns']:
        context['columns'].remove('uuid')

    try:
        current_page = int(request.GET.get('page', 1))
    except ValueError:
        return http.HttpResponseBadRequest('Invalid page')

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    context['current_page'] = current_page
    context['results_offset'] = results_per_page * (current_page - 1)

    params['signature'] = '=' + signature
    params['_results_number'] = results_per_page
    params['_results_offset'] = context['results_offset']
    params['_facets'] = []  # We don't need no facets.

    context['current_url'] = '%s?%s' % (reverse('signature:signature_report'),
                                        urlencode_obj(current_query))

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    search_results['total_pages'] = int(
        math.ceil(search_results['total'] / float(results_per_page)))
    search_results['total_count'] = search_results['total']

    context['query'] = search_results

    return render(request, 'signature/signature_reports.html', context)
Пример #17
0
def signature_summary(request, params):
    '''Return a list of specific aggregations. '''

    context = {}

    params['signature'] = '=' + params['signature'][0]
    params['_results_number'] = 0
    params['_facets'] = [
        'platform_pretty_version',
        'cpu_name',
        'process_type',
        'flash_version',
    ]
    params['_histogram.uptime'] = ['product']
    params['_histogram_interval.uptime'] = 60
    params['_aggs.adapter_vendor_id'] = ['adapter_device_id']
    params['_aggs.android_cpu_abi.android_manufacturer.android_model'] = [
        'android_version'
    ]

    # If the user has permissions, show exploitability.
    all_fields = SuperSearchFields().get()
    if has_permissions(request.user,
                       all_fields['exploitability']['permissions_needed']):
        params['_histogram.date'] = ['exploitability']

    api = SuperSearchUnredacted()

    # Now make the actual request with all expected parameters.
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    facets = search_results['facets']

    # We need to make a separate query so that we can show all versions and
    # not just the one asked for.
    params_copy = {
        'signature': params['signature'],
        '_aggs.product.version': ['_cardinality.install_time'],
    }

    try:
        product_results = api.get(**params_copy)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery
        # to pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    if 'product' in product_results['facets']:
        facets['product'] = product_results['facets']['product']
    else:
        facets['product'] = []

    context['product_version_total'] = product_results['total']

    _transform_uptime_summary(facets)
    _transform_graphics_summary(facets)
    _transform_mobile_summary(facets)
    _transform_exploitability_summary(facets)

    context['query'] = search_results

    return render(request, 'signature/signature_summary.html', context)
Пример #18
0
def exploitability_report(request, default_context=None):
    context = default_context or {}

    if not request.GET.get('product'):
        url = reverse('crashstats:exploitability_report')
        url += '?' + urllib.urlencode({'product': settings.DEFAULT_PRODUCT})
        return redirect(url)

    form = forms.ExploitabilityReportForm(
        request.GET,
        active_versions=context['active_versions'],
    )
    if not form.is_valid():
        return http.HttpResponseBadRequest(str(form.errors))

    product = form.cleaned_data['product']
    version = form.cleaned_data['version']

    api = SuperSearchUnredacted()
    params = {
        'product': product,
        'version': version,
        '_results_number': 0,
        # This aggregates on crashes that do NOT contain these
        # key words. For example, if a crash has
        # {'exploitability': 'error: unable to analyze dump'}
        # then it won't get included.
        'exploitability': ['!error', '!interesting'],
        '_aggs.signature': 'exploitability',
        '_facets_size': settings.EXPLOITABILITY_BATCH_SIZE,
    }
    results = api.get(**params)

    base_signature_report_dict = {
        'product': product,
    }
    if version:
        base_signature_report_dict['version'] = version

    crashes = []
    categories = ('high', 'none', 'low', 'medium', 'null')
    for signature_facet in results['facets']['signature']:
        # this 'signature_facet' will look something like this:
        #
        #  {
        #      'count': 1234,
        #      'term': 'My | Signature',
        #      'facets': {
        #          'exploitability': [
        #              {'count': 1, 'term': 'high'},
        #              {'count': 23, 'term': 'medium'},
        #              {'count': 11, 'term': 'other'},
        #
        # And we only want to include those where:
        #
        #   low or medium or high are greater than 0
        #

        exploitability = signature_facet['facets']['exploitability']
        if not any(x['count'] for x in exploitability
                   if x['term'] in ('high', 'medium', 'low')):
            continue
        crash = {
            'bugs': [],
            'signature':
            signature_facet['term'],
            'high_count':
            0,
            'medium_count':
            0,
            'low_count':
            0,
            'none_count':
            0,
            'url':
            (reverse('signature:signature_report') + '?' + urllib.urlencode(
                dict(base_signature_report_dict,
                     signature=signature_facet['term']))),
        }
        for cluster in exploitability:
            if cluster['term'] in categories:
                crash['{}_count'.format(cluster['term'])] = (cluster['count'])
        crash['med_or_high'] = (crash.get('high_count', 0) +
                                crash.get('medium_count', 0))
        crashes.append(crash)

    # Sort by the 'med_or_high' key first (descending),
    # and by the signature second (ascending).
    crashes.sort(key=lambda x: (-x['med_or_high'], x['signature']))

    # now, let's go back and fill in the bugs
    signatures = [x['signature'] for x in crashes]
    if signatures:
        api = models.Bugs()
        bugs = defaultdict(list)
        for b in api.get(signatures=signatures)['hits']:
            bugs[b['signature']].append(b['id'])

        for crash in crashes:
            crash['bugs'] = bugs.get(crash['signature'], [])

    context['crashes'] = crashes
    context['product'] = product
    context['version'] = version
    context['report'] = 'exploitable'
    return render(request, 'crashstats/exploitability_report.html', context)
Пример #19
0
def _get_crashes_per_day_with_adu(params, start_date, end_date, platforms,
                                  _date_range_type):
    api = SuperSearchUnredacted()
    results = api.get(**params)

    platforms_api = models.Platforms()
    platforms = platforms_api.get()

    # now get the ADI for these product versions
    api = models.ADI()
    adi_counts = api.get(
        product=params['product'],
        versions=params['version'],
        start_date=start_date,
        end_date=end_date,
        platforms=[x['name'] for x in platforms if x.get('display')],
    )

    api = models.ProductBuildTypes()
    product_build_types = api.get(product=params['product'])['hits']

    # This `adi_counts` is a list of dicts that looks like this:
    #    {
    #       'adi_count': 123,
    #       'date': '2015-08-15',
    #       'version': '40.0.2'
    #       'build_type': 'beta',
    #    }
    # We need to turn this around so that it's like this:
    #   {
    #       '40.0.2': {
    #           '2015-08-15': [123, 1.0],
    #            ...
    #       },
    #       ...
    #   }
    # So it can easily be looked up how many counts there are per
    # version per date.
    #
    # Note!! that the 1.0 in the example above is the throttle value
    # for this build_type. We got that from the ProductBuildTypes API.
    adi_by_version = {}

    # If any of the versions end with a 'b' we want to collect
    # and group them together under one.
    # I.e. if we have adi count of '42.0b1':123 and '42.0b2':345
    # then we want to combine that to just '42.0b':123+345

    beta_versions = [x for x in params['version'] if x.endswith('b')]

    def get_parent_version(ver):
        try:
            return [x for x in beta_versions if ver.startswith(x)][0]
        except IndexError:
            return None

    def add_version_to_adi(version, count, date, throttle):
        if version not in adi_by_version:
            adi_by_version[version] = {}

        try:
            before = adi_by_version[version][date][0]
        except KeyError:
            before = 0
        adi_by_version[version][date] = [count + before, throttle]

    for group in adi_counts['hits']:
        version = group['version']
        # Make this a string so it can be paired with the facets 'term'
        # key which is also a date in ISO format.
        date = group['date'].isoformat()
        build_type = group['build_type']
        count = group['adi_count']
        throttle = product_build_types[build_type]

        # If this version was requested, add it to the data structure.
        if version in params['version']:
            add_version_to_adi(version, count, date, throttle)

        # If this version is part of a beta, add it to the data structure.
        parent_version = get_parent_version(version)
        if parent_version is not None:
            version = parent_version
            add_version_to_adi(version, count, date, throttle)

    # We might have queried for aggregates for version ['19.0a1', '18.0b']
    # but SuperSearch will give us facets for versions:
    # ['19.0a1', '18.0b1', '18.0b2', '18.0b3']
    # The facets look something like this:
    #        {
    #            'histogram_date': [
    #                {
    #                    'count': 1234,
    #                    'facets': [
    #                        {'count': 201, 'term': '19.0a1'},
    #                        {'count': 196, 'term': '18.0b1'},
    #                        {'count': 309, 'term': '18.0b2'},
    #                        {'count': 991, 'term': '18.0b3'},
    #                    ],
    #                    'term': '2015-01-10T00:00:00'
    #                },
    #                ...
    #
    #            'version': [
    #                {'count': 45234, 'term': '19.0a1'},
    #                {'count': 39001, 'term': '18.0b1'},
    #                {'count': 56123, 'term': '18.0b2'},
    #                {'count': 90133, 'term': '18.0b3'},
    #
    # Our job is to rewrite that so it looks like this:
    #        {
    #            'histogram_date': [
    #                {
    #                    'count': 1234,
    #                    'facets': [
    #                        {'count': 201, 'term': '19.0a1'},
    #                        {'count': 196+309+991, 'term': '18.0b'},
    #                    ],
    #                    'term': '2015-01-10T00:00:00'
    #                },
    #                ...
    #
    #            'version': [
    #                {'count': 45234, 'term': '19.0a1'},
    #                {'count': 39001+56123+90133, 'term': '18.0b'},
    #

    histogram = results['facets']['histogram_date']
    for date_cluster in histogram:
        parent_totals = defaultdict(int)

        for facet_cluster in list(date_cluster['facets']['version']):
            version = facet_cluster['term']
            parent_version = get_parent_version(version)
            if parent_version is not None:
                parent_totals[parent_version] += facet_cluster['count']
            if version not in params['version']:
                date_cluster['facets']['version'].remove(facet_cluster)
        for version in parent_totals:
            date_cluster['facets']['version'].append({
                'count':
                parent_totals[version],
                'term':
                version
            })

    parent_totals = defaultdict(int)
    for facet_cluster in list(results['facets']['version']):
        version = facet_cluster['term']
        parent_version = get_parent_version(version)
        if parent_version is not None:
            parent_totals[parent_version] += facet_cluster['count']
        if version not in params['version']:
            results['facets']['version'].remove(facet_cluster)
    for version in parent_totals:
        results['facets']['version'].append({
            'count': parent_totals[version],
            'term': version,
        })

    graph_data = {}
    graph_data = build_data_object_for_crashes_per_day_graph(
        start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'),
        results['facets'], adi_by_version, _date_range_type)
    graph_data['product_versions'] = get_product_versions_for_crashes_per_day(
        results['facets'],
        params['product'],
    )

    return graph_data, results, adi_by_version
Пример #20
0
def get_topcrashers_results(**kwargs):
    '''Return the results of a search. '''
    results = []

    params = kwargs
    params['_aggs.signature'] = [
        'platform',
        'is_garbage_collecting',
        'hang_type',
        'process_type',
        '_histogram.uptime',
    ]
    params['_histogram_interval.uptime'] = 60

    # We don't care about no results, only facets.
    params['_results_number'] = 0

    if params.get('process_type') in ('any', 'all'):
        params['process_type'] = None

    api = SuperSearchUnredacted()
    search_results = api.get(**params)

    if search_results['total'] > 0:
        results = search_results['facets']['signature']

        platforms = models.Platforms().get_all()['hits']
        platform_codes = [
            x['code'] for x in platforms if x['code'] != 'unknown'
        ]

        for i, hit in enumerate(results):
            hit['signature'] = hit['term']
            hit['rank'] = i + 1
            hit['percent'] = 100.0 * hit['count'] / search_results['total']

            # Number of crash per platform.
            for platform in platform_codes:
                hit[platform + '_count'] = 0

            sig_platforms = hit['facets']['platform']
            for platform in sig_platforms:
                code = platform['term'][:3].lower()
                if code in platform_codes:
                    hit[code + '_count'] = platform['count']

            # Number of crashes happening during garbage collection.
            hit['is_gc_count'] = 0

            sig_gc = hit['facets']['is_garbage_collecting']
            for row in sig_gc:
                if row['term'].lower() == 't':
                    hit['is_gc_count'] = row['count']

            # Number of plugin crashes.
            hit['plugin_count'] = 0

            sig_process = hit['facets']['process_type']
            for row in sig_process:
                if row['term'].lower() == 'plugin':
                    hit['plugin_count'] = row['count']

            # Number of hang crashes.
            hit['hang_count'] = 0

            sig_hang = hit['facets']['hang_type']
            for row in sig_hang:
                # Hangs have weird values in the database: a value of 1 or -1
                # means it is a hang, a value of 0 or missing means it is not.
                if row['term'] in (1, -1):
                    hit['hang_count'] += row['count']

            # Number of startup crashes.
            hit['startup_percent'] = 0

            sig_startup = hit['facets']['histogram_uptime']
            for row in sig_startup:
                if row['term'] == 0:
                    ratio = 1.0 * row['count'] / hit['count']
                    hit['startup_crash'] = ratio > 0.5

        # Run the same query but for the previous date range, so we can
        # compare the rankings and show rank changes.
        dates = get_date_boundaries(params)
        delta = (dates[1] - dates[0]) * 2
        params['date'] = [
            '>=' + (dates[1] - delta).isoformat(), '<' + dates[0].isoformat()
        ]
        params['_aggs.signature'] = [
            'platform',
        ]

        previous_range_results = api.get(**params)
        total = previous_range_results['total']

        compare_signatures = {}
        if total > 0 and 'signature' in previous_range_results['facets']:
            signatures = previous_range_results['facets']['signature']
            for i, hit in enumerate(signatures):
                compare_signatures[hit['term']] = {
                    'count': hit['count'],
                    'rank': i + 1,
                    'percent': 100.0 * hit['count'] / total
                }

        for hit in results:
            sig = compare_signatures.get(hit['term'])
            if sig:
                hit['diff'] = sig['percent'] - hit['percent']
                hit['rank_diff'] = sig['rank'] - hit['rank']
                hit['previous_percent'] = sig['percent']
            else:
                hit['diff'] = 'new'
                hit['rank_diff'] = 0
                hit['previous_percent'] = 0

    return search_results
Пример #21
0
def get_topcrashers_stats(**kwargs):
    """Return the results of a search. """
    params = kwargs
    range_type = params.pop("_range_type")
    dates = get_date_boundaries(params)

    params["_aggs.signature"] = [
        "platform",
        "is_garbage_collecting",
        "hang_type",
        "process_type",
        "startup_crash",
        "_histogram.uptime",
        "_cardinality.install_time",
    ]
    params["_histogram_interval.uptime"] = 60

    # We don't care about no results, only facets.
    params["_results_number"] = 0

    if params.get("process_type") in ("any", "all"):
        params["process_type"] = None

    if range_type == "build":
        params["build_id"] = [
            ">=" + datetime_to_build_id(dates[0]),
            "<" + datetime_to_build_id(dates[1]),
        ]

    api = SuperSearchUnredacted()
    search_results = api.get(**params)

    signatures_stats = []
    if search_results["total"] > 0:
        # Run the same query but for the previous date range, so we can
        # compare the rankings and show rank changes.
        delta = (dates[1] - dates[0]) * 2
        params["date"] = [
            ">=" + (dates[1] - delta).isoformat(),
            "<" + dates[0].isoformat(),
        ]
        params["_aggs.signature"] = ["platform"]
        params["_facets_size"] *= 2

        if range_type == "build":
            params["date"][1] = "<" + dates[1].isoformat()
            params["build_id"] = [
                ">=" + datetime_to_build_id(dates[1] - delta),
                "<" + datetime_to_build_id(dates[0]),
            ]

        previous_range_results = api.get(**params)
        previous_signatures = get_comparison_signatures(previous_range_results)

        for index, signature in enumerate(
                search_results["facets"]["signature"]):
            previous_signature = previous_signatures.get(signature["term"])
            signatures_stats.append(
                SignatureStats(
                    signature=signature,
                    num_total_crashes=search_results["total"],
                    rank=index,
                    platforms=models.Platform.objects.values(),
                    previous_signature=previous_signature,
                ))
    return signatures_stats
Пример #22
0
def search_results(request):
    """Return the results of a search"""
    try:
        params = get_params(request)
    except ValidationError as e:
        # There was an error in the form, let's return it.
        return http.HttpResponseBadRequest(str(e))

    context = {}
    context["query"] = {"total": 0, "total_count": 0, "total_pages": 0}

    current_query = request.GET.copy()
    if "page" in current_query:
        del current_query["page"]

    context["params"] = current_query.copy()

    if "_columns" in context["params"]:
        del context["params"]["_columns"]

    if "_facets" in context["params"]:
        del context["params"]["_facets"]

    context["sort"] = list(params["_sort"])

    # Copy the list of columns so that they can differ.
    context["columns"] = list(params["_columns"])

    # The `uuid` field is a special case, it is always displayed in the first
    # column of the table. Hence we do not want to show it again in the
    # auto-generated list of columns, so we remove it from the list of
    # columns to display.
    if "uuid" in context["columns"]:
        context["columns"].remove("uuid")

    try:
        current_page = int(request.GET.get("page", 1))
    except ValueError:
        return http.HttpResponseBadRequest("Invalid page")

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    context["current_page"] = current_page
    context["results_offset"] = results_per_page * (current_page - 1)

    params["_results_number"] = results_per_page
    params["_results_offset"] = context["results_offset"]

    context["current_url"] = "%s?%s" % (
        reverse("supersearch:search"),
        urlencode_obj(current_query),
    )

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as exception:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(exception))

    if "signature" in search_results["facets"]:
        # Bugs for each signature
        signatures = [h["term"] for h in search_results["facets"]["signature"]]

        if signatures:
            bugs = defaultdict(list)
            qs = models.BugAssociation.objects.filter(signature__in=signatures).values(
                "bug_id", "signature"
            )
            for item in qs:
                bugs[item["signature"]].append(item["bug_id"])

            for hit in search_results["facets"]["signature"]:
                sig = hit["term"]
                if sig in bugs:
                    if "bugs" in hit:
                        hit["bugs"].extend(bugs[sig])
                    else:
                        hit["bugs"] = bugs[sig]
                    # most recent bugs first
                    hit["bugs"].sort(reverse=True)

    search_results["total_pages"] = int(
        math.ceil(search_results["total"] / float(results_per_page))
    )
    search_results["total_count"] = search_results["total"]

    context["query"] = search_results

    return render(request, "supersearch/search_results.html", context)
Пример #23
0
def signature_reports(request, params):
    '''Return the results of a search. '''

    signature = params['signature'][0]

    data = {}
    data['query'] = {
        'total': 0,
        'total_count': 0,
        'total_pages': 0
    }

    allowed_fields = get_allowed_fields(request.user)

    current_query = request.GET.copy()
    if 'page' in current_query:
        del current_query['page']

    data['params'] = current_query.copy()

    if '_columns' in data['params']:
        del data['params']['_columns']

    data['columns'] = request.GET.getlist('_columns') or DEFAULT_COLUMNS

    # Make sure only allowed fields are used
    data['columns'] = [
        x for x in data['columns'] if x in allowed_fields
    ]

    # Copy the list of columns so that they can differ.
    params['_columns'] = list(data['columns'])

    # The uuid is always displayed in the UI so we need to make sure it is
    # always returned by the model.
    if 'uuid' not in params['_columns']:
        params['_columns'].append('uuid')

    # The `uuid` field is a special case, it is always displayed in the first
    # column of the table. Hence we do not want to show it again in the
    # auto-generated list of columns, so we its name from the list of
    # columns to display.
    if 'uuid' in data['columns']:
        data['columns'].remove('uuid')

    try:
        current_page = int(request.GET.get('page', 1))
    except ValueError:
        return http.HttpResponseBadRequest('Invalid page')

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    data['current_page'] = current_page
    data['results_offset'] = results_per_page * (current_page - 1)

    params['signature'] = '=' + signature
    params['_results_number'] = results_per_page
    params['_results_offset'] = data['results_offset']
    params['_facets'] = []  # We don't need no facets.

    data['current_url'] = '%s?%s' % (
        reverse('signature:signature_report'),
        current_query.urlencode()
    )

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except models.BadStatusCodeError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest('<ul><li>%s</li></ul>' % e)

    search_results['total_pages'] = int(
        math.ceil(
            search_results['total'] / float(results_per_page)
        )
    )
    search_results['total_count'] = search_results['total']

    data['query'] = search_results

    return render(request, 'signature/signature_reports.html', data)
Пример #24
0
def get_topcrashers_stats(**kwargs):
    """Return the results of a search. """
    params = kwargs
    range_type = params.pop('_range_type')
    dates = get_date_boundaries(params)

    params['_aggs.signature'] = [
        'platform',
        'is_garbage_collecting',
        'hang_type',
        'process_type',
        'startup_crash',
        '_histogram.uptime',
        '_cardinality.install_time',
    ]
    params['_histogram_interval.uptime'] = 60

    # We don't care about no results, only facets.
    params['_results_number'] = 0

    if params.get('process_type') in ('any', 'all'):
        params['process_type'] = None

    if range_type == 'build':
        params['build_id'] = [
            '>=' + datetime_to_build_id(dates[0]),
            '<' + datetime_to_build_id(dates[1])
        ]

    api = SuperSearchUnredacted()
    search_results = api.get(**params)

    signatures_stats = []
    if search_results['total'] > 0:
        # Run the same query but for the previous date range, so we can
        # compare the rankings and show rank changes.
        delta = (dates[1] - dates[0]) * 2
        params['date'] = [
            '>=' + (dates[1] - delta).isoformat(), '<' + dates[0].isoformat()
        ]
        params['_aggs.signature'] = [
            'platform',
        ]
        params['_facets_size'] *= 2

        if range_type == 'build':
            params['date'][1] = '<' + dates[1].isoformat()
            params['build_id'] = [
                '>=' + datetime_to_build_id(dates[1] - delta),
                '<' + datetime_to_build_id(dates[0])
            ]

        previous_range_results = api.get(**params)
        previous_signatures = get_comparison_signatures(previous_range_results)

        for index, signature in enumerate(
                search_results['facets']['signature']):
            previous_signature = previous_signatures.get(signature['term'])
            signatures_stats.append(
                SignatureStats(
                    signature=signature,
                    num_total_crashes=search_results['total'],
                    rank=index,
                    platforms=models.Platforms().get_all()['hits'],
                    previous_signature=previous_signature,
                ))
    return signatures_stats
Пример #25
0
def exploitability_report(request, default_context=None):
    context = default_context or {}

    if not request.GET.get("product"):
        url = reverse("exploitability:report")
        url += "?" + urlencode({"product": settings.DEFAULT_PRODUCT})
        return redirect(url)

    form = ExploitabilityReportForm(
        request.GET, active_versions=context["active_versions"]
    )
    if not form.is_valid():
        return http.HttpResponseBadRequest(str(form.errors))

    product = form.cleaned_data["product"]
    version = form.cleaned_data["version"]

    api = SuperSearchUnredacted()
    params = {
        "product": product,
        "version": version,
        "_results_number": 0,
        # This aggregates on crashes that do NOT contain these
        # key words. For example, if a crash has
        # {'exploitability': 'error: unable to analyze dump'}
        # then it won't get included.
        "exploitability": ["!error", "!interesting"],
        "_aggs.signature": "exploitability",
        "_facets_size": settings.EXPLOITABILITY_BATCH_SIZE,
    }
    results = api.get(**params)

    base_signature_report_dict = {"product": product}
    if version:
        base_signature_report_dict["version"] = version

    crashes = []
    categories = ("high", "none", "low", "medium", "null")
    for signature_facet in results["facets"]["signature"]:
        # this 'signature_facet' will look something like this:
        #
        #  {
        #      'count': 1234,
        #      'term': 'My | Signature',
        #      'facets': {
        #          'exploitability': [
        #              {'count': 1, 'term': 'high'},
        #              {'count': 23, 'term': 'medium'},
        #              {'count': 11, 'term': 'other'},
        #
        # And we only want to include those where:
        #
        #   low or medium or high are greater than 0
        #

        exploitability = signature_facet["facets"]["exploitability"]
        if not any(
            x["count"] for x in exploitability if x["term"] in ("high", "medium", "low")
        ):
            continue
        crash = {
            "bugs": [],
            "signature": signature_facet["term"],
            "high_count": 0,
            "medium_count": 0,
            "low_count": 0,
            "none_count": 0,
            "url": (
                reverse("signature:signature_report")
                + "?"
                + urlencode(
                    dict(base_signature_report_dict, signature=signature_facet["term"])
                )
            ),
        }
        for cluster in exploitability:
            if cluster["term"] in categories:
                crash["{}_count".format(cluster["term"])] = cluster["count"]
        crash["med_or_high"] = crash.get("high_count", 0) + crash.get("medium_count", 0)
        crashes.append(crash)

    # Sort by the 'med_or_high' key first (descending),
    # and by the signature second (ascending).
    crashes.sort(key=lambda x: (-x["med_or_high"], x["signature"]))

    # now, let's go back and fill in the bugs
    signatures = [x["signature"] for x in crashes]
    if signatures:
        qs = (
            models.BugAssociation.objects.filter(signature__in=signatures)
            .values("bug_id", "signature")
            .order_by("-bug_id", "signature")
        )
        bugs = defaultdict(list)
        for item in qs:
            bugs[item["signature"]].append(item["bug_id"])

        for crash in crashes:
            crash["bugs"] = bugs.get(crash["signature"], [])

    context["crashes"] = crashes
    context["product"] = product
    context["version"] = version
    context["report"] = "exploitable"

    return render(request, "exploitability/report.html", context)
def search_results(request):
    """Return the results of a search"""
    try:
        params = get_params(request)
    except ValidationError as e:
        # There was an error in the form, let's return it.
        return http.HttpResponseBadRequest(str(e))

    context = {}
    context['query'] = {
        'total': 0,
        'total_count': 0,
        'total_pages': 0
    }

    current_query = request.GET.copy()
    if 'page' in current_query:
        del current_query['page']

    context['params'] = current_query.copy()

    if '_columns' in context['params']:
        del context['params']['_columns']

    if '_facets' in context['params']:
        del context['params']['_facets']

    context['sort'] = list(params['_sort'])

    # Copy the list of columns so that they can differ.
    context['columns'] = list(params['_columns'])

    # The `uuid` field is a special case, it is always displayed in the first
    # column of the table. Hence we do not want to show it again in the
    # auto-generated list of columns, so we remove it from the list of
    # columns to display.
    if 'uuid' in context['columns']:
        context['columns'].remove('uuid')

    try:
        current_page = int(request.GET.get('page', 1))
    except ValueError:
        return http.HttpResponseBadRequest('Invalid page')

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    context['current_page'] = current_page
    context['results_offset'] = results_per_page * (current_page - 1)

    params['_results_number'] = results_per_page
    params['_results_offset'] = context['results_offset']

    context['current_url'] = '%s?%s' % (
        reverse('supersearch:search'),
        urlencode_obj(current_query)
    )

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as exception:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(
            render_exception(exception)
        )

    if 'signature' in search_results['facets']:
        # Bugs for each signature
        signatures = [h['term'] for h in search_results['facets']['signature']]

        if signatures:
            bugs = defaultdict(list)
            qs = (
                models.BugAssociation.objects
                .filter(signature__in=signatures)
                .values('bug_id', 'signature')
            )
            for item in qs:
                bugs[item['signature']].append(item['bug_id'])

            for hit in search_results['facets']['signature']:
                sig = hit['term']
                if sig in bugs:
                    if 'bugs' in hit:
                        hit['bugs'].extend(bugs[sig])
                    else:
                        hit['bugs'] = bugs[sig]
                    # most recent bugs first
                    hit['bugs'].sort(reverse=True)

    search_results['total_pages'] = int(math.ceil(
        search_results['total'] / float(results_per_page)))
    search_results['total_count'] = search_results['total']

    context['query'] = search_results

    return render(request, 'supersearch/search_results.html', context)
Пример #27
0
def signature_reports(request, params):
    """Return the results of a search. """

    signature = params["signature"][0]

    context = {}
    context["query"] = {"total": 0, "total_count": 0, "total_pages": 0}

    allowed_fields = get_allowed_fields(request.user)

    current_query = request.GET.copy()
    if "page" in current_query:
        del current_query["page"]

    context["params"] = current_query.copy()

    if "_sort" in context["params"]:
        del context["params"]["_sort"]

    if "_columns" in context["params"]:
        del context["params"]["_columns"]

    context["sort"] = request.GET.getlist("_sort")
    context["columns"] = request.GET.getlist("_columns") or DEFAULT_COLUMNS

    # Make sure only allowed fields are used.
    context["sort"] = [
        x for x in context["sort"] if x in allowed_fields or (
            x.startswith("-") and x[1:] in allowed_fields)
    ]
    context["columns"] = [x for x in context["columns"] if x in allowed_fields]

    params["_sort"] = context["sort"]

    # Copy the list of columns so that they can differ.
    params["_columns"] = list(context["columns"])

    # The uuid is always displayed in the UI so we need to make sure it is
    # always returned by the model.
    if "uuid" not in params["_columns"]:
        params["_columns"].append("uuid")

    # We require the cpu_info field to show a special marker on some AMD CPU
    # related crash reports.
    if "cpu_info" not in params["_columns"]:
        params["_columns"].append("cpu_info")

    # The `uuid` field is a special case, it is always displayed in the first
    # column of the table. Hence we do not want to show it again in the
    # auto-generated list of columns, so we its name from the list of
    # columns to display.
    if "uuid" in context["columns"]:
        context["columns"].remove("uuid")

    try:
        current_page = int(request.GET.get("page", 1))
    except ValueError:
        return http.HttpResponseBadRequest("Invalid page")

    if current_page <= 0:
        current_page = 1

    results_per_page = 50
    context["current_page"] = current_page
    context["results_offset"] = results_per_page * (current_page - 1)

    params["signature"] = "=" + signature
    params["_results_number"] = results_per_page
    params["_results_offset"] = context["results_offset"]
    params["_facets"] = []  # We don't need no facets.

    context["current_url"] = "%s?%s" % (
        reverse("signature:signature_report"),
        urlencode_obj(current_query),
    )

    api = SuperSearchUnredacted()
    try:
        search_results = api.get(**params)
    except BadArgumentError as e:
        # We need to return the error message in some HTML form for jQuery to
        # pick it up.
        return http.HttpResponseBadRequest(render_exception(e))

    search_results["total_pages"] = int(
        math.ceil(search_results["total"] / float(results_per_page)))
    search_results["total_count"] = search_results["total"]

    context["query"] = search_results

    return render(request, "signature/signature_reports.html", context)