WebKit Bugzilla
Attachment 342058 Details for
Bug 186291
: EWS for security bugs
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Work-in-progress - Second pass
WIP_060618.patch (text/plain), 46.60 KB, created by
Daniel Bates
on 2018-06-06 10:28:54 PDT
(
hide
)
Description:
Work-in-progress - Second pass
Filename:
MIME Type:
Creator:
Daniel Bates
Created:
2018-06-06 10:28:54 PDT
Size:
46.60 KB
patch
obsolete
>From 3a8baf1197960c98dc3d6fba62d39108f819e2db Mon Sep 17 00:00:00 2001 >From: Daniel Bates <dabates@apple.com> >Date: Tue, 5 Jun 2018 16:52:25 -0700 >Subject: [PATCH] Work-in-progress > >--- > .../handlers/fetchattachment.py | 33 ++++++++++ > .../handlers/fetchattachmentcontents.py | 33 ++++++++++ > .../handlers/releasepatch.py | 2 + > .../QueueStatusServer/handlers/submittoews.py | 2 +- > .../handlers/uploadattachment.py | 39 ++++++++++++ > Tools/QueueStatusServer/main.py | 6 ++ > .../QueueStatusServer/model/attachmentdata.py | 46 ++++++++++++++ > .../templates/uploadattachment.html | 6 ++ > .../common/net/bugzilla/attachment.py | 18 +++++- > .../net/bugzilla/attachment_unittest.py | 36 +++++++++++ > .../webkitpy/common/net/bugzilla/bug.py | 7 +++ > .../webkitpy/common/net/bugzilla/bugzilla.py | 56 ++++++++++++----- > .../common/net/bugzilla/bugzilla_mock.py | 62 +++++++++++++++---- > .../webkitpy/common/net/bugzilla/constants.py | 5 ++ > .../webkitpy/common/net/statusserver.py | 33 +++++++++- > .../webkitpy/common/net/statusserver_mock.py | 10 +++ > Tools/Scripts/webkitpy/tool/bot/feeders.py | 14 ++++- > .../tool/commands/earlywarningsystem.py | 12 +++- > .../tool/commands/queries_unittest.py | 3 +- > .../Scripts/webkitpy/tool/commands/queues.py | 8 ++- > .../webkitpy/tool/commands/queues_unittest.py | 7 ++- > .../webkitpy/tool/commands/upload_unittest.py | 24 +++++++ > .../webkitpy/tool/steps/submittoews.py | 5 ++ > 23 files changed, 429 insertions(+), 38 deletions(-) > create mode 100644 Tools/QueueStatusServer/handlers/fetchattachment.py > create mode 100644 Tools/QueueStatusServer/handlers/fetchattachmentcontents.py > create mode 100644 Tools/QueueStatusServer/handlers/uploadattachment.py > create mode 100644 Tools/QueueStatusServer/model/attachmentdata.py > create mode 100644 Tools/QueueStatusServer/templates/uploadattachment.html > create mode 100644 Tools/Scripts/webkitpy/common/net/bugzilla/attachment_unittest.py > create mode 100644 Tools/Scripts/webkitpy/common/net/bugzilla/constants.py > >diff --git a/Tools/QueueStatusServer/handlers/fetchattachment.py b/Tools/QueueStatusServer/handlers/fetchattachment.py >new file mode 100644 >index 00000000000..54e8cc68600 >--- /dev/null >+++ b/Tools/QueueStatusServer/handlers/fetchattachment.py >@@ -0,0 +1,33 @@ >+# Copyright (C) 2018 Apple Inc. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND >+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED >+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE >+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR >+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL >+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR >+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER >+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, >+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+from google.appengine.ext import webapp >+from model.attachmentdata import AttachmentData >+ >+ >+class FetchAttachment(webapp.RequestHandler): >+ def get(self, attachment_id): >+ attachment_data = AttachmentData.lookup_if_exists(attachment_id) >+ if not attachment_data: >+ self.error(404) >+ return >+ self.response.out.write(attachment_data.metadata) >diff --git a/Tools/QueueStatusServer/handlers/fetchattachmentcontents.py b/Tools/QueueStatusServer/handlers/fetchattachmentcontents.py >new file mode 100644 >index 00000000000..8ec7ac84c47 >--- /dev/null >+++ b/Tools/QueueStatusServer/handlers/fetchattachmentcontents.py >@@ -0,0 +1,33 @@ >+# Copyright (C) 2018 Apple Inc. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND >+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED >+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE >+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR >+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL >+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR >+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER >+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, >+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+from google.appengine.ext import webapp >+from model.attachmentdata import AttachmentData >+ >+ >+class FetchAttachmentContents(webapp.RequestHandler): >+ def get(self, attachment_id): >+ attachment_data = AttachmentData.lookup_if_exists(attachment_id) >+ if not attachment_data: >+ self.error(404) >+ return >+ self.response.out.write(attachment_data.data) >diff --git a/Tools/QueueStatusServer/handlers/releasepatch.py b/Tools/QueueStatusServer/handlers/releasepatch.py >index 3a36370a6a9..0a4c22fd9fe 100644 >--- a/Tools/QueueStatusServer/handlers/releasepatch.py >+++ b/Tools/QueueStatusServer/handlers/releasepatch.py >@@ -32,6 +32,7 @@ from google.appengine.ext.webapp import template > from handlers.updatebase import UpdateBase > from loggers.recordpatchevent import RecordPatchEvent > from model.attachment import Attachment >+from model.attachmentdata import AttachmentData > from model.queues import Queue > > >@@ -58,3 +59,4 @@ class ReleasePatch(UpdateBase): > RecordPatchEvent.stopped(attachment_id, queue_name, last_status.message) > > queue.active_work_items().expire_item(attachment_id) >+ AttachmentData.remove_attachment_data(attachment_id) >diff --git a/Tools/QueueStatusServer/handlers/submittoews.py b/Tools/QueueStatusServer/handlers/submittoews.py >index 5a79d0cbc04..850815eab97 100644 >--- a/Tools/QueueStatusServer/handlers/submittoews.py >+++ b/Tools/QueueStatusServer/handlers/submittoews.py >@@ -41,7 +41,7 @@ class SubmitToEWS(UpdateBase): > > def _should_add_to_ews_queue(self, queue, attachment): > # This assert() is here to make sure we're not submitting to the commit-queue. >- # The commit-queue clients check each patch anyway, but there is not sense >+ # The commit-queue clients check each patch anyway, but there is no sense > # in adding things to the commit-queue when they won't be processed by it. > assert(queue.is_ews()) > latest_status = attachment.status_for_queue(queue) >diff --git a/Tools/QueueStatusServer/handlers/uploadattachment.py b/Tools/QueueStatusServer/handlers/uploadattachment.py >new file mode 100644 >index 00000000000..1dd2931f859 >--- /dev/null >+++ b/Tools/QueueStatusServer/handlers/uploadattachment.py >@@ -0,0 +1,39 @@ >+# Copyright (C) 2018 Apple Inc. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND >+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED >+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE >+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR >+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL >+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR >+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER >+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, >+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+from google.appengine.ext import webapp, db >+from google.appengine.ext.webapp import template >+ >+from handlers.updatebase import UpdateBase >+from model.attachmentdata import AttachmentData >+ >+ >+class UploadAttachment(UpdateBase): >+ def get(self): >+ self.response.out.write(template.render("templates/uploadattachment.html", None)) >+ >+ def post(self): >+ attachment_id = self._int_from_request("attachment_id") >+ attachment_metadata = self.request.get("attachment_metadata") >+ attachment_data = self.request.get("attachment_data") >+ AttachmentData.add_attachment_data(attachment_id, str(attachment_metadata), str(attachment_data)) >+ self.response.out.write(attachment_id) >diff --git a/Tools/QueueStatusServer/main.py b/Tools/QueueStatusServer/main.py >index 085cdba37b6..d6b81945fa1 100644 >--- a/Tools/QueueStatusServer/main.py >+++ b/Tools/QueueStatusServer/main.py >@@ -35,6 +35,8 @@ from google.appengine.ext import webapp > from google.appengine.ext.webapp.util import run_wsgi_app > > from handlers.activebots import ActiveBots >+from handlers.fetchattachment import FetchAttachment >+from handlers.fetchattachmentcontents import FetchAttachmentContents > from handlers.gc import GC > from handlers.nextpatch import NextPatch > from handlers.patch import Patch >@@ -55,6 +57,7 @@ from handlers.syncqueuelogs import SyncQueueLogs > from handlers.updatestatus import UpdateStatus > from handlers.updatesvnrevision import UpdateSVNRevision > from handlers.updateworkitems import UpdateWorkItems >+from handlers.uploadattachment import UploadAttachment > > > webapp.template.register_template_library('filters.webkit_extras') >@@ -76,11 +79,14 @@ routes = [ > (r'/queue-status/(.*)', QueueStatus), > (r'/queue-status-json/(.*)', QueueStatusJSON), > (r'/next-patch/(.*)', NextPatch), >+ (r'/attachment/(.*)', FetchAttachment), >+ (r'/attachment-contents/(.*)', FetchAttachmentContents), > ('/release-patch', ReleasePatch), > ('/release-lock', ReleaseLock), > ('/update-status', UpdateStatus), > ('/update-work-items', UpdateWorkItems), > ('/update-svn-revision', UpdateSVNRevision), >+ ('/upload-attachment', UploadAttachment), > ('/active-bots', ActiveBots), > (r'/processing-times-json/(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)\-(\d+)', ProcessingTimesJSON), > ] >diff --git a/Tools/QueueStatusServer/model/attachmentdata.py b/Tools/QueueStatusServer/model/attachmentdata.py >new file mode 100644 >index 00000000000..9e3be487ed4 >--- /dev/null >+++ b/Tools/QueueStatusServer/model/attachmentdata.py >@@ -0,0 +1,46 @@ >+# Copyright (C) 2018 Apple Inc. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND >+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED >+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE >+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR >+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL >+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR >+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER >+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, >+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+from google.appengine.ext import db >+ >+ >+class AttachmentData(db.Model): >+ # FIXME: Use Blobstore API, <https://cloud.google.com/appengine/docs/standard/python/blobstore/>, as >+ # Blob properties are limited to 1MB in length. In practice, this should be sufficient for our purposes >+ # of storing security sensitive patches and their metadata. >+ attachment_id = db.IntegerProperty() >+ metadata = db.BlobProperty() >+ data = db.BlobProperty() >+ >+ @classmethod >+ def add_attachment_data(cls, attachment_id, metadata, data): >+ cls.get_or_insert(str(attachment_id), attachment_id=attachment_id, metadata=db.Blob(metadata), data=db.Blob(data)) >+ >+ @classmethod >+ def lookup_if_exists(cls, attachment_id): >+ return cls.get_by_key_name(str(attachment_id)) >+ >+ @classmethod >+ def remove_attachment_data(cls, attachment_id): >+ attachment_data = cls.lookup_if_exists(attachment_id) >+ if attachment_data: >+ attachment_data.delete() >diff --git a/Tools/QueueStatusServer/templates/uploadattachment.html b/Tools/QueueStatusServer/templates/uploadattachment.html >new file mode 100644 >index 00000000000..3f51c4e38cf >--- /dev/null >+++ b/Tools/QueueStatusServer/templates/uploadattachment.html >@@ -0,0 +1,6 @@ >+<form name="upload_attachment" enctype="multipart/form-data" method="POST"> >+Attachment id: <input name="attachment_id"><br> >+Metadata: <input type="file" name="attachment_metadata"><br> >+Data: <input type="file" name="attachment_data"><br> >+<input type="submit" value="Upload Attachment"> >+</form> >diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/attachment.py b/Tools/Scripts/webkitpy/common/net/bugzilla/attachment.py >index c749a1512d7..c3b532cc803 100644 >--- a/Tools/Scripts/webkitpy/common/net/bugzilla/attachment.py >+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/attachment.py >@@ -1,5 +1,5 @@ > # Copyright (c) 2009 Google Inc. All rights reserved. >-# Copyright (c) 2009 Apple Inc. All rights reserved. >+# Copyright (c) 2009, 2018 Apple Inc. All rights reserved. > # Copyright (c) 2010 Research In Motion Limited. All rights reserved. > # > # Redistribution and use in source and binary forms, with or without >@@ -28,9 +28,12 @@ > # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > >+import json > import logging > >+from datetime import datetime > from webkitpy.common.memoized import memoized >+from webkitpy.common.net.bugzilla.constants import BUGZILLA_DATE_FORMAT > > _log = logging.getLogger(__name__) > >@@ -119,3 +122,16 @@ class Attachment(object): > if not self._committer: > self._committer = self._validate_flag_value("committer") > return self._committer >+ >+ def to_json(self): >+ temp = dict(self._attachment_dictionary) >+ if 'attach_date' in temp: >+ temp['attach_date'] = temp['attach_date'].strftime(BUGZILLA_DATE_FORMAT) >+ return json.dumps(temp) >+ >+ @classmethod >+ def from_json(cls, json_string): >+ temp = json.loads(json_string) >+ if 'attach_date' in temp: >+ temp['attach_date'] = datetime.strptime(temp['attach_date'], BUGZILLA_DATE_FORMAT) >+ return Attachment(temp, None) >diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/attachment_unittest.py b/Tools/Scripts/webkitpy/common/net/bugzilla/attachment_unittest.py >new file mode 100644 >index 00000000000..8553bc6926b >--- /dev/null >+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/attachment_unittest.py >@@ -0,0 +1,36 @@ >+# Copyright (C) 2018 Apple Inc. All rights reserved. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND >+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED >+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE >+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR >+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL >+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR >+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER >+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, >+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+import unittest >+ >+from datetime import datetime >+from webkitpy.common.net.bugzilla.constants import BUGZILLA_DATE_FORMAT >+ >+from .attachment import Attachment >+ >+ >+class AttachmentTest(unittest.TestCase): >+ def test_convert_to_json_and_back(self): >+ bugzilla_formatted_date_string = datetime.today().strftime(BUGZILLA_DATE_FORMAT) >+ expected_date = datetime.strptime(bugzilla_formatted_date_string, BUGZILLA_DATE_FORMAT) >+ attachment = Attachment({'attach_date': expected_date}, None) >+ self.assertEqual(Attachment.from_json(attachment.to_json()).attach_date(), expected_date) >diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py >index 72344a3b4df..d83b09e5187 100644 >--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py >+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py >@@ -69,6 +69,13 @@ class Bug(object): > def status(self): > return self.bug_dictionary["bug_status"] > >+ def group(self): >+ return self.bug_dictionary.get('group', None) >+ >+ def is_security_sensitive(self): >+ group = self.group() >+ return bool(group and group == 'Security-Sensitive') >+ > # Bugzilla has many status states we don't really use in WebKit: > # https://bugs.webkit.org/page.cgi?id=fields.html#status > _open_states = ["UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED"] >diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py >index 510ec062423..4595e92b45d 100644 >--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py >+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py >@@ -1,5 +1,5 @@ > # Copyright (c) 2011 Google Inc. All rights reserved. >-# Copyright (c) 2009 Apple Inc. All rights reserved. >+# Copyright (c) 2009, 2018 Apple Inc. All rights reserved. > # Copyright (c) 2010 Research In Motion Limited. All rights reserved. > # Copyright (c) 2013 University of Szeged. All rights reserved. > # >@@ -45,6 +45,7 @@ from .bug import Bug > > from webkitpy.common.config import committers > import webkitpy.common.config.urls as config_urls >+from webkitpy.common.net.bugzilla.constants import BUGZILLA_DATE_FORMAT > from webkitpy.common.net.credentials import Credentials > from webkitpy.common.net.networktransaction import NetworkTransaction > from webkitpy.common.system.user import User >@@ -278,8 +279,10 @@ class BugzillaQueries(object): > > # NOTE: This is the only client of _fetch_attachment_ids_request_query > # This method only makes one request to bugzilla. >- def fetch_attachment_ids_from_review_queue(self, since=None): >+ def fetch_attachment_ids_from_review_queue(self, since=None, only_security_bugs=False): > review_queue_url = "request.cgi?action=queue&type=review&group=type" >+ if only_security_bugs: >+ review_queue_url += '&product=Security' > return self._fetch_attachment_ids_request_query(review_queue_url, since) > > # This only works if your account has edituser privileges. >@@ -399,12 +402,6 @@ class Bugzilla(object): > # convert from NavigableString to a real unicode() object using unicode(). > return unicode(soup.string) > >- # Example: 2010-01-20 14:31 PST >- # FIXME: Some bugzilla dates seem to have seconds in them? >- # Python does not support timezones out of the box. >- # Assume that bugzilla always uses PST (which is true for bugs.webkit.org) >- _bugzilla_date_format = "%Y-%m-%d %H:%M:%S" >- > @classmethod > def _parse_date(cls, date_string): > (date, time, time_zone) = date_string.split(" ") >@@ -413,7 +410,7 @@ class Bugzilla(object): > time += ':0' > # Ignore the timezone because python doesn't understand timezones out of the box. > date_string = "%s %s" % (date, time) >- return datetime.strptime(date_string, cls._bugzilla_date_format) >+ return datetime.strptime(date_string, BUGZILLA_DATE_FORMAT) > > def _date_contents(self, soup): > return self._parse_date(self._string_contents(soup)) >@@ -463,6 +460,9 @@ class Bugzilla(object): > bug["cc_emails"] = [self._string_contents(element) for element in soup.findAll('cc')] > bug["attachments"] = [self._parse_attachment_element(element, bug["id"]) for element in soup.findAll('attachment')] > bug["comments"] = [self._parse_log_descr_element(element) for element in soup.findAll('long_desc')] >+ group = soup.find('group') >+ if group: >+ bug['group'] = self._string_contents(group) > > return bug > >@@ -493,9 +493,12 @@ class Bugzilla(object): > self.authenticate() > return self.open_url(attachment_url).read() > >+ def _parse_bug_title_from_attachment_page(self, page): >+ return BeautifulSoup(page).find('div', attrs={'id': 'bug_title'}) >+ > def _parse_bug_id_from_attachment_page(self, page): > # The "Up" relation happens to point to the bug. >- title = BeautifulSoup(page).find('div', attrs={'id':'bug_title'}) >+ title = self._parse_bug_title_from_attachment_page(page) > if not title : > _log.warning("This attachment does not exist (or you don't have permissions to view it).") > return None >@@ -505,25 +508,46 @@ class Bugzilla(object): > return None > return int(match.group('bug_id')) > >- def bug_id_for_attachment_id(self, attachment_id): >- return NetworkTransaction().run(lambda: self.get_bug_id_for_attachment_id(attachment_id)) >+ def bug_id_for_attachment_id(self, attachment_id, throw_on_access_error=False): >+ return NetworkTransaction().run(lambda: self.get_bug_id_for_attachment_id(attachment_id, throw_on_access_error)) >+ >+ class AccessError(Exception): >+ NOT_PERMITTED = 1 << 0 >+ OTHER = 1 << 1 > >- def get_bug_id_for_attachment_id(self, attachment_id): >+ def __init__(self, attachment_id, error_code, bug_title): >+ super(Bugzilla.AccessError, self).__init__('Failed to access {}'.format(attachment_id)) >+ self.attachment_id = attachment_id >+ self.error_code = error_code >+ self.bug_title = bug_title >+ >+ def _parse_attachment_page_for_title_and_error(self, page): >+ title = self._parse_bug_title_from_attachment_page(page) >+ if title == 'Bug Access Denied': >+ return (title, Bugzilla.AccessError.NOT_PERMITTED) >+ return (title, Bugzilla.AccessError.OTHER) >+ >+ def get_bug_id_for_attachment_id(self, attachment_id, throw_on_access_error=False): > self.authenticate() > > attachment_url = self.attachment_url_for_id(attachment_id, 'edit') > _log.info("Fetching: %s" % attachment_url) > page = self.open_url(attachment_url) >- return self._parse_bug_id_from_attachment_page(page) >+ bug_id = self._parse_bug_id_from_attachment_page(page) >+ if bug_id: >+ return bug_id >+ if throw_on_access_error: >+ raise Bugzilla.AccessError(attachment_id, *self._parse_attachment_page_for_title_and_error(page)) >+ return None > > # FIXME: This should just return Attachment(id), which should be able to > # lazily fetch needed data. > >- def fetch_attachment(self, attachment_id): >+ def fetch_attachment(self, attachment_id, throw_on_access_error=False): > # We could grab all the attachment details off of the attachment edit > # page but we already have working code to do so off of the bugs page, > # so re-use that. >- bug_id = self.bug_id_for_attachment_id(attachment_id) >+ bug_id = self.bug_id_for_attachment_id(attachment_id, throw_on_access_error) > if not bug_id: > _log.warning("Unable to parse bug_id from attachment {}".format(attachment_id)) > return None >diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla_mock.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla_mock.py >index d117ff8e3f8..9498542350f 100644 >--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla_mock.py >+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla_mock.py >@@ -1,4 +1,5 @@ > # Copyright (C) 2011 Google Inc. All rights reserved. >+# Copyright (C) 2018 Apple Inc. All rights reserved. > # > # Redistribution and use in source and binary forms, with or without > # modification, are permitted provided that the following conditions are >@@ -30,6 +31,7 @@ import datetime > import logging > > from .bug import Bug >+from .bugzilla import Bugzilla > from .attachment import Attachment > from webkitpy.common.config.committers import CommitterList, Reviewer > >@@ -151,6 +153,19 @@ _patch8 = { # Resolved bug, without review flag, not marked obsolete (maybe alr > "attacher_email": "eric@webkit.org", > } > >+_patch9 = { >+ 'id': 10008, >+ 'bug_id': 50007, >+ 'url': 'http://example.com/10008', >+ 'name': 'Patch9', >+ 'is_obsolete': False, >+ 'is_patch': True, >+ 'review': '?', >+ 'commit-queue': '-', >+ 'attacher_email': 'dbates@webkit.org', >+ 'attach_date': datetime.datetime.today(), >+} >+ > # This matches one of Bug.unassigned_emails > _unassigned_email = "webkit-unassigned@lists.webkit.org" > # This is needed for the FlakyTestReporter to believe the bug >@@ -278,6 +293,23 @@ _bug7 = { > } > > >+_bug8 = { >+ 'id': 50007, >+ 'title': 'Security bug with a patch needing review.', >+ 'reporter_email': 'dbates@webkit.org', >+ 'assigned_to_email': 'foo@foo.com', >+ 'cc_emails': [], >+ 'attachments': [_patch9], >+ 'bug_status': 'ASSIGNED', >+ 'group': 'Security-Sensitive', >+ 'comments': [{'comment_date': datetime.datetime(2011, 6, 11, 9, 4, 3), >+ 'comment_email': 'bar@foo.com', >+ 'text': 'Message1.\nCommitted r35: <https://trac.webkit.org/changeset/35>', >+ }, >+ ], >+} >+ >+ > class MockBugzillaQueries(object): > > def __init__(self, bugzilla): >@@ -293,12 +325,14 @@ class MockBugzillaQueries(object): > self._all_bugs()) > return map(lambda bug: bug.id(), bugs_with_commit_queued_patches) > >- def fetch_attachment_ids_from_review_queue(self, since=None): >+ def fetch_attachment_ids_from_review_queue(self, since=None, only_security_bugs=False): > unreviewed_patches = sum([bug.unreviewed_patches() > for bug in self._all_bugs()], []) > if since: > unreviewed_patches = [patch for patch in unreviewed_patches > if patch.attach_date() >= since] >+ if only_security_bugs: >+ unreviewed_patches = filter(lambda patch: patch.bug().is_security_sensitive(), unreviewed_patches) > return map(lambda patch: patch.id(), unreviewed_patches) > > def fetch_patches_from_commit_queue(self): >@@ -344,16 +378,10 @@ class MockBugzilla(object): > > bug_server_url = "http://example.com" > >- bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4, _bug5, _bug6, _bug7) >+ bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4, _bug5, _bug6, _bug7, _bug8) > >- attachment_cache = _id_to_object_dictionary(_patch1, >- _patch2, >- _patch3, >- _patch4, >- _patch5, >- _patch6, >- _patch7, >- _patch8) >+ attachment_cache = _id_to_object_dictionary(_patch1, _patch2, _patch3, _patch4, _patch5, _patch6, >+ _patch7, _patch8, _patch9) > > def __init__(self): > self.queries = MockBugzillaQueries(self) >@@ -395,19 +423,24 @@ class MockBugzilla(object): > def set_override_patch(self, patch): > self._override_patch = patch > >- def fetch_attachment(self, attachment_id): >+ def fetch_attachment(self, attachment_id, throw_on_access_error=False): > if self._override_patch: > return self._override_patch > >- attachment_dictionary = self.attachment_cache.get(attachment_id) >+ attachment_dictionary = self.attachment_cache.get(int(attachment_id)) > if not attachment_dictionary: > print("MOCK: fetch_attachment: %s is not a known attachment id" % attachment_id) > return None > bug = self.fetch_bug(attachment_dictionary["bug_id"]) >+ if bug.is_security_sensitive() and throw_on_access_error: >+ raise Bugzilla.AccessError(attachment_id, Bugzilla.AccessError.NOT_PERMITTED, 'Bug Access Denied') > for attachment in bug.attachments(include_obsolete=True): > if attachment.id() == int(attachment_id): > return attachment > >+ def fetch_attachment_contents(self, attachment_id): >+ return 'Patch' >+ > def bug_url_for_bug_id(self, bug_id): > return "%s/%s" % (self.bug_server_url, bug_id) > >@@ -461,6 +494,11 @@ class MockBugzilla(object): > _log.info("-- Begin comment --") > _log.info(comment_text) > _log.info("-- End comment --") >+ bug = self.fetch_bug(bug_id) >+ if bug.bug_dictionary: >+ patches = bug.patches() >+ if len(patches) == 1: >+ return patches[0].id() > return '10001' > > def add_cc_to_bug(self, bug_id, ccs): >diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/constants.py b/Tools/Scripts/webkitpy/common/net/bugzilla/constants.py >new file mode 100644 >index 00000000000..5a8e57c19f9 >--- /dev/null >+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/constants.py >@@ -0,0 +1,5 @@ >+# Example: 2010-01-20 14:31 PST >+# FIXME: Some bugzilla dates seem to have seconds in them? >+# Python does not support timezones out of the box. >+# Assume that bugzilla always uses PST (which is true for bugs.webkit.org) >+BUGZILLA_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" >diff --git a/Tools/Scripts/webkitpy/common/net/statusserver.py b/Tools/Scripts/webkitpy/common/net/statusserver.py >index c07ab65e3e3..af4d2974161 100644 >--- a/Tools/Scripts/webkitpy/common/net/statusserver.py >+++ b/Tools/Scripts/webkitpy/common/net/statusserver.py >@@ -1,4 +1,5 @@ > # Copyright (C) 2009 Google Inc. All rights reserved. >+# Copyright (C) 2018 Apple Inc. All rights reserved. > # > # Redistribution and use in source and binary forms, with or without > # modification, are permitted provided that the following conditions are >@@ -28,10 +29,12 @@ > # > # This the client designed to talk to Tools/QueueStatusServer. > >+from webkitpy.common.config.urls import statusserver_default_host >+from webkitpy.common.net.bugzilla.attachment import Attachment > from webkitpy.common.net.networktransaction import NetworkTransaction > from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup >-from webkitpy.common.config.urls import statusserver_default_host > >+import StringIO > import logging > import urllib2 > >@@ -111,6 +114,21 @@ class StatusServer: > self._browser["high_priority_work_items"] = " ".join(high_priority_work_items) > return self._browser.submit().read() > >+ def _upload_attachment_to_server(self, attachment_id, attachment_metadata, attachment_data): >+ upload_attachment_url = '{}/upload-attachment'.format(self.url) >+ self._browser.open(upload_attachment_url) >+ self._browser.select_form(name='upload_attachment') >+ self._browser['attachment_id'] = unicode(attachment_id) >+ self._browser.add_file(StringIO.StringIO(unicode(attachment_metadata)), 'application/json', 'attachment-{}-metadata.json'.format(attachment_id), 'attachment_metadata') >+ if isinstance(attachment_data, unicode): >+ attachment_data = attachment_data.encode('utf-8') >+ self._browser.add_file(StringIO.StringIO(attachment_data), 'text/plain', 'attachment-{}.patch'.format(attachment_id), 'attachment_data') >+ self._browser.submit() >+ >+ def upload_attachment(self, attachment): >+ _log.info('Uploading attachment {} to status server'.format(attachment.id())) >+ return NetworkTransaction().run(lambda: self._upload_attachment_to_server(attachment.id(), attachment.to_json(), attachment.contents())) >+ > def _post_work_item_to_ews(self, attachment_id): > submit_to_ews_url = "%s/submit-to-ews" % self.url > self._browser.open(submit_to_ews_url) >@@ -163,6 +181,19 @@ class StatusServer: > _log.info("SVN revision: %s broke %s" % (svn_revision_number, broken_bot)) > return NetworkTransaction().run(lambda: self._post_svn_revision_to_server(svn_revision_number, broken_bot)) > >+ def _fetch_attachment_contents(self, attachment_id): >+ attachment_contents_url = '{}/attachment-contents/{}'.format(self.url, attachment_id) >+ return self._fetch_url(attachment_contents_url) >+ >+ def _fetch_attachment_metadata(self, attachment_id): >+ attachment_url = '{}/attachment/{}'.format(self.url, attachment_id) >+ return self._fetch_url(attachment_url) >+ >+ def fetch_attachment(self, attachment_id): >+ attachment = Attachment.from_json(self._fetch_attachment_metadata(attachment_id)) >+ attachment.contents = lambda: self._fetch_attachment_contents(attachment_id) >+ return attachment >+ > def _fetch_url(self, url): > # FIXME: This should use NetworkTransaction's 404 handling instead. > try: >diff --git a/Tools/Scripts/webkitpy/common/net/statusserver_mock.py b/Tools/Scripts/webkitpy/common/net/statusserver_mock.py >index 0fdd0ebe4f6..4abf86c4ff1 100644 >--- a/Tools/Scripts/webkitpy/common/net/statusserver_mock.py >+++ b/Tools/Scripts/webkitpy/common/net/statusserver_mock.py >@@ -28,6 +28,8 @@ > > import logging > >+from webkitpy.common.net.bugzilla.attachment import Attachment >+ > _log = logging.getLogger(__name__) > > >@@ -59,6 +61,9 @@ class MockStatusServer(object): > self._work_items = work_items > _log.info("MOCK: update_work_items: %s %s" % (queue_name, high_priority_work_items + work_items)) > >+ def upload_attachment(self, attachment): >+ _log.info('MOCK: upload_attachment: {}'.format(attachment.id())) >+ > def submit_to_ews(self, patch_id): > _log.info("MOCK: submit_to_ews: %s" % (patch_id)) > >@@ -71,3 +76,8 @@ class MockStatusServer(object): > > def results_url_for_status(self, status_id): > return "http://dummy_url" >+ >+ def fetch_attachment(self, attachment_id): >+ attachment = Attachment({'id': 10008}, None) >+ attachment.content = lambda: '' >+ return attachment >diff --git a/Tools/Scripts/webkitpy/tool/bot/feeders.py b/Tools/Scripts/webkitpy/tool/bot/feeders.py >index 7d673a2760e..f4453978b84 100644 >--- a/Tools/Scripts/webkitpy/tool/bot/feeders.py >+++ b/Tools/Scripts/webkitpy/tool/bot/feeders.py >@@ -1,4 +1,5 @@ > # Copyright (c) 2010 Google Inc. All rights reserved. >+# Copyright (C) 2018 Apple Inc. All rights reserved. > # > # Redistribution and use in source and binary forms, with or without > # modification, are permitted provided that the following conditions are >@@ -84,9 +85,20 @@ class EWSFeeder(AbstractFeeder): > AbstractFeeder.__init__(self, tool) > > def feed(self): >- ids_needing_review = set(self._tool.bugs.queries.fetch_attachment_ids_from_review_queue(datetime.today() - timedelta(7))) >+ current_time = datetime.today() >+ ids_needing_review = set(self._tool.bugs.queries.fetch_attachment_ids_from_review_queue(current_time - timedelta(7))) >+ security_ids_needing_review = frozenset(self._tool.bugs.queries.fetch_attachment_ids_from_review_queue(current_time - timedelta(7), only_security_bugs=True)) > new_ids = ids_needing_review.difference(self._ids_sent_to_server) > _log.info("Feeding EWS (%s, %s new)" % (pluralize(len(ids_needing_review), "r? patch"), len(new_ids))) > for attachment_id in new_ids: # Order doesn't really matter for the EWS. >+ # Upload patches for security bugs to the status server since the EWS queue machines may not >+ # have permission to fetch them directly from Bugzilla. >+ attachment_data = None >+ if attachment_id in security_ids_needing_review: >+ attachment = self._tool.bugs.fetch_attachment(attachment_id) >+ if not attachment: >+ _log.error('Failed to retrieve attachment {}'.format(attachment_id)) >+ continue >+ self._tool.status_server.upload_attachment(attachment) > self._tool.status_server.submit_to_ews(attachment_id) > self._ids_sent_to_server.add(attachment_id) >diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >index d8c5a93b5f4..ca07f991fb2 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py >@@ -34,6 +34,7 @@ from optparse import make_option > > from webkitpy.common.config.committers import CommitterList > from webkitpy.common.config.ports import DeprecatedPort >+from webkitpy.common.net.bugzilla import Bugzilla > from webkitpy.common.system.filesystem import FileSystem > from webkitpy.common.system.executive import ScriptError > from webkitpy.tool.bot.earlywarningsystemtask import EarlyWarningSystemTask, EarlyWarningSystemTaskDelegate >@@ -150,7 +151,16 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele > return self._group > > def refetch_patch(self, patch): >- return self._tool.bugs.fetch_attachment(patch.id()) >+ patch_id = patch.id() >+ try: >+ patch = self._tool.bugs.fetch_attachment(patch_id, throw_on_access_error=True) >+ except Bugzilla.AccessError as e: >+ # FIXME: Need a way to ask the status server to fetch the patch again. For now >+ # we return the attachment as it was when it was originally uploaded to the >+ # status server. >+ if e.error_code == Bugzilla.AccessError.NOT_PERMITTED: >+ patch = self._tool.status_server.fetch_attachment(patch_id) >+ return patch > > def report_flaky_tests(self, patch, flaky_test_results, results_archive): > pass >diff --git a/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py b/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py >index 42eef66ef9e..82254a32bc3 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/queries_unittest.py >@@ -118,7 +118,8 @@ class QueryCommandsTest(CommandsTest): > "Bugs with attachments pending review:\n" \ > "http://webkit.org/b/bugid Description (age in days)\n" \ > "http://webkit.org/b/50001 Bug with a patch needing review. (0)\n" \ >- "Total: 1\n" >+ "http://webkit.org/b/50007 Security bug with a patch needing review. (0)\n" \ >+ "Total: 2\n" > self.assert_execute_outputs(PatchesToReview(), None, expected_stdout, expected_stderr, options=options) > > options.cc_email = None >diff --git a/Tools/Scripts/webkitpy/tool/commands/queues.py b/Tools/Scripts/webkitpy/tool/commands/queues.py >index 77c2d8827bc..21fdf5e273d 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/queues.py >+++ b/Tools/Scripts/webkitpy/tool/commands/queues.py >@@ -40,7 +40,7 @@ from StringIO import StringIO > > from webkitpy.common.config.committervalidator import CommitterValidator > from webkitpy.common.config.ports import DeprecatedPort >-from webkitpy.common.net.bugzilla import Attachment >+from webkitpy.common.net.bugzilla import Bugzilla, Attachment > from webkitpy.common.system.executive import ScriptError > from webkitpy.tool.bot.botinfo import BotInfo > from webkitpy.tool.bot.commitqueuetask import CommitQueueTask, CommitQueueTaskDelegate >@@ -217,7 +217,11 @@ class AbstractPatchQueue(AbstractQueue): > patch_id = self._tool.status_server.next_work_item(self.name) > if not patch_id: > return None >- patch = self._tool.bugs.fetch_attachment(patch_id) >+ try: >+ patch = self._tool.bugs.fetch_attachment(patch_id, throw_on_access_error=True) >+ except Bugzilla.AccessError as e: >+ if e.error_code == Bugzilla.AccessError.NOT_PERMITTED: >+ patch = self._tool.status_server.fetch_attachment(patch_id) > if not patch: > # FIXME: Using a fake patch because release_work_item has the wrong API. > # We also don't really need to release the lock (although that's fine), >diff --git a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py >index 6d722e8b9a5..e0b7550d6d3 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py >@@ -143,7 +143,9 @@ MOCK setting flag 'commit-queue' to '-' on attachment '10001' with comment 'Reje > - If you have committer rights please correct the error in Tools/Scripts/webkitpy/common/config/contributors.json by adding yourself to the file (no review needed). The commit-queue restarts itself every 2 hours. After restart the commit-queue will correctly respect your committer rights.' > Feeding commit-queue high priority items [10005], regular items [10000] > MOCK: update_work_items: commit-queue [10005, 10000] >-Feeding EWS (1 r? patch, 1 new) >+Feeding EWS (2 r? patches, 2 new) >+MOCK: upload_attachment: 10008 >+MOCK: submit_to_ews: 10008 > MOCK: submit_to_ews: 10002 > """, > "handle_unexpected_error": "Mock error message\n", >@@ -159,13 +161,14 @@ class AbstractPatchQueueTest(CommandsTest): > queue._options = Mock() > queue._options.port = None > self.assertIsNone(queue._next_patch()) >- tool.status_server = MockStatusServer(work_items=[2, 10000, 10001]) >+ tool.status_server = MockStatusServer(work_items=[2, 10000, 10001, 10008]) > expected_stdout = "MOCK: fetch_attachment: 2 is not a known attachment id\n" # A mock-only message to prevent us from making mistakes. > expected_logs = "MOCK: release_work_item: None 2\n" > patch = OutputCapture().assert_outputs(self, queue._next_patch, expected_stdout=expected_stdout, expected_logs=expected_logs) > # The patch.id() == 2 is ignored because it doesn't exist. > self.assertEqual(patch.id(), 10000) > self.assertEqual(queue._next_patch().id(), 10001) >+ self.assertEqual(queue._next_patch().id(), 10008) > self.assertEqual(queue._next_patch(), None) # When the queue is empty > > >diff --git a/Tools/Scripts/webkitpy/tool/commands/upload_unittest.py b/Tools/Scripts/webkitpy/tool/commands/upload_unittest.py >index e2777c49e30..b51d3e409fb 100644 >--- a/Tools/Scripts/webkitpy/tool/commands/upload_unittest.py >+++ b/Tools/Scripts/webkitpy/tool/commands/upload_unittest.py >@@ -1,4 +1,5 @@ > # Copyright (C) 2009 Google Inc. All rights reserved. >+# Copyright (C) 2018 Apple Inc. All rights reserved. > # > # Redistribution and use in source and binary forms, with or without > # modification, are permitted provided that the following conditions are >@@ -159,6 +160,29 @@ MOCK: submit_to_ews: 10001 > """ > self.assert_execute_outputs(Upload(), [50000], options=options, expected_logs=expected_logs) > >+ def test_upload_of_security_sensitive_patch_with_no_review_and_ews(self): >+ options = MockOptions() >+ options.cc = None >+ options.check_style = True >+ options.check_style_filter = None >+ options.comment = None >+ options.description = 'MOCK description' >+ options.non_interactive = False >+ options.request_commit = False >+ options.review = False >+ options.ews = True >+ options.sort_xcode_project = False >+ options.suggest_reviewers = False >+ expected_logs = """MOCK: user.open_url: file://... >+Was that diff correct? >+Obsoleting 1 old patch on bug 50007 >+MOCK add_patch_to_bug: bug_id=50007, description=MOCK description, mark_for_review=False, mark_for_commit_queue=False, mark_for_landing=False >+MOCK: user.open_url: http://example.com/50007 >+MOCK: upload_attachment: 10008 >+MOCK: submit_to_ews: 10008 >+""" >+ self.assert_execute_outputs(Upload(), [50007], options=options, expected_logs=expected_logs) >+ > def test_mark_bug_fixed(self): > tool = MockTool() > tool._scm.last_svn_commit_log = lambda: "r9876 |" >diff --git a/Tools/Scripts/webkitpy/tool/steps/submittoews.py b/Tools/Scripts/webkitpy/tool/steps/submittoews.py >index 1eade14c22d..c2ebd8dde53 100644 >--- a/Tools/Scripts/webkitpy/tool/steps/submittoews.py >+++ b/Tools/Scripts/webkitpy/tool/steps/submittoews.py >@@ -35,4 +35,9 @@ class SubmitToEWS(AbstractStep): > > def run(self, state): > for attachment_id in state.get('attachment_ids', []): >+ attachment = self._tool.bugs.fetch_attachment(attachment_id) >+ if attachment is None: >+ continue # Either Bugzilla is down or we do not have permission to view the attachment. >+ if attachment.bug().is_security_sensitive(): >+ self._tool.status_server.upload_attachment(attachment) > self._tool.status_server.submit_to_ews(attachment_id) >-- >2.17.0 (Apple Git-105) >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 186291
:
341938
|
341939
|
342058
|
342741
|
342744
|
342765
|
342766
|
342767
|
342787
|
342789
|
342819
|
342836
|
342844
|
342858
|
342892
|
342907
|
343010