22 |
23 |
23 class NetworkUrlInterceptor(QWebEngineUrlRequestInterceptor): |
24 class NetworkUrlInterceptor(QWebEngineUrlRequestInterceptor): |
24 """ |
25 """ |
25 Class implementing an URL request handler. |
26 Class implementing an URL request handler. |
26 """ |
27 """ |
|
28 |
27 def __init__(self, parent=None): |
29 def __init__(self, parent=None): |
28 """ |
30 """ |
29 Constructor |
31 Constructor |
30 |
32 |
31 @param parent reference to the parent object |
33 @param parent reference to the parent object |
32 @type QObject |
34 @type QObject |
33 """ |
35 """ |
34 super().__init__(parent) |
36 super().__init__(parent) |
35 |
37 |
36 self.__interceptors = [] |
38 self.__interceptors = [] |
37 self.__mutex = QMutex() |
39 self.__mutex = QMutex() |
38 |
40 |
39 self.__loadSettings() |
41 self.__loadSettings() |
40 |
42 |
41 def interceptRequest(self, info): |
43 def interceptRequest(self, info): |
42 """ |
44 """ |
43 Public method handling an URL request. |
45 Public method handling an URL request. |
44 |
46 |
45 @param info URL request information |
47 @param info URL request information |
46 @type QWebEngineUrlRequestInfo |
48 @type QWebEngineUrlRequestInfo |
47 """ |
49 """ |
48 with EricMutexLocker(self.__mutex): |
50 with EricMutexLocker(self.__mutex): |
49 # Do Not Track feature |
51 # Do Not Track feature |
50 if self.__doNotTrack: |
52 if self.__doNotTrack: |
51 info.setHttpHeader(b"DNT", b"1") |
53 info.setHttpHeader(b"DNT", b"1") |
52 info.setHttpHeader(b"X-Do-Not-Track", b"1") |
54 info.setHttpHeader(b"X-Do-Not-Track", b"1") |
53 |
55 |
54 # Send referrer header? |
56 # Send referrer header? |
55 if info.requestUrl().host() not in Preferences.getWebBrowser( |
57 if info.requestUrl().host() not in Preferences.getWebBrowser( |
56 "SendRefererWhitelist"): |
58 "SendRefererWhitelist" |
|
59 ): |
57 self.__setRefererHeader(info) |
60 self.__setRefererHeader(info) |
58 |
61 |
59 # User Agents header |
62 # User Agents header |
60 userAgent = WebBrowserPage.userAgentForUrl(info.requestUrl()) |
63 userAgent = WebBrowserPage.userAgentForUrl(info.requestUrl()) |
61 info.setHttpHeader(b"User-Agent", userAgent.encode()) |
64 info.setHttpHeader(b"User-Agent", userAgent.encode()) |
62 |
65 |
63 for interceptor in self.__interceptors: |
66 for interceptor in self.__interceptors: |
64 interceptor.interceptRequest(info) |
67 interceptor.interceptRequest(info) |
65 |
68 |
66 def installUrlInterceptor(self, interceptor): |
69 def installUrlInterceptor(self, interceptor): |
67 """ |
70 """ |
68 Public method to install an URL interceptor. |
71 Public method to install an URL interceptor. |
69 |
72 |
70 @param interceptor URL interceptor to be installed |
73 @param interceptor URL interceptor to be installed |
71 @type UrlInterceptor |
74 @type UrlInterceptor |
72 """ |
75 """ |
73 with EricMutexLocker(self.__mutex): |
76 with EricMutexLocker(self.__mutex): |
74 if interceptor not in self.__interceptors: |
77 if interceptor not in self.__interceptors: |
75 self.__interceptors.append(interceptor) |
78 self.__interceptors.append(interceptor) |
76 |
79 |
77 def removeUrlInterceptor(self, interceptor): |
80 def removeUrlInterceptor(self, interceptor): |
78 """ |
81 """ |
79 Public method to remove an URL interceptor. |
82 Public method to remove an URL interceptor. |
80 |
83 |
81 @param interceptor URL interceptor to be removed |
84 @param interceptor URL interceptor to be removed |
82 @type UrlInterceptor |
85 @type UrlInterceptor |
83 """ |
86 """ |
84 with EricMutexLocker(self.__mutex): |
87 with EricMutexLocker(self.__mutex): |
85 if interceptor in self.__interceptors: |
88 if interceptor in self.__interceptors: |
86 self.__interceptors.remove(interceptor) |
89 self.__interceptors.remove(interceptor) |
87 |
90 |
88 def __loadSettings(self): |
91 def __loadSettings(self): |
89 """ |
92 """ |
90 Private method to load the Network Manager settings. |
93 Private method to load the Network Manager settings. |
91 """ |
94 """ |
92 with EricMutexLocker(self.__mutex): |
95 with EricMutexLocker(self.__mutex): |
93 self.__doNotTrack = Preferences.getWebBrowser( |
96 self.__doNotTrack = Preferences.getWebBrowser("DoNotTrack") |
94 "DoNotTrack") |
97 self.__sendReferer = Preferences.getWebBrowser("RefererSendReferer") |
95 self.__sendReferer = Preferences.getWebBrowser( |
|
96 "RefererSendReferer") |
|
97 self.__refererDefaultPolicy = Preferences.getWebBrowser( |
98 self.__refererDefaultPolicy = Preferences.getWebBrowser( |
98 "RefererDefaultPolicy") |
99 "RefererDefaultPolicy" |
|
100 ) |
99 self.__refererTrimmingPolicy = Preferences.getWebBrowser( |
101 self.__refererTrimmingPolicy = Preferences.getWebBrowser( |
100 "RefererTrimmingPolicy") |
102 "RefererTrimmingPolicy" |
101 |
103 ) |
|
104 |
102 def preferencesChanged(self): |
105 def preferencesChanged(self): |
103 """ |
106 """ |
104 Public slot to handle a change of preferences. |
107 Public slot to handle a change of preferences. |
105 """ |
108 """ |
106 self.__loadSettings() |
109 self.__loadSettings() |
107 |
110 |
108 def __setRefererHeader(self, info): |
111 def __setRefererHeader(self, info): |
109 """ |
112 """ |
110 Private method to set the 'Referer' header depending on the configured |
113 Private method to set the 'Referer' header depending on the configured |
111 rule set. |
114 rule set. |
112 |
115 |
113 @param info URL request information |
116 @param info URL request information |
114 @type QWebEngineUrlRequestInfo |
117 @type QWebEngineUrlRequestInfo |
115 @see <a href="https://wiki.mozilla.org/Security/Referrer"> |
118 @see <a href="https://wiki.mozilla.org/Security/Referrer"> |
116 Mozilla Referrer</a> |
119 Mozilla Referrer</a> |
117 @see <a href="https://www.w3.org/TR/referrer-policy/"> |
120 @see <a href="https://www.w3.org/TR/referrer-policy/"> |
132 # 1 = same-origin |
135 # 1 = same-origin |
133 # 2 = strict-origin-when-cross-origin |
136 # 2 = strict-origin-when-cross-origin |
134 # 3 = no-referrer-when-downgrade (default) |
137 # 3 = no-referrer-when-downgrade (default) |
135 # see: https://wiki.mozilla.org/Security/Referrer |
138 # see: https://wiki.mozilla.org/Security/Referrer |
136 # see: https://www.w3.org/TR/referrer-policy/ |
139 # see: https://www.w3.org/TR/referrer-policy/ |
137 |
140 |
138 if ( |
141 if ( |
139 self.__sendReferer == 0 or |
142 self.__sendReferer == 0 |
140 # never send referer header |
143 # never send referrer header |
141 |
144 or ( |
142 (self.__sendReferer == 1 and |
145 self.__sendReferer == 1 |
143 (info.navigationType() != |
146 and ( |
144 QWebEngineUrlRequestInfo.NavigationType.NavigationTypeLink)) |
147 info.navigationType() |
145 # send referer header only on click |
148 != QWebEngineUrlRequestInfo.NavigationType.NavigationTypeLink |
|
149 ) |
|
150 ) |
|
151 # send referrer header only on click |
146 ): |
152 ): |
147 info.setHttpHeader(b"Referer", b"") |
153 info.setHttpHeader(b"Referer", b"") |
148 else: |
154 else: |
149 # send referer header always applying further policies |
155 # send referer header always applying further policies |
150 url = info.firstPartyUrl() |
156 url = info.firstPartyUrl() |
169 refererUrl = b"" |
175 refererUrl = b"" |
170 else: |
176 else: |
171 refererUrl = self.__refererOrigin(url) |
177 refererUrl = self.__refererOrigin(url) |
172 else: |
178 else: |
173 # no-referrer-when-downgrade |
179 # no-referrer-when-downgrade |
174 if ( |
180 if url.scheme() in ( |
175 url.scheme() in ("https", "wss") and |
181 "https", |
176 not self.__potentiallyTrustworthy(url) |
182 "wss", |
177 ): |
183 ) and not self.__potentiallyTrustworthy(url): |
178 refererUrl = b"" |
184 refererUrl = b"" |
179 else: |
185 else: |
180 refererUrl = self.__trimmedReferer(url) |
186 refererUrl = self.__trimmedReferer(url) |
181 |
187 |
182 info.setHttpHeader(b"Referer", refererUrl) |
188 info.setHttpHeader(b"Referer", refererUrl) |
183 |
189 |
184 def __sameOrigin(self, url1, url2): |
190 def __sameOrigin(self, url1, url2): |
185 """ |
191 """ |
186 Private method to test the "same origin" policy. |
192 Private method to test the "same origin" policy. |
187 |
193 |
188 @param url1 first URL for the test |
194 @param url1 first URL for the test |
189 @type QUrl |
195 @type QUrl |
190 @param url2 second URL for the test |
196 @param url2 second URL for the test |
191 @type QUrl |
197 @type QUrl |
192 @return flag indicating that both URLs have the same origin |
198 @return flag indicating that both URLs have the same origin |
193 @rtype bool |
199 @rtype bool |
194 """ |
200 """ |
195 origin1 = url1.url(QUrl.UrlFormattingOption.RemoveUserInfo | |
201 origin1 = url1.url( |
196 QUrl.UrlFormattingOption.RemovePath) |
202 QUrl.UrlFormattingOption.RemoveUserInfo |
197 origin2 = url2.url(QUrl.UrlFormattingOption.RemoveUserInfo | |
203 | QUrl.UrlFormattingOption.RemovePath |
198 QUrl.UrlFormattingOption.RemovePath) |
204 ) |
199 |
205 origin2 = url2.url( |
|
206 QUrl.UrlFormattingOption.RemoveUserInfo |
|
207 | QUrl.UrlFormattingOption.RemovePath |
|
208 ) |
|
209 |
200 return origin1 == origin2 |
210 return origin1 == origin2 |
201 |
211 |
202 def __potentiallyTrustworthy(self, url): |
212 def __potentiallyTrustworthy(self, url): |
203 """ |
213 """ |
204 Private method to check, if the given URL is potentially trustworthy. |
214 Private method to check, if the given URL is potentially trustworthy. |
205 |
215 |
206 @param url URL to be checked |
216 @param url URL to be checked |
207 @type QUrl |
217 @type QUrl |
208 @return flag indicating a potentially trustworthy URL |
218 @return flag indicating a potentially trustworthy URL |
209 @rtype bool |
219 @rtype bool |
210 """ |
220 """ |
211 if url.scheme() == "data": |
221 if url.scheme() == "data": |
212 return False |
222 return False |
213 |
223 |
214 if url.toString() in ("about:blank", "about:srcdoc"): |
224 if url.toString() in ("about:blank", "about:srcdoc"): |
215 return True |
225 return True |
216 |
226 |
217 origin = url.adjusted(QUrl.UrlFormattingOption.RemoveUserInfo | |
227 origin = url.adjusted( |
218 QUrl.UrlFormattingOption.RemovePath) |
228 QUrl.UrlFormattingOption.RemoveUserInfo |
219 |
229 | QUrl.UrlFormattingOption.RemovePath |
|
230 ) |
|
231 |
220 if origin.isEmpty() or origin.scheme() == "": |
232 if origin.isEmpty() or origin.scheme() == "": |
221 return False |
233 return False |
222 if origin.scheme() in ("https", "wss"): |
234 if origin.scheme() in ("https", "wss"): |
223 return True |
235 return True |
224 if origin.host().startswith("127.") or origin.host().endswith(":1"): |
236 if origin.host().startswith("127.") or origin.host().endswith(":1"): |
225 return True |
237 return True |
226 if ( |
238 if origin.host() == "localhost" or origin.host().endswith(".localhost"): |
227 origin.host() == "localhost" or |
|
228 origin.host().endswith(".localhost") |
|
229 ): |
|
230 return True |
239 return True |
231 if origin.scheme() == "file": |
240 if origin.scheme() == "file": |
232 return True |
241 return True |
233 if origin.scheme() in ("qrc", "qthelp", "eric"): |
242 if origin.scheme() in ("qrc", "qthelp", "eric"): |
234 return True |
243 return True |
235 |
244 |
236 return False |
245 return False |
237 |
246 |
238 def __trimmedReferer(self, url): |
247 def __trimmedReferer(self, url): |
239 """ |
248 """ |
240 Private method to generate the trimmed referer header URL. |
249 Private method to generate the trimmed referer header URL. |
241 |
250 |
242 @param url URL to be trimmed as a referer header |
251 @param url URL to be trimmed as a referer header |
243 @type QUrl |
252 @type QUrl |
244 @return trimmed referer header URL |
253 @return trimmed referer header URL |
245 @rtype QByteArray or bytes |
254 @rtype QByteArray or bytes |
246 """ |
255 """ |
247 if self.__refererTrimmingPolicy == 0: |
256 if self.__refererTrimmingPolicy == 0: |
248 # send full URL (no trimming) (default) |
257 # send full URL (no trimming) (default) |
249 refererUrl = url.toEncoded( |
258 refererUrl = url.toEncoded( |
250 QUrl.UrlFormattingOption.RemoveUserInfo | |
259 QUrl.UrlFormattingOption.RemoveUserInfo |
251 QUrl.UrlFormattingOption.RemoveFragment |
260 | QUrl.UrlFormattingOption.RemoveFragment |
252 ) |
261 ) |
253 elif self.__refererTrimmingPolicy == 1: |
262 elif self.__refererTrimmingPolicy == 1: |
254 # send the URL without its query string |
263 # send the URL without its query string |
255 refererUrl = url.toEncoded( |
264 refererUrl = url.toEncoded( |
256 QUrl.UrlFormattingOption.RemoveUserInfo | |
265 QUrl.UrlFormattingOption.RemoveUserInfo |
257 QUrl.UrlFormattingOption.RemoveFragment | |
266 | QUrl.UrlFormattingOption.RemoveFragment |
258 QUrl.UrlFormattingOption.RemoveQuery |
267 | QUrl.UrlFormattingOption.RemoveQuery |
259 ) |
268 ) |
260 else: |
269 else: |
261 # only send the origin (ensure trailing /) |
270 # only send the origin (ensure trailing /) |
262 refererUrl = self.__refererOrigin(url) |
271 refererUrl = self.__refererOrigin(url) |
263 |
272 |
264 return refererUrl |
273 return refererUrl |
265 |
274 |
266 def __refererOrigin(self, url): |
275 def __refererOrigin(self, url): |
267 """ |
276 """ |
268 Private method to generate an origin referer header URL. |
277 Private method to generate an origin referer header URL. |
269 |
278 |
270 @param url URL to generate the header from |
279 @param url URL to generate the header from |
271 @type QUrl |
280 @type QUrl |
272 @return origin referer header URL |
281 @return origin referer header URL |
273 @rtype QByteArray or bytes |
282 @rtype QByteArray or bytes |
274 """ |
283 """ |
275 referer = url.toEncoded( |
284 referer = url.toEncoded( |
276 QUrl.UrlFormattingOption.RemoveUserInfo | |
285 QUrl.UrlFormattingOption.RemoveUserInfo |
277 QUrl.UrlFormattingOption.RemovePath |
286 | QUrl.UrlFormattingOption.RemovePath |
278 ) |
287 ) |
279 if not referer.endsWith(b"/"): |
288 if not referer.endsWith(b"/"): |
280 referer += b"/" |
289 referer += b"/" |
281 |
290 |
282 return referer |
291 return referer |