Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f443629

Browse files
committed
Some axes.prop_cycler param improvements.
* Restricted prop_cycler to only have cycler() available * Added tests for both nominal and fail cases. * Even added some potential malicious strings to test fail.
1 parent 98fb775 commit f443629

2 files changed

Lines changed: 63 additions & 9 deletions

File tree

lib/matplotlib/rcsetup.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,30 @@ def validate_cycler(s):
323323
if isinstance(s, six.string_types):
324324
try:
325325
# TODO: We might want to rethink this...
326-
cycler_inst = eval(s, {'cycler': cycler, 'Cycler': Cycler})
326+
# While I think I have it quite locked down,
327+
# it is execution of arbitrary code without
328+
# sanitation.
329+
# Combine this with the possibility that rcparams
330+
# might come from the internet (future plans), this
331+
# could be downright dangerous.
332+
# I locked it down by only having the 'cycler()' function
333+
# available. Imports and defs should not
334+
# be possible. However, it is entirely possible that
335+
# a security hole could open up via attributes to the
336+
# function (this is why I decided against allowing the
337+
# Cycler class object just to reduce the number of
338+
# degrees of freedom (but maybe it is safer to use?).
339+
# One possible hole I can think of (in theory) is if
340+
# someone managed to hack the cycler module. But, if
341+
# someone does that, this wouldn't make anything
342+
# worse because we have to import the module anyway.
343+
s = eval(s, {'cycler': cycler})
327344
except BaseException as e:
328345
raise ValueError("'%s' is not a valid cycler construction: %s" %
329346
(s, e))
330-
elif isinstance(s, Cycler):
347+
# Should make sure what comes from the above eval()
348+
# is a Cycler object.
349+
if isinstance(s, Cycler):
331350
cycler_inst = s
332351
else:
333352
raise ValueError("object was not a string or Cycler instance: %s" % s)

lib/matplotlib/tests/test_rcparams.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import sys
99
import warnings
1010

11+
from cycler import cycler, Cycler
12+
1113
import matplotlib as mpl
1214
import matplotlib.pyplot as plt
1315
from matplotlib.tests import assert_str_equal
@@ -22,7 +24,8 @@
2224
validate_stringlist,
2325
validate_bool,
2426
validate_nseq_int,
25-
validate_nseq_float)
27+
validate_nseq_float,
28+
validate_cycler)
2629

2730

2831
mpl.rc('text', usetex=False)
@@ -238,13 +241,14 @@ def test_Issue_1713():
238241
del os.environ['LANG']
239242
assert rc.get('timezone') == 'UTC'
240243

241-
if __name__ == '__main__':
242-
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)
243-
244244

245245
def _validation_test_helper(validator, arg, target):
246246
res = validator(arg)
247-
assert_equal(res, target)
247+
if not isinstance(target, Cycler):
248+
assert_equal(res, target)
249+
else:
250+
# Cyclers can't simply be asserted equal. They don't implement __eq__
251+
assert_equal(list(res), list(target))
248252

249253

250254
def _validation_fail_helper(validator, arg, exception_type):
@@ -293,8 +297,35 @@ def test_validators():
293297
for _ in ('aardvark', ('a', 1),
294298
(1, 2, 3)
295299
))
296-
}
297-
300+
},
301+
{'validator': validate_cycler,
302+
'success': (('cycler("color", "rgb")',
303+
cycler("color", 'rgb')),
304+
(cycler('linestyle', ['-', '--']),
305+
cycler('linestyle', ['-', '--'])),
306+
("""(cycler("color", ["r", "g", "b"]) +
307+
cycler("mew", [2, 3, 5]))""",
308+
cycler("color", 'rgb') + cycler("mew", [2, 3, 5])),
309+
),
310+
# This is *so* incredibly important: validate_cycler() eval's
311+
# an arbitrary string! I think I have it locked down enough,
312+
# and that is what this is testing.
313+
# TODO: Note that these tests are actually insufficient, as it may
314+
# be that they raised errors, but still did an action prior to
315+
# raising the exception. We should devise some additional tests
316+
# for that...
317+
'fail': ((4, ValueError), # Gotta be a string or Cycler object
318+
('cycler("bleh, [])', ValueError), # syntax error
319+
('Cycler("linewidth", [1, 2, 3])',
320+
ValueError), # only 'cycler()' function is allowed
321+
('1 + 2', ValueError), # doesn't produce a Cycler object
322+
('os.system("echo Gotcha")', ValueError), # os not available
323+
('import os', ValueError), # should not be able to import
324+
('def badjuju(a): return a; badjuju(cycler("color", "rgb"))',
325+
ValueError), # Should not be able to define anything
326+
# even if it does return a cycler
327+
)
328+
},
298329
)
299330

300331
for validator_dict in validation_tests:
@@ -331,3 +362,7 @@ def test_rcparams_reset_after_fail():
331362
pass
332363

333364
assert mpl.rcParams['text.usetex'] is False
365+
366+
367+
if __name__ == '__main__':
368+
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

0 commit comments

Comments
 (0)