CodeQL分析python代码7-使用 API 图
API 图是一个用于引用外部库中定义的函数,类和方法的统一接口
关于本文
本文介绍如何使用API图来引用python第三方库代码中定义的类和函数。在定义例如flask的用户输入这种外部输入流等内容时,我们可以使用API图来更方便地引用外部库函数。
模块导入
API图中最常见的入口点是导入外部模块的module
或package
。我们可以使用模块semmle.python.ApiGraphs
中定义的方法API::moduleImport
访问与re
库对应的API图节点,如下
import python
import semmle.python.ApiGraphs
select API::moduleImport("re")
该查询会显示与我们所输入模块(这里是re)对应的API图节点。该节点表示re
模块是否被导入这件事,并不是程序中发生导入的某一个位置,同时每个项目最多会有一个结果。
当然这不是我们想要的,我们往往想要获得某一个模块在程序中调用的具体的位置,这时候我们可以使用getAUse()
方法,查询对于re
模块的所有引用
import python
import semmle.python.ApiGraphs
select API::moduleImport("re").getAUse()
需要注意的是,getAUse()
方法考虑了局部数据流,因此my_re_compile
在以下代码段中被正确识别为对re.compile
函数的引用
from re import compile as re_compile
my_re_compile = re_compile
r = my_re_compile(".*")
如果我们只需要立即使用而不考虑局部数据流,可以使用getAnImmediateUse()
方法
需要注意的是,传入的模块名称不得包含任何点。所以如果我们想要导入flask.views
的时候,如果使用API::moduleImport("flask.views")
,那么结果往往是错误的。这时我们应该分解为views
对API图节点flask
成员的访问,这也是马上要介绍的内容
访问属性
给定API图中的一个节点,我们可以使用getMember
方法访问其属性,比如对上面上面的代码来说,我们要找到re.compile
的引用
import python
import semmle.python.ApiGraphs
select API::moduleImport("re").getMember("compile").getAUse()
除了getMember()
方法外,我们还可以使用getUnknownMember()
方法查找对静态名称未知的API组件的引用。我们也可以使用getAMember()
访问所有成员,包括已知成员和未知成员
调用和类实例化
要跟踪外部库中定义的类的示例,或调用外部定义函数的结果,我们可以使用getReturn()
方法,下面我们查找所有使用re.compile()
作为返回值的地方
import python
import semmle.python.ApiGraphs
select API::moduleImport("re").getMember("compile").getReturn().getAUse()
需要注意的是,查询结果包括了所有对re.compile
的使用,包括可通过局部数据流访问的那些。要获得对re.compile
的调用,我们可以使用getAnImmediateUse()
而不是getAUse()
。更好的一种方式是使用getACall()
,这样就可以避免getReturn
后面跟着getAnImmediateUse()
另外,API图不区分类实例化和函数调用,对它而言两者都只是调用API图节点的地方
子类
对于很多库来说,主要的使用方式是扩展一个或多个库,我们想要在API图中跟踪这一点,可以使用getASubclass()
方法获取与该节点的所有直接子类对应的API图节点。要查找所有子类,只需要重复使用*
或+
应用在方法上,例如getASubclass*
在flask.views.View
中有一个预定义的子类MethodView
,要查找所有的子类View
,我们需要显示包含MethodView
子类
import python
import semmle.python.ApiGraphs
API::Node viewClass() {
result =
API::moduleImport("flask").getMember("views").getMember(["View", "MethodView"]).getASubclass*()
}
select viewClass().getAUse()
内置函数和类
我们可以使用API::builtin
方法访问内置函数和类,将内置函数的名称作为参数提供
例如,查找对内置函数open
的所有调用
import python
import semmle.python.ApiGraphs
select API::builtin("open").getACall()
END
建了一个微信的安全交流群,欢迎添加我微信备注进群
,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃