如何产出规范、安全、高质量的代码?
对于一个软件开发团队,可以通过哪些代码质量指标和扫描方法让团队产出规范、安全、高质量的代码?让开发团队运行的安全、透明、可靠?本文总结了其中一些实践和工具,包含常见代码质量扫描工具、代码质量指标、第三方依赖管理、安全运维等几个方面,主要适用于 Java/JavaScript 技术栈的 web 项目,希望对于想要规范化自己的项目的 Tech Lead 有所帮助。
代码扫描和常见质量指标
“祸患常积于忽微”,往往一些奇怪的 bug 都是一些不规范的小问题造成的。德国飞机涡轮机的发明者帕布斯·海恩提出的一个在航空界关于飞行安全的法则,法则指出: 每一起严重事故的背后,必然有 29 次轻微事故和 300 起未遂先兆以及 1000 起事故隐患。应用于软件开发中,如果项目中代码混乱不堪,必然会在某个时候最终爆发大量的问题。
扫描工具
checkstyle
findbugs
通过 Bug Patterns 的概念,寻找代码中可能出现的 bug,检查项目主要包括:不良编程习惯导致的问题、性能问题、安全问题、线程问题等。例如,应使用 equals 判断相等,而不是 “ =” 操作符、流需要关闭、线程资源需要释放等问题。findbugs 的模式库对编程经验也有较好的提升作用。还可以导入和编写自己的 Bug Patterns 完善检查机制。
simian
pmd
pmd 是一款跨语言的通用静态扫描工具,具备一部分 checkstyle、findbugs 的功能,不再赘述。
ESlint/TSlint
前端界的 checkstyle , TSlint 设计用来做 TypeScript 类型检查,ESlint 作为代码风格检查工具。不过现在 ESlint 也提供了TypeScript 类型检查功能,基本上 ESlint 能整合这两个功能。由于性能问题, TypeScript 也采用了 ESLint 作为 TSlint替代的检查工具。
SonarQube
SonarQube 是一款用于代码质量管理的开源工具,它主要用于管理源代码的质量。SonarQube 和上面的工具不太一样,SonarQube 设计目的是提供一个平台,通过插件的方式提供对各个语言进行支持,也可以和 checkstyle、pmd、simian 等工具进行集成。SonarQube 一般需要单独部署成一个服务,提供数据库,可以记录扫描结果等信息。
npm audit
npm audit 是 npm 6 之后的版本 自带的一个前端安全扫描工具,可以扫描 npm 依赖中的潜在的漏洞威胁。这些引入的漏洞可能威胁用户开发的机,另外也可能被带入 bundle 文件发布到线上,带来安全问题。目前 npm audit 会在 npm install 完成后自动执行,需要留意安全威胁报告。
Fortify SCA
Fortify SCA(Source Code Analyzer) 是一款非常优秀的代码安全扫描工具,用于分析代码中潜在的安全问题。通过调用语言的编译器或者解释器把代码(Java、C、C++等源代码)转换成一种中间媒体文件 NST(Normal Syntax Trcc),然后通过模式匹配相关的方式抓取存在于漏洞库中的漏洞。例如,上传的文件没有做检查等 XSS 攻击。
OWASP Dependency-Track
开放式 Web 应用程序安全项目(OWASP)是一个非营利组织,提供了很多安全标准、数据库、社区和培训。其中一个工具就是 OWASP Dependency-Track,可以对第三方依赖包中的知名漏洞进行检查,扫描结果受到漏洞数据库的更新影响。
archunit 架构规范检查
前面的检查是代码层面,archunit 可以用于代码架构检查,可以定义规则检查每个包中的实现是否符合规范。例如,controller 包中的类不能实现 service 的接口,repository 下的类必须实现 Repository 接口。通过 archunit 可以减少 codereview 的工作量,避免项目的结构被破坏。
统计工具
最佳搭配
Java 后端:
checkstyle Java 代码风格守护,Java 项目至少应该配置一个默认的 checkstyle 规则。至少让项目干净,没有无用、重复的代码,以及超大的类和方法。建议做到每次提交代码前检查。
findbugs 常见不规范的代码检查,一些空指针、equals 检查非常有用,而且 IDE 的插件也很好用。
前端:
eslint 守护 JavaScript 代码风格,eslint 搭配一个 .editorconfig ,可以方便的让编辑器保持同 eslint 一致的代码风格。 npm audit 项目中第三方包的威胁扫描,npm 自带无需额外安装,npm 6 以后自运行,需要关注并修复报出的安全问题。
安全:
fortify 扫描代码中的漏洞,用它检查出来的大部分安全问题都是注入攻击、XSS 等攻击,这些问题明显可以在开发过程中避免。可以作为 Jenkins 插件配置,和单元测试作为同一阶段运行。 OWASP 插件 用来扫描第三方依赖漏洞,因为项目中的依赖不会像源代码一样频繁变化,推荐使用 Jekins 插件,定期执行即可。
常用代码质量指标参考
编译告警数,大部分程序员基本上忽略 warning,但是编译器出现了告警是一种不好的体现,意味着软件可能工作,但是存在不好的实践,而这种不确定性,会带来不确定的 bug 最终让人一头雾水。编译过程中的告警,尽量消除掉,编译告警的值推荐消除到 0。 平均函数代码行数,过大的函数会导致阅读困难,而且往往过大的函数职责不够单一,一般将一个方法代码行数控制到 30 - 50 行。 平均文件代码行,和平均函数代码行一样,过长的文件一样难以维护,一般一个文件10多个方法,因此文件的代码行数一般控制到 300 - 500 行。 冗余代码,有时候我们代码中可能存在未使用的方法、变量等代码,这让维护者一头雾水,通常需要清零。 总文件重复率,出现重复文件的次数。除了编写单元测试的情况下,业务代码不应该出现重复代码,推荐值为 0。 总代码重复度,代码的重复度检查,限于扫描工具的识别模式,需要有一定的容忍度,推荐值在 5% - 10% 平均函数圈复杂度,圈复杂度用来衡量一个模块判定结构的复杂程度。如果一个方法内部有大量的 if 语句嵌套,意味着这个方法的实现质量低下,且程序复杂度高不利于维护,推荐值小于 5%。 安全告警,如果配置了安全扫描工具,例如 Fortify,安全威胁应该被清零。 代码缺陷,如果配置了缺陷扫描工具,例如 Findbugs,需要清零。
第三方依赖规范化
作为开发工具引入,例如 gcc、Jenkins,基本没有开源协议问题,但是需要注意开发机、CI 会有安全风险。Jenkins 曾出现过漏洞,CI 服务器被当做远程矿机使用。
作为服务部署使用(SaaS),部分开源协议会限制这种使用方式,第三方依赖的安全问题会威胁服务器。
通过软件包再发布,大部分开源软件对这种使用方式有较多要求,例如 GPL 开源协议具有传染性,要求使用了 GPL 的项目也要开源。
拷贝源代码引入项目,非常不推荐这种方式,尽量通过包管理的方式引入。
常见商业友好的开源协议
第三方依赖管理
是否有开源义务需要履行 引入的第三方依赖是否有 CVEs等漏洞 第三方开源软件是否仍然在维护
运维安全
防火墙用于环境隔离
凭据管理
使用环境变量对密码信息进行覆盖。
使用Spring boot 的项目可以配置 jasypt,使用 jasypt 将密码加密,将生成的加密串配置 ENC(加密串) 到工程的配置文件中。加密过程可以加盐作为解密的凭据,“盐” 可以不存放到工程中,在工程部署的时候注入即可。
如果使用 Jenkins 等 CI/CD 工具,可以使用构建平台提供的凭证管理工具。
如果使用 Spring cloud,可以使用 spring cloud vault 组件部署一个凭证管理服务
堡垒机
定期对系统软件扫描
写在后面