本文作者:潘金赤 —— CODING 产品总监 腾讯云研发平台负责人,十年研发能效建设经验 CODING 代码扫描产品负责人
有位小伙子在办公大楼门口抽烟,一位路人经过他的身边对他说:“你知不知道这个东西会危害你的健康?你有没有注意到香烟盒上的那个警告(Warning)?”
小伙子说:“没事儿,我是一个程序员。”
路人说:“这又怎样?”
程序员回答道:“我们从来不关心 Warning,只关心 Error。”
以笑开场,这是一篇写给极少使用/了解代码扫描工具的用户的“启蒙”读物。一方面因为代码扫描存在一定的技术壁垒,涉及到词法/语法分析、编译注入、模式识别及安全等相关领域,想要了解这方面的内容可能难以下手;另一方面,由于目前大众对于代码扫描产品及其领域还存在着较多误解,极大程度上影响了代码扫描的使用体验,更有甚者直接把 Lint/Style 与扫描划了等号,让人啼笑皆非。
CODING 代码扫描自开放试用以来,已累计为 5000+ 团队提供扫描服务,帮助开发团队及时发现了大量潜藏的代码缺陷、安全漏洞以及不规范代码。希望通过这篇文章,以一些常见场景为例,通俗易懂地解释代码扫描的价值与使用方法,帮助读者深入理解,快速上手,让代码扫描产品在助力企业建设 DevSecOps 的道路上,发挥最大的价值。
抛开那些老生常谈的 质量前移 或 质量内建 的概念,从实际运用的角度来看,代码扫描往往是一个团队向 DevOps 转型的第二步(第一步是持续集成/流水线)。一是因为流水线上只跑了编译打包部署还是略显单薄,二是相比单侧、接口自动化及 e2e 自动化,接入代码扫描的成本是最低的。以 Jenkins 为例,只需要运维在 Jenkins 集群中安装 SonarQube 插件,再在 Jenkinsfile 中增加一行命令,就可以在无需开发人员介入的前提下,完成代码扫描的接入。
此外,稍有代码文化意识的开发也会在本地 IDE 中安装插件做本地检查,一旦出现语法或者风格问题时能直接在 IDE 上标注警示甚至自动修复。
越容易得到的东西往往也最容易被忽视,IDE 的 auto inspect/format 或流水线的静默执行,容易让研发淡化代码扫描的感知和价值:我有做 Style check,完成了代码检查任务、研发上线周期太紧张,扫描的问题以后再看、代码扫描发现的问题无伤大雅、代码扫描这个工具/环节可有可无。事实上,各大软件/互联网厂商每年都会花费上百万购买各类扫描软件 License(SonarQube、Coverity、Checkmarx 等),而这些公司也均为估值十多亿的行业佼佼者(16 年 Sonar 获 4500 万美金融资,14 年 Coverity 以 3.75 亿被收购)。巨大的市场价值和卑微的存在感,为什么会出现这样的现象?要弄清楚这个问题,首先看看代码扫描能帮助我们发现哪些问题。
之所以列为第 0 项,是因为我认为这个问题甚至不属于代码扫描的范畴。目前有非常多的 IDE 和插件集成了语法检查相关的功能,帮助开发在研发过程中检查、提示甚至是自动修复语法问题,解决了一些代码质量上的问题,但这是语法解析器的职责,与代码扫描关系甚微。
很多读者看到这可能会露出“我又懂了”的表情,这是目前代码扫描给大家留下的最普遍的感知:检查有没有注释、缩进是空格还是 Tab、大括号是另起一行还是接着上一行等等。诸如此类的检查标准很容易在团队中引发论战,同时因为这类问题也并不阻碍功能逻辑的正确运行(不关心 Warning,只关心 Error),所以很多人对代码扫描的尝试就到这里了。
然而,代码规范真的无足轻重吗?
如果此处没有特别提醒,你能意识到动态语言中返回值不一致,后面可能存在什么困扰么?
如果此处没有特别提醒,你能意识到入参这里为可变对象,可能会引入什么问题么?
如果继续放任 for、if、try 的嵌套,之后这部分代码该如何读?
后续变更模型字段后,还能记得需要修改多处相同代码吗?
代码规范类的扫描就是为了解决 “在代码里下毒” 的最有效手段。同时针对多人合作项目,如果要避免 “当我写这个代码时,只有我和上帝知道是什么意思;一个月后,只有上帝知道” 的场景,遵循统一的代码规范也是非常必要的。
很多人抱有侥幸心理: “我的代码可能就这一个版本,以后不需要再维护,所以能跑就 OK 了”。 那么让代码扫描来帮助确认一下,你的代码真的能跑吗?
这些空指针问题,你确定都能测试出来?
数组越界的问题,通过人肉 CR 发现的难度有多大?
更别提这种内存泄漏问题,没有工具帮助人肉定位,管理内存还是需要一些功夫。
从这个角度来看,代码扫描等同于测试环节,是保障应用功能正常的有效手段,也能更高效地发掘出更有深度的技术问题。
可能有的读者还会觉得 “我的功能很简单,点几下就测试通过了,没什么别的问题”。要知道一个应用程序除了要从功能上满足用户,还需要盯防虎视眈眈的黑产, 万豪泄露用户数据遭受重罚 类似的案件历历在目,我们有多大的把握能保证不是下一个目标?
拖库很重要的一个切入点就是 SQL 注入,而这类问题用代码扫描工具可以很容易发现。
远程命令执行也是攻克目标机器的常用手段,许多常用的开源组件都被爆出过类似问题,你确定你的安全意识比 Apache 还要好吗?
还有 CSRF、XSS、XXE、反序列化等多重攻击手段,如果每个一线程序员都需要对这些了如指掌小心避让,那管控成本将直线上升。通过代码扫描快速查找与定位风险,可以以最低成本为数字资产保驾护航。静态代码分析(SAST)也是 DevSevOps 里最基础、最低门槛的检测方式之一。
“别逗了,聊代码质量怎么还能聊出公关问题”, 先别笑,让我们看一则新闻: vivo 的升降摄像头:流氓软件检测器还是智商鉴定器?
简单来说,Android 应用程序在获取摄像头参数时,调用的函数可能会触发摄像头的升起,但实际上看客并不会深究这里的技术实现细节。打开了摄像头,就是想偷拍用户,这在当时是一场不折不扣的公关危机,还掀起了不小的风波,波及到了各方。在对外科普辟谣的同时,腾讯内部也组织出了一套敏感 API 扫描方案,通过代码扫描工具来扫描项目中的敏感接口,提醒开发人员自查确认,防止造成更大的风险。
通过上文,大家可能逐步意识到了代码扫描为团队带来的价值: 以低门槛无侵入的方式保障代码质量和安全,那么就去下载了 SonarQube、Spotbugs、Checkstyle 等工具,简单配置后便在本地或 Jenkins 流水线上跑起来了。但既然代码扫描更偏向于本地离线的工具,CODING 为什么要在线上平台提供代码扫描呢?
即便是本地扫描,我们也不希望本地规则和远端的规则有差异,导致本地扫描通过提交后又被驳回。解决这个问题最合理的方式是 IaC,即扫描方案和过滤条件等都以本地配置文件的方式去保存。
但并不是所有工具的规则配置都可以本地化管理,例如过滤条件、对比分支等和应用场景强相关的配置项。针对这类诉求,应对的思路有两种:
1. 用户在平台侧完成统一的配置(包括工具规则、过滤条件、对比分支等),配置完成后生成配置 ID。本地扫描时不再基于本地配置文件,而是基于远端的配置 ID。
codedog_client localscan --config 001
2. 平台配置的本地化,即扫描平台定义完整的规则格式。不仅本地扫描时遵循此配置,在平台展示时也能解析文件配置生成可视化展示,从而达到统一的 IaC 配置。
本地扫描可以发现问题,但是难以发现此问题的引入人和引入时机,因此这些问题要不要改、谁来改,存在着纠缠和推诿的可能。而平台可以基于代码的提交记录,追溯到代码的变更时机,发现问题责任人,从而以责任人为视图进行问题跟踪,甚至可以转成 Bug 专项跟进。谁污染谁治理,这很合理。
此外,平台还可以根据下一次扫描的结果,自动关闭当前已经修复的代码问题,节省人工操作。
把问题放在平台归档还有一个好处,就是可以很清晰的知道某个仓库的代码质量趋势,例如在某个时间点引入了新问题导致整体质量变差,或是在某个时间点解除历史负载质量提升。可视化的质量波动趋势图也可以帮助团队管理者更直观的判断,当前是否需要为团队的代码质量敲敲警钟。
只是做本地扫描,还是会有“心大”的开发者不修复问题直接 push 到远端,这时就可以通过平台侧提供的质量门禁功能来做拦截了。质量门禁可以定义当前仓库可以允许的问题数量,当超出问题数量,这次提交或者合并请求将被拦截。
通常,历史项目在接入扫描时会一次性扫出成百上千个遗留问题,而团队也不太可能专门预留时间一次性根治,导致“刚入门就劝退”。针对这类场景我们的建议是,设置 MR 的质量门禁为新增问题数,保证在代码合入时不会有新代码质量问题引入,控制增量的同时再逐步清理存量问题(业务需求会改到哪个文件,就修复这个文件的代码质量问题),通过这种方式慢慢将代码质量拉回正轨。
某种程度上我们认可越大规模的团队越需要代码扫描工具,来帮助团队提升面对规范和复杂问题的标准和效率。针对 SMB 和个人开发者,代码扫描也依然是接入成本最低的质量提升工具。希望通过以上案例和场景,能帮助各位读者快速定位项目中的卡点并顺利解决,关注每行代码迭代,传承卓越代码文化。
点击体验代码扫描工具实现团队效率提升
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。