Merge remote-tracking branch 'origin/master'

# Conflicts:
#	sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/RulesList.java
wuhaoyang
DESKTOP-CQC5JJO\Administrator 11 months ago
commit 1952a415a6
  1. 5
      .gitignore
  2. 26
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/RulesList.java
  3. 58
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/DynamicCodeChecker.java
  4. 87
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/DynamicLibraryLoadChecker.java
  5. 92
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/PathAndKeywordCheck.java
  6. 93
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/SystemFunctionChecker.java
  7. 0
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/AbsolutePathDetectorChecker.html
  8. 0
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/AbsolutePathDetectorChecker.json
  9. 9
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/DynamicCodeChecker.html
  10. 13
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/DynamicCodeChecker.json
  11. 9
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/DynamicLibraryLoadChecker.html
  12. 13
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/DynamicLibraryLoadChecker.json
  13. 9
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PathAndKeywordCheck.html
  14. 13
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/PathAndKeywordCheck.json
  15. 9
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SystemFunctionChecker.html
  16. 13
      sonar-keyware-plugins-java/src/main/resources/org/sonar/l10n/java/rules/java/SystemFunctionChecker.json
  17. 22
      sonar-keyware-plugins-java/src/test/files/DynamicCodeCheckerRule.java
  18. 10
      sonar-keyware-plugins-java/src/test/files/DynamicLibraryLoadChecker.java
  19. 10
      sonar-keyware-plugins-java/src/test/files/PathAndKeywordCheck.java
  20. 13
      sonar-keyware-plugins-java/src/test/files/SystemFunctionChecker.java
  21. 29
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/DynamicCodeCheckerTest.java
  22. 23
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/DynamicLibraryLoadCheckerTest.java
  23. 32
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/PathAndKeywordCheckTest.java
  24. 24
      sonar-keyware-plugins-java/src/test/java/com/keyware/sonar/java/rules/checkers/SystemFunctionCheckerTest.java

5
.gitignore vendored

@ -4,10 +4,7 @@ target/
!**/src/test/**/target/ !**/src/test/**/target/
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea/modules.xml .idea/
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws *.iws
*.iml *.iml
*.ipr *.ipr

@ -6,13 +6,10 @@
*/ */
package com.keyware.sonar.java.rules; package com.keyware.sonar.java.rules;
import com.keyware.sonar.java.rules.checkers.ABCVarNameChecker; import com.keyware.sonar.java.rules.checkers.*;
import com.keyware.sonar.java.rules.checkers.AbsolutePathDetectorChecker;
import com.keyware.sonar.java.rules.checkers.UploadFileVerifyChecker;
import org.sonar.plugins.java.api.JavaCheck; import org.sonar.plugins.java.api.JavaCheck;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -32,27 +29,20 @@ public final class RulesList {
* These rules are going to target MAIN code only * These rules are going to target MAIN code only
*/ */
public static List<Class<? extends JavaCheck>> getJavaChecks() { public static List<Class<? extends JavaCheck>> getJavaChecks() {
return Collections.unmodifiableList(Arrays.asList( return List.of(
ABCVarNameChecker.class, ABCVarNameChecker.class,
AbsolutePathDetectorChecker.class, AbsolutePathDetectorChecker.class,
UploadFileVerifyChecker.class PathAndKeywordCheck.class,
/*SpringControllerRequestMappingEntityRule.class, DynamicCodeChecker.class,
AvoidAnnotationRule.class, SystemFunctionChecker.class,
AvoidBrandInMethodNamesRule.class, DynamicLibraryLoadChecker.class
AvoidMethodDeclarationRule.class, );
AvoidSuperClassRule.class,
AvoidTreeListRule.class,
MyCustomSubscriptionRule.class,
SecurityAnnotationMandatoryRule.class*/
));
} }
/** /**
* These rules are going to target TEST code only * These rules are going to target TEST code only
*/ */
public static List<Class<? extends JavaCheck>> getJavaTestChecks() { public static List<Class<? extends JavaCheck>> getJavaTestChecks() {
return Collections.unmodifiableList(Arrays.asList( return Collections.emptyList();
/*NoIfStatementInTestsRule.class*/
));
} }
} }

@ -0,0 +1,58 @@
package com.keyware.sonar.java.rules.checkers;
import org.sonar.check.Rule;
import org.sonar.java.ast.visitors.SubscriptionVisitor;
import org.sonar.plugins.java.api.tree.*;
import org.springframework.expression.EvaluationException;
import java.lang.reflect.Method;
import java.sql.ClientInfoStatus;
import java.util.Collections;
import java.util.List;
@Rule(key = "SessionExpirationDateChecker")
//检测代码中包含动态代码执行操作时,工具进行提示
public class DynamicCodeChecker extends SubscriptionVisitor {
@Override
public List<Tree.Kind> nodesToVisit() {
/**
* Tree.Kind.METHOD方法节点
* Tree.Kind.BLOCK方法的代码块节点
* Tree.Kind.METHOD_INVOCATION 方法的调用节点
*/
return Collections.singletonList(
Tree.Kind.METHOD_INVOCATION
);
}
@Override
public void visitNode(Tree tree) {
MethodInvocationTree node = (MethodInvocationTree) tree;
System.out.println(node);
var expressionTree = node.methodSelect();
if (expressionTree instanceof MemberSelectExpressionTree) {
var exprTree = (MemberSelectExpressionTree) expressionTree;
var name = exprTree.identifier();
if ("eval".equals(name.toString())) {
var varNameNode = exprTree.expression();
if (varNameNode instanceof IdentifierTree) {
var varName = (IdentifierTree) varNameNode;
var symbol = varName.symbol();
var varDecler = symbol.declaration();
if (varDecler != null) {
var variableTree = (VariableTree) varDecler;
var typeName = variableTree.type().toString();
if ("ScriptEngine".equals(typeName)) {
context.reportIssue(this, tree, "程序设计时禁止动态构建代码进行功能实现");
}
}
}
}
}
}
}

@ -0,0 +1,87 @@
package com.keyware.sonar.java.rules.checkers;
import org.sonar.check.Rule;
import org.sonar.java.ast.visitors.SubscriptionVisitor;
import org.sonar.plugins.java.api.tree.*;
import org.springframework.lang.NonNull;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 在动态加载库前应对输入数据进行验证确保输入数据仅能用于加载允许加载的代码库
* 判断逻辑: 当检测到函数中包含调用System.loadLibrary()方法时则认为是在加载动态库然后判断其参数是否由方法入参传递的
*
* @author GuoXin
* @date 2024/1/10
*/
@Rule(key = "DynamicLibraryLoadChecker")
public class DynamicLibraryLoadChecker extends SubscriptionVisitor {
@Override
public List<Tree.Kind> nodesToVisit() {
var nodeType = new Tree.Kind[]{Tree.Kind.METHOD};
return Arrays.asList(nodeType);
}
@Override
public void visitNode(@NonNull Tree tree) {
if (tree.is(Tree.Kind.METHOD)) {
MethodTree method = (MethodTree) tree;
var block = method.block();
if (block != null) {
// 拿到方法入参列表
var methodNames = method.parameters().stream().map((item) -> item.simpleName().name()).collect(Collectors.toSet());
// 使用方法体查看器对代码块进行遍历
block.accept(new MethodBodyVisitor(this, methodNames));
}
}
}
/**
* 该内部类用于遍历方法体中的代码块并检查是否调用了System.loadLibrary()方法并且方法参数为方法入参
*/
static class MethodBodyVisitor extends BaseTreeVisitor {
private final DynamicLibraryLoadChecker checker;
private final Set<String> methodParameters;
/**
* 构造方法
*
* @param checker 动态库加载检查器
* @param methodParameters 方法入参集合
*/
public MethodBodyVisitor(DynamicLibraryLoadChecker checker, Set<String> methodParameters) {
this.checker = checker;
this.methodParameters = methodParameters;
}
/**
* 遍历方法调用语法树节点
*
* @param tree 语法树节点
*/
@Override
public void visitMethodInvocation(MethodInvocationTree tree) {
var methodSelect = tree.methodSelect();
if (methodSelect instanceof MemberSelectExpressionTree) {
var mset = (MemberSelectExpressionTree) methodSelect;
// 判断是否调用了System.loadLibrary()
if (mset.firstToken() != null && "System".equals(mset.firstToken().text()) && "loadLibrary".equals(mset.identifier().name())) {
// 判断方法调用的参数是否是方法入参,在System.loadLibrary()函数仅接受一个参数
var exp = tree.arguments().get(0);
if (exp.is(Tree.Kind.IDENTIFIER)) {
var id = (IdentifierTree) exp;
if (methodParameters.contains(id.name())) {
// 报告问题
checker.context.reportIssue(checker, tree, "在动态加载库前对输入数据进行验证,确保输入数据仅能用于加载允许加载的代码库");
}
}
}
}
}
}
}

@ -0,0 +1,92 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称Java 信息安全性设计准则
* 项目描述用于检查Java源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.java.rules.checkers;
import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 在构建路径名前对输入数据进行验证确保外部输入仅包含允许构成路径名的字符或限制允许访问的目录
*
* @author WuHaoYang
* @date 2024/1/9
*/
@Rule(key = "PathAndKeywordCheck")
public class PathAndKeywordCheck extends IssuableSubscriptionVisitor {
private static final Set<String> TARGET_CLASS_NAMES = new HashSet<>(Arrays.asList("URL","URI","File"));
@Override
public List<Tree.Kind> nodesToVisit() {
return Arrays.asList(Tree.Kind.NEW_CLASS);
}
@Override
public void visitNode(Tree tree) {
if (tree.is(Tree.Kind.NEW_CLASS)) {
NewClassTree newClassTree = (NewClassTree) tree;
String className = newClassTree.symbolType().name();
if (TARGET_CLASS_NAMES.contains(className)) {
checkAndReportIssueIfRequired(newClassTree);
}
}
}
private void checkAndReportIssueIfRequired(NewClassTree newClassTree) {
Tree parent = findEnclosingMethod(newClassTree);
if (parent != null && parent.is(Tree.Kind.METHOD)) {
MethodTree methodTree = (MethodTree) parent;
methodTree.parameters().forEach(parameter -> {
if (parameter.type() != null) {
String parameterType = parameter.type().symbolType().fullyQualifiedName();
System.out.println(parameterType);
String className = newClassTree.symbolType().name();
if (TARGET_CLASS_NAMES.contains(className)) {
// 获取构造方法的参数
List<ExpressionTree> arguments = newClassTree.arguments();
if (!arguments.isEmpty()) {
ExpressionTree firstArgument = arguments.get(0);
String constructorArgument = getArgumentValue(firstArgument);
// 获取方法的入参名称
String parameterName = parameter.simpleName().name();
if (constructorArgument != null && constructorArgument.equals(parameterName)) {
System.out.println("避免在参数中使用 " + className + " 对象的构造方法的参数:" + parameterName);
reportIssue(newClassTree, "避免在参数中使用禁止的关键字");
}
}
}
}
});
}
}
private String getArgumentValue(ExpressionTree argument) {
if (argument.is(Tree.Kind.STRING_LITERAL)) {
return ((LiteralTree) argument).value();
} else if (argument.is(Tree.Kind.IDENTIFIER)) {
return ((IdentifierTree) argument).name();
}
return null;
}
private Tree findEnclosingMethod(Tree tree) {
while (tree != null && !tree.is(Tree.Kind.METHOD)) {
tree = tree.parent();
}
return tree;
}
}

@ -0,0 +1,93 @@
package com.keyware.sonar.java.rules.checkers;/*
*@title SystemFunctionChecker
*@description
*@author Admin
*@version 1.0
*@create 2024/1/11 9:29
*/
import org.sonar.check.Rule;
import org.sonar.java.checks.helpers.ExpressionsHelper;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.tree.*;
import java.util.*;
/**
* 在构建命令前对输入数据进行白名单机制验证
*
* @author WuHaoYang
* @date 2024/1/10
*/
@Rule(key = "SystemFunctionChecker")
public class SystemFunctionChecker extends IssuableSubscriptionVisitor {
private static final Set<String> SYSTEM_FUNCTION_NAMES = new HashSet<>(Arrays.asList(
"exec",
"getRuntime().exec",
"ProcessBuilder.command"
));
@Override
public List<Tree.Kind> nodesToVisit() {
return Arrays.asList(Tree.Kind.METHOD_INVOCATION, Tree.Kind.NEW_CLASS);
}
@Override
public void visitNode(Tree tree) {
if (tree.is(Tree.Kind.METHOD_INVOCATION)) {
MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree;
ExpressionTree methodSelect = methodInvocationTree.methodSelect();
if (methodSelect.is(Tree.Kind.MEMBER_SELECT)) {
MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) methodSelect;
String methodName = memberSelect.identifier().name();
if (SYSTEM_FUNCTION_NAMES.contains(methodName)) {
checkSystemCommand(methodInvocationTree.arguments(), methodInvocationTree);
}
}
} else if (tree.is(Tree.Kind.NEW_CLASS)) {
NewClassTree newClassTree = (NewClassTree) tree;
String className = newClassTree.symbolType().name();
if (className.equals("ProcessBuilder")) {//判断是否为ProcessBuilder
checkSystemCommand(newClassTree.arguments(), newClassTree);
}
}
}
private void checkSystemCommand(List<ExpressionTree> arguments, Tree tree) {
for (int i = 0; i < arguments.size(); i++) {
ExpressionTree argument = arguments.get(i);
// 获取方法的参数
Tree parentMethod = findEnclosingMethod(tree);
if (parentMethod != null && parentMethod.is(Tree.Kind.METHOD)) {
MethodTree methodTree = (MethodTree) parentMethod;
List<VariableTree> parameters = methodTree.parameters();
// 检查执行系统命令对象的参数是否与方法的入参相等
if (i < parameters.size()) {
String parameterName = parameters.get(i).simpleName().name();
if (argument.is(Tree.Kind.IDENTIFIER) && ((IdentifierTree) argument).name().equals(parameterName)) {
System.out.println("参数"+parameterName);
reportIssue(tree, "在构建命令前对输入数据进行验证");
}
}
}
}
}
public void reportIssue(Tree tree, String message) {
context.reportIssue(this, tree, message);
}
private Tree findEnclosingMethod(Tree tree) {
while (tree != null && !tree.is(Tree.Kind.METHOD)) {
tree = tree.parent();
}
return tree;
}
}

@ -0,0 +1,9 @@
<h2>程序设计时禁止动态构建代码进行功能实现</h2>
<p>程序设计时禁止动态构建代码进行功能实现,如必须动态构建,应在动态构建代码语句前对输入数据进行验证,确保仅能用于构建允许执行的代码</p>
<pre>
</pre>
<h2>合规解决方案</h2>
<pre>
</pre>

@ -0,0 +1,13 @@
{
"title": "程序设计时禁止动态构建代码进行功能实现",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"28suo"
],
"defaultSeverity": "Minor"
}

@ -0,0 +1,9 @@
<h2>在动态加载库前对输入数据进行验证,确保输入数据仅能用于加载允许加载的代码库</h2>
<p>在动态加载库前对输入数据进行验证,确保输入数据仅能用于加载允许加载的代码库。</p>
<pre>
</pre>
<h2>合规解决方案</h2>
<pre>
</pre>

@ -0,0 +1,13 @@
{
"title": "在动态加载库前对输入数据进行验证,确保输入数据仅能用于加载允许加载的代码库",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"28suo"
],
"defaultSeverity": "Minor"
}

@ -0,0 +1,9 @@
<p>避免在参数中使用禁止的关键字</p>
<h2>避免在参数中使用禁止的关键字</h2>
<pre>
</pre>
<h2>合规解决方案</h2>
<pre>
</pre>

@ -0,0 +1,13 @@
{
"title": "避免在参数中使用禁止的关键字",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"28suo"
],
"defaultSeverity": "Minor"
}

@ -0,0 +1,9 @@
<p>在构建命令前对输入数据进行验证</p>
<h2>在构建命令前对输入数据进行验证</h2>
<pre>
</pre>
<h2>合规解决方案</h2>
<pre>
</pre>

@ -0,0 +1,13 @@
{
"title": "在构建命令前对输入数据进行验证",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"28suo"
],
"defaultSeverity": "Minor"
}

@ -0,0 +1,22 @@
class DynamicCode {
public void dyan() {
String regular = "function regular(args1,args2,args3){................}";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
try {
engine.eval(regular); // Noncompliant {{程序设计时禁止动态构建代码进行功能实现}}
if (engine instanceof Invocable) {
Invocable invoke = (Invocable) engine;
String result = (String) invoke.invokeFunction(
"regular",
args1,
args2,
args3);
System.out.println(result);
} else {
System.out.println("error");
}
} catch (ScriptException e) {
System.out.println("表达式runtime错误:" + e.getMessage());
}
}
}

@ -0,0 +1,10 @@
// 在动态加载库前对输入数据进行验证,确保输入数据仅能用于加载允许加载的代码库
class DynamicLibraryLoadCheckerExample {
public void loadLibrary(String libraryName, int number) {
String abc = "bac";
System.loadLibrary("/path/to/your/library");
System.loadLibrary(libraryName); // Noncompliant {{在动态加载库前对输入数据进行验证,确保输入数据仅能用于加载允许加载的代码库}}
}
}

@ -0,0 +1,10 @@
class PathAndKeywordCheckRule {
public void getParameter(int arg,String brg,float crg) {
URL url = new URL(arg);// Noncompliant {{避免在参数中使用禁止的关键字}}
URI url = new URI(brg);// Noncompliant {{避免在参数中使用禁止的关键字}}
File url = new File(crg);// Noncompliant {{避免在参数中使用禁止的关键字}}
}
}

@ -0,0 +1,13 @@
class SystemFunctionChecker{
public void add(String command){
Process process = Runtime.getRuntime().exec(command); // Noncompliant {{在构建命令前对输入数据进行验证}}
}
public void det(String commands){
ProcessBuilder pb = new ProcessBuilder(commands);// Noncompliant {{在构建命令前对输入数据进行验证}}
}
}

@ -0,0 +1,29 @@
package com.keyware.sonar.java.rules.checkers;
import com.keyware.sonar.java.utils.FilesUtils;
import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;
public class DynamicCodeCheckerTest {
@Test
void detected() {
DynamicCodeChecker rule = new DynamicCodeChecker();
// Verifies that the check will raise the adequate issues with the expected message.
// In the test file, lines which should raise an issue have been commented out
// by using the following syntax: "// Noncompliant {{EXPECTED_MESSAGE}}"
CheckVerifier.newVerifier()
.onFile("src/test/files/DynamicCodeCheckerRule.java")
.withCheck(rule)
.withClassPath(FilesUtils.getClassPath("target/test-jars"))
.verifyIssues();
}
}

@ -0,0 +1,23 @@
package com.keyware.sonar.java.rules.checkers;
import com.keyware.sonar.java.utils.FilesUtils;
import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;
/**
* 在动态加载库前对输入数据进行验证 单元测试类
*
* @author GuoXin
* @date 2024/1/10
*/
public class DynamicLibraryLoadCheckerTest {
@Test
public void test() {
CheckVerifier.newVerifier()
.onFile("src/test/files/DynamicLibraryLoadChecker.java")
.withCheck(new DynamicLibraryLoadChecker())
.withClassPath(FilesUtils.getClassPath("target/test-jars"))
.verifyIssues();
}
}

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称Java 信息安全性设计准则
* 项目描述用于检查Java源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.java.rules.checkers;/*
*@title PathAndKeywordCheckTest
*@description
*@author Admin
*@version 1.0
*@create 2024/1/9 15:26
*/
import com.keyware.sonar.java.utils.FilesUtils;
import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;
public class PathAndKeywordCheckTest {
@Test
void detected() {
PathAndKeywordCheck rule = new PathAndKeywordCheck();
CheckVerifier.newVerifier()
.onFile("src/test/files/PathAndKeywordCheck.java")
.withCheck(rule)
.withClassPath(FilesUtils.getClassPath("target/test-jars"))
.verifyIssues();
}
}

@ -0,0 +1,24 @@
package com.keyware.sonar.java.rules.checkers;/*
*@title SystemFunctionCheckerTest
*@description
*@author Admin
*@version 1.0
*@create 2024/1/11 9:35
*/
import com.keyware.sonar.java.utils.FilesUtils;
import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;
public class SystemFunctionCheckerTest {
@Test
public void test(){
CheckVerifier.newVerifier()
.onFile("src/test/files/SystemFunctionChecker.java")
.withCheck(new SystemFunctionChecker())
.withClassPath(FilesUtils.getClassPath("target/test-jars"))
.verifyIssues();
}
}
Loading…
Cancel
Save