Skip to content

Commit

Permalink
Add integration tests for private variables
Browse files Browse the repository at this point in the history
Summary:
Python has a concept of "private variables", see https://docs.python.org/3/tutorial/classes.html#private-variables
This diff adds tests to demonstrate how we handle these.
To answer that question: we handle them properly in most cases.
The only thing we don't support currently is annotating the non-mangled name in a `.pysa` file.

Reviewed By: tianhan0

Differential Revision: D51159207

fbshipit-source-id: 5c2c83aa95e8a844fc2c711eea7ebea145fd2ae6
  • Loading branch information
arthaud authored and facebook-github-bot committed Nov 9, 2023
1 parent 4011675 commit 8177187
Show file tree
Hide file tree
Showing 5 changed files with 1,948 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from builtins import _test_sink, _test_source
from typing import List


class Simple:
def __init__(self, private: str = "", public: str = "") -> None:
self.__value: str = private
self.value: str = public

def private_into_sink(self) -> None:
_test_sink(self.__value)

def public_into_sink(self) -> None:
_test_sink(self.value)

@staticmethod
def expand_subexpression(values: List[Simple]) -> None:
# Private variables are expended in all expressions.
_test_sink(values[0].__value)

def getattr_public(self) -> str:
return getattr(self, "value")

def getattr_private(self) -> str:
return getattr(self, "_Simple__value")

def getattr_invalid(self) -> str:
# This should not work according to the documentation.
# pyre-ignore
return getattr(self, "__value")


def test_simple() -> None:
Simple(private=_test_source()).private_into_sink()


def test_private_public_different() -> None:
Simple(private=_test_source()).private_into_sink() # Error.
Simple(private=_test_source()).public_into_sink() # No error.
Simple(public=_test_source()).private_into_sink() # No error.
Simple(public=_test_source()).public_into_sink() # Error.


def test_expand_subexpression() -> None:
Simple.expand_subexpression([Simple(private=_test_source())]) # Error
Simple.expand_subexpression([Simple(), Simple(private=_test_source())]) # No error.


def test_getattr() -> None:
# Expect no error, currently a false positive.
_test_sink(Simple(private=_test_source()).getattr_public())
# Expect no error, currently a false positive.
_test_sink(Simple(private=_test_source()).getattr_private())
# Expect no error, currently a false positive.
_test_sink(Simple(private=_test_source()).getattr_invalid())
# Expect no error, currently a false positive.
_test_sink(Simple(public=_test_source()).getattr_public())
# Expect no error, currently a false positive.
_test_sink(Simple(public=_test_source()).getattr_private())
# Expect no error, currently a false positive.
_test_sink(Simple(public=_test_source()).getattr_invalid())


def test_bypass_private() -> None:
_test_sink(Simple(private=_test_source())._Simple__value) # Error.
_test_sink(Simple(public=_test_source())._Simple__value) # No error.
# pyre-ignore
_test_sink(Simple(private=_test_source()).__value) # No error.
_test_sink(Simple(public=_test_source()).__value) # No error.


class Other:
@staticmethod
def private_into_sink(s: Simple) -> None:
# Should produce a sink on _Other__value, not _Simple__value.
# pyre-ignore
_test_sink(s.__value)


def test_access_from_other_class() -> None:
Other.private_into_sink(Simple(private=_test_source())) # No error.


class PrivateAttributeSourceModels:
def __init__(self):
# See private_variables.py.pysa
self.__model_mangled: str = ""
self.__model_unmangled: str = ""
self.__model_query: str = ""

def get_model_mangled(self) -> str:
return self.__model_mangled

def get_model_unmangled(self) -> str:
return self.__model_unmangled

def get_model_query(self) -> str:
return self.__model_query


def test_private_attribute_source_models() -> None:
# Error.
_test_sink(PrivateAttributeSourceModels().get_model_mangled())
# TODO(T169448194): Support models on private attributes
_test_sink(PrivateAttributeSourceModels().get_model_unmangled())
# Error
_test_sink(PrivateAttributeSourceModels().get_model_query())


class PrivateAttributeSinkModels:
def __init__(self):
# See private_variables.py.pysa
self.__model_mangled: str = ""
self.__model_unmangled: str = ""
self.__model_query: str = ""

def set_model_mangled(self, value: str) -> None:
self.__model_mangled = value

def set_model_unmangled(self, value: str) -> None:
self.__model_unmangled = value

def set_model_query(self, value: str) -> None:
self.__model_query = value


def test_private_attribute_sink_models() -> None:
# Error.
PrivateAttributeSinkModels().set_model_mangled(_test_source())
# TODO(T169448194): Support models on private attributes
PrivateAttributeSinkModels().set_model_unmangled(_test_source())
# Error
PrivateAttributeSinkModels().set_model_query(_test_source())
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@generated
Call dependencies
private_variables.test_access_from_other_class (fun) -> [_test_source (fun) object.__new__ (method) private_variables.Other.private_into_sink (method) private_variables.Simple.__init__ (method)]
private_variables.test_expand_subexpression (fun) -> [_test_source (fun) object.__new__ (method) private_variables.Simple.__init__ (method) private_variables.Simple.expand_subexpression (method)]
private_variables.test_private_attribute_sink_models (fun) -> [_test_source (fun) object.__new__ (method) private_variables.PrivateAttributeSinkModels.__init__ (method) private_variables.PrivateAttributeSinkModels.set_model_mangled (method) private_variables.PrivateAttributeSinkModels.set_model_query (method) private_variables.PrivateAttributeSinkModels.set_model_unmangled (method)]
private_variables.test_private_attribute_source_models (fun) -> [_test_sink (fun) object.__new__ (method) private_variables.PrivateAttributeSourceModels.__init__ (method) private_variables.PrivateAttributeSourceModels.get_model_mangled (method) private_variables.PrivateAttributeSourceModels.get_model_query (method) private_variables.PrivateAttributeSourceModels.get_model_unmangled (method)]
private_variables.test_private_public_different (fun) -> [_test_source (fun) object.__new__ (method) private_variables.Simple.__init__ (method) private_variables.Simple.private_into_sink (method) private_variables.Simple.public_into_sink (method)]
private_variables.$toplevel (fun) -> []
private_variables.test_bypass_private (fun) -> [_test_sink (fun) _test_source (fun) object.__new__ (method) private_variables.Simple.__init__ (method)]
private_variables.test_getattr (fun) -> [_test_sink (fun) _test_source (fun) object.__new__ (method) private_variables.Simple.__init__ (method) private_variables.Simple.getattr_invalid (method) private_variables.Simple.getattr_private (method) private_variables.Simple.getattr_public (method)]
private_variables.test_simple (fun) -> [_test_source (fun) object.__new__ (method) private_variables.Simple.__init__ (method) private_variables.Simple.private_into_sink (method)]
private_variables.Other.private_into_sink (method) -> [_test_sink (fun)]
private_variables.Other.$class_toplevel (method) -> []
private_variables.PrivateAttributeSinkModels.$class_toplevel (method) -> []
private_variables.PrivateAttributeSinkModels.__init__ (method) -> [private_variables.PrivateAttributeSinkModels._PrivateAttributeSinkModels__model_mangled (object) private_variables.PrivateAttributeSinkModels._PrivateAttributeSinkModels__model_query (object)]
private_variables.PrivateAttributeSinkModels.set_model_mangled (method) -> [private_variables.PrivateAttributeSinkModels._PrivateAttributeSinkModels__model_mangled (object)]
private_variables.PrivateAttributeSinkModels.set_model_query (method) -> [private_variables.PrivateAttributeSinkModels._PrivateAttributeSinkModels__model_query (object)]
private_variables.PrivateAttributeSinkModels.set_model_unmangled (method) -> []
private_variables.PrivateAttributeSourceModels.$class_toplevel (method) -> []
private_variables.PrivateAttributeSourceModels.__init__ (method) -> [private_variables.PrivateAttributeSourceModels._PrivateAttributeSourceModels__model_mangled (object) private_variables.PrivateAttributeSourceModels._PrivateAttributeSourceModels__model_query (object)]
private_variables.PrivateAttributeSourceModels.get_model_mangled (method) -> [private_variables.PrivateAttributeSourceModels._PrivateAttributeSourceModels__model_mangled (object)]
private_variables.PrivateAttributeSourceModels.get_model_query (method) -> [private_variables.PrivateAttributeSourceModels._PrivateAttributeSourceModels__model_query (object)]
private_variables.PrivateAttributeSourceModels.get_model_unmangled (method) -> []
private_variables.Simple.$class_toplevel (method) -> []
private_variables.Simple.expand_subexpression (method) -> [_test_sink (fun) list.__getitem__ (method)]
private_variables.Simple.getattr_invalid (method) -> [getattr (fun)]
private_variables.Simple.getattr_private (method) -> [getattr (fun)]
private_variables.Simple.private_into_sink (method) -> [_test_sink (fun)]
private_variables.Simple.public_into_sink (method) -> [_test_sink (fun)]
private_variables.Simple.__init__ (method) -> []
private_variables.Simple.getattr_public (method) -> [getattr (fun)]
Loading

0 comments on commit 8177187

Please sign in to comment.