一、sonarqube代码扫描中有很多插件可以扩展我们的扫描规则,比如mybatis插件、sql插件,这些可以在设置->应用市场->搜索->下载即可。
另外还有一些不在应用市场中的插件,可以在github中下载然后打包,将jar包放在sonarqube的 sonarqube-8.9.1/extensions/plugins目录下,重启sonarqube服务即可。
sql插件地址:
https://github.com/gretard/sonar-sql-plugin/releases
mybatis插件地址
https://github.com/donhui/sonar-mybatis/releases/
plsql插件地址
https://github.com/felipebz/zpa/releases
二、sonarqube提供了自定义插件写法的例子,供大家参考学习
三、自定义插件
1.新建一个maven项目,在pom文件中添加如下依赖,注意packaging一定要写,不然启动的时候会报plugin-key找不到。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.csii</groupId>
<artifactId>sonar-cola</artifactId>
<packaging>sonar-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sonar.apiVersion>7.9</sonar.apiVersion>
<jdk.min.version>1.8</jdk.min.version>
<sonar.sources>src/main/java,src/main/resources</sonar.sources>
</properties>
<description>Rules to check SQL statements in MyBatis Mapper XML files</description>
<dependencies>
<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>${sonar.apiVersion}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
<artifactId>sonar-packaging-maven-plugin</artifactId>
<version>1.18.0.372</version>
<extensions>true</extensions>
<configuration>
<pluginClass>com.csii.plugin.MyPlugin</pluginClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${jdk.min.version}</source>
<target>${jdk.min.version}</target>
</configuration>
</plugin>
<plugin>
<!-- UTF-8 bundles are not supported by Java, so they must be converted during build -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>native2ascii-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<executions>
<execution>
<goals>
<goal>native2ascii</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2.编写Plugin类,实现Plugin接口,重写define方法,这里是plugin的定义
3.质量配置,实现BuiltInQualityProfileDefinition接口,设置质量的语言和规则的名称,多个规则则定义多个。
4.定义规则,实现RulesDefinition接口,主要设置了规则的xml文件路径,语言类型,规则的key和name。
5.规则xml文件
<mylint-rules>
<rule>
<key>MyRule1</key>
<name>不应该使用Statement,建议使用PrepareStatement</name>
<internalKey>MyRule1</internalKey>
<description>不应该使用Statement,建议使用PrepareStatement
</description>
<severity>MINOR</severity>
<cardinality>SINGLE</cardinality>
<status>READY</status>
<type>BUG</type>
<tag>mylint</tag>
<remediationFunction>CONSTANT_ISSUE</remediationFunction>
<remediationFunctionBaseEffort>20min</remediationFunctionBaseEffort>
</rule>
</mylint-rules>
6.扫描文件的类,实现Senior接口,这里主要是做代码扫描和规则匹配,添加问题的功能。
package com.csii.plugin.rules;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.issue.NewIssue;
import org.sonar.api.batch.sensor.issue.NewIssueLocation;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import java.io.*;
import static com.csii.plugin.rules.MyLintRulesDefinition.REPO_KEY;
public class MyLintSensor implements Sensor {
private static final Logger LOGGER = Loggers.get(MyLintSensor.class);
@Override
public void describe(SensorDescriptor descriptor) {
descriptor.name("MyLint Sensor");
descriptor.onlyOnLanguage("java");
}
@Override
public void execute(SensorContext context) {
FileSystem fs = context.fileSystem();
Iterable<InputFile> javaFiles = fs.inputFiles(fs.predicates().hasLanguage("java"));
BufferedReader input = null;
try {
for (InputFile javaFile : javaFiles) {
String path = javaFile.uri().getPath();
File file = new File(path);
input = new BufferedReader (new FileReader(file));
String text;
int line = 0;
CheckBoolFlag f = new CheckBoolFlag();
f.setFlag(false);
while((text = input.readLine()) != null) {
line++;
LOGGER.info("java text==============="+text+ " line ====="+line);
text = text.trim();
if (f.isFlag()){
if (text.endsWith("*/")) {
f.setFlag(false);
}else{
continue;
}
}
if (text.startsWith("/*")&&text.endsWith("*/")){
continue;
}else if (text.startsWith("/*")&&(!text.endsWith("*/"))){
f.setFlag(true);
continue;
}
if (text.startsWith("//")){
continue;
}
matchAndCheckRules(context,text,javaFile,line);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (input != null){
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void matchAndCheckRules(SensorContext context, String text, InputFile file, int line){
if (text.contains(".createStatement()")){
LOGGER.info("java text==============="+text+ " line ====="+line);
RuleKey ruleKey = RuleKey.of(REPO_KEY,"MyRule1");
NewIssue newIssue = context.newIssue()
.forRule(ruleKey);
NewIssueLocation primaryLocation = newIssue.newLocation()
.on(file)
.at(file.selectLine(line))
.message("不应该使用Statement,建议使用PrepareStatement");
newIssue.at(primaryLocation);
newIssue.save();
}
}
}
class CheckBoolFlag {
private boolean flag;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
7.将打包的jar包放在plugins目录下,重启服务。
四、配置规则生效
在质量配置,搜索java,可以看到我们自己定义的规则在列表中,现在需要让他生效。
有如下方法可以让其生效:
①设置为默认
②为其设置项目
③复制出一个新的规则,修改父规则为系统规则,然后设置为默认规则,这样系统规则也会生效,而我们的规则也会生效。
五、扫描测试,可以看到我们自定义的插件是可以正常工作的。