Compare commits

...

5 Commits

  1. 188
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/FormatFunctionCheck.java
  2. 40
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/rules/checkers/FormatFunctionCheckTest.java
  3. 23
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/FormatFunctionCheck.cc
  4. 196
      sonar-keyware-plugins-java/src/main/java/com/keyware/sonar/java/rules/checkers/ConfigurationFileChecker.java

@ -0,0 +1,188 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称C++ 信息安全性设计准则
* 项目描述用于检查C++源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx.rules.checkers;
import com.sonar.cxx.sslr.api.AstNode;
import com.sonar.cxx.sslr.api.Grammar;
import org.apache.commons.lang.StringUtils;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.cxx.parser.CxxGrammarImpl;
import org.sonar.cxx.squidbridge.annotations.ActivatedByDefault;
import org.sonar.cxx.squidbridge.annotations.SqaleConstantRemediation;
import org.sonar.cxx.squidbridge.checks.SquidCheck;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* FormatFunctionCheck
*
* @author WuHaoYang
* @date 2024/1/24
*/
@Rule(key = "FormatFunctionCheck", name = "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", description = "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", priority = Priority.INFO, tags = {"28suo"})
@ActivatedByDefault
@SqaleConstantRemediation("5min")
public class FormatFunctionCheck extends SquidCheck<Grammar> {
//函数列表
private static final List<String> TARGET_FUNCS = Arrays.asList("printf", "sprintf", "snprintf", "fprintf", "format");
@Override
public void init() {
//节点类型
subscribeTo(CxxGrammarImpl.postfixExpression);
}
@Override
public void visitNode(AstNode astNode) {
if (isFunctionCall(astNode)) {
validateFunctionCall(astNode);
}
}
private boolean isFunctionCall(AstNode astNode) {
AstNode potentialFunctionNameNode = astNode.getFirstDescendant(CxxGrammarImpl.className);
return potentialFunctionNameNode != null && TARGET_FUNCS.contains(potentialFunctionNameNode.getTokenValue());
}
private void validateFunctionCall(AstNode astNode) {
// 分别针对不同函数调用进行处理,替换原有的认证方法
String functionName = astNode.getFirstDescendant(CxxGrammarImpl.className).getTokenValue();
if(functionName.equals("printf")){
validatePrintfFunctionCall(astNode);
} else if (functionName.equals("sprintf")) {
validateSprintfFunctionCall(astNode);
} else if (functionName.equals("snprintf")) {
validateSnprintfFunctionCall(astNode);
} else if (functionName.equals("fprintf")) {
validateFprintfFunctionCall(astNode);
} else if (functionName.equals("format")) {
validateFormatFunctionCall(astNode);
}
}
// 从AstNode节点获取参数列表
private List<AstNode> getParameters(AstNode functionCallNode) {
// 获取所有子节点,包括是逗号(,)的
List<AstNode> allChildren = functionCallNode.getFirstDescendant(CxxGrammarImpl.initializerList).getChildren();
// 创建一个新的列表来保存非逗号(,)的子节点,即参数
List<AstNode> parameters = new ArrayList<>();
for (AstNode child : allChildren) {
if (!child.getTokenValue().equals(",")) {
parameters.add(child);
}
}
return parameters;
}
private boolean isFunctionFormatStatic(AstNode functionCallNode) {
if (isFunctionCall(functionCallNode)) {
String functionName = functionCallNode.getFirstDescendant(CxxGrammarImpl.className).getTokenValue();
List<AstNode> parameters = getParameters(functionCallNode);
if (TARGET_FUNCS.contains(functionName)) {
int argIndex;
if (functionName.equals("fprintf") || functionName.equals("sprintf")) {
argIndex = 1;
} else if (functionName.equals("snprintf")) {
argIndex = 2;
} else if (functionName.equals("format")) {
argIndex = 0;
} else {
argIndex = 0;
}
if (parameters.size() > argIndex) {
AstNode formatArgNode = parameters.get(argIndex);
String potentialFormatStringParam = formatArgNode.getTokenValue();
return potentialFormatStringParam.startsWith("\"") && potentialFormatStringParam.endsWith("\"");
} else {
}
}
}
return false;
}
private boolean isParameterCountValid(AstNode functionCallNode) {
String functionName = functionCallNode.getFirstDescendant(CxxGrammarImpl.className).getTokenValue();
List<AstNode> parameters = getParameters(functionCallNode);
if (TARGET_FUNCS.contains(functionName)) {
int argIndex;
if (functionName.equals("fprintf") || functionName.equals("sprintf")) {
argIndex = 1;
} else if (functionName.equals("snprintf")) {
argIndex = 2;
} else if (functionName.equals("format")) {
argIndex = 0;
} else {
argIndex = 0;
}
if (parameters.size() <= argIndex) {
return false;
}
String format = removeEscapedPercentage(parameters.get(argIndex).getTokenValue());
format = format.substring(1, format.length() - 1);
int expectedCount = StringUtils.countMatches(format, "%");
if(functionName.equals("format")) {
expectedCount = StringUtils.countMatches(format, "{}");
}
return expectedCount == parameters.size() - 1 - argIndex;
}
return false;
}
private String removeEscapedPercentage(String format){
return format.replace("%%","");
}
private void validatePrintfFunctionCall(AstNode astNode) {
if (!isFunctionFormatStatic(astNode) || !isParameterCountValid(astNode)) {
getContext().createLineViolation(this, "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", astNode);
}
}
private void validateSprintfFunctionCall(AstNode astNode) {
if (!isFunctionFormatStatic(astNode) || !isParameterCountValid(astNode)) {
getContext().createLineViolation(this, "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", astNode);
}
}
private void validateSnprintfFunctionCall(AstNode astNode) {
if (!isFunctionFormatStatic(astNode) || !isParameterCountValid(astNode)) {
getContext().createLineViolation(this, "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", astNode);
}
}
private void validateFprintfFunctionCall(AstNode astNode) {
if (!isFunctionFormatStatic(astNode) || !isParameterCountValid(astNode)) {
getContext().createLineViolation(this, "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", astNode);
}
}
private void validateFormatFunctionCall(AstNode astNode) {
if (!isFunctionFormatStatic(astNode) || !isParameterCountValid(astNode)) {
getContext().createLineViolation(this, "确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数", astNode);
}
}
}

@ -0,0 +1,40 @@
/*
* 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.SourceFile;
import org.sonar.cxx.squidbridge.checks.CheckMessagesVerifier;
import java.io.IOException;
/**
* FormatFunctionCheckTest
*
* @author WuHaoYang
* @date 2024/1/24
*/
public class FormatFunctionCheckTest {
@Test
public void checkTest() throws IOException {
var checker = new FormatFunctionCheck();
var tester = CxxFileTesterHelper.create("FormatFunctionCheck.cc");
SourceFile file = CxxAstScanner.scanSingleInputFile(tester.asInputFile(), checker);
CheckMessagesVerifier.verify(file.getCheckMessages())
.next().atLine(6).withMessage("确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数")
.next().atLine(9).withMessage("确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数")
.next().atLine(12).withMessage("确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数")
.next().atLine(16).withMessage("确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数")
.next().atLine(20).withMessage("确保向所有格式字符串函数都传递一个不能由用户控制的静态格式化字符串,并且向该函数发送正确数量的参数")
.noMore();
}
}

@ -0,0 +1,23 @@
int main()
{
const char* name = "World";
printf("Hello, %s!\n", name,name1,name2);
char buffer[50];
sprintf(buffer,"Hello, %s!\n",name,name1,name2);
char buffer[50];
snprintf(buffer, sizeof(buffer), "Hello, %s!\n",name, name1, name2);
FILE* fp = fopen("file.txt", "w");
if(fp) {
fprintf(fp, "Hel1o, %s!\n", name, name1, name2);
fclose(fp);
}
string str = format("Hello, {}!\n",name, name1, name2);
return 0;
}

@ -29,16 +29,15 @@ import java.util.Scanner;
/**
* 禁止在容易受攻击的地方明文存储口令密码
* 通过用户名口令数据证书等其他手段对用户身份进行验证
*
* @author WuHaoYang
* @date 2024/1/22
*/
@Rule(key = "ConfigurationFileChecker")
public class ConfigurationFileChecker implements ConfigCheck {
public class ConfigurationFileChecker implements ConfigCheck{
@Override
public void execute(SensorContext context, InputFile inputFile, RuleKey ruleKey){
//文件名称
String filename = inputFile.filename();
@ -76,35 +75,28 @@ public class ConfigurationFileChecker implements ConfigCheck {
if (filename.endsWith(".ini")){
// 获取当前输入文件的绝对路径
File file1 = inputFile.file();
File absoluteFile = file1.getAbsoluteFile();
// 构建目录路径
File folder = new File(String.valueOf(absoluteFile)).getParentFile();
System.out.println("---------------ini文件路径----------------"+folder);
File[] listOfFiles = folder.listFiles();
System.out.println("---------------ini文件路径----------------"+file1);
int lineNum = 1;
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);
NewIssue newIssue = context.newIssue();
newIssue
.forRule(ruleKey)
.at(newIssue.newLocation()
.on(inputFile)
.at(inputFile.selectLine(lineNum)))
.save();
} catch (IOException e) {
e.printStackTrace();
}
}
lineNum++;
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++;
}
@ -112,50 +104,41 @@ public class ConfigurationFileChecker implements ConfigCheck {
if (filename.endsWith(".conf")){
// 获取当前输入文件的绝对路径
File file1 = inputFile.file();
File absoluteFile = file1.getAbsoluteFile();
// 构建目录路径
File folder = new File(String.valueOf(absoluteFile)).getParentFile();
System.out.println("---------------conf文件路径----------------"+folder);
System.out.println("---------------conf文件路径----------------"+file1);
File[] listOfFiles = folder.listFiles((dir, name) -> name.endsWith(".conf"));
int lineNum = 1;
for (File file : listOfFiles) {
if (file.isFile()) {
Properties prop = new Properties();
InputStream input = null;
try {
input = new FileInputStream(file);
prop.load(input);
Properties prop = new Properties();
InputStream input = null;
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++;
}
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++;
}
@ -167,23 +150,17 @@ public class ConfigurationFileChecker implements ConfigCheck {
// 构建目录路径
File dir = new File(String.valueOf(absoluteFile)).getParentFile();
System.out.println("---------------xml文件路径----------------"+dir);
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);
}
}
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();
}
@ -195,18 +172,19 @@ public class ConfigurationFileChecker implements ConfigCheck {
// 构建目录路径
File folder = new File(String.valueOf(absoluteFile)).getParentFile();
System.out.println("---------------json文件路径----------------"+folder);
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);
}
}
}
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();
@ -221,22 +199,26 @@ public class ConfigurationFileChecker implements ConfigCheck {
// 构建目录路径
File dir = new File(String.valueOf(absoluteFile)).getParentFile();
System.out.println("---------------yml文件路径----------------"+dir);
System.out.println("---------------yml文件路径----------------"+file1);
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);
}
}
} catch (IOException e) {
e.printStackTrace();
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();
}
}

Loading…
Cancel
Save