##// END OF EJS Templates
dispatch: add HGPLAIN=+strictflags to restrict early parsing of global options...
Yuya Nishihara -
r35180:c9740b69 stable
parent child Browse files
Show More
@@ -220,8 +220,17 b' def _loadnewui(srcui, args):'
220 newui._csystem = srcui._csystem
220 newui._csystem = srcui._csystem
221
221
222 # command line args
222 # command line args
223 args = args[:]
223 options = {}
224 dispatch._parseconfig(newui, dispatch._earlygetopt(['--config'], args))
224 if srcui.plain('strictflags'):
225 options.update(dispatch._earlyparseopts(args))
226 else:
227 args = args[:]
228 options['config'] = dispatch._earlygetopt(['--config'], args)
229 cwds = dispatch._earlygetopt(['--cwd'], args)
230 options['cwd'] = cwds and cwds[-1] or ''
231 rpath = dispatch._earlygetopt(["-R", "--repository", "--repo"], args)
232 options['repository'] = rpath and rpath[-1] or ''
233 dispatch._parseconfig(newui, options['config'])
225
234
226 # stolen from tortoisehg.util.copydynamicconfig()
235 # stolen from tortoisehg.util.copydynamicconfig()
227 for section, name, value in srcui.walkconfig():
236 for section, name, value in srcui.walkconfig():
@@ -232,10 +241,9 b' def _loadnewui(srcui, args):'
232 newui.setconfig(section, name, value, source)
241 newui.setconfig(section, name, value, source)
233
242
234 # load wd and repo config, copied from dispatch.py
243 # load wd and repo config, copied from dispatch.py
235 cwds = dispatch._earlygetopt(['--cwd'], args)
244 cwd = options['cwd']
236 cwd = cwds and os.path.realpath(cwds[-1]) or None
245 cwd = cwd and os.path.realpath(cwd) or None
237 rpath = dispatch._earlygetopt(["-R", "--repository", "--repo"], args)
246 rpath = options['repository']
238 rpath = rpath and rpath[-1] or ''
239 path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
247 path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
240
248
241 return (newui, newlui)
249 return (newui, newlui)
@@ -150,6 +150,8 b' def dispatch(req):'
150 try:
150 try:
151 if not req.ui:
151 if not req.ui:
152 req.ui = uimod.ui.load()
152 req.ui = uimod.ui.load()
153 if req.ui.plain('strictflags'):
154 req.earlyoptions.update(_earlyparseopts(req.args))
153 if _earlyreqoptbool(req, 'traceback', ['--traceback']):
155 if _earlyreqoptbool(req, 'traceback', ['--traceback']):
154 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
156 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
155
157
@@ -644,6 +646,12 b' def _parseconfig(ui, config):'
644
646
645 return configs
647 return configs
646
648
649 def _earlyparseopts(args):
650 options = {}
651 fancyopts.fancyopts(args, commands.globalopts, options,
652 gnu=False, early=True)
653 return options
654
647 def _earlygetopt(aliases, args, strip=True):
655 def _earlygetopt(aliases, args, strip=True):
648 """Return list of values for an option (or aliases).
656 """Return list of values for an option (or aliases).
649
657
@@ -732,12 +740,16 b' def _earlygetopt(aliases, args, strip=Tr'
732
740
733 def _earlyreqopt(req, name, aliases):
741 def _earlyreqopt(req, name, aliases):
734 """Peek a list option without using a full options table"""
742 """Peek a list option without using a full options table"""
743 if req.ui.plain('strictflags'):
744 return req.earlyoptions[name]
735 values = _earlygetopt(aliases, req.args, strip=False)
745 values = _earlygetopt(aliases, req.args, strip=False)
736 req.earlyoptions[name] = values
746 req.earlyoptions[name] = values
737 return values
747 return values
738
748
739 def _earlyreqoptstr(req, name, aliases):
749 def _earlyreqoptstr(req, name, aliases):
740 """Peek a string option without using a full options table"""
750 """Peek a string option without using a full options table"""
751 if req.ui.plain('strictflags'):
752 return req.earlyoptions[name]
741 value = (_earlygetopt(aliases, req.args, strip=False) or [''])[-1]
753 value = (_earlygetopt(aliases, req.args, strip=False) or [''])[-1]
742 req.earlyoptions[name] = value
754 req.earlyoptions[name] = value
743 return value
755 return value
@@ -745,13 +757,15 b' def _earlyreqoptstr(req, name, aliases):'
745 def _earlyreqoptbool(req, name, aliases):
757 def _earlyreqoptbool(req, name, aliases):
746 """Peek a boolean option without using a full options table
758 """Peek a boolean option without using a full options table
747
759
748 >>> req = request([b'x', b'--debugger'])
760 >>> req = request([b'x', b'--debugger'], uimod.ui())
749 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
761 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
750 True
762 True
751
763
752 >>> req = request([b'x', b'--', b'--debugger'])
764 >>> req = request([b'x', b'--', b'--debugger'], uimod.ui())
753 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
765 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
754 """
766 """
767 if req.ui.plain('strictflags'):
768 return req.earlyoptions[name]
755 try:
769 try:
756 argcount = req.args.index("--")
770 argcount = req.args.index("--")
757 except ValueError:
771 except ValueError:
@@ -56,9 +56,17 b' HGPLAIN'
56 localization. This can be useful when scripting against Mercurial
56 localization. This can be useful when scripting against Mercurial
57 in the face of existing user configuration.
57 in the face of existing user configuration.
58
58
59 In addition to the features disabled by ``HGPLAIN=``, the following
60 values can be specified to adjust behavior:
61
62 ``+strictflags``
63 Restrict parsing of command line flags.
64
59 Equivalent options set via command line flags or environment
65 Equivalent options set via command line flags or environment
60 variables are not overridden.
66 variables are not overridden.
61
67
68 See :hg:`help scripting` for details.
69
62 HGPLAINEXCEPT
70 HGPLAINEXCEPT
63 This is a comma-separated list of features to preserve when
71 This is a comma-separated list of features to preserve when
64 HGPLAIN is enabled. Currently the following values are supported:
72 HGPLAIN is enabled. Currently the following values are supported:
@@ -74,6 +74,32 b' HGRCPATH'
74 like the username and extensions that may be required to interface
74 like the username and extensions that may be required to interface
75 with a repository.
75 with a repository.
76
76
77 Command-line Flags
78 ==================
79
80 Mercurial's default command-line parser is designed for humans, and is not
81 robust against malicious input. For instance, you can start a debugger by
82 passing ``--debugger`` as an option value::
83
84 $ REV=--debugger sh -c 'hg log -r "$REV"'
85
86 This happens because several command-line flags need to be scanned without
87 using a concrete command table, which may be modified while loading repository
88 settings and extensions.
89
90 Since Mercurial 4.4.2, the parsing of such flags may be restricted by setting
91 ``HGPLAIN=+strictflags``. When this feature is enabled, all early options
92 (e.g. ``-R/--repository``, ``--cwd``, ``--config``) must be specified first
93 amongst the other global options, and cannot be injected to an arbitrary
94 location::
95
96 $ HGPLAIN=+strictflags hg -R "$REPO" log -r "$REV"
97
98 In earlier Mercurial versions where ``+strictflags`` isn't available, you
99 can mitigate the issue by concatenating an option value with its flag::
100
101 $ hg log -r"$REV" --keyword="$KEYWORD"
102
77 Consuming Command Output
103 Consuming Command Output
78 ========================
104 ========================
79
105
@@ -761,6 +761,7 b' class ui(object):'
761
761
762 The return value can either be
762 The return value can either be
763 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
763 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
764 - False if feature is disabled by default and not included in HGPLAIN
764 - True otherwise
765 - True otherwise
765 '''
766 '''
766 if ('HGPLAIN' not in encoding.environ and
767 if ('HGPLAIN' not in encoding.environ and
@@ -768,6 +769,9 b' class ui(object):'
768 return False
769 return False
769 exceptions = encoding.environ.get('HGPLAINEXCEPT',
770 exceptions = encoding.environ.get('HGPLAINEXCEPT',
770 '').strip().split(',')
771 '').strip().split(',')
772 # TODO: add support for HGPLAIN=+feature,-feature syntax
773 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
774 exceptions.append('strictflags')
771 if feature and exceptions:
775 if feature and exceptions:
772 return feature not in exceptions
776 return feature not in exceptions
773 return True
777 return True
@@ -137,6 +137,20 b' typical client does not want echo-back m'
137 summary: 1
137 summary: 1
138
138
139
139
140 check strict parsing of early options:
141
142 >>> import os
143 >>> from hgclient import check, readchannel, runcommand
144 >>> os.environ['HGPLAIN'] = '+strictflags'
145 >>> @check
146 ... def cwd(server):
147 ... readchannel(server)
148 ... runcommand(server, ['log', '-b', '--config=alias.log=!echo pwned',
149 ... 'default'])
150 *** runcommand log -b --config=alias.log=!echo pwned default
151 abort: unknown revision '--config=alias.log=!echo pwned'!
152 [255]
153
140 check that "histedit --commands=-" can read rules from the input channel:
154 check that "histedit --commands=-" can read rules from the input channel:
141
155
142 >>> import cStringIO
156 >>> import cStringIO
@@ -113,6 +113,51 b' Shell aliases bypass any command parsing'
113 $ hg log -b '--config=alias.log=!echo howdy'
113 $ hg log -b '--config=alias.log=!echo howdy'
114 howdy
114 howdy
115
115
116 Early options must come first if HGPLAIN=+strictflags is specified:
117 (BUG: chg cherry-picks early options to pass them as a server command)
118
119 #if no-chg
120 $ HGPLAIN=+strictflags hg log -b --config='hooks.pre-log=false' default
121 abort: unknown revision '--config=hooks.pre-log=false'!
122 [255]
123 $ HGPLAIN=+strictflags hg log -b -R. default
124 abort: unknown revision '-R.'!
125 [255]
126 $ HGPLAIN=+strictflags hg log -b --cwd=. default
127 abort: unknown revision '--cwd=.'!
128 [255]
129 #endif
130 $ HGPLAIN=+strictflags hg log -b --debugger default
131 abort: unknown revision '--debugger'!
132 [255]
133 $ HGPLAIN=+strictflags hg log -b --config='alias.log=!echo pwned' default
134 abort: unknown revision '--config=alias.log=!echo pwned'!
135 [255]
136
137 $ HGPLAIN=+strictflags hg log --config='hooks.pre-log=false' -b default
138 abort: option --config may not be abbreviated!
139 [255]
140 $ HGPLAIN=+strictflags hg log -q --cwd=.. -b default
141 abort: option --cwd may not be abbreviated!
142 [255]
143 $ HGPLAIN=+strictflags hg log -q -R . -b default
144 abort: option -R has to be separated from other options (e.g. not -qR) and --repository may only be abbreviated as --repo!
145 [255]
146
147 $ HGPLAIN=+strictflags hg --config='hooks.pre-log=false' log -b default
148 abort: pre-log hook exited with status 1
149 [255]
150 $ HGPLAIN=+strictflags hg --cwd .. -q -Ra log -b default
151 0:cb9a9f314b8b
152
153 For compatibility reasons, HGPLAIN=+strictflags is not enabled by plain HGPLAIN:
154
155 $ HGPLAIN= hg log --config='hooks.pre-log=false' -b default
156 abort: pre-log hook exited with status 1
157 [255]
158 $ HGPLAINEXCEPT= hg log --cwd .. -q -Ra -b default
159 0:cb9a9f314b8b
160
116 [defaults]
161 [defaults]
117
162
118 $ hg cat a
163 $ hg cat a
General Comments 0
You need to be logged in to leave comments. Login now