12018-06-15 Daniel Bates <dabates@apple.com>
2
3 EWS for security bugs
4 https://bugs.webkit.org/show_bug.cgi?id=186291
5 <rdar://problem/40829658>
6
7 Reviewed by NOBODY (OOPS!).
8
9 Part 1 of 2.
10
11 Implements support for EWS processing of patches on security sensitive bugs. We add new
12 endpoints to the status server to support uploading and downloading of patches and associated
13 metadata. When webkit-patch submits a patch for EWS processing it will now upload the contents
14 and metadata for the patch to the status server if the patch is on a security sensitive bug.
15 We teach the EWS machinery in webkitpy to query the status server for a patch only if fetching
16 the patch from Bugzilla is not permitted due to an authorization error.
17
18 Fetching patches from the status server requires an API key. The API key is read from the
19 environment variable WEBKIT_STATUS_API_KEY or the value of the Git configuration key webkit.status_api_key
20 (in that order). Contact me or another Apple engineer for an API key.
21
22 Additionally, expose an optional command line option called --status-host-uses-https to
23 query the status server over HTTPS as opposed to HTTP.
24
25 * QueueStatusServer/config/authorization.py: Added.
26 (_path_to_authorized_api_keys_file): Returns the absolute filesystem path to the file authorized_api_keys.txt.
27 (_parse_authorized_api_keys):
28 (authorized_api_keys):
29 (_parse_authorization_header): Parses the API key from the Authorization header. We use a
30 custom authentication scheme: "APIKey". See remark below for more details.
31 (is_authorized): Checks if the request includes an API key and whether that API key is in the
32 list of authorized keys (performs a case-sensitive match). The API key may be specified either
33 in a HTTP header Authorization or in the query string argument "api-key". When using the HTTP
34 headers approach the Authorization header should have the form: "Authorization: APIKey X" where
35 X is the case-sensitive API key.
36 * QueueStatusServer/handlers/fetchattachment.py: Added.
37 (FetchAttachment):
38 (FetchAttachment.get):
39 * QueueStatusServer/handlers/releasepatch.py:
40 (ReleasePatch.check_complete): Returns whether the specified attachment was processed by all the queues.
41 (ReleasePatch.post): Delete the patch from AppEngine (if we have it) once the patch was processed
42 by all the queues.
43 * QueueStatusServer/handlers/submittoews.py:
44 (SubmitToEWS._should_add_to_ews_queue): Fix a typo in a comment while I am working in this code.
45 * QueueStatusServer/handlers/uploadattachment.py: Added.
46 (UploadAttachment):
47 (UploadAttachment.get):
48 (UploadAttachment.post):
49 * QueueStatusServer/main.py: Add new routes /upload-attachment and /attachment to upload an attachment
50 and view an attachment (or its metadata), respectively.
51 * QueueStatusServer/model/attachmentdata.py: Added.
52 (AttachmentData):
53 (AttachmentData.add_attachment_data):
54 (AttachmentData.lookup_if_exists):
55 (AttachmentData.remove_attachment_data):
56 * QueueStatusServer/templates/uploadattachment.html: Added.
57 * Scripts/webkitpy/common/net/bugzilla/attachment.py:
58 (Attachment.committer):
59 (Attachment):
60 (Attachment.to_json): Serialize to JSON so that we can upload it to AppEngine.
61 (Attachment.from_json): Deserialize from JSON. This is used as part of downloading a patch from AppEngine.
62 * Scripts/webkitpy/common/net/bugzilla/attachment_unittest.py: Added.
63 (AttachmentTest):
64 (AttachmentTest.test_convert_to_json_and_back):
65 * Scripts/webkitpy/common/net/bugzilla/bug.py:
66 (Bug.group): Returns the group that bug is in or the empty string.
67 (Bug.is_security_sensitive): Returns whether the bug is in group Security-Sensitive.
68 * Scripts/webkitpy/common/net/bugzilla/bugzilla.py:
69 (BugzillaQueries.fetch_attachment_ids_from_review_queue): Modified to take an optional boolean, only_security_bugs,
70 as to whether to only fetch attachment ids for unreviewed patches associated with security bugs. By default, we
71 keep the current behavior and query for the attachment ids of all unreviewed patches that the currently logged in
72 Bugzilla user can see, which may include patches associated with security bugs.
73 (Bugzilla._parse_date): Update for moved and renamed constant. See remark for class Bugzilla.
74 (Bugzilla._parse_bug_dictionary_from_xml): Modified to return an empty dictionary if we do not have access to view the bug.
75 Otherwise, extract the name of the group the bug is in.
76 (Bugzilla.fetch_bug): Modified to return None if we do not have access to view the bug.
77 (Bugzilla._parse_bug_title_from_attachment_page): Extracted out logic to parse the title of the Attachment page
78 from _parse_bug_id_from_attachment_page() so that it can be used from both _parse_bug_id_from_attachment_page()
79 and get_bug_id_for_attachment_id().
80 (Bugzilla): Moved class constant _bugzilla_date_format to Scripts/webkitpy/common/net/bugzilla/constants.py
81 and renamed it to BUGZILLA_DATE_FORMAT.
82 (Bugzilla.AccessError):
83 (Bugzilla.AccessError.__init__):
84 (Bugzilla._parse_bug_id_from_attachment_page): Modified to return a tuple of ("bug id", "error code") so that
85 the caller can know the reason the parse failed if it did. The parse will fail if we do not have access to view
86 the bug.
87 (Bugzilla.bug_id_for_attachment_id): Modified to take a boolean throw_on_access_error (default: False)
88 as to whether to raise a Bugzilla.AccessError exception and pass it through to get_bug_id_for_attachment_id().
89 (Bugzilla.get_bug_id_for_attachment_id): Modified to take a boolean throw_on_access_error (default: False)
90 as to whether to raise a Bugzilla.AccessError exception if we do not have access to the bug associated with
91 the specified attachment id.
92 (Bugzilla.fetch_attachment):
93 * Scripts/webkitpy/common/net/bugzilla/bugzilla_mock.py:
94 (MockBugzillaQueries.fetch_attachment_ids_from_review_queue):
95 (MockBugzilla):
96 (MockBugzilla.fetch_attachment):
97 (MockBugzilla.fetch_attachment_contents):
98 (MockBugzilla.add_patch_to_bug):
99 * Scripts/webkitpy/common/net/bugzilla/bugzilla_unittest.py:
100 * Scripts/webkitpy/common/net/bugzilla/constants.py: Added.
101 * Scripts/webkitpy/common/net/statusserver.py:
102 (StatusServer.set_host): Modified to take an boolean use_https as to whether to query the server using
103 HTTPS (default: False - use HTTP; our current behavior).
104 (StatusServer.set_api_key): Added.
105 (StatusServer._upload_attachment_to_server): Added.
106 (StatusServer.upload_attachment): Added.
107 (StatusServer._fetch_attachment_page): Added.
108 (StatusServer.fetch_attachment): Added.
109 * Scripts/webkitpy/common/net/statusserver_mock.py:
110 (MockStatusServer.upload_attachment): Added.
111 (MockStatusServer.fetch_attachment): Added.
112 * Scripts/webkitpy/tool/bot/feeders.py:
113 (EWSFeeder.feed): Modified to download patches on security bugs and upload them to the status server (AppEngine).
114 * Scripts/webkitpy/tool/commands/download.py:
115 (ProcessAttachmentsMixin._fetch_list_of_patches_to_process): Modified to handle the case when fetching the
116 bug details from Bugzilla fail, say because we are not allowed to the view the bug.
117 (ProcessBugsMixin._fetch_list_of_patches_to_process): Filter out None values for attachments that we failed
118 to fetch, say because we are not allowed to the view the bug the attachment is on.
119 * Scripts/webkitpy/tool/commands/earlywarningsystem.py:
120 (AbstractEarlyWarningSystem.refetch_patch): For now, refetch the patch from the status server. Ideally, we
121 need a way to ask the status server to fetch the patch again from Bugzilla (or at least its metadata) so
122 that the EWS can check the current state of the patch (i.e. is it still marked r?).
123 * Scripts/webkitpy/tool/commands/queries_unittest.py:
124 (QueryCommandsTest.test_patches_to_review): Update expected result.
125 * Scripts/webkitpy/tool/commands/queues.py:
126 (AbstractPatchQueue._next_patch): Fetch the patch from the status server if we failed to fetch it from
127 Bugzilla because we do not have permission to view it.
128 * Scripts/webkitpy/tool/commands/queues_unittest.py:
129 * Scripts/webkitpy/tool/commands/upload_unittest.py:
130 (test_upload_of_security_sensitive_patch_with_no_review_and_ews): Added.
131 * Scripts/webkitpy/tool/main.py:
132 (WebKitPatch):
133 (WebKitPatch._status_server_api_key_from_git): Read the API key from the Git configuration key webkit.status_api_key.
134 (WebKitPatch._status_server_api_key): Read the API key from the environment variable WEBKIT_STATUS_API_KEY.
135 (WebKitPatch.handle_global_options): Read the API key and update the state of the StatusServer object, if applicable.
136 * Scripts/webkitpy/tool/steps/obsoletepatches.py:
137 (ObsoletePatches.run): Modified to handle the case when fetching the bug details from Bugzilla fail, say because we
138 are not allowed to the view the bug.
139 * Scripts/webkitpy/tool/steps/submittoews.py:
140 (SubmitToEWS.run): Upload the contents of the patch and the Bugzilla metadata about it to the status server
141 if the patch was posted to a security bug.
142