diff --git a/sonar-keyware-plugins-cxx/pom.xml b/sonar-keyware-plugins-cxx/pom.xml index 7baecb8..7cb658c 100644 --- a/sonar-keyware-plugins-cxx/pom.xml +++ b/sonar-keyware-plugins-cxx/pom.xml @@ -82,7 +82,16 @@ sslr-core ${sonar-cxx.versin} - + + org.yaml + snakeyaml + 1.28 + + + springframework + spring + 1.2.6 + com.google.code.findbugs jsr305 diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/ConfigurationFileLanguage.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/ConfigurationFileLanguage.java new file mode 100644 index 0000000..87e8107 --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/ConfigurationFileLanguage.java @@ -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"}; + } +} diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java index 28008d5..91d2e69 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxPlugin.java @@ -99,6 +99,7 @@ public final class CxxPlugin implements Plugin { var l = new ArrayList(); l.add(LogLanguage.class); + l.add(ConfigurationFileLanguage.class); // utility classes l.add(CxxUnitTestResultsAggregator.class); diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java index 4c98b73..b0b8727 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java @@ -286,7 +286,7 @@ public class CxxSquidSensor implements ProjectSensor { public void describe(SensorDescriptor descriptor) { descriptor .name("CXX") - .onlyOnLanguages(CxxLanguage.KEY, LogLanguage.KEY) + .onlyOnLanguages(CxxLanguage.KEY, LogLanguage.KEY,ConfigurationFileLanguage.KEY) .onlyOnFileType(InputFile.Type.MAIN) .onlyWhenConfiguration(conf -> !conf.getBoolean(SQUID_DISABLED_KEY).orElse(false)); } @@ -373,7 +373,7 @@ public class CxxSquidSensor implements ProjectSensor { private Iterable getInputFiles(SensorContext context, CxxSquidConfiguration squidConfig) { Iterable inputFiles = context.fileSystem().inputFiles( 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) ) ); diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileChecker.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileChecker.java new file mode 100644 index 0000000..26c420c --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileChecker.java @@ -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 { + + + @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 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 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 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) map.get(key)); + if (password != null) { + return password; + } + } + } + return null; + } +} diff --git a/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileCheckerTest.java b/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileCheckerTest.java new file mode 100644 index 0000000..787c36d --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/rules/checkers/ConfigurationFileCheckerTest.java @@ -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 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); + } + } + + +} diff --git a/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.conf b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.conf new file mode 100644 index 0000000..52ad898 --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.conf @@ -0,0 +1,3 @@ +# 这是一个.conf配置文件 + +password = yourpassword \ No newline at end of file diff --git a/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.ini b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.ini new file mode 100644 index 0000000..c57ef7f --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.ini @@ -0,0 +1,3 @@ +[UserCredentials] +username = exampleUser +password = examplePassword \ No newline at end of file diff --git a/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.json b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.json new file mode 100644 index 0000000..6502f1a --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.json @@ -0,0 +1,5 @@ +{ + "username": "john_doe", + "password": "s3cr3tP@ssw0rd", + "email": "john.doe@example.com" +} diff --git a/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.properties b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.properties new file mode 100644 index 0000000..ed2cc37 --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.properties @@ -0,0 +1,2 @@ +# ConfigurationFileChecker.properties +password=abc123 \ No newline at end of file diff --git a/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.xml b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.xml new file mode 100644 index 0000000..dc130d7 --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.xml @@ -0,0 +1,7 @@ + + + + exampleUser + examplePassword + + diff --git a/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.yml b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.yml new file mode 100644 index 0000000..f560970 --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/configFile/ConfigurationFileChecker.yml @@ -0,0 +1,3 @@ +database: + user: admin + password: mySecurePassword \ No newline at end of file