def test_load_function_definition(self,
                                      os_path_exists: unittest.mock._patch,
                                      open: unittest.mock._patch) -> None:
        arguments = MagicMock()
        arguments.as_view_base = ["BaseView"]

        def _os_path_exists_implementation(path: str) -> bool:
            return path in ["urls.py", "module.py"]

        os_path_exists.side_effect = _os_path_exists_implementation
        open.side_effect = _open_implementation({
            "module.py":
            textwrap.dedent("""
                    A = 1
                    def function():
                        pass
                    async def async_function():
                        pass
                    def nesting():
                        def nested():
                            pass
                    class Class:
                        def method(self):
                            pass
                    class View(BaseView):
                        pass
                    """)
        })
        definition = generate_taint_models._load_function_definition(
            arguments, "module.unknown")
        self.assertIsNone(definition)

        definition = generate_taint_models._load_function_definition(
            arguments, "module.function")
        self.assertIsNotNone(definition)
        self.assertEqual(definition.name, "function")

        definition = generate_taint_models._load_function_definition(
            arguments, "module.async_function")
        self.assertIsNotNone(definition)
        self.assertEqual(definition.name, "async_function")

        definition = generate_taint_models._load_function_definition(
            arguments, "module.nesting.nested")
        self.assertIsNone(definition)

        definition = generate_taint_models._load_function_definition(
            arguments, "module.Class.method")
        self.assertIsNotNone(definition)
        self.assertEqual(definition.name, "method")

        definition = generate_taint_models._load_function_definition(
            arguments, "module.View.as_view")
        self.assertIsNotNone(definition)
        self.assertEqual(definition.name, "as_view")

        definition = generate_taint_models._load_function_definition(
            arguments, "module.View.other")
        self.assertIsNone(definition)
    def test_get_REST_api_sources(self, os_path_exists: unittest.mock._patch,
                                  open: unittest.mock._patch) -> None:

        open.side_effect = _open_implementation({
            "urls.py":
            textwrap.dedent("""
                    url(r"derp", "module.view.function")
                    url(r"derp", "module.view.unannotated")
                    """),
            "module/view.py":
            textwrap.dedent("""
                    def function(request: derp.HttpRequest, other: int) -> HttpResponse:
                        pass
                    def unannotated(request, other, *starred):
                        pass
                    def unrelated() -> None: pass
                """),
        })
        arguments = MagicMock()
        arguments.urls_path = "urls.py"
        arguments.graphql_path = None
        arguments.whitelisted_class = ["HttpRequest"]
        models = generate_taint_models._get_REST_api_sources(arguments)
        self.assertSetEqual(
            models,
            {
                "def module.view.function(request, other: "
                "TaintSource[UserControlled]): ...",
                "def module.view.unannotated("
                "request: TaintSource[UserControlled], "
                "other: TaintSource[UserControlled], "
                "*starred): ...",
            },
        )
Пример #3
0
 def test_get_exit_nodes(
     self, os_path_exists: unittest.mock._patch, open: unittest.mock._patch
 ) -> None:
     open.side_effect = _open_implementation(
         {
             "urls.py": """url(r"derp", "module.view.function")""",
             "module/view.py": textwrap.dedent(
                 """
                 def function(request: HttpRequest, **kwargs) -> HttpResponse:
                     pass
                 def unrelated() -> None: pass
             """
             ),
         }
     )
     arguments = MagicMock()
     arguments.urls_path = "urls.py"
     models = generate_taint_models._get_exit_nodes(arguments)
     self.assertSetEqual(
         models,
         {
             "def module.view.function(request, **kwargs)"
             " -> TaintSink[ReturnedToUser]: ..."
         },
     )
    def test_visit_views(self, os_path_exists: unittest.mock._patch,
                         open: unittest.mock._patch) -> None:
        arguments = MagicMock()
        arguments.as_view_base = []

        # Simple `url()` call.
        with patch(
                "tools.pyre.tools.generate_taint_models.generate_taint_models."
                "_load_function_definition") as load_function_definition:
            open.side_effect = _open_implementation({
                "urls.py":
                textwrap.dedent("""
                        async def local(request): pass

                        url(r"^p-ng/?$", "module.views.function")
                        url(r"^p-ng/?$", module.views.imported)
                        url(r"^p-ng/?$", local)
                        """)
            })
            generate_taint_models._visit_views(arguments, ".", "urls.py",
                                               lambda *_: None)
            load_function_definition.assert_has_calls(
                [
                    call(arguments, "module.views.function"),
                    call(arguments, "module.views.imported"),
                ],
                any_order=True,
            )

        # Simple `url()` call to method.
        with patch(
                "tools.pyre.tools.generate_taint_models.generate_taint_models."
                "_load_function_definition") as load_function_definition:
            open.side_effect = _open_implementation({
                "urls.py":
                textwrap.dedent("""
                        from module.view import Class
                        url(r"^p-ng/?$", Class.method)
                        """)
            })
            generate_taint_models._visit_views(arguments, ".", "urls.py",
                                               lambda *_: None)
            load_function_definition.assert_called_once_with(
                arguments, "module.view.Class.method")

        # Multiple `url()` calls.
        with patch(
                "tools.pyre.tools.generate_taint_models.generate_taint_models."
                "_load_function_definition") as load_function_definition:
            open.side_effect = _open_implementation({
                "urls.py":
                textwrap.dedent("""
                        url(r"^p-ng/?$", "some.view")
                        url(r"^p-ng/?$", "some.other.view")
                        """)
            })
            generate_taint_models._visit_views(arguments, ".", "urls.py",
                                               lambda *_: None)
            load_function_definition.assert_has_calls(
                [
                    call(arguments, "some.view"),
                    call(arguments, "some.other.view")
                ],
                any_order=True,
            )

        # Simple `include()` call.
        with patch(
                "tools.pyre.tools.generate_taint_models.generate_taint_models."
                "_load_function_definition") as load_function_definition:
            open.side_effect = _open_implementation({
                "urls.py":
                """url(r"derp", include("indirect.urls"))""",
                "indirect/urls.py":
                textwrap.dedent("""
                        url(r"^p-ng/?$", "indirect.view")
                        """),
            })
            generate_taint_models._visit_views(arguments, ".", "urls.py",
                                               lambda *_: None)
            load_function_definition.assert_called_once_with(
                arguments, "indirect.view")

        # Recursive `include()` calls.
        with patch(
                "tools.pyre.tools.generate_taint_models.generate_taint_models."
                "_load_function_definition") as load_function_definition:
            open.side_effect = _open_implementation({
                "urls.py":
                """url(r"derp", include("indirect.urls"))""",
                "indirect/urls.py":
                textwrap.dedent("""
                        url(r"^p-ng/?$", include("admin.admin_urls"))
                        """),
                "admin/admin_urls.py":
                textwrap.dedent("""
                        url(r"^p-ng/?$", "admin.view")
                        """),
            })
            generate_taint_models._visit_views(arguments, ".", "urls.py",
                                               lambda *_: None)
            load_function_definition.assert_called_once_with(
                arguments, "admin.view")

        # Patterns.
        with patch(
                "tools.pyre.tools.generate_taint_models.generate_taint_models."
                "_load_function_definition") as load_function_definition:
            open.side_effect = _open_implementation({
                "urls.py":
                textwrap.dedent("""
                        from module import Class
                        patterns(
                            "base",
                            (r"derp", "first_view"),
                            (r"derp", "second_view", { "extra": "information"}),
                            (r"derp", Class.method),
                            (r"derp", include("indirect.urls")),
                            url(r"derp", "from_url")
                        )
                        patterns("", (r"derp", "absolute.module.path"))
                        """),
                "indirect/urls.py":
                textwrap.dedent("""
                        url(r"derp", "indirect.view.function")
                        """),
            })
            generate_taint_models._visit_views(arguments, ".", "urls.py",
                                               lambda *_: None)
            load_function_definition.assert_has_calls(
                [
                    call(arguments, "base.first_view"),
                    call(arguments, "base.second_view"),
                    call(arguments, "absolute.module.path"),
                    call(arguments, "indirect.view.function"),
                    call(arguments, "base.from_url"),
                ],
                any_order=True,
            )
    def test_get_graphql_sources(
        self,
        os_path_exists: unittest.mock._patch,
        open: unittest.mock._patch,
        path_iter: unittest.mock._patch,
    ) -> None:

        custom_objects = MagicMock()
        # pyre-ignore: repr needs to be overridden in this fashion.
        custom_objects.__repr__ = lambda self: "custom/custom_objects.py"
        custom_objects.is_file = lambda: True
        path_iter.side_effect = [[custom_objects]] * 10

        arguments = MagicMock()
        arguments.urls_path = "urls.py"
        arguments.graphql_path = "custom_objects.py"
        arguments.whitelisted_class = ["HttpRequest"]

        # 'GraphQLField' direct nested within 'GraphQLObjectType'
        open.side_effect = _open_implementation({
            "custom/custom_objects.py":
            textwrap.dedent("""
                    from graphql.type import (
                        GraphQLBoolean,
                        GraphQLField,
                        GraphQLObjectType
                    )
                    from fetchers.user import function, unannotated, unrelated
                    GraphUserType = GraphQLObjectType(
                        name="GraphUser",
                        description="User in Instagram Graph",
                        fields={
                            "field1": GraphQLField(GraphQLBoolean,
                                                   resolver=function),
                            "field2": GraphQLField(GraphQLBoolean,
                                                   resolver=unannotated),
                        }
                    )
                    """),
            "fetchers/user.py":
            textwrap.dedent("""
                    def function(obj: None, info: ResolveInfo, **args) -> bool:
                        pass
                    def unannotated(obj, info, **args):
                        pass
                    def unrelated() -> None:
                        pass
                """),
        })

        models = generate_taint_models._get_graphql_sources(arguments)
        self.assertSetEqual(
            models,
            {
                "def fetchers.user.function(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
                "def fetchers.user.unannotated(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
            },
        )

        # 'GraphQLField' indirectly nested within 'GraphQLObjectType'
        open.side_effect = _open_implementation({
            "custom/custom_objects.py":
            textwrap.dedent("""
                    from graphql.type import (
                        GraphQLBoolean,
                        GraphQLField,
                        GraphQLObjectType
                    )
                    from fetchers.user import function, unannotated, unrelated

                    fields_dict = {
                        "field1": GraphQLField(GraphQLBoolean,
                                               resolver=function),
                        "field2": GraphQLField(GraphQLBoolean,
                                               resolver=unannotated),
                    }

                    GraphUserType = GraphQLObjectType(
                        name="GraphUser",
                        description="User in Instagram Graph",
                        fields=fields_dict
                    )
                    """),
            "fetchers/user.py":
            textwrap.dedent("""
                    def function(obj: None, info: ResolveInfo, **args) -> bool:
                        pass
                    def unannotated(obj, info, **args):
                        pass
                    def unrelated() -> None:
                        pass
                """),
        })

        models = generate_taint_models._get_graphql_sources(arguments)

        self.assertSetEqual(
            models,
            {
                "def fetchers.user.function(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
                "def fetchers.user.unannotated(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
            },
        )

        # 'GraphQLField' nested within 'GraphQLInterfaceType'
        open.side_effect = _open_implementation({
            "custom/custom_objects.py":
            textwrap.dedent("""
                    from graphql.type import (
                        GraphQLBoolean,
                        GraphQLField,
                        GraphQLInterfaceType
                    )
                    from fetchers.user import function, unannotated, unrelated

                    GraphUserType = GraphQLInterfaceType(
                        name="GraphMediaResourceInterface",
                        description="One possible source of a media object",
                        fields={
                            "field1": GraphQLField(GraphQLBoolean,
                                                   resolver=function),
                            "field2": GraphQLField(GraphQLBoolean,
                                                   resolver=unannotated),
                        }
                    )
                    """),
            "fetchers/user.py":
            textwrap.dedent("""
                    def function(obj: None, info: ResolveInfo, **args) -> bool:
                        pass
                    def unannotated(obj, info, **args):
                        pass
                    def unrelated() -> None:
                        pass
                """),
        })

        models = generate_taint_models._get_graphql_sources(arguments)
        self.assertSetEqual(
            models,
            {
                "def fetchers.user.function(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
                "def fetchers.user.unannotated(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
            },
        )

        # Resolver passed into 'add_connection' function
        open.side_effect = _open_implementation({
            "custom/custom_objects.py":
            textwrap.dedent("""
                    from graphql.type import (
                        GraphQLBoolean,
                        GraphQLField,
                        GraphQLObjectType
                    )
                    from fetchers.user import function, unannotated, unrelated
                    from schemas.connection import add_connection

                    add_connection(None, connection_resolver=function)
                    add_connection(None, connection_resolver=unannotated)
                    """),
            "fetchers/user.py":
            textwrap.dedent("""
                    def function(obj: None, info: ResolveInfo, **args) -> bool:
                        pass
                    def unannotated(obj, info, **args):
                        pass
                    def unrelated() -> None:
                        pass
                """),
            "schemas/connection.py":
            textwrap.dedent("""
                    def add_connection(some_arg, connection_resolver: Callable):
                        pass
                """),
        })

        models = generate_taint_models._get_graphql_sources(arguments)
        self.assertSetEqual(
            models,
            {
                "def fetchers.user.function(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
                "def fetchers.user.unannotated(obj, info, "
                "**args: TaintSource[UserControlled]): ...",
            },
        )
Пример #6
0
 def test_globals(self, open: unittest.mock._patch) -> None:
     open.side_effect = _open_implementation({
         "/root/module.py":
         textwrap.dedent("""
                 A = 1
                 def function():
                   B = 2
                 if "version" is None:
                   C = 2
                 __all__ = {}
                 D, E = 1, 2
                 class Class:
                   F: typing.ClassVar[int] = ...
                 """)
     })
     self.assertSetEqual(
         generate_taint_models._globals("/root", "/root/module.py"),
         {
             "module.A: TaintSink[Global] = ...",
             "module.D: TaintSink[Global] = ...",
             "module.E: TaintSink[Global] = ...",
         },
     )
     open.side_effect = _open_implementation({
         "/root/attributes.py":
         textwrap.dedent("""
                 Z.X = 1
                 A, B.C, D = 1, 2, 3
                 [Y, Q.W] = [1, 2]
                 """)
     })
     self.assertSetEqual(
         generate_taint_models._globals("/root", "/root/attributes.py"),
         {
             "attributes.A: TaintSink[Global] = ...",
             "attributes.D: TaintSink[Global] = ...",
             "attributes.Y: TaintSink[Global] = ...",
         },
     )
     open.side_effect = _open_implementation({
         "/root/namedtuples.py":
         textwrap.dedent("""
                 from collections import namedtuple
                 x = collections.namedtuple()
                 y = namedtuple()
                 """)
     })
     self.assertSetEqual(
         generate_taint_models._globals("/root", "/root/namedtuples.py"),
         set())
     open.side_effect = _open_implementation({
         "/root/alias_assignments.py":
         textwrap.dedent("""
                 x = a
                 y = b.c
                 """)
     })
     self.assertSetEqual(
         generate_taint_models._globals("/root",
                                        "/root/alias_assignments.py"),
         set())
     open.side_effect = _open_implementation({
         "/root/assignment_to_fields.py":
         textwrap.dedent("""
                 x[1] = 123
                 y.field = 456
                 """)
     })
     self.assertSetEqual(
         generate_taint_models._globals("/root",
                                        "/root/assignment_to_fields.py"),
         set(),
     )
     open.side_effect = _open_implementation({
         "/root/annotated_assignments.py":
         textwrap.dedent("""
                 x: int = 1
                 y: str
                 z: Any = alias_that_we_skip
                 """)
     })
     self.assertSetEqual(
         generate_taint_models._globals("/root",
                                        "/root/annotated_assignments.py"),
         {
             "annotated_assignments.x: TaintSink[Global] = ...",
             "annotated_assignments.y: TaintSink[Global] = ...",
         },
     )
Пример #7
0
 def test_globals(self, open: unittest.mock._patch) -> None:
     # pyre-fixme[16]: `_patch` has no attribute `side_effect`.
     open.side_effect = _open_implementation({
         "/root/module.py":
         textwrap.dedent("""
                 A = 1
                 def function():
                   B = 2
                 if "version" is None:
                   C = 2
                 D, E = 1, 2
                 __all__ = {}
                 """)
     })
     generator = GlobalModelGenerator()
     self.assertSetEqual(
         set(generator._globals("/root", "/root/module.py")),
         {
             "module.A: TaintSink[Global] = ...",
             "module.D: TaintSink[Global] = ...",
             "module.E: TaintSink[Global] = ...",
         },
     )
     open.side_effect = _open_implementation({
         "/root/class.py":
         textwrap.dedent("""
                 class Class:
                   F: typing.ClassVar[int] = ...
                   G: int = ...
                   class Nested:
                     H: typing.ClassVar[int] = ...
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/class.py")),
         {
             "class.Class.F: TaintSink[Global] = ...",
             "class.Class.G: TaintSink[Global] = ...",
         },
     )
     open.side_effect = _open_implementation({
         "/root/attributes.py":
         textwrap.dedent("""
                 Z.X = 1
                 A, B.C, D = 1, 2, 3
                 [Y, Q.W] = [1, 2]
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/attributes.py")),
         {
             "attributes.A: TaintSink[Global] = ...",
             "attributes.D: TaintSink[Global] = ...",
             "attributes.Y: TaintSink[Global] = ...",
         },
     )
     open.side_effect = _open_implementation({
         "/root/namedtuples.py":
         textwrap.dedent("""
                 from collections import namedtuple
                 x = collections.namedtuple()
                 y = namedtuple()
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/namedtuples.py")), set())
     open.side_effect = _open_implementation({
         "/root/alias_assignments.py":
         textwrap.dedent("""
                 x = a
                 y = b.c
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/alias_assignments.py")),
         set())
     open.side_effect = _open_implementation({
         "/root/assignment_to_fields.py":
         textwrap.dedent("""
                 x[1] = 123
                 y.field = 456
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/assignment_to_fields.py")),
         set())
     open.side_effect = _open_implementation({
         "/root/annotated_assignments.py":
         textwrap.dedent("""
                 x: int = 1
                 y: str  # this is ignored, as it might not exist in the runtime
                 z: Any = alias_that_we_skip
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/annotated_assignments.py")),
         {"annotated_assignments.x: TaintSink[Global] = ..."},
     )
     open.side_effect = _open_implementation({
         "/root/blacklisted_module.py":
         textwrap.dedent("""
                 A, B = 1
                 class Class:
                   C: typing.ClassVar[int] = ...
                   D: int = ...
                 """)
     })
     Configuration.blacklisted_globals = {
         "blacklisted_module.A",
         "blacklisted_module.Class.C",
     }
     generator = GlobalModelGenerator()
     self.assertSetEqual(
         set(generator._globals("/root", "/root/blacklisted_module.py")),
         {
             "blacklisted_module.B: TaintSink[Global] = ...",
             "blacklisted_module.Class.D: TaintSink[Global] = ...",
         },
     )
     open.side_effect = _open_implementation({
         "/root/dataclass_test.py":
         textwrap.dedent("""
                 from dataclasses import dataclass
                 @dataclass
                 class Class:
                   C: int = ...
                   D: int = ...
                 @dataclass(frozen=True)
                 class Frozen:
                   C: int = ...
                   D: int = ...
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/dataclass_test.py")), set())
     open.side_effect = _open_implementation({
         "/root/qualified_dataclass_test.py":
         textwrap.dedent("""
                 import dataclasses
                 @dataclasses.dataclass
                 class Class:
                   C: int = ...
                   D: int = ...
                 @dataclasses.dataclass(frozen=True)
                 class Frozen:
                   C: int = ...
                   D: int = ...
                 """)
     })
     self.assertSetEqual(
         set(
             generator._globals("/root",
                                "/root/qualified_dataclass_test.py")),
         set())
     open.side_effect = _open_implementation({
         "/root/attributes.py":
         textwrap.dedent("""
                 class MyClass:
                   C: int = ...
                   D: int = ...
                   def __init__(self):
                     self.C = 1
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/attributes.py")),
         {"attributes.MyClass.D: TaintSink[Global] = ..."},
     )
     # We ignore ClassVar for now.
     open.side_effect = _open_implementation({
         "/root/attributes.py":
         textwrap.dedent("""
                 class MyClass:
                   C: ClassVar[int] = ...
                   def __init__(self):
                     self.C = 1
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/attributes.py")), set())
     # Any attribute accessed in a method is considered to be an instance variable.
     open.side_effect = _open_implementation({
         "/root/attributes.py":
         textwrap.dedent("""
                 class MyClass:
                   C: ClassVar[int] = ...
                   def foo(self):
                     self.C = 1
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/attributes.py")), set())
     # Inherit attributes aren't considered.
     open.side_effect = _open_implementation({
         "/root/attributes.py":
         textwrap.dedent("""
                 class MyClass:
                   C: int = ...
                   def __init__(self):
                     self.C = 1
                 class SubClass(MyClass):
                   C: int = ...
                 """)
     })
     self.assertSetEqual(
         set(generator._globals("/root", "/root/attributes.py")),
         {"attributes.SubClass.C: TaintSink[Global] = ..."},
     )