新增准则:避免在容易受攻击的地方存储口令。如果需要,应存储口令的加密hash值,以替代明文口令存储。

wuhaoyang
wuhaoyang 10 months ago
parent e6fb96d32c
commit 6af21ef2db
  1. 11
      sonar-keyware-plugins-cxx/pom.xml
  2. 30
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/ConfigurationFileLanguage.java
  3. 1
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java
  4. 4
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java
  5. 265
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileChecker.java
  6. 72
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileCheckerTest.java
  7. 3
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.conf
  8. 3
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.ini
  9. 5
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.json
  10. 2
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.properties
  11. 7
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.xml
  12. 3
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.yml

@ -82,7 +82,16 @@
<artifactId>sslr-core</artifactId> <artifactId>sslr-core</artifactId>
<version>${sonar-cxx.versin}</version> <version>${sonar-cxx.versin}</version>
</dependency> </dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.28</version>
</dependency>
<dependency>
<groupId>springframework</groupId>
<artifactId>spring</artifactId>
<version>1.2.6</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.findbugs</groupId> <groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId> <artifactId>jsr305</artifactId>

@ -0,0 +1,30 @@
/*
* 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"};
}
}

@ -99,6 +99,7 @@ public final class CxxPlugin implements Plugin {
var l = new ArrayList<Object>(); var l = new ArrayList<Object>();
l.add(LogLanguage.class); l.add(LogLanguage.class);
l.add(ConfigurationFileLanguage.class);
// utility classes // utility classes
l.add(CxxUnitTestResultsAggregator.class); l.add(CxxUnitTestResultsAggregator.class);

@ -286,7 +286,7 @@ public class CxxSquidSensor implements ProjectSensor {
public void describe(SensorDescriptor descriptor) { public void describe(SensorDescriptor descriptor) {
descriptor descriptor
.name("CXX") .name("CXX")
.onlyOnLanguages(CxxLanguage.KEY, LogLanguage.KEY) .onlyOnLanguages(CxxLanguage.KEY, LogLanguage.KEY,ConfigurationFileLanguage.KEY)
.onlyOnFileType(InputFile.Type.MAIN) .onlyOnFileType(InputFile.Type.MAIN)
.onlyWhenConfiguration(conf -> !conf.getBoolean(SQUID_DISABLED_KEY).orElse(false)); .onlyWhenConfiguration(conf -> !conf.getBoolean(SQUID_DISABLED_KEY).orElse(false));
} }
@ -373,7 +373,7 @@ public class CxxSquidSensor implements ProjectSensor {
private Iterable<InputFile> getInputFiles(SensorContext context, CxxSquidConfiguration squidConfig) { private Iterable<InputFile> getInputFiles(SensorContext context, CxxSquidConfiguration squidConfig) {
Iterable<InputFile> inputFiles = context.fileSystem().inputFiles( Iterable<InputFile> inputFiles = context.fileSystem().inputFiles(
context.fileSystem().predicates().and( context.fileSystem().predicates().and(
context.fileSystem().predicates().hasLanguages(CxxLanguage.KEY, LogLanguage.KEY), context.fileSystem().predicates().hasLanguages(CxxLanguage.KEY, LogLanguage.KEY,ConfigurationFileLanguage.KEY),
context.fileSystem().predicates().hasType(InputFile.Type.MAIN) context.fileSystem().predicates().hasType(InputFile.Type.MAIN)
) )
); );

@ -0,0 +1,265 @@
/*
* 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,72 @@
/*
* 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);
}
}
}

@ -0,0 +1,5 @@
{
"username": "john_doe",
"password": "s3cr3tP@ssw0rd",
"email": "john.doe@example.com"
}

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<user>
<username>exampleUser</username>
<password>examplePassword</password>
</user>
Loading…
Cancel
Save