1

Urgently trying to find a fix for an unidentified and undocumented error.

Running pysa analysis and getting this:

ƛ Uncaught exception:
ƛ 
ƛ   AnalysisClassHierarchy.Untracked(_)
ƛ 
ƛ Raised at file "string.ml", line 115, characters 19-34
ƛ Called from file "src/sexp.ml", line 113, characters 13-47
ƛ Client exited with error code 1```

pyre config file:

{
  "source_directories": [
    "."
  ],
  "taint_models_path": ".",
  "exclude": "/home/zeus/work/test-protecc/env/lib/python3.9/.*"
}

Our model:

model_content = """
protecc.vortex.Vortex.SECRETS: TaintSource[Secret]
protecc.vortex.Vortex.endpoints: TaintSink[Endpoint]
"""

The class its modelling:

...
class Vortex:
    def __init__(self):
        self.SECRETS = {}   # Populate with sources
        self.endpoints = set() # Populate with sinks
...

We tried tweaking a lot of things and we keep getting the same error. Only found 3 mentions of this problem so far here, here at the end of the page and also here. None have been useful so far.

stax
  • 11
  • 3

2 Answers2

1

This is also tracked as an issue on GitHub, so I'll keep the discussion there and leave this link as a breadcrumb for others who run into the problem and find this post.

GBleaney
  • 2,096
  • 2
  • 22
  • 40
1

I have also encountered this error. The AnalysisClassHierarchy.Untracked error happens when Pyre typeshed directory is not configured or misconfigured in the .pyre_configuration file.

For example, if you have installed Pyre into /home/max/.local/lib/pyre_check/, then the typeshed directory is /home/max/.local/lib/pyre_check/typeshed.

To reproduce this error right now, I have changed the typeshed directory in the config file to an incorrect directory. Thus, I got the following error:

Analysis__ClassHierarchy.Untracked("typing.NamedTuple")

After correcting the path, Pyre was able to find vulnerabilities. See below. I will also give practical examples on how did it find the errors and how to configure it, and what changes in the source code are needed.

If you wish to install custom stubs (a directory with .pyi files), add them to typeshed/third_party/2and3/, or, in my case, it is /home/max/.local/lib/pyre_check/typeshed/third_party/2and3/.

Therefore, you .pyre_configuration file will look like this:

{
  "source_directories": ["/home/max/myproject"],
  "taint_models_path": "/home/max/myproject/taint",
  "typeshed": "/home/max/.local/lib/pyre_check/typeshed"
}

Do not use default Pyre taint directory. You have specified your own taint directory, which is correct. You have created it in ., but it's better to create a new directory for it withous subdirectories. Please create a new taint directory like /home/max/myproject/taint and copy into it the taint.config file from the Pyre installation folder. Also, copy the .pysa files to this directory from the Pyre installation, but only those that you need, not all of them. Also put you model .pisa file there.

Let me give you a few examples.

For instance, with https://github.com/fportantier/vulpy Pyre found the following errors:

  {
    "line": 20,
    "column": 43,
    "stop_line": 20,
    "stop_column": 51,
    "path": "mod_user.py",
    "code": 5005,
    "name": "SQL injection.",
    "description":
      "SQL injection. [5005]: Data from [UserControlled] source(s) may reach [SQL] sink(s)",
    "inference": null,
    "define": "mod_user.do_login"
  },
  {
    "line": 20,
    "column": 33,
    "stop_line": 20,
    "stop_column": 41,
    "path": "mod_user.py",
    "code": 5005,
    "name": "SQL injection.",
    "description":
      "SQL injection. [5005]: Data from [UserControlled] source(s) may reach [SQL] sink(s)",
    "inference": null,
    "define": "mod_user.do_login"
  },
  {
    "line": 52,
    "column": 33,
    "stop_line": 52,
    "stop_column": 41,
    "path": "mod_user.py",
    "code": 5005,
    "name": "SQL injection.",
    "description":
      "SQL injection. [5005]: Data from [UserControlled] source(s) may reach [SQL] sink(s)",
    "inference": null,
    "define": "mod_user.do_create"
  },
  {
    "line": 52,
    "column": 23,
    "stop_line": 52,
    "stop_column": 31,
    "path": "mod_user.py",
    "code": 5005,
    "name": "SQL injection.",
    "description":
      "SQL injection. [5005]: Data from [UserControlled] source(s) may reach [SQL] sink(s)",
    "inference": null,
    "define": "mod_user.do_create"
  },
  {
    "line": 80,
    "column": 55,
    "stop_line": 80,
    "stop_column": 63,
    "path": "mod_user.py",
    "code": 5005,
    "name": "SQL injection.",
    "description":
      "SQL injection. [5005]: Data from [UserControlled] source(s) may reach [SQL] sink(s)",
    "inference": null,
    "define": "mod_user.do_chpasswd"
  },
  {
    "line": 39,
    "column": 24,
    "stop_line": 39,
    "stop_column": 40,
    "path": "mod_api.py",
    "code": 6060,
    "name": "User-controlled data flows into filesystem API (other)",
    "description":
      "User-controlled data flows into filesystem API (other) [6060]: Data from [UserControlled] source(s) may reach [FileSystem_Other] sink(s)",
    "inference": null,
    "define": "mod_api.do_key_create"
  },
  {
    "line": 39,
    "column": 24,
    "stop_line": 39,
    "stop_column": 40,
    "path": "mod_api.py",
    "code": 5011,
    "name": "User data to filesystem operation (read/write)",
    "description":
      "User data to filesystem operation (read/write) [5011]: Data from [UserControlled] source(s) may reach [FileSystem_ReadWrite] sink(s)",
    "inference": null,
    "define": "mod_api.do_key_create"
  }
]

And for https://github.com/lchsk/django-insecure Pyre found the following errors:

  {
    "line": 16,
    "column": 29,
    "stop_line": 16,
    "stop_column": 80,
    "path": "security/views.py",
    "code": 5005,
    "name": "SQL injection.",
    "description":
      "SQL injection. [5005]: Data from [UserControlled] source(s) may reach [StringMayBeSQL] sink(s)",
    "inference": null,
    "define": "security.views.unsafe_users"
  },
  {
    "line": 32,
    "column": 14,
    "stop_line": 32,
    "stop_column": 22,
    "path": "security/views.py",
    "code": 5011,
    "name": "User data to filesystem operation (read/write)",
    "description":
      "User data to filesystem operation (read/write) [5011]: Data from [UserControlled] source(s) may reach [FileSystem_ReadWrite] sink(s)",
    "inference": null,
    "define": "security.views.read_file"
  },
  {
    "line": 41,
    "column": 14,
    "stop_line": 41,
    "stop_column": 17,
    "path": "security/views.py",
    "code": 5001,
    "name": "Possible shell injection",
    "description":
      "Possible shell injection [5001]: Data from [UserControlled] source(s) may reach [RemoteCodeExecution] sink(s)",
    "inference": null,
    "define": "security.views.copy_file"
  },
  {
    "line": 72,
    "column": 24,
    "stop_line": 72,
    "stop_column": 29,
    "path": "security/views.py",
    "code": 6066,
    "name": "Unsafe deserialization may result in RCE",
    "description":
      "Unsafe deserialization may result in RCE [6066]: Data from [UserControlled] source(s) may reach [ExecDeserializationSink] sink(s)",
    "inference": null,
    "define": "security.views.admin_index"
  }
]

The stubs directory for the https://github.com/lchsk/django-insecure project in my case containted the following files:

collection_propagation.pysa
django_sources_sinks.pysa
filesystem_other_sinks.pysa
filesystem_sinks.pysa
flask_sources_sinks.pysa
format_string_sinks.pysa
general.pysa
github-django-insecure-views.pysa
http_server.pysa
logging_sinks.pysa
protocols.pysa
rce_sinks.pysa
requests_api_sinks.pysa
sanitizers.pysa
skipped_overrides.pysa
sqlite3_sinks.pysa
taint.config
wsgi_ref.pysa
xss_sinks.pysa

The contents of the github-django-insecure-views.pysa file is the following. It was needed to define that the string argument for the view functions is also a taint source:

def security.views.unsafe_users(request, user_id: TaintSource[UserControlled]): ...
def security.views.safe_users(request, user_id: TaintSource[UserControlled]): ...
def security.views.read_file(request, filename: TaintSource[UserControlled]): ...
def security.views.copy_file(request, filename: TaintSource[UserControlled]): ...
def security.views.admin_index(request): ...
def security.views.search(request): ...
def security.views.log(request): ...

I have also edited the views.py file to specify the type for the request argument, like this:

def unsafe_users(request: HttpRequest, user_id):

You cannot just create .pyi file for views.py to define the type of the request parameter, because if Pyre finds a .pyi file, it skips scanning this file, so you have to make sure that the type is specified explicitly in the code. Otherwise, Pyre will be unable to follow the execution flow from taint source to the taint sink.

Maxim Masiutin
  • 3,991
  • 4
  • 55
  • 72