Skip to content

Commit

Permalink
bug 974968, 1629943: improve Extensions tab
Browse files Browse the repository at this point in the history
This improves the data in the Extensions tab by combining the data in
the "Add-ons" crash report annotation with the data in the
"addons.activeAddons" Telemetry Environment section. In particular,
it adds "name", "is_system", and "signed_state" bits.
  • Loading branch information
willkg committed Apr 16, 2021
1 parent 31fe07e commit 8686fcf
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 21 deletions.
1 change: 1 addition & 0 deletions socorro/processor/rules/mozilla.py
Expand Up @@ -178,6 +178,7 @@ def _get_formatted_addon(self, addon):
This is used because some addons are missing a version. In order to
simplify subsequent queries, we make sure the format is consistent.
"""
return addon if ":" in addon else addon + ":NO_VERSION"

Expand Down
Expand Up @@ -1051,20 +1051,21 @@ <h3>minidump-stackwalk output</h3>
<table class="data-table">
<thead>
<tr>
<th scope="col">Extension</th>
<th scope="col">Extension Id</th>
<th scope="col">Name</th>
<th scope="col">Version</th>
<th scope="col">Current?</th>
<th scope="col">System?</th>
<th scope="col">Signed state</th>
</tr>
</thead>
<tbody>
{% for addon in report.addons %}
{% set split = addon.split(':') %}
<tr>
<td><a href=""></a></td>
<td>{{ split[0] }}</td>
<td>{{ split[1] }}</td>
<td><b>{{ split[2] }}</b></td>
<td>{{ addon.id or "" }}</td>
<td>{{ addon.name or "" }}</td>
<td>{{ addon.version or "" }}</td>
<td>{{ addon.is_system or "" }}</td>
<td>{{ addon.get_signed_state_name() }}</td>
</tr>
{% endfor %}
</tbody>
Expand Down
62 changes: 62 additions & 0 deletions webapp-django/crashstats/crashstats/tests/test_utils.py
Expand Up @@ -9,6 +9,8 @@
from django.http import HttpResponse
from django.utils.encoding import smart_text

import pytest

from crashstats.crashstats import utils


Expand Down Expand Up @@ -504,3 +506,63 @@ def test_parse_graphics_devices_iterable__pci_ids():
"vendor_name": "Silicon Image, Inc. (Wrong ID)",
},
]


@pytest.mark.parametrize(
"raw, processed, expected",
[
# Missing data cases
({}, {}, []),
({}, {"addons": []}, []),
({"TelemetryEnvironment": "{}"}, {"addons": []}, []),
# TelemetryEnvironment with bad JSON
(
{"TelemetryEnvironment": "{foo"},
{"addons": ["someid:5.0"]},
[utils.Addon(id="someid", version="5.0")],
),
# Valid data
(
{
"TelemetryEnvironment": json.dumps(
{
"addons": {
"activeAddons": {
"someid": {
"version": "5.0",
"scope": 1,
"type": "extension",
"updateDay": 18140,
"isSystem": False,
"isWebExtension": True,
"multiprocessCompatible": True,
"blocklisted": False,
"description": "this is an addon",
"name": "Some Addon",
"userDisabled": False,
"appDisabled": False,
"foreignInstall": False,
"hasBinaryComponents": False,
"installDay": 18140,
"signedState": 2,
},
}
}
}
)
},
{"addons": ["someid:5.0"]},
[
utils.Addon(
id="someid",
version="5.0",
name="Some Addon",
is_system=False,
signed_state=2,
)
],
),
],
)
def test_enhance_addons(raw, processed, expected):
assert utils.enhance_addons(raw, processed) == expected
79 changes: 79 additions & 0 deletions webapp-django/crashstats/crashstats/utils.py
Expand Up @@ -3,13 +3,15 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from collections import OrderedDict
from dataclasses import dataclass
import datetime
import functools
import isodate
import json
import logging
import random
import re
from typing import Optional
from urllib.parse import urlencode

from django import http
Expand All @@ -20,6 +22,8 @@
from django.utils.encoding import smart_text
from django.utils.functional import cached_property

from glom import glom

from crashstats import productlib
from crashstats.crashstats import models
import crashstats.supersearch.models as supersearch_models
Expand Down Expand Up @@ -365,6 +369,81 @@ def enhance_raw(raw_crash):
raw_crash["AdapterDeviceName"] = result[1]


# From /toolkit/mozapps/extensions/AddonManager.jsm Addon.signedState section
# in mozilla-central
SIGNED_STATE_ID_TO_NAME = {
None: "SIGNEDSTATE_NOT_REQUIRED",
-2: "SIGNEDSTATE_BROKEN",
# Add-on may be signed but by an certificate that doesn't chain to our
# our trusted certificate.
-1: "SIGNEDSTATE_UNKNOWN",
# Add-on is unsigned.
0: "SIGNEDSTATE_MISSING",
# Add-on is preliminarily reviewed.
1: "SIGNEDSTATE_PRELIMINARY",
# Add-on is fully reviewed.
2: "SIGNEDSTATE_SIGNED",
# Add-on is system add-on.
3: "SIGNEDSTATE_SYSTEM",
# Add-on is signed with a "Mozilla Extensions" certificate
4: "SIGNEDSTATE_PRIVILEGED",
}


@dataclass
class Addon:
id: str
version: str
name: Optional[str] = None
is_system: Optional[bool] = None
signed_state: Optional[int] = None

def get_signed_state_name(self):
return SIGNED_STATE_ID_TO_NAME.get(self.signed_state, self.signed_state)


def enhance_addons(raw_crash, processed_crash):
"""Enhances "addons" in processed crash with TelemetryEnvironment information
:arg raw_crash: the raw crash structure
:arg processed_crash: the processed crash structure
:returns: list of Addon instances
"""
addon_data = processed_crash.get("addons", [])
if not addon_data:
return []

try:
telemetry_environment = json.loads(raw_crash.get("TelemetryEnvironment", "{}"))
active_addons = glom(telemetry_environment, "addons.activeAddons", default={})
except json.JSONDecodeError:
active_addons = {}

ret = []
for addon_line in addon_data:
split_data = addon_line.split(":")
extension_id = ""
version = ""

if len(split_data) >= 1:
extension_id = split_data[0]
if len(split_data) >= 2:
version = split_data[1]

addon = Addon(id=extension_id, version=version)

more_data = active_addons.get(extension_id, {})
addon.name = more_data.get("name", None)
addon.is_system = more_data.get("isSystem", None)
addon.signed_state = more_data.get("signedState", None)

ret.append(addon)

return ret


def get_versions_for_product(product, use_cache=True):
"""Returns list of recent version strings for specified product
Expand Down
18 changes: 4 additions & 14 deletions webapp-django/crashstats/crashstats/views.py
Expand Up @@ -172,6 +172,10 @@ def report_index(request, crash_id, default_context=None):
.order_by("-bug_id")
)

context["report"]["addons"] = utils.enhance_addons(
context["raw"], context["report"]
)

context["public_raw_keys"] = [
x for x in context["raw"] if x in models.RawCrash.API_ALLOWLIST()
]
Expand Down Expand Up @@ -261,20 +265,6 @@ def make_raw_crash_key(key):
context["fields_desc"] = descriptions
context["empty_desc"] = "No description for this field. Search: unknown"

# report.addons used to be a list of lists.
# In https://bugzilla.mozilla.org/show_bug.cgi?id=1250132
# we changed it from a list of lists to a list of strings, using
# a ':' to split the name and version.
# See https://bugzilla.mozilla.org/show_bug.cgi?id=1250132#c7
# Considering legacy, let's tackle both.
# In late 2017, this code is going to be useless and can be removed.
if context["report"].get("addons") and isinstance(
context["report"]["addons"][0], (list, tuple)
):
# This is the old legacy format. This crash hasn't been processed
# the new way.
context["report"]["addons"] = [":".join(x) for x in context["report"]["addons"]]

content = loader.render_to_string("crashstats/report_index.html", context, request)
utf8_content = content.encode("utf-8", errors="backslashreplace")
return HttpResponse(utf8_content, charset="utf-8")
Expand Down

0 comments on commit 8686fcf

Please sign in to comment.