Mercurial > public > mercurial-scm > hg-stable
annotate mercurial/hgweb/common.py @ 30766:d7bf7d2bd5ab
hgweb: support Content Security Policy
Content-Security-Policy (CSP) is a web security feature that allows
servers to declare what loaded content is allowed to do. For example,
a policy can prevent loading of images, JavaScript, CSS, etc unless
the source of that content is whitelisted (by hostname, URI scheme,
hashes of content, etc). It's a nifty security feature that provides
extra mitigation against some attacks, notably XSS.
Mitigation against these attacks is important for Mercurial because
hgweb renders repository data, which is commonly untrusted. While we
make attempts to escape things, etc, there's the possibility that
malicious data could be injected into the site content. If this happens
today, the full power of the web browser is available to that
malicious content. A restrictive CSP policy (defined by the server
operator and sent in an HTTP header which is outside the control of
malicious content), could restrict browser capabilities and mitigate
security problems posed by malicious data.
CSP works by emitting an HTTP header declaring the policy that browsers
should apply. Ideally, this header would be emitted by a layer above
Mercurial (likely the HTTP server doing the WSGI "proxying"). This
works for some CSP policies, but not all.
For example, policies to allow inline JavaScript may require setting
a "nonce" attribute on <script>. This attribute value must be unique
and non-guessable. And, the value must be present in the HTTP header
and the HTML body. This means that coordinating the value between
Mercurial and another HTTP server could be difficult: it is much
easier to generate and emit the nonce in a central location.
This commit introduces support for emitting a
Content-Security-Policy header from hgweb. A config option defines
the header value. If present, the header is emitted. A special
"%nonce%" syntax in the value triggers generation of a nonce and
inclusion in <script> elements in templates. The inclusion of a
nonce does not occur unless "%nonce%" is present. This makes this
commit completely backwards compatible and the feature opt-in.
The nonce is a type 4 UUID, which is the flavor that is randomly
generated. It has 122 random bits, which should be plenty to satisfy
the guarantees of a nonce.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Tue, 10 Jan 2017 23:37:08 -0800 |
parents | f1c9fafcbf46 |
children | 7dafa8d0e006 |
rev | line source |
---|---|
2391
d351a3be3371
Fixing up comment headers for split up code.
Eric Hopper <hopper@omnifarious.org>
parents:
2356
diff
changeset
|
1 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod |
131 | 2 # |
238
3b92f8fe47ae
hgweb.py: kill #! line, clean up copyright notice
mpm@selenic.com
parents:
222
diff
changeset
|
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> |
2859 | 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> |
131 | 5 # |
8225
46293a0c7e9f
updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents:
7966
diff
changeset
|
6 # This software may be used and distributed according to the terms of the |
10263 | 7 # GNU General Public License version 2 or any later version. |
131 | 8 |
27046
37fcfe52c68c
hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents:
27044
diff
changeset
|
9 from __future__ import absolute_import |
37fcfe52c68c
hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents:
27044
diff
changeset
|
10 |
30766
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
11 import base64 |
27046
37fcfe52c68c
hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents:
27044
diff
changeset
|
12 import errno |
37fcfe52c68c
hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents:
27044
diff
changeset
|
13 import mimetypes |
37fcfe52c68c
hgweb: use absolute_import
Yuya Nishihara <yuya@tcha.org>
parents:
27044
diff
changeset
|
14 import os |
30766
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
15 import uuid |
5561
22713dce19f6
hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents:
4462
diff
changeset
|
16 |
30620
bb77654dc7ae
py3: replace os.sep with pycompat.ossep (part 3 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29566
diff
changeset
|
17 from .. import ( |
30641
f1c9fafcbf46
py3: replace os.environ with encoding.environ (part 3 of 5)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
30630
diff
changeset
|
18 encoding, |
30620
bb77654dc7ae
py3: replace os.sep with pycompat.ossep (part 3 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29566
diff
changeset
|
19 pycompat, |
bb77654dc7ae
py3: replace os.sep with pycompat.ossep (part 3 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29566
diff
changeset
|
20 util, |
bb77654dc7ae
py3: replace os.sep with pycompat.ossep (part 3 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29566
diff
changeset
|
21 ) |
29566
075146e85bb6
py3: conditionalize BaseHTTPServer, SimpleHTTPServer and CGIHTTPServer import
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29491
diff
changeset
|
22 |
075146e85bb6
py3: conditionalize BaseHTTPServer, SimpleHTTPServer and CGIHTTPServer import
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29491
diff
changeset
|
23 httpserver = util.httpserver |
075146e85bb6
py3: conditionalize BaseHTTPServer, SimpleHTTPServer and CGIHTTPServer import
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29491
diff
changeset
|
24 |
5993
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
25 HTTP_OK = 200 |
12183
f64b416b0ac8
hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
10264
diff
changeset
|
26 HTTP_NOT_MODIFIED = 304 |
5993
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
27 HTTP_BAD_REQUEST = 400 |
6926
57b954d8d003
hgweb: raise ErrorResponses to communicate protocol errors
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6924
diff
changeset
|
28 HTTP_UNAUTHORIZED = 401 |
7029
b84d27386285
hgweb: Respond with HTTP 403 for disabled archive types instead of 404
Rocco Rutte <pdmef@gmx.net>
parents:
6926
diff
changeset
|
29 HTTP_FORBIDDEN = 403 |
5993
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
30 HTTP_NOT_FOUND = 404 |
6926
57b954d8d003
hgweb: raise ErrorResponses to communicate protocol errors
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
6924
diff
changeset
|
31 HTTP_METHOD_NOT_ALLOWED = 405 |
5993
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
32 HTTP_SERVER_ERROR = 500 |
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
33 |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
34 |
19032
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
35 def ismember(ui, username, userlist): |
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
36 """Check if username is a member of userlist. |
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
37 |
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
38 If userlist has a single '*' member, all users are considered members. |
19951
d51c4d85ec23
spelling: random spell checker fixes
Mads Kiilerich <madski@unity3d.com>
parents:
19032
diff
changeset
|
39 Can be overridden by extensions to provide more complex authorization |
19032
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
40 schemes. |
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
41 """ |
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
42 return userlist == ['*'] or username in userlist |
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
43 |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
44 def checkauthz(hgweb, req, op): |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
45 '''Check permission for operation based on request data (including |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
46 authentication info). Return if op allowed, else raise an ErrorResponse |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
47 exception.''' |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
48 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
49 user = req.env.get('REMOTE_USER') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
50 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
51 deny_read = hgweb.configlist('web', 'deny_read') |
19032
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
52 if deny_read and (not user or ismember(hgweb.repo.ui, user, deny_read)): |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
53 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
54 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
55 allow_read = hgweb.configlist('web', 'allow_read') |
19032
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
56 if allow_read and (not ismember(hgweb.repo.ui, user, allow_read)): |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
57 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
58 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
59 if op == 'pull' and not hgweb.allowpull: |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
60 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
61 elif op == 'pull' or op is None: # op is None for interface requests |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
62 return |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
63 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
64 # enforce that you can only push using POST requests |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
65 if req.env['REQUEST_METHOD'] != 'POST': |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
66 msg = 'push requires POST request' |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
67 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
68 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
69 # require ssl by default for pushing, auth info cannot be sniffed |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
70 # and replayed |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
71 scheme = req.env.get('wsgi.url_scheme') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
72 if hgweb.configbool('web', 'push_ssl', True) and scheme != 'https': |
17456
59a168019255
hgweb: respond 403 forbidden for ssl required error
Yuya Nishihara <yuya@tcha.org>
parents:
16687
diff
changeset
|
73 raise ErrorResponse(HTTP_FORBIDDEN, 'ssl required') |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
74 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
75 deny = hgweb.configlist('web', 'deny_push') |
19032
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
76 if deny and (not user or ismember(hgweb.repo.ui, user, deny)): |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
77 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
78 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
79 allow = hgweb.configlist('web', 'allow_push') |
19032
7d31f2e42a8a
hgweb: refactor checks for granting and revoking user permissions
Wagner Bruna <wbruna@softwareexpress.com.br>
parents:
18645
diff
changeset
|
80 if not (allow and ismember(hgweb.repo.ui, user, allow)): |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
81 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
82 |
14058
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
83 # Hooks for hgweb permission checks; extensions can add hooks here. |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
84 # Each hook is invoked like this: hook(hgweb, request, operation), |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
85 # where operation is either read, pull or push. Hooks should either |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
86 # raise an ErrorResponse exception, or just return. |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
87 # |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
88 # It is possible to do both authentication and authorization through |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
89 # this. |
3233b39d756f
hgweb: initialize permhooks at definition time
Martin Geisler <mg@lazybytes.net>
parents:
13959
diff
changeset
|
90 permhooks = [checkauthz] |
9910
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
91 |
6f92997dbdca
hgweb: add support for extension-provided permission hooks
Sune Foldager <cryo@cyanite.org>
parents:
9694
diff
changeset
|
92 |
5561
22713dce19f6
hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents:
4462
diff
changeset
|
93 class ErrorResponse(Exception): |
7741
a3d7f99c23c0
hgweb: support custom http headers in ErrorResponse
Sune Foldager <cryo@cyanite.org>
parents:
7310
diff
changeset
|
94 def __init__(self, code, message=None, headers=[]): |
13444
75f5f312df5f
hgweb: give ErrorResponse a descriptive string/Exception representation
Mads Kiilerich <mads@kiilerich.com>
parents:
13400
diff
changeset
|
95 if message is None: |
75f5f312df5f
hgweb: give ErrorResponse a descriptive string/Exception representation
Mads Kiilerich <mads@kiilerich.com>
parents:
13400
diff
changeset
|
96 message = _statusmessage(code) |
26200 | 97 Exception.__init__(self, message) |
5561
22713dce19f6
hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents:
4462
diff
changeset
|
98 self.code = code |
7741
a3d7f99c23c0
hgweb: support custom http headers in ErrorResponse
Sune Foldager <cryo@cyanite.org>
parents:
7310
diff
changeset
|
99 self.headers = headers |
5563
d61fea133f2d
hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents:
5561
diff
changeset
|
100 |
13570
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
101 class continuereader(object): |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
102 def __init__(self, f, write): |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
103 self.f = f |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
104 self._write = write |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
105 self.continued = False |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
106 |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
107 def read(self, amt=-1): |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
108 if not self.continued: |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
109 self.continued = True |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
110 self._write('HTTP/1.1 100 Continue\r\n\r\n') |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
111 return self.f.read(amt) |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
112 |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
113 def __getattr__(self, attr): |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
114 if attr in ('close', 'readline', 'readlines', '__iter__'): |
617a87cb7eb2
hgweb: add support for 100-continue as recommended by PEP 333.
Augie Fackler <durin42@gmail.com>
parents:
13444
diff
changeset
|
115 return getattr(self.f, attr) |
16687
e34106fa0dc3
cleanup: "raise SomeException()" -> "raise SomeException"
Brodie Rao <brodie@sf.io>
parents:
14058
diff
changeset
|
116 raise AttributeError |
5563
d61fea133f2d
hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents:
5561
diff
changeset
|
117 |
d61fea133f2d
hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents:
5561
diff
changeset
|
118 def _statusmessage(code): |
29566
075146e85bb6
py3: conditionalize BaseHTTPServer, SimpleHTTPServer and CGIHTTPServer import
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29491
diff
changeset
|
119 responses = httpserver.basehttprequesthandler.responses |
5563
d61fea133f2d
hgweb: fix breaking tests on Python < 2.5
Bryan O'Sullivan <bos@serpentine.com>
parents:
5561
diff
changeset
|
120 return responses.get(code, ('Error', 'Unknown error'))[0] |
5760
0145f9afb0e7
Removed tabs and trailing whitespace in python files
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5563
diff
changeset
|
121 |
9694
8269fe2d48f6
hgweb: send proper error messages to the client
Sune Foldager <cryo@cyanite.org>
parents:
9031
diff
changeset
|
122 def statusmessage(code, message=None): |
8269fe2d48f6
hgweb: send proper error messages to the client
Sune Foldager <cryo@cyanite.org>
parents:
9031
diff
changeset
|
123 return '%d %s' % (code, message or _statusmessage(code)) |
131 | 124 |
25717
46e2c57026bc
hgweb: drop the default argument for get_stat
Pierre-Yves David <pierre-yves.david@fb.com>
parents:
25660
diff
changeset
|
125 def get_stat(spath, fn): |
46e2c57026bc
hgweb: drop the default argument for get_stat
Pierre-Yves David <pierre-yves.david@fb.com>
parents:
25660
diff
changeset
|
126 """stat fn if it exists, spath otherwise""" |
22577
a111e460318a
hgweb: refresh hgweb.repo on phase change (issue4061)
Anton Shestakov <engored@ya.ru>
parents:
19951
diff
changeset
|
127 cl_path = os.path.join(spath, fn) |
3853
c0b449154a90
switch to the .hg/store layout, fix the tests
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
3276
diff
changeset
|
128 if os.path.exists(cl_path): |
13958
71f51cc71652
hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents:
13444
diff
changeset
|
129 return os.stat(cl_path) |
1418
68f81ba07b2a
Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
1416
diff
changeset
|
130 else: |
13958
71f51cc71652
hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents:
13444
diff
changeset
|
131 return os.stat(spath) |
71f51cc71652
hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents:
13444
diff
changeset
|
132 |
71f51cc71652
hgweb: detect change based on changelog size too
Martin Geisler <mg@lazybytes.net>
parents:
13444
diff
changeset
|
133 def get_mtime(spath): |
25717
46e2c57026bc
hgweb: drop the default argument for get_stat
Pierre-Yves David <pierre-yves.david@fb.com>
parents:
25660
diff
changeset
|
134 return get_stat(spath, "00changelog.i").st_mtime |
1418
68f81ba07b2a
Make hgweb work when the repository is empty (no 00changelog.i)
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
1416
diff
changeset
|
135 |
2514
419c42223bee
Really fix http headers for web UI and issue 254.
Eric Hopper <hopper@omnifarious.org>
parents:
2391
diff
changeset
|
136 def staticfile(directory, fname, req): |
5930
c301f15c965a
send conservatively capitalized HTTP headers
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5779
diff
changeset
|
137 """return a file inside directory with guessed Content-Type header |
1825
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
138 |
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
139 fname always uses '/' as directory separator and isn't allowed to |
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
140 contain unusual path components. |
5930
c301f15c965a
send conservatively capitalized HTTP headers
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5779
diff
changeset
|
141 Content-Type is guessed using the mimetypes module. |
1825
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
142 Return an empty string if fname is illegal or file not found. |
1793
83c6d8355909
Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1792
diff
changeset
|
143 |
1825
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
144 """ |
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
145 parts = fname.split('/') |
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
146 for part in parts: |
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
147 if (part in ('', os.curdir, os.pardir) or |
30620
bb77654dc7ae
py3: replace os.sep with pycompat.ossep (part 3 of 4)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
29566
diff
changeset
|
148 pycompat.ossep in part or |
30630
bcf4a975f93d
py3: replace os.altsep with pycompat.altsep
Pulkit Goyal <7895pulkit@gmail.com>
parents:
30620
diff
changeset
|
149 pycompat.osaltsep is not None and pycompat.osaltsep in part): |
18645
76ff3a715cf2
hgweb: simplify internal staticfile return codes
Mads Kiilerich <mads@kiilerich.com>
parents:
18352
diff
changeset
|
150 return |
7288
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
151 fpath = os.path.join(*parts) |
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
152 if isinstance(directory, str): |
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
153 directory = [directory] |
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
154 for d in directory: |
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
155 path = os.path.join(d, fpath) |
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
156 if os.path.exists(path): |
9c399c53469d
Allow per-file shadowing of static directory in templatepath
Brendan Cully <brendan@kublai.com>
parents:
7107
diff
changeset
|
157 break |
1793
83c6d8355909
Allow serving static files from hgwebdir to fix CSS and favicon.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1792
diff
changeset
|
158 try: |
1825
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
159 os.stat(path) |
a9343f9d7365
Make hgweb.staticfile() more secure and portable.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
1796
diff
changeset
|
160 ct = mimetypes.guess_type(path)[0] or "text/plain" |
13400
14f3795a5ed7
explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents:
12183
diff
changeset
|
161 fp = open(path, 'rb') |
14f3795a5ed7
explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents:
12183
diff
changeset
|
162 data = fp.read() |
14f3795a5ed7
explicitly close files
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents:
12183
diff
changeset
|
163 fp.close() |
18352
e33b9b92a200
hgweb: pass the actual response body to request.response, not just the length
Mads Kiilerich <mads@kiilerich.com>
parents:
17456
diff
changeset
|
164 req.respond(HTTP_OK, ct, body=data) |
5561
22713dce19f6
hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents:
4462
diff
changeset
|
165 except TypeError: |
8761
0289f384e1e5
Generally replace "file name" with "filename" in help and comments.
timeless <timeless@gmail.com>
parents:
8225
diff
changeset
|
166 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename') |
25660
328739ea70c3
global: mass rewrite to use modern exception syntax
Gregory Szorc <gregory.szorc@gmail.com>
parents:
22577
diff
changeset
|
167 except OSError as err: |
5561
22713dce19f6
hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents:
4462
diff
changeset
|
168 if err.errno == errno.ENOENT: |
5993
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
169 raise ErrorResponse(HTTP_NOT_FOUND) |
5561
22713dce19f6
hgweb: return meaningful HTTP status codes instead of nonsense
Bryan O'Sullivan <bos@serpentine.com>
parents:
4462
diff
changeset
|
170 else: |
5993
948a41e77902
hgweb: explicit response status
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
5930
diff
changeset
|
171 raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror) |
3276
db9d2a624521
hgweb: Search templates in templatepath/style/map, too, using a common function.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
3244
diff
changeset
|
172 |
4462
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
173 def paritygen(stripecount, offset=0): |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
174 """count parity of horizontal stripes for easier reading""" |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
175 if stripecount and offset: |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
176 # account for offset, e.g. due to building the list in reverse |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
177 count = (stripecount + offset) % stripecount |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
178 parity = (stripecount + offset) / stripecount & 1 |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
179 else: |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
180 count = 0 |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
181 parity = 0 |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
182 while True: |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
183 yield parity |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
184 count += 1 |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
185 if stripecount and count >= stripecount: |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
186 parity = 1 - parity |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
187 count = 0 |
12e4d9524951
hgweb: use generator to count parity of horizontal stripes for easier reading.
Thomas Arendsen Hein <thomas@intevation.de>
parents:
4038
diff
changeset
|
188 |
5779
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
189 def get_contact(config): |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
190 """Return repo contact information or empty string. |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
191 |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
192 web.contact is the primary source, but if that is not set, try |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
193 ui.username or $EMAIL as a fallback to display something useful. |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
194 """ |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
195 return (config("web", "contact") or |
e9f68860d5ed
Don't let ui.username override web.contact (issue900)
Thomas Arendsen Hein <thomas@intevation.de>
parents:
5760
diff
changeset
|
196 config("ui", "username") or |
30641
f1c9fafcbf46
py3: replace os.environ with encoding.environ (part 3 of 5)
Pulkit Goyal <7895pulkit@gmail.com>
parents:
30630
diff
changeset
|
197 encoding.environ.get("EMAIL") or "") |
12183
f64b416b0ac8
hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
10264
diff
changeset
|
198 |
f64b416b0ac8
hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
10264
diff
changeset
|
199 def caching(web, req): |
29491
7f0498bd284e
hgweb: emit a valid, weak ETag
Anton Shestakov <av6@dwimlabs.net>
parents:
27046
diff
changeset
|
200 tag = 'W/"%s"' % web.mtime |
12183
f64b416b0ac8
hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
10264
diff
changeset
|
201 if req.env.get('HTTP_IF_NONE_MATCH') == tag: |
f64b416b0ac8
hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
10264
diff
changeset
|
202 raise ErrorResponse(HTTP_NOT_MODIFIED) |
f64b416b0ac8
hgweb: support very simple caching model (issue1845)
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents:
10264
diff
changeset
|
203 req.headers.append(('ETag', tag)) |
30766
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
204 |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
205 def cspvalues(ui): |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
206 """Obtain the Content-Security-Policy header and nonce value. |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
207 |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
208 Returns a 2-tuple of the CSP header value and the nonce value. |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
209 |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
210 First value is ``None`` if CSP isn't enabled. Second value is ``None`` |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
211 if CSP isn't enabled or if the CSP header doesn't need a nonce. |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
212 """ |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
213 # Don't allow untrusted CSP setting since it be disable protections |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
214 # from a trusted/global source. |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
215 csp = ui.config('web', 'csp', untrusted=False) |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
216 nonce = None |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
217 |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
218 if csp and '%nonce%' in csp: |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
219 nonce = base64.urlsafe_b64encode(uuid.uuid4().bytes).rstrip('=') |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
220 csp = csp.replace('%nonce%', nonce) |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
221 |
d7bf7d2bd5ab
hgweb: support Content Security Policy
Gregory Szorc <gregory.szorc@gmail.com>
parents:
30641
diff
changeset
|
222 return csp, nonce |