def test_insert_on_long_tuple(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type( UniversalLupleType( [ IntegerType(), IntegerType() ], IntegerType() ) ) foo.insert(0, 2) self.assertEqual(list(foo), [ 2, 4, 6, 8 ])
def list_template_op(values): for v in values: check_is_opcode(v) return PythonObject( { "opcode": "template", "opcodes": PythonList( [PythonList([literal_op(i), v]) for i, v in enumerate(values)]) }, debug_reason="code")
def test_misc2(self): # Came up writing test_misc1 PythonObject({ "local": PythonList([ 39, 3 ]) }, bind=UniversalObjectType({ "local": UniversalTupleType([ IntegerType(), IntegerType() ]) }, wildcard_type=RICH_READONLY_TYPE))
def test_basic_tuple_of_ints(self): foo = PythonList([ 1, 2, 3 ]) get_manager(foo).add_composite_type(UniversalTupleType([ IntegerType(), IntegerType(), IntegerType() ])) foo[0] = 42 self.assertEqual(foo[0], 42)
def test_basic_list_of_ints(self): foo = PythonList([ 1, 2, 3 ]) get_manager(foo).add_composite_type(UniversalListType(IntegerType())) foo[0] = 42 self.assertEqual(foo[0], 42)
def test_outside_tuple_access_allowed(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalListType(AnyType())) self.assertEqual(foo[2], 8) foo[2] = "hello" self.assertEqual(foo[2], "hello")
def object_template_op(values, debug_reason="code", **kwargs): values_list = [] for k, v in values.items(): check_is_opcode(v) if isinstance(k, basestring): k = literal_op(k) values_list.append(PythonList([k, v])) return PythonObject(spread_dict( { "opcode": "template", "opcodes": PythonList(values_list, debug_reason=debug_reason) }, **kwargs), debug_reason=debug_reason)
def test_outside_tuple_access_blocked(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalTupleType([ IntegerType(), AnyType() ])) with self.assertRaises(IndexError): foo[2] with self.assertRaises(IndexError): foo[2] = "hello"
def comma_op(*opcodes): for v in opcodes: check_is_opcode(v) return PythonObject({ "opcode": "comma", "opcodes": PythonList(opcodes) }, debug_reason="code")
def test_misc1(self): # Came up testing lockdown local variable binding PythonObject({ "local": PythonList([ 39, 3 ]), "types": PythonObject({}) }, bind=UniversalObjectType({ "local": UniversalTupleType([ IntegerType(), IntegerType() ]), "types": DEFAULT_READONLY_COMPOSITE_TYPE }, wildcard_type=RICH_READONLY_TYPE))
def test_1(self): code = parse(""" function(int foo) => int { return foo; } """, debug=True) _, result = bootstrap_function(code, argument=PythonList([5])) self.assertEqual(result.value, 5)
def test_3(self): code = parse(""" function(any foo) { return foo + 3; } """, debug=True) _, result = bootstrap_function(code, argument=PythonList([5]), check_safe_exit=False) self.assertEqual(result.value, 8)
def match_op(value_expression, matchers): check_is_opcode(value_expression) for matcher in matchers: check_is_opcode(matcher) return PythonObject( { "opcode": "match", "value": value_expression, "matchers": PythonList(matchers) }, debug_reason="code")
def test_mixed_type_tuple(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalTupleType([ IntegerType(), AnyType() ])) with self.assertRaises(TypeError): foo[0] = "hello" self.assertEqual(foo[0], 4) foo[1] = "what" self.assertEqual(foo[1], "what")
def test_2(self): code = parse(""" function(any foo) { return foo.bar; } """, debug=True) _, result = bootstrap_function( code, argument=PythonList([ PythonObject({"bar": 5}, bind=DEFAULT_READONLY_COMPOSITE_TYPE) ]), check_safe_exit=False) self.assertEqual(result.value, 5)
def test_add_locals(self): func = function_lit( no_value_type(), build_break_types(int_type()), return_op( addition_op( dereference_op( dereference_op( dereference_op(context_op(), literal_op("outer"), True), literal_op("local"), True), literal_op(0), True), dereference_op( dereference_op( dereference_op(context_op(), literal_op("outer"), True), literal_op("local"), True), literal_op(1), True)))) context = PythonObject( { "local": PythonList([39, 3]), "_types": PythonObject({ "local": UniversalTupleType([IntegerType(), IntegerType()]) }) }, bind=UniversalObjectType({ "local": UniversalTupleType([IntegerType(), IntegerType()]), "_types": DEFAULT_READONLY_COMPOSITE_TYPE })) _, result = bootstrap_function(func, outer_context=context) self.assertEquals(result.caught_break_mode, "return") self.assertEquals(result.value, 42)
def build_closed_function_type(data): if not isinstance(data._get("break_types"), Universal): raise FatalError() for mode, break_types in data._get("break_types")._items(): if not isinstance(mode, basestring): raise FatalError() if not isinstance(break_types, Universal): raise FatalError() for break_type in break_types._values(): if not isinstance(break_type, Universal): raise FatalError(break_type) return ClosedFunctionType( enrich_type(data._get("argument")), PythonDict( { mode: PythonList( [enrich_break_type(b) for b in break_types._values()]) for mode, break_types in data._get("break_types")._items() }, bind=DEFAULT_READONLY_COMPOSITE_TYPE, debug_reason="type"))
def test_simple_list_assignment(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalListType(IntegerType()))
def test_set_on_non_sparse_blocked(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalListType(IntegerType(), is_sparse=False)) with self.assertRaises(IndexError): foo[4] = 12
def test_incorrect_type_blocked(self): foo = PythonList([ 4, 6, 8 ]) with self.assertRaises(Exception): get_manager(foo).add_composite_type(UniversalListType(StringType()))
def prepare(data, outer_context, frame_manager, immediate_context=None): if not isinstance(data, Composite): raise FatalError() get_manager(data).add_composite_type(DEFAULT_READONLY_COMPOSITE_TYPE) if not hasattr(data, "code"): raise PreparationException("Code missing from function") actual_break_types_factory = BreakTypesFactory(None) context = Universal(True, initial_wrapped={"prepare": outer_context}, bind=DEFAULT_READONLY_COMPOSITE_TYPE, debug_reason="static-prepare-context") static = evaluate( enrich_opcode( data.static, combine(type_conditional_converter, UnboundDereferenceBinder(context))), context, frame_manager) get_manager(static).add_composite_type(DEFAULT_READONLY_COMPOSITE_TYPE) argument_type = enrich_type(static._get("argument")) outer_type = enrich_type(static._get("outer")) suggested_argument_type = suggested_outer_type = None if immediate_context: suggested_argument_type = immediate_context.get( "suggested_argument_type", None) suggested_outer_type = immediate_context.get("suggested_outer_type", None) if suggested_outer_type is None: suggested_outer_type = NoValueType() try: argument_type = prepare_piece_of_context(argument_type, suggested_argument_type) except DanglingInferredType: raise PreparationException( "Failed to infer argument types in {} from {}".format( argument_type, suggested_argument_type)) try: outer_type = prepare_piece_of_context(outer_type, suggested_outer_type) except DanglingInferredType: raise PreparationException( "Failed to infer outer types in {} from {}".format( argument_type, suggested_argument_type)) local_type = enrich_type(static._get("local")) context = Universal( True, initial_wrapped={ "prepare": outer_context, "static": static, # "types": derich_type( # UniversalObjectType({ # "outer": outer_type, # "argument": argument_type, ## "local": local_type # }), {} # ), "_types": Universal( True, initial_wrapped={ "outer": outer_type, "argument": argument_type, # "local": local_type }, debug_reason="local-prepare-context") }, bind=DEFAULT_READONLY_COMPOSITE_TYPE, debug_reason="local-prepare-context") # optimization to avoid generating context_type lazily get_manager(context)._context_type = UniversalObjectType( { "outer": outer_type, "argument": argument_type, # "local": local_type }, wildcard_type=AnyType(), name="local-prepare-context-type") local_initializer = enrich_opcode( data.local_initializer, combine(type_conditional_converter, UnboundDereferenceBinder(context))) actual_local_type, local_other_break_types = get_expression_break_types( local_initializer, context, frame_manager) get_manager(context).remove_composite_type(DEFAULT_READONLY_COMPOSITE_TYPE) if actual_local_type is MISSING: raise PreparationException( "Actual local type missing. local_other_break_types: {}".format( local_other_break_types)) actual_local_type = flatten_out_types(actual_local_type) local_type = prepare_piece_of_context(local_type, actual_local_type) local_type_reasoner = Reasoner() if not local_type.is_copyable_from(actual_local_type, local_type_reasoner): raise PreparationException("Invalid local type: {} != {}: {}".format( local_type, actual_local_type, local_type_reasoner.to_message())) actual_break_types_factory.merge(local_other_break_types) declared_break_types = PythonDict({ mode: PythonList([ enrich_break_type(break_type) for break_type in break_types._values() ]) for mode, break_types in static._get("break_types")._items() }) get_manager(declared_break_types).add_composite_type( DEFAULT_READONLY_COMPOSITE_TYPE) context = Universal( True, initial_wrapped={ "prepare": outer_context, "static": static, # "types": derich_type( # UniversalObjectType({ # "outer": outer_type, # "argument": argument_type, # "local": local_type # }), {} # ), "_types": Universal(True, initial_wrapped={ "outer": outer_type, "argument": argument_type, "local": local_type }, debug_reason="code-prepare-context") }, bind=DEFAULT_READONLY_COMPOSITE_TYPE, debug_reason="code-prepare-context") get_manager(context)._context_type = UniversalObjectType( { "outer": outer_type, "argument": argument_type, "local": local_type }, wildcard_type=AnyType(), name="code-prepare-context-type") code = enrich_opcode( data.code, combine(type_conditional_converter, UnboundDereferenceBinder(context))) code_break_types = code.get_break_types(context, frame_manager) get_manager(context).remove_composite_type(DEFAULT_READONLY_COMPOSITE_TYPE) actual_break_types_factory.merge(code_break_types) final_declared_break_types = BreakTypesFactory(None) for mode, actual_break_types in actual_break_types_factory.build().items(): for actual_break_type in actual_break_types: declared_break_types_for_mode = declared_break_types.get( mode, declared_break_types.get("wildcard", [])) for declared_break_type_for_mode in declared_break_types_for_mode: # Check if this declared_break_type_for_mode is enough to capture the actual_break_types declared_out = declared_break_type_for_mode["out"] declared_in = declared_break_type_for_mode.get("in", None) actual_out = actual_break_type["out"] actual_in = actual_break_type.get("in", None) final_out = prepare_lhs_type(declared_out, actual_out) if declared_in is not None: if isinstance(declared_in, InferredType) and actual_in is None: final_in = None else: final_in = prepare_lhs_type(declared_in, actual_in) else: final_in = declared_in # if declared_in is not None and actual_in is None: # continue out_is_compatible = final_out.is_copyable_from( actual_out, DUMMY_REASONER) in_is_compatible = final_in is None or actual_in.is_copyable_from( final_in, DUMMY_REASONER) if out_is_compatible and in_is_compatible: final_declared_break_types.add(mode, final_out, final_in) break else: raise PreparationException( """Nothing declared for {}, {}.\nFunction declares break types {}.\nBut local_initialization breaks {}, code breaks {}""" .format(mode, actual_break_type, declared_break_types, local_other_break_types, code_break_types)) return OpenFunction(data, code, outer_context, static, argument_type, outer_type, local_type, local_initializer, final_declared_break_types.build())
def test_insert_at_start(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalListType(IntegerType())) foo.insert(0, 2) self.assertEqual(list(foo), [ 2, 4, 6, 8 ])
def test_combined_const_list_and_tuple(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalLupleType([ IntegerType(), AnyType() ], Const(AnyType()))) self.assertEqual(foo[2], 8)
def test_list_set_and_get(self): foo = PythonList([ 123 ]) foo._set(0, 42) self.assertEqual(foo._get(0), 42)
def test_list_insert(self): foo = PythonList([ 123 ]) foo._insert(0, 42) self.assertEqual(foo._to_list(), [ 42, 123 ])
def test_list_appending_blocked(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalTupleType([])) with self.assertRaises(IndexError): foo.append(10)
def test_list_modification_right_type_ok(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalListType(IntegerType())) foo.append(10)
def test_bounds_enforced(self): foo = PythonList([ 1, 2 ]) with self.assertRaises(Exception): get_manager(foo).add_composite_type(UniversalTupleType([ IntegerType(), IntegerType(), IntegerType() ]))
def get_manager(obj, trigger=None): """ Returns the run time CompositeManager for an object (if it needs one!) CompositeManager objects are needed for objects that can have CompositeTypes. In Lockdown, this is Python Lists, Objects, Dictionaries, Tuples, etc CompositeManagers exist alongside the base Python objects for their lifetime. They are GCed when the Python object is GCed. When code interacts with these base Python objects, they will often use the CompositeManager to determine what interactions are allowed or not, to maintain consistency with the rest of the Lockdown application. The CompositeManager maintains a weak ref to the object for which it is responsible. """ manager = managers_by_object_id.get(id(obj), None) if manager: return manager if isinstance(obj, InternalMarker): return None from lockdown.type_system.composites import Composite if not isinstance(obj, (list, tuple, dict, Composite)) and not hasattr( obj, "__dict__"): return None if isinstance(obj, Type): return None from lockdown.executor.function import LockdownFunction, OpenFunction if isinstance(obj, (LockdownFunction, OpenFunction)): return None from lockdown.executor.opcodes import Opcode if isinstance(obj, Opcode): return None from lockdown.type_system.composites import CompositeObjectManager old_obj = obj if isinstance(obj, Composite): manager = CompositeObjectManager(obj, obj_cleared_callback, False) elif isinstance(obj, list): if not get_environment().consume_python_objects: raise FatalError() from lockdown.type_system.universal_type import PythonList obj = PythonList(obj) replace_all_refs(old_obj, obj) manager = CompositeObjectManager(obj, obj_cleared_callback, False) elif isinstance(obj, tuple): if not get_environment().consume_python_objects: raise FatalError() from lockdown.type_system.universal_type import PythonList obj = PythonList(obj) replace_all_refs(old_obj, obj) manager = CompositeObjectManager(obj, obj_cleared_callback, False) elif isinstance(obj, dict): if not get_environment().consume_python_objects: raise FatalError() from lockdown.type_system.universal_type import PythonDict obj = PythonDict(obj, debug_reason="monkey-patch") replace_all_refs(old_obj, obj) manager = CompositeObjectManager(obj, obj_cleared_callback, True) elif isinstance(obj, object) and hasattr(obj, "__dict__"): if not get_environment().consume_python_objects: raise FatalError() from lockdown.type_system.universal_type import PythonObject original_type = obj.__class__ new_type = type( "Lockdown{}".format(original_type.__name__).encode("utf-8"), ( PythonObject, original_type, ), {}) obj = new_type(obj.__dict__) replace_all_refs(old_obj, obj) manager = CompositeObjectManager(obj, obj_cleared_callback, True) manager.debug_reason = "monkey-patch" else: raise FatalError() managers_by_object_id[id(obj)] = manager return manager
def test_list_modification_wrong_type_blocked(self): foo = PythonList([ 4, 6, 8 ]) get_manager(foo).add_composite_type(UniversalListType(IntegerType())) with self.assertRaises(TypeError): foo.append("hello")