Open kylinlingh opened 4 months ago
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.
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.
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
issues
pymysql
isunknown
, 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 basepymysql
isunknown
2024-04-24 09:56:02,144 [PID 6032] WARNING Resolved callees for expressionpymysql.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 ifDictCursor
is an attribute, property or global variable. Resolved type for basepymysql.cursors
isunknown
2024-04-24 09:56:02,147 [PID 6032] WARNING Resolved callees for expressionpymysql.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 callpymysql.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 istyping_extensions.Literal['localhost']
2024-04-24 09:56:02,152 [PID 6032] WARNING Checking if"user"
is a callable, resolved type istyping_extensions.Literal['user']
2024-04-24 09:56:02,152 [PID 6032] WARNING Checking if"passwd"
is a callable, resolved type istyping_extensions.Literal['passwd']
2024-04-24 09:56:02,153 [PID 6032] WARNING Checking if"db"
is a callable, resolved type istyping_extensions.Literal['db']
2024-04-24 09:56:02,154 [PID 6032] WARNING Checking ifpymysql.cursors.DictCursor
is a callable, resolved type istyping.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 ifpymysql.connect
is a callable, resolved type istyping.Any
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 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 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 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 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 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 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
[]
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:cursor
: {} 2024-04-24 10:13:11,595 [PID 6426] WARNING djangosql.main:20:4-20:24: Revealed backward taint forcursor
: {} 2024-04-24 10:13:11,595 [PID 6426] WARNING djangosql.main:16:4-16:21: Revealed backward taint forsql
: {} 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 forsql
: {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: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 forsql
: {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: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:cursor
: {} 2024-04-24 10:13:11,599 [PID 6426] WARNING djangosql.main:20:4-20:24: Revealed backward taint forcursor
: {} 2024-04-24 10:13:11,599 [PID 6426] WARNING djangosql.main:16:4-16:21: Revealed backward taint forsql
: {} 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 forsql
: {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: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 forsql
: {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:Additional information
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.