前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >IOS 代码扫描从放弃到入门

IOS 代码扫描从放弃到入门

作者头像
高楼Zee
发布2021-09-23 17:06:55
2.9K0
发布2021-09-23 17:06:55
举报
文章被收录于专栏:7DGroup7DGroup

一、前言

我司今年开始尝试一些代码质量相关建设,比如组织 codereview、修复代码扫描漏洞.这是一个很好的现象,当我们为了快速迭代,往往为了让需求上线,导致代码并不是很规范,时间长了就留下了一堆技术债.

前日的一天,iOS 老哥找我说让看看能不能弄弄 IOS 代码扫描,扫描出一些代码漏洞,尝试去修复漏洞和 bug.

于是乎,下面就是我记录一下折腾了几天完成的 IOS 代码扫描初探的过程.

二、工具选择

从去年开始,就一直研究 IOS 代码扫描这款.无奈乎,IOS 在代码扫描这个领域能选的工具其实不算太多.

这次主要介绍如下几个工具:

  1. oclint
  2. infer
  3. sonar-swift

1、Oclint + SonarQube方案

所需安装工具一览

下面是在mac机器上安装的工具

  • homebrew(mac命令管理软件工具)
  • Java JDK(推荐jdk而不是jre,最新的即可)
  • maven
  • xcode(通过appstore下面)
  • xcpretty(用于对xcodebuild的输出进行格式化)
  • sonarqube(代码扫描平台)
  • sonar-scanner(扫描工具)
  • oclint
  • SonarQube Plugin for Objective C(扫描插件)
oclint

OCLint是基于Clang Tooling开发的静态分析工具,主要用来发现编译器检查不到的那些潜在的关键技术问题.

命令安装

代码语言:javascript
复制
brew tap oclint/formulae
brew install oclint

下载安装包安装

代码语言:javascript
复制
https://github.com/oclint/oclint/releases

配置环境变量

代码语言:javascript
复制
OCLint_PATH=/Users/xinxi/Documents/oclint/build/oclint-release
export PATH=$OCLint_PATH/bin:$PATH

source .bash_profile

验证是否安装成功。在终端输入 oclint --version

xcpretty

用于对xcodebuild的输出进行格式化

代码语言:javascript
复制
gem install xcpretty

用法:

紧跟在xcodebuild 相关语句后面,比如:

代码语言:javascript
复制
xcodebuild [flags] | xcpretty

可以结合tee进行日志收集

代码语言:javascript
复制
xcodebuild [flags] | tee xcodebuild.log | xcpretty
SonarQube安装

官网地址,sonarqube分社区版本和商业化版本,能扫描多种语言并且开源

代码语言:javascript
复制
https://www.sonarqube.org/downloads/

docker 安装

代码语言:javascript
复制
docker pull sonarqube:8.6-community

二进制文件安装

在bin/macosx-universal-64目录下的输入:

代码语言:javascript
复制
sh sonar.sh start

控制台输出"Started SonarQube"说明启动成功.

在浏览器访问,能打开页面说明启动成功.

代码语言:javascript
复制
http://127.0.0.1:9000/

需要说明的是SonarQube如果想持久化保存数据,是需要依赖mysql数据库的.

SonarQube 默认提供H2存储,只能暂时存储一些小项目结果,仅为了演示使用.

在 conf/sonar.properties 下配置数据库地址即可.

可选 MySQL、Oracle、PostgreSQL

sonar-objective-c插件

sonarqube 默认没有扫描 oc 的检查,sonarqube 官方的 sonar-objective-c 插件是收费的.

需要在找一个免费的插件,在github找到两个项目

插件一

代码语言:javascript
复制
https://github.com/Backelite/sonar-objective-c

这个插件在三年前没有修改了,在使用中发现有些扫描规则并没有.

插件二

这个项目稍微更新的时间短一些,有些规则适当的更新了

代码语言:javascript
复制
https://github.com/raatiniemi/sonar-objective-c

下载插件放到 /extensions/plugins 目录下

sonar-scanner

sonar-scanner用来扫描本地代码,并且上传到SonarQube平台中.

下载地址: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/

按照不同的操作系统选择不同安装包即可.

配置环境变量:

代码语言:javascript
复制
vim /etc/profile

export SONAR_HOME=/usr/local/sonarqube-6.7.5
export SONAR_SCANNER_HOME=/usr/local/sonar-scanner
PATH=$PATH:$SONAR_HOME/bin:$SONAR_RUNNER_HOME/bin

source /etc/profile

sonar-scanner分为两种使用方式:

配置文件方式:

在项目根目录下新建 sonar-project.properties 文件,内容如下:

代码语言:javascript
复制
//项目的key

sonar.projectKey=projectKey

//项目的名字

sonar.projectName=projectName

//项目的版本

sonar.projectVersion=1.0.0

//需要分析的源码的目录,多个目录用英文逗号隔开
sonar.sources=D:/workspace/Demo/src

进入项目根目录下,然后输入“sonar-scanner”命令,执行代码分析

命令行方式:

在命令中设置了参数

代码语言:javascript
复制
sonar-scanner -Dsonar.host.url=http://sonarqube -Dsonar.projectKey=app -Dsonar.sources=.

项目实验

上面的软件安装完成后,基本上具备的代码扫描的条件.找一个开源项目实验下

使用网络库AFNetworking项目:https://github.com/AFNetworking/AFNetworking

脚本
  • 首先clone代码到本地,然后再清理项目工程
代码语言:javascript
复制
xcodebuild -workspace AFNetworking.xcworkspace -scheme AFNetworking\ iOS -sdk iphonesimulator11.2 -configuration Debug clean
  • 生成compile_commands.json
代码语言:javascript
复制
xcodebuild -workspace AFNetworking.xcworkspace -scheme AFNetworking\ iOS -sdk iphonesimulator11.2 -configuration Debug COMPILER_INDEX_STORE_ENABLE=NO | xcpretty -r json-compilation-database -o compile_commands.json
  • 生成oclint.xml
代码语言:javascript
复制
oclint-json-compilation-database -- -report-type pmd -o oclint.xml -max-priority-1 100000  -max-priority-2 100000  -max-priority-3 100000
  • 处理oclint.xml

oclint生成的报告中如下形式的规则会导致Objective-c分析插件出错(ERROR: The rule 'OCLint:compiler warning' does not exist, 刚才上面提到的sonar-objective-c插件并没有处理warning这些规则. 所以需要通过脚本删除这个结果.

脚本

代码语言:javascript
复制
#!/usr/bin/python

import xml.etree.ElementTree as ET
import os

os.system('mv oclint.xml oclint.xml.origin')
tree = ET.ElementTree(file='oclint.xml.origin')
root = tree.getroot()
del_items = []
for child in root:
    for one in child:
        if one.attrib['ruleset'] == 'clang':
            print child.attrib['name']
            del_items.append(child)
            break

for del_item in del_items:
    root.remove(del_item)

tree.write('oclint.xml')

在和 oclint.xml 一个目录下,执行该脚本

  • 生成 Sonar 报告

将如下内容保存为 sonar-project.properties 文件,放到 AFNetworking 目录下

代码语言:javascript
复制
sonar.projectKey=AFNetworking
sonar.host.url=http://localhost:9000
sonar.login=admin
sonar.password=admin

sonar.language=objc
sonar.objectivec.workspace=AFNetworking.xcworkspace
sonar.objectivec.appScheme=AFNetworking iOS
sonar.sources=AFNetworking

sonar.objectivec.oclint.report=oclint.xml
  • Sonar平台展示

扫描结果图一:

扫描结果图二:

问题记录

在使用demo中非常顺滑,没什么问题.但是接入了实际项目,出现了如下问题.

  • 问题一:编译项目失败

解决方案:

命令行编译的问题,必须携带参数" COMPILER_INDEX_STORE_ENABLE=NO"

  • 问题二:oclint: error: violations exceed threshold

解决方案:

代码语言:javascript
复制
maxPriority=15000
代码语言:javascript
复制

${oclint_in} $${oclint_ex} -- -o=$$BUILD_WORK_DIR/oclint/lint.xml -report-type=pmd -stats -max-priority-1=$$maxPriority -max-priority-2=$$maxPriority -max-priority-3=$maxPriority -rc LONG_LINE=500 -rc LONG_VARIABLE_NAME=10
  • 问题三:如果扫描的生成的 compile_commands.json 文件过大,oclint-json-compilation-database会提示出错“OSError: [Errno 7] Argument list too long”

这个问题在网上看了很多帖子都是如下解决方案,但是实际中使用根本没有解决问题.

https://github.com/oclint/oclint/issues/233有网友给出解决方案https://github.com/wuwen1030/oclint_argument_list_too_long_solution/tree/master

解决方案:

oclint-json-compilation-database可以过滤不想扫描的文件和需要扫描的文件夹

-e忽略扫描和-i是指定扫描路径

代码语言:javascript
复制
oclint-json-compilation-database -e pods -i build

虽然使用上面的命令扫描,不报错误,但是在平台中扫描的bug数是0,这个问题目前一直未解决.

  • 问题四:mysql存储问题

2、infer + sonar-swift

基于上面失败方案一度想放弃,但是无意中在社区中,看到了好未来开源的iOS代码扫描的帖子"我们开源了一款SonarQube iOS代码扫描插件",https://testerhome.com/topics/26967, 又激起了我想重新尝试的勇气.

github地址:

代码语言:javascript
复制
https://github.com/tal-tech/sonar-swift

简单看了一下需要工具,需要infer、xcpretty、sonar、sonar-swift插件.

扫描规则:

代码语言:javascript
复制
https://github.com/tal-tech/sonar-swift/blob/master/docs/rule.md

这次尝试并没有急于着手干,看到帖子下面有个微信群并加了群,询问了开发者一些细节,确认是可以扫描 oc 项目的.

sonar-swift

插件地址:

代码语言:javascript
复制
https://github.com/tal-tech/sonar-swift/releases

当时我下载的是v1.0.2版本,把插件放到 /extensions/plugins 目录下,重启sonar

脚本

官方提供的脚本

代码语言:javascript
复制
xcodebuild clean build -workspace app.xcworkspace -scheme scheme -destination 'generic/platform=iOS' COMPILER_INDEX_STORE_ENABLE=NO | tee xcodebuild.log > /dev/null
xcpretty -r json-compilation-database -o compile_commands.json < xcodebuild.log > /dev/null
# --skip-analysis-in-path 是忽略扫描目录
infer run --skip-analysis-in-path Pods --compilation-database compile_commands.json

# 可选,如果有 swift 语言使用
swiftlint lint > swiftlint.txt

lizard --xml > lizard-report.xml
sonar-scanner -Dsonar.host.url=http://sonarqube -Dsonar.projectKey=app -Dsonar.sources=. -Dsonar.swift.swiftlint.report=swiftlint.txt -Dsonar.swift.lizard.report=lizard-report.xml -Dsonar.swift.infer.report=infer-out/report.json

infer

infer是facebook开源的一款代码扫描软件,可以分析 Objective-C, Java 或者 C 代码,报告潜在的问题

在releases页面中下载二进制文件

代码语言:javascript
复制
https://github.com/facebook/infer/releases

设置环境变量

代码语言:javascript
复制
tar xf infer-osx-vXX.tar.xz
# this assumes you use bash, adapt to your needs in case you use
# another shell
echo "export PATH=$PATH:`pwd`/infer-osx/infer/infer/bin" \
     >> ~/.bashrc && source ~/.bashrc

通过infer --version查看infer版本信息,说明安装成功.

扫描iOS命令:

代码语言:javascript
复制
infer -- xcodebuild -workspace "test.xcworkspace" -scheme "scheme"

扫描出的结果会在工程目录下的infer-out文件中,其中具体的代码会以csv,txt,json的格式分别存在对应的文件中。可以供我们分析.

扫描过程

infer扫描阶段

扫描的bug数量

扫描规则

结果上传成功

sonar 平台展示数据

扫描结果图三:

扫描结果图四:

从下载代码到上传扫描结果,大概1小时30分支,和项目规模成正比.

问题记录

问题1

解决方案: lizard 这个报告不要了,暂时去掉

问题2:java包中没有这个规则

解决方案:

1、用 -Dsonar.exclusions=文件路径这个排除 2、在report.json中删除这个规则

问题3:没有这个规则

解决方案:使用新版本的jar包

问题4:有个异常,去掉-Dsonar.swift.swiftlint.report=swiftlint.txt
问题5

因为每次扫描都是增量扫描,如果使用多个分支同一个项目扫描,结果会被覆盖,sonar本身也不支持多个分支扫描. 每次扫描的时候想知道是扫描的哪个版本的数据,通过参数-Dsonar.projectVersion参数可以上传版本号.

shell中获取版本号

代码语言:javascript
复制
version_number=`sed -n '/MARKETING_VERSION/{s/MARKETING_VERSION = //;s/;//;s/^[[:space:]]*//;p;q;}' ./PROJECTNAME.xcodeproj/project.pbxproj`

shell中获取版本号构建号

代码语言:javascript
复制
build_number=`sed -n '/CURRENT_PROJECT_VERSION/{s/CURRENT_PROJECT_VERSION = //;s/;//;s/^[[:space:]]*//;p;q;}' ./PROJECTNAME.xcodeproj/project.pbxproj`

在活动页面展示了版本号

结语

经过折腾了几天,也算顺利的完成了基础环境搭建,能正常扫描出结果来了.

给我最大的启发是: 做事需要有专业的"社区",如果我没有去社区中有浏览的习惯,也很难找到不错的工具. 做事需要有专业的"圈子",专业的人做专业事,方可事半功倍.

参考资料:

  • [1]:如何使用脚本读取Xcode 11中的当前应用程序版本(https://stackoom.com/question/3q09t/如何使用脚本读取Xcode-中的当前应用程序版本)
  • [2]:iOS+Jenkins持续构建-代码扫描(https://www.jianshu.com/p/c0d49bcefeb0)
  • [3]:使用Jenkins+OCLint+SonarCube对iOS项目进行代码分析(https://juejin.cn/post/6844903575680729102)
  • [4]:iOS 静态代码扫描平台 Sonarqube 实战 Objective-C、Swift(https://testerhome.com/topics/13158)
  • [5]:ios fastline sonarqube(https://medium.com/@aamir.ali/sonarqube-integration-with-fastlane-in-ios-3cd33e5abdc8)
  • [6]:oclint_argument_list_too_long_solution解决方案(https://github.com/wuwen1030/oclint_argument_list_too_long_solution)
  • [7]:OCLint静态代码检测实践(https://juejin.cn/post/6844904017424809998)
  • [8]:OCLint基本使用(https://www.jianshu.com/p/b2513f16d246)
  • [9]:iOS 静态代码扫描平台 Sonarqube 实战 Objective-C、Swift(https://testerhome.com/topics/13158)
  • [10]:Sonarqube & ObjectiveC 环境搭建(https://www.jianshu.com/p/5a01e56176bf)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 7DGroup 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、工具选择
    • 1、Oclint + SonarQube方案
      • 所需安装工具一览
      • 项目实验
      • 问题记录
    • 2、infer + sonar-swift
      • sonar-swift
      • 脚本
      • infer
      • 扫描过程
      • 问题记录
  • 结语
  • 参考资料:
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档