60 info.setHttpHeader(b"User-Agent", userAgent.encode()) |
61 info.setHttpHeader(b"User-Agent", userAgent.encode()) |
61 |
62 |
62 for interceptor in self.__interceptors: |
63 for interceptor in self.__interceptors: |
63 interceptor.interceptRequest(info) |
64 interceptor.interceptRequest(info) |
64 |
65 |
|
66 def installUrlInterceptor(self, interceptor): |
|
67 """ |
|
68 Public method to install an URL interceptor. |
|
69 |
|
70 @param interceptor URL interceptor to be installed |
|
71 @type UrlInterceptor |
|
72 """ |
|
73 locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ |
|
74 |
|
75 if interceptor not in self.__interceptors: |
|
76 self.__interceptors.append(interceptor) |
|
77 |
|
78 def removeUrlInterceptor(self, interceptor): |
|
79 """ |
|
80 Public method to remove an URL interceptor. |
|
81 |
|
82 @param interceptor URL interceptor to be removed |
|
83 @type UrlInterceptor |
|
84 """ |
|
85 locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ |
|
86 |
|
87 if interceptor in self.__interceptors: |
|
88 self.__interceptors.remove(interceptor) |
|
89 |
|
90 def __loadSettings(self): |
|
91 """ |
|
92 Private method to load the Network Manager settings. |
|
93 """ |
|
94 locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ |
|
95 |
|
96 self.__doNotTrack = Preferences.getWebBrowser("DoNotTrack") |
|
97 self.__sendReferer = Preferences.getWebBrowser("RefererSendReferer") |
|
98 self.__refererDefaultPolicy = \ |
|
99 Preferences.getWebBrowser("RefererDefaultPolicy") |
|
100 self.__refererTrimmingPolicy = \ |
|
101 Preferences.getWebBrowser("RefererTrimmingPolicy") |
|
102 |
|
103 def preferencesChanged(self): |
|
104 """ |
|
105 Public slot to handle a change of preferences. |
|
106 """ |
|
107 self.__loadSettings() |
|
108 |
65 def __setRefererHeader(self, info): |
109 def __setRefererHeader(self, info): |
66 """ |
110 """ |
67 Private method to set the 'Referer' header depending on the configured |
111 Private method to set the 'Referer' header depending on the configured |
68 rule set. |
112 rule set. |
69 |
113 |
70 @param info URL request information |
114 @param info URL request information |
71 @type QWebEngineUrlRequestInfo |
115 @type QWebEngineUrlRequestInfo |
72 """ |
116 @see <a href="https://wiki.mozilla.org/Security/Referrer"> |
73 # TODO: extend referrer handling like that: |
117 Mozilla Referrer</a> |
|
118 @see <a href="https://www.w3.org/TR/referrer-policy/"> |
|
119 W3C Referrer Policy</a> |
|
120 """ |
74 # 1. SendReferer: |
121 # 1. SendReferer: |
75 # 0 = never |
122 # 0 = never |
76 # 1 = only on click (NavigationTypeLink) |
123 # 1 = only on click (NavigationTypeLink) |
77 # 2 = always (default) |
124 # 2 = always (default) |
78 # 2. RefererTrimmingPolicy: |
125 # 2. RefererTrimmingPolicy: |
79 # 0 = send full URL (no trimming) (default) |
126 # 0 = send full URL (no trimming) (default) |
80 # 1 = send the URL without its query string |
127 # 1 = send the URL without its query string |
81 # 2 = only send the origin (ensure trailing /) |
128 # 2 = only send the origin (ensure trailing /) |
82 # 3. RefererDefaultPolicy: |
129 # 3. RefererDefaultPolicy: |
83 # set the default referrer policy (which can be overriden by |
130 # set the default referrer policy (which can be overriden by |
86 # 1 = same-origin |
133 # 1 = same-origin |
87 # 2 = strict-origin-when-cross-origin |
134 # 2 = strict-origin-when-cross-origin |
88 # 3 = no-referrer-when-downgrade (default) |
135 # 3 = no-referrer-when-downgrade (default) |
89 # see: https://wiki.mozilla.org/Security/Referrer |
136 # see: https://wiki.mozilla.org/Security/Referrer |
90 # see: https://www.w3.org/TR/referrer-policy/ |
137 # see: https://www.w3.org/TR/referrer-policy/ |
91 if not self.__sendReferer: |
138 |
|
139 if self.__sendReferer == 0: |
|
140 # never send referer header |
92 info.setHttpHeader(b"Referer", b"") |
141 info.setHttpHeader(b"Referer", b"") |
93 |
142 elif (self.__sendReferer == 1 and |
94 def installUrlInterceptor(self, interceptor): |
143 info.navigationType() != |
95 """ |
144 QWebEngineUrlRequestInfo.NavigationTypeLink): |
96 Public method to install an URL interceptor. |
145 # send referer header only on click |
97 |
146 info.setHttpHeader(b"Referer", b"") |
98 @param interceptor URL interceptor to be installed |
147 else: |
99 @type UrlInterceptor |
148 # send referer header always applying further policies |
100 """ |
149 url = info.firstPartyUrl() |
101 locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ |
150 reqUrl = info.requestUrl() |
102 |
151 if self.__refererDefaultPolicy == 0: |
103 if interceptor not in self.__interceptors: |
152 # no-referrer |
104 self.__interceptors.append(interceptor) |
153 refererUrl = b"" |
105 |
154 elif self.__refererDefaultPolicy == 1: |
106 def removeUrlInterceptor(self, interceptor): |
155 # same-origin |
107 """ |
156 if self.__sameOrigin(url, reqUrl): |
108 Public method to remove an URL interceptor. |
157 refererUrl = self.__trimmedReferer(url) |
109 |
158 else: |
110 @param interceptor URL interceptor to be removed |
159 refererUrl = b"" |
111 @type UrlInterceptor |
160 elif self.__refererDefaultPolicy == 2: |
112 """ |
161 # strict-origin-when-cross-origin |
113 locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ |
162 if self.__sameOrigin(url, reqUrl): |
114 |
163 refererUrl = self.__trimmedReferer(url) |
115 if interceptor in self.__interceptors: |
164 elif url.scheme() in ("https", "wss"): |
116 self.__interceptors.remove(interceptor) |
165 if self.__potentiallyTrustworthy(url): |
117 |
166 refererUrl = self.__refererOrigin(url) |
118 def __loadSettings(self): |
167 else: |
119 """ |
168 refererUrl = b"" |
120 Private method to load the Network Manager settings. |
169 else: |
121 """ |
170 refererUrl = self.__refererOrigin(url) |
122 locker = QMutexLocker(self.__mutex) # __IGNORE_WARNING__ |
171 else: |
123 |
172 # no-referrer-when-downgrade |
124 self.__doNotTrack = Preferences.getWebBrowser("DoNotTrack") |
173 if url.scheme() in ("https", "wss") and \ |
125 self.__sendReferer = Preferences.getWebBrowser("SendReferer") |
174 not self.__potentiallyTrustworthy(url): |
126 |
175 refererUrl = b"" |
127 def preferencesChanged(self): |
176 else: |
128 """ |
177 refererUrl = self.__trimmedReferer(url) |
129 Public slot to handle a change of preferences. |
178 |
130 """ |
179 info.setHttpHeader(b"Referer", refererUrl) |
131 self.__loadSettings() |
180 |
|
181 def __sameOrigin(self, url1, url2): |
|
182 """ |
|
183 Private method to test the "same origin" policy. |
|
184 |
|
185 @param url1 first URL for the test |
|
186 @type QUrl |
|
187 @param url2 second URL for the test |
|
188 @type QUrl |
|
189 @return flag indicating that both URLs have the same origin |
|
190 @rtype bool |
|
191 """ |
|
192 origin1 = url1.url(QUrl.RemoveUserInfo | QUrl.RemovePath) |
|
193 origin2 = url2.url(QUrl.RemoveUserInfo | QUrl.RemovePath) |
|
194 |
|
195 return origin1 == origin2 |
|
196 |
|
197 def __potentiallyTrustworthy(self, url): |
|
198 """ |
|
199 Private method to check, if the given URL is potentially trustworthy. |
|
200 |
|
201 @param url URL to be checked |
|
202 @type QUrl |
|
203 @return flag indicating a potentially trustworthy URL |
|
204 @rtype bool |
|
205 """ |
|
206 if url.scheme() == "data": |
|
207 return False |
|
208 |
|
209 if url.toString() in ("about:blank", "about:srcdoc"): |
|
210 return True |
|
211 |
|
212 origin = url.adjusted(QUrl.RemoveUserInfo | QUrl.RemovePath) |
|
213 |
|
214 if origin.isEmpty() or origin.scheme() == "": |
|
215 return False |
|
216 if origin.scheme() in ("https", "wss"): |
|
217 return True |
|
218 if origin.host().startswith("127.") or origin.host().endswith(":1"): |
|
219 return True |
|
220 if origin.host() == "localhost" or \ |
|
221 origin.host().endswith(".localhost"): |
|
222 return True |
|
223 if origin.scheme() == "file": |
|
224 return True |
|
225 if origin.scheme() in ("qrc", "qthelp", "eric"): |
|
226 return True |
|
227 |
|
228 return False |
|
229 |
|
230 def __trimmedReferer(self, url): |
|
231 """ |
|
232 Private method to generate the trimmed referer header URL. |
|
233 |
|
234 @param url URL to be trimmed as a referer header |
|
235 @type QUrl |
|
236 @return trimmed referer header URL |
|
237 @rtype QByteArray or bytes |
|
238 """ |
|
239 if self.__refererTrimmingPolicy == 0: |
|
240 # send full URL (no trimming) (default) |
|
241 refererUrl = url.toEncoded( |
|
242 QUrl.RemoveUserInfo | QUrl.RemoveFragment) |
|
243 elif self.__refererTrimmingPolicy == 1: |
|
244 # send the URL without its query string |
|
245 refererUrl = url.toEncoded( |
|
246 QUrl.RemoveUserInfo | QUrl.RemoveFragment | |
|
247 QUrl.RemoveQuery) |
|
248 else: |
|
249 # only send the origin (ensure trailing /) |
|
250 refererUrl = self.__refererOrigin(url) |
|
251 |
|
252 return refererUrl |
|
253 |
|
254 def __refererOrigin(self, url): |
|
255 """ |
|
256 Private method to generate an origin referer header URL. |
|
257 |
|
258 @param url URL to generate the header from |
|
259 @type QUrl |
|
260 @return origin referer header URL |
|
261 @rtype QByteArray or bytes |
|
262 """ |
|
263 referer = url.toEncoded(QUrl.RemoveUserInfo | QUrl.RemovePath) |
|
264 if not referer.endsWith(b"/"): |
|
265 referer += b"/" |
|
266 |
|
267 return referer |