最近在对java语言做静态分析,发现javalang这个好用的python包。特别记录一下。
我们可以把javalang解析出的语法树进行再加工,生成我们需要的元信息,再结合校验规则,做一些常见编码问题的分析。
这就是lint类工具的一般性原理。
语法树打印
javalang的顶层结构叫CompilationUnit,就是一颗语法树。但直接打印出来,没有层级结构,很难看。尝试用json.dumps转为json,报错:
Object of type PackageDeclaration is not JSON serializable
需要为json.dumps指定default方法:
fp.write(json.dumps(cu, indent=2, default=node2dict))
node2dict方法定义如下:
def node2dict(obj):
if isinstance(obj, Node):
return obj.__dict__
elif isinstance(obj, set):
return list(obj)
else:
return obj
这样就可以打印出比较好看的语法树结构了。
不支持的java语法特性
python的javalang包对java的方法引用(method reference)还没完全支持,像下面写法:
Arrays.stream(ids).filter(myFilter::contains).toArray(Long[]::new);
解析会报错,形如:
Expected '.' at Keyword "new" line xx, position xx
可改写为:
Arrays.stream(ids).filter(myFilter::contains).toArray(sz -> new Long[sz]);
临时规避之。
也可以做一个Parser的派生类,修复该问题:
class ParserEx(Parser):
def parse_identifier_suffix(self):
if self.try_accept('[', ']'):
array_dimension = [None] + self.parse_array_dimension()
if self.try_accept('.', 'class'):
return tree.ClassReference(type=tree.Type(dimensions=array_dimension))
# patch: support Long[]::new syntax
return tree.MemberReference()
else:
return super().parse_identifier_suffix()
竞品对比
还有一个javac-parser包,该包会启动JVM实例,并解析传入的java源码,但它只输出lex符号,而不会输出语法树,这点不满足我们的要求。
用途
我是用javalang来做java源码安全性方面的静态扫描。举两个例子:
1、若要排查redos攻击,可通过遍历javalang语法树,找到所有调用Pattern.compile、Pattern.matches或xxx.matches的MethodInvocation节点,取出其第一个参数,如果该参数为Literal,直接调用redosHunter工具判断是否有redos风险,否则尝试分析其是否为常量还是变量,再做进一步处理。
2、若要排查日志打印隐私风险,可在javalang语法树上找到类似LOGGER.info、LOGGER.warn这样的MethodInvocation节点,设法找到其参数所涉及的变量,如果变量是基本类型,根据变量名判断是否可能是隐私字段;如果变量是data类,则找出其数据成员中是否包含隐私字段。
界面
涉及推广,需要做一个界面。python的GUI开发有如下选择:
tk
wxPython
pyQt/pySide
pyQt和pySide是很类似的,就是协议不同,后者是LGPL的。QT系因为有QtDesigner工具自动生成代码,生成的界面也比较漂亮,再加上协议的考量,最后优选pySide6。
QT的核心概念是signal和slot,分别对应MFC里的事件消息和事件响应函数。在做多线程应用时,同样要注意:UI控件的操作只能在主线程里进行,如要在子线程里修改UI,须发送signal到主线程,在主线程的slot函数里修改UI。
另外,QT里的子线程不要用python自带的Thread类,而要用QT的QThread,这样才能在子线程里使用signal。并且,我们要让QThread作为类成员常驻,而非局部变量,否则会报错:
QThread:Destroyed while thread is still running