Skip to content

Commit

Permalink
Bug 1730918 - use default security groups as defined by product in Bu…
Browse files Browse the repository at this point in the history
…gzilla (#7281)

Support for filing issues as security bugs got added in bug 1369067. Having the
security group hardcoded and not matching the default one for the component
causes bugs to get missed during triage.
  • Loading branch information
Archaeopteryx authored Oct 7, 2021
1 parent 94b78c0 commit 8a9f7e1
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 16 deletions.
3 changes: 3 additions & 0 deletions docker/entrypoint_prod.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ elif [ "$1" == "update_bugscache" ]; then
elif [ "$1" == "update_files_bugzilla_map" ]; then
newrelic-admin run-program ./manage.py update_files_bugzilla_map

elif [ "$1" == "update_bugzilla_security_groups" ]; then
newrelic-admin run-program ./manage.py update_bugzilla_security_groups

elif [ "$1" == "cache_failure_history" ]; then
newrelic-admin run-program ./manage.py cache_failure_history

Expand Down
2 changes: 1 addition & 1 deletion newrelic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ shutdown_timeout = 15
# List finite-duration commands here to enable their annotation by the agent.
# For infinite duration commands (such as `pulse_listener_*`) see:
# https://docs.newrelic.com/docs/agents/python-agent/supported-features/python-background-tasks#wrapping
instrumentation.scripts.django_admin = update_changelog check cycle_data load_initial_data perf_sheriff report_backfill_outcome migrate update_bugscache update_files_bugzilla_map run_intermittents_commenter synthesize_backfill_report backfill_text_log_error_jobs
instrumentation.scripts.django_admin = update_changelog check cycle_data load_initial_data perf_sheriff report_backfill_outcome migrate update_bugscache update_files_bugzilla_map update_bugzilla_security_groups run_intermittents_commenter synthesize_backfill_report backfill_text_log_error_jobs
10 changes: 5 additions & 5 deletions tests/webapp/api/test_bugzilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def request_callback(request):
"comment": u"Intermittent Description",
"comment_tags": "treeherder",
"keywords": ["intermittent-failure"],
"groups": [],
"is_security_issue": False,
},
)
assert resp.status_code == 200
Expand Down Expand Up @@ -103,7 +103,7 @@ def request_callback(request):
"comment": u"Intermittent “description” string",
"comment_tags": "treeherder",
"keywords": ["intermittent-failure"],
"groups": [],
"is_security_issue": False,
},
)
assert resp.status_code == 200
Expand Down Expand Up @@ -158,7 +158,7 @@ def request_callback(request):
"crash_signature": "[@crashsig]",
"priority": "--",
"keywords": ["intermittent-failure", "crash"],
"groups": [],
"is_security_issue": False,
},
)
assert resp.status_code == 200
Expand Down Expand Up @@ -207,7 +207,7 @@ def request_callback(request):
"comment_tags": "treeherder",
"keywords": ["intermittent-failure"],
"see_also": "12345",
"groups": [],
"is_security_issue": False,
},
)
assert resp.status_code == 403
Expand Down Expand Up @@ -265,7 +265,7 @@ def request_callback(request):
"crash_signature": crashsig,
"regressed_by": "123",
"see_also": "12345",
"groups": [],
"is_security_issue": False,
},
)
assert resp.status_code == 400
Expand Down
86 changes: 81 additions & 5 deletions treeherder/etl/files_bugzilla_map.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import logging
import sys

from treeherder.model.models import BugzillaComponent, FilesBugzillaMap, Repository
from treeherder.model.models import (
BugzillaComponent,
BugzillaSecurityGroup,
FilesBugzillaMap,
Repository,
)
from treeherder.utils.github import fetch_json

logger = logging.getLogger(__name__)
Expand All @@ -25,7 +31,7 @@ def get_or_add_bugzilla_component(self, files_bugzilla_data, path):
component = product_component_data[1]
if len(product) > self.max_product_length:
logger.error(
"error inserting Bugzilla product and component \"'%s' :: '%s'\" into db (file skipped: '%s'): product is too long (has %d characters, max is %d",
"error inserting Bugzilla product and component \"'%s' :: '%s'\" into db (file skipped: '%s'): product is too long (has %d characters, max is %d)",
product,
component,
path,
Expand All @@ -35,17 +41,17 @@ def get_or_add_bugzilla_component(self, files_bugzilla_data, path):
return
if len(component) > self.max_component_length:
logger.error(
"error inserting Bugzilla product and component \"'%s' :: '%s'\" into db (file skipped: '%s'): component is too long (has %d characters, max is %d",
"error inserting Bugzilla product and component \"'%s' :: '%s'\" into db (file skipped: '%s'): component is too long (has %d characters, max is %d)",
product,
component,
path,
len(product),
len(component),
self.max_component_length,
)
return
if len(path) > self.max_path_length:
logger.error(
"error inserting Bugzilla product and component \"'%s' :: '%s'\" into db (file skipped: '%s'): path is too long (has %d characters, max is %d",
"error inserting Bugzilla product and component \"'%s' :: '%s'\" into db (file skipped: '%s'): path is too long (has %d characters, max is %d)",
product,
component,
path,
Expand Down Expand Up @@ -189,3 +195,73 @@ def run(self):
)
bugzilla_components_unused = bugzilla_components_all.difference(bugzilla_components_used)
(BugzillaComponent.objects.filter(id__in=bugzilla_components_unused).delete())


class ProductSecurityGroupProcess:
max_product_length = BugzillaSecurityGroup._meta.get_field('product').max_length
max_security_group_length = BugzillaSecurityGroup._meta.get_field('security_group').max_length

def fetch_data(self):
url = 'https://bugzilla.mozilla.org/latest/configuration'
product_security_group_data = None
exception = None
try:
product_security_group_data = fetch_json(url)
except Exception as e:
exception = e
return {
"url": url,
"product_security_group_data": product_security_group_data,
"exception": exception,
}

def run(self):
data_returned = self.fetch_data()
if data_returned["exception"] is not None:
logger.error(
"error fetching file with map of source paths to Bugzilla products and components: url: %s ; %s",
data_returned["url"],
data_returned["exception"],
)
sys.exit()
fields_data = data_returned["product_security_group_data"]["field"]["product"]["values"]
groups_data = data_returned["product_security_group_data"]["group"]
products = set()
for field_data in fields_data:
product_name = str(field_data["name"])
security_group_id = str(field_data["security_group_id"])
if security_group_id in groups_data:
security_group_name = str(groups_data[security_group_id]["name"])
products.add(product_name)
try:
if len(product_name) > self.max_product_length:
logger.error(
"error inserting Bugzilla product and security group \"'%s' :: '%s'\" into db: product is too long (has %d characters, max is %d)",
product_name,
security_group_name,
len(product_name),
self.max_product_length,
)
continue
if len(security_group_name) > self.max_security_group_length:
logger.error(
"error inserting Bugzilla product and security group \"'%s' :: '%s'\" into db: security group is too long (has %d characters, max is %d)",
product_name,
security_group_name,
len(security_group_name),
self.max_security_group_length,
)
continue
BugzillaSecurityGroup.objects.get_or_create(
product=product_name,
security_group=security_group_name,
)
except Exception as e:
logger.error(
"error inserting Bugzilla product and security group \"'%s' :: '%s'\" into db: %s",
product_name,
security_group_name,
e,
)
continue
BugzillaSecurityGroup.objects.exclude(product__in=products).delete()
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.core.management.base import BaseCommand

from treeherder.etl.files_bugzilla_map import ProductSecurityGroupProcess


class Command(BaseCommand):
"""Management command to manually update security groups for bugzilla products"""

def handle(self, *args, **options):
process = ProductSecurityGroupProcess()
process.run()
30 changes: 30 additions & 0 deletions treeherder/model/migrations/0024_add_bugzillasecuritygroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 3.1.12 on 2021-09-29 12:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('model', '0023_add_filebugzillacomponent'),
]

operations = [
migrations.CreateModel(
name='BugzillaSecurityGroup',
fields=[
(
'id',
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name='ID'
),
),
('product', models.CharField(db_index=True, max_length=60, unique=True)),
('security_group', models.CharField(max_length=60)),
],
options={
'verbose_name_plural': 'bugzilla_security_groups',
'db_table': 'bugzilla_security_group',
},
),
]
9 changes: 9 additions & 0 deletions treeherder/model/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,15 @@ def __str__(self):
return "{0}".format(self.path)


class BugzillaSecurityGroup(models.Model):
product = models.CharField(max_length=60, unique=True, db_index=True)
security_group = models.CharField(max_length=60)

class Meta:
db_table = 'bugzilla_security_group'
verbose_name_plural = 'bugzilla_security_groups'


class Machine(NamedModel):
class Meta:
db_table = 'machine'
Expand Down
17 changes: 15 additions & 2 deletions treeherder/webapp/api/bugzilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUEST

from treeherder.model.models import BugzillaSecurityGroup
from treeherder.utils.http import make_request


Expand Down Expand Up @@ -51,8 +52,20 @@ def create_bug(self, request):
'description': description,
'comment_tags': "treeherder",
}
if len(params.get('groups')) > 0:
data['groups'] = params.get('groups')
if params.get("is_security_issue"):
security_group_list = list(
BugzillaSecurityGroup.objects.filter(product=data.get("product")).values_list(
"security_group", flat=True
)
)
if len(security_group_list) == 0:
return Response(
{
"failure": "Cannot file security bug for product without default security group in Bugzilla."
},
status=HTTP_400_BAD_REQUEST,
)
data["groups"] = security_group_list

try:
response = make_request(url, method='POST', headers=headers, json=data)
Expand Down
2 changes: 1 addition & 1 deletion ui/helpers/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const getData = async function getData(url, options = {}) {
const contentType =
response.headers.get('content-type') || ''.startsWith('text/html');

if (contentType && failureStatus) {
if (contentType && contentType !== 'application/json' && failureStatus) {
const errorMessage = processErrorMessage(
`${failureStatus}: ${response.statusText}`,
failureStatus,
Expand Down
4 changes: 2 additions & 2 deletions ui/shared/BugFiler.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ export class BugFilerClass extends React.Component {
crash_signature: crashSignature,
severity: 'S4',
priority,
groups: isSecurityIssue ? ['core-security'] : [],
is_security_issue: isSecurityIssue,
comment: descriptionStrings,
comment_tags: 'treeherder',
};
Expand Down Expand Up @@ -473,7 +473,7 @@ export class BugFilerClass extends React.Component {
submitFailure = (source, status, statusText, data) => {
const { notify } = this.props;

let failureString = `${source} returned status ${status}(${statusText})`;
let failureString = `${source} returned status ${status} (${statusText})`;
if (data && data.failure) {
failureString += `\n\n${data.failure}`;
}
Expand Down

0 comments on commit 8a9f7e1

Please sign in to comment.