##// 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 50 particular repository. This file is not version-controlled, and
51 51 will not get transferred during a "clone" operation. Options in
52 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
54 or to a trusted group.
53 On Unix, most of this file will be ignored if it doesn't belong
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 57 SYNTAX
57 58 ------
@@ -367,11 +368,16 b' server::'
367 368 data transfer overhead. Default is False.
368 369
369 370 trusted::
370 Mercurial will only read the .hg/hgrc file from a repository if
371 it belongs to a trusted user or to a trusted group. This section
372 specifies what users and groups are trusted. The current user is
373 always trusted. To trust everybody, list a user or a group with
374 name "*".
371 For security reasons, Mercurial will not use the settings in
372 the .hg/hgrc file from a repository if it doesn't belong to a
373 trusted user or to a trusted group. The main exception is the
374 web interface, which automatically uses some safe settings, since
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 381 users;;
376 382 Comma-separated list of trusted users.
377 383 groups;;
@@ -41,7 +41,9 b' class ui(object):'
41 41 self.traceback = traceback
42 42 self.trusted_users = {}
43 43 self.trusted_groups = {}
44 # if ucdata is not None, its keys must be a superset of cdata's
44 45 self.cdata = util.configparser()
46 self.ucdata = None
45 47 self.readconfig(util.rcpath())
46 48 self.updateopts(verbose, debug, quiet, interactive)
47 49 else:
@@ -51,6 +53,8 b' class ui(object):'
51 53 self.trusted_users = parentui.trusted_users.copy()
52 54 self.trusted_groups = parentui.trusted_groups.copy()
53 55 self.cdata = dupconfig(self.parentui.cdata)
56 if self.parentui.ucdata:
57 self.ucdata = dupconfig(self.parentui.ucdata)
54 58 if self.parentui.overlay:
55 59 self.overlay = dupconfig(self.parentui.overlay)
56 60
@@ -95,7 +99,7 b' class ui(object):'
95 99 group = util.groupname(st.st_gid)
96 100 if user not in tusers and group not in tgroups:
97 101 if warn:
98 self.warn(_('Not reading file %s from untrusted '
102 self.warn(_('Not trusting file %s from untrusted '
99 103 'user %s, group %s\n') % (f, user, group))
100 104 return False
101 105 return True
@@ -108,12 +112,30 b' class ui(object):'
108 112 fp = open(f)
109 113 except IOError:
110 114 continue
111 if not self._is_trusted(fp, f):
112 continue
115 cdata = self.cdata
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 126 try:
114 self.cdata.readfp(fp, f)
127 cdata.readfp(fp, f)
115 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 139 # override data from config files with data set with ui.setconfig
118 140 if self.overlay:
119 141 updateconfig(self.overlay, self.cdata)
@@ -127,7 +149,10 b' class ui(object):'
127 149 self.readhooks.append(hook)
128 150
129 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 156 if not sections:
132 157 return
133 158
@@ -143,6 +168,8 b' class ui(object):'
143 168 cdata.add_section(section)
144 169
145 170 updateconfig(cdata, self.cdata, sections)
171 if self.ucdata:
172 updateconfig(cdata, self.ucdata, sections)
146 173
147 174 def fixconfig(self, section=None, name=None, value=None, root=None):
148 175 # translate paths relative to root (or home) into absolute paths
@@ -150,7 +177,7 b' class ui(object):'
150 177 if root is None:
151 178 root = os.getcwd()
152 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 181 if not cdata: continue
155 182 if not items and cdata.has_section('paths'):
156 183 pathsitems = cdata.items('paths')
@@ -181,59 +208,98 b' class ui(object):'
181 208 def setconfig(self, section, name, value):
182 209 if not self.overlay:
183 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 213 if not cdata.has_section(section):
186 214 cdata.add_section(section)
187 215 cdata.set(section, name, value)
188 216 self.fixconfig(section, name, value)
189 217
190 def _config(self, section, name, default, funcname):
191 if self.cdata.has_option(section, name):
218 def _get_cdata(self, untrusted):
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 226 try:
193 func = getattr(self.cdata, funcname)
227 func = getattr(cdata, funcname)
194 228 return func(section, name)
195 229 except ConfigParser.InterpolationError, inst:
196 raise util.Abort(_("Error in configuration section [%s] "
197 "parameter '%s':\n%s")
198 % (section, name, inst))
230 msg = _("Error in configuration section [%s] "
231 "parameter '%s':\n%s") % (section, name, inst)
232 if abort:
233 raise util.Abort(msg)
234 self.warn(_("Ignored: %s\n") % msg)
199 235 return default
200 236
201 def config(self, section, name, default=None):
202 return self._config(section, name, default, 'get')
237 def _configcommon(self, section, name, default, funcname, untrusted):
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):
205 return self._config(section, name, default, 'getboolean')
248 def config(self, section, name, default=None, untrusted=False):
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 256 """Return a list of comma/space separated strings"""
209 result = self.config(section, name)
257 result = self.config(section, name, untrusted=untrusted)
210 258 if result is None:
211 259 result = default or []
212 260 if isinstance(result, basestring):
213 261 result = result.replace(",", " ").split()
214 262 return result
215 263
216 def has_config(self, section):
264 def has_config(self, section, untrusted=False):
217 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 270 items = {}
222 if self.cdata.has_section(section):
271 cdata = self._get_cdata(untrusted)
272 if cdata.has_section(section):
223 273 try:
224 items.update(dict(self.cdata.items(section)))
274 items.update(dict(cdata.items(section)))
225 275 except ConfigParser.InterpolationError, inst:
226 raise util.Abort(_("Error in configuration section [%s]:\n%s")
227 % (section, inst))
276 msg = _("Error in configuration section [%s]:\n"
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 293 x = items.items()
229 294 x.sort()
230 295 return x
231 296
232 def walkconfig(self):
233 sections = self.cdata.sections()
297 def walkconfig(self, untrusted=False):
298 cdata = self._get_cdata(untrusted)
299 sections = cdata.sections()
234 300 sections.sort()
235 301 for section in sections:
236 for name, value in self.configitems(section):
302 for name, value in self.configitems(section, untrusted):
237 303 yield section, name, value.replace('\n', '\\n')
238 304
239 305 def extensions(self):
@@ -9,7 +9,7 b' from mercurial import ui, util'
9 9 hgrc = os.environ['HGRCPATH']
10 10
11 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 13 # user, group => owners of the file
14 14 # tusers, tgroups => trusted users/groups
15 15 # cuser, cgroup => user/group of the current process
@@ -56,8 +56,18 b" def testui(user='foo', group='bar', tuse"
56 56 parentui.updateopts(debug=debug)
57 57 u = ui.ui(parentui=parentui)
58 58 u.readconfig('.hg/hgrc')
59 if silent:
60 return u
61 print 'trusted'
59 62 for name, path in u.configitems('paths'):
60 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 71 print
62 72
63 73 return u
@@ -68,6 +78,7 b" os.mkdir('.hg')"
68 78 f = open('.hg/hgrc', 'w')
69 79 f.write('[paths]\n')
70 80 f.write('local = /another/path\n\n')
81 f.write('interpolated = %(global)s%(local)s\n\n')
71 82 f.close()
72 83
73 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 123 print "# Can't figure out the name of the user running this process"
113 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 1 # same user, same group
2 trusted
2 3 global = /some/path
4 interpolated = /some/path/another/path
3 5 local = /another/path
6 untrusted
7 . . global = /some/path
8 . . interpolated = /some/path/another/path
9 . . local = /another/path
4 10
5 11 # same user, different group
12 trusted
6 13 global = /some/path
14 interpolated = /some/path/another/path
7 15 local = /another/path
16 untrusted
17 . . global = /some/path
18 . . interpolated = /some/path/another/path
19 . . local = /another/path
8 20
9 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 24 global = /some/path
25 untrusted
26 . . global = /some/path
27 . . interpolated = /some/path/another/path
28 . . local = /another/path
12 29
13 30 # different user, same group, but we trust the group
31 trusted
14 32 global = /some/path
33 interpolated = /some/path/another/path
15 34 local = /another/path
35 untrusted
36 . . global = /some/path
37 . . interpolated = /some/path/another/path
38 . . local = /another/path
16 39
17 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 43 global = /some/path
44 untrusted
45 . . global = /some/path
46 . . interpolated = /some/path/another/path
47 . . local = /another/path
20 48
21 49 # different user, different group, but we trust the user
50 trusted
22 51 global = /some/path
52 interpolated = /some/path/another/path
23 53 local = /another/path
54 untrusted
55 . . global = /some/path
56 . . interpolated = /some/path/another/path
57 . . local = /another/path
24 58
25 59 # different user, different group, but we trust the group
60 trusted
26 61 global = /some/path
62 interpolated = /some/path/another/path
27 63 local = /another/path
64 untrusted
65 . . global = /some/path
66 . . interpolated = /some/path/another/path
67 . . local = /another/path
28 68
29 69 # different user, different group, but we trust the user and the group
70 trusted
30 71 global = /some/path
72 interpolated = /some/path/another/path
31 73 local = /another/path
74 untrusted
75 . . global = /some/path
76 . . interpolated = /some/path/another/path
77 . . local = /another/path
32 78
33 79 # we trust all users
34 80 # different user, different group
81 trusted
35 82 global = /some/path
83 interpolated = /some/path/another/path
36 84 local = /another/path
85 untrusted
86 . . global = /some/path
87 . . interpolated = /some/path/another/path
88 . . local = /another/path
37 89
38 90 # we trust all groups
39 91 # different user, different group
92 trusted
40 93 global = /some/path
94 interpolated = /some/path/another/path
41 95 local = /another/path
96 untrusted
97 . . global = /some/path
98 . . interpolated = /some/path/another/path
99 . . local = /another/path
42 100
43 101 # we trust all users and groups
44 102 # different user, different group
103 trusted
45 104 global = /some/path
105 interpolated = /some/path/another/path
46 106 local = /another/path
107 untrusted
108 . . global = /some/path
109 . . interpolated = /some/path/another/path
110 . . local = /another/path
47 111
48 112 # we don't get confused by users and groups with the same name
49 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 116 global = /some/path
117 untrusted
118 . . global = /some/path
119 . . interpolated = /some/path/another/path
120 . . local = /another/path
52 121
53 122 # list of user names
54 123 # different user, different group, but we trust the user
124 trusted
55 125 global = /some/path
126 interpolated = /some/path/another/path
56 127 local = /another/path
128 untrusted
129 . . global = /some/path
130 . . interpolated = /some/path/another/path
131 . . local = /another/path
57 132
58 133 # list of group names
59 134 # different user, different group, but we trust the group
135 trusted
60 136 global = /some/path
137 interpolated = /some/path/another/path
61 138 local = /another/path
139 untrusted
140 . . global = /some/path
141 . . interpolated = /some/path/another/path
142 . . local = /another/path
62 143
63 144 # Can't figure out the name of the user running this process
64 145 # different user, different group
146 trusted
65 147 global = /some/path
148 interpolated = /some/path/another/path
66 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