整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

这五款牛逼的 IDEA 插件,堪称代码质量检查利器

着业务的发展,系统会越来越庞大,原本简单稳定的功能,可能在不断迭代后复杂度上升,潜在的风险也随之暴露,导致最终服务不稳定,造成业务价值的损失。而为了减少这种情况,其中一种比较好的方式就是提高代码质量,比如通过代码审查,从而降低错误风险,但是,代码审查难度大,代码缺陷、漏洞不易发现,且审查工作随着代码量增加而增加,审查效率低。

工欲善其事,必先利其器,因此,这篇文章给大家介绍几种检查代码质量的利器,Alibaba Java Coding Guidelines、CheckStyle、PMD、FindBugs、SonarLint,让你在关注代码质量的同时,减少 code review 的工作量,提高 code review 的效率,并通过代码质量分析去反向提升我们的代码编写能力

一、Alibaba Java Coding Guidelines

1、整体介绍:

Alibaba Java Coding Guidelines 专注于Java代码规范,目的是让开发者更加方便、快速规范代码格式。该插件在扫描代码后,将不符合规约的代码按 Blocker、Critical、Major 三个等级显示出来,并且大部分可以自动修复,它还基于 Inspection 机制提供了实时检测功能,编写代码的同时也能快速发现问题所在。

阿里巴巴规约扫描包括:

  • OOP规约
  • 并发处理
  • 控制语句
  • 命名规约
  • 常量定义
  • 注释规范

2、安装步骤:

File > Settings > Plugins > Marketplace 搜索 “Alibaba Java Coding Guidelines”,按照提示进行安装,然后重启即可。

3、使用说明:

3.1、运行方式:

(1)可以Tools > 阿里编码规约 > 编码规约扫描

(2)在编辑界面或者项目区域点击右键,在右键菜单中选择“编码规约扫描”即可:

3.2、菜单功能:

  • 编码规约扫描:开始扫描代码
  • 打开/关闭实时检测功能:实时检测代码,一般机器性能比较好的话可以开启这项功能
  • 切换语言至英文:中英文切换

3.3、运行结果:

扫描完成后显示结果如下,我们可以看到扫描结果主要分为 Blocker(阻挡者)、Critical(严重问题)、Major(主要的)三个大类,它们表示的是问题的严重程度,严重程度由高到低为:Blocker > Critical > Major,至于每一类中都会包含什么样的问题,图中的内容已经说明了一切。

选中其中的一个问题项目,会出现如下内容(如果当前鼠标点击的是最终项,右边区域显示的是其它的内容,后面会再讲到):

(1)指定区域搜索同一类问题:

当点击③处的按钮时,会弹出如下按钮:

这里选择扫描区域,来扫描鼠标选中的同类问题。如果按照默认选择,那么运行后的结果就如下图所示:

这里我们可以看到,显示了整个Project中的所有该类的问题。

(2)预览具体的不规范代码:

如果点击的是最终的问题点或者问题所在的类文件,那显示的就是如下界面,预览该处不规范的代码。

3.4、工具栏功能介绍:

  • Rerun Inspection: 重新运行一次扫描
  • Close:关闭真个AJCG面板
  • Expand All:展开结果的树状结构,整个结果是树状结构的。
  • Collapse All:收起结果的树状结构
  • Go Pre Problem:选择上一个问题
  • Go Next Problem:选择下一个问题
  • Help:帮助
  • Group by Serverity:(不知道如何描述)
  • Group by derectory:按目录分组/按类名分组间切换
  • Filter resoled items:过滤掉已经解决的项
  • Autoscroll to Source:自动滚动到源码
  • Export:导出,可以导出为XML和HTML两种格式
  • Edit Settings:编辑设置

二、CheckStyle:

1、整体介绍:

CheckStyle 侧重检查编码格式和代码风格规范,如命名规范、Javadoc注释规范、空格规范、size度量(如过长的方法)、重复代码、多余Imports等,从而有效约束开发人员更好地遵循代码编写规范。Checkstyle主要是文法层面的代码编写规范的分析,对bug几乎没什么发现能力。

Checkstyle插件中默认内置有2个执行代码检查的配置文件(Sun Checks 和 Sun Checks),但是这两个文件检查的非常详细严格,即使优秀的开源项目也会检查出来有非常多的错误告信息,所以需要导入我们自定义的配置文件。

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “CheckStyle”,按照提示进行安装,然后重启即可。

3、使用说明:

可以看到基本都是一些缩进啥的编码规范,可以不用太关注

三、PMD

1、整体介绍:

PMD侧重面向安全编码规则,且具备一定的数据流分析和路径分析能力,能力比CheckStyle稍微强点,并且 PMD 支持自定义规则,PMD可以直接使用的规则包括以下内容:

  • 潜在的bug:空的try/catch/finally/switch语句
  • 未使用的代码(Dead code):未使用的变量、参数、私有方法等
  • 可选的代码String/StringBuffer的滥用
  • 复杂的表达式:不必须的if语句、可被while替代的for循环
  • 重复的代码:拷贝/粘贴代码意味着拷贝/粘贴bugs
  • 循环体创建新对象:尽量不要在循环体内实例化新对象
  • 资源关闭:Connect,Result,Statement等使用之后确保关闭掉

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “PMDPlugin”,按照提示进行安装,然后重启即可

3、使用说明:

  • 参考文章:http://wjhsh.net/andy-songwei-p-11830812.html

3.1、运行方式:

(1)从Tools菜单中启动:

通过 Tools > Run PMD 可以看到如下的界面,如果通过该方式启动,扫描的范围就是整个项目中的文件了。

  • Pre Defined:预定义的规则,也就是插件自带的检测规则。后面展开的列表中列出了所有的规则列表,想扫描哪一种类型的问题,点击即可。其中“All”表示使用所有的规则。
  • Custom Rules:自定义的检测规则,PMD允许用户根据需要自定义检查规则,默认这里是不可点击的,需要在设置中导入自定义规则文件后方可选择。

(2)从右键菜单中启动:

在文件或者编辑器中点击右键,也可以看到“Run PMD”选项,如果通过该方式启动, 检测范围取决于鼠标或光标当前所选中的区域。

3.2、运行结果:

运行后会出现如上所示的面板,左边工具栏,鼠标停留在上面会提示其功能;右边显示了检测结果,当点击具体某一问题项时,会跳转到对应的源码中。

3.3、配置检测规则:

通过 File > Settings > Other Settings > PMD 可以打开检测规则的设置界面:

在 “RuleSets(规则设置)” 界面可以管理自定义的检测规则。因为在实际工作中,可能需要根据实际情况自定义检测规则,就可以通过这里导入,如果要使用它,需要在启动PMD进行检测时选择该自定义规则。

点击“Options”选项卡,在其中可以配置一些检测规则选项:

其中重点需要留意的是“Skip TestSource”这一项,因为在项目中有不少Android Studio自动生成的测试代码,如下所示,选择上述选项后可以将其过滤掉。

四、FindBugs:

1、整体介绍:

FindBugs 侧重于发现代码中存在的bug,如运行时错误检测(空指针检查、未合理关闭资源、字符串相同判断错(==,而不是equals)等),它可以简单高效全面地帮助我们发现程序代码中存在的bug以及潜在隐患,针对各种问题,它提供了简单的修改意见供我们参考

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “FindBugs”,按照提示进行安装,然后重启即可

3、使用说明:

FindBugs 可以分析单个文件、包下面的所有文件、整个module下的文件、整个project下的文件,右键想要分析的文件名/包名/module/project

分析完之后就会出现结果面板

点击对应的item在右边会定位到具体的代码,这是根据提示进行处理修改就行

4、附:常见的错误信息

4.1、Bad practice 代码坏习惯:

4.2、Dodgy code 糟糕的代码:

4.3、Internationalization 代码国际化相关:

4.4、Performance 代码性能相关:

4.5、Experimental:

4.6、Malicious code vulnerability 恶意破坏代码相关:

4.7、Multithreaded correctness 多线程代码正确性相关:

4.8、Correctness 代码正确性相关:

五、SonarLint:

1、整体介绍:

sonar 比 Findbugs 高了一个层级,它不仅关注常规静态BUG,还关注到了如代码质量、包与包、类与类之间的依赖情况,代码耦合情况,类、方法、文件的复杂度,代码中是否包含大量复制粘贴的代码,关注的是项目代码整体的健康情况。sonar 有两种使用方式:插件和客户端,sonar 的插件名称为 sonarLint

2、安装步骤:

通过 File > Settings > Plugins > Marketplace 搜索 “SonarLint”,按照提示进行安装,然后重启即可

3、使用说明:

右键项目或者文件进行如上图所示操作,执行之后可以看到如下信息,如果代码中有不合理的地方会在report中显示,同时点击错误的地方在右边会给出建议的修改供参考。

4、配置 SonarLint 服务端:

4.1、配置 Sonar 服务器:

sonarLint 插件的使用场景是自用自审,但 sonar 也提供了平台版本,使用场景则是他审,sonar 平台的搭建就不在这篇文章介绍了,感兴趣的读者可以自己上网查看,我们这里主要介绍如何在 sonarLint 插件中配置关联 sonar 平台服务器的工程,进行本地检查:

点击新增按钮,输入Configuration Name,配置sonarlint 服务器的地址,然后下拉框选择 Login/Password,输入 sonarlint服务器的账号密码

4.2、具体 Sonar工程配置:

配置完服务器之后,需要针对具体工程进行配置,点击 connection下拉框,选择上面配置好的服务器连接,然后点击 Search in list,找到对应的工程:

4.3、使用 SonarLint 检查:

配置完上面两步之后,接下来就可以选择要进行检查的类或者目录进行 sonarlint 检查了(跟第3点的使用方式一致),同时,在 commit 代码的时候,勾选 “Perform Sonarlint analysis”,会针对你要提交的代码进行sonarlint检查

总结

  • 检查代码规范的话,直接使用 Alibaba Java Coding Guidelines 就可以了
  • 找 BUG 的话,使用 PMD、Findbugs、SonarLint 相互补充:
    • PMD 自定义能力强,用来自定义项目BUG规则非常好用
    • Findbugs 找 BUG 能力很强,我们拿找到的BUG给新员工培训也很好。
    • SonarLint 规则丰富,比 Findbugs 能覆盖到更全的场景

onarSource目前提供三类产品,分别是SonarLint、SonarQube以及SonarCloud。三类产品虽然应用场景不同,但都给开发人员提供了静态代码分析(SAST)的能力,也就是在不运行程序的情况下通过一系列程序分析技术持续对代码进行理解与检测,发现代码中存在的漏洞。

SonarSource的愿景是希望从根本上改变组织交付软件的方式,帮助研发人员交付清洁代码,即安全、健壮且可以发展的代码。

"代码重构/改善代码质量"这件事情比作投资:“我们拥有的这种方法的美妙之处在于它不再花费;对于相同的投资,你有很好的代码,以后你会有更大的投资回报,所以一开始的‘正确’就尤为重要,这是使软件成为资产而非负债所需的唯一步骤

静态代码扫描工具目前市场现状

在整个的开发链条中还有其他工具也可以检测软件中存在的漏洞,比如IAST,DAST,FUZZ。这些动态测试工具受限于检测漏洞的原理,往往需要较高的使用条件,比如需要准备软件的运行环境、需要在测试过程中与软件进行交互来触发软件的功能、需要准备大量的测试用例。

而基于不同理念所产生的静态分析工具则没有此类限制,只需要接触源代码即可完成分析。这样的天然优势给予了静态代码分析类工具更广阔的市场空间,使得静态代码分析类工具更有可能成为走向研发者市场,成为一类具备普适性的研发效能工具。SonarSource的成功路径也从侧面证明了这一点。然而当我们把目光聚焦在国内市场,并没有发现类似SonarSource的企业。在中国,静态分析类工具仍然处在一个突破使用场景和市场空间的过程中,究其原因,我们认为以下三个方面的问题可能能够做一些说明:

落地成本高

目前国内市场上常见的静态代码分析工具具备较高的使用门槛。从开始接触到成功落地往往需要较多的适配与培训工作,需要企业投入较多的时间与人力成本。在研发工具接受程度与使用习惯还未养成的中国市场,这些先决条件往往成为劝退企业的第一道门槛,导致了很多有相关需求的中小型企业望而却步。

传统产品形态老旧,缺乏现代化集成能力

从用途来说,静态代码分析工具属于软件研发体系的支撑性工具,其使用场景往往会随着软件研发体系的变化而变化。

目前国内市场主流的静态代码分析工具仍然停留在传统的客户端软件时代,用户通过上传代码进行集中式的分析与结果展示。当用户需要在现代化的研发体系(如模块化DevOps敏捷流水线)中去使用相关能力时,这种传统的软件形态往往会带来较多的麻烦,导致落地成本的提升和自动化程度的降低。

目前国外走在前沿的分析类工具,如SonarQube、 Snyk和Coverity均采用了解耦式的模块化设计,方便用户进行流水线集成和自动化调度。

分析精度较差

随着软件产业的飞速发展,过去二十年间用户对于静态代码分析类工具的需求也在不断提升。从最早期进行代码风格的管理,到嵌入式时代对于代码质量的合规要求,到今天对于安全漏洞的治理。软件的复杂程度日益提升,静态代码分析工具试图解决的问题也越发复杂。现有的工具在面对复杂的软件安全漏洞时,分析精度直线下降,误报率往往超过50%。过高的误报率带来的是使用成本的大幅提升,同时也造就了静态代码分析类工具落地成本高,使用复杂的刻板印象。能否解决好复杂代码场景的分析精度问题,是决定SAST类工具接受程度的核心难题。

SonarSource的解决方案

SonarSource提供的一整套解决方案会在开发过程的整个生命周期,以及每个阶段执行检查。整套解决方案的核心理念是“Clean as You Code”。在开发者完成一部分新的代码之后,可以将代码推给扫描工具,并对这一部分的代码的品质进行分析和评价,以保证开发者的代码一直保持满足标准的状态。这个标准的状态通过“Quality Gate”来严格把守,它是发布前的最后一道检查,只有满足所有的要求才能发布到生产环境中,比如代码覆盖率,可靠性评级等等。开发人员可以设置自己的标准,也可以使用SonarSource提供的默认标准“SonarWay”。

三类产品

SonarLint

SonarLint是一个IDE扩展,可帮助开发人员在编写代码时检测和修复质量问题。就像Grammarly拼写检查器一样,SonarLint会实时提供反馈,以便在提交代码之前修复它们。此外,SonarLint会提供详细的规则描述和最佳实践来展示代码问题的原因与解决方案,开发人员可以在编码的同时学习提高自己的技能。

SonarQube

公司主打的SAST产品。产品是分离式的架构,一侧为SonarQube平台,可以在web端查看扫描结果;另一侧是SonarScanner,这是公司十多年来构建的代码分析引擎,内含5000多条规则积累。此外SonarQube 并不是简单地将各种质量检测工具的结果直接展现给客户,而是通过不同的算法来对这些结果进行再加工,最终以量化的方式来衡量代码质量,并进行管理。

SonarCloud

公司推出的SaaS云服务,针对开源项目进行分析检测。基本功能和普通的SonarQube差不多,但提供更多的SaaS级别的服务。

三类问题

上述三种产品本质上共同解决了三类代码问题,分别是代码可靠性,代码安全性以及技术负债(代码可维护性)。

代码可靠性

分析引擎可以针对代码中的Bug以及开发路径存在的问题进行检测。Bug通常是指一种编码错误,可能会导致程序运行时出现错误或意外。

代码安全性检测

SonarQube可以展示代码中的安全热点(Security Hotspots),也就是一些敏感代码,但它们不会影响整体应用程序安全性,也不需要研发人员立刻进行修复。但是事后审查的判断是有必要的,因为研发人员可能也会发现这些“敏感地带”会存在风险。除了安全热点以外,分析引擎还可以针对安全漏洞(Vulnerabilities)进行检测,安全漏洞是需要立即修复的,它们会严重影响应用程序安全性。这样的分类方式进一步降低了误报率,对研发人员更加友好。

分析引擎主要检测的安全漏洞分为两类。第一种是安全注入问题,第二种是安全配置问题。

技术负债(代码可维护性检测)

分析引擎可以检测代码异味,也就是代码可维护性的问题。通常它们使代码混乱且难以维护,换而言之,维护人员不知道如何修复这段代码,最坏的情况是原来的问题还没有修复,就引入新的问题。代码异味包括“七宗罪”,分别是不遵循代码标准,潜在缺陷,糟糕的复杂度分布,重复,注释不足或过多,缺乏单元测试,糟糕的设计

这七点也是检测技术负债的标准(技术负债是指修复代码异味所花费的精力)。

对于修复技术负债的问题,SonarSource也有着自己独到的解决方案。通常来说,主动对整个代码库的修复是费时费力的,也可能会产生功能回归风险。然而程序员总是会在老代码的基础上进行新的修改。所以SonarSource的产品只对于新的代码或者是在老代码上做过修改的代码进行扫描分析,也就是在不引入新的问题的同时逐渐对代码库进行修复,逐渐改进软件的质量。实际上就算没有主动去修复老的代码,通过SonarSource的解决方案,也会被逐渐地被动修复。据估计,第一年后整体代码的大约20%的问题将会被修复,两年后大约35%的代码问题会被修复,5年后大约50%的代码问题会被修复。

销售模式

三款产品中SonarLint和SonarCloud是开源免费的。除了SonarCloud定制的私人服务需要付费之外,公司主要的收入来源来自SonarQube,一共有四个版本,分别是社区版,研发者版,企业版和数据中心版。除了版本之间功能的不同之外,SonarQube的收费模式是根据可分析的最大代码行数每年收license费用。

社区版是完全免费的,它实际上覆盖了SonarQube绝大部分功能,包括支持大多数语言,对代码质量和简单安全问题检测的能力,超过50种的插件等等。但问题是社区版不支持一个项目多分支的形式,这会导致一次扫描产生多个项目结果,增加管理的难度。

研发者版本起价是150美金一年,可使用的最大代码限制行数是10万行,已经可以满足一般研发人员日常的检测需求。此外,增加了C/C++语言的覆盖,增加了分支分析的能力,增强了安全分析的能力,尤其是针对注入这个问题可以做到深度精确分析。

企业版起价2万美金一年,可使用的最大代码限制行数是100万行。除了支持全部的27种语言之外,还提供了可以配置的SAST引擎功能,提供OWASP/SANS安全报告的能力。还赋能企业产出组合管理的能力。

数据中心版本起价10万美金一年,可使用的最大代码限制行数是200万行。这个版本对高可用性和横向扩展性有更好的支持。

此外,三个收费版本的产品价格均会随着最大代码限制行数的增加而提高。

历史沿革

2006年,Freddy Mallet意识到代码质量管理至关重要。代码的缺陷最终会反映到软件交付的质量上,导致各种各样的威胁与损失,比如数据泄露,隐私威胁,财产损失,甚至是对人物理上带来的伤害。Freddy Mallet认为市场上需要一种提供自动化代码审查的产品。Freddy Mallet与好友Simon Brandhof通过集成Java的最佳开源工具在2007年开始了对Sonar平台的开发。Olivier Gaudin随后加入团队,SonarSource于2008年成立。

2010年,SonarSource实现了公司的第一个里程碑——Sonar平台被社区和企业接受,每月下载量超过2K次。各种插件的集成让Sonar备受好评。公司随后加大研发力度,在2013年正式推出SonarQube。

2016年,SonarSource从Insight Venture Partners筹集了4500万美元投资,以进一步加速增长。Richard Wells,MD和Insight Partners副总裁Matt Gatto加入公司董事会。此时的SonarQube已经完成了多次迭代,支持了大量的语言和规则,代码质量分析的能力已经逐渐体现出优势。

2018年,第一个SaaS平台SonarCloud基于市场需求和用户需求推出。在此之前的两年中,SonarSource也纳入了一些与代码安全性相关的检测能力,但是一直没有办法做到高精度的分析。

直到2020年,SonarSource收购RIPS。RIPS Technologies首席执行官兼联合创始人Johannes Dahse博士加入SonarSource担任研发主管。RIPS Technologies是一家致力于创新安全测试技术的公司,以从头构建了其一流的 PHP 代码分析引擎而闻名。它的团队在构建高效的代码分析解决方案方面有着悠久的历史,他们的引擎甚至可以检测复杂和深层嵌套的漏洞。这次收购让SonarSource语言覆盖的广度和RIPS检测的深度相结合,最终实现了多种语言上对安全漏洞的精准分析检测。这也标志着SonarSource正式走入了安全性SAST的领域。

2022年,Sonar从新的和现有的投资者那里筹集了4.12亿美元,估值为47亿美元。该公司将利用这笔投资来扩大销售团队,以实现10亿美元的收入。

核心团队

创始团队初识

联合创始人兼CEO Olivier Gaudin来自瑞士日内瓦,于1998年毕业于法国国立应用科学学院(INSA)的应用数学工程专业。因为通过软件更容易去计算和模拟数学理论的最后结果,所以Olivier选择了偏计算机的方向,主攻Java语言。曾经在IT行业,金融行业,建筑行业工作过。

在一次代码审查任务中,Olivier发现自己很难读懂自己下属写的代码,开始在自己的团队内部开始设置一些基本的编写规范。同时,Freddy和Simon也被同样的问题困扰,他们正在试图寻找一种自动化的工具来解决这个问题,但是并没有找到。Freddy和Simon当时已经在IT咨询公司Hortis共事了几年,分别担任CTO和高级Java研发员。Freddy有着二十多年软件相关工作经验,包括软件研发,软件基础架构设计,软件咨询。Simon也有着十几年的开发经验,两人率先开始研发Sonar平台。

后来,Olivier遇到了Freddy和Simon,被二人的想法吸引并于2007年加入了他们团队。有一次,他询问了Freddy和Simon对“干净的代码”的理解,发现三个人产生了分歧。这更加坚定了他们做出一套被行业认可的代码规范的想法,三人便于2008年创立SonarSource。

新成员的加入

Andrea Malagodi(CTO)

Andrea来自于瑞士日内瓦,于2003年毕业于欧洲工商管理学院的金融专业。曾在摩根大通担任首席技术官和技术负责人 25 年。工作的同时又在2018年攻读完成了美国斯坦福大学的创新科技项目。Andrea于2021年加入SonarSource,他的加入可以帮助SonarSource建立应对强劲增长所需的基础设施。

Gordon Pothier(CFO)

Gordon来自美国马塞诸塞州,于2000年毕业于本特利大学的金融MBA项目。他曾在软件安全公司 Carbon Black担任财务副总裁兼首席财务官,该公司于2019年被VMWare收购。他也曾担任过领先的应用程序安全初创公司Onapsis的首席财务官,于2021年加入SonarSource。Gordon在这一领域有着十多年的经验,有着非常广的人脉与资源。

Manish Gupta (CMO)

Gupta于2022年加入SonarSource,之前曾担任Redis的首席营销官,最近担任Oracle的Java和GraalVM全球营销副总裁,在他任职期间领导了这两家公司实现了多倍增长。他的经验将有助于SonarSource实现10亿美元的收入目标。

未来发展

市场教育方面

Olivier曾在一次采访中表示,“我们希望更多研发人员使用我们的产品,要知道全球有大约7000多万的研发人员。这是我们在此之前从没有遇到过的挑战,我认为我们的产品已经准备好了,剩下的工作就是安全意识上的教育和启发”。

实际上,SonarSource从很早的时候就开始尝试进行安全意识教育的工作。比如每年12月,会在Twitter上发布24个面向研发人员的安全问题解谜挑战,并为成功解谜者提供奖励。这些安全问题都是SonarSource团队在本年中花费大量时间研究和理解现实世界中的漏洞。公司正在尝试通过各种形式增加与用户之间的互动来传递安全意识。

产品创新方面

SonarSource希望在未来可以提高分析引擎的扫描的速度以及对代码安全问题深度分析的能力。SonarSource会继续为开发人员增加价值,优化SonarLint的能力,让研发人员在IDE侧有更好的无摩擦开发体验。

参考资料

  • SonarSource官网,PitchBook,The Forrester
  • https://zhuanlan.zhihu.com/p/532943997
  • https://www.zjdz.gov.cn/dzj/dzj/aqzx/20220113/16876.html
  • https://techcrunch.com/2022/04/26/sonarsource-raises-412m-to-scan-codebases-for-bugs-and-vulnerabilities/?guccounter=1&guce_referrer=aHR0cHM6Ly9hZ2g4MnJubTVsLmZlaXNodS5jbi8&guce_referrer_sig=AQAAAIQbF0gtoD9VOAE5IfLTjzV2QBs3SNa9EGp-EP7xPkX9JfgAyngs0Tt
  • https://www.usine-digitale.fr/article/sonarsource-leve-375-millions-d-euros-pour-detecter-les-erreurs-dans-le-code.N1997837
  • https://zhuanlan.zhihu.com/p/497718625
  • https://www.mordorintelligence.com/industry-reports/application-security-market
  • https://blog.csdn.net/libin95188/article/details/123038727
  • https://www.likecs.com/show-204321455.html#sc=1948
  • https://www.sonarsource.com/blog/code-security-now-theres-a-tool-for-developers/
  • https://www.sonarsource.com/plans-and-pricing/?gads_campaign=North-America-Sonar&gads_ad_group=Sonar&gads_keyword=sonarsource&gclid=EAIaIQobChMIscGv8tD1-wIVHBitBh2WnAXtEAAYASABEgKJrvD_BwE
  • https://ecapital.vc/news/rips-technologies-sold-to-sonarsource/?cookie-state-change=1670567160629
  • https://console.dev/interviews/sonarsource-olivier-gaudin/
  • https://www.challenges.fr/classements/fortune/olivier-gaudin-freddy-mallet-et-simon-brandhof_3328
  • https://www.pme.ch/business/2022/01/24/le-logiciel-libre-etait-pour-nous-une-evidence

场景


  • 极狐GitLab旗舰版已经具备扫描能力,但是某些场景下扫描能力偏弱,所以希望集成第三方扫描工具作为补充。但是,单纯的通过Runner调用的方式,只是松散的集成,无法真正形成「深度集成」的体验。
  • 若可以「在极狐GitLab的漏洞报告中展现第三方扫描工具的扫描结果」,那么,对于集成的体验则会提升很多。本文即针对此目的进行展开说明。


外部扫描器选型 SonarQube-CE


  • 以非常流行的扫描工具SonarQube社区版为例加以说明。


快速入门


直接在.gitlab-ci.yml文件中填入如下内容,触发扫描,即可在漏洞报告中查看结果。


variables:
  sonar_host_url: http://1.13.160.207:9000
  sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d

sonarqube:
  artifacts:
    reports:
      sast:
      - gl-sast-report.json
  script:
  - "/scan.sh"
  image:
     name: satomic/sonarscanner:v6


以下仓库参考:

  • Java: https://jihudemo.online/demo/standard-demo/hello-order/hairou-app-downsteam/-/security/vulnerability_report
  • cpp: https://jihudemo.online/demo/standard-demo/hello-order/cpp-example/-/security/vulnerability_report
  • Python: https://jihudemo.online/mumutech/dev-backend/bigdata/funny-toolkits/-/security/vulnerability_report
  • PHP: https://jihudemo.online/mumutech/dev-backend/dvwa/-/security/vulnerability_report


使用方法

参考dvwa仓库中的配置方法。


SonarQube-CE部署

  • 公网环境中已经部署好SonarQube,可以忽略部署过程,继续往下。
  • 若为私网环境,参考下文部署章节。


环境变量配置

在project级别中,settings - CI/CD - Variables中添加如下2个变量指向SonarQube服务。

  • sonar_host_url: http://1.13.160.207:9000
  • sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d


.gitlab-ci.yml配置

在待扫描仓库中增加.gitlab-ci.yml配置文件,如下,不建议直接把敏感信息如快速入门那样配置在ci文件中。

sonarqube:
  artifacts:
    reports:
      sast:
      - gl-sast-report.json
  script:
  - "/scan.sh"
  image:
     name: satomic/sonarscanner:v6

然后进行扫描即可得到扫描结果。

报告总览

特定issue展现image

文件定位image


SonarQube说明

SonarQube原理

docker方式部署 SonarQube-CE 7.6

根据文章How to get the sonar-report.json file created with sonarqube?、How to get sonar-report.json file to display sonar issues at gerrit level itself中所言,从7.7版本开始,不支持在scanner端导出json格式的报告,因此部署支持的旧版本中的最后一个版本,即7.6版。


PgSql

参考Docker 安装SonarQube 步骤以及遇到的坑进行部署,

创建工作目录


mkdir -p /home/sonar/postgres/postgresql
mkdir -p /home/sonar/postgres/data

创建网络

docker network create sonarqube-network

部署pg

docker run --name postgres -d -p 5432:5432 --network sonarqube-network \
-v /home/sonar/postgres/postgresql:/var/lib/postgresql \
-v /home/sonar/postgres/data:/var/lib/postgresql/data \
-v /etc/localtime:/etc/localtime:ro \
-e POSTGRES_USER=sonar \
-e POSTGRES_PASSWORD=sonar \
-e POSTGRES_DB=sonar \
-e TZ=Asia/Shanghai \
--restart always \
--privileged=true \
--network-alias postgres \
postgres

SonarQube

创建工作目录

mkdir -p /data/sonarqube_dir

修改系统参数

echo "vm.max_map_count=262144" > /etc/sysctl.conf
sysctl -p

运行测试容器

docker run -d --name sonartest sonarqube:7.6-community

拷贝必须文件到本地,并修改权限为777

docker cp sonartest:/opt/sonarqube/conf /data/sonarqube_dir
docker cp sonartest:/opt/sonarqube/data /data/sonarqube_dir
docker cp sonartest:/opt/sonarqube/logs /data/sonarqube_dir
docker cp sonartest:/opt/sonarqube/extensions /data/sonarqube_dir

chmod -R 777 /data/sonarqube_dir/

删除容器

docker stop sonartest
docker rm sonartest

启动SonarQube,其中SONARQUBE_JDBC_URL的IP地址需要修改为实际PgSql数据库的IP

 docker run -itd --name sonar -p 9000:9000 \
 -e ALLOW_EMPTY_PASSWORD=yes \
 -e SONARQUBE_DATABASE_USER=sonar \
 -e SONARQUBE_DATABASE_NAME=sonar \
 -e SONARQUBE_DATABASE_PASSWORD=sonar \
 -e SONARQUBE_JDBC_URL="jdbc:postgresql://192.168.1.4:5432/sonar" \
 --privileged=true \
 --network sonarqube-network \
 --restart always \
 -v /data/sonarqube_dir/logs:/opt/sonarqube/logs \
 -v /data/sonarqube_dir/conf:/opt/sonarqube/conf \
 -v /data/sonarqube_dir/data:/opt/sonarqube/data \
 -v /data/sonarqube_dir/extensions:/opt/sonarqube/extensions\
 sonarqube:7.6-community


生成token

登录SonarQube UI地址( http://IP:9000 ),默认用户名密码为admin/admin,在My Account - Security中生成Token。


部署使用sonar-scanner

参考SonarScanner下载最新版的扫描器。

解压后,进入bin目录,执行如下命令即可完成扫描,扫描完成后可以去SonarQube中查看扫描结果。

./sonar-scanner \
-Dsonar.host.url=http://1.13.160.207:9000 \
-Dsonar.login=333f3410ce3e575d559329e8f3d0a5d4ec8a499d \
-Dsonar.projectKey=my:test \
-Dsonar.sources=/path_to_codes

如果想要在本地生成json格式报告,则增加如下参数

-Dsonar.report.export.path=report.json \
-Dsonar.analysis.mode=preview


扫描器制作

json 格式转换

基于以上内容,关键是把扫描结果转化为极狐GitLab旗舰版所识别的json格式。

json 样本

SonarQube扫描结果issue样本

{
    "key": "AX-Gc4tIjhpt-OIbVVk0",
    "component": "my:fuck:fuck/dvwa/includes/DBMS/MySQL.php",
    "line": 70,
    "startLine": 70,
    "endLine": 70,
    "message": "Extract this nested ternary operation into an independent statement.",
    "severity": "MAJOR",
    "rule": "php:S3358",
    "status": "OPEN",
    "isNew": False,
    "creationDate": "2022-03-14T11:22:52+0800"
}

极狐GitLab报告展现issue样本

{
  "category": "test",
  "message": "这个问题不怎么严重",
  "cve": "python-webhook/MicroService/Service.py:960662f9bd521d32692b07bd8d5b10538924c23c37cec891847f40e436c5c2f:B104",
  "severity": "Medium",
  "confidence": "Medium",
  "scanner": {
    "id": "test",
    "name": "test"
  },
  "location": {
    "file": "python-webhook/MicroService/Service.py",
    "start_line": 26,
    "end_line": 28
  },
  "identifiers": [
    {
      "type": "bandit_test_id",
      "name": "Bandit Test ID B104",
      "value": "B104",
      "url": "https://bandit.readthedocs.io/en/latest/plugins/b104_hardcoded_bind_all_interfaces.htl"
    }
  ]
}

转换器 converter.py

转码程序采用python,编写converter.py文件,内容如下:

# coding=utf-8
# Copyright 2022 Xuefeng Yin, All Rights Reserved

from datetime import datetime
import json
import hashlib

f = open(".scannerwork/report.json", "r")

report = json.loads(f.read())
issues = report.get("issues")


# {u'INFO': 50, u'BLOCKER': 3, u'MAJOR': 5724, u'CRITICAL': 1089, u'MINOR': 1103}
severitys_mapper = {
    "INFO": "info",
    "BLOCKER":"Unknown",
    "MAJOR":"High",
    "CRITICAL":"Critical",
    "MINOR":"Low",
}


# = issue.get("")
def conv(issue):

    component = issue.get("component")
    startLine = issue.get("startLine")
    endLine = issue.get("endLine")
    message = issue.get("message")
    severity = issue.get("severity")
    rule = issue.get("rule")

  	# "": ,
    ret = {
        "category": "sast",
        "message": message,
        "cve": "",
        "severity": severitys_mapper.get(severity, "Unknown"),
        "confidence": severitys_mapper.get(severity, "Unknown"),
        "scanner": {
            "id": "sonarqube",
            "name": "sonarqube"
        },
        "location": {
            "file": component.split(":")[-1],
            "start_line": startLine,
            "end_line": endLine
        },
        "identifiers": [
            {
                "type": rule,
                "name": rule,
                "value": rule,
                "url": ""
            }
        ]
    }

    id = hashlib.sha256(json.dumps(ret, sort_keys=True)).hexdigest()
    ret["id"] = id

    return ret



dateTimeObj = datetime.now()
timeStr = dateTimeObj.strftime("%Y-%m-%dT%H:%M:%S")

gl_sast_report = {
  "version": "3.0.0",
  "vulnerabilities": [],
  "remediations": [],
  "scan": {
    "scanner": {
      "id": "sonarqube",
      "name": "SonarQube",
      "url": "https://docs.sonarqube.org/",
      "vendor": {
        "name": "GitLab"
      },
      "version": "1.7.0"
    },
    "type": "sast",
    "start_time": timeStr,
    "end_time": timeStr,
    "status": "success"
  }
}



for i, issue in enumerate(issues[:]):
    #print("Issue No. %s ---------------------" % i)
    #print("SonarQube: %s" % issue)
    issue_gitlab = conv(issue)
    #print("GitLab: %s" % issue_gitlab)
    gl_sast_report["vulnerabilities"].append(issue_gitlab)


gl_sast_report_file = open("gl-sast-report.json", "w")
gl_sast_report_file.write(json.dumps(gl_sast_report, indent=4, sort_keys=True))
gl_sast_report_file.close()


scan.sh

基于环境变量进行扫描

pwd
/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner -Dsonar.host.url=$sonar_host_url -Dsonar.login=$sonar_login -Dsonar.projectKey=my:test -Dsonar.sources=. -Dsonar.report.export.path=report.json -Dsonar.analysis.mode=preview
ls -l
ls -l .scannerwork
python /sonar-scanner-4.7.0.2747-linux/bin/converter.py
ls -l gl-sast-report.json


Dockerfile

  • 基于ubuntu:18.04制作扫描器
  • 默认没有python环境,安装python环境
  • 拷贝提前下载好的sonar-scanner到镜像内
  • 设定默认工作目录
  • 复制转换器
from ubuntu:18.04
run apt update -y
run apt install python -y
add sonar-scanner-4.7.0.2747-linux.tar .
workdir ./sonar-scanner-4.7.0.2747-linux/bin
add converter.py .
add scan.sh /

构建与推送

制作好的镜像已经推送到DockerHub中,采用前文使用方法中所说的内容即可实现扫描。

docker build -t satomic/sonarscanner:v6 .
docker push satomic/sonarscanner:v6


Findbugs支持

安装插件

Server端安装Findbugs插件

  • 参考SonarQube的使用-集成Findbugs

到SonarQube的容器内部

docker exec -it sonar bash

在插件路径中下载findbugs的jar包

cd /opt/sonarqube/extensions/plugins
wget https://github.com/spotbugs/sonarfindbugs/releases/download/3.10.0/sonar-findbugs-plugin-3.10.0.jar

然后重启sonar即可

docker restart sonar

然后到sonar的UI上检查是否安装成功,出现如下画面即表示可以。


配置Java默认规则

在sonar UI上进行如下配置,这样Java的默认规则就是Findbugs了


Java maven 扫描器制作

因为扫描Java项目需要代码经过编译,所以需要对扫描器配置maven扫描能力。理论上,通过在Dockerfile中添加安装maven包即可,但是失败,因此直接在前述步骤构建出的镜像中操作完后commit出镜像。操作步骤如下:

安装软件包列表

apt install openjdk-11-jdk-headless -y
apt install maven -y
apt install git
apt install wget
apt install unzip
apt install zip
apt install vim
apt install tree

commit命令

docker commit 100fd2c54f0a satomic/sonarscanner:v7-mvn

此时,考虑到参数暴露的问题,仅仅把如下4个参数内置到扫描器内部,因此修改scan.sh文件如下

  • sonar.host.url SonarQube Server地址
  • sonar.login token
  • sonar.report.export.path 本地report.json路径
  • sonar.analysis.mode=preview 本地预览模式
pwd
/sonar-scanner-4.7.0.2747-linux/bin/sonar-scanner -X -Dsonar.host.url=$sonar_host_url -Dsonar.login=$sonar_login -Dsonar.report.export.path=report.json -Dsonar.analysis.mode=preview
ls -l
ls -l .scannerwork
python /sonar-scanner-4.7.0.2747-linux/bin/converter.py
ls -l gl-sast-report.json

再更新如上的scan.sh文件,所以Dockerfile更新为

from satomic/sonarscanner:v7-mvn
add scan.sh /

构建出最新镜像

docker build -t satomic/sonarscanner:v9-mvn .
docker push satomic/sonarscanner:v9-mvn


使用

.gitlab-ci.yml更新

为了支持Findbug的触发,需要使用如下配置

# 正常情况下应该配置在settings设置的环境变量中,而不是如此明文暴露
variables:
  sonar_host_url: http://1.13.160.207:9000
  sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d

sonarqube:
  artifacts:
    reports:
      sast:
      - gl-sast-report.json
  script:
  # 通过在此动态生成 sonar-project.properties 配置,而无需侵入源码仓库
  # 同同时可以提高自由度
  - echo -e "sonar.projectKey=JavaProj\nsonar.projectName=JavaProj\nsonar.projectVersion=1.0\nsonar.sourceEncoding=UTF-8\nsonar.language=java\nsonar.sources=.\nsonar.java.binaries=./target/classes\nsonar.language=java\nsonar.ce.javaOpts=-Xmx2560m -Xms853m -XX:+HeapDumpOnOutOfMemoryError" > sonar-project.properties
  # 检查生成的配置是否正常
  - cat sonar-project.properties
  # 创建构建物目录
  - mkdir -p target/classes
  # 此命令是否执行成功依赖mvn配置的合理性,需要实际仓库的研发介入,配合配置的可用性,默认可访问官方mvn库的外网环境问题不大,内网则不太可能编译成功
  - mvn compile
  # 查看编译产物
  - tree target/classes
  # 执行sonar扫描
  - /scan.sh
  # 查看报告生成大小
  - ls -l .scannerwork/report.json
  image:
     name: satomic/sonarscanner:v9-mvn

扫描结果

参考

  • JavaVulnerableLab Fingbugs报告 / GitHub原始仓库
  • Vulnerability Java Samples Fingbugs报告 / GitHub原始仓库


问题与风险

  • 从SonarQube 7.7版本开始不支持在scanner端直接生成json报告。
  • 7.7之前的版本的报告中也未包含特别详细的信息。


文章转自:《》https://note.youdao.com/ynoteshare/index.html?id=05b5ba9c1c49628901ebd1eadece97cd&type=note&_time=1703486325797