配置流分析深度
流分析通过分析的代码构建路径来检测不同类型的问题。鉴于可能无法分析整个应用程序中的所有路径,您可以设置所需的分析深度级别。更深入的分析会报告更多任务,但对性能有一定影响,内存消耗也会略有增加。
您可以使用 DTP 中的测试配置界面来指定分析的深度。前往报告中心> 测试配置> 静态分析> 流分析高级设置> 性能> 深度分析并选择以下选项之一:
- 最浅(最快):只发现源代码中最明显的问题。仅限于问题的成因与问题出现的位置紧密相邻的情况。此分析类型发现的违规的执行路径通常涉及单个函数中的几行代码,很少会涉及超过 3 个函数调用。
- 浅(快):与“最浅”分析类型类似,只发现源代码中最明显的问题。然而,该类型产生的任务总数更多,并允许检查更长的执行路径。
- 标准:发现包含数十个元素的执行路径的复杂问题。标准分析在浅层分析基础上还能查找更复杂的问题,这些问题可能是由单个函数中的不良流程或由被分析项目不同部分中不同函数间的不当交互所导致的。此分析类型发现的违规通常可以揭示被分析源代码中存在的重要错误,并且通常涉及数十行代码。
- 深(慢):检测与“标准”深度定义的复杂性和性质相同的更多问题。此分析类型的执行比标准分析慢。
- 彻底(最慢):发现更复杂的问题。此分析类型将对代码库进行彻底扫描;这需要更多的时间,但可以发现许多非常复杂的问题,这些问题的违规路径可能涉及被扫描应用程序不同部分的一百多行代码。建议针对夜间运行使用此选项。
流分析深度默认设置为标准。
设置超时策略
除了分析的深度,流分析还利用额外的超时保护来确保分析在合理时间内完成。使用 DTP 中的测试配置界面可以设置合适的策略。前往报告中心> 测试配置> 静态分析> 流分析高级设置> 性能> 超时策略并选择以下选项之一:
- 时间:针对给定热点的分析会在花费指定的时间后停止。注意:在某些情况下,使用此选项可能会导致报告的违规数量略微不稳定。
- 指令:在执行指定数量的流分析指令之后,停止对给定热点进行分析。注意:要为您的环境设置合适的指令数量,请查看生成报告的“设置问题”部分中有关超时的信息。
- 关闭:无超时设置。注意:使用此选项可能需要更多的时间来完成分析。
默认的超时选项是时间,值设置为 60 秒。要获取有关分析期间发生的流分析超时的信息,可查看分析后生成报告中的设置问题部分。
在启用分析数据交换的情况下运行流分析
在此模式下,分析数据将写入磁盘。分析数据交换使用相同的持久存储,与增量分析的过程类似。如果对大型项目运行分析,那么代表着所分析源代码语义模型的分析数据可能会消耗用于运行流分析的所有内存。若发生这种情况,流分析将从内存中删除部分当前不需要的分析数据,之后再从磁盘中重新读取。
一般而言,我们建议在配置了 Xmx JVM 选项的大型 JVM 堆中运行Jtest。这样做是为了最大限度减少交换,从而提高性能。如果内存足够,则可以禁用分析数据交换以加快代码分析速度。
您可以使用 DTP 中的测试配置界面来启用或禁用该模式:
允许将分析数据交换到磁盘: 默认启用 。如果对不需要大量内存的中小型项目运行流分析,或者在内存充足的情况下(例如,对于 64 位系统),可以禁用此选项来加快分析速度。
配置流分析详细度
您可以使用 DTP 中的测试配置界面来配置以下选项:
- 当无法显示原因时,不报告违规:指定流分析在无法显示原因的情况下是否报告违规。
一些流分析规则要求流分析检查可到达某一点的所有可能路径,并验证所有这些路径是否都满足某个条件。在这种情况下,一个违规会与多个路径关联(而在简单的情况下,违规仅由一条路径表示)。这类违规中的所有路径都以违规中所有路径共有的违规点结束。不过,不同的路径可能从代码中不同的点开始。每条路径的开始是一个违规原因(代码中的一个点,明确在违规点出现代码中某个条件的违规)。如果多路径违规的不同路径有不同的原因,流分析将只显示违规点(而不是违规原因)。
与流分析显示的从违规原因开始到违规点的完整路径相比,仅包含违规点的违规信息可能不太容易理解。因此,我们提供了一个选项,可以隐藏无法显示原因的违规。 - 每个点报告的违规不超过一项限制针对每个可疑点报告一个违规(针对一个规则)。例如,当流分析检测到潜在的空指针解引用,并且空值的来源有多个时,将报告一个违规。当详细度设置为这个级别时,流分析速度会快一些。
- 构建分析数据的报告问题:指定当流分析在构建分析数据时遇到问题时是否报告设置问题。
指定 null 检查方法
Null-checking 方法选项允许您指定将空参数传递给方法时的期望返回值。这可以减少误报和冗余路径(当空变量的返回值未知时通常会构建这些路径)的产生。
选择已启用复选框并提供以下信息:
- 完全限定的类型名称 (通配符):包含了方法的类型的完全限定名称。
- 方法名 (通配符):方法的名称。
- 为 null 时返回的值:将空参数传递给方法时应返回的值。
- +子类中的定义:指定是否将子类中的 null 检查函数定义也视为 null 检查函数。
null 检查方法示例
流分析会对分析范围内的方法进行分析。当方法超出当前分析范围(例如第三方库)时,需要进行 null 检查方法参数化。以下示例中的类定义在不同的程序集中,并且超出了分析范围(UString
)。该类使用静态方法以多种方式操作包含 Java 字符串的代码,包括 UString.isEmptyOrNull(字符串)
,其定义为:
public class UString { static boolean isEmptyOrNull(String variable) { if ((variable == null) || (variable.length() == 0)) { return true; } return false; } }
下面的类由流分析的 BD-EXCEPT-NP 规则进行分析:
public class SomeClass { void someMethod(String variable) { boolean flag = variable == null; // violation search starts here if (UString.isEmptyOrNull(variable)) { /** * In case variable is null, we will always get to this branch of the if clause. */ System.out.println("String is empty"); } else { variable.toString(); // FALSE POSITIVE BD-EXCEPT-NP VIOLATION } } }
流分析假设 isEmptyOrNull()
方法的返回值未知。这是因为该方法不在分析范围内。流分析将分析 if
语句的 then
和 else
分支,并在其中一个语句中发现违规。但是,当变量为空时,调用此方法会返回 true。
将 UString.isEmptyOrNull()
添加到 null 检查方法的参数化列表中并指定返回值后,如果跟踪的变量被传递给该方法,流分析将不会报告违规。因为这样就不会分析 if
语句的错误分支,从而避免误报。
null 检查方法限制
添加到此参数化列表中的方法应该是具有原始 boolean 返回值的静态方法。该限制是为了避免参数化过于复杂,并确保不存在可能影响 null 检查方法结果的其他变量。
指定资源
资源选项卡用于定义 BD.RES 分类(资源)规则应该检查哪些资源。这些规则检查在此选项卡上定义和启用的所有资源是否正确使用。
- 指定资源类型。
- 选择已启用复选框。
- 如果合适/需要,禁用不要在应用程序终止时报告违规行为选项。
点击箭头展开资源分配器和资源关闭器选项卡,并完成表格中分配器和关闭器的相关信息。下面提供了有关完成这些选项卡的详细信息。
配置资源分配器
资源分配器表格可以用能够产生资源的方法的描述符来设置。该表格包含以下列:
- 已启用:指定在分析期间是否应考虑分配器。
- 完全限定类型名称 (通配符):声明方法的类型的完全限定名称。如果要描述在任何类型中声明的函数,或者在任何类型之外声明的全局函数,则使用‘*’。
- 方法名 (通配符):分配方法的名称。‘*’可用于表示任意数量的任何符号。
- 资源参数:指定方法在其一个或多个参数中分配资源。要么指定方法分配的从 1 开始的参数编号,要么使用‘*’表示分配所有参数。
- + 子类中的定义:指定子类中的定义(具有给定名称的方法的定义)是否也应被视为分配器。需注意,该设置适用于实例方法和静态方法。
- "this" 对象是一种资源:指定方法在调用此方法的对象中分配资源。
- 返回一个资源对象:指定方法返回分配的资源。
分配方法返回表示分配失败的错误码十分常见。当分配方法返回一个资源时,NULL 值通常表示分配失败。流分析在寻找资源泄漏时需要了解分配是否成功;这有助于只报告实际发生分配的路径中缺失的释放方法调用。在资源分配器方法返回一个资源的情况下,如果返回值不为 NULL,则流分析假设资源已成功分配。
配置资源关闭器
资源关闭器表格可以用能够关闭资源的方法的描述符来设置。该表格包含以下列:
- 已启用:指定在分析期间是否应考虑关闭器。
- 完全限定类型名称 (通配符):声明方法的类型的完全限定名称。如果要描述在任何类型中声明的函数,或者在任何类型之外声明的全局函数,则使用‘*’。
- 方法名 (通配符):关闭方法的名称。‘*’可用于表示任意数量的任何符号。
- + 子类中的定义:指定子类中的定义(具有给定名称的方法的定义)是否也应被视为关闭器。需注意,该设置适用于实例方法和静态方法。
- "this" 对象是一种资源:指定在调用方法的对象中关闭资源。
- 资源参数:指定在一个或多个参数中关闭资源。要么指定方法关闭的从 1 开始的参数编号,要么使用‘*’表示关闭所有参数。
指定始终要分析的方法
总是要分析的方法选项用于定义在执行路径中遇到时始终会被分析的方法。这有助于确保规则在检查特定路径时,会分析通常不会检查的方法。
选择启用复选框并在表格中输入以下信息:
- 完全限定的类型名称 (通配符):包含了方法的类型的完全限定名称。
- 方法名 (通配符):方法的名称。
- + 子类中的定义:指定是否也应在子类中考虑该方法。
指定污染方法参数的 Spring 注解
污染了方法参数的 Spring HTTP 请求注释选项用于配置会污染一个或多个方法参数的 Spring 注解。此选项仅影响 BD.SECURITY 分类中包含‘受污染数据来源’参数的规则,并且该参数已配置为将来自‘Spring http 请求’的数据视为受污染数据。
您可以在以下表格之一配置注解:
- 参数注解表格指定的参数注解仅污染带注解的方法参数。
- 方法注解表格指定的方法注解会污染带注解的方法中所有参数。
这些表格包含以下列:
- 已启用:启用后,规则将在分析过程中检查注解。
- 完全限定的类型名称 (通配符):注解的完全限定名称。
指定注入注解
注入注解选项用于定义针对注入的注解。因此,如果字段或 setter 方法带注解,流分析将不会认为字段值未初始化或为空。
该表格包含以下列:
- 已启用:指定在分析期间是否应考虑该注解。
- 完全限定的类型名称 (通配符):注解的完全限定名称。
分析使用 Spring 框架的项目
分析使用 Spring 框架的项目不需要额外的配置。流分析能够自动识别 Spring 框架特性,并调整报告的违规以提供准确的结果。
以下部分详细介绍了流分析如何处理该框架功能以及对流分析规则违规报告的影响。
处理自动初始化的字段
流分析可以识别用于 Spring 注入的注解,这些注解会自动初始化带注解的字段。如果某个字段带注解,流分析将不会认为字段值未初始化或为空。这可以防止以下规则误报:
- BD-PB-CC - 避免始终计算为相同值的条件
- BD-PB-NOTEXPLINIT - 避免在显式初始化之前使用
- BD-EXCEPT-NP - 避免 NullPointerException 空指针异常
您可以查看自动初始化字段的默认注入注解列表,或通过修改测试配置(静态分析设置> 控制反转 (IOC) 框架设置)来自定义该列表。
处理控制器类
流分析可以识别使用了 Spring 控制器类和 HTTP 请求处理方法的情况。因此:
- 当未验证从 HTTP 请求处理方法返回的潜在受污染数据时,BD.SECURITY.TDRESP 将报告违规(您可以调整规则参数以自定义返回污染数据时报告的控制器及其方法的列表 - 详细信息,请参阅规则文档)。
- 当未验证传递给危险方法的 HTTP 请求参数时,检测受污染数据的规则将报告违规。
流分析假定 Java Validation API 不足以确保正确验证受污染的数据。Java Validation 通常用于验证从外部源获取的数据,而在特定上下文中使用受污染数据前必须进行适当的清理。这意味着,要确保正确的验证,需要根据数据是被传入 SQL 查询语句还是嵌入到 HTTP 响应中来选择不同的验证方法。
识别受污染的数据源
流分析可以识别潜在受污染数据的 Spring 特定来源。在未经验证的情况下将以下来源的数据传递给危险方法时,检测受污染数据的流分析规则会报告违规:
- Spring 环境属性(org.springframework.core.env.PropertyResolver)
- Spring http 请求(org.springframework.web.context.request.WebRequest)
- Spring 特定的数据库接口(org.springframework.jdbc 类)
- Spring 特定的 JMS(Java 消息服务)类(org.springframework.jms.core.JmsTemplate)
指定 Spring HTTP 请求注解
对于假设 Spring http 请求是潜在被污染数据来源的规则,您可以指定会污染方法中一个或多个参数的 Spring 注解;详细信息,请参阅指定污染方法参数的 Spring 注解。
指定可以在测试范围外重写虚方法
为确保流分析识别出指定的虚方法可在测试范围外重写,需在该方法的 Javadoc 注释中添加 @jtest.canBeOverriden。
例如以下代码:
public abstract class Test { protected boolean get() { return true; } void foo(Test test) { if (test.get()) {} // BD.PB.CC VIOLATION } }
如果流分析未发现 Test
类中 get
方法的其他实现(重写该方法且可能返回 false),则会假定 get
方法始终返回 true,并会报告 BD.PB.CC 违规。
但是,继承 Test
类并重写 get
方法的类可以不在测试范围内。在该情况下,可以提供 @jtest.can beoverriden Javadoc 标记,这样流分析就会知道可能存在其他重写实现。
public abstract class Test { /** * @jtest.canBeOverriden */ protected boolean get() { return true; } void foo(Test test) { if (test.get()) {} // BD.PB.CC NO VIOLATION } }
在该情况下,不会报告 BD.PB.CC 违规。