parent
b4147782d6
commit
e76715936e
@ -0,0 +1,268 @@ |
||||
<?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> |
||||
|
||||
<parent> |
||||
<groupId>com.keyware.sonar</groupId> |
||||
<artifactId>sonar-keyware</artifactId> |
||||
<version>1.0</version> |
||||
</parent> |
||||
|
||||
<name>配置文件信息安全性设计准则</name> |
||||
<artifactId>sonar-keyware-plugins-configurationDetection</artifactId> |
||||
<packaging>sonar-plugin</packaging> |
||||
<version>1.0</version> |
||||
<description>用于检查配置文件信息的安全性设计准则的Sonarqube插件</description> |
||||
|
||||
<properties> |
||||
<sonar.sources>src/main/java,src/main/resources</sonar.sources> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
|
||||
|
||||
<dependency> |
||||
<groupId>org.sonarsource.api.plugin</groupId> |
||||
<artifactId>sonar-plugin-api</artifactId> |
||||
<version>9.9.0.229</version> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.slf4j</groupId> |
||||
<artifactId>slf4j-api</artifactId> |
||||
<scope>provided</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.sonarsource.html</groupId> |
||||
<artifactId>sonar-html-plugin</artifactId> |
||||
<version>${sonar.html.version}</version> |
||||
<!--<type>sonar-plugin</type>--> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.sonarsource.analyzer-commons</groupId> |
||||
<artifactId>sonar-analyzer-commons</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.apache.logging.log4j</groupId> |
||||
<artifactId>log4j-slf4j-impl</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.logging.log4j</groupId> |
||||
<artifactId>log4j-core</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- unit tests --> |
||||
<dependency> |
||||
<groupId>org.assertj</groupId> |
||||
<artifactId>assertj-core</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.junit.jupiter</groupId> |
||||
<artifactId>junit-jupiter</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.junit.jupiter</groupId> |
||||
<artifactId>junit-jupiter-api</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.junit.jupiter</groupId> |
||||
<artifactId>junit-jupiter-engine</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.junit.jupiter</groupId> |
||||
<artifactId>junit-jupiter-migrationsupport</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.sonarsource.sonarqube</groupId> |
||||
<artifactId>sonar-plugin-api-impl</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.fasterxml.jackson.core</groupId> |
||||
<artifactId>jackson-databind</artifactId> |
||||
<version>2.16.0</version> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.yaml</groupId> |
||||
<artifactId>snakeyaml</artifactId> |
||||
<version>1.28</version> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.fasterxml.jackson.core</groupId> |
||||
<artifactId>jackson-core</artifactId> |
||||
<version>2.12.3</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>xerces</groupId> |
||||
<artifactId>xercesImpl</artifactId> |
||||
<version>2.12.2</version> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.fasterxml.jackson.dataformat</groupId> |
||||
<artifactId>jackson-dataformat-yaml</artifactId> |
||||
<version>2.16.1</version> |
||||
</dependency> |
||||
|
||||
</dependencies> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId> |
||||
<artifactId>sonar-packaging-maven-plugin</artifactId> |
||||
<extensions>true</extensions> |
||||
<configuration> |
||||
<pluginKey>keywareConfigurationDetectionPlugin</pluginKey> |
||||
<pluginName>配置文件信息设计准则</pluginName> |
||||
<pluginClass>com.keyware.sonar.Configuration.ConfigurationSecurityDesignRulesPlugin</pluginClass> |
||||
<sonarLintSupported>true</sonarLintSupported> |
||||
<skipDependenciesPackaging>true</skipDependenciesPackaging> |
||||
<sonarQubeMinVersion>8.9</sonarQubeMinVersion> |
||||
<jreMinVersion>11</jreMinVersion> |
||||
</configuration> |
||||
</plugin> |
||||
|
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-shade-plugin</artifactId> |
||||
<configuration> |
||||
</configuration> |
||||
<executions> |
||||
<execution> |
||||
<phase>package</phase> |
||||
<goals> |
||||
<goal>shade</goal> |
||||
</goals> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
|
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-compiler-plugin</artifactId> |
||||
<configuration> |
||||
<source>11</source> |
||||
<target>11</target> |
||||
</configuration> |
||||
</plugin> |
||||
|
||||
<plugin> |
||||
<groupId>org.jacoco</groupId> |
||||
<artifactId>jacoco-maven-plugin</artifactId> |
||||
<version>${version.jacoco.plugin}</version> |
||||
<executions> |
||||
<execution> |
||||
<id>prepare-agent</id> |
||||
<goals> |
||||
<goal>prepare-agent</goal> |
||||
</goals> |
||||
</execution> |
||||
<execution> |
||||
<id>report</id> |
||||
<goals> |
||||
<goal>report</goal> |
||||
</goals> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
|
||||
<!-- only required to run UT - these are UT dependencies --> |
||||
<plugin> |
||||
<groupId>org.apache.maven.plugins</groupId> |
||||
<artifactId>maven-dependency-plugin</artifactId> |
||||
<executions> |
||||
<execution> |
||||
<id>copy</id> |
||||
<phase>test-compile</phase> |
||||
<goals> |
||||
<goal>copy</goal> |
||||
</goals> |
||||
<configuration> |
||||
<artifactItems> |
||||
<artifactItem> |
||||
<groupId>org.slf4j</groupId> |
||||
<artifactId>slf4j-api</artifactId> |
||||
<version>1.7.30</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
<artifactItem> |
||||
<groupId>org.apache.commons</groupId> |
||||
<artifactId>commons-collections4</artifactId> |
||||
<version>4.0</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
<artifactItem> |
||||
<groupId>javax</groupId> |
||||
<artifactId>javaee-api</artifactId> |
||||
<version>6.0</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
<artifactItem> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-webmvc</artifactId> |
||||
<version>4.3.3.RELEASE</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
<artifactItem> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-web</artifactId> |
||||
<version>4.3.3.RELEASE</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
<artifactItem> |
||||
<groupId>org.springframework</groupId> |
||||
<artifactId>spring-context</artifactId> |
||||
<version>4.3.3.RELEASE</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
<artifactItem> |
||||
<groupId>junit</groupId> |
||||
<artifactId>junit</artifactId> |
||||
<version>4.13.2</version> |
||||
<type>jar</type> |
||||
</artifactItem> |
||||
</artifactItems> |
||||
<outputDirectory>${project.build.directory}/test-jars</outputDirectory> |
||||
</configuration> |
||||
</execution> |
||||
</executions> |
||||
</plugin> |
||||
<plugin> |
||||
<groupId>com.mycila</groupId> |
||||
<artifactId>license-maven-plugin</artifactId> |
||||
<configuration> |
||||
<header>${project.basedir}/src/main/resources/license-header.txt</header> |
||||
<!--排除文件--> |
||||
<excludes> |
||||
<exclude>**/*.properties</exclude> |
||||
<exclude>*.sh</exclude> |
||||
<exclude>*.yml</exclude> |
||||
<exclude>.editorconfig</exclude> |
||||
<exclude>.gitignore</exclude> |
||||
<exclude>**/*.md</exclude> |
||||
<exclude>**/*.xml</exclude> |
||||
</excludes> |
||||
</configuration> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
</project> |
@ -0,0 +1,37 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.Configuration; |
||||
|
||||
import com.keyware.sonar.Configuration.rules.ConfigurationSecurityDesignRulesRepository; |
||||
import org.sonar.api.Plugin; |
||||
|
||||
/** |
||||
* 安全性设计准则插件 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/6 |
||||
*/ |
||||
public class ConfigurationSecurityDesignRulesPlugin implements Plugin { |
||||
@Override |
||||
public void define(Context context) { |
||||
// 服务器扩展 - >对象在服务器启动期间实例化
|
||||
context.addExtension(ConfigurationSecurityDesignRulesRepository.class); |
||||
|
||||
// 服务器扩展 - >对象在服务器启动期间实例化
|
||||
context.addExtension(ConfigurationSecurityDesignWayProfile.class); |
||||
|
||||
|
||||
context.addExtension(ConfigurationFileLanguage.class); |
||||
|
||||
|
||||
context.addExtension(ConfigFileSquidSensor.class); |
||||
|
||||
|
||||
context.addExtensions(ConfigurationFileLanguage.getProperties()); |
||||
|
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.Configuration; |
||||
|
||||
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; |
||||
import org.sonarsource.api.sonarlint.SonarLintSide; |
||||
|
||||
/** |
||||
* 定义配置信息安全性质量配置 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/11 |
||||
*/ |
||||
@SonarLintSide |
||||
public class ConfigurationSecurityDesignWayProfile implements BuiltInQualityProfilesDefinition { |
||||
|
||||
@Override |
||||
public void define(Context context) { |
||||
|
||||
var cfgWay = context.createBuiltInQualityProfile("配置信息安全性设计规则", ConfigurationFileLanguage.KEY); |
||||
cfgWay.activateRule("config", "ConfigurationFileChecker"); |
||||
cfgWay.activateRule("config", "SessionDateChecker"); |
||||
cfgWay.done(); |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.Configuration.rules; |
||||
|
||||
import com.keyware.sonar.Configuration.rules.checkers.SessionDateChecker; |
||||
import com.keyware.sonar.Configuration.rules.checkers.ConfigurationFileChecker; |
||||
import org.sonar.api.SonarRuntime; |
||||
import org.sonar.api.server.rule.RulesDefinition; |
||||
import org.sonarsource.analyzer.commons.RuleMetadataLoader; |
||||
|
||||
import java.util.*; |
||||
|
||||
/** |
||||
* 用于定义出现在规则页面中规则的元数据 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/6 |
||||
*/ |
||||
public class ConfigurationSecurityDesignRulesRepository implements RulesDefinition { |
||||
// 不要修改这个值,因为路径在 CheckVerifier 中是硬编码的
|
||||
private static final String RESOURCE_BASE_PATH = "org/sonar/l10n/java/rules/java"; |
||||
|
||||
// 添加需要视为模板规则的规则的规则键
|
||||
private static final Set<String> RULE_TEMPLATES_KEY = Collections.emptySet(); |
||||
|
||||
private final SonarRuntime runtime; |
||||
|
||||
public ConfigurationSecurityDesignRulesRepository(SonarRuntime runtime) { |
||||
this.runtime = runtime; |
||||
} |
||||
|
||||
@Override |
||||
public void define(RulesDefinition.Context context) { |
||||
RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_BASE_PATH, runtime); |
||||
|
||||
|
||||
RulesDefinition.NewRepository configRepo = context.createRepository("config", "cfg").setName("config"); |
||||
ruleMetadataLoader.addRulesByAnnotatedClass(configRepo, List.of(ConfigurationFileChecker.class, SessionDateChecker.class)); |
||||
setTemplates(configRepo); |
||||
configRepo.done(); |
||||
} |
||||
|
||||
private static void setTemplates(RulesDefinition.NewRepository repository) { |
||||
RULE_TEMPLATES_KEY.stream() |
||||
.map(repository::rule) |
||||
.filter(Objects::nonNull) |
||||
.forEach(rule -> rule.setTemplate(true)); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,23 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
|
||||
package com.keyware.sonar.Configuration.rules.checkers; |
||||
|
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.rule.RuleKey; |
||||
|
||||
/** |
||||
* ConfigCheck |
||||
* |
||||
* @author RenFengJiang |
||||
* @date 2024/1/23 |
||||
*/ |
||||
public interface ConfigCheck { |
||||
default void execute(SensorContext context, InputFile inputFile, RuleKey ruleKey){} |
||||
default void endOfCheck(SensorContext context, RuleKey ruleKey){} |
||||
} |
@ -0,0 +1,250 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.Configuration.rules.checkers; |
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory; |
||||
import com.fasterxml.jackson.core.JsonParser; |
||||
import com.fasterxml.jackson.core.JsonToken; |
||||
import com.fasterxml.jackson.databind.MappingJsonFactory; |
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.batch.sensor.issue.NewIssue; |
||||
import org.sonar.api.rule.RuleKey; |
||||
import org.sonar.check.Rule; |
||||
import org.w3c.dom.Document; |
||||
import org.w3c.dom.Element; |
||||
import org.w3c.dom.Node; |
||||
import org.w3c.dom.NodeList; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
|
||||
import javax.xml.parsers.*; |
||||
import java.io.*; |
||||
import java.util.Map; |
||||
import java.util.Scanner; |
||||
|
||||
|
||||
/** |
||||
* 通过用户名口令、数据证书等其他手段对用户身份进行验证。 |
||||
* |
||||
* @author WuHaoYang |
||||
* @date 2024/1/22 |
||||
*/ |
||||
@Rule(key = "ConfigurationFileChecker") |
||||
public class ConfigurationFileChecker implements ConfigCheck{ |
||||
|
||||
|
||||
public void execute(SensorContext context, InputFile inputFile, RuleKey ruleKey){ |
||||
//文件名称
|
||||
String filename = inputFile.filename(); |
||||
System.out.println("[ConfigurationFileChecker]>>>>>" + filename); |
||||
|
||||
//校验文件后缀
|
||||
if (filename.endsWith(".properties")) { |
||||
try { |
||||
File file = new File(inputFile.absolutePath()); |
||||
try (Scanner scanner = new Scanner(file)) { |
||||
int lineNum = 1; |
||||
while (scanner.hasNextLine()) { |
||||
String line = scanner.nextLine(); |
||||
|
||||
String[] keyValue = line.split("="); // 分割行的内容
|
||||
|
||||
// 检查键是否为 "password" 且其对应的值不为空
|
||||
if (keyValue.length == 2 && keyValue[0].trim().equals("password") |
||||
&& !keyValue[1].trim().isEmpty()) { |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
} |
||||
lineNum++; |
||||
} |
||||
} |
||||
} catch (FileNotFoundException e) { |
||||
System.out.println("文件未找到: " + e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
if (filename.endsWith(".ini")) { |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(file1))) { |
||||
String line; |
||||
int lineNum = 1; |
||||
while ((line = br.readLine()) != null) { |
||||
if (line.trim().startsWith("password")) { |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
break; |
||||
} |
||||
lineNum++; |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
if (filename.endsWith(".conf")) { |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
int lineNum = 0; // 初始化行数
|
||||
|
||||
try { |
||||
BufferedReader br = new BufferedReader(new FileReader(file1)); |
||||
String line; |
||||
|
||||
while ((line = br.readLine()) != null) { |
||||
lineNum++; // 递增行数
|
||||
|
||||
String[] keyValue = line.split("="); // 分割行的内容
|
||||
|
||||
// 进行基本有效性检查 && keyValue[0]可能包含前导空格, keyValue[1]可能包含尾随空格
|
||||
if (keyValue.length == 2 && keyValue[0].trim().equals("password")) { |
||||
|
||||
// 如果键是 "password", 输出其值
|
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
} |
||||
} |
||||
br.close(); |
||||
} catch(FileNotFoundException ex) { |
||||
ex.printStackTrace(); |
||||
} catch(IOException ex) { |
||||
ex.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
if (filename.endsWith(".xml") && !filename.equals("pom.xml")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
File absoluteFile = file1.getAbsoluteFile(); |
||||
|
||||
File xmlFile = absoluteFile; |
||||
|
||||
processXML(xmlFile); |
||||
} |
||||
|
||||
if (filename.endsWith(".json")) { |
||||
try { |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
JsonFactory f = new MappingJsonFactory(); |
||||
JsonParser jp = f.createParser(file1); |
||||
JsonToken current; |
||||
|
||||
current = jp.nextToken(); |
||||
if (current != JsonToken.START_OBJECT) { |
||||
return; |
||||
} |
||||
|
||||
while (jp.nextToken() != JsonToken.END_OBJECT) { |
||||
String fieldName = jp.getCurrentName(); |
||||
current = jp.nextToken(); |
||||
|
||||
if ("password".equals(fieldName)) { |
||||
|
||||
int lineNum = jp.getCurrentLocation().getLineNr(); |
||||
|
||||
String passwordValue = jp.getText(); |
||||
|
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
break; |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
if (filename.endsWith(".yml")){ |
||||
|
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
Yaml yaml = new Yaml(); |
||||
try (FileInputStream fis = new FileInputStream(file1)) { |
||||
Map<String, Object> obj = yaml.load(fis); |
||||
if (obj != null) { |
||||
String password = searchPassword(obj, 1); |
||||
if (password != null) { |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
private static String searchPassword(Map<String, Object> map, int lineNum) { |
||||
for (String key : map.keySet()) { |
||||
if ("password".equals(key) && map.get(key) instanceof String) { |
||||
return (String) map.get(key); |
||||
} else if (map.get(key) instanceof Map) { |
||||
lineNum++; |
||||
String password = searchPassword((Map<String, Object>) map.get(key), lineNum); |
||||
if (password != null) { |
||||
return password; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static int getLineNumber(Node node) { |
||||
Document document = node.getOwnerDocument(); |
||||
document.getDocumentElement().normalize(); |
||||
String xmlContent = document.getDocumentElement().getTextContent(); |
||||
String[] lines = xmlContent.split("\n"); |
||||
for(int i = 0; i < lines.length; i++) { |
||||
if(lines[i].contains(node.getTextContent())) { |
||||
return i+2; |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
public static void processXML(File xmlFile) { |
||||
try { |
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); |
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); |
||||
Document doc = dBuilder.parse(xmlFile); |
||||
doc.getDocumentElement().normalize(); |
||||
NodeList nList = doc.getElementsByTagName("password"); |
||||
for (int i = 0; i < nList.getLength(); i++) { |
||||
Node nNode = nList.item(i); |
||||
if (nNode.getNodeType() == Node.ELEMENT_NODE) { |
||||
Element eElement = (Element) nNode; |
||||
int lineNumber = getLineNumber(nNode); |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,96 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
|
||||
package com.keyware.sonar.Configuration.rules.checkers; |
||||
|
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.rule.RuleKey; |
||||
import org.sonar.check.Rule; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.FileNotFoundException; |
||||
import java.io.IOException; |
||||
import java.util.Map; |
||||
import java.util.Scanner; |
||||
|
||||
/** |
||||
* 设置会话过期的日期 |
||||
* |
||||
* @author RenFengJiang |
||||
* @date 2024/1/22 |
||||
*/ |
||||
@Rule(key = "SessionDateChecker") |
||||
public class SessionDateChecker implements ConfigCheck { |
||||
|
||||
private boolean boo = true; |
||||
public void execute(SensorContext context, InputFile inputFile, RuleKey ruleKey){ |
||||
if(boo){ |
||||
//文件名称
|
||||
String filename = inputFile.filename(); |
||||
//校验文件后缀
|
||||
if (filename.endsWith(".properties")) { |
||||
try { |
||||
File file = new File(inputFile.absolutePath()); |
||||
try (Scanner scanner = new Scanner(file)) { |
||||
while (scanner.hasNextLine()) { |
||||
String line = scanner.nextLine(); |
||||
if (line.contains("server.servlet.session.timeout")) { |
||||
boo = false; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} catch (FileNotFoundException e) { |
||||
System.out.println("文件未找到: " + e.getMessage()); |
||||
return; // 文件未找到时立即返回
|
||||
} |
||||
} |
||||
if (filename.endsWith(".yml")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
File absoluteFile = file1.getAbsoluteFile(); |
||||
// 构建目录路径
|
||||
Yaml yaml = new Yaml(); |
||||
try (FileInputStream fis = new FileInputStream(file1)) { |
||||
Map<String, Object> obj = yaml.load(fis); |
||||
if (obj != null){ |
||||
String sessionTimeout = searchForSessionTimeout(obj, "server", "servlet", "session", "timeout"); |
||||
if (sessionTimeout != null) { |
||||
boo = false; |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private String searchForSessionTimeout(Map<String, Object> map, String... keys) { |
||||
Map<String, Object> currentLevel = map; |
||||
for (int i = 0; i < keys.length - 1; ++i) { |
||||
Object nextLevel = currentLevel.get(keys[i]); |
||||
if (nextLevel instanceof Map) { |
||||
currentLevel = (Map<String, Object>) nextLevel; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
return currentLevel.get(keys[keys.length - 1]).toString(); |
||||
} |
||||
|
||||
@Override |
||||
public void endOfCheck(SensorContext context, RuleKey ruleKey) { |
||||
if(boo){ |
||||
var issue = context.newIssue(); |
||||
issue.at(issue.newLocation().on(context.project()).message("设置会话过期的日期")).forRule(ruleKey).save(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
/** |
||||
* TODO package-info |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/12 |
||||
*/ |
||||
package com.keyware.sonar.Configuration.rules; |
@ -0,0 +1,4 @@ |
||||
Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
项目名称:信息安全性设计准则检查插件 |
||||
项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
@ -0,0 +1,9 @@ |
||||
<p>禁止在容易受攻击的地方明文存储口令密码</p> |
||||
<h2>禁止在容易受攻击的地方明文存储口令密码,如果需要,考虑存储口令的单向加密散列,以替代明文口令存储。</h2> |
||||
<pre> |
||||
|
||||
</pre> |
||||
<h2>合规解决方案</h2> |
||||
<pre> |
||||
|
||||
</pre> |
@ -0,0 +1,13 @@ |
||||
{ |
||||
"title": "禁止在容易受攻击的地方明文存储口令密码", |
||||
"type": "CODE_SMELL", |
||||
"status": "ready", |
||||
"remediation": { |
||||
"func": "Constant\/Issue", |
||||
"constantCost": "5min" |
||||
}, |
||||
"tags": [ |
||||
"28suo" |
||||
], |
||||
"defaultSeverity": "Minor" |
||||
} |
@ -0,0 +1,16 @@ |
||||
<!-- |
||||
~ Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
~ 项目名称:信息安全性设计准则检查插件 |
||||
~ 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
~ 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
--> |
||||
|
||||
<p>设置会话过期的日期</p> |
||||
<h2>设置会话过期的日期</h2> |
||||
<pre> |
||||
|
||||
</pre> |
||||
<h2>合规解决方案</h2> |
||||
<pre> |
||||
|
||||
</pre> |
@ -0,0 +1,13 @@ |
||||
{ |
||||
"title": "设置会话过期的日期", |
||||
"type": "CODE_SMELL", |
||||
"status": "ready", |
||||
"remediation": { |
||||
"func": "Constant\/Issue", |
||||
"constantCost": "5min" |
||||
}, |
||||
"tags": [ |
||||
"28suo" |
||||
], |
||||
"defaultSeverity": "Minor" |
||||
} |
@ -0,0 +1,2 @@ |
||||
# 这是一个.conf配置文件 |
||||
password = confPassword |
@ -0,0 +1,3 @@ |
||||
[UserCredentials] |
||||
username = exampleUser |
||||
password = iniPassword |
@ -0,0 +1,5 @@ |
||||
{ |
||||
"username": "john_doe", |
||||
"password": "jsonPassword", |
||||
"email": "john.doe@example.com" |
||||
} |
@ -0,0 +1,2 @@ |
||||
# ConfigurationFileChecker.properties |
||||
password=propertiesPassword |
@ -0,0 +1,7 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
||||
<user> |
||||
<username>exampleUser</username> |
||||
<password>xmlPassword</password> |
||||
</user> |
||||
|
@ -0,0 +1,3 @@ |
||||
database: |
||||
user: admin |
||||
password: ymlPassword |
@ -0,0 +1,9 @@ |
||||
# |
||||
# Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
# 项目名称:信息安全性设计准则检查插件 |
||||
# 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
# 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
# |
||||
|
||||
# 设置会话超时时间为30分钟 |
||||
server.servlet.session.timeout=30m |
@ -0,0 +1,4 @@ |
||||
server: |
||||
servlet: |
||||
session: |
||||
timeout: 30m |
@ -0,0 +1,70 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.java; |
||||
|
||||
import com.keyware.sonar.Configuration.ConfigurationSecurityDesignRulesPlugin; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.sonar.api.*; |
||||
import org.sonar.api.config.PropertyDefinition; |
||||
import org.sonar.api.utils.Version; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* TODO JavaSecurityDesignRulesPluginTest |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/6 |
||||
*/ |
||||
public class ConfigurationSecurityDesignRulesPluginTest { |
||||
@Test |
||||
void testName() { |
||||
Plugin.Context context = new Plugin.Context(new MockedSonarRuntime()); |
||||
|
||||
new ConfigurationSecurityDesignRulesPlugin().define(context); |
||||
|
||||
assertThat(context.getExtensions()) |
||||
.extracting((ext) -> { |
||||
var type = ext.getClass().getSimpleName(); |
||||
if("Class".equals(type)){ |
||||
return ((Class) ext).getSimpleName(); |
||||
}else if("PropertyDefinition".equals(type)){ |
||||
return ((PropertyDefinition) ext).name(); |
||||
} |
||||
return ext.getClass().getSimpleName(); |
||||
}) |
||||
.containsExactlyInAnyOrder( |
||||
"ConfigurationSecurityDesignRulesRepository", |
||||
"ConfigurationSecurityDesignWayProfile", |
||||
"ConfigurationFileLanguage", |
||||
"ConfigFileSquidSensor", |
||||
"File Suffixes"); |
||||
} |
||||
|
||||
public static class MockedSonarRuntime implements SonarRuntime { |
||||
|
||||
@Override |
||||
public Version getApiVersion() { |
||||
return Version.create(9, 9); |
||||
} |
||||
|
||||
@Override |
||||
public SonarProduct getProduct() { |
||||
return SonarProduct.SONARQUBE; |
||||
} |
||||
|
||||
@Override |
||||
public SonarQubeSide getSonarQubeSide() { |
||||
return SonarQubeSide.SCANNER; |
||||
} |
||||
|
||||
@Override |
||||
public SonarEdition getEdition() { |
||||
return SonarEdition.COMMUNITY; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,34 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.java.rules; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* TODO JavaSecurityDesignRulesRepositoryTest |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/6 |
||||
*/ |
||||
public class ConfigurationSecurityDesignRulesRepositoryTest { |
||||
|
||||
@Test |
||||
void test() { |
||||
// JavaSecurityDesignRulesRepository rulesDefinition = new JavaSecurityDesignRulesRepository(new JavaSecurityDesignRulesRepository.MockedSonarRuntime());
|
||||
// RulesDefinition.Context context = new RulesDefinition.Context();
|
||||
// rulesDefinition.define(context);
|
||||
// RulesDefinition.Repository repository = context.repository(JavaSecurityDesignRulesRepository.REPOSITORY_KEY);
|
||||
//
|
||||
// assertThat(repository.name()).isEqualTo(JavaSecurityDesignRulesRepository.REPOSITORY_NAME);
|
||||
// assertThat(repository.language()).isEqualTo("java");
|
||||
// assertThat(repository.rules()).hasSize(RulesList.getJavaRules().size());
|
||||
// assertThat(repository.rules().stream().filter(RulesDefinition.Rule::template)).isEmpty();
|
||||
|
||||
} |
||||
} |
@ -0,0 +1,75 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.java.utils; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.nio.file.*; |
||||
import java.nio.file.attribute.BasicFileAttributes; |
||||
import java.util.ArrayList; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Duplicates org.sonar.java.checks.verifier.FilesUtils to locate test jars within the custom-rules plugin |
||||
*/ |
||||
public class FilesUtils { |
||||
|
||||
private FilesUtils() { |
||||
} |
||||
|
||||
/** |
||||
* Default location of the jars/zips to be taken into account when performing the analysis. |
||||
*/ |
||||
private static final String DEFAULT_TEST_JARS_DIRECTORY = "target/test-jars"; |
||||
|
||||
public static List<File> getClassPath(String jarsDirectory) { |
||||
List<File> classpath = new LinkedList<>(); |
||||
Path testJars = Paths.get(jarsDirectory); |
||||
if (testJars.toFile().exists()) { |
||||
classpath = getFilesRecursively(testJars, "jar", "zip"); |
||||
} else if (!DEFAULT_TEST_JARS_DIRECTORY.equals(jarsDirectory)) { |
||||
throw new AssertionError("The directory to be used to extend class path does not exists (" |
||||
+ testJars.toAbsolutePath() |
||||
+ ")."); |
||||
} |
||||
classpath.add(new File("target/test-classes")); |
||||
return classpath; |
||||
} |
||||
|
||||
private static List<File> getFilesRecursively(Path root, String... extensions) { |
||||
final List<File> files = new ArrayList<>(); |
||||
|
||||
FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() { |
||||
@Override |
||||
public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) { |
||||
for (String extension : extensions) { |
||||
if (filePath.toString().endsWith("." |
||||
+ extension)) { |
||||
files.add(filePath.toFile()); |
||||
break; |
||||
} |
||||
} |
||||
return FileVisitResult.CONTINUE; |
||||
} |
||||
|
||||
@Override |
||||
public FileVisitResult visitFileFailed(Path file, IOException exc) { |
||||
return FileVisitResult.CONTINUE; |
||||
} |
||||
}; |
||||
|
||||
try { |
||||
Files.walkFileTree(root, visitor); |
||||
} catch (IOException e) { |
||||
// we already ignore errors in the visitor
|
||||
} |
||||
|
||||
return files; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,113 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
|
||||
package com.keyware.sonar.java.utils; |
||||
|
||||
import org.sonar.plugins.html.checks.HtmlIssue; |
||||
import org.sonar.plugins.html.checks.PreciseHtmlIssue; |
||||
|
||||
import javax.annotation.Nullable; |
||||
import java.util.*; |
||||
|
||||
/** |
||||
* Html检查消息验证器 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/20 |
||||
*/ |
||||
public final class HtmlCheckMessagesVerifier { |
||||
|
||||
public static HtmlCheckMessagesVerifier verify(Collection<HtmlIssue> messages) { |
||||
return new HtmlCheckMessagesVerifier(messages); |
||||
} |
||||
|
||||
private final Iterator<HtmlIssue> iterator; |
||||
private HtmlIssue current; |
||||
|
||||
private static final Comparator<HtmlIssue> ORDERING = (left, right) -> { |
||||
if (Objects.equals(left.line(), right.line())) { |
||||
return left.message().compareTo(right.message()); |
||||
} else if (left.line() == null) { |
||||
return -1; |
||||
} else if (right.line() == null) { |
||||
return 1; |
||||
} else { |
||||
return left.line().compareTo(right.line()); |
||||
} |
||||
}; |
||||
|
||||
private HtmlCheckMessagesVerifier(Collection<HtmlIssue> messages) { |
||||
ArrayList<HtmlIssue> messagesList = new ArrayList<>(messages); |
||||
messagesList.sort(ORDERING); |
||||
iterator = messagesList.iterator(); |
||||
} |
||||
|
||||
public HtmlCheckMessagesVerifier next() { |
||||
if (!iterator.hasNext()) { |
||||
throw new AssertionError("\nExpected violation"); |
||||
} |
||||
current = iterator.next(); |
||||
return this; |
||||
} |
||||
|
||||
public void noMore() { |
||||
if (iterator.hasNext()) { |
||||
HtmlIssue next = iterator.next(); |
||||
throw new AssertionError("\nNo more violations expected\ngot: at line " + next.line()); |
||||
} |
||||
} |
||||
|
||||
private void checkStateOfCurrent() { |
||||
if (current == null) { |
||||
throw new IllegalStateException("Prior to this method you should call next()"); |
||||
} |
||||
} |
||||
|
||||
public HtmlCheckMessagesVerifier atLine(@Nullable Integer expectedLine) { |
||||
checkStateOfCurrent(); |
||||
if (!Objects.equals(expectedLine, current.line())) { |
||||
throw new AssertionError("\nExpected: " + expectedLine + "\ngot: " + current.line()); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public HtmlCheckMessagesVerifier atLocation(int startLine, int startColumn, int endLine, int endColumn) { |
||||
checkStateOfCurrent(); |
||||
PreciseHtmlIssue preciseHtmlIssue = (PreciseHtmlIssue) current; |
||||
if (!Objects.equals(startLine, current.line())) { |
||||
throw new AssertionError("\nExpected: " + startLine + "\ngot: " + current.line()); |
||||
} |
||||
if (!Objects.equals(startColumn, preciseHtmlIssue.startColumn())) { |
||||
throw new AssertionError("\nExpected: " + startColumn + "\ngot: " + preciseHtmlIssue.startColumn()); |
||||
} |
||||
if (!Objects.equals(endLine, preciseHtmlIssue.endLine())) { |
||||
throw new AssertionError("\nExpected: " + endLine + "\ngot: " + preciseHtmlIssue.endLine()); |
||||
} |
||||
if (!Objects.equals(endColumn, preciseHtmlIssue.endColumn())) { |
||||
throw new AssertionError("\nExpected: " + endColumn + "\ngot: " + preciseHtmlIssue.endColumn()); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public HtmlCheckMessagesVerifier withMessage(String expectedMessage) { |
||||
checkStateOfCurrent(); |
||||
String actual = current.message(); |
||||
if (!actual.equals(expectedMessage)) { |
||||
throw new AssertionError("\nExpected: \"" + expectedMessage + "\"\ngot: \"" + actual + "\""); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public HtmlCheckMessagesVerifier withCost(@Nullable Double expectedCost) { |
||||
checkStateOfCurrent(); |
||||
if (!Objects.equals(expectedCost, current.cost())) { |
||||
throw new AssertionError("\nExpected: " + expectedCost + "\ngot: " + current.cost()); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,43 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
|
||||
package com.keyware.sonar.java.utils; |
||||
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback; |
||||
import org.junit.jupiter.api.extension.ExtensionContext; |
||||
import org.sonar.plugins.html.checks.HtmlIssue; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Html规则校验 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/20 |
||||
*/ |
||||
public class HtmlCheckMessagesVerifierRule implements AfterEachCallback { |
||||
private final List<HtmlCheckMessagesVerifier> verifiers = new ArrayList<>(); |
||||
|
||||
public HtmlCheckMessagesVerifier verify(Collection<HtmlIssue> messages) { |
||||
HtmlCheckMessagesVerifier verifier = HtmlCheckMessagesVerifier.verify(messages); |
||||
verifiers.add(verifier); |
||||
return verifier; |
||||
} |
||||
|
||||
protected void verify() { |
||||
for (HtmlCheckMessagesVerifier verifier : verifiers) { |
||||
verifier.noMore(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void afterEach(ExtensionContext extensionContext) throws Exception { |
||||
verify(); |
||||
} |
||||
} |
@ -0,0 +1,66 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
|
||||
package com.keyware.sonar.java.utils; |
||||
|
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; |
||||
import org.sonar.plugins.html.analyzers.ComplexityVisitor; |
||||
import org.sonar.plugins.html.analyzers.PageCountLines; |
||||
import org.sonar.plugins.html.api.HtmlConstants; |
||||
import org.sonar.plugins.html.lex.PageLexer; |
||||
import org.sonar.plugins.html.lex.VueLexer; |
||||
import org.sonar.plugins.html.visitor.DefaultNodeVisitor; |
||||
import org.sonar.plugins.html.visitor.HtmlAstScanner; |
||||
import org.sonar.plugins.html.visitor.HtmlSourceCode; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileNotFoundException; |
||||
import java.io.FileReader; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Html测试工具类 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/20 |
||||
*/ |
||||
public class HtmlTestHelper { |
||||
|
||||
private HtmlTestHelper() { |
||||
} |
||||
|
||||
public static HtmlSourceCode scan(File file, DefaultNodeVisitor visitor) { |
||||
FileReader fileReader; |
||||
try { |
||||
fileReader = new FileReader(file); |
||||
} catch (FileNotFoundException e) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
|
||||
HtmlSourceCode result = new HtmlSourceCode( |
||||
new TestInputFileBuilder("key", file.getPath()) |
||||
.setLanguage(HtmlConstants.LANGUAGE_KEY) |
||||
.setType(InputFile.Type.MAIN) |
||||
.setModuleBaseDir(new File(".").toPath()) |
||||
.setCharset(StandardCharsets.UTF_8) |
||||
.build() |
||||
); |
||||
|
||||
HtmlAstScanner walker = new HtmlAstScanner(List.of(new PageCountLines(), new ComplexityVisitor())); |
||||
PageLexer lexer = file.getName().endsWith(".vue") ? new VueLexer() : new PageLexer(); |
||||
walker.addVisitor(visitor); |
||||
walker.scan( |
||||
lexer.parse(fileReader), |
||||
result |
||||
); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
} |
@ -1,30 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
|
||||
|
||||
package com.keyware.sonar.cxx; |
||||
|
||||
import org.sonar.api.resources.AbstractLanguage; |
||||
|
||||
import java.lang.module.Configuration; |
||||
|
||||
public final class ConfigurationFileLanguage extends AbstractLanguage { |
||||
|
||||
public static final String NAME = "Configuration"; |
||||
public static final String KEY = "cfg"; |
||||
private final Configuration config; |
||||
|
||||
public ConfigurationFileLanguage (Configuration config) { |
||||
super(KEY, NAME); |
||||
this.config = config; |
||||
} |
||||
|
||||
@Override |
||||
public String[] getFileSuffixes() { |
||||
return new String[] {".yml", ".properties", ".ini",".conf" ,".xml" ,".json"}; |
||||
} |
||||
} |
@ -0,0 +1,47 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:28所 C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.cxx; |
||||
|
||||
import com.keyware.sonar.cxx.rules.checkers.FlagLineRule; |
||||
import com.keyware.sonar.cxx.rules.checkers.SqlVarNameChecker; |
||||
import org.sonar.api.batch.fs.FilePredicates; |
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.rule.CheckFactory; |
||||
import org.sonar.api.batch.rule.Checks; |
||||
import org.sonar.api.batch.sensor.Sensor; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.batch.sensor.SensorDescriptor; |
||||
|
||||
public class FlagLineSensor implements Sensor { |
||||
private final Checks<FlagLineRule> checks; |
||||
|
||||
|
||||
public FlagLineSensor(CheckFactory checkFactory) { |
||||
checks = checkFactory.create("cxx-security-design-rules"); |
||||
checks.addAnnotatedChecks(SqlVarNameChecker.class); |
||||
} |
||||
|
||||
@Override |
||||
public void describe(SensorDescriptor descriptor) { |
||||
//传感器名称
|
||||
descriptor.name("FlagLine1Rule" + "sensor"); |
||||
//传感器识别的语言
|
||||
descriptor.onlyOnLanguages(CxxLanguage.KEY); |
||||
//传感器扫描的规则库
|
||||
descriptor.createIssuesForRuleRepository("cxx-security-design-rules"); |
||||
} |
||||
|
||||
@Override |
||||
public void execute(SensorContext context) { |
||||
FilePredicates p = context.fileSystem().predicates(); |
||||
for (InputFile inputFile : context.fileSystem().inputFiles(p.hasLanguages(CxxLanguage.KEY))) { |
||||
checks.all().forEach(check -> { |
||||
check.execute(context, inputFile, checks.ruleKey(check)); |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:28所 C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.cxx; |
||||
|
||||
import org.sonar.api.server.rule.RulesDefinition; |
||||
|
||||
public class LogRuleRepository implements RulesDefinition { |
||||
private static final String REPOSITORY_NAME = "SonarQube"; |
||||
|
||||
@Override |
||||
public void define(Context context) { |
||||
// 创建规则仓库
|
||||
NewRepository repository = context.createRepository("log", LogLanguage.KEY) |
||||
.setName(REPOSITORY_NAME); |
||||
|
||||
// 创建新规则
|
||||
|
||||
NewRule errorRecoveryRule = repository.createRule("ParsingErrorRecovery"); |
||||
errorRecoveryRule.setName("Parsing Error Recovery"); |
||||
|
||||
// 为另一个规则设置描述
|
||||
errorRecoveryRule.setHtmlDescription("<p>This rule checks for parsing error recovery issues</p>"); |
||||
|
||||
// 完成规则创建
|
||||
repository.done(); |
||||
} |
||||
} |
@ -1,265 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.cxx.rules.checkers; |
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
import com.sonar.cxx.sslr.api.AstNode; |
||||
import com.sonar.cxx.sslr.api.Grammar; |
||||
import org.sonar.check.Rule; |
||||
import org.sonar.cxx.squidbridge.annotations.ActivatedByDefault; |
||||
import org.sonar.cxx.squidbridge.annotations.SqaleConstantRemediation; |
||||
import org.sonar.cxx.squidbridge.checks.SquidCheck; |
||||
import org.w3c.dom.Document; |
||||
import org.w3c.dom.Element; |
||||
import org.w3c.dom.Node; |
||||
import org.w3c.dom.NodeList; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
|
||||
import javax.annotation.Nullable; |
||||
import javax.xml.parsers.DocumentBuilder; |
||||
import javax.xml.parsers.DocumentBuilderFactory; |
||||
import java.io.*; |
||||
import java.util.Iterator; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
import java.util.Scanner; |
||||
|
||||
/** |
||||
* TODO LogChecker |
||||
* |
||||
* @author WuHaoYang |
||||
* @date 2024/1/19 |
||||
*/ |
||||
@Rule(key = "ConfigurationFileChecker", name = "避免在容易受攻击的地方存储口令", description = "避免在容易受攻击的地方存储口令") |
||||
@ActivatedByDefault |
||||
@SqaleConstantRemediation("5min") |
||||
public class ConfigurationFileChecker extends SquidCheck<Grammar> { |
||||
|
||||
|
||||
@Override |
||||
public void visitFile(@Nullable AstNode astNode) { |
||||
String filename = getContext().getInputFile().filename(); |
||||
|
||||
if (filename.endsWith(".properties")) { |
||||
try { |
||||
File file = new File(getContext().getInputFile().absolutePath()); |
||||
try (Scanner scanner = new Scanner(file)) { |
||||
while (scanner.hasNextLine()) { |
||||
String line = scanner.nextLine(); |
||||
if (line.contains("password")) { |
||||
System.out.println(line); |
||||
getContext().createFileViolation(this, "避免在容易受攻击的地方存储口令"); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} catch (FileNotFoundException e) { |
||||
System.out.println("文件未找到: " + e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
if (filename.endsWith(".yml")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File inputFile = getContext().getInputFile().file(); |
||||
String absolutePath = inputFile.getAbsolutePath(); |
||||
|
||||
// 构建目录路径
|
||||
File dir = new File(absolutePath).getParentFile(); |
||||
|
||||
Yaml yaml = new Yaml(); |
||||
for (File file : dir.listFiles()) { |
||||
if (file.isFile() && file.getName().endsWith(".yml")) { |
||||
try (FileInputStream fis = new FileInputStream(file)) { |
||||
Map<String, Object> obj = yaml.load(fis); |
||||
if (obj != null){ |
||||
String password = searchPassword(obj); |
||||
if (password != null) { |
||||
System.out.println("password="+password); |
||||
getContext().createFileViolation(this, "避免在容易受攻击的地方存储口令"); |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (filename.endsWith(".ini")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File inputFile = getContext().getInputFile().file(); |
||||
String absolutePath = inputFile.getAbsolutePath(); |
||||
|
||||
// 构建目录路径
|
||||
File folder = new File(absolutePath).getParentFile(); |
||||
File[] listOfFiles = folder.listFiles(); |
||||
|
||||
for (File file : listOfFiles) { |
||||
if (file.isFile() && file.getName().endsWith(".ini")) { |
||||
Properties properties = new Properties(); |
||||
|
||||
try (FileInputStream fileInput = new FileInputStream(file)) { |
||||
properties.load(fileInput); |
||||
String password = properties.getProperty("password"); |
||||
System.out.println("password=" + password); |
||||
getContext().createFileViolation(this, "避免在容易受攻击的地方存储口令"); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
if (filename.endsWith(".xml")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File inputFile = getContext().getInputFile().file(); |
||||
String absolutePath = inputFile.getAbsolutePath(); |
||||
|
||||
// 构建目录路径
|
||||
File dir = new File(absolutePath).getParentFile(); |
||||
|
||||
FilenameFilter filter = new FilenameFilter() { |
||||
public boolean accept(File dir, String name) { |
||||
return name.endsWith(".xml"); |
||||
} |
||||
}; |
||||
|
||||
String[] children = dir.list(filter); |
||||
if (children == null) { |
||||
System.out.println("目录不存在或不是目录"); |
||||
} else { |
||||
for (int i = 0; i < children.length; i++) { |
||||
String filename1 = children[i]; |
||||
File xmlFile = new File(dir, filename1); |
||||
processXML(xmlFile); |
||||
getContext().createFileViolation(this, "避免在容易受攻击的地方存储口令"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
if (filename.endsWith(".json")){ |
||||
try { |
||||
// 获取当前输入文件的绝对路径
|
||||
File inputFile = getContext().getInputFile().file(); |
||||
String absolutePath = inputFile.getAbsolutePath(); |
||||
|
||||
// 构建目录路径
|
||||
File folder = new File(absolutePath).getParentFile(); |
||||
|
||||
File[] listOfFiles = folder.listFiles(); |
||||
|
||||
if (listOfFiles != null) { |
||||
ObjectMapper mapper = new ObjectMapper(); |
||||
for (File file : listOfFiles) { |
||||
if (file.isFile() && file.getName().endsWith(".json")) { |
||||
JsonNode rootNode = mapper.readTree(file); |
||||
extractPassword(rootNode); |
||||
getContext().createFileViolation(this, "避免在容易受攻击的地方存储口令"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
|
||||
if (filename.endsWith(".conf")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File inputFile = getContext().getInputFile().file(); |
||||
String absolutePath = inputFile.getAbsolutePath(); |
||||
|
||||
// 构建目录路径
|
||||
File folder = new File(absolutePath).getParentFile(); |
||||
|
||||
File[] listOfFiles = folder.listFiles((dir, name) -> name.endsWith(".conf")); |
||||
|
||||
for (File file : listOfFiles) { |
||||
if (file.isFile()) { |
||||
Properties prop = new Properties(); |
||||
InputStream input = null; |
||||
|
||||
try { |
||||
input = new FileInputStream(file); |
||||
prop.load(input); |
||||
|
||||
if (prop.containsKey("password")) { |
||||
System.out.println("password="+ prop.getProperty("password")); |
||||
getContext().createFileViolation(this, "避免在容易受攻击的地方存储口令"); |
||||
} |
||||
|
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} finally { |
||||
if (input != null) { |
||||
try { |
||||
input.close(); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
public static void extractPassword(JsonNode node) { |
||||
Iterator<String> fieldNames = node.fieldNames(); |
||||
while (fieldNames.hasNext()) { |
||||
String fieldName = fieldNames.next(); |
||||
if (fieldName.equals("password")) { |
||||
System.out.println("Password= " + node.get(fieldName).asText()); |
||||
} |
||||
if (node.get(fieldName).isContainerNode()) { |
||||
extractPassword(node.get(fieldName)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void processXML(File xmlFile) { |
||||
try { |
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); |
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); |
||||
Document doc = dBuilder.parse(xmlFile); |
||||
doc.getDocumentElement().normalize(); |
||||
|
||||
NodeList nList = doc.getElementsByTagName("password"); |
||||
|
||||
for (int i = 0; i < nList.getLength(); i++) { |
||||
Node nNode = nList.item(i); |
||||
if (nNode.getNodeType() == Node.ELEMENT_NODE) { |
||||
Element eElement = (Element) nNode; |
||||
System.out.println("Password="+ eElement.getTextContent()); |
||||
|
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
private static String searchPassword(Map<String, Object> map) { |
||||
for (String key : map.keySet()) { |
||||
if ("password".equals(key) && map.get(key) instanceof String) { |
||||
return (String) map.get(key); |
||||
} else if (map.get(key) instanceof Map) { |
||||
String password = searchPassword((Map<String, Object>) map.get(key)); |
||||
if (password != null) { |
||||
return password; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,16 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:28所 C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.cxx.rules.checkers; |
||||
|
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.rule.RuleKey; |
||||
|
||||
public interface FlagLineRule { |
||||
|
||||
void execute(SensorContext sensorContext, InputFile file, RuleKey ruleKey); |
||||
} |
@ -0,0 +1,78 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:28所 C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.cxx.rules.checkers; |
||||
|
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.batch.sensor.issue.NewIssue; |
||||
import org.sonar.api.rule.RuleKey; |
||||
import org.sonar.check.Priority; |
||||
import org.sonar.check.Rule; |
||||
import org.sonar.cxx.squidbridge.annotations.ActivatedByDefault; |
||||
import org.sonar.cxx.squidbridge.annotations.SqaleConstantRemediation; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Scanner; |
||||
|
||||
/** |
||||
* C++规则检查器的实现示例 |
||||
* |
||||
* @author GuoXin |
||||
* @date 2024/1/6 |
||||
*/ |
||||
@Rule(key = "FlagLine1Rule", name = "sql注入", description = "sql注入有一定风险", priority = Priority.INFO, tags = {"28suo"}) |
||||
@ActivatedByDefault |
||||
@SqaleConstantRemediation("5min") |
||||
public class SqlVarNameChecker implements FlagLineRule { |
||||
|
||||
@Override |
||||
public void execute(SensorContext sensorContext, InputFile file, RuleKey ruleKey) { |
||||
try (Scanner scanner = new Scanner(file.inputStream(), StandardCharsets.UTF_8.name())) { |
||||
int lineNumber = 1; |
||||
while (scanner.hasNextLine()) { |
||||
String line = scanner.nextLine(); |
||||
if (line.contains("= '") || line.contains("OR 1=1") || line.contains("='") |
||||
|| line.contains("DROP TABLE") || line.contains("' OR 'a'='a") || line.contains("'; DROP TABLE users; --") |
||||
|| line.contains("'; EXEC xp_cmdshell 'dir'") || line.contains("' OR username LIKE '%") || line.contains("' AND SLEEP(5)") |
||||
|| line.contains("= \\'' +") || line.contains("= ?")) { // 根据特定的关键词或模式匹配来定位 SQL 注入
|
||||
NewIssue newIssue = sensorContext.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(file) |
||||
.at(file.selectLine(lineNumber))) |
||||
.save(); |
||||
} |
||||
lineNumber++; |
||||
} |
||||
} catch (IOException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
/* |
||||
@Override |
||||
public void visitNode(AstNode node) { |
||||
File file = getContext().getFile(); |
||||
System.out.println("文件路径: " + file.getAbsolutePath()); |
||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) { |
||||
String line; |
||||
int lineNumber = 0; |
||||
while ((line = reader.readLine()) != null) { |
||||
lineNumber++; |
||||
if (line.contains("= '") || line.contains("OR 1=1") || line.contains("='") |
||||
|| line.contains("DROP TABLE") || line.contains("' OR 'a'='a") || line.contains("'; DROP TABLE users; --") |
||||
|| line.contains("'; EXEC xp_cmdshell 'dir'") || line.contains("' OR username LIKE '%") || line.contains("' AND SLEEP(5)") |
||||
|| line.contains("= \\'' +") || line.contains("= ?")) { |
||||
getContext().createLineViolation(ABCVarNameChecker.this, "sql 注入有一定风险", lineNumber); |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
}*/ |
||||
} |
@ -1,72 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:C++ 信息安全性设计准则 |
||||
* 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.cxx.rules.checkers; |
||||
|
||||
|
||||
import com.keyware.sonar.cxx.CxxFileTesterHelper; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.sonar.cxx.CxxAstScanner; |
||||
import org.sonar.cxx.squidbridge.api.CheckMessage; |
||||
import org.sonar.cxx.squidbridge.api.SourceFile; |
||||
|
||||
import java.io.File; |
||||
import java.io.FilenameFilter; |
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* TODO ConfigurationFileCheckerTest |
||||
* |
||||
* @author WuHaoYang |
||||
* @date 2024/1/19 |
||||
*/ |
||||
public class ConfigurationFileCheckerTest { |
||||
|
||||
@Test |
||||
public void checkDirectory() throws IOException { |
||||
|
||||
|
||||
File folder = new File("src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile"); // 文件夹路径
|
||||
|
||||
File[] files = folder.listFiles(new FilenameFilter() { |
||||
public boolean accept(File dir, String name) { |
||||
return name.endsWith(".yml") || name.endsWith(".ini") || name.endsWith(".properties") || |
||||
name.endsWith(".xml") || name.endsWith(".json") || name.endsWith(".conf"); |
||||
} |
||||
}); |
||||
|
||||
List<String> problems = new ArrayList<>(); // 存储问题的列表
|
||||
|
||||
for (File file : files) { |
||||
|
||||
try { |
||||
if (file.isFile()) { |
||||
var tester = CxxFileTesterHelper.create("configFile/" + file.getName()); |
||||
System.out.println("配置文件名称:" + file.getName()); |
||||
var checker = new ConfigurationFileChecker(); |
||||
SourceFile sourceFile = CxxAstScanner.scanSingleInputFile(tester.asInputFile(), checker); |
||||
|
||||
for (CheckMessage message : sourceFile.getCheckMessages()) { |
||||
if (message.formatDefaultMessage().equals("避免在容易受攻击的地方存储口令")) { |
||||
problems.add("文件:" + file.getName() + " ,问题:" + message.getDefaultMessage()); |
||||
} |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
System.out.println("在处理文件时遇到问题:" + file.getName()); |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
for (String problem : problems) { |
||||
System.out.println(problem); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -1,276 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. |
||||
* 项目名称:信息安全性设计准则检查插件 |
||||
* 项目描述:用于检查源代码的安全性设计准则的Sonarqube插件 |
||||
* 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 |
||||
*/ |
||||
package com.keyware.sonar.java.rules.checkers; |
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
import org.sonar.api.batch.fs.InputFile; |
||||
import org.sonar.api.batch.sensor.SensorContext; |
||||
import org.sonar.api.batch.sensor.issue.NewIssue; |
||||
import org.sonar.api.rule.RuleKey; |
||||
import org.sonar.check.Rule; |
||||
import org.w3c.dom.Document; |
||||
import org.w3c.dom.Element; |
||||
import org.w3c.dom.Node; |
||||
import org.w3c.dom.NodeList; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
|
||||
import javax.xml.parsers.DocumentBuilder; |
||||
import javax.xml.parsers.DocumentBuilderFactory; |
||||
import java.io.*; |
||||
import java.util.Iterator; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
import java.util.Scanner; |
||||
|
||||
|
||||
/** |
||||
* 通过用户名口令、数据证书等其他手段对用户身份进行验证。 |
||||
* |
||||
* @author WuHaoYang |
||||
* @date 2024/1/22 |
||||
*/ |
||||
@Rule(key = "ConfigurationFileChecker") |
||||
public class ConfigurationFileChecker implements ConfigCheck{ |
||||
|
||||
|
||||
public void execute(SensorContext context, InputFile inputFile, RuleKey ruleKey){ |
||||
//文件名称
|
||||
String filename = inputFile.filename(); |
||||
System.out.println("[ConfigurationFileChecker]>>>>>" + filename); |
||||
|
||||
//校验文件后缀
|
||||
if (filename.endsWith(".properties")) { |
||||
try { |
||||
File file = new File(inputFile.absolutePath()); |
||||
System.out.println("---------------properties文件路径----------------"+file); |
||||
try (Scanner scanner = new Scanner(file)) { |
||||
int lineNum = 1; |
||||
while (scanner.hasNextLine()) { |
||||
String line = scanner.nextLine(); |
||||
if (line.contains("password")) { |
||||
System.out.println(line); |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
break; |
||||
} |
||||
lineNum ++; |
||||
} |
||||
} |
||||
} catch (FileNotFoundException e) { |
||||
System.out.println("文件未找到: " + e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
|
||||
if (filename.endsWith(".ini")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
// 构建目录路径
|
||||
System.out.println("---------------ini文件路径----------------"+file1); |
||||
|
||||
int lineNum = 1; |
||||
Properties properties = new Properties(); |
||||
|
||||
try (FileInputStream fileInput = new FileInputStream(file1)) { |
||||
properties.load(fileInput); |
||||
String password = properties.getProperty("password"); |
||||
System.out.println("password=" + password); |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
lineNum++; |
||||
} |
||||
|
||||
|
||||
|
||||
if (filename.endsWith(".conf")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
|
||||
// 构建目录路径
|
||||
System.out.println("---------------conf文件路径----------------"+file1); |
||||
|
||||
|
||||
int lineNum = 1; |
||||
Properties prop = new Properties(); |
||||
InputStream input = null; |
||||
|
||||
try { |
||||
input = new FileInputStream(file1); |
||||
prop.load(input); |
||||
|
||||
if (prop.containsKey("password")) { |
||||
System.out.println("password="+ prop.getProperty("password")); |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
} |
||||
} catch (IOException ex) { |
||||
ex.printStackTrace(); |
||||
} finally { |
||||
if (input != null) { |
||||
try { |
||||
input.close(); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
lineNum++; |
||||
} |
||||
|
||||
|
||||
|
||||
if (filename.endsWith(".xml")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
File absoluteFile = file1.getAbsoluteFile(); |
||||
|
||||
// 构建目录路径
|
||||
File dir = new File(String.valueOf(absoluteFile)).getParentFile(); |
||||
System.out.println("---------------xml文件路径----------------"+file1); |
||||
File xmlFile = new File(dir, filename); |
||||
processXML(xmlFile); |
||||
int lineNum = 1; |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
} |
||||
|
||||
|
||||
if (filename.endsWith(".json")){ |
||||
try { |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
File absoluteFile = file1.getAbsoluteFile(); |
||||
|
||||
// 构建目录路径
|
||||
File folder = new File(String.valueOf(absoluteFile)).getParentFile(); |
||||
|
||||
System.out.println("---------------json文件路径----------------"+file1); |
||||
ObjectMapper mapper = new ObjectMapper(); |
||||
JsonNode rootNode = mapper.readTree(file1); |
||||
extractPassword(rootNode); |
||||
int lineNum = 1; |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
|
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
|
||||
if (filename.endsWith(".yml")){ |
||||
// 获取当前输入文件的绝对路径
|
||||
File file1 = inputFile.file(); |
||||
File absoluteFile = file1.getAbsoluteFile(); |
||||
|
||||
// 构建目录路径
|
||||
File dir = new File(String.valueOf(absoluteFile)).getParentFile(); |
||||
System.out.println("---------------yml文件路径----------------"+file1); |
||||
Yaml yaml = new Yaml(); |
||||
try (FileInputStream fis = new FileInputStream(file1)) { |
||||
Map<String, Object> obj = yaml.load(fis); |
||||
if (obj != null){ |
||||
String password = searchPassword(obj); |
||||
if (password != null) { |
||||
System.out.println("password="+password); |
||||
int lineNum = 1; |
||||
NewIssue newIssue = context.newIssue(); |
||||
newIssue |
||||
.forRule(ruleKey) |
||||
.at(newIssue.newLocation() |
||||
.on(inputFile) |
||||
.at(inputFile.selectLine(lineNum))) |
||||
.save(); |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
public static void processXML(File xmlFile) { |
||||
try { |
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); |
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); |
||||
Document doc = dBuilder.parse(xmlFile); |
||||
doc.getDocumentElement().normalize(); |
||||
|
||||
NodeList nList = doc.getElementsByTagName("password"); |
||||
|
||||
for (int i = 0; i < nList.getLength(); i++) { |
||||
Node nNode = nList.item(i); |
||||
if (nNode.getNodeType() == Node.ELEMENT_NODE) { |
||||
Element eElement = (Element) nNode; |
||||
System.out.println("Password="+ eElement.getTextContent()); |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
|
||||
public static void extractPassword(JsonNode node) { |
||||
Iterator<String> fieldNames = node.fieldNames(); |
||||
while (fieldNames.hasNext()) { |
||||
String fieldName = fieldNames.next(); |
||||
if (fieldName.equals("password")) { |
||||
System.out.println("Password= " + node.get(fieldName).asText()); |
||||
} |
||||
if (node.get(fieldName).isContainerNode()) { |
||||
extractPassword(node.get(fieldName)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
private static String searchPassword(Map<String, Object> map) { |
||||
for (String key : map.keySet()) { |
||||
if ("password".equals(key) && map.get(key) instanceof String) { |
||||
return (String) map.get(key); |
||||
} else if (map.get(key) instanceof Map) { |
||||
String password = searchPassword((Map<String, Object>) map.get(key)); |
||||
if (password != null) { |
||||
return password; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
Loading…
Reference in new issue