Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot resolve type for pymysql #831

Open
kylinlingh opened this issue Apr 24, 2024 · 3 comments
Open

Cannot resolve type for pymysql #831

kylinlingh opened this issue Apr 24, 2024 · 3 comments

Comments

@kylinlingh
Copy link

kylinlingh commented Apr 24, 2024

Hi, my code is fairly simple, using pymysql within the Flask framework, but I have been unable to detect SQL injection. By using pyre_dump() and reveal_type(), I have identified the following issues

source code

import pymysql
from flask import Request

def request_parse(req_data: Request):
    data = None
    if req_data.method == 'POST':
        data = req_data.get_json()
    elif req_data.method == 'GET':
        data = req_data.args
    return data

def update_case_by_Pymysql(request):
    req = request_parse(request)
    sql = req["sql"]
    reveal_type(sql)
    reveal_taint(sql)
    conn = pymysql.connect(host='localhost',user='user',password='passwd',database='db', cursorclass=pymysql.cursors.DictCursor)
    # pyre_dump()
    cursor = conn.cursor()
    reveal_type(cursor)
    reveal_taint(cursor)
    cursor.execute(sql)

issues

  1. with pyre_dump(), I got the exception result: Resolved type for base pymysql is unknown , as below:

2024-04-24 09:56:02,141 [PID 6032] WARNING Checking if cursors is an attribute, property or global variable. Resolved type for base pymysql is unknown
2024-04-24 09:56:02,144 [PID 6032] WARNING Resolved callees for expression pymysql.cursors:
2024-04-24 09:56:02,145 [PID 6032] WARNING { call = None;
2024-04-24 09:56:02,146 [PID 6032] WARNING attribute_access =
2024-04-24 09:56:02,146 [PID 6032] WARNING (Some { property_targets = []; global_targets = []; is_attribute = true });
2024-04-24 09:56:02,146 [PID 6032] WARNING identifier = None; string_format = None }
2024-04-24 09:56:02,147 [PID 6032] WARNING Checking if DictCursor is an attribute, property or global variable. Resolved type for base pymysql.cursors is unknown
2024-04-24 09:56:02,147 [PID 6032] WARNING Resolved callees for expression pymysql.cursors.DictCursor:
2024-04-24 09:56:02,148 [PID 6032] WARNING { call = None;
2024-04-24 09:56:02,149 [PID 6032] WARNING attribute_access =
2024-04-24 09:56:02,150 [PID 6032] WARNING (Some { property_targets = []; global_targets = []; is_attribute = true });
2024-04-24 09:56:02,151 [PID 6032] WARNING identifier = None; string_format = None }
2024-04-24 09:56:02,151 [PID 6032] WARNING Resolving function call pymysql.connect($parameter$host = "localhost", $parameter$user = "user", $parameter$password = "passwd", $parameter$database = "db", $parameter$cursorclass = pymysql.cursors.DictCursor)
2024-04-24 09:56:02,151 [PID 6032] WARNING Checking if "localhost" is a callable, resolved type is typing_extensions.Literal['localhost']
2024-04-24 09:56:02,152 [PID 6032] WARNING Checking if "user" is a callable, resolved type is typing_extensions.Literal['user']
2024-04-24 09:56:02,152 [PID 6032] WARNING Checking if "passwd" is a callable, resolved type is typing_extensions.Literal['passwd']
2024-04-24 09:56:02,153 [PID 6032] WARNING Checking if "db" is a callable, resolved type is typing_extensions.Literal['db']
2024-04-24 09:56:02,154 [PID 6032] WARNING Checking if pymysql.cursors.DictCursor is a callable, resolved type is typing.Type[pymysql.cursors.DictCursor]
2024-04-24 09:56:02,155 [PID 6032] WARNING Resolved callee from its resolved type:
2024-04-24 09:56:02,156 [PID 6032] WARNING { call_targets = []; 2024-04-24 09:56:02,157 [PID 6032] WARNING new_targets = 2024-04-24 09:56:02,158 [PID 6032] WARNING [{ target = 2024-04-24 09:56:02,159 [PID 6032] WARNING (Method 2024-04-24 09:56:02,160 [PID 6032] WARNING { class_name = "object"; method_name = "__new__"; kind = Normal }); 2024-04-24 09:56:02,161 [PID 6032] WARNING implicit_self = true; implicit_dunder_call = false; index = 0; 2024-04-24 09:56:02,161 [PID 6032] WARNING return_type = (Some {}); receiver_class = None } 2024-04-24 09:56:02,162 [PID 6032] WARNING ]; 2024-04-24 09:56:02,162 [PID 6032] WARNING init_targets = 2024-04-24 09:56:02,163 [PID 6032] WARNING [{ target = 2024-04-24 09:56:02,163 [PID 6032] WARNING (Method 2024-04-24 09:56:02,164 [PID 6032] WARNING { class_name = "pymysql.cursors.Cursor"; method_name = "__init__"; 2024-04-24 09:56:02,165 [PID 6032] WARNING kind = Normal }); 2024-04-24 09:56:02,165 [PID 6032] WARNING implicit_self = true; implicit_dunder_call = false; index = 0; 2024-04-24 09:56:02,167 [PID 6032] WARNING return_type = (Some {}); receiver_class = None } 2024-04-24 09:56:02,167 [PID 6032] WARNING ]; 2024-04-24 09:56:02,168 [PID 6032] WARNING higher_order_parameters = {}; unresolved = false }
2024-04-24 09:56:02,170 [PID 6032] WARNING Checking if pymysql.connect is a callable, resolved type is typing.Any

  1. After removing the pyre_dump statement, the results obtained from reveal_type / reveal_taint are as follows. It can be seen that the pymysql sink cannot be captured:

2024-04-24 10:13:11,594 [PID 6426] WARNING djangosql.main:15:4-15:15: Revealed type for sql: typing.Any
2024-04-24 10:13:11,594 [PID 6426] WARNING djangosql.main:16:4-16:16: Revealed forward taint for sql: {CallSite(callees=[djangosql.main.request_parse], location=djangosql.main:13:10-13:32, port=result) -> LocalTaint(Kinds: {UserControlled -> Frame(Breadcrumb: [ModelShaping(-), TitoBroadening(-), Tito(-), Broadening(-), ModelTitoShaping(-), ObscureUnknownCallee(-)], TraceLength: 1, LeafName: [LeafName(djangosql.main.request_parse, port=Leaf(req_data)), LeafName(werkzeug.wrappers.request.Request.get_data, port=Leaf(return)), LeafName(werkzeug.wrappers.request.Request.get_json, port=Leaf(return))], FirstField: ["args"])}, Breadcrumb: [], FirstIndex: ["sql"], CallInfoIntervals: [caller_interval: receiver_interval: is_self_call: false])}
2024-04-24 10:13:11,594 [PID 6426] WARNING djangosql.main:19:4-19:15: Revealed type for cursor: typing.Any
2024-04-24 10:13:11,595 [PID 6426] WARNING djangosql.main:20:4-20:16: Revealed forward taint for cursor: {}
2024-04-24 10:13:11,595 [PID 6426] WARNING djangosql.main:20:4-20:24: Revealed backward taint for cursor: {}
2024-04-24 10:13:11,595 [PID 6426] WARNING djangosql.main:16:4-16:21: Revealed backward taint for sql: {}
2024-04-24 10:13:11,595 [PID 6426] WARNING flasksql.main:26:4-26:15: Revealed type for sql: typing.Any
2024-04-24 10:13:11,595 [PID 6426] WARNING flasksql.main:27:4-27:16: Revealed forward taint for sql: {CallSite(callees=[flasksql.main.request_parse], location=flasksql.main:23:10-23:32, port=result) -> LocalTaint(Kinds: {UserControlled -> Frame(Breadcrumb: [ObscureUnknownCallee(-)], TraceLength: 1, LeafName: [LeafName(werkzeug.wrappers.request.Request.get_data, port=Leaf(return)), LeafName(werkzeug.wrappers.request.Request.get_json, port=Leaf(return))])}, Breadcrumb: [], FirstIndex: ["sql"], CallInfoIntervals: [caller_interval: receiver_interval: is_self_call: false])}
2024-04-24 10:13:11,596 [PID 6426] WARNING flasksql.main:37:8-37:19: Revealed type for db: typing.Any
2024-04-24 10:13:11,596 [PID 6426] WARNING flasksql.main:38:8-38:20: Revealed forward taint for db: {}
2024-04-24 10:13:11,596 [PID 6426] WARNING flasksql.main:40:8-40:19: Revealed type for sql: typing.Any
2024-04-24 10:13:11,596 [PID 6426] WARNING flasksql.main:41:8-41:20: Revealed forward taint for sql: {CallSite(callees=[flasksql.main.request_parse], location=flasksql.main:23:10-23:32, port=result) -> LocalTaint(Kinds: {UserControlled -> Frame(Breadcrumb: [ObscureUnknownCallee(-)], TraceLength: 1, LeafName: [LeafName(werkzeug.wrappers.request.Request.get_data, port=Leaf(return)), LeafName(werkzeug.wrappers.request.Request.get_json, port=Leaf(return))])}, Breadcrumb: [], FirstIndex: ["sql"], CallInfoIntervals: [caller_interval: receiver_interval: is_self_call: false])}
2024-04-24 10:13:11,597 [PID 6426] INFO Processed 43 of 43 callables
2024-04-24 10:13:11,597 [PID 6426] INFO Iteration #0, 43 callables, heap size 0.371GB took 0.05s
2024-04-24 10:13:11,597 [PID 6426] INFO Iteration #1. 30 callables [...]
2024-04-24 10:13:11,598 [PID 6426] WARNING djangosql.main:15:4-15:15: Revealed type for sql: typing.Any
2024-04-24 10:13:11,598 [PID 6426] WARNING djangosql.main:16:4-16:16: Revealed forward taint for sql: {CallSite(callees=[djangosql.main.request_parse], location=djangosql.main:13:10-13:32, port=result) -> LocalTaint(Kinds: {UserControlled -> Frame(Breadcrumb: [ModelShaping(-), TitoBroadening(-), Tito(-), Broadening(-), ModelTitoShaping(-), ObscureUnknownCallee(-)], TraceLength: 1, LeafName: [LeafName(djangosql.main.request_parse, port=Leaf(req_data)), LeafName(werkzeug.wrappers.request.Request.get_data, port=Leaf(return)), LeafName(werkzeug.wrappers.request.Request.get_json, port=Leaf(return))], FirstField: ["args"])}, Breadcrumb: [], FirstIndex: ["sql"], CallInfoIntervals: [caller_interval: receiver_interval: is_self_call: false])}
2024-04-24 10:13:11,598 [PID 6426] WARNING djangosql.main:19:4-19:15: Revealed type for cursor: typing.Any
2024-04-24 10:13:11,599 [PID 6426] WARNING djangosql.main:20:4-20:16: Revealed forward taint for cursor: {}
2024-04-24 10:13:11,599 [PID 6426] WARNING djangosql.main:20:4-20:24: Revealed backward taint for cursor: {}
2024-04-24 10:13:11,599 [PID 6426] WARNING djangosql.main:16:4-16:21: Revealed backward taint for sql: {}
2024-04-24 10:13:11,600 [PID 6426] WARNING flasksql.main:26:4-26:15: Revealed type for sql: typing.Any
2024-04-24 10:13:11,600 [PID 6426] WARNING flasksql.main:27:4-27:16: Revealed forward taint for sql: {CallSite(callees=[flasksql.main.request_parse], location=flasksql.main:23:10-23:32, port=result) -> LocalTaint(Kinds: {UserControlled -> Frame(Breadcrumb: [ObscureUnknownCallee(-)], TraceLength: 1, LeafName: [LeafName(werkzeug.wrappers.request.Request.get_data, port=Leaf(return)), LeafName(werkzeug.wrappers.request.Request.get_json, port=Leaf(return))])}, Breadcrumb: [], FirstIndex: ["sql"], CallInfoIntervals: [caller_interval: receiver_interval: is_self_call: false])}
2024-04-24 10:13:11,600 [PID 6426] WARNING flasksql.main:37:8-37:19: Revealed type for db: typing.Any
2024-04-24 10:13:11,600 [PID 6426] WARNING flasksql.main:38:8-38:20: Revealed forward taint for db: {}
2024-04-24 10:13:11,601 [PID 6426] WARNING flasksql.main:40:8-40:19: Revealed type for sql: typing.Any
2024-04-24 10:13:11,601 [PID 6426] WARNING flasksql.main:41:8-41:20: Revealed forward taint for sql: {CallSite(callees=[flasksql.main.request_parse], location=flasksql.main:23:10-23:32, port=result) -> LocalTaint(Kinds: {UserControlled -> Frame(Breadcrumb: [ObscureUnknownCallee(-)], TraceLength: 1, LeafName: [LeafName(werkzeug.wrappers.request.Request.get_data, port=Leaf(return)), LeafName(werkzeug.wrappers.request.Request.get_json, port=Leaf(return))])}, Breadcrumb: [], FirstIndex: ["sql"], CallInfoIntervals: [caller_interval: receiver_interval: is_self_call: false])}
2024-04-24 10:13:11,601 [PID 6426] INFO Processed 30 of 30 callables
2024-04-24 10:13:11,602 [PID 6426] INFO Iteration #1, 30 callables, heap size 0.371GB took 0.03s
2024-04-24 10:13:11,602 [PID 6426] INFO Post-processing issues for multi-source rules...
2024-04-24 10:13:11,602 [PID 6426] PERFORMANCE Finished issue post-processing for multi-source rules: 0.197s
2024-04-24 10:13:11,603 [PID 6426] INFO Found 0 issues
2024-04-24 10:13:11,603 [PID 6426] PERFORMANCE Analysis fixpoint complete (iterations: 2, heap size: 370619776, issues: 0): 2.128s
2024-04-24 10:13:11,603 [PID 6426] PERFORMANCE Analyze: 80.480s
[]

Additional information

  1. As far as I know, the pyre-check project already includes a sink file for pymysql. Could it be that this file has become outdated?
  2. When I replaced pymysql in the code with django.db, I was able to detect SQL injection.

I look forward to your prompt reply and would greatly appreciate it.

@ebrahimsofi123
Copy link

Hey! It seems the issue you're encountering with pyre-check when using pymysql may stem from outdated or incomplete type stubs in the library, which hinders the tool's ability to accurately recognize and handle types like pymysql.cursors.DictCursor. This issue can prevent pyre-check from effectively tracking taints and resolving types, thereby affecting its ability to detect vulnerabilities like SQL injection, which you observed works correctly with django.db. To address this, you might consider updating the stubs or configurations in pyre-check to better recognize pymysql components, or consult the Pyre project documentation for guidance on enhancing type support.

@kylinlingh
Copy link
Author

I have already tried to update the type stubs of pymysql from git repository before I posted this issue. But it still didn't work. Maybe you can run the pyre to analyze my source code I mentioned above to see if you can figure out this problem.

@ossie-git
Copy link

Tried it and ran into the same issues that you ran into as well (even with the updated typeshed stubs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants