Mercurial > hg
comparison mercurial/hgweb/hgweb_mod.py @ 3555:881064004fd0
use untrusted settings in hgweb
The only exceptions are web.static and web.templates, since they can
be used to get any file that is readable by the user running the CGI
script.
Other options can be (ab)used to increase the use of the cpu
(allow_bz2) or of the bandwidth (server.uncompressed), but they're
trusted anyway.
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Thu, 26 Oct 2006 19:25:45 +0200 |
parents | e0db0b7934f2 |
children | f7dee427cd14 |
comparison
equal
deleted
inserted
replaced
3554:da3ee7ca620f | 3555:881064004fd0 |
---|---|
75 | 75 |
76 self.mtime = -1 | 76 self.mtime = -1 |
77 self.reponame = name | 77 self.reponame = name |
78 self.archives = 'zip', 'gz', 'bz2' | 78 self.archives = 'zip', 'gz', 'bz2' |
79 self.stripecount = 1 | 79 self.stripecount = 1 |
80 self.templatepath = self.repo.ui.config("web", "templates", | 80 # a repo owner may set web.templates in .hg/hgrc to get any file |
81 templater.templatepath()) | 81 # readable by the user running the CGI script |
82 self.templatepath = self.config("web", "templates", | |
83 templater.templatepath(), | |
84 untrusted=False) | |
85 | |
86 # The CGI scripts are often run by a user different from the repo owner. | |
87 # Trust the settings from the .hg/hgrc files by default. | |
88 def config(self, section, name, default=None, untrusted=True): | |
89 return self.repo.ui.config(section, name, default, | |
90 untrusted=untrusted) | |
91 | |
92 def configbool(self, section, name, default=False, untrusted=True): | |
93 return self.repo.ui.configbool(section, name, default, | |
94 untrusted=untrusted) | |
95 | |
96 def configlist(self, section, name, default=None, untrusted=True): | |
97 return self.repo.ui.configlist(section, name, default, | |
98 untrusted=untrusted) | |
82 | 99 |
83 def refresh(self): | 100 def refresh(self): |
84 mtime = get_mtime(self.repo.root) | 101 mtime = get_mtime(self.repo.root) |
85 if mtime != self.mtime: | 102 if mtime != self.mtime: |
86 self.mtime = mtime | 103 self.mtime = mtime |
87 self.repo = hg.repository(self.repo.ui, self.repo.root) | 104 self.repo = hg.repository(self.repo.ui, self.repo.root) |
88 self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10)) | 105 self.maxchanges = int(self.config("web", "maxchanges", 10)) |
89 self.stripecount = int(self.repo.ui.config("web", "stripes", 1)) | 106 self.stripecount = int(self.config("web", "stripes", 1)) |
90 self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60)) | 107 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) |
91 self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10)) | 108 self.maxfiles = int(self.config("web", "maxfiles", 10)) |
92 self.allowpull = self.repo.ui.configbool("web", "allowpull", True) | 109 self.allowpull = self.configbool("web", "allowpull", True) |
93 | 110 |
94 def archivelist(self, nodeid): | 111 def archivelist(self, nodeid): |
95 allowed = self.repo.ui.configlist("web", "allow_archive") | 112 allowed = self.configlist("web", "allow_archive") |
96 for i, spec in self.archive_specs.iteritems(): | 113 for i, spec in self.archive_specs.iteritems(): |
97 if i in allowed or self.repo.ui.configbool("web", "allow" + i): | 114 if i in allowed or self.configbool("web", "allow" + i): |
98 yield {"type" : i, "extension" : spec[2], "node" : nodeid} | 115 yield {"type" : i, "extension" : spec[2], "node" : nodeid} |
99 | 116 |
100 def listfilediffs(self, files, changeset): | 117 def listfilediffs(self, files, changeset): |
101 for f in files[:self.maxfiles]: | 118 for f in files[:self.maxfiles]: |
102 yield self.t("filedifflink", node=hex(changeset), file=f) | 119 yield self.t("filedifflink", node=hex(changeset), file=f) |
167 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5] | 184 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5] |
168 if files: | 185 if files: |
169 modified, added, removed = map(lambda x: filterfiles(files, x), | 186 modified, added, removed = map(lambda x: filterfiles(files, x), |
170 (modified, added, removed)) | 187 (modified, added, removed)) |
171 | 188 |
172 diffopts = patch.diffopts(self.repo.ui) | 189 diffopts = patch.diffopts(self.repo.ui, untrusted=True) |
173 for f in modified: | 190 for f in modified: |
174 to = r.file(f).read(mmap1[f]) | 191 to = r.file(f).read(mmap1[f]) |
175 tn = r.file(f).read(mmap2[f]) | 192 tn = r.file(f).read(mmap2[f]) |
176 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, | 193 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, |
177 opts=diffopts), f, tn) | 194 opts=diffopts), f, tn) |
569 count = cl.count() | 586 count = cl.count() |
570 start = max(0, count - self.maxchanges) | 587 start = max(0, count - self.maxchanges) |
571 end = min(count, start + self.maxchanges) | 588 end = min(count, start + self.maxchanges) |
572 | 589 |
573 yield self.t("summary", | 590 yield self.t("summary", |
574 desc = self.repo.ui.config("web", "description", "unknown"), | 591 desc = self.config("web", "description", "unknown"), |
575 owner = (self.repo.ui.config("ui", "username") or # preferred | 592 owner = (self.config("ui", "username") or # preferred |
576 self.repo.ui.config("web", "contact") or # deprecated | 593 self.config("web", "contact") or # deprecated |
577 self.repo.ui.config("web", "author", "unknown")), # also | 594 self.config("web", "author", "unknown")), # also |
578 lastchange = cl.read(cl.tip())[2], | 595 lastchange = cl.read(cl.tip())[2], |
579 tags = tagentries, | 596 tags = tagentries, |
580 heads = heads, | 597 heads = heads, |
581 shortlog = changelist, | 598 shortlog = changelist, |
582 node = hex(cl.tip()), | 599 node = hex(cl.tip()), |
648 | 665 |
649 def footer(**map): | 666 def footer(**map): |
650 yield self.t("footer", **map) | 667 yield self.t("footer", **map) |
651 | 668 |
652 def motd(**map): | 669 def motd(**map): |
653 yield self.repo.ui.config("web", "motd", "") | 670 yield self.config("web", "motd", "") |
654 | 671 |
655 def expand_form(form): | 672 def expand_form(form): |
656 shortcuts = { | 673 shortcuts = { |
657 'cl': [('cmd', ['changelog']), ('rev', None)], | 674 'cl': [('cmd', ['changelog']), ('rev', None)], |
658 'sl': [('cmd', ['shortlog']), ('rev', None)], | 675 'sl': [('cmd', ['shortlog']), ('rev', None)], |
746 | 763 |
747 def sessionvars(**map): | 764 def sessionvars(**map): |
748 fields = [] | 765 fields = [] |
749 if req.form.has_key('style'): | 766 if req.form.has_key('style'): |
750 style = req.form['style'][0] | 767 style = req.form['style'][0] |
751 if style != self.repo.ui.config('web', 'style', ''): | 768 if style != self.config('web', 'style', ''): |
752 fields.append(('style', style)) | 769 fields.append(('style', style)) |
753 | 770 |
754 separator = req.url[-1] == '?' and ';' or '?' | 771 separator = req.url[-1] == '?' and ';' or '?' |
755 for name, value in fields: | 772 for name, value in fields: |
756 yield dict(name=name, value=value, separator=separator) | 773 yield dict(name=name, value=value, separator=separator) |
759 self.refresh() | 776 self.refresh() |
760 | 777 |
761 expand_form(req.form) | 778 expand_form(req.form) |
762 rewrite_request(req) | 779 rewrite_request(req) |
763 | 780 |
764 style = self.repo.ui.config("web", "style", "") | 781 style = self.config("web", "style", "") |
765 if req.form.has_key('style'): | 782 if req.form.has_key('style'): |
766 style = req.form['style'][0] | 783 style = req.form['style'][0] |
767 mapfile = style_map(self.templatepath, style) | 784 mapfile = style_map(self.templatepath, style) |
768 | 785 |
769 port = req.env["SERVER_PORT"] | 786 port = req.env["SERVER_PORT"] |
770 port = port != "80" and (":" + port) or "" | 787 port = port != "80" and (":" + port) or "" |
771 urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port) | 788 urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port) |
772 | 789 |
773 if not self.reponame: | 790 if not self.reponame: |
774 self.reponame = (self.repo.ui.config("web", "name") | 791 self.reponame = (self.config("web", "name") |
775 or req.env.get('REPO_NAME') | 792 or req.env.get('REPO_NAME') |
776 or req.url.strip('/') or self.repo.root) | 793 or req.url.strip('/') or self.repo.root) |
777 | 794 |
778 self.t = templater.templater(mapfile, templater.common_filters, | 795 self.t = templater.templater(mapfile, templater.common_filters, |
779 defaults={"url": req.url, | 796 defaults={"url": req.url, |
983 req.write(z.flush()) | 1000 req.write(z.flush()) |
984 | 1001 |
985 def do_archive(self, req): | 1002 def do_archive(self, req): |
986 changeset = self.repo.lookup(req.form['node'][0]) | 1003 changeset = self.repo.lookup(req.form['node'][0]) |
987 type_ = req.form['type'][0] | 1004 type_ = req.form['type'][0] |
988 allowed = self.repo.ui.configlist("web", "allow_archive") | 1005 allowed = self.configlist("web", "allow_archive") |
989 if (type_ in self.archives and (type_ in allowed or | 1006 if (type_ in self.archives and (type_ in allowed or |
990 self.repo.ui.configbool("web", "allow" + type_, False))): | 1007 self.configbool("web", "allow" + type_, False))): |
991 self.archive(req, changeset, type_) | 1008 self.archive(req, changeset, type_) |
992 return | 1009 return |
993 | 1010 |
994 req.write(self.t("error")) | 1011 req.write(self.t("error")) |
995 | 1012 |
996 def do_static(self, req): | 1013 def do_static(self, req): |
997 fname = req.form['file'][0] | 1014 fname = req.form['file'][0] |
998 static = self.repo.ui.config("web", "static", | 1015 # a repo owner may set web.static in .hg/hgrc to get any file |
999 os.path.join(self.templatepath, | 1016 # readable by the user running the CGI script |
1000 "static")) | 1017 static = self.config("web", "static", |
1018 os.path.join(self.templatepath, "static"), | |
1019 untrusted=False) | |
1001 req.write(staticfile(static, fname, req) | 1020 req.write(staticfile(static, fname, req) |
1002 or self.t("error", error="%r not found" % fname)) | 1021 or self.t("error", error="%r not found" % fname)) |
1003 | 1022 |
1004 def do_capabilities(self, req): | 1023 def do_capabilities(self, req): |
1005 caps = ['unbundle', 'lookup', 'changegroupsubset'] | 1024 caps = ['unbundle', 'lookup', 'changegroupsubset'] |
1006 if self.repo.ui.configbool('server', 'uncompressed'): | 1025 if self.configbool('server', 'uncompressed'): |
1007 caps.append('stream=%d' % self.repo.revlogversion) | 1026 caps.append('stream=%d' % self.repo.revlogversion) |
1008 resp = ' '.join(caps) | 1027 resp = ' '.join(caps) |
1009 req.httphdr("application/mercurial-0.1", length=len(resp)) | 1028 req.httphdr("application/mercurial-0.1", length=len(resp)) |
1010 req.write(resp) | 1029 req.write(resp) |
1011 | 1030 |
1014 return true if op allowed, else false. | 1033 return true if op allowed, else false. |
1015 default is policy to use if no config given.''' | 1034 default is policy to use if no config given.''' |
1016 | 1035 |
1017 user = req.env.get('REMOTE_USER') | 1036 user = req.env.get('REMOTE_USER') |
1018 | 1037 |
1019 deny = self.repo.ui.configlist('web', 'deny_' + op) | 1038 deny = self.configlist('web', 'deny_' + op) |
1020 if deny and (not user or deny == ['*'] or user in deny): | 1039 if deny and (not user or deny == ['*'] or user in deny): |
1021 return False | 1040 return False |
1022 | 1041 |
1023 allow = self.repo.ui.configlist('web', 'allow_' + op) | 1042 allow = self.configlist('web', 'allow_' + op) |
1024 return (allow and (allow == ['*'] or user in allow)) or default | 1043 return (allow and (allow == ['*'] or user in allow)) or default |
1025 | 1044 |
1026 def do_unbundle(self, req): | 1045 def do_unbundle(self, req): |
1027 def bail(response, headers={}): | 1046 def bail(response, headers={}): |
1028 length = int(req.env['CONTENT_LENGTH']) | 1047 length = int(req.env['CONTENT_LENGTH']) |
1034 req.write('0\n') | 1053 req.write('0\n') |
1035 req.write(response) | 1054 req.write(response) |
1036 | 1055 |
1037 # require ssl by default, auth info cannot be sniffed and | 1056 # require ssl by default, auth info cannot be sniffed and |
1038 # replayed | 1057 # replayed |
1039 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True) | 1058 ssl_req = self.configbool('web', 'push_ssl', True) |
1040 if ssl_req: | 1059 if ssl_req: |
1041 if not req.env.get('HTTPS'): | 1060 if not req.env.get('HTTPS'): |
1042 bail(_('ssl required\n')) | 1061 bail(_('ssl required\n')) |
1043 return | 1062 return |
1044 proto = 'https' | 1063 proto = 'https' |