CodeQL学习笔记(4)-CodeQL for Java(程序元素)

最近在学习CodeQL,对于CodeQL就不介绍了,目前网上一搜一大把。本系列是学习CodeQL的个人学习笔记,根据个人知识库笔记修改整理而来的,分享出来共同学习。个人觉得QL的语法比较反人类,至少与目前主流的这些OOP语言相比,还是有一定难度的。与现在网上的大多数所谓CodeQL教程不同,本系列基于官方文档和情景实例,包含大量的个人理解、思考和延伸,直入主题,只切要害,几乎没有废话,并且坚持用从每一个实例中学习总结归纳,再到实例中验证。希望能给各位一点不一样的见解和思路。当然,也正是如此必定会包含一定的错误,希望各位大佬能在评论区留言指正。

为了更好的阅读体验和更快的更新,请访问个人博客
CodeQL学习笔记(1)
CodeQL学习笔记(2)
CodeQL学习笔记(3)


CodeQL for Java

在了解一些理论性很强的内容之前,我们需要对整个知识体系有一个宏观的概念和一定的理解,后面学习的内容才会不容易迷茫,才不会不知道自己在干什么、学什么、目的是什么、有什么用。下面根据我的理解把CodeQL的逻辑和操作过程简单的描绘出来。

简单来说,我们的程序代码是由逐层嵌套而成的,例如各个package包、类、方法、表达式、变量等,这些都被称作为代码元素。而CodeQL借助了树的概念,创建了自己的一套“抽象语法树AST”概念。在这个概念体系中,它把源代码的每一个代码元素部分全部打散,按照源代码中的嵌套结构逐层形成一个树,顶层节点通常是整个代码文件(即CompilationUnit),然后按照嵌套结构逐层展开,从package到class,从class到method,逐层深入,生成了一棵自顶向下的分层结构的树,而每一个子节点就表示package、class、method、variable等代码元素,这些子节点的代码元素类型体现为整个节点的节点类型。

而对于打散源代码后生成的AST来说,使用Java原本的一些元素定义已经不再合适,因此CodeQL提供了一套类库来表示各种节点的节点类型,即对应java源码的各种元素类型。我们使用这些codeql类库去查询AST中的对应节点的对应信息。

下面给出某个Java demo的AST语法树结构

image-20241104145920869

在学习完一定的内容之后发现CodeQL最难的地方是在QL for java的类库,要去根据相应的源码写出逐步匹配的过程,而在这个过程中的QL类库又非常多,较为杂乱,需要去记Java中每一种类型的数据元素对应的QL类库,这是一个相当痛苦的过程,并且这些类库也有自己的继承、包含关系,要想掌握难度很大。

基本结构

这一部分不要求对每一个方法、每一个用法都了解,只是简单的看一下整个的流程。下面用一个简单的例子跑通全流程来整体理解,只要看一个思路就可以。导入官方数据库进行测试,vscode – shift+command+p:Quick Query

from MethodAccess ma
wherema.getMethod().hasName("equals") andma.getArgument(0).(StringLiteral).getValue() = ""
select ma, "This comparison to empty string is inefficient, use isEmpty() instead."
语句目的细节
import java导入CodeQL支持的标准库每一条查询开头都会导入至少一条
from MethodCall ma定义一个变量使用MethodCall 变量来查找表达式
where ma.getMethod().hasName("equals") and ma.getArgument(0).(StringLiteral).getValue() = ""约束变量,设定条件进行查找ma.getMethod().hasName("equals")限制ma去查找叫做equals的方法。 ma.getArgument(0).(StringLiteral).getValue() = ""表示参数必须是空值。
select ma, "This comparison to empty string is inefficient, use isEmpty() instead."按照格式输出查找结果select <program element>, "<alert message>"输出存在问题的地方

整个的这段代码的目的是查找java中haha.equals("")的部分,因为这个用法不推荐使用,更建议用haha.isEmpty()来代替

写codeql的思维是一种迭代过程,通过比较初级的语句去定位查询到一个比较大的目标,然后根据查询出来的结果再去写一些限制条件来缩减范围,达到更精确地定位到目标的效果。

例如在上面这个例子中,事实上只有当String类型的变量去调用.equals方法才会出现问题,而我们查询出来的结果当中包含着各种类型的变量,我们需要把除了String类型的变量进行排除筛选,因此需要修改代码新增一条匹配规则:

ma.getQualifier().getType() instanceof TypeString and

目的是让找到的目标是String类型

CodeQL库

最重要的是以下五类,下面也只介绍这五类中分别最重要的内容:

  1. 表示程序元素(例如类和方法)的类
  2. 表示 AST 节点(例如语句和表达式)的类
  3. 表示元数据(例如注释和评论)的类
  4. 计算度量的类(例如圈复杂度和耦合)
  5. 浏览程序调用图的类

1. 程序元素

// CodeQL提供的类库
Element
├── Package                    对应java中的包package
├── CompilationUnit            对应java中的编译单元,通常指一个Java源文件
├── Type                       对应Java中的类型
├── Method                     对应Java中的方法
├── Constructor                对应Java中的构造函数
└── Variable                   对应Java中的变量

这些类库用来表示Java中对应的代码元素,但是各个类库之间在继承关系上有一定重合(例如某两个库继承自同一个父类),但是在逻辑表达上不重合,例如Method和Constructor拥有公共父类Callable。

下面重点讲一下其中的Type类库。

Type

PrimitiveType和Reftype是Type的子类

  • PrimitiveType:基础类型,包含java中的boolean, byte, char, double, float, int, long, short,此外在QL中也可以表示java中的void和null
  • RefType:引用类型(非基础类型)
    • Class:Java类
    • Interface:Java接口
    • EnumType:枚举
    • Array:Java数组
    • TopLevelType/TopLevelClass:编译单元顶层声明的类型/类,Type中包含Class;Type可以是类、接口、枚举、注解
    • NestedType/NestedClass:嵌套在另一种类型中的类型/类
      • NestedClass分为LocalClass和AnonymousCLass,分别表示声明在方法或构造函数中的类和匿名类

看下面几个例子

import javafrom Variable v, PrimitiveType pt
where v.getType() = pt andpt.hasName("int")
select v

Variable v表示取出所有的变量,PrimitiveType pt表示定义一个基础类型,v.getType() = pt表示把所有变量各自的类型是什么赋给pt,然后从中选出int类型

import javafrom TopLevelType tl
where tl.getName() != tl.getCompilationUnit().getName()
select tl

tl.getName() 获取的是类型 tl 的名称。tl.getCompilationUnit().getName()获取包含该类型的编译单元(通常是 Java 文件)的名称。

import javafrom NestedClass nc
where nc.getASupertype() instanceof TypeObject
select nc

从 NestedClass 中选择一个实例,取名为 nc,即表示查询将专注于嵌套类,然后判断 nc 的某个父类型是否是 TypeObject 类型的实例

Generics(泛型)
泛型类型定义

在 Java 中,泛型类型(GenericType)可以是泛型接口(GenericInterface)或泛型类(GenericClass)

GenericType
├── GenericInterface     泛型接口
└── GenericClass         泛型类

对于如下java源码

public interface Map<K, V> {int size();// ...
}

在这个例子中,K和V是类型参数,是通过TypeVariable类来表示的。所以要想匹配K和V就要用from TypeVariable tv来匹配

泛型类型实例化

当给泛型类型提供具体的类型参数时(比如 Map<String, File>),我们称之为“参数化类型”(ParameterizedType)。这个 ParameterizedType 是一个具体的实例,与原始的泛型类型(GenericType)不同。

要从参数化类型回到对应的泛型类型,可以使用 getSourceDeclaration 谓词。例如,我们可以写一个查询来查找所有 java.util.Map 的参数化实例:

import javafrom GenericInterface map, ParameterizedType pt
where map.hasQualifiedName("java.util", "Map") andpt.getSourceDeclaration() = map
select pt
  1. hasQualifiedName方法用于找到java.util.Map的所有实例
  2. getSourceDeclaration 在这里具体的效果是帮助我们从参数化类型(ParameterizedType)、通配符类型(WildcardTypeAccess)或原始类型(RawType)获取它们对应的泛型类型(GenericType)。也就是说,如果我们有一个具体的泛型实例,比如 Map<String, File>,使用 getSourceDeclaration 可以回到它的泛型类型声明 Map<K, V>。
泛型的类型边界

再例如下列java源码

class StringToNumMap<N extends Number> implements Map<String, N> {// ...
}

这里N的类型被限制为Number或Number的子类,称N为“有边界的类型函数”,Number是上限。codeql中可以用getATypeBound谓词查询类型参数的边界。

下列codeql代码能查找到所有类型边界为Number的类型变量

import javafrom TypeVariable tv, TypeBound tb
where tb = tv.getATypeBound() andtb.getType().hasQualifiedName("java.lang", "Number")
select tv
  1. TypeVariable tv对应java中Map<K,V>中的K和V。getATypeBound()用于获取tv的类型边界

  2. getType()用于返回边界的具体类型

  3. hasQualifiedName用于判断返回类型是否属于java.lang.Number。

整体思路就是:先找到<>中的variable变量,然后通过它们获取到TypeBound边界,然后判断这个边界是否是Number

泛型的原始类型RawType

再看下面这段java源码

Map m1 = new HashMap();
Map<String, String> m2 = new HashMap<String, String>();

用下列ql语句:

import javafrom Variable v, RawType rt
where rt = v.getType() andrt.getSourceDeclaration().hasQualifiedName("java.util", "Map")
select v

整体思路:拿到所有变量,取出其中类型为原始类型Raw的,然后拿他的通用类型(getSourceDeclaration方法),再判断是否叫做java.util.Map。

v是匹配变量m1和m2,然后通过getType()获取到变量类型(实例),分别对应为Map和Map<String, String>,(在当前这个具体的例子中要筛选类型为Raw原始类型的,因此后面一个被排除,因为他是ParameterizedType参数化类型),再通过getSourceDeclaration()方法获取到这些变量类型的类型源Map和Map<K, V>,最后通过hasQualifiedName方法判断是否是Map方法

上述这段代码只匹配原始类型m1,而不会匹配m2

通配符类型
Map<? extends Number, ? super Float> m;

? extends Number 表示一个上限为 Number 的通配符,而 ? super Float 表示一个下限为 Float 的通配符

在 CodeQL 中,用 WildcardTypeAccess类来表示通配符类型,可以用getUpperBound和 getLowerBound来分别获取上限和下限。

变量
  • Field表示一个 Java 字段。
  • LocalVariableDecl表示局部变量。
  • Parameter表示方法或者构造函数的参数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/8014.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

动态规划28:376. 摆动序列

动态规划解题步骤&#xff1a; 1.确定状态表示&#xff1a;dp[i]是什么 2.确定状态转移方程&#xff1a;dp[i]等于什么 3.初始化&#xff1a;确保状态转移方程不越界 4.确定填表顺序&#xff1a;根据状态转移方程即可确定填表顺序 5.确定返回值 题目链接&#xff1a;376.…

【zlm】h264 vp9 尝试研究

目录 编译与使用libvpx 打包lib 解决方案一 libvpx直接引用 IVF格式 编译libvpx windows下编译libvpx 参考文章 编译与使用libvpx 我们用最新的&#xff1a; x86_64-win64-vs16 最简单的视频编码器&#xff1a;编译&#xff08;libx264&#xff0c;libx265&#xff…

顺序表专题

目录 0. 什么是数据结构&#xff1f; 0. 为什么需要数据结构&#xff1f; 1.顺序表的概念及结构 2.顺序表分类&#xff1a; 3.动态顺序表的实现 4. 顺序表的应用 5. 顺序表的问题及思考 0. 什么是数据结构&#xff1f; 数据结构是由“数据”和“结构”两词结合而来 什…

关于使用svgIcon 菜单折叠 显示文字情况

使用的工具&#xff1a;vue2&#xff0c;ant design vue 问题&#xff1a; **解决&#xff1a;在<svg-icon> 外面包一层 <a-icon> ** 使用: 在 main.js 中&#xff1a;

【JAVA毕业设计】基于Vue和SpringBoot的师生健康管理系统

博主说明&#xff1a;本文项目编号 T 052 &#xff0c;文末自助获取源码 \color{red}{T052&#xff0c;文末自助获取源码} T052&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

双向链表专题

双向链表 1. 双向链表的定义和结构2. 双向链表的实现2.1 结构声明2.2 双向链表的初始化2.3 双向链表的打印2.4 尾插2.5 头插2.6 在指定位置之前插入2.7 在指定位置之后插入数据2.8 尾删2.9 头删2.10 删除指定位置的节点2.11 查找2.12 链表的销毁 3. 双向链表的细节 &#x1f52…

发票真伪查验方式-python数电票批量查验接口、发票ocr文字识别提取

在当今的商业环境中&#xff0c;确保交易的安全性和透明度是每个企业追求的目标。随着电子商务的迅猛发展&#xff0c;发票管理成为了企业财务管理中不可或缺的一环。面对海量的电子发票&#xff0c;企业财务也无需惊慌&#xff0c;翔云发票查验API接口&#xff0c;可以为企业提…

html+js+css实现拖拽式便签留言

前些日子在网上冲浪时&#xff0c;看到一个便签式留言墙&#xff0c;让人耳目一新。心想这个看着不错&#xff0c;额想要。于是便开始搜寻是否有相应开源插件&#xff0c;想将其引入自己的博客中。但是搜寻了一圈&#xff0c;都没有符合预期的,要么功能不符合。有的功能符合&am…

初识网络编程TCP/IP

目录 前言相关名词解释应用层协议——HTTP传输层协议socketTCP帧头格式三次握手、四次挥手 UDPTCP的socket实现 参考博文 前言 刚碰到网络编程&#xff0c;会出现一堆协议、概念、这层次那技术的&#xff0c;头都大了&#xff0c;还是得总结总结…… 相关名词解释 ✨✨网络…

Vue2进阶之Vue3高级用法

Vue3高级用法 响应式Vue2&#xff1a;Object.definePropertyObject.definePropertythis.$set设置响应式 Vue3&#xff1a;Proxy composition APIVue2 option API和Vue3 compositionAPIreactive和shallowReactivereadonly效果toRefs效果 生命周期main.jsindex.htmlLifeCycle.vue…

树叶分类竞赛(Baseline)以及kaggle的GPU使用

树叶分类竞赛(Baseline)-kaggle的GPU使用 文章目录 树叶分类竞赛(Baseline)-kaggle的GPU使用竞赛的步骤代码实现创建自定义dataset定义data_loader模型定义超参数训练模型预测和保存结果 kaggle使用 竞赛的步骤 本文来自于Neko Kiku提供的Baseline&#xff0c;感谢大佬提供代码…

与C语言的旅程之分支与循环(2)

与C语言的旅程之分支与循环 C语⾔是结构化的程序设计语⾔&#xff0c;这⾥的结构指的是顺序结构、选择结构、循环结构&#xff0c; 目录 与C语言的旅程之分支与循环 1. if语句 1.1 if ​编辑1.2 else 1.3 分⽀中包含多条语句 1.4 嵌套if 1.5 悬空else问题 2. 关系操作符…

springBoot 自动配置与starter

目录 一、自动配置 Springboot实现自动配置的核心机制 Conditional的作用是什么&#xff1f; 如何自定义自动配置&#xff1f; 步骤 例子分析 自动配置的优先级 如何禁用特定的自动配置&#xff1f; 二、starter 如何理解Spring Boot中的starter&#xff1f; 如何自…

《Python编程实训快速上手》第三天--字典和结构化数据

一、字典 1、字典数据类型介绍 myCat {"size":"fat","color":"gray"} 特征&#xff1a; 字典输入时带上{}字典中每一个值是以键值对形式存在&#xff0c;先写键&#xff0c;再写值 2、字典与列表 列表索引必须是整数&#xff0c;字…

Pinia小菠萝(状态管理器)

Pinia 是一个专为 Vue 3 设计的状态管理库&#xff0c;它借鉴了 Vuex 的一些概念&#xff0c;但更加轻量灵活。下面将详细介绍如何使用 Pinia 状态管理库&#xff1a; 安装 Pinia 使用 npm&#xff1a;在项目目录下运行npm install pinia。使用 yarn&#xff1a;在项目目录下运…

【智能算法应用】哈里斯鹰算法优化二维栅格路径规划问题

摘要 本文研究了基于哈里斯鹰优化算法&#xff08;Harris Hawks Optimization, HHO&#xff09;的二维栅格路径规划方法。HHO算法模拟哈里斯鹰的猎食行为&#xff0c;通过迭代搜索过程找到从起点到终点的最优路径&#xff0c;避开栅格中的障碍物。实验结果表明&#xff0c;HHO…

vue/react做多语言国际化的时候,在语言配置中不同的语言配置不同的字体,动态引入scss里面

如果想直接在vue文件的css里面使用&#xff0c;就可以使用i18n的t函数&#xff0c;注意t外层也有引号&#xff1a; font-size: v-bind("t(style.teamCurModelFontSize)"); 前提是要引入t函数&#xff1a;

优衣库在淘宝平台的全方位竞品分析与店铺表现研究:市场定位与竞争策略透视

优衣库品牌在淘宝平台的全方位竞品与店铺表现分析 一、品牌商品分析 1.商品列表与分类分析&#xff08;数据来源&#xff1a;关键词商品搜索接口&#xff1b;获取时间&#xff1a;2024.08.30&#xff09; 商品类别分布柱状图&#xff1a; 根据关键词商品搜索接口获取到的优衣…

spark新能源汽车推荐系统-计算机设计毕业源码42422

摘要 本论文致力于探讨基于Spark技术的新能源汽车推荐系统新能源汽车分析及可视化内容。系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;利用Python编程语言中的爬虫功能&#xff0c;实现对懂车帝的汽车信息数据的爬取&#xff0c;作为系统的数据来源&#xff0c;并…

Element UI组件Dialog显示闪动问题【解决方案】

在ElementUI中&#xff0c;el-dialog弹窗确实有时会导致页面出现抖动或闪动的问题。这通常是由于弹窗出现时对页面布局的影响&#xff0c;特别是滚动条的出现或消失&#xff0c;导致了页面的重新布局和渲染。以下是一些解决或缓解这一问题的方法&#xff1a; 解决方案 1. 关闭…