##// END OF EJS Templates
save settings from untrusted config files in a separate configparser...
Alexis S. L. Carvalho -
r3552:9b52239d default
parent child Browse files
Show More
@@ -50,8 +50,9 b' installed.'
50 particular repository. This file is not version-controlled, and
50 particular repository. This file is not version-controlled, and
51 will not get transferred during a "clone" operation. Options in
51 will not get transferred during a "clone" operation. Options in
52 this file override options in all other configuration files.
52 this file override options in all other configuration files.
53 On Unix, this file is only read if it belongs to a trusted user
53 On Unix, most of this file will be ignored if it doesn't belong
54 or to a trusted group.
54 to a trusted user or to a trusted group. See the documentation
55 for the trusted section below for more details.
55
56
56 SYNTAX
57 SYNTAX
57 ------
58 ------
@@ -367,11 +368,16 b' server::'
367 data transfer overhead. Default is False.
368 data transfer overhead. Default is False.
368
369
369 trusted::
370 trusted::
370 Mercurial will only read the .hg/hgrc file from a repository if
371 For security reasons, Mercurial will not use the settings in
371 it belongs to a trusted user or to a trusted group. This section
372 the .hg/hgrc file from a repository if it doesn't belong to a
372 specifies what users and groups are trusted. The current user is
373 trusted user or to a trusted group. The main exception is the
373 always trusted. To trust everybody, list a user or a group with
374 web interface, which automatically uses some safe settings, since
374 name "*".
375 it's common to serve repositories from different users.
376
377 This section specifies what users and groups are trusted. The
378 current user is always trusted. To trust everybody, list a user
379 or a group with name "*".
380
375 users;;
381 users;;
376 Comma-separated list of trusted users.
382 Comma-separated list of trusted users.
377 groups;;
383 groups;;
@@ -41,7 +41,9 b' class ui(object):'
41 self.traceback = traceback
41 self.traceback = traceback
42 self.trusted_users = {}
42 self.trusted_users = {}
43 self.trusted_groups = {}
43 self.trusted_groups = {}
44 # if ucdata is not None, its keys must be a superset of cdata's
44 self.cdata = util.configparser()
45 self.cdata = util.configparser()
46 self.ucdata = None
45 self.readconfig(util.rcpath())
47 self.readconfig(util.rcpath())
46 self.updateopts(verbose, debug, quiet, interactive)
48 self.updateopts(verbose, debug, quiet, interactive)
47 else:
49 else:
@@ -51,6 +53,8 b' class ui(object):'
51 self.trusted_users = parentui.trusted_users.copy()
53 self.trusted_users = parentui.trusted_users.copy()
52 self.trusted_groups = parentui.trusted_groups.copy()
54 self.trusted_groups = parentui.trusted_groups.copy()
53 self.cdata = dupconfig(self.parentui.cdata)
55 self.cdata = dupconfig(self.parentui.cdata)
56 if self.parentui.ucdata:
57 self.ucdata = dupconfig(self.parentui.ucdata)
54 if self.parentui.overlay:
58 if self.parentui.overlay:
55 self.overlay = dupconfig(self.parentui.overlay)
59 self.overlay = dupconfig(self.parentui.overlay)
56
60
@@ -95,7 +99,7 b' class ui(object):'
95 group = util.groupname(st.st_gid)
99 group = util.groupname(st.st_gid)
96 if user not in tusers and group not in tgroups:
100 if user not in tusers and group not in tgroups:
97 if warn:
101 if warn:
98 self.warn(_('Not reading file %s from untrusted '
102 self.warn(_('Not trusting file %s from untrusted '
99 'user %s, group %s\n') % (f, user, group))
103 'user %s, group %s\n') % (f, user, group))
100 return False
104 return False
101 return True
105 return True
@@ -108,12 +112,30 b' class ui(object):'
108 fp = open(f)
112 fp = open(f)
109 except IOError:
113 except IOError:
110 continue
114 continue
111 if not self._is_trusted(fp, f):
115 cdata = self.cdata
112 continue
116 trusted = self._is_trusted(fp, f)
117 if not trusted:
118 if self.ucdata is None:
119 self.ucdata = dupconfig(self.cdata)
120 cdata = self.ucdata
121 elif self.ucdata is not None:
122 # use a separate configparser, so that we don't accidentally
123 # override ucdata settings later on.
124 cdata = util.configparser()
125
113 try:
126 try:
114 self.cdata.readfp(fp, f)
127 cdata.readfp(fp, f)
115 except ConfigParser.ParsingError, inst:
128 except ConfigParser.ParsingError, inst:
116 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
129 msg = _("Failed to parse %s\n%s") % (f, inst)
130 if trusted:
131 raise util.Abort(msg)
132 self.warn(_("Ignored: %s\n") % msg)
133
134 if trusted:
135 if cdata != self.cdata:
136 updateconfig(cdata, self.cdata)
137 if self.ucdata is not None:
138 updateconfig(cdata, self.ucdata)
117 # override data from config files with data set with ui.setconfig
139 # override data from config files with data set with ui.setconfig
118 if self.overlay:
140 if self.overlay:
119 updateconfig(self.overlay, self.cdata)
141 updateconfig(self.overlay, self.cdata)
@@ -127,7 +149,10 b' class ui(object):'
127 self.readhooks.append(hook)
149 self.readhooks.append(hook)
128
150
129 def readsections(self, filename, *sections):
151 def readsections(self, filename, *sections):
130 "read filename and add only the specified sections to the config data"
152 """Read filename and add only the specified sections to the config data
153
154 The settings are added to the trusted config data.
155 """
131 if not sections:
156 if not sections:
132 return
157 return
133
158
@@ -143,6 +168,8 b' class ui(object):'
143 cdata.add_section(section)
168 cdata.add_section(section)
144
169
145 updateconfig(cdata, self.cdata, sections)
170 updateconfig(cdata, self.cdata, sections)
171 if self.ucdata:
172 updateconfig(cdata, self.ucdata, sections)
146
173
147 def fixconfig(self, section=None, name=None, value=None, root=None):
174 def fixconfig(self, section=None, name=None, value=None, root=None):
148 # translate paths relative to root (or home) into absolute paths
175 # translate paths relative to root (or home) into absolute paths
@@ -150,7 +177,7 b' class ui(object):'
150 if root is None:
177 if root is None:
151 root = os.getcwd()
178 root = os.getcwd()
152 items = section and [(name, value)] or []
179 items = section and [(name, value)] or []
153 for cdata in self.cdata, self.overlay:
180 for cdata in self.cdata, self.ucdata, self.overlay:
154 if not cdata: continue
181 if not cdata: continue
155 if not items and cdata.has_section('paths'):
182 if not items and cdata.has_section('paths'):
156 pathsitems = cdata.items('paths')
183 pathsitems = cdata.items('paths')
@@ -181,59 +208,98 b' class ui(object):'
181 def setconfig(self, section, name, value):
208 def setconfig(self, section, name, value):
182 if not self.overlay:
209 if not self.overlay:
183 self.overlay = util.configparser()
210 self.overlay = util.configparser()
184 for cdata in (self.overlay, self.cdata):
211 for cdata in (self.overlay, self.cdata, self.ucdata):
212 if not cdata: continue
185 if not cdata.has_section(section):
213 if not cdata.has_section(section):
186 cdata.add_section(section)
214 cdata.add_section(section)
187 cdata.set(section, name, value)
215 cdata.set(section, name, value)
188 self.fixconfig(section, name, value)
216 self.fixconfig(section, name, value)
189
217
190 def _config(self, section, name, default, funcname):
218 def _get_cdata(self, untrusted):
191 if self.cdata.has_option(section, name):
219 if untrusted and self.ucdata:
220 return self.ucdata
221 return self.cdata
222
223 def _config(self, section, name, default, funcname, untrusted, abort):
224 cdata = self._get_cdata(untrusted)
225 if cdata.has_option(section, name):
192 try:
226 try:
193 func = getattr(self.cdata, funcname)
227 func = getattr(cdata, funcname)
194 return func(section, name)
228 return func(section, name)
195 except ConfigParser.InterpolationError, inst:
229 except ConfigParser.InterpolationError, inst:
196 raise util.Abort(_("Error in configuration section [%s] "
230 msg = _("Error in configuration section [%s] "
197 "parameter '%s':\n%s")
231 "parameter '%s':\n%s") % (section, name, inst)
198 % (section, name, inst))
232 if abort:
233 raise util.Abort(msg)
234 self.warn(_("Ignored: %s\n") % msg)
199 return default
235 return default
200
236
201 def config(self, section, name, default=None):
237 def _configcommon(self, section, name, default, funcname, untrusted):
202 return self._config(section, name, default, 'get')
238 value = self._config(section, name, default, funcname,
239 untrusted, abort=True)
240 if self.debugflag and not untrusted and self.ucdata:
241 uvalue = self._config(section, name, None, funcname,
242 untrusted=True, abort=False)
243 if uvalue is not None and uvalue != value:
244 self.warn(_("Ignoring untrusted configuration option "
245 "%s.%s = %s\n") % (section, name, uvalue))
246 return value
203
247
204 def configbool(self, section, name, default=False):
248 def config(self, section, name, default=None, untrusted=False):
205 return self._config(section, name, default, 'getboolean')
249 return self._configcommon(section, name, default, 'get', untrusted)
206
250
207 def configlist(self, section, name, default=None):
251 def configbool(self, section, name, default=False, untrusted=False):
252 return self._configcommon(section, name, default, 'getboolean',
253 untrusted)
254
255 def configlist(self, section, name, default=None, untrusted=False):
208 """Return a list of comma/space separated strings"""
256 """Return a list of comma/space separated strings"""
209 result = self.config(section, name)
257 result = self.config(section, name, untrusted=untrusted)
210 if result is None:
258 if result is None:
211 result = default or []
259 result = default or []
212 if isinstance(result, basestring):
260 if isinstance(result, basestring):
213 result = result.replace(",", " ").split()
261 result = result.replace(",", " ").split()
214 return result
262 return result
215
263
216 def has_config(self, section):
264 def has_config(self, section, untrusted=False):
217 '''tell whether section exists in config.'''
265 '''tell whether section exists in config.'''
218 return self.cdata.has_section(section)
266 cdata = self._get_cdata(untrusted)
267 return cdata.has_section(section)
219
268
220 def configitems(self, section):
269 def _configitems(self, section, untrusted, abort):
221 items = {}
270 items = {}
222 if self.cdata.has_section(section):
271 cdata = self._get_cdata(untrusted)
272 if cdata.has_section(section):
223 try:
273 try:
224 items.update(dict(self.cdata.items(section)))
274 items.update(dict(cdata.items(section)))
225 except ConfigParser.InterpolationError, inst:
275 except ConfigParser.InterpolationError, inst:
226 raise util.Abort(_("Error in configuration section [%s]:\n%s")
276 msg = _("Error in configuration section [%s]:\n"
227 % (section, inst))
277 "%s") % (section, inst)
278 if abort:
279 raise util.Abort(msg)
280 self.warn(_("Ignored: %s\n") % msg)
281 return items
282
283 def configitems(self, section, untrusted=False):
284 items = self._configitems(section, untrusted=untrusted, abort=True)
285 if self.debugflag and not untrusted and self.ucdata:
286 uitems = self._configitems(section, untrusted=True, abort=False)
287 keys = uitems.keys()
288 keys.sort()
289 for k in keys:
290 if uitems[k] != items.get(k):
291 self.warn(_("Ignoring untrusted configuration option "
292 "%s.%s = %s\n") % (section, k, uitems[k]))
228 x = items.items()
293 x = items.items()
229 x.sort()
294 x.sort()
230 return x
295 return x
231
296
232 def walkconfig(self):
297 def walkconfig(self, untrusted=False):
233 sections = self.cdata.sections()
298 cdata = self._get_cdata(untrusted)
299 sections = cdata.sections()
234 sections.sort()
300 sections.sort()
235 for section in sections:
301 for section in sections:
236 for name, value in self.configitems(section):
302 for name, value in self.configitems(section, untrusted):
237 yield section, name, value.replace('\n', '\\n')
303 yield section, name, value.replace('\n', '\\n')
238
304
239 def extensions(self):
305 def extensions(self):
@@ -9,7 +9,7 b' from mercurial import ui, util'
9 hgrc = os.environ['HGRCPATH']
9 hgrc = os.environ['HGRCPATH']
10
10
11 def testui(user='foo', group='bar', tusers=(), tgroups=(),
11 def testui(user='foo', group='bar', tusers=(), tgroups=(),
12 cuser='foo', cgroup='bar', debug=False):
12 cuser='foo', cgroup='bar', debug=False, silent=False):
13 # user, group => owners of the file
13 # user, group => owners of the file
14 # tusers, tgroups => trusted users/groups
14 # tusers, tgroups => trusted users/groups
15 # cuser, cgroup => user/group of the current process
15 # cuser, cgroup => user/group of the current process
@@ -56,8 +56,18 b" def testui(user='foo', group='bar', tuse"
56 parentui.updateopts(debug=debug)
56 parentui.updateopts(debug=debug)
57 u = ui.ui(parentui=parentui)
57 u = ui.ui(parentui=parentui)
58 u.readconfig('.hg/hgrc')
58 u.readconfig('.hg/hgrc')
59 if silent:
60 return u
61 print 'trusted'
59 for name, path in u.configitems('paths'):
62 for name, path in u.configitems('paths'):
60 print ' ', name, '=', path
63 print ' ', name, '=', path
64 print 'untrusted'
65 for name, path in u.configitems('paths', untrusted=True):
66 print '.',
67 u.config('paths', name) # warning with debug=True
68 print '.',
69 u.config('paths', name, untrusted=True) # no warnings
70 print name, '=', path
61 print
71 print
62
72
63 return u
73 return u
@@ -68,6 +78,7 b" os.mkdir('.hg')"
68 f = open('.hg/hgrc', 'w')
78 f = open('.hg/hgrc', 'w')
69 f.write('[paths]\n')
79 f.write('[paths]\n')
70 f.write('local = /another/path\n\n')
80 f.write('local = /another/path\n\n')
81 f.write('interpolated = %(global)s%(local)s\n\n')
71 f.close()
82 f.close()
72
83
73 #print '# Everything is run by user foo, group bar\n'
84 #print '# Everything is run by user foo, group bar\n'
@@ -111,3 +122,83 b" testui(user='abc', group='def', tusers=["
111
122
112 print "# Can't figure out the name of the user running this process"
123 print "# Can't figure out the name of the user running this process"
113 testui(user='abc', group='def', cuser=None)
124 testui(user='abc', group='def', cuser=None)
125
126 print "# prints debug warnings"
127 u = testui(user='abc', group='def', cuser='foo', debug=True)
128
129 print "# ui.readsections"
130 filename = 'foobar'
131 f = open(filename, 'w')
132 f.write('[foobar]\n')
133 f.write('baz = quux\n')
134 f.close()
135 u.readsections(filename, 'foobar')
136 print u.config('foobar', 'baz')
137
138 print
139 print "# read trusted, untrusted, new ui, trusted"
140 u = ui.ui()
141 u.updateopts(debug=True)
142 u.readconfig(filename)
143 u2 = ui.ui(parentui=u)
144 def username(uid=None):
145 return 'foo'
146 util.username = username
147 u2.readconfig('.hg/hgrc')
148 print 'trusted:'
149 print u2.config('foobar', 'baz')
150 print u2.config('paths', 'interpolated')
151 print 'untrusted:'
152 print u2.config('foobar', 'baz', untrusted=True)
153 print u2.config('paths', 'interpolated', untrusted=True)
154
155 print
156 print "# error handling"
157
158 def assertraises(f, exc=util.Abort):
159 try:
160 f()
161 except exc, inst:
162 print 'raised', inst.__class__.__name__
163 else:
164 print 'no exception?!'
165
166 print "# file doesn't exist"
167 os.unlink('.hg/hgrc')
168 assert not os.path.exists('.hg/hgrc')
169 testui(debug=True, silent=True)
170 testui(user='abc', group='def', debug=True, silent=True)
171
172 print
173 print "# parse error"
174 f = open('.hg/hgrc', 'w')
175 f.write('foo = bar')
176 f.close()
177 testui(user='abc', group='def', silent=True)
178 assertraises(lambda: testui(debug=True, silent=True))
179
180 print
181 print "# interpolation error"
182 f = open('.hg/hgrc', 'w')
183 f.write('[foo]\n')
184 f.write('bar = %(')
185 f.close()
186 u = testui(debug=True, silent=True)
187 print '# regular config:'
188 print ' trusted',
189 assertraises(lambda: u.config('foo', 'bar'))
190 print 'untrusted',
191 assertraises(lambda: u.config('foo', 'bar', untrusted=True))
192
193 u = testui(user='abc', group='def', debug=True, silent=True)
194 print ' trusted ',
195 print u.config('foo', 'bar')
196 print 'untrusted',
197 assertraises(lambda: u.config('foo', 'bar', untrusted=True))
198
199 print '# configitems:'
200 print ' trusted ',
201 print u.configitems('foo')
202 print 'untrusted',
203 assertraises(lambda: u.configitems('foo', untrusted=True))
204
@@ -1,67 +1,212 b''
1 # same user, same group
1 # same user, same group
2 trusted
2 global = /some/path
3 global = /some/path
4 interpolated = /some/path/another/path
3 local = /another/path
5 local = /another/path
6 untrusted
7 . . global = /some/path
8 . . interpolated = /some/path/another/path
9 . . local = /another/path
4
10
5 # same user, different group
11 # same user, different group
12 trusted
6 global = /some/path
13 global = /some/path
14 interpolated = /some/path/another/path
7 local = /another/path
15 local = /another/path
16 untrusted
17 . . global = /some/path
18 . . interpolated = /some/path/another/path
19 . . local = /another/path
8
20
9 # different user, same group
21 # different user, same group
10 Not reading file .hg/hgrc from untrusted user abc, group bar
22 Not trusting file .hg/hgrc from untrusted user abc, group bar
23 trusted
11 global = /some/path
24 global = /some/path
25 untrusted
26 . . global = /some/path
27 . . interpolated = /some/path/another/path
28 . . local = /another/path
12
29
13 # different user, same group, but we trust the group
30 # different user, same group, but we trust the group
31 trusted
14 global = /some/path
32 global = /some/path
33 interpolated = /some/path/another/path
15 local = /another/path
34 local = /another/path
35 untrusted
36 . . global = /some/path
37 . . interpolated = /some/path/another/path
38 . . local = /another/path
16
39
17 # different user, different group
40 # different user, different group
18 Not reading file .hg/hgrc from untrusted user abc, group def
41 Not trusting file .hg/hgrc from untrusted user abc, group def
42 trusted
19 global = /some/path
43 global = /some/path
44 untrusted
45 . . global = /some/path
46 . . interpolated = /some/path/another/path
47 . . local = /another/path
20
48
21 # different user, different group, but we trust the user
49 # different user, different group, but we trust the user
50 trusted
22 global = /some/path
51 global = /some/path
52 interpolated = /some/path/another/path
23 local = /another/path
53 local = /another/path
54 untrusted
55 . . global = /some/path
56 . . interpolated = /some/path/another/path
57 . . local = /another/path
24
58
25 # different user, different group, but we trust the group
59 # different user, different group, but we trust the group
60 trusted
26 global = /some/path
61 global = /some/path
62 interpolated = /some/path/another/path
27 local = /another/path
63 local = /another/path
64 untrusted
65 . . global = /some/path
66 . . interpolated = /some/path/another/path
67 . . local = /another/path
28
68
29 # different user, different group, but we trust the user and the group
69 # different user, different group, but we trust the user and the group
70 trusted
30 global = /some/path
71 global = /some/path
72 interpolated = /some/path/another/path
31 local = /another/path
73 local = /another/path
74 untrusted
75 . . global = /some/path
76 . . interpolated = /some/path/another/path
77 . . local = /another/path
32
78
33 # we trust all users
79 # we trust all users
34 # different user, different group
80 # different user, different group
81 trusted
35 global = /some/path
82 global = /some/path
83 interpolated = /some/path/another/path
36 local = /another/path
84 local = /another/path
85 untrusted
86 . . global = /some/path
87 . . interpolated = /some/path/another/path
88 . . local = /another/path
37
89
38 # we trust all groups
90 # we trust all groups
39 # different user, different group
91 # different user, different group
92 trusted
40 global = /some/path
93 global = /some/path
94 interpolated = /some/path/another/path
41 local = /another/path
95 local = /another/path
96 untrusted
97 . . global = /some/path
98 . . interpolated = /some/path/another/path
99 . . local = /another/path
42
100
43 # we trust all users and groups
101 # we trust all users and groups
44 # different user, different group
102 # different user, different group
103 trusted
45 global = /some/path
104 global = /some/path
105 interpolated = /some/path/another/path
46 local = /another/path
106 local = /another/path
107 untrusted
108 . . global = /some/path
109 . . interpolated = /some/path/another/path
110 . . local = /another/path
47
111
48 # we don't get confused by users and groups with the same name
112 # we don't get confused by users and groups with the same name
49 # different user, different group
113 # different user, different group
50 Not reading file .hg/hgrc from untrusted user abc, group def
114 Not trusting file .hg/hgrc from untrusted user abc, group def
115 trusted
51 global = /some/path
116 global = /some/path
117 untrusted
118 . . global = /some/path
119 . . interpolated = /some/path/another/path
120 . . local = /another/path
52
121
53 # list of user names
122 # list of user names
54 # different user, different group, but we trust the user
123 # different user, different group, but we trust the user
124 trusted
55 global = /some/path
125 global = /some/path
126 interpolated = /some/path/another/path
56 local = /another/path
127 local = /another/path
128 untrusted
129 . . global = /some/path
130 . . interpolated = /some/path/another/path
131 . . local = /another/path
57
132
58 # list of group names
133 # list of group names
59 # different user, different group, but we trust the group
134 # different user, different group, but we trust the group
135 trusted
60 global = /some/path
136 global = /some/path
137 interpolated = /some/path/another/path
61 local = /another/path
138 local = /another/path
139 untrusted
140 . . global = /some/path
141 . . interpolated = /some/path/another/path
142 . . local = /another/path
62
143
63 # Can't figure out the name of the user running this process
144 # Can't figure out the name of the user running this process
64 # different user, different group
145 # different user, different group
146 trusted
65 global = /some/path
147 global = /some/path
148 interpolated = /some/path/another/path
66 local = /another/path
149 local = /another/path
150 untrusted
151 . . global = /some/path
152 . . interpolated = /some/path/another/path
153 . . local = /another/path
67
154
155 # prints debug warnings
156 # different user, different group
157 Not trusting file .hg/hgrc from untrusted user abc, group def
158 trusted
159 Ignoring untrusted configuration option paths.interpolated = /some/path/another/path
160 Ignoring untrusted configuration option paths.local = /another/path
161 global = /some/path
162 untrusted
163 . . global = /some/path
164 .Ignoring untrusted configuration option paths.interpolated = /some/path/another/path
165 . interpolated = /some/path/another/path
166 .Ignoring untrusted configuration option paths.local = /another/path
167 . local = /another/path
168
169 # ui.readsections
170 quux
171
172 # read trusted, untrusted, new ui, trusted
173 Not trusting file foobar from untrusted user abc, group def
174 trusted:
175 Ignoring untrusted configuration option foobar.baz = quux
176 None
177 /some/path/another/path
178 untrusted:
179 quux
180 /some/path/another/path
181
182 # error handling
183 # file doesn't exist
184 # same user, same group
185 # different user, different group
186
187 # parse error
188 # different user, different group
189 Not trusting file .hg/hgrc from untrusted user abc, group def
190 Ignored: Failed to parse .hg/hgrc
191 File contains no section headers.
192 file: .hg/hgrc, line: 1
193 'foo = bar'
194 # same user, same group
195 raised Abort
196
197 # interpolation error
198 # same user, same group
199 # regular config:
200 trusted raised Abort
201 untrusted raised Abort
202 # different user, different group
203 Not trusting file .hg/hgrc from untrusted user abc, group def
204 trusted Ignored: Error in configuration section [foo] parameter 'bar':
205 bad interpolation variable reference '%('
206 None
207 untrusted raised Abort
208 # configitems:
209 trusted Ignored: Error in configuration section [foo]:
210 bad interpolation variable reference '%('
211 []
212 untrusted raised Abort
General Comments 0
You need to be logged in to leave comments. Login now