新增准则:应通过用户名口令、数字证书等其他手段对主机身份进行鉴别

wuhaoyang
Guo XIn 8 months ago
parent aaee78b3fd
commit c0365cb297
  1. 41
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/CxxSquidSensor.java
  2. 17
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/SecurityDesignRuleProperties.java
  3. 133
      sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/HostIdentityVerifyChecker.java
  4. 45
      sonar-keyware-plugins-cxx/src/test/java/com/keyware/sonar/cxx/rules/checkers/HostIdentityVerifyCheckerTest.java
  5. 80
      sonar-keyware-plugins-cxx/src/test/resources/com/keyware/sonar/cxx/rules/checkers/HostIdentityVerifyChecker.cc

@ -38,6 +38,7 @@ import org.sonar.cxx.sensors.utils.CxxUtils;
import org.sonar.cxx.squidbridge.SquidAstVisitor; import org.sonar.cxx.squidbridge.SquidAstVisitor;
import org.sonar.cxx.squidbridge.api.SourceCode; import org.sonar.cxx.squidbridge.api.SourceCode;
import org.sonar.cxx.squidbridge.api.SourceFile; import org.sonar.cxx.squidbridge.api.SourceFile;
import org.sonar.cxx.squidbridge.api.SourceProject;
import org.sonar.cxx.squidbridge.indexer.QueryByType; import org.sonar.cxx.squidbridge.indexer.QueryByType;
import org.sonar.cxx.visitors.CxxCpdVisitor; import org.sonar.cxx.visitors.CxxCpdVisitor;
import org.sonar.cxx.visitors.CxxHighlighterVisitor; import org.sonar.cxx.visitors.CxxHighlighterVisitor;
@ -286,7 +287,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,ConfigurationFileLanguage.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));
} }
@ -315,7 +316,7 @@ public class CxxSquidSensor implements ProjectSensor {
Iterable<InputFile> inputFiles = getInputFiles(context, squidConfig); Iterable<InputFile> inputFiles = getInputFiles(context, squidConfig);
scanner.scanInputFiles(inputFiles); scanner.scanInputFiles(inputFiles);
Collection<SourceCode> squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceFile.class)); Collection<SourceCode> squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceProject.class), new QueryByType(SourceFile.class));
save(squidSourceFiles); save(squidSourceFiles);
} }
@ -373,7 +374,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,ConfigurationFileLanguage.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)
) )
); );
@ -400,9 +401,10 @@ public class CxxSquidSensor implements ProjectSensor {
} }
private void save(Collection<SourceCode> sourceCodeFiles) { private void save(Collection<SourceCode> sourceCodeFiles) {
for (var sourceCodeFile : sourceCodeFiles) { for (var sourceCode : sourceCodeFiles) {
try { try {
var sourceFile = (SourceFile) sourceCodeFile; if (sourceCode instanceof SourceFile) {
var sourceFile = (SourceFile) sourceCode;
InputFile inputFile = context.fileSystem().inputFile( InputFile inputFile = context.fileSystem().inputFile(
context.fileSystem().predicates().hasPath(sourceFile.getKey()) context.fileSystem().predicates().hasPath(sourceFile.getKey())
); );
@ -411,8 +413,13 @@ public class CxxSquidSensor implements ProjectSensor {
saveFileLinesContext(inputFile, sourceFile); saveFileLinesContext(inputFile, sourceFile);
saveCpdTokens(inputFile, sourceFile); saveCpdTokens(inputFile, sourceFile);
saveHighlighting(inputFile, sourceFile); saveHighlighting(inputFile, sourceFile);
} else if (sourceCode instanceof SourceProject) {
var sourceProject = (SourceProject) sourceCode;
saveProjectViolations(sourceProject);
}
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
var msg = "Cannot save all measures for file '" + sourceCodeFile.getKey() + "'"; var msg = "Cannot save all measures for file '" + sourceCode.getKey() + "'";
CxxUtils.validateRecovery(msg, e, context.config()); CxxUtils.validateRecovery(msg, e, context.config());
} }
} }
@ -451,6 +458,28 @@ public class CxxSquidSensor implements ProjectSensor {
saveMetric(inputFile, CxxMetrics.BIG_FUNCTIONS_LOC, sourceFile.getInt(CxxMetric.BIG_FUNCTIONS_LOC)); saveMetric(inputFile, CxxMetrics.BIG_FUNCTIONS_LOC, sourceFile.getInt(CxxMetric.BIG_FUNCTIONS_LOC));
} }
/**
* 保存项目问题
*
* @param sourceProject
*/
private void saveProjectViolations(SourceProject sourceProject) {
sourceProject.getCheckMessages().forEach(message -> {
var checker = (SquidAstVisitor<Grammar>) message.getCheck();
RuleKey ruleKey = checks.ruleKey(checker);
if (ruleKey != null) {
var newIssue = context.newIssue().forRule(ruleKey);
var location = newIssue.newLocation()
.on(context.project())
.message(message.getText(Locale.CHINA));
newIssue.at(location);
newIssue.save();
LOG.debug("[KeyWare]>>保存问题项:{}, {}, {}", context.project().key(), ruleKey.toString(), message.getText(Locale.CHINA));
}
});
}
private void saveViolations(InputFile inputFile, SourceFile sourceFile) { private void saveViolations(InputFile inputFile, SourceFile sourceFile) {
if (sourceFile.hasCheckMessages()) { if (sourceFile.hasCheckMessages()) {
for (var message : sourceFile.getCheckMessages()) { for (var message : sourceFile.getCheckMessages()) {

@ -0,0 +1,17 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称信息安全性设计准则检查插件
* 项目描述用于检查源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx.rules;
/**
* C++ 安全设计准则检查规则属性
*
* @author GuoXin
* @date 2024/1/18
*/
public class SecurityDesignRuleProperties {
}

@ -0,0 +1,133 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称信息安全性设计准则检查插件
* 项目描述用于检查源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx.rules.checkers;
import com.sonar.cxx.sslr.api.AstNode;
import com.sonar.cxx.sslr.api.Grammar;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.cxx.parser.CxxGrammarImpl;
import org.sonar.cxx.squidbridge.api.CheckMessage;
import org.sonar.cxx.squidbridge.api.SourceCode;
import org.sonar.cxx.visitors.MultiLocatitionSquidCheck;
import javax.annotation.Nonnull;
import java.util.Set;
/**
* 应通过用户名口令数字证书等其他手段对主机身份进行鉴别
* <p>仅检测代码中有没有用户名口令和数字证书</p>
*
* @author GuoXin
* @date 2024/1/24
*/
@Rule(key = "HostIdentityVerifyChecker", name = "程序中未识别到通过用户名口令或者数字证书验证等安全认证逻辑", description = "应通过用户名口令、数字证书等其他手段对主机身份进行鉴别", priority = Priority.MAJOR, tags = {"28suo"})
public class HostIdentityVerifyChecker extends MultiLocatitionSquidCheck<Grammar> {
private static final Logger LOG = Loggers.get(HostIdentityVerifyChecker.class);
private SourceCode project;
private boolean hasUsername = false;
private boolean hasPassword = false;
private boolean hasCertificate = false;
private final Set<String> SSL_FUNCTIONS = Set.of(
"SSL_use_certificate_file",
"SSL_use_PrivateKey_file",
"SSL_use_RSAPrivateKey_file",
"SSL_CTX_use_certificate_file",
"SSL_CTX_use_RSAPrivateKey_file",
"SSL_CTX_use_PrivateKey_file",
"SSL_CTX_use_certificate_chain_file",
"SSL_use_certificate_chain_file",
"SSL_library_init",
"PEM_read_X509",
"d2i_X509_bio",
"X509_verify",
"X509_check_issued",
"X509_get_notBefore",
"X509_get_notAfter",
"X509_get_subject_name",
"X509_get_issuer_name",
"X509_get_pubkey",
"X509_STORE_CTX_init",
"X509_STORE_add_cert",
"X509_verify_cert"
);
@Override
public void init() {
LOG.info("[KeyWare][C++检查] >> 应通过用户名口令、数字证书等其他手段对主机身份进行鉴别");
if (project == null) {
project = getContext().peekSourceCode();
}
subscribeTo(CxxGrammarImpl.functionDefinition, CxxGrammarImpl.simpleDeclaration, CxxGrammarImpl.postfixExpression);
}
@Override
public void visitNode(@Nonnull AstNode astNode) {
if ((hasUsername && hasPassword) || hasCertificate) {
return;
}
if (astNode.is(CxxGrammarImpl.functionDefinition)) {
visitFunctionDefinition(astNode);
} else if (astNode.is(CxxGrammarImpl.simpleDeclaration)) {
visitSimpleDeclaration(astNode);
} else if (astNode.is(CxxGrammarImpl.postfixExpression)) {
visitPostfixExpression(astNode);
}
}
private void visitFunctionDefinition(@Nonnull AstNode astNode) {
astNode.getDescendants(CxxGrammarImpl.parameterDeclaration).forEach(this::checkDeclarationNode);
}
private void visitSimpleDeclaration(@Nonnull AstNode astNode) {
astNode.getDescendants(CxxGrammarImpl.declarator).forEach(this::checkDeclarationNode);
}
private void visitPostfixExpression(@Nonnull AstNode astNode) {
var nameNode = astNode.getFirstDescendant(CxxGrammarImpl.declaratorId);
if (nameNode == null) {
nameNode = astNode.getFirstDescendant(CxxGrammarImpl.className);
}
if (nameNode != null) {
var nameStr = nameNode.getTokenValue();
if (SSL_FUNCTIONS.contains(nameStr)) {
hasCertificate = true;
}
}
}
private void checkDeclarationNode(@Nonnull AstNode astNode) {
var paramNode = astNode.getFirstDescendant(CxxGrammarImpl.declaratorId);
if (paramNode != null) {
var paramName = paramNode.getTokenValue();
paramName = paramName.toLowerCase().replace("-", "").replace("_", "");
if ("username".equals(paramName)) {
hasUsername = true;
} else if ("password".equals(paramName)) {
hasPassword = true;
}
}
}
/**
* 当访问器销毁时执行代表所有文件已经被访问完毕可以进行最后的处理
*/
@Override
public void destroy() {
if (!(hasPassword && hasUsername) && !hasCertificate) {
project.log(new CheckMessage(this, "应通过用户名口令、数字证书等其他手段对主机身份进行鉴别"));
}
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved.
* 项目名称信息安全性设计准则检查插件
* 项目描述用于检查源代码的安全性设计准则的Sonarqube插件
* 版权说明本软件属北京关键科技股份有限公司所有在未获得北京关键科技股份有限公司正式授权情况下任何企业和个人不能获取阅读安装传播本软件涉及的任何受知识产权保护的内容
*/
package com.keyware.sonar.cxx.rules.checkers;
import com.keyware.sonar.cxx.CxxFileTesterHelper;
import org.junit.jupiter.api.Test;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.cxx.CxxAstScanner;
import org.sonar.cxx.config.CxxSquidConfiguration;
import org.sonar.cxx.squidbridge.api.SourceCode;
import org.sonar.cxx.squidbridge.api.SourceProject;
import org.sonar.cxx.squidbridge.indexer.QueryByType;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 测试规则应通过用户名口令数字证书等其他手段对主机身份进行鉴别
*
* @author GuoXin
* @date 2024/1/24
*/
public class HostIdentityVerifyCheckerTest {
@Test
public void checkTest() throws IOException {
var checker = new HostIdentityVerifyChecker();
var tester = CxxFileTesterHelper.create("HostIdentityVerifyChecker.cc");
var scanner = CxxAstScanner.create(new CxxSquidConfiguration(), checker);
Set<InputFile> inputFiles = Set.of(tester.asInputFile());
scanner.scanInputFiles(inputFiles);
Collection<SourceCode> squidSourceFiles = scanner.getIndex().search(new QueryByType(SourceProject.class));
assertThat(squidSourceFiles).size().isEqualTo(1);
squidSourceFiles.forEach(sourceCode -> {
assertThat(sourceCode.getCheckMessages()).size().isEqualTo(1);
});
}
}

@ -0,0 +1,80 @@
#include <iostream>
#include <string>
#include <boost/asio.hpp>
// 本示例中对合规代码进行了注释
// 假设有一个函数去异步验证用户名和密码
/*void verify_credentials(const std::string& username, const std::string& password) {
// 如果验证成功,则执行相应操作
if (is_valid(username, password)) {
std::cout << "Authentication successful." << std::endl;
} else {
std::cout << "Invalid credentials." << std::endl;
}
}*/
int main() {
/*std::string user_name, pass_word;
std::cout << "Enter your username: ";
std::cin >> user_name;
std::cout << "Enter your password: ";
std::getline(std::cin, pass_word);
// 调用验证函数
verify_credentials(user_name, pass_word);*/
return 0;
}
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
bool verifyCertificate(const std::string& certPath) {
// 初始化OpenSSL库
/*SSL_library_init();
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
// 加载证书文件
FILE* certFile = fopen(certPath.c_str(), "r");
if (!certFile) {
perror("Failed to open certificate file");
return false;
}
X509* x509Cert = PEM_read_X509(certFile, NULL, NULL, NULL);
fclose(certFile);
if (!x509Cert) {
ERR_print_errors_fp(stderr);
return false;
}
// 这里只做简单的证书有效性检查,真实场景下需要构建完整的信任链并验证到根CA
int result = X509_verify(x509Cert, NULL);
// 释放资源
X509_free(x509Cert);
// 返回验证结果
return (result == 1);*/
}
int main2() {
const std::string certPath = "/path/to/certificate.pem";
bool isVerified = verifyCertificate(certPath);
if (isVerified) {
std::cout << "The certificate has been verified successfully." << std::endl;
} else {
std::cout << "Failed to verify the certificate." << std::endl;
}
// 清理OpenSSL库
EVP_cleanup();
ERR_free_strings();
return 0;
}
Loading…
Cancel
Save