| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The LUCI Authors. All rights reserved. | 2 # Copyright 2015 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 """Tests that recipes are on their best behavior. | 6 """Tests that recipes are on their best behavior. |
| 7 | 7 |
| 8 Checks that recipes only import modules from a whitelist. Imports are | 8 Checks that recipes only import modules from a whitelist. Imports are |
| 9 generally not safe in recipes if they depend on the platform, since | 9 generally not safe in recipes if they depend on the platform, since |
| 10 e.g. you can run a recipe simulation for a Windows recipe on Linux. | 10 e.g. you can run a recipe simulation for a Windows recipe on Linux. |
| 11 """ | 11 """ |
| 12 | 12 |
| 13 # TODO(luqui): Implement lint for recipe modules also. | 13 # TODO(luqui): Implement lint for recipe modules also. |
| 14 | 14 |
| 15 from __future__ import absolute_import | 15 from __future__ import absolute_import |
| 16 import re | 16 import re |
| 17 import types | 17 import types |
| 18 | 18 |
| 19 | 19 |
| 20 MODULES_WHITELIST = [ | 20 MODULES_WHITELIST = [ |
| 21 r'ast', |
| 21 r'base64', | 22 r'base64', |
| 22 r'collections', | 23 r'collections', |
| 23 r'contextlib', | 24 r'contextlib', |
| 24 r'copy', | 25 r'copy', |
| 25 r'datetime', | 26 r'datetime', |
| 26 r'functools', | 27 r'functools', |
| 27 r'hashlib', | 28 r'hashlib', |
| 28 r'itertools', | 29 r'itertools', |
| 29 r'json', | 30 r'json', |
| 30 r'math', | 31 r'math', |
| 31 r're', | 32 r're', |
| 32 r'urlparse', | 33 r'urlparse', |
| 33 r'zlib', | 34 r'zlib', |
| 34 ] | 35 ] |
| 35 | 36 |
| 36 | 37 |
| 37 class ImportViolationError(Exception): | |
| 38 pass | |
| 39 | |
| 40 | |
| 41 class TestFailure(Exception): | |
| 42 pass | |
| 43 | |
| 44 | |
| 45 def ImportsTest(recipe_path, recipe_name, whitelist, universe_view): | 38 def ImportsTest(recipe_path, recipe_name, whitelist, universe_view): |
| 46 """Tests that recipe_name only uses allowed imports. | 39 """Tests that recipe_name only uses allowed imports. |
| 47 | 40 |
| 48 Returns a list of errors, or an empty list if there are no errors (duh). | 41 Returns a list of errors, or an empty list if there are no errors (duh). |
| 49 """ | 42 """ |
| 50 | 43 |
| 51 recipe = universe_view.load_recipe(recipe_name) | 44 recipe = universe_view.load_recipe(recipe_name) |
| 52 for _, val in sorted(recipe.globals.iteritems()): | 45 for _, val in sorted(recipe.globals.iteritems()): |
| 53 if isinstance(val, types.ModuleType): | 46 if isinstance(val, types.ModuleType): |
| 54 module_name = val.__name__ | 47 module_name = val.__name__ |
| (...skipping 27 matching lines...) Expand all Loading... |
| 82 universe_view = loader.UniverseView(universe, package_deps.root_package) | 75 universe_view = loader.UniverseView(universe, package_deps.root_package) |
| 83 | 76 |
| 84 whitelist = map(re.compile, MODULES_WHITELIST + args.whitelist) | 77 whitelist = map(re.compile, MODULES_WHITELIST + args.whitelist) |
| 85 | 78 |
| 86 errors = [] | 79 errors = [] |
| 87 for recipe_path, recipe_name in universe_view.loop_over_recipes(): | 80 for recipe_path, recipe_name in universe_view.loop_over_recipes(): |
| 88 errors.extend( | 81 errors.extend( |
| 89 ImportsTest(recipe_path, recipe_name, whitelist, universe_view)) | 82 ImportsTest(recipe_path, recipe_name, whitelist, universe_view)) |
| 90 | 83 |
| 91 if errors: | 84 if errors: |
| 92 raise TestFailure('\n'.join(map(str, errors))) | 85 for line in map(str, errors): |
| 86 print line |
| 87 return 1 |
| 88 return 0 |
| OLD | NEW |