SonarQubeJava自定义规则编写分享

SonarQubeJava自定义规则编写分享

2023年6月26日发(作者:)

SonarQubeJava⾃定义规则编写分享背景当今的⼤部分公司都有对⾃⼰的业务代码进⾏安全性审计的需求,来保证业务代码的安全性,同时代码审计作为SDL中重要的⼀环,可有效保证业务的CIA。但是⼈⼯审计存在严重的性能瓶颈,单纯的使⽤代码扫描器效果也不尽如意,误报问题较多。⽬前较好的⽅法:结合业务,⾃定义规则,结合两者优势。但是⽹上关于这⽅⾯的介绍较少,希望本⽂章能帮助到有需求的同学。选择的扫描为SonarQube,这款扫描器是开源扫描器中较为出⾊的⼀款,有丰富的图像化界⾯和强⼤的语法解析能⼒。准备⼯作1. 下载并运⾏SonarQube,具体步骤请参考官⽹教程。2. 下载sonar-java插件源代码,这也是Java扫描规则集,我们会基于这个规则集编写我们⾃⼰的规则,下载地址:/SonarSource/sonar-javasonar-java插件关键结构java-checks模块:该模块包含最重要的JAVA扫描规则集java-frontend模块:该模块提供JAVA语法解析类,是该插件的基础⼀条规则的必要构成1. java-check中添加⼀条规则2. java-check test模块中添加测试⽤例3. java-check resource模块中添加规则描述,包括⼀个html和⼀个json⽂件4. 在ist中注册规则⽰例解析我们先使⽤java-check中的⼀条扫描规则作为⽰例,先了解下如何编写和注册规则,规则路径如下:tMappingMethodPublicCheck先看规则本体:package ;import ;import tions;import ;import ;import leSubscriptionVisitor;import ;import Tree;import ;@Rule(key = "S3751")public class RequestMappingMethodPublicCheck extends IssuableSubscriptionVisitor { @Override public List<> nodesToVisit() { return tonList(); } private static final List CONTROLLER_ANNOTATIONS = ( "ller", "ntroller" ); private static final List REQUEST_ANNOTATIONS = ( "tMapping", "ping", "pping", "ping", "Mapping", "apping" ); @Override public void visitNode(Tree tree) { if (!hasSemantic()) { return; } MethodTree methodTree = (MethodTree) tree; Symbol methodSymbol = (); if (isClassController(methodSymbol) && isRequestMappingAnnotated(methodSymbol) && !ic()) { reportIssue(Name(), "Make this method "public"."); } } private static boolean isClassController(Symbol methodSymbol) { return CONTROLLER_().anyMatch(().metadata()::isAnnotatedWith); } private static boolean isRequestMappingAnnotated(Symbol methodSymbol) { return REQUEST_().anyMatch(ta()::isAnnotatedWith); }}该规则的核⼼是visitNode,通过重写该⽅法,来遍历被扫描Java⽂件的语法树,该⽅法会在扫描任务运⾏时⾃动被调⽤。该规则的运⾏流程:1. 扫描器加载插件进⾏扫描,解析被扫描⽂件的语法树2. visitNode被调⽤,并将解析好的语法树传⼊3. 运⾏⾃定义⽅法isClassController和isRequestMappingAnnotated4. 使⽤reportIssue上报问题但是只有⼀个规则⽂件是⽆法被扫描器正常加载的,还需要⼀个规则定义⽂件和⼀个规则详情描述⽂件:规则定义⽂件 S3751_3751为该扫描规则的Key,在规则⽂件⾥由@Rule(key = "S3751")定义{ "title": ""@RequestMapping" methods should be "public"", "type": "VULNERABILITY", "status": "ready", "remediation": { "func": "ConstantIssue", "constantCost": "2min" }, "tags": [ "spring", "owasp-a6" ], "standards": [ "OWASP Top Ten" ], "defaultSeverity": "Blocker", "ruleSpecification": "RSPEC-3751", "sqKey": "S3751", "scope": "Main", "securityStandards": { "OWASP": [ "A6" ] }}type:为该规则的类型,包括VULNERABILITY、BUG SECURITY_HOTSPOT、CODE_SMELL等。constantCost:代表要解决该问题⼤概需要花费多长时间。scope:定义要被扫描项⽬的范⽂,包括All、Main、Test。规则详情描述⽂件 S3751_规则详细描述,会在SonarQube Web端规则详情页⾯⾥展⽰出来

A method with a @RequestMapping annotation part of a class annotated with @Controller (directly or indirectly through ameta annotation - @RestController from Spring Boot is a good example) will be called to handle matching web requests. That will happeneven if the method is private, because Spring invokes such methods via reflection, without checking visibility.

So marking a sensitive method private may seem like a good way to control how such code is called. Unfortunately, not all Springframeworks ignore visibility in this way. For instance, if you've tried to control web access to your sensitive, private,@RequestMapping method by marking it @Secured ... it will still be called, whether or not the user is authorized to accessit. That's because AOP proxies are not applied to non-public methods.

In addition to @RequestMapping, this rule also considers the annotations introduced in Spring Framework 4.3: @GetMapping,@PostMapping, @PutMapping, @DeleteMapping, @PatchMapping.

Noncompliant Code Example

@RequestMapping("/greet", method = GET)private String greet(String greetee) {  // Noncompliant

Compliant Solution

@RequestMapping("/greet", method = GET)public String greet(String greetee) {

See

  • OWASP Top 10 2017 Category A6 - Security Misconfiguration
这两个⽂件都位于sonar-java/java-checks/src/main/resources/org/sonar/l10n/java/rules/squid/⽬录下,我们⾃定义的规则json和html⽂件也要放在该⽬录下,⽂件名为KEY_、KEY_。KEY在规则中使⽤@Rule注解定义。测试⽂件编写⼀条好的规则往往需要很多次测试,通过TDD(测试驱动开发)的⽅式来帮助我们写出⼀条好的规则。该⽰例规则的测试⽂件:tMappingMethodPublicCheckTestpackage ;import ;import eckVerifier;public class RequestMappingMethodPublicCheckTest { @Test public void test() { ("src/test/files/checks/spring/", new RequestMappingMethodPublicCheck()); NoIssueWithoutSemantic("src/test/files/checks/spring/", new RequestMappingMethodPublicCheck()); }}通过JavaCheckVerifier类中提供的⽅法,来启动我们的规则扫描⽂件。注册规则在ist中进⾏注册,使⽤add⽅法添加需要注册的规则⾃定义⽰例分享Struts2 S2-057检查规则说明:扫描项⽬中是否使⽤包含S2-057漏洞版本的struts2依赖package ;import ependencyCollector;import ck;import bleList;import ;import Utils;import ty;import ;import ckContext;import dAttribute;import ency;import le;import ;@Rule(key = "Struts2_S2_057Check")public class Struts2_S2_057Check implements PomCheck { @Override public void scanFile(PomCheckContext context) { List dependencies = new MavenDependencyCollector(enProject()).allDependencies(); for (Dependency dependency : dependencies) { LocatedAttribute artifactId = ifactId(); LocatedAttribute version = sion(); if (version != null && artifactId != null && "struts2-core".equalsIgnoreCase(ue()) && !strutsVerCompare(ue())) { String message = "此版本Struts2包含⾼危漏洞"; List secondaries = getSecondary(version); int line = ocation().line(); Issue(this, line, message, secondaries); } } } private static List getSecondary(@Nullable LocatedAttribute systemPath) { if (systemPath != null && lank(ue())) { return ayList(new on("configure check", systemPath)); } return (); } private static boolean strutsVerCompare(String version){ String StrutsVersion1 = "2.3.35"; String StrutsVersion2 = "2.5.17"; String[] versionArray1 = ("."); if(versionArray1[1].equalsIgnoreCase("3")){ if(compareVersion(StrutsVersion1, version) > 0){ return false; } } if(versionArray1[1].equalsIgnoreCase("5")){ if(compareVersion(StrutsVersion2, version) > 0){ return false; } } return true; } private static int compareVersion(String version1, String version2){ String[] versionArray1 = ("."); String[] versionArray2 = ("."); int idx = 0; int minLength = (, ); int diff = 0; while (idx < minLength && (diff = versionArray1[idx].length() - versionArray2[idx].length()) == 0 && (diff = versionArray1[idx].compareTo(versionArray2[idx])) == 0) { ++idx; } diff = (diff != 0) ? diff : - ; return diff; }}规则详细说明:先看⼏个关键点:1.

@Rule(key = "Struts2_S2_057Check")该注解声明本条规则的Key2.

implements PomCheckpublic void scanFile(PomCheckContext context)本条规则实现PomCheck类,重写scanFile,这样插件和扫描器会⾃动解析⽂件,并将解析完后的pom⽂件语法树传递进来3.

strutsVerCompare ⽤来定义哪些版本的Struts2依赖存在漏洞之后在sonar-java/java-checks/src/main/resources/org/sonar/l10n/java/rules/squid/中新建Struts2_S2_057Check_和Struts2_S2_057Check_两个⽂件,⼤家可以参考其他规则来编写。最后在CheckList中进⾏注册。插件编译mvn clean package -==true 跳过签名检查可能遇到的问题编译时提⽰找不到maven2相关类,在IDE中将sonar-java/java-maven-model/target/generated-sources⽬录设置为Generated SourcesRoot2020年3⽉更新:有同学在使⽤⾃⼰插件时,sonarqube会报如下错误:lStateException: Name of rule [repository=squid, key=${YourRuleName}] is empty解决⽅法:将 java-checks/src/main/resources/org/sonar/l10n/java/rules/squid ⽬录下和⾃⼰规则对应的html以及json⽂件更名为${YourRuleName}_和${YourRuleName}_

发布者:admin,转转请注明出处:http://www.yc00.com/news/1687776463a43258.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信