著業(yè)務(wù)的發(fā)展,系統(tǒng)會(huì)越來越龐大,原本簡(jiǎn)單穩(wěn)定的功能,可能在不斷迭代后復(fù)雜度上升,潛在的風(fēng)險(xiǎn)也隨之暴露,導(dǎo)致最終服務(wù)不穩(wěn)定,造成業(yè)務(wù)價(jià)值的損失。而為了減少這種情況,其中一種比較好的方式就是提高代碼質(zhì)量,比如通過代碼審查,從而降低錯(cuò)誤風(fēng)險(xiǎn),但是,代碼審查難度大,代碼缺陷、漏洞不易發(fā)現(xiàn),且審查工作隨著代碼量增加而增加,審查效率低。
工欲善其事,必先利其器,因此,這篇文章給大家介紹幾種檢查代碼質(zhì)量的利器,Alibaba Java Coding Guidelines、CheckStyle、PMD、FindBugs、SonarLint,讓你在關(guān)注代碼質(zhì)量的同時(shí),減少 code review 的工作量,提高 code review 的效率,并通過代碼質(zhì)量分析去反向提升我們的代碼編寫能力
Alibaba Java Coding Guidelines 專注于Java代碼規(guī)范,目的是讓開發(fā)者更加方便、快速規(guī)范代碼格式。該插件在掃描代碼后,將不符合規(guī)約的代碼按 Blocker、Critical、Major 三個(gè)等級(jí)顯示出來,并且大部分可以自動(dòng)修復(fù),它還基于 Inspection 機(jī)制提供了實(shí)時(shí)檢測(cè)功能,編寫代碼的同時(shí)也能快速發(fā)現(xiàn)問題所在。
阿里巴巴規(guī)約掃描包括:
File > Settings > Plugins > Marketplace 搜索 “Alibaba Java Coding Guidelines”,按照提示進(jìn)行安裝,然后重啟即可。
3.1、運(yùn)行方式:
(1)可以Tools > 阿里編碼規(guī)約 > 編碼規(guī)約掃描
(2)在編輯界面或者項(xiàng)目區(qū)域點(diǎn)擊右鍵,在右鍵菜單中選擇“編碼規(guī)約掃描”即可:
3.2、菜單功能:
3.3、運(yùn)行結(jié)果:
掃描完成后顯示結(jié)果如下,我們可以看到掃描結(jié)果主要分為 Blocker(阻擋者)、Critical(嚴(yán)重問題)、Major(主要的)三個(gè)大類,它們表示的是問題的嚴(yán)重程度,嚴(yán)重程度由高到低為:Blocker > Critical > Major,至于每一類中都會(huì)包含什么樣的問題,圖中的內(nèi)容已經(jīng)說明了一切。
選中其中的一個(gè)問題項(xiàng)目,會(huì)出現(xiàn)如下內(nèi)容(如果當(dāng)前鼠標(biāo)點(diǎn)擊的是最終項(xiàng),右邊區(qū)域顯示的是其它的內(nèi)容,后面會(huì)再講到):
(1)指定區(qū)域搜索同一類問題:
當(dāng)點(diǎn)擊③處的按鈕時(shí),會(huì)彈出如下按鈕:
這里選擇掃描區(qū)域,來掃描鼠標(biāo)選中的同類問題。如果按照默認(rèn)選擇,那么運(yùn)行后的結(jié)果就如下圖所示:
這里我們可以看到,顯示了整個(gè)Project中的所有該類的問題。
(2)預(yù)覽具體的不規(guī)范代碼:
如果點(diǎn)擊的是最終的問題點(diǎn)或者問題所在的類文件,那顯示的就是如下界面,預(yù)覽該處不規(guī)范的代碼。
3.4、工具欄功能介紹:
CheckStyle 側(cè)重檢查編碼格式和代碼風(fēng)格規(guī)范,如命名規(guī)范、Javadoc注釋規(guī)范、空格規(guī)范、size度量(如過長(zhǎng)的方法)、重復(fù)代碼、多余Imports等,從而有效約束開發(fā)人員更好地遵循代碼編寫規(guī)范。Checkstyle主要是文法層面的代碼編寫規(guī)范的分析,對(duì)bug幾乎沒什么發(fā)現(xiàn)能力。
Checkstyle插件中默認(rèn)內(nèi)置有2個(gè)執(zhí)行代碼檢查的配置文件(Sun Checks 和 Sun Checks),但是這兩個(gè)文件檢查的非常詳細(xì)嚴(yán)格,即使優(yōu)秀的開源項(xiàng)目也會(huì)檢查出來有非常多的錯(cuò)誤告信息,所以需要導(dǎo)入我們自定義的配置文件。
通過 File > Settings > Plugins > Marketplace 搜索 “CheckStyle”,按照提示進(jìn)行安裝,然后重啟即可。
可以看到基本都是一些縮進(jìn)啥的編碼規(guī)范,可以不用太關(guān)注
PMD側(cè)重面向安全編碼規(guī)則,且具備一定的數(shù)據(jù)流分析和路徑分析能力,能力比CheckStyle稍微強(qiáng)點(diǎn),并且 PMD 支持自定義規(guī)則,PMD可以直接使用的規(guī)則包括以下內(nèi)容:
通過 File > Settings > Plugins > Marketplace 搜索 “PMDPlugin”,按照提示進(jìn)行安裝,然后重啟即可
3.1、運(yùn)行方式:
(1)從Tools菜單中啟動(dòng):
通過 Tools > Run PMD 可以看到如下的界面,如果通過該方式啟動(dòng),掃描的范圍就是整個(gè)項(xiàng)目中的文件了。
(2)從右鍵菜單中啟動(dòng):
在文件或者編輯器中點(diǎn)擊右鍵,也可以看到“Run PMD”選項(xiàng),如果通過該方式啟動(dòng), 檢測(cè)范圍取決于鼠標(biāo)或光標(biāo)當(dāng)前所選中的區(qū)域。
3.2、運(yùn)行結(jié)果:
運(yùn)行后會(huì)出現(xiàn)如上所示的面板,左邊工具欄,鼠標(biāo)停留在上面會(huì)提示其功能;右邊顯示了檢測(cè)結(jié)果,當(dāng)點(diǎn)擊具體某一問題項(xiàng)時(shí),會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的源碼中。
3.3、配置檢測(cè)規(guī)則:
通過 File > Settings > Other Settings > PMD 可以打開檢測(cè)規(guī)則的設(shè)置界面:
在 “RuleSets(規(guī)則設(shè)置)” 界面可以管理自定義的檢測(cè)規(guī)則。因?yàn)樵趯?shí)際工作中,可能需要根據(jù)實(shí)際情況自定義檢測(cè)規(guī)則,就可以通過這里導(dǎo)入,如果要使用它,需要在啟動(dòng)PMD進(jìn)行檢測(cè)時(shí)選擇該自定義規(guī)則。
點(diǎn)擊“Options”選項(xiàng)卡,在其中可以配置一些檢測(cè)規(guī)則選項(xiàng):
其中重點(diǎn)需要留意的是“Skip TestSource”這一項(xiàng),因?yàn)樵陧?xiàng)目中有不少Android Studio自動(dòng)生成的測(cè)試代碼,如下所示,選擇上述選項(xiàng)后可以將其過濾掉。
FindBugs 側(cè)重于發(fā)現(xiàn)代碼中存在的bug,如運(yùn)行時(shí)錯(cuò)誤檢測(cè)(空指針檢查、未合理關(guān)閉資源、字符串相同判斷錯(cuò)(==,而不是equals)等),它可以簡(jiǎn)單高效全面地幫助我們發(fā)現(xiàn)程序代碼中存在的bug以及潛在隱患,針對(duì)各種問題,它提供了簡(jiǎn)單的修改意見供我們參考
通過 File > Settings > Plugins > Marketplace 搜索 “FindBugs”,按照提示進(jìn)行安裝,然后重啟即可
FindBugs 可以分析單個(gè)文件、包下面的所有文件、整個(gè)module下的文件、整個(gè)project下的文件,右鍵想要分析的文件名/包名/module名/project
分析完之后就會(huì)出現(xiàn)結(jié)果面板
點(diǎn)擊對(duì)應(yīng)的item在右邊會(huì)定位到具體的代碼,這是根據(jù)提示進(jìn)行處理修改就行
4.1、Bad practice 代碼壞習(xí)慣:
4.2、Dodgy code 糟糕的代碼:
4.3、Internationalization 代碼國(guó)際化相關(guān):
4.4、Performance 代碼性能相關(guān):
4.5、Experimental:
4.6、Malicious code vulnerability 惡意破壞代碼相關(guān):
4.7、Multithreaded correctness 多線程代碼正確性相關(guān):
4.8、Correctness 代碼正確性相關(guān):
sonar 比 Findbugs 高了一個(gè)層級(jí),它不僅關(guān)注常規(guī)靜態(tài)BUG,還關(guān)注到了如代碼質(zhì)量、包與包、類與類之間的依賴情況,代碼耦合情況,類、方法、文件的復(fù)雜度,代碼中是否包含大量復(fù)制粘貼的代碼,關(guān)注的是項(xiàng)目代碼整體的健康情況。sonar 有兩種使用方式:插件和客戶端,sonar 的插件名稱為 sonarLint。
通過 File > Settings > Plugins > Marketplace 搜索 “SonarLint”,按照提示進(jìn)行安裝,然后重啟即可
右鍵項(xiàng)目或者文件進(jìn)行如上圖所示操作,執(zhí)行之后可以看到如下信息,如果代碼中有不合理的地方會(huì)在report中顯示,同時(shí)點(diǎn)擊錯(cuò)誤的地方在右邊會(huì)給出建議的修改供參考。
4.1、配置 Sonar 服務(wù)器:
sonarLint 插件的使用場(chǎng)景是自用自審,但 sonar 也提供了平臺(tái)版本,使用場(chǎng)景則是他審,sonar 平臺(tái)的搭建就不在這篇文章介紹了,感興趣的讀者可以自己上網(wǎng)查看,我們這里主要介紹如何在 sonarLint 插件中配置關(guān)聯(lián) sonar 平臺(tái)服務(wù)器的工程,進(jìn)行本地檢查:
點(diǎn)擊新增按鈕,輸入Configuration Name,配置sonarlint 服務(wù)器的地址,然后下拉框選擇 Login/Password,輸入 sonarlint服務(wù)器的賬號(hào)密碼
4.2、具體 Sonar工程配置:
配置完服務(wù)器之后,需要針對(duì)具體工程進(jìn)行配置,點(diǎn)擊 connection下拉框,選擇上面配置好的服務(wù)器連接,然后點(diǎn)擊 Search in list,找到對(duì)應(yīng)的工程:
4.3、使用 SonarLint 檢查:
配置完上面兩步之后,接下來就可以選擇要進(jìn)行檢查的類或者目錄進(jìn)行 sonarlint 檢查了(跟第3點(diǎn)的使用方式一致),同時(shí),在 commit 代碼的時(shí)候,勾選 “Perform Sonarlint analysis”,會(huì)針對(duì)你要提交的代碼進(jìn)行sonarlint檢查
onarSource目前提供三類產(chǎn)品,分別是SonarLint、SonarQube以及SonarCloud。三類產(chǎn)品雖然應(yīng)用場(chǎng)景不同,但都給開發(fā)人員提供了靜態(tài)代碼分析(SAST)的能力,也就是在不運(yùn)行程序的情況下通過一系列程序分析技術(shù)持續(xù)對(duì)代碼進(jìn)行理解與檢測(cè),發(fā)現(xiàn)代碼中存在的漏洞。
SonarSource的愿景是希望從根本上改變組織交付軟件的方式,幫助研發(fā)人員交付清潔代碼,即安全、健壯且可以發(fā)展的代碼。
"代碼重構(gòu)/改善代碼質(zhì)量"這件事情比作投資:“我們擁有的這種方法的美妙之處在于它不再花費(fèi);對(duì)于相同的投資,你有很好的代碼,以后你會(huì)有更大的投資回報(bào),所以一開始的‘正確’就尤為重要,這是使軟件成為資產(chǎn)而非負(fù)債所需的唯一步驟
在整個(gè)的開發(fā)鏈條中還有其他工具也可以檢測(cè)軟件中存在的漏洞,比如IAST,DAST,F(xiàn)UZZ。這些動(dòng)態(tài)測(cè)試工具受限于檢測(cè)漏洞的原理,往往需要較高的使用條件,比如需要準(zhǔn)備軟件的運(yùn)行環(huán)境、需要在測(cè)試過程中與軟件進(jìn)行交互來觸發(fā)軟件的功能、需要準(zhǔn)備大量的測(cè)試用例。
而基于不同理念所產(chǎn)生的靜態(tài)分析工具則沒有此類限制,只需要接觸源代碼即可完成分析。這樣的天然優(yōu)勢(shì)給予了靜態(tài)代碼分析類工具更廣闊的市場(chǎng)空間,使得靜態(tài)代碼分析類工具更有可能成為走向研發(fā)者市場(chǎng),成為一類具備普適性的研發(fā)效能工具。SonarSource的成功路徑也從側(cè)面證明了這一點(diǎn)。然而當(dāng)我們把目光聚焦在國(guó)內(nèi)市場(chǎng),并沒有發(fā)現(xiàn)類似SonarSource的企業(yè)。在中國(guó),靜態(tài)分析類工具仍然處在一個(gè)突破使用場(chǎng)景和市場(chǎng)空間的過程中,究其原因,我們認(rèn)為以下三個(gè)方面的問題可能能夠做一些說明:
目前國(guó)內(nèi)市場(chǎng)上常見的靜態(tài)代碼分析工具具備較高的使用門檻。從開始接觸到成功落地往往需要較多的適配與培訓(xùn)工作,需要企業(yè)投入較多的時(shí)間與人力成本。在研發(fā)工具接受程度與使用習(xí)慣還未養(yǎng)成的中國(guó)市場(chǎng),這些先決條件往往成為勸退企業(yè)的第一道門檻,導(dǎo)致了很多有相關(guān)需求的中小型企業(yè)望而卻步。
從用途來說,靜態(tài)代碼分析工具屬于軟件研發(fā)體系的支撐性工具,其使用場(chǎng)景往往會(huì)隨著軟件研發(fā)體系的變化而變化。
目前國(guó)內(nèi)市場(chǎng)主流的靜態(tài)代碼分析工具仍然停留在傳統(tǒng)的客戶端軟件時(shí)代,用戶通過上傳代碼進(jìn)行集中式的分析與結(jié)果展示。當(dāng)用戶需要在現(xiàn)代化的研發(fā)體系(如模塊化DevOps敏捷流水線)中去使用相關(guān)能力時(shí),這種傳統(tǒng)的軟件形態(tài)往往會(huì)帶來較多的麻煩,導(dǎo)致落地成本的提升和自動(dòng)化程度的降低。
目前國(guó)外走在前沿的分析類工具,如SonarQube、 Snyk和Coverity均采用了解耦式的模塊化設(shè)計(jì),方便用戶進(jìn)行流水線集成和自動(dòng)化調(diào)度。
隨著軟件產(chǎn)業(yè)的飛速發(fā)展,過去二十年間用戶對(duì)于靜態(tài)代碼分析類工具的需求也在不斷提升。從最早期進(jìn)行代碼風(fēng)格的管理,到嵌入式時(shí)代對(duì)于代碼質(zhì)量的合規(guī)要求,到今天對(duì)于安全漏洞的治理。軟件的復(fù)雜程度日益提升,靜態(tài)代碼分析工具試圖解決的問題也越發(fā)復(fù)雜。現(xiàn)有的工具在面對(duì)復(fù)雜的軟件安全漏洞時(shí),分析精度直線下降,誤報(bào)率往往超過50%。過高的誤報(bào)率帶來的是使用成本的大幅提升,同時(shí)也造就了靜態(tài)代碼分析類工具落地成本高,使用復(fù)雜的刻板印象。能否解決好復(fù)雜代碼場(chǎng)景的分析精度問題,是決定SAST類工具接受程度的核心難題。
SonarSource提供的一整套解決方案會(huì)在開發(fā)過程的整個(gè)生命周期,以及每個(gè)階段執(zhí)行檢查。整套解決方案的核心理念是“Clean as You Code”。在開發(fā)者完成一部分新的代碼之后,可以將代碼推給掃描工具,并對(duì)這一部分的代碼的品質(zhì)進(jìn)行分析和評(píng)價(jià),以保證開發(fā)者的代碼一直保持滿足標(biāo)準(zhǔn)的狀態(tài)。這個(gè)標(biāo)準(zhǔn)的狀態(tài)通過“Quality Gate”來嚴(yán)格把守,它是發(fā)布前的最后一道檢查,只有滿足所有的要求才能發(fā)布到生產(chǎn)環(huán)境中,比如代碼覆蓋率,可靠性評(píng)級(jí)等等。開發(fā)人員可以設(shè)置自己的標(biāo)準(zhǔn),也可以使用SonarSource提供的默認(rèn)標(biāo)準(zhǔn)“SonarWay”。
SonarLint是一個(gè)IDE擴(kuò)展,可幫助開發(fā)人員在編寫代碼時(shí)檢測(cè)和修復(fù)質(zhì)量問題。就像Grammarly拼寫檢查器一樣,SonarLint會(huì)實(shí)時(shí)提供反饋,以便在提交代碼之前修復(fù)它們。此外,SonarLint會(huì)提供詳細(xì)的規(guī)則描述和最佳實(shí)踐來展示代碼問題的原因與解決方案,開發(fā)人員可以在編碼的同時(shí)學(xué)習(xí)提高自己的技能。
公司主打的SAST產(chǎn)品。產(chǎn)品是分離式的架構(gòu),一側(cè)為SonarQube平臺(tái),可以在web端查看掃描結(jié)果;另一側(cè)是SonarScanner,這是公司十多年來構(gòu)建的代碼分析引擎,內(nèi)含5000多條規(guī)則積累。此外SonarQube 并不是簡(jiǎn)單地將各種質(zhì)量檢測(cè)工具的結(jié)果直接展現(xiàn)給客戶,而是通過不同的算法來對(duì)這些結(jié)果進(jìn)行再加工,最終以量化的方式來衡量代碼質(zhì)量,并進(jìn)行管理。
公司推出的SaaS云服務(wù),針對(duì)開源項(xiàng)目進(jìn)行分析檢測(cè)。基本功能和普通的SonarQube差不多,但提供更多的SaaS級(jí)別的服務(wù)。
上述三種產(chǎn)品本質(zhì)上共同解決了三類代碼問題,分別是代碼可靠性,代碼安全性以及技術(shù)負(fù)債(代碼可維護(hù)性)。
分析引擎可以針對(duì)代碼中的Bug以及開發(fā)路徑存在的問題進(jìn)行檢測(cè)。Bug通常是指一種編碼錯(cuò)誤,可能會(huì)導(dǎo)致程序運(yùn)行時(shí)出現(xiàn)錯(cuò)誤或意外。
SonarQube可以展示代碼中的安全熱點(diǎn)(Security Hotspots),也就是一些敏感代碼,但它們不會(huì)影響整體應(yīng)用程序安全性,也不需要研發(fā)人員立刻進(jìn)行修復(fù)。但是事后審查的判斷是有必要的,因?yàn)檠邪l(fā)人員可能也會(huì)發(fā)現(xiàn)這些“敏感地帶”會(huì)存在風(fēng)險(xiǎn)。除了安全熱點(diǎn)以外,分析引擎還可以針對(duì)安全漏洞(Vulnerabilities)進(jìn)行檢測(cè),安全漏洞是需要立即修復(fù)的,它們會(huì)嚴(yán)重影響應(yīng)用程序安全性。這樣的分類方式進(jìn)一步降低了誤報(bào)率,對(duì)研發(fā)人員更加友好。
分析引擎主要檢測(cè)的安全漏洞分為兩類。第一種是安全注入問題,第二種是安全配置問題。
分析引擎可以檢測(cè)代碼異味,也就是代碼可維護(hù)性的問題。通常它們使代碼混亂且難以維護(hù),換而言之,維護(hù)人員不知道如何修復(fù)這段代碼,最壞的情況是原來的問題還沒有修復(fù),就引入新的問題。代碼異味包括“七宗罪”,分別是不遵循代碼標(biāo)準(zhǔn),潛在缺陷,糟糕的復(fù)雜度分布,重復(fù),注釋不足或過多,缺乏單元測(cè)試,糟糕的設(shè)計(jì)。
這七點(diǎn)也是檢測(cè)技術(shù)負(fù)債的標(biāo)準(zhǔn)(技術(shù)負(fù)債是指修復(fù)代碼異味所花費(fèi)的精力)。
對(duì)于修復(fù)技術(shù)負(fù)債的問題,SonarSource也有著自己獨(dú)到的解決方案。通常來說,主動(dòng)對(duì)整個(gè)代碼庫(kù)的修復(fù)是費(fèi)時(shí)費(fèi)力的,也可能會(huì)產(chǎn)生功能回歸風(fēng)險(xiǎn)。然而程序員總是會(huì)在老代碼的基礎(chǔ)上進(jìn)行新的修改。所以SonarSource的產(chǎn)品只對(duì)于新的代碼或者是在老代碼上做過修改的代碼進(jìn)行掃描分析,也就是在不引入新的問題的同時(shí)逐漸對(duì)代碼庫(kù)進(jìn)行修復(fù),逐漸改進(jìn)軟件的質(zhì)量。實(shí)際上就算沒有主動(dòng)去修復(fù)老的代碼,通過SonarSource的解決方案,也會(huì)被逐漸地被動(dòng)修復(fù)。據(jù)估計(jì),第一年后整體代碼的大約20%的問題將會(huì)被修復(fù),兩年后大約35%的代碼問題會(huì)被修復(fù),5年后大約50%的代碼問題會(huì)被修復(fù)。
三款產(chǎn)品中SonarLint和SonarCloud是開源免費(fèi)的。除了SonarCloud定制的私人服務(wù)需要付費(fèi)之外,公司主要的收入來源來自SonarQube,一共有四個(gè)版本,分別是社區(qū)版,研發(fā)者版,企業(yè)版和數(shù)據(jù)中心版。除了版本之間功能的不同之外,SonarQube的收費(fèi)模式是根據(jù)可分析的最大代碼行數(shù)每年收l(shuí)icense費(fèi)用。
社區(qū)版是完全免費(fèi)的,它實(shí)際上覆蓋了SonarQube絕大部分功能,包括支持大多數(shù)語(yǔ)言,對(duì)代碼質(zhì)量和簡(jiǎn)單安全問題檢測(cè)的能力,超過50種的插件等等。但問題是社區(qū)版不支持一個(gè)項(xiàng)目多分支的形式,這會(huì)導(dǎo)致一次掃描產(chǎn)生多個(gè)項(xiàng)目結(jié)果,增加管理的難度。
研發(fā)者版本起價(jià)是150美金一年,可使用的最大代碼限制行數(shù)是10萬(wàn)行,已經(jīng)可以滿足一般研發(fā)人員日常的檢測(cè)需求。此外,增加了C/C++語(yǔ)言的覆蓋,增加了分支分析的能力,增強(qiáng)了安全分析的能力,尤其是針對(duì)注入這個(gè)問題可以做到深度精確分析。
企業(yè)版起價(jià)2萬(wàn)美金一年,可使用的最大代碼限制行數(shù)是100萬(wàn)行。除了支持全部的27種語(yǔ)言之外,還提供了可以配置的SAST引擎功能,提供OWASP/SANS安全報(bào)告的能力。還賦能企業(yè)產(chǎn)出組合管理的能力。
數(shù)據(jù)中心版本起價(jià)10萬(wàn)美金一年,可使用的最大代碼限制行數(shù)是200萬(wàn)行。這個(gè)版本對(duì)高可用性和橫向擴(kuò)展性有更好的支持。
此外,三個(gè)收費(fèi)版本的產(chǎn)品價(jià)格均會(huì)隨著最大代碼限制行數(shù)的增加而提高。
2006年,F(xiàn)reddy Mallet意識(shí)到代碼質(zhì)量管理至關(guān)重要。代碼的缺陷最終會(huì)反映到軟件交付的質(zhì)量上,導(dǎo)致各種各樣的威脅與損失,比如數(shù)據(jù)泄露,隱私威脅,財(cái)產(chǎn)損失,甚至是對(duì)人物理上帶來的傷害。Freddy Mallet認(rèn)為市場(chǎng)上需要一種提供自動(dòng)化代碼審查的產(chǎn)品。Freddy Mallet與好友Simon Brandhof通過集成Java的最佳開源工具在2007年開始了對(duì)Sonar平臺(tái)的開發(fā)。Olivier Gaudin隨后加入團(tuán)隊(duì),SonarSource于2008年成立。
2010年,SonarSource實(shí)現(xiàn)了公司的第一個(gè)里程碑——Sonar平臺(tái)被社區(qū)和企業(yè)接受,每月下載量超過2K次。各種插件的集成讓Sonar備受好評(píng)。公司隨后加大研發(fā)力度,在2013年正式推出SonarQube。
2016年,SonarSource從Insight Venture Partners籌集了4500萬(wàn)美元投資,以進(jìn)一步加速增長(zhǎng)。Richard Wells,MD和Insight Partners副總裁Matt Gatto加入公司董事會(huì)。此時(shí)的SonarQube已經(jīng)完成了多次迭代,支持了大量的語(yǔ)言和規(guī)則,代碼質(zhì)量分析的能力已經(jīng)逐漸體現(xiàn)出優(yōu)勢(shì)。
2018年,第一個(gè)SaaS平臺(tái)SonarCloud基于市場(chǎng)需求和用戶需求推出。在此之前的兩年中,SonarSource也納入了一些與代碼安全性相關(guān)的檢測(cè)能力,但是一直沒有辦法做到高精度的分析。
直到2020年,SonarSource收購(gòu)RIPS。RIPS Technologies首席執(zhí)行官兼聯(lián)合創(chuàng)始人Johannes Dahse博士加入SonarSource擔(dān)任研發(fā)主管。RIPS Technologies是一家致力于創(chuàng)新安全測(cè)試技術(shù)的公司,以從頭構(gòu)建了其一流的 PHP 代碼分析引擎而聞名。它的團(tuán)隊(duì)在構(gòu)建高效的代碼分析解決方案方面有著悠久的歷史,他們的引擎甚至可以檢測(cè)復(fù)雜和深層嵌套的漏洞。這次收購(gòu)讓SonarSource語(yǔ)言覆蓋的廣度和RIPS檢測(cè)的深度相結(jié)合,最終實(shí)現(xiàn)了多種語(yǔ)言上對(duì)安全漏洞的精準(zhǔn)分析檢測(cè)。這也標(biāo)志著SonarSource正式走入了安全性SAST的領(lǐng)域。
2022年,Sonar從新的和現(xiàn)有的投資者那里籌集了4.12億美元,估值為47億美元。該公司將利用這筆投資來擴(kuò)大銷售團(tuán)隊(duì),以實(shí)現(xiàn)10億美元的收入。
聯(lián)合創(chuàng)始人兼CEO Olivier Gaudin來自瑞士日內(nèi)瓦,于1998年畢業(yè)于法國(guó)國(guó)立應(yīng)用科學(xué)學(xué)院(INSA)的應(yīng)用數(shù)學(xué)工程專業(yè)。因?yàn)橥ㄟ^軟件更容易去計(jì)算和模擬數(shù)學(xué)理論的最后結(jié)果,所以O(shè)livier選擇了偏計(jì)算機(jī)的方向,主攻Java語(yǔ)言。曾經(jīng)在IT行業(yè),金融行業(yè),建筑行業(yè)工作過。
在一次代碼審查任務(wù)中,Olivier發(fā)現(xiàn)自己很難讀懂自己下屬寫的代碼,開始在自己的團(tuán)隊(duì)內(nèi)部開始設(shè)置一些基本的編寫規(guī)范。同時(shí),F(xiàn)reddy和Simon也被同樣的問題困擾,他們正在試圖尋找一種自動(dòng)化的工具來解決這個(gè)問題,但是并沒有找到。Freddy和Simon當(dāng)時(shí)已經(jīng)在IT咨詢公司Hortis共事了幾年,分別擔(dān)任CTO和高級(jí)Java研發(fā)員。Freddy有著二十多年軟件相關(guān)工作經(jīng)驗(yàn),包括軟件研發(fā),軟件基礎(chǔ)架構(gòu)設(shè)計(jì),軟件咨詢。Simon也有著十幾年的開發(fā)經(jīng)驗(yàn),兩人率先開始研發(fā)Sonar平臺(tái)。
后來,Olivier遇到了Freddy和Simon,被二人的想法吸引并于2007年加入了他們團(tuán)隊(duì)。有一次,他詢問了Freddy和Simon對(duì)“干凈的代碼”的理解,發(fā)現(xiàn)三個(gè)人產(chǎn)生了分歧。這更加堅(jiān)定了他們做出一套被行業(yè)認(rèn)可的代碼規(guī)范的想法,三人便于2008年創(chuàng)立SonarSource。
Andrea來自于瑞士日內(nèi)瓦,于2003年畢業(yè)于歐洲工商管理學(xué)院的金融專業(yè)。曾在摩根大通擔(dān)任首席技術(shù)官和技術(shù)負(fù)責(zé)人 25 年。工作的同時(shí)又在2018年攻讀完成了美國(guó)斯坦福大學(xué)的創(chuàng)新科技項(xiàng)目。Andrea于2021年加入SonarSource,他的加入可以幫助SonarSource建立應(yīng)對(duì)強(qiáng)勁增長(zhǎng)所需的基礎(chǔ)設(shè)施。
Gordon來自美國(guó)馬塞諸塞州,于2000年畢業(yè)于本特利大學(xué)的金融MBA項(xiàng)目。他曾在軟件安全公司 Carbon Black擔(dān)任財(cái)務(wù)副總裁兼首席財(cái)務(wù)官,該公司于2019年被VMWare收購(gòu)。他也曾擔(dān)任過領(lǐng)先的應(yīng)用程序安全初創(chuàng)公司Onapsis的首席財(cái)務(wù)官,于2021年加入SonarSource。Gordon在這一領(lǐng)域有著十多年的經(jīng)驗(yàn),有著非常廣的人脈與資源。
Gupta于2022年加入SonarSource,之前曾擔(dān)任Redis的首席營(yíng)銷官,最近擔(dān)任Oracle的Java和GraalVM全球營(yíng)銷副總裁,在他任職期間領(lǐng)導(dǎo)了這兩家公司實(shí)現(xiàn)了多倍增長(zhǎng)。他的經(jīng)驗(yàn)將有助于SonarSource實(shí)現(xiàn)10億美元的收入目標(biāo)。
Olivier曾在一次采訪中表示,“我們希望更多研發(fā)人員使用我們的產(chǎn)品,要知道全球有大約7000多萬(wàn)的研發(fā)人員。這是我們?cè)诖酥皬臎]有遇到過的挑戰(zhàn),我認(rèn)為我們的產(chǎn)品已經(jīng)準(zhǔn)備好了,剩下的工作就是安全意識(shí)上的教育和啟發(fā)”。
實(shí)際上,SonarSource從很早的時(shí)候就開始嘗試進(jìn)行安全意識(shí)教育的工作。比如每年12月,會(huì)在Twitter上發(fā)布24個(gè)面向研發(fā)人員的安全問題解謎挑戰(zhàn),并為成功解謎者提供獎(jiǎng)勵(lì)。這些安全問題都是SonarSource團(tuán)隊(duì)在本年中花費(fèi)大量時(shí)間研究和理解現(xiàn)實(shí)世界中的漏洞。公司正在嘗試通過各種形式增加與用戶之間的互動(dòng)來傳遞安全意識(shí)。
SonarSource希望在未來可以提高分析引擎的掃描的速度以及對(duì)代碼安全問題深度分析的能力。SonarSource會(huì)繼續(xù)為開發(fā)人員增加價(jià)值,優(yōu)化SonarLint的能力,讓研發(fā)人員在IDE側(cè)有更好的無(wú)摩擦開發(fā)體驗(yàn)。
直接在.gitlab-ci.yml文件中填入如下內(nèi)容,觸發(fā)掃描,即可在漏洞報(bào)告中查看結(jié)果。
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
以下倉(cāng)庫(kù)參考:
參考dvwa倉(cāng)庫(kù)中的配置方法。
在project級(jí)別中,settings - CI/CD - Variables中添加如下2個(gè)變量指向SonarQube服務(wù)。
在待掃描倉(cāng)庫(kù)中增加.gitlab-ci.yml配置文件,如下,不建議直接把敏感信息如快速入門那樣配置在ci文件中。
sonarqube:
artifacts:
reports:
sast:
- gl-sast-report.json
script:
- "/scan.sh"
image:
name: satomic/sonarscanner:v6
然后進(jìn)行掃描即可得到掃描結(jié)果。
報(bào)告總覽
特定issue展現(xiàn)image
文件定位image
根據(jù)文章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端導(dǎo)出json格式的報(bào)告,因此部署支持的舊版本中的最后一個(gè)版本,即7.6版。
參考Docker 安裝SonarQube 步驟以及遇到的坑進(jìn)行部署,
創(chuàng)建工作目錄
mkdir -p /home/sonar/postgres/postgresql
mkdir -p /home/sonar/postgres/data
創(chuàng)建網(wǎng)絡(luò)
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
創(chuàng)建工作目錄
mkdir -p /data/sonarqube_dir
修改系統(tǒng)參數(shù)
echo "vm.max_map_count=262144" > /etc/sysctl.conf
sysctl -p
運(yùn)行測(cè)試容器
docker run -d --name sonartest sonarqube:7.6-community
拷貝必須文件到本地,并修改權(quán)限為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
啟動(dòng)SonarQube,其中SONARQUBE_JDBC_URL的IP地址需要修改為實(shí)際PgSql數(shù)據(jù)庫(kù)的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
登錄SonarQube UI地址( http://IP:9000 ),默認(rèn)用戶名密碼為admin/admin,在My Account - Security中生成Token。
參考SonarScanner下載最新版的掃描器。
解壓后,進(jìn)入bin目錄,執(zhí)行如下命令即可完成掃描,掃描完成后可以去SonarQube中查看掃描結(jié)果。
./sonar-scanner \
-Dsonar.host.url=http://1.13.160.207:9000 \
-Dsonar.login=333f3410ce3e575d559329e8f3d0a5d4ec8a499d \
-Dsonar.projectKey=my:test \
-Dsonar.sources=/path_to_codes
如果想要在本地生成json格式報(bào)告,則增加如下參數(shù)
-Dsonar.report.export.path=report.json \
-Dsonar.analysis.mode=preview
基于以上內(nèi)容,關(guān)鍵是把掃描結(jié)果轉(zhuǎn)化為極狐GitLab旗艦版所識(shí)別的json格式。
SonarQube掃描結(jié)果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報(bào)告展現(xiàn)issue樣本
{
"category": "test",
"message": "這個(gè)問題不怎么嚴(yán)重",
"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"
}
]
}
轉(zhuǎn)碼程序采用python,編寫converter.py文件,內(nèi)容如下:
# 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()
基于環(huán)境變量進(jìn)行掃描
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
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 /
制作好的鏡像已經(jīng)推送到DockerHub中,采用前文使用方法中所說的內(nèi)容即可實(shí)現(xiàn)掃描。
docker build -t satomic/sonarscanner:v6 .
docker push satomic/sonarscanner:v6
到SonarQube的容器內(nèi)部
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上檢查是否安裝成功,出現(xiàn)如下畫面即表示可以。
在sonar UI上進(jìn)行如下配置,這樣Java的默認(rèn)規(guī)則就是Findbugs了
因?yàn)閽呙鐹ava項(xiàng)目需要代碼經(jīng)過編譯,所以需要對(duì)掃描器配置maven掃描能力。理論上,通過在Dockerfile中添加安裝maven包即可,但是失敗,因此直接在前述步驟構(gòu)建出的鏡像中操作完后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
此時(shí),考慮到參數(shù)暴露的問題,僅僅把如下4個(gè)參數(shù)內(nèi)置到掃描器內(nèi)部,因此修改scan.sh文件如下
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 /
構(gòu)建出最新鏡像
docker build -t satomic/sonarscanner:v9-mvn .
docker push satomic/sonarscanner:v9-mvn
為了支持Findbug的觸發(fā),需要使用如下配置
# 正常情況下應(yīng)該配置在settings設(shè)置的環(huán)境變量中,而不是如此明文暴露
variables:
sonar_host_url: http://1.13.160.207:9000
sonar_login: 333f3410ce3e575d559329e8f3d0a5d4ec8a499d
sonarqube:
artifacts:
reports:
sast:
- gl-sast-report.json
script:
# 通過在此動(dòng)態(tài)生成 sonar-project.properties 配置,而無(wú)需侵入源碼倉(cāng)庫(kù)
# 同同時(shí)可以提高自由度
- 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
# 創(chuàng)建構(gòu)建物目錄
- mkdir -p target/classes
# 此命令是否執(zhí)行成功依賴mvn配置的合理性,需要實(shí)際倉(cāng)庫(kù)的研發(fā)介入,配合配置的可用性,默認(rèn)可訪問官方mvn庫(kù)的外網(wǎng)環(huán)境問題不大,內(nèi)網(wǎng)則不太可能編譯成功
- mvn compile
# 查看編譯產(chǎn)物
- tree target/classes
# 執(zhí)行sonar掃描
- /scan.sh
# 查看報(bào)告生成大小
- ls -l .scannerwork/report.json
image:
name: satomic/sonarscanner:v9-mvn
參考
文章轉(zhuǎn)自:《》https://note.youdao.com/ynoteshare/index.html?id=05b5ba9c1c49628901ebd1eadece97cd&type=note&_time=1703486325797
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。